Merge mozilla-central to fx-team

This commit is contained in:
Carsten "Tomcat" Book 2015-03-23 13:35:36 +01:00
commit 8a86055b89
165 changed files with 2682 additions and 884 deletions

View File

@ -12,6 +12,7 @@
#include "nsMai.h"
#include "nsIAccessibleTypes.h"
#include "nsIURI.h"
#include "ProxyAccessible.h"
using namespace mozilla;
using namespace mozilla::a11y;
@ -23,15 +24,19 @@ static void
getImagePositionCB(AtkImage* aImage, gint* aAccX, gint* aAccY,
AtkCoordType aCoordType)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aImage));
if (!accWrap || !accWrap->IsImage())
return;
ImageAccessible* image = accWrap->AsImage();
nsIntPoint pos;
uint32_t geckoCoordType = (aCoordType == ATK_XY_WINDOW) ?
nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE :
nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE;
nsIntPoint pos = image->Position(geckoCoordType);
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aImage));
if (accWrap && accWrap->IsImage()) {
ImageAccessible* image = accWrap->AsImage();
pos = image->Position(geckoCoordType);
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aImage))) {
pos = proxy->ImagePosition(geckoCoordType);
}
*aAccX = pos.x;
*aAccY = pos.y;
}
@ -45,11 +50,14 @@ getImageDescriptionCB(AtkImage* aImage)
static void
getImageSizeCB(AtkImage* aImage, gint* aAccWidth, gint* aAccHeight)
{
nsIntSize size;
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aImage));
if (!accWrap || !accWrap->IsImage())
return;
if (accWrap && accWrap->IsImage()) {
size = accWrap->AsImage()->Size();
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aImage))) {
size = proxy->ImageSize();
}
nsIntSize size = accWrap->AsImage()->Size();
*aAccWidth = size.width;
*aAccHeight = size.height;
}

View File

@ -10,6 +10,7 @@
#include "ProxyAccessible.h"
#include "Relation.h"
#include "HyperTextAccessible-inl.h"
#include "ImageAccessible.h"
#include "nsIPersistentProperties2.h"
#include "nsISimpleEnumerator.h"
@ -46,19 +47,26 @@ SerializeTree(Accessible* aRoot, nsTArray<AccessibleData>& aTree)
}
Accessible*
DocAccessibleChild::IdToAccessible(const uint64_t& aID)
DocAccessibleChild::IdToAccessible(const uint64_t& aID) const
{
return mDoc->GetAccessibleByUniqueID(reinterpret_cast<void*>(aID));
}
HyperTextAccessible*
DocAccessibleChild::IdToHyperTextAccessible(const uint64_t& aID)
DocAccessibleChild::IdToHyperTextAccessible(const uint64_t& aID) const
{
Accessible* acc = IdToAccessible(aID);
MOZ_ASSERT(!acc || acc->IsHyperText());
return acc ? acc->AsHyperText() : nullptr;
}
ImageAccessible*
DocAccessibleChild::IdToImageAccessible(const uint64_t& aID) const
{
Accessible* acc = IdToAccessible(aID);
return (acc && acc->IsImage()) ? acc->AsImage() : nullptr;
}
void
DocAccessibleChild::ShowEvent(AccShowEvent* aShowEvent)
{
@ -584,5 +592,31 @@ DocAccessibleChild::RecvPasteText(const uint64_t& aID,
return true;
}
bool
DocAccessibleChild::RecvImagePosition(const uint64_t& aID,
const uint32_t& aCoordType,
nsIntPoint* aRetVal)
{
ImageAccessible* acc = IdToImageAccessible(aID);
if (acc) {
*aRetVal = acc->Position(aCoordType);
}
return true;
}
bool
DocAccessibleChild::RecvImageSize(const uint64_t& aID,
nsIntSize* aRetVal)
{
ImageAccessible* acc = IdToImageAccessible(aID);
if (acc) {
*aRetVal = acc->Size();
}
return true;
}
}
}

View File

@ -15,6 +15,7 @@ namespace mozilla {
namespace a11y {
class Accessible;
class HyperTextAccessible;
class ImageAccessible;
class AccShowEvent;
@ -34,9 +35,6 @@ public:
MOZ_COUNT_DTOR(DocAccessibleChild);
}
Accessible* IdToAccessible(const uint64_t& aID);
HyperTextAccessible* IdToHyperTextAccessible(const uint64_t& aID);
void ShowEvent(AccShowEvent* aShowEvent);
/*
@ -184,7 +182,19 @@ public:
virtual bool RecvPasteText(const uint64_t& aID,
const int32_t& aPosition) override;
virtual bool RecvImagePosition(const uint64_t& aID,
const uint32_t& aCoordType,
nsIntPoint* aRetVal) override;
virtual bool RecvImageSize(const uint64_t& aID,
nsIntSize* aRetVal) override;
private:
Accessible* IdToAccessible(const uint64_t& aID) const;
HyperTextAccessible* IdToHyperTextAccessible(const uint64_t& aID) const;
ImageAccessible* IdToImageAccessible(const uint64_t& aID) const;
bool PersistentPropertiesToArray(nsIPersistentProperties* aProps,
nsTArray<Attribute>* aAttributes);

View File

@ -8,6 +8,8 @@ include protocol PContent;
include "mozilla/GfxMessageUtils.h";
using struct nsIntPoint from "nsRect.h";
using struct nsIntSize from "nsRect.h";
using struct nsIntRect from "nsRect.h";
namespace mozilla {
@ -121,6 +123,9 @@ child:
prio(high) sync CutText(uint64_t aID, int32_t aStartPos, int32_t aEndPos);
prio(high) sync DeleteText(uint64_t aID, int32_t aStartPos, int32_t aEndPos);
prio(high) sync PasteText(uint64_t aID, int32_t aPosition);
prio(high) sync ImagePosition(uint64_t aID, uint32_t aCoordType) returns(nsIntPoint aRetVal);
prio(high) sync ImageSize(uint64_t aID) returns(nsIntSize aRetVal);
};
}

View File

@ -363,5 +363,21 @@ ProxyAccessible::PasteText(int32_t aPosition)
unused << mDoc->SendPasteText(mID, aPosition);
}
nsIntPoint
ProxyAccessible::ImagePosition(uint32_t aCoordType)
{
nsIntPoint retVal;
unused << mDoc->SendImagePosition(mID, aCoordType, &retVal);
return retVal;
}
nsIntSize
ProxyAccessible::ImageSize()
{
nsIntSize retVal;
unused << mDoc->SendImageSize(mID, &retVal);
return retVal;
}
}
}

View File

@ -177,6 +177,10 @@ public:
void PasteText(int32_t aPosition);
nsIntPoint ImagePosition(uint32_t aCoordType);
nsIntSize ImageSize();
/**
* Allow the platform to store a pointers worth of data on us.
*/

View File

@ -26,6 +26,24 @@ if CONFIG['ACCESSIBILITY']:
'../generic',
]
if CONFIG['MOZ_ENABLE_GTK']:
LOCAL_INCLUDES += [
'/accessible/atk',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
LOCAL_INCLUDES += [
'/accessible/windows/ia2',
'/accessible/windows/msaa',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
LOCAL_INCLUDES += [
'/accessible/mac',
]
else:
LOCAL_INCLUDES += [
'/accessible/other',
]
FINAL_LIBRARY = 'xul'
include('/ipc/chromium/chromium-config.mozbuild')

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>

View File

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "9b6f3024e4d0e62dd057231f4b14abe1782932ab",
"git_revision": "8eac260ee81a8aca05770d18c5736536d44ee7a7",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "4c2751f5fc24ffad381aae2c50b160cba0f33d36",
"revision": "1400d176ecef76d06b012fb082c246eb17d1d30f",
"repo_path": "integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="9b6f3024e4d0e62dd057231f4b14abe1782932ab"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -315,8 +315,10 @@ function assert_records(expected, desc) {
assert_records([{ added: [firstPlayer], changed: [], removed: [] }],
"records after transition start");
// Wait a bit longer for the transition to take effect.
// Wait for the AnimationPlayer to get going, then seek well into
// the transition.
yield await_frame();
firstPlayer.currentTime = 50000;
// Reverse the transition by setting the background-color back to its
// original value.
@ -328,6 +330,9 @@ function assert_records(expected, desc) {
var secondPlayer = players[0];
ok(firstPlayer != secondPlayer,
"second AnimationPlayer should be different from the first");
// Wait for the single MutationRecord for the removal of the original
// AnimationPlayer and the addition of the new AnimationPlayer to
// be delivered.
@ -365,8 +370,10 @@ function assert_records(expected, desc) {
assert_records([{ added: players, changed: [], removed: [] }],
"records after transition starts");
// Wait for the AnimationPlayers to get going.
// Wait for the AnimationPlayers to get going, then seek well into
// the transitions.
yield await_frame();
players.forEach(p => p.currentTime = 50000);
is(players.filter(p => p.playState == "running").length, 3, "number of running AnimationPlayers");

View File

@ -7,6 +7,7 @@
#include "mozilla/dom/cache/AutoUtils.h"
#include "mozilla/unused.h"
#include "mozilla/dom/cache/CachePushStreamChild.h"
#include "mozilla/dom/cache/CacheStreamControlParent.h"
#include "mozilla/dom/cache/ReadStream.h"
#include "mozilla/dom/cache/SavedTypes.h"
@ -19,6 +20,7 @@
namespace {
using mozilla::unused;
using mozilla::dom::cache::CachePushStreamChild;
using mozilla::dom::cache::PCacheReadStream;
using mozilla::dom::cache::PCacheReadStreamOrVoid;
using mozilla::ipc::FileDescriptor;
@ -28,8 +30,8 @@ using mozilla::ipc::OptionalFileDescriptorSet;
enum CleanupAction
{
ForgetFds,
DeleteFds
Forget,
Delete
};
void
@ -46,7 +48,7 @@ CleanupChildFds(PCacheReadStream& aReadStream, CleanupAction aAction)
static_cast<FileDescriptorSetChild*>(aReadStream.fds().get_PFileDescriptorSetChild());
MOZ_ASSERT(fdSetActor);
if (aAction == DeleteFds) {
if (aAction == Delete) {
unused << fdSetActor->Send__delete__(fdSetActor);
}
@ -57,13 +59,39 @@ CleanupChildFds(PCacheReadStream& aReadStream, CleanupAction aAction)
}
void
CleanupChildFds(PCacheReadStreamOrVoid& aReadStreamOrVoid, CleanupAction aAction)
CleanupChildPushStream(PCacheReadStream& aReadStream, CleanupAction aAction)
{
if (!aReadStream.pushStreamChild()) {
return;
}
auto pushStream =
static_cast<CachePushStreamChild*>(aReadStream.pushStreamChild());
if (aAction == Delete) {
pushStream->StartDestroy();
return;
}
// If we send the stream, then we need to start it before forgetting about it.
pushStream->Start();
}
void
CleanupChild(PCacheReadStream& aReadStream, CleanupAction aAction)
{
CleanupChildFds(aReadStream, aAction);
CleanupChildPushStream(aReadStream, aAction);
}
void
CleanupChild(PCacheReadStreamOrVoid& aReadStreamOrVoid, CleanupAction aAction)
{
if (aReadStreamOrVoid.type() == PCacheReadStreamOrVoid::Tvoid_t) {
return;
}
CleanupChildFds(aReadStreamOrVoid.get_PCacheReadStream(), aAction);
CleanupChild(aReadStreamOrVoid.get_PCacheReadStream(), aAction);
}
void
@ -80,7 +108,7 @@ CleanupParentFds(PCacheReadStream& aReadStream, CleanupAction aAction)
static_cast<FileDescriptorSetParent*>(aReadStream.fds().get_PFileDescriptorSetParent());
MOZ_ASSERT(fdSetActor);
if (aAction == DeleteFds) {
if (aAction == Delete) {
unused << fdSetActor->Send__delete__(fdSetActor);
}
@ -133,8 +161,8 @@ AutoChildRequest::~AutoChildRequest()
return;
}
CleanupAction action = mSent ? ForgetFds : DeleteFds;
CleanupChildFds(mRequestOrVoid.get_PCacheRequest().body(), action);
CleanupAction action = mSent ? Forget : Delete;
CleanupChild(mRequestOrVoid.get_PCacheRequest().body(), action);
}
void
@ -173,9 +201,9 @@ AutoChildRequestList::AutoChildRequestList(TypeUtils* aTypeUtils,
AutoChildRequestList::~AutoChildRequestList()
{
CleanupAction action = mSent ? ForgetFds : DeleteFds;
CleanupAction action = mSent ? Forget : Delete;
for (uint32_t i = 0; i < mRequestList.Length(); ++i) {
CleanupChildFds(mRequestList[i].body(), action);
CleanupChild(mRequestList[i].body(), action);
}
}
@ -223,9 +251,9 @@ AutoChildRequestResponse::AutoChildRequestResponse(TypeUtils* aTypeUtils)
AutoChildRequestResponse::~AutoChildRequestResponse()
{
CleanupAction action = mSent ? ForgetFds : DeleteFds;
CleanupChildFds(mRequestResponse.request().body(), action);
CleanupChildFds(mRequestResponse.response().body(), action);
CleanupAction action = mSent ? Forget : Delete;
CleanupChild(mRequestResponse.request().body(), action);
CleanupChild(mRequestResponse.response().body(), action);
}
void
@ -311,7 +339,7 @@ AutoParentRequestList::AutoParentRequestList(PBackgroundParent* aManager,
AutoParentRequestList::~AutoParentRequestList()
{
CleanupAction action = mSent ? ForgetFds : DeleteFds;
CleanupAction action = mSent ? Forget : Delete;
for (uint32_t i = 0; i < mRequestList.Length(); ++i) {
CleanupParentFds(mRequestList[i].body(), action);
}
@ -355,7 +383,7 @@ AutoParentResponseList::AutoParentResponseList(PBackgroundParent* aManager,
AutoParentResponseList::~AutoParentResponseList()
{
CleanupAction action = mSent ? ForgetFds : DeleteFds;
CleanupAction action = mSent ? Forget : Delete;
for (uint32_t i = 0; i < mResponseList.Length(); ++i) {
CleanupParentFds(mResponseList[i].body(), action);
}
@ -402,7 +430,7 @@ AutoParentResponseOrVoid::~AutoParentResponseOrVoid()
return;
}
CleanupAction action = mSent ? ForgetFds : DeleteFds;
CleanupAction action = mSent ? Forget : Delete;
CleanupParentFds(mResponseOrVoid.get_PCacheResponse().body(), action);
}

13
dom/cache/Cache.cpp vendored
View File

@ -14,6 +14,7 @@
#include "mozilla/dom/CacheBinding.h"
#include "mozilla/dom/cache/AutoUtils.h"
#include "mozilla/dom/cache/CacheChild.h"
#include "mozilla/dom/cache/CachePushStreamChild.h"
#include "mozilla/dom/cache/ReadStream.h"
#include "mozilla/dom/cache/TypeUtils.h"
#include "mozilla/ErrorResult.h"
@ -526,6 +527,18 @@ Cache::AssertOwningThread() const
}
#endif
CachePushStreamChild*
Cache::CreatePushStream(nsIAsyncInputStream* aStream)
{
NS_ASSERT_OWNINGTHREAD(Cache);
MOZ_ASSERT(mActor);
MOZ_ASSERT(aStream);
auto actor = mActor->SendPCachePushStreamConstructor(
new CachePushStreamChild(mActor->GetFeature(), aStream));
MOZ_ASSERT(actor);
return static_cast<CachePushStreamChild*>(actor);
}
void
Cache::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
{

7
dom/cache/Cache.h vendored
View File

@ -39,8 +39,8 @@ class PCacheResponse;
class PCacheResponseOrVoid;
class Cache final : public PromiseNativeHandler
, public nsWrapperCache
, public TypeUtils
, public nsWrapperCache
, public TypeUtils
{
public:
Cache(nsIGlobalObject* aGlobal, CacheChild* aActor);
@ -97,6 +97,9 @@ public:
virtual void AssertOwningThread() const override;
#endif
virtual CachePushStreamChild*
CreatePushStream(nsIAsyncInputStream* aStream) override;
// PromiseNativeHandler methods
virtual void
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;

View File

@ -9,6 +9,7 @@
#include "mozilla/unused.h"
#include "mozilla/dom/cache/ActorUtils.h"
#include "mozilla/dom/cache/Cache.h"
#include "mozilla/dom/cache/PCachePushStreamChild.h"
#include "mozilla/dom/cache/StreamUtils.h"
namespace mozilla {
@ -94,6 +95,20 @@ CacheChild::ActorDestroy(ActorDestroyReason aReason)
RemoveFeature();
}
PCachePushStreamChild*
CacheChild::AllocPCachePushStreamChild()
{
MOZ_CRASH("CachePushStreamChild should be manually constructed.");
return nullptr;
}
bool
CacheChild::DeallocPCachePushStreamChild(PCachePushStreamChild* aActor)
{
delete aActor;
return true;
}
bool
CacheChild::RecvMatchResponse(const RequestId& requestId, const nsresult& aRv,
const PCacheResponseOrVoid& aResponse)

View File

@ -17,7 +17,7 @@ namespace cache {
class Cache;
class CacheChild final : public PCacheChild
, public ActorChild
, public ActorChild
{
public:
CacheChild();
@ -41,6 +41,12 @@ private:
virtual void
ActorDestroy(ActorDestroyReason aReason) override;
virtual PCachePushStreamChild*
AllocPCachePushStreamChild() override;
virtual bool
DeallocPCachePushStreamChild(PCachePushStreamChild* aActor) override;
virtual bool
RecvMatchResponse(const RequestId& requestId, const nsresult& aRv,
const PCacheResponseOrVoid& aResponse) override;

View File

@ -8,6 +8,7 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/dom/cache/AutoUtils.h"
#include "mozilla/dom/cache/CachePushStreamParent.h"
#include "mozilla/dom/cache/CacheStreamControlParent.h"
#include "mozilla/dom/cache/ReadStream.h"
#include "mozilla/dom/cache/SavedTypes.h"
@ -61,6 +62,19 @@ CacheParent::ActorDestroy(ActorDestroyReason aReason)
mManager = nullptr;
}
PCachePushStreamParent*
CacheParent::AllocPCachePushStreamParent()
{
return CachePushStreamParent::Create();
}
bool
CacheParent::DeallocPCachePushStreamParent(PCachePushStreamParent* aActor)
{
delete aActor;
return true;
}
bool
CacheParent::RecvTeardown()
{
@ -259,13 +273,27 @@ CacheParent::DeserializeCacheStream(const PCacheReadStreamOrVoid& aStreamOrVoid)
return nullptr;
}
nsCOMPtr<nsIInputStream> stream;
const PCacheReadStream& readStream = aStreamOrVoid.get_PCacheReadStream();
nsCOMPtr<nsIInputStream> stream = ReadStream::Create(readStream);
// Option 1: A push stream actor was sent for nsPipe data
if (readStream.pushStreamParent()) {
MOZ_ASSERT(!readStream.controlParent());
CachePushStreamParent* pushStream =
static_cast<CachePushStreamParent*>(readStream.pushStreamParent());
stream = pushStream->TakeReader();
MOZ_ASSERT(stream);
return stream.forget();
}
// Option 2: One of our own ReadStreams was passed back to us with a stream
// control actor.
stream = ReadStream::Create(readStream);
if (stream) {
return stream.forget();
}
// Option 3: A stream was serialized using normal methods.
nsAutoTArray<FileDescriptor, 4> fds;
if (readStream.fds().type() ==
OptionalFileDescriptorSet::TPFileDescriptorSetChild) {

View File

@ -22,8 +22,8 @@ namespace cache {
struct SavedResponse;
class CacheParent final : public PCacheParent
, public Manager::Listener
, public FetchPut::Listener
, public Manager::Listener
, public FetchPut::Listener
{
public:
CacheParent(cache::Manager* aManager, CacheId aCacheId);
@ -32,6 +32,8 @@ public:
private:
// PCacheParent method
virtual void ActorDestroy(ActorDestroyReason aReason) override;
virtual PCachePushStreamParent* AllocPCachePushStreamParent() override;
virtual bool DeallocPCachePushStreamParent(PCachePushStreamParent* aActor) override;
virtual bool RecvTeardown() override;
virtual bool
RecvMatch(const RequestId& aRequestId, const PCacheRequest& aRequest,

259
dom/cache/CachePushStreamChild.cpp vendored Normal file
View File

@ -0,0 +1,259 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/cache/CachePushStreamChild.h"
#include "mozilla/unused.h"
#include "nsIAsyncInputStream.h"
#include "nsICancelableRunnable.h"
#include "nsIThread.h"
#include "nsStreamUtils.h"
namespace mozilla {
namespace dom {
namespace cache {
class CachePushStreamChild::Callback final : public nsIInputStreamCallback
, public nsICancelableRunnable
{
public:
explicit Callback(CachePushStreamChild* aActor)
: mActor(aActor)
, mOwningThread(NS_GetCurrentThread())
{
MOZ_ASSERT(mActor);
}
NS_IMETHOD
OnInputStreamReady(nsIAsyncInputStream* aStream) override
{
// any thread
if (mOwningThread == NS_GetCurrentThread()) {
return Run();
}
// If this fails, then it means the owning thread is a Worker that has
// been shutdown. Its ok to lose the event in this case because the
// CachePushStreamChild listens for this event through the Feature.
nsresult rv = mOwningThread->Dispatch(this, nsIThread::DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to dispatch stream readable event to owning thread");
}
return NS_OK;
}
NS_IMETHOD
Run() override
{
MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
if (mActor) {
mActor->OnStreamReady(this);
}
return NS_OK;
}
NS_IMETHOD
Cancel() override
{
// Cancel() gets called when the Worker thread is being shutdown. We have
// nothing to do here because CachePushStreamChild handles this case via
// the Feature.
return NS_OK;
}
void
ClearActor()
{
MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
MOZ_ASSERT(mActor);
mActor = nullptr;
}
private:
~Callback()
{
// called on any thread
// ClearActor() should be called before the Callback is destroyed
MOZ_ASSERT(!mActor);
}
CachePushStreamChild* mActor;
nsCOMPtr<nsIThread> mOwningThread;
NS_DECL_THREADSAFE_ISUPPORTS
};
NS_IMPL_ISUPPORTS(CachePushStreamChild::Callback, nsIInputStreamCallback,
nsIRunnable,
nsICancelableRunnable);
CachePushStreamChild::CachePushStreamChild(Feature* aFeature,
nsIAsyncInputStream* aStream)
: mStream(aStream)
, mClosed(false)
{
MOZ_ASSERT(mStream);
MOZ_ASSERT_IF(!NS_IsMainThread(), aFeature);
SetFeature(aFeature);
}
CachePushStreamChild::~CachePushStreamChild()
{
NS_ASSERT_OWNINGTHREAD(CachePushStreamChild);
MOZ_ASSERT(mClosed);
MOZ_ASSERT(!mCallback);
}
void
CachePushStreamChild::Start()
{
DoRead();
}
void
CachePushStreamChild::StartDestroy()
{
// called if we are running on a Worker and the thread gets shutdown
OnEnd(NS_ERROR_ABORT);
}
void
CachePushStreamChild::ActorDestroy(ActorDestroyReason aReason)
{
NS_ASSERT_OWNINGTHREAD(CachePushStreamChild);
// If the parent side runs into a problem then the actor will be destroyed.
// In this case we have not run OnEnd(), so still need to close the input
// stream.
if (!mClosed) {
mStream->CloseWithStatus(NS_ERROR_ABORT);
mClosed = true;
}
if (mCallback) {
mCallback->ClearActor();
mCallback = nullptr;
}
RemoveFeature();
}
void
CachePushStreamChild::DoRead()
{
NS_ASSERT_OWNINGTHREAD(CachePushStreamChild);
MOZ_ASSERT(!mClosed);
MOZ_ASSERT(!mCallback);
// The input stream (likely a pipe) probably uses a segment size of
// 4kb. If there is data already buffered it would be nice to aggregate
// multiple segments into a single IPC call. Conversely, don't send too
// too large of a buffer in a single call to avoid spiking memory.
static const uint64_t kMaxBytesPerMessage = 32 * 1024;
static_assert(kMaxBytesPerMessage <= static_cast<uint64_t>(UINT32_MAX),
"kMaxBytesPerMessage must cleanly cast to uint32_t");
while (!mClosed) {
// Use non-auto here as we're unlikely to hit stack storage with the
// sizes we are sending. Also, it would be nice to avoid another copy
// to the IPC layer which we avoid if we use COW strings. Unfortunately
// IPC does not seem to support passing dependent storage types.
nsCString buffer;
uint64_t available = 0;
nsresult rv = mStream->Available(&available);
if (NS_FAILED(rv)) {
OnEnd(rv);
return;
}
if (available == 0) {
Wait();
return;
}
uint32_t expectedBytes =
static_cast<uint32_t>(std::min(available, kMaxBytesPerMessage));
buffer.SetLength(expectedBytes);
uint32_t bytesRead = 0;
rv = mStream->Read(buffer.BeginWriting(), buffer.Length(), &bytesRead);
buffer.SetLength(bytesRead);
// If we read any data from the stream, send it across.
if (!buffer.IsEmpty()) {
unused << SendBuffer(buffer);
}
if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
Wait();
return;
}
// Any other error or zero-byte read indicates end-of-stream
if (NS_FAILED(rv) || buffer.IsEmpty()) {
OnEnd(rv);
return;
}
}
}
void
CachePushStreamChild::Wait()
{
NS_ASSERT_OWNINGTHREAD(CachePushStreamChild);
MOZ_ASSERT(!mClosed);
MOZ_ASSERT(!mCallback);
// Set mCallback immediately instead of waiting for success. Its possible
// AsyncWait() will callback synchronously.
mCallback = new Callback(this);
nsresult rv = mStream->AsyncWait(mCallback, 0, 0, nullptr);
if (NS_FAILED(rv)) {
OnEnd(rv);
return;
}
}
void
CachePushStreamChild::OnStreamReady(Callback* aCallback)
{
NS_ASSERT_OWNINGTHREAD(CachePushStreamChild);
MOZ_ASSERT(mCallback);
MOZ_ASSERT(aCallback == mCallback);
mCallback->ClearActor();
mCallback = nullptr;
DoRead();
}
void
CachePushStreamChild::OnEnd(nsresult aRv)
{
NS_ASSERT_OWNINGTHREAD(CachePushStreamChild);
MOZ_ASSERT(aRv != NS_BASE_STREAM_WOULD_BLOCK);
if (mClosed) {
return;
}
mClosed = true;
mStream->CloseWithStatus(aRv);
if (aRv == NS_BASE_STREAM_CLOSED) {
aRv = NS_OK;
}
// This will trigger an ActorDestroy() from the parent side
unused << SendClose(aRv);
}
} // namespace cache
} // namespace dom
} // namespace mozilla

57
dom/cache/CachePushStreamChild.h vendored Normal file
View File

@ -0,0 +1,57 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_cache_CachePushStreamChild_h
#define mozilla_dom_cache_CachePushStreamChild_h
#include "mozilla/dom/cache/ActorChild.h"
#include "mozilla/dom/cache/PCachePushStreamChild.h"
#include "nsCOMPtr.h"
class nsIAsyncInputStream;
namespace mozilla {
namespace dom {
namespace cache {
class CachePushStreamChild final : public PCachePushStreamChild
, public ActorChild
{
public:
CachePushStreamChild(Feature* aFeature, nsIAsyncInputStream* aStream);
~CachePushStreamChild();
virtual void StartDestroy() override;
void Start();
private:
class Callback;
// PCachePushStreamChild methods
virtual void
ActorDestroy(ActorDestroyReason aReason) override;
void DoRead();
void Wait();
void OnStreamReady(Callback* aCallback);
void OnEnd(nsresult aRv);
nsCOMPtr<nsIAsyncInputStream> mStream;
nsRefPtr<Callback> mCallback;
bool mClosed;
NS_DECL_OWNINGTHREAD
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_CachePushStreamChild_h

97
dom/cache/CachePushStreamParent.cpp vendored Normal file
View File

@ -0,0 +1,97 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/cache/CachePushStreamParent.h"
#include "mozilla/unused.h"
#include "nsIAsyncInputStream.h"
#include "nsIAsyncOutputStream.h"
#include "nsIPipe.h"
namespace mozilla {
namespace dom {
namespace cache {
// static
CachePushStreamParent*
CachePushStreamParent::Create()
{
// use async versions for both reader and writer even though we are
// opening the writer as an infinite stream. We want to be able to
// use CloseWithStatus() to communicate errors through the pipe.
nsCOMPtr<nsIAsyncInputStream> reader;
nsCOMPtr<nsIAsyncOutputStream> writer;
// Use an "infinite" pipe because we cannot apply back-pressure through
// the async IPC layer at the moment. Blocking the IPC worker thread
// is not desirable, either.
nsresult rv = NS_NewPipe2(getter_AddRefs(reader),
getter_AddRefs(writer),
true, true, // non-blocking
0, // segment size
UINT32_MAX); // "infinite" pipe
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
return new CachePushStreamParent(reader, writer);
}
CachePushStreamParent::~CachePushStreamParent()
{
}
already_AddRefed<nsIInputStream>
CachePushStreamParent::TakeReader()
{
MOZ_ASSERT(mReader);
return mReader.forget();
}
void
CachePushStreamParent::ActorDestroy(ActorDestroyReason aReason)
{
// If we were gracefully closed we should have gotten RecvClose(). In
// that case, the writer will already be closed and this will have no
// effect. This just aborts the writer in the case where the child process
// crashes.
mWriter->CloseWithStatus(NS_ERROR_ABORT);
}
bool
CachePushStreamParent::RecvBuffer(const nsCString& aBuffer)
{
uint32_t numWritten = 0;
// This should only fail if we hit an OOM condition.
nsresult rv = mWriter->Write(aBuffer.get(), aBuffer.Length(), &numWritten);
if (NS_WARN_IF(NS_FAILED(rv))) {
RecvClose(rv);
}
return true;
}
bool
CachePushStreamParent::RecvClose(const nsresult& aRv)
{
mWriter->CloseWithStatus(aRv);
unused << Send__delete__(this);
return true;
}
CachePushStreamParent::CachePushStreamParent(nsIAsyncInputStream* aReader,
nsIAsyncOutputStream* aWriter)
: mReader(aReader)
, mWriter(aWriter)
{
MOZ_ASSERT(mReader);
MOZ_ASSERT(mWriter);
}
} // namespace cache
} // namespace dom
} // namespace mozilla

55
dom/cache/CachePushStreamParent.h vendored Normal file
View File

@ -0,0 +1,55 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_cache_CachePushStreamParent_h
#define mozilla_dom_cache_CachePushStreamParent_h
#include "mozilla/dom/cache/PCachePushStreamParent.h"
class nsIAsyncInputStream;
class nsIAsyncOutputStream;
class nsIInputStream;
namespace mozilla {
namespace dom {
namespace cache {
class CachePushStreamParent final : public PCachePushStreamParent
{
public:
static CachePushStreamParent*
Create();
~CachePushStreamParent();
already_AddRefed<nsIInputStream>
TakeReader();
private:
CachePushStreamParent(nsIAsyncInputStream* aReader,
nsIAsyncOutputStream* aWriter);
// PCachePushStreamParent methods
virtual void
ActorDestroy(ActorDestroyReason aReason) override;
virtual bool
RecvBuffer(const nsCString& aBuffer) override;
virtual bool
RecvClose(const nsresult& aRv) override;
nsCOMPtr<nsIAsyncInputStream> mReader;
nsCOMPtr<nsIAsyncOutputStream> mWriter;
NS_DECL_OWNINGTHREAD
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_CachePushStreamParent_h

View File

@ -516,6 +516,13 @@ CacheStorage::AssertOwningThread() const
}
#endif
CachePushStreamChild*
CacheStorage::CreatePushStream(nsIAsyncInputStream* aStream)
{
// This is true because CacheStorage always uses IgnoreBody for requests.
MOZ_CRASH("CacheStorage should never create a push stream.");
}
void
CacheStorage::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
{

View File

@ -44,9 +44,9 @@ class Feature;
class PCacheResponseOrVoid;
class CacheStorage final : public nsIIPCBackgroundChildCreateCallback
, public nsWrapperCache
, public TypeUtils
, public PromiseNativeHandler
, public nsWrapperCache
, public TypeUtils
, public PromiseNativeHandler
{
typedef mozilla::ipc::PBackgroundChild PBackgroundChild;
@ -97,6 +97,9 @@ public:
virtual void AssertOwningThread() const override;
#endif
virtual CachePushStreamChild*
CreatePushStream(nsIAsyncInputStream* aStream) override;
// PromiseNativeHandler methods
virtual void
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;

View File

@ -20,7 +20,7 @@ class PCacheChild;
class Feature;
class CacheStorageChild final : public PCacheStorageChild
, public ActorChild
, public ActorChild
{
public:
CacheStorageChild(CacheStorage* aListener, Feature* aFeature);
@ -41,6 +41,7 @@ public:
private:
// PCacheStorageChild methods
virtual void ActorDestroy(ActorDestroyReason aReason) override;
virtual bool RecvMatchResponse(const RequestId& aRequestId,
const nsresult& aRv,
const PCacheResponseOrVoid& response) override;

View File

@ -23,8 +23,8 @@ class CacheStreamControlParent;
class ManagerId;
class CacheStorageParent final : public PCacheStorageParent
, public PrincipalVerifier::Listener
, public Manager::Listener
, public PrincipalVerifier::Listener
, public Manager::Listener
{
public:
CacheStorageParent(PBackgroundParent* aManagingActor, Namespace aNamespace,

View File

@ -19,8 +19,8 @@ namespace cache {
class ReadStream;
class CacheStreamControlChild final : public PCacheStreamControlChild
, public StreamControl
, public ActorChild
, public StreamControl
, public ActorChild
{
public:
CacheStreamControlChild();

View File

@ -18,8 +18,8 @@ namespace cache {
class ReadStream;
class StreamList;
class CacheStreamControlParent : public PCacheStreamControlParent
, public StreamControl
class CacheStreamControlParent final : public PCacheStreamControlParent
, public StreamControl
{
public:
CacheStreamControlParent();

View File

@ -320,7 +320,7 @@ Context::QuotaInitRunnable::Run()
// runnable executes the Action on the appropriate threads while the Context
// is initialized.
class Context::ActionRunnable final : public nsIRunnable
, public Action::Resolver
, public Action::Resolver
{
public:
ActionRunnable(Context* aContext, nsIEventTarget* aTarget, Action* aAction,

View File

@ -458,6 +458,12 @@ FetchPut::AssertOwningThread() const
}
#endif
CachePushStreamChild*
FetchPut::CreatePushStream(nsIAsyncInputStream* aStream)
{
MOZ_CRASH("FetchPut should never create a push stream!");
}
} // namespace cache
} // namespace dom
} // namespace mozilla

View File

@ -30,7 +30,7 @@ class Response;
namespace cache {
class FetchPut final : public Manager::Listener
, public TypeUtils
, public TypeUtils
{
public:
typedef std::pair<nsRefPtr<Request>, nsRefPtr<Response>> PutPair;
@ -94,6 +94,9 @@ private:
virtual void AssertOwningThread() const override;
#endif
virtual CachePushStreamChild*
CreatePushStream(nsIAsyncInputStream* aStream) override;
Listener* mListener;
nsRefPtr<Manager> mManager;
const RequestId mRequestId;

View File

@ -3,6 +3,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PBackground;
include protocol PCachePushStream;
include PCacheTypes;
include protocol PFileDescriptorSet;
@ -19,8 +20,10 @@ namespace cache {
protocol PCache
{
manager PBackground;
manages PCachePushStream;
parent:
PCachePushStream();
Teardown();
Match(RequestId requestId, PCacheRequest request, PCacheQueryParams params);
MatchAll(RequestId requestId, PCacheRequestOrVoid request, PCacheQueryParams params);

28
dom/cache/PCachePushStream.ipdl vendored Normal file
View File

@ -0,0 +1,28 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PCache;
namespace mozilla {
namespace dom {
namespace cache {
protocol PCachePushStream
{
manager PCache;
parent:
Buffer(nsCString aBuffer);
Close(nsresult aRv);
child:
// Stream is always destroyed from the parent side. This occurs if the
// parent encounters an error while writing to its pipe or if the child
// signals the stream should close by SendClose().
__delete__();
};
} // namespace cache
} // namespace dom
} // namespace mozilla

View File

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PCachePushStream;
include protocol PCacheStreamControl;
include PHeaders;
include InputStreamParams;
@ -32,6 +33,7 @@ struct PCacheReadStream
OptionalInputStreamParams params;
OptionalFileDescriptorSet fds;
nullable PCacheStreamControl control;
nullable PCachePushStream pushStream;
};
union PCacheReadStreamOrVoid

View File

@ -210,6 +210,11 @@ ReadStream::Inner::Serialize(PCacheReadStream* aReadStreamOut)
MOZ_ASSERT(mState == Open);
MOZ_ASSERT(mControl);
// If we are sending a ReadStream, then we never want to set the
// pushStream actors at the same time.
aReadStreamOut->pushStreamChild() = nullptr;
aReadStreamOut->pushStreamParent() = nullptr;
aReadStreamOut->id() = mId;
mControl->SerializeControl(aReadStreamOut);
@ -406,6 +411,9 @@ ReadStream::Create(const PCacheReadStream& aReadStream)
return nullptr;
}
MOZ_ASSERT(!aReadStream.pushStreamChild());
MOZ_ASSERT(!aReadStream.pushStreamParent());
// Control is guaranteed to survive this method as ActorDestroy() cannot
// run on this thread until we complete.
StreamControl* control;

View File

@ -21,7 +21,7 @@ class CacheStreamControlParent;
class Context;
class Manager;
class StreamList
class StreamList final
{
public:
StreamList(Manager* aManager, Context* aContext);

View File

@ -11,6 +11,7 @@
#include "mozilla/dom/InternalRequest.h"
#include "mozilla/dom/Request.h"
#include "mozilla/dom/Response.h"
#include "mozilla/dom/cache/CachePushStreamChild.h"
#include "mozilla/dom/cache/PCacheTypes.h"
#include "mozilla/dom/cache/ReadStream.h"
#include "mozilla/ipc/BackgroundChild.h"
@ -29,6 +30,13 @@
namespace {
using mozilla::ErrorResult;
using mozilla::unused;
using mozilla::void_t;
using mozilla::dom::cache::PCacheReadStream;
using mozilla::ipc::BackgroundChild;
using mozilla::ipc::FileDescriptor;
using mozilla::ipc::PBackgroundChild;
using mozilla::ipc::PFileDescriptorSetChild;
// Utility function to remove the fragment from a URL, check its scheme, and optionally
// provide a URL without the query. We're not using nsIURL or URL to do this because
@ -96,6 +104,31 @@ ProcessURL(nsAString& aUrl, bool* aSchemeValidOut,
*aUrlWithoutQueryOut = Substring(aUrl, 0, queryPos - 1);
}
void
SerializeNormalStream(nsIInputStream* aStream, PCacheReadStream& aReadStreamOut)
{
nsAutoTArray<FileDescriptor, 4> fds;
SerializeInputStream(aStream, aReadStreamOut.params(), fds);
PFileDescriptorSetChild* fdSet = nullptr;
if (!fds.IsEmpty()) {
// We should not be serializing until we have an actor ready
PBackgroundChild* manager = BackgroundChild::GetForCurrentThread();
MOZ_ASSERT(manager);
fdSet = manager->SendPFileDescriptorSetConstructor(fds[0]);
for (uint32_t i = 1; i < fds.Length(); ++i) {
unused << fdSet->SendAddFileDescriptor(fds[i]);
}
}
if (fdSet) {
aReadStreamOut.fds() = fdSet;
} else {
aReadStreamOut.fds() = void_t();
}
}
} // anonymous namespace
namespace mozilla {
@ -413,64 +446,61 @@ TypeUtils::SerializeCacheStream(nsIInputStream* aStream,
return;
}
// Option 1: Send a cache-specific ReadStream if we can.
nsRefPtr<ReadStream> controlled = do_QueryObject(aStream);
if (controlled) {
controlled->Serialize(aStreamOut);
return;
}
// TODO: implement CrossProcessPipe if we cannot directly serialize (bug 1110814)
nsCOMPtr<nsIIPCSerializableInputStream> serial = do_QueryInterface(aStream);
if (!serial) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
PCacheReadStream readStream;
readStream.controlChild() = nullptr;
readStream.controlParent() = nullptr;
readStream.pushStreamChild() = nullptr;
readStream.pushStreamParent() = nullptr;
nsAutoTArray<FileDescriptor, 4> fds;
SerializeInputStream(aStream, readStream.params(), fds);
// Option 2: Do normal stream serialization if its supported.
nsCOMPtr<nsIIPCSerializableInputStream> serial = do_QueryInterface(aStream);
if (serial) {
SerializeNormalStream(aStream, readStream);
PFileDescriptorSetChild* fdSet = nullptr;
if (!fds.IsEmpty()) {
// We should not be serializing until we have an actor ready
PBackgroundChild* manager = BackgroundChild::GetForCurrentThread();
MOZ_ASSERT(manager);
fdSet = manager->SendPFileDescriptorSetConstructor(fds[0]);
for (uint32_t i = 1; i < fds.Length(); ++i) {
unused << fdSet->SendAddFileDescriptor(fds[i]);
}
}
if (fdSet) {
readStream.fds() = fdSet;
// Option 3: As a last resort push data across manually. Should only be
// needed for nsPipe input stream. Only works for async,
// non-blocking streams.
} else {
readStream.fds() = void_t();
SerializePushStream(aStream, readStream, aRv);
if (NS_WARN_IF(aRv.Failed())) { return; }
}
*aStreamOut = readStream;
}
nsIThread*
TypeUtils::GetStreamThread()
void
TypeUtils::SerializePushStream(nsIInputStream* aStream,
PCacheReadStream& aReadStreamOut,
ErrorResult& aRv)
{
AssertOwningThread();
if (!mStreamThread) {
// Named threads only allow 16 bytes for their names. Try to make
// it meaningful...
// TODO: use a thread pool or singleton thread here (bug 1119864)
nsresult rv = NS_NewNamedThread("DOMCacheTypeU",
getter_AddRefs(mStreamThread));
if (NS_FAILED(rv) || !mStreamThread) {
MOZ_CRASH("Failed to create DOM Cache serialization thread.");
}
nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aStream);
if (NS_WARN_IF(!asyncStream)) {
aRv = NS_ERROR_FAILURE;
return;
}
return mStreamThread;
bool nonBlocking = false;
aRv = asyncStream->IsNonBlocking(&nonBlocking);
if (NS_WARN_IF(aRv.Failed())) { return; }
if (NS_WARN_IF(!nonBlocking)) {
aRv = NS_ERROR_FAILURE;
return;
}
aReadStreamOut.pushStreamChild() = CreatePushStream(asyncStream);
MOZ_ASSERT(aReadStreamOut.pushStreamChild());
aReadStreamOut.params() = void_t();
aReadStreamOut.fds() = void_t();
// CachePushStreamChild::Start() must be called after sending the stream
// across to the parent side.
}
} // namespace cache

13
dom/cache/TypeUtils.h vendored
View File

@ -9,10 +9,10 @@
#include "mozilla/Attributes.h"
#include "mozilla/dom/BindingUtils.h"
#include "nsCOMPtr.h"
#include "nsError.h"
class nsIGlobalObject;
class nsIAsyncInputStream;
class nsIInputStream;
namespace mozilla {
@ -28,7 +28,9 @@ class Response;
namespace cache {
class CachePushStreamChild;
class PCacheQueryParams;
class PCacheReadStream;
class PCacheReadStreamOrVoid;
class PCacheRequest;
class PCacheResponse;
@ -63,6 +65,9 @@ public:
inline void AssertOwningThread() const { }
#endif
virtual CachePushStreamChild*
CreatePushStream(nsIAsyncInputStream* aStream) = 0;
already_AddRefed<InternalRequest>
ToInternalRequest(const RequestOrUSVString& aIn, BodyAction aBodyAction,
ErrorResult& aRv);
@ -107,9 +112,9 @@ private:
SerializeCacheStream(nsIInputStream* aStream, PCacheReadStreamOrVoid* aStreamOut,
ErrorResult& aRv);
nsIThread* GetStreamThread();
nsCOMPtr<nsIThread> mStreamThread;
void
SerializePushStream(nsIInputStream* aStream, PCacheReadStream& aReadStreamOut,
ErrorResult& aRv);
};
} // namespace cache

5
dom/cache/moz.build vendored
View File

@ -12,6 +12,8 @@ EXPORTS.mozilla.dom.cache += [
'Cache.h',
'CacheChild.h',
'CacheParent.h',
'CachePushStreamChild.h',
'CachePushStreamParent.h',
'CacheStorage.h',
'CacheStorageChild.h',
'CacheStorageParent.h',
@ -44,6 +46,8 @@ UNIFIED_SOURCES += [
'Cache.cpp',
'CacheChild.cpp',
'CacheParent.cpp',
'CachePushStreamChild.cpp',
'CachePushStreamParent.cpp',
'CacheStorage.cpp',
'CacheStorageChild.cpp',
'CacheStorageParent.cpp',
@ -69,6 +73,7 @@ UNIFIED_SOURCES += [
IPDL_SOURCES += [
'CacheInitData.ipdlh',
'PCache.ipdl',
'PCachePushStream.ipdl',
'PCacheStorage.ipdl',
'PCacheStreamControl.ipdl',
'PCacheTypes.ipdlh',

View File

@ -16,6 +16,7 @@ support-files =
vary.sjs
test_caches.js
test_cache_keys.js
test_cache_put.js
[test_cache.html]
[test_cache_add.html]
@ -25,3 +26,4 @@ support-files =
[test_cache_match_vary.html]
[test_caches.html]
[test_cache_keys.html]
[test_cache_put.html]

View File

@ -0,0 +1,20 @@
<!-- Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ -->
<!DOCTYPE HTML>
<html>
<head>
<title>Validate Interfaces Exposed to Workers</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="driver.js"></script>
</head>
<body>
<iframe id="frame"></iframe>
<script class="testbody" type="text/javascript">
runTests("test_cache_put.js")
.then(function() {
SimpleTest.finish();
});
</script>
</body>
</html>

View File

@ -0,0 +1,26 @@
var url = 'test_cache.js';
var cache;
var fetchResponse;
Promise.all([fetch(url),
caches.open('putter' + context)]).then(function(results) {
fetchResponse = results[0];
cache = results[1];
return cache.put(url, fetchResponse.clone());
}).then(function(result) {
is(undefined, result, 'Successful put() should resolve undefined');
return cache.match(url);
}).then(function(response) {
ok(response, 'match() should find resppnse that was previously put()');
ok(response.url.endsWith(url), 'matched response should match original url');
return Promise.all([fetchResponse.text(),
response.text()]);
}).then(function(results) {
// suppress large assert spam unless its relevent
if (results[0] !== results[1]) {
is(results[0], results[1], 'stored response body should match original');
}
return caches.delete('putter' + context);
}).then(function(deleted) {
ok(deleted, "The cache should be deleted successfully");
testDone();
});

View File

@ -4218,6 +4218,13 @@ CanvasRenderingContext2D::DrawImage(const HTMLImageOrCanvasOrVideoElement& image
return;
}
#ifdef MOZ_EME
if (video->ContainsRestrictedContent()) {
error.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
#endif
uint16_t readyState;
if (NS_SUCCEEDED(video->GetReadyState(&readyState)) &&
readyState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA) {

View File

@ -671,14 +671,6 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest,
NS_WARNING("Failed to visit all headers.");
}
mResponse = BeginAndGetFilteredResponse(response);
nsCOMPtr<nsISupports> securityInfo;
rv = channel->GetSecurityInfo(getter_AddRefs(securityInfo));
if (securityInfo) {
mResponse->SetSecurityInfo(securityInfo);
}
// We open a pipe so that we can immediately set the pipe's read end as the
// response's body. Setting the segment size to UINT32_MAX means that the
// pipe has infinite space. The nsIChannel will continue to buffer data in
@ -697,8 +689,17 @@ FetchDriver::OnStartRequest(nsIRequest* aRequest,
// Cancel request.
return rv;
}
response->SetBody(pipeInputStream);
mResponse->SetBody(pipeInputStream);
nsCOMPtr<nsISupports> securityInfo;
rv = channel->GetSecurityInfo(getter_AddRefs(securityInfo));
if (securityInfo) {
response->SetSecurityInfo(securityInfo);
}
// Resolves fetch() promise which may trigger code running in a worker. Make
// sure the Response is fully initialized before calling this.
mResponse = BeginAndGetFilteredResponse(response);
nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {

View File

@ -3066,7 +3066,7 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
nsAutoPtr<const MetadataTags> aTags)
{
mMediaInfo = *aInfo;
mIsEncrypted = aInfo->mIsEncrypted;
mIsEncrypted = aInfo->IsEncrypted();
mTags = aTags.forget();
mLoadedDataFired = false;
ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
@ -3086,6 +3086,16 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
ProcessMediaFragmentURI();
mDecoder->SetFragmentEndTime(mFragmentEnd);
}
if (mIsEncrypted) {
if (!mMediaSource) {
DecodeError();
return;
}
#ifdef MOZ_EME
DispatchEncrypted(aInfo->mCrypto.mInitData, aInfo->mCrypto.mType);
#endif
}
// Expose the tracks to JS directly.
for (OutputMediaStream& out : mOutputStreams) {

View File

@ -571,6 +571,11 @@ child:
int32_t aModifiers,
bool aPreventDefault);
/**
* APZ notification for mouse scroll testing events.
*/
MouseScrollTestEvent(ViewID aScrollId, nsString aEvent);
CompositionEvent(WidgetCompositionEvent event);
SelectionEvent(WidgetSelectionEvent event);

View File

@ -2199,6 +2199,13 @@ TabChild::RecvMouseWheelEvent(const WidgetWheelEvent& aEvent,
return true;
}
bool
TabChild::RecvMouseScrollTestEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent)
{
APZCCallbackHelper::NotifyMozMouseScrollEvent(aScrollId, aEvent);
return true;
}
static Touch*
GetTouchForIdentifier(const WidgetTouchEvent& aEvent, int32_t aId)
{

View File

@ -365,6 +365,8 @@ public:
const int32_t& aCharCode,
const int32_t& aModifiers,
const bool& aPreventDefault) override;
virtual bool RecvMouseScrollTestEvent(const FrameMetrics::ViewID& aScrollId,
const nsString& aEvent) override;
virtual bool RecvCompositionEvent(const mozilla::WidgetCompositionEvent& event) override;
virtual bool RecvSelectionEvent(const mozilla::WidgetSelectionEvent& event) override;
virtual bool RecvActivateFrameEvent(const nsString& aType, const bool& capture) override;

View File

@ -973,6 +973,14 @@ void TabParent::NotifyAPZStateChange(ViewID aViewId,
}
}
void
TabParent::NotifyMouseScrollTestEvent(const ViewID& aScrollId, const nsString& aEvent)
{
if (!mIsDestroyed) {
unused << SendMouseScrollTestEvent(aScrollId, aEvent);
}
}
void
TabParent::Activate()
{

View File

@ -242,6 +242,7 @@ public:
void NotifyAPZStateChange(ViewID aViewId,
APZStateChange aChange,
int aArg);
void NotifyMouseScrollTestEvent(const ViewID& aScrollId, const nsString& aEvent);
void Activate();
void Deactivate();

View File

@ -1282,6 +1282,7 @@ static const char* const gMachineStateStr[] = {
"NONE",
"DECODING_METADATA",
"WAIT_FOR_RESOURCES",
"WAIT_FOR_CDM",
"DECODING_FIRSTFRAME",
"DORMANT",
"DECODING",
@ -1582,13 +1583,19 @@ void MediaDecoderStateMachine::DoNotifyWaitingForResourcesStatusChanged()
{
NS_ASSERTION(OnDecodeThread(), "Should be on decode thread.");
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
if (mState != DECODER_STATE_WAIT_FOR_RESOURCES) {
return;
}
DECODER_LOG("DoNotifyWaitingForResourcesStatusChanged");
// The reader is no longer waiting for resources (say a hardware decoder),
// we can now proceed to decode metadata.
SetState(DECODER_STATE_DECODING_NONE);
if (mState == DECODER_STATE_WAIT_FOR_RESOURCES) {
// The reader is no longer waiting for resources (say a hardware decoder),
// we can now proceed to decode metadata.
SetState(DECODER_STATE_DECODING_NONE);
} else if (mState == DECODER_STATE_WAIT_FOR_CDM &&
!mReader->IsWaitingOnCDMResource()) {
SetState(DECODER_STATE_DECODING_FIRSTFRAME);
EnqueueDecodeFirstFrameTask();
}
ScheduleStateMachine();
}
@ -2269,6 +2276,13 @@ nsresult MediaDecoderStateMachine::DecodeMetadata()
}
if (mState == DECODER_STATE_DECODING_METADATA) {
if (mReader->IsWaitingOnCDMResource()) {
// Metadata parsing was successful but we're still waiting for CDM caps
// to become available so that we can build the correct decryptor/decoder.
SetState(DECODER_STATE_WAIT_FOR_CDM);
return NS_OK;
}
SetState(DECODER_STATE_DECODING_FIRSTFRAME);
res = EnqueueDecodeFirstFrameTask();
if (NS_FAILED(res)) {
@ -2678,6 +2692,7 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
return NS_OK;
}
case DECODER_STATE_WAIT_FOR_CDM:
case DECODER_STATE_WAIT_FOR_RESOURCES: {
return NS_OK;
}

View File

@ -139,6 +139,7 @@ public:
DECODER_STATE_DECODING_NONE,
DECODER_STATE_DECODING_METADATA,
DECODER_STATE_WAIT_FOR_RESOURCES,
DECODER_STATE_WAIT_FOR_CDM,
DECODER_STATE_DECODING_FIRSTFRAME,
DECODER_STATE_DORMANT,
DECODER_STATE_DECODING,

View File

@ -10,6 +10,7 @@
#include "nsRect.h"
#include "ImageTypes.h"
#include "nsString.h"
#include "nsTArray.h"
#include "StreamBuffer.h" // for TrackID
namespace mozilla {
@ -102,10 +103,22 @@ public:
TrackInfo mTrackInfo;
};
class EncryptionInfo {
public:
EncryptionInfo() : mIsEncrypted(false) {}
// Encryption type to be passed to JS. Usually `cenc'.
nsString mType;
// Encryption data.
nsTArray<uint8_t> mInitData;
// True if the stream has encryption metadata
bool mIsEncrypted;
};
class MediaInfo {
public:
MediaInfo() : mIsEncrypted(false) {}
bool HasVideo() const
{
return mVideo.mHasVideo;
@ -116,16 +129,21 @@ public:
return mAudio.mHasAudio;
}
bool IsEncrypted() const
{
return mCrypto.mIsEncrypted;
}
bool HasValidMedia() const
{
return HasVideo() || HasAudio();
}
bool mIsEncrypted;
// TODO: Store VideoInfo and AudioIndo in arrays to support multi-tracks.
VideoInfo mVideo;
AudioInfo mAudio;
EncryptionInfo mCrypto;
};
} // namespace mozilla

View File

@ -2213,6 +2213,7 @@ MediaStream::RunAfterPendingUpdates(already_AddRefed<nsIRunnable> aRunnable)
// runnable will run in finite time.
if (!(graph->mRealtime || graph->mNonRealtimeProcessing)) {
runnable->Run();
return;
}
class Message : public ControlMessage {

View File

@ -265,12 +265,6 @@ AVCCDecoderModule::Startup()
return mPDM->Startup();
}
nsresult
AVCCDecoderModule::Shutdown()
{
return mPDM->Shutdown();
}
already_AddRefed<MediaDataDecoder>
AVCCDecoderModule::CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,

View File

@ -28,7 +28,6 @@ public:
virtual ~AVCCDecoderModule();
virtual nsresult Startup() override;
virtual nsresult Shutdown() override;
virtual already_AddRefed<MediaDataDecoder>
CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,

View File

@ -206,11 +206,6 @@ private:
class BlankDecoderModule : public PlatformDecoderModule {
public:
// Called when the decoders have shutdown. Main thread only.
virtual nsresult Shutdown() override {
return NS_OK;
}
// Decode thread.
virtual already_AddRefed<MediaDataDecoder>
CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,

View File

@ -156,6 +156,7 @@ MP4Reader::MP4Reader(AbstractMediaDecoder* aDecoder)
, mDemuxerInitialized(false)
, mFoundSPSForTelemetry(false)
, mIsEncrypted(false)
, mAreDecodersSetup(false)
, mIndexReady(false)
, mDemuxerMonitor("MP4 Demuxer")
#if defined(MP4_READER_DORMANT_HEURISTIC)
@ -204,10 +205,7 @@ MP4Reader::Shutdown()
// Dispose of the queued sample before shutting down the demuxer
mQueuedVideoSample = nullptr;
if (mPlatform) {
mPlatform->Shutdown();
mPlatform = nullptr;
}
mPlatform = nullptr;
return MediaDecoderReader::Shutdown();
}
@ -266,41 +264,13 @@ MP4Reader::Init(MediaDecoderReader* aCloneDonor)
return NS_OK;
}
#ifdef MOZ_EME
class DispatchKeyNeededEvent : public nsRunnable {
public:
DispatchKeyNeededEvent(AbstractMediaDecoder* aDecoder,
nsTArray<uint8_t>& aInitData,
const nsString& aInitDataType)
: mDecoder(aDecoder)
, mInitData(aInitData)
, mInitDataType(aInitDataType)
{
}
NS_IMETHOD Run() {
// Note: Null check the owner, as the decoder could have been shutdown
// since this event was dispatched.
MediaDecoderOwner* owner = mDecoder->GetOwner();
if (owner) {
owner->DispatchEncrypted(mInitData, mInitDataType);
}
mDecoder = nullptr;
return NS_OK;
}
private:
nsRefPtr<AbstractMediaDecoder> mDecoder;
nsTArray<uint8_t> mInitData;
nsString mInitDataType;
};
#endif
void MP4Reader::RequestCodecResource() {
if (mVideo.mDecoder) {
mVideo.mDecoder->AllocateMediaResources();
}
}
bool MP4Reader::IsWaitingOnCodecResource() {
bool MP4Reader::IsWaitingMediaResources() {
return mVideo.mDecoder && mVideo.mDecoder->IsWaitingMediaResources();
}
@ -330,15 +300,6 @@ bool MP4Reader::IsWaitingOnCDMResource() {
#endif
}
bool MP4Reader::IsWaitingMediaResources()
{
// IsWaitingOnCDMResource() *must* come first, because we don't know whether
// we can create a decoder until the CDM is initialized and it has told us
// whether *it* will decode, or whether we need to create a PDM to do the
// decoding
return IsWaitingOnCDMResource() || IsWaitingOnCodecResource();
}
void
MP4Reader::ExtractCryptoInitData(nsTArray<uint8_t>& aInitData)
{
@ -407,7 +368,7 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
{
MonitorAutoUnlock unlock(mDemuxerMonitor);
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
mInfo.mIsEncrypted = mIsEncrypted = mCrypto.valid;
mInfo.mCrypto.mIsEncrypted = mIsEncrypted = mCrypto.valid;
}
// Remember that we've initialized the demuxer, so that if we're decoding
@ -420,90 +381,18 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
return NS_OK;
}
if (mCrypto.valid) {
#ifdef MOZ_EME
// We have encrypted audio or video. We'll need a CDM to decrypt and
// possibly decode this. Wait until we've received a CDM from the
// JavaScript player app. Note: we still go through the motions here
// even if EME is disabled, so that if script tries and fails to create
// a CDM, we can detect that and notify chrome and show some UI explaining
// that we failed due to EME being disabled.
nsRefPtr<CDMProxy> proxy;
nsTArray<uint8_t> initData;
ExtractCryptoInitData(initData);
if (initData.Length() == 0) {
return NS_ERROR_FAILURE;
}
if (!mInitDataEncountered.Contains(initData)) {
mInitDataEncountered.AppendElement(initData);
NS_DispatchToMainThread(new DispatchKeyNeededEvent(mDecoder, initData, NS_LITERAL_STRING("cenc")));
}
if (IsWaitingMediaResources()) {
return NS_OK;
}
MOZ_ASSERT(!IsWaitingMediaResources());
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
proxy = mDecoder->GetCDMProxy();
}
MOZ_ASSERT(proxy);
mPlatform = PlatformDecoderModule::CreateCDMWrapper(proxy,
HasAudio(),
HasVideo());
NS_ENSURE_TRUE(mPlatform, NS_ERROR_FAILURE);
#else
// EME not supported.
return NS_ERROR_FAILURE;
#endif
} else {
mPlatform = PlatformDecoderModule::Create();
NS_ENSURE_TRUE(mPlatform, NS_ERROR_FAILURE);
}
if (HasAudio()) {
const AudioDecoderConfig& audio = mDemuxer->AudioConfig();
if (mInfo.mAudio.mHasAudio && !IsSupportedAudioMimeType(audio.mime_type)) {
return NS_ERROR_FAILURE;
}
mInfo.mAudio.mRate = audio.samples_per_second;
mInfo.mAudio.mChannels = audio.channel_count;
mAudio.mCallback = new DecoderCallback(this, kAudio);
mAudio.mDecoder = mPlatform->CreateAudioDecoder(audio,
mAudio.mTaskQueue,
mAudio.mCallback);
NS_ENSURE_TRUE(mAudio.mDecoder != nullptr, NS_ERROR_FAILURE);
nsresult rv = mAudio.mDecoder->Init();
NS_ENSURE_SUCCESS(rv, rv);
}
if (HasVideo()) {
const VideoDecoderConfig& video = mDemuxer->VideoConfig();
if (mInfo.mVideo.mHasVideo && !IsSupportedVideoMimeType(video.mime_type)) {
return NS_ERROR_FAILURE;
}
mInfo.mVideo.mDisplay =
nsIntSize(video.display_width, video.display_height);
mVideo.mCallback = new DecoderCallback(this, kVideo);
if (mSharedDecoderManager && mPlatform->SupportsSharedDecoders(video)) {
mVideo.mDecoder =
mSharedDecoderManager->CreateVideoDecoder(mPlatform,
video,
mLayersBackendType,
mDecoder->GetImageContainer(),
mVideo.mTaskQueue,
mVideo.mCallback);
} else {
mVideo.mDecoder = mPlatform->CreateVideoDecoder(video,
mLayersBackendType,
mDecoder->GetImageContainer(),
mVideo.mTaskQueue,
mVideo.mCallback);
}
NS_ENSURE_TRUE(mVideo.mDecoder != nullptr, NS_ERROR_FAILURE);
nsresult rv = mVideo.mDecoder->Init();
NS_ENSURE_SUCCESS(rv, rv);
// Collect telemetry from h264 AVCC SPS.
if (!mFoundSPSForTelemetry) {
@ -511,6 +400,17 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
}
}
if (mIsEncrypted) {
nsTArray<uint8_t> initData;
ExtractCryptoInitData(initData);
if (initData.Length() == 0) {
return NS_ERROR_FAILURE;
}
mInfo.mCrypto.mInitData = initData;
mInfo.mCrypto.mType = NS_LITERAL_STRING("cenc");
}
// Get the duration, and report it to the decoder if we have it.
Microseconds duration;
{
@ -525,12 +425,96 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo,
*aInfo = mInfo;
*aTags = nullptr;
if (!IsWaitingMediaResources() && !IsWaitingOnCDMResource()) {
NS_ENSURE_TRUE(EnsureDecodersSetup(), NS_ERROR_FAILURE);
}
MonitorAutoLock mon(mDemuxerMonitor);
UpdateIndex();
return NS_OK;
}
bool
MP4Reader::EnsureDecodersSetup()
{
if (mAreDecodersSetup) {
return !!mPlatform;
}
if (mIsEncrypted) {
#ifdef MOZ_EME
// We have encrypted audio or video. We'll need a CDM to decrypt and
// possibly decode this. Wait until we've received a CDM from the
// JavaScript player app. Note: we still go through the motions here
// even if EME is disabled, so that if script tries and fails to create
// a CDM, we can detect that and notify chrome and show some UI explaining
// that we failed due to EME being disabled.
nsRefPtr<CDMProxy> proxy;
if (IsWaitingMediaResources()) {
return true;
}
MOZ_ASSERT(!IsWaitingMediaResources());
{
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
proxy = mDecoder->GetCDMProxy();
}
MOZ_ASSERT(proxy);
mPlatform = PlatformDecoderModule::CreateCDMWrapper(proxy,
HasAudio(),
HasVideo());
NS_ENSURE_TRUE(mPlatform, false);
#else
// EME not supported.
return false;
#endif
} else {
mPlatform = PlatformDecoderModule::Create();
NS_ENSURE_TRUE(mPlatform, false);
}
if (HasAudio()) {
NS_ENSURE_TRUE(IsSupportedAudioMimeType(mDemuxer->AudioConfig().mime_type),
false);
mAudio.mDecoder = mPlatform->CreateAudioDecoder(mDemuxer->AudioConfig(),
mAudio.mTaskQueue,
mAudio.mCallback);
NS_ENSURE_TRUE(mAudio.mDecoder != nullptr, false);
nsresult rv = mAudio.mDecoder->Init();
NS_ENSURE_SUCCESS(rv, false);
}
if (HasVideo()) {
NS_ENSURE_TRUE(IsSupportedVideoMimeType(mDemuxer->VideoConfig().mime_type),
false);
if (mSharedDecoderManager && mPlatform->SupportsSharedDecoders(mDemuxer->VideoConfig())) {
mVideo.mDecoder =
mSharedDecoderManager->CreateVideoDecoder(mPlatform,
mDemuxer->VideoConfig(),
mLayersBackendType,
mDecoder->GetImageContainer(),
mVideo.mTaskQueue,
mVideo.mCallback);
} else {
mVideo.mDecoder = mPlatform->CreateVideoDecoder(mDemuxer->VideoConfig(),
mLayersBackendType,
mDecoder->GetImageContainer(),
mVideo.mTaskQueue,
mVideo.mCallback);
}
NS_ENSURE_TRUE(mVideo.mDecoder != nullptr, false);
nsresult rv = mVideo.mDecoder->Init();
NS_ENSURE_SUCCESS(rv, false);
}
mAreDecodersSetup = true;
return true;
}
void
MP4Reader::ReadUpdatedMetadata(MediaInfo* aInfo)
{
@ -579,10 +563,10 @@ void
MP4Reader::DisableHardwareAcceleration()
{
if (HasVideo() && mSharedDecoderManager) {
mPlatform->DisableHardwareAcceleration();
mSharedDecoderManager->DisableHardwareAcceleration();
const VideoDecoderConfig& video = mDemuxer->VideoConfig();
if (!mSharedDecoderManager->Recreate(mPlatform, video, mLayersBackendType, mDecoder->GetImageContainer())) {
if (!mSharedDecoderManager->Recreate(video, mLayersBackendType, mDecoder->GetImageContainer())) {
MonitorAutoLock mon(mVideo.mMonitor);
mVideo.mError = true;
if (mVideo.HasPromise()) {
@ -618,6 +602,11 @@ MP4Reader::RequestVideoData(bool aSkipToNextKeyframe,
MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
VLOG("skip=%d time=%lld", aSkipToNextKeyframe, aTimeThreshold);
if (!EnsureDecodersSetup()) {
NS_WARNING("Error constructing MP4 decoders");
return VideoDataPromise::CreateAndReject(DECODE_ERROR, __func__);
}
if (mShutdown) {
NS_WARNING("RequestVideoData on shutdown MP4Reader!");
return VideoDataPromise::CreateAndReject(CANCELED, __func__);
@ -653,6 +642,12 @@ MP4Reader::RequestAudioData()
{
MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
VLOG("");
if (!EnsureDecodersSetup()) {
NS_WARNING("Error constructing MP4 decoders");
return AudioDataPromise::CreateAndReject(DECODE_ERROR, __func__);
}
if (mShutdown) {
NS_WARNING("RequestAudioData on shutdown MP4Reader!");
return AudioDataPromise::CreateAndReject(CANCELED, __func__);

View File

@ -103,6 +103,8 @@ private:
bool InitDemuxer();
void ReturnOutput(MediaData* aData, TrackType aTrack);
bool EnsureDecodersSetup();
// Sends input to decoder for aTrack, and output to the state machine,
// if necessary.
void Update(TrackType aTrack);
@ -135,7 +137,6 @@ private:
bool IsSupportedVideoMimeType(const char* aMimeType);
void NotifyResourcesStatusChanged();
void RequestCodecResource();
bool IsWaitingOnCodecResource();
virtual bool IsWaitingOnCDMResource() override;
Microseconds GetNextKeyframeTime();
@ -285,6 +286,8 @@ private:
// Synchronized by decoder monitor.
bool mIsEncrypted;
bool mAreDecodersSetup;
bool mIndexReady;
Monitor mDemuxerMonitor;
nsRefPtr<SharedDecoderManager> mSharedDecoderManager;

View File

@ -86,13 +86,6 @@ public:
bool aHasVideo);
#endif
// Called to shutdown the decoder module and cleanup state. The PDM
// is deleted immediately after Shutdown() is called. Shutdown() is
// called after Shutdown() has been called on all MediaDataDecoders
// created from this PlatformDecoderModule.
// This is called on the decode task queue.
virtual nsresult Shutdown() = 0;
// Creates an H.264 decoder. The layers backend is passed in so that
// decoders can determine whether hardware accelerated decoding can be used.
// Asynchronous decoding of video should be done in runnables dispatched

View File

@ -12,7 +12,10 @@ namespace mozilla {
class SharedDecoderCallback : public MediaDataDecoderCallback
{
public:
explicit SharedDecoderCallback(SharedDecoderManager* aManager) : mManager(aManager) {}
explicit SharedDecoderCallback(SharedDecoderManager* aManager)
: mManager(aManager)
{
}
virtual void Output(MediaData* aData) override
{
@ -66,25 +69,34 @@ SharedDecoderManager::SharedDecoderManager()
mCallback = new SharedDecoderCallback(this);
}
SharedDecoderManager::~SharedDecoderManager() {}
SharedDecoderManager::~SharedDecoderManager()
{
}
already_AddRefed<MediaDataDecoder>
SharedDecoderManager::CreateVideoDecoder(
PlatformDecoderModule* aPDM,
const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer,
FlushableMediaTaskQueue* aVideoTaskQueue, MediaDataDecoderCallback* aCallback)
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer,
FlushableMediaTaskQueue* aVideoTaskQueue,
MediaDataDecoderCallback* aCallback)
{
if (!mDecoder) {
// We use the manager's task queue for the decoder, rather than the one
// passed in, so that none of the objects sharing the decoder can shutdown
// the task queue while we're potentially still using it for a *different*
// object also sharing the decoder.
mDecoder = aPDM->CreateVideoDecoder(
aConfig, aLayersBackend, aImageContainer, mTaskQueue, mCallback);
mDecoder = aPDM->CreateVideoDecoder(aConfig,
aLayersBackend,
aImageContainer,
mTaskQueue,
mCallback);
if (!mDecoder) {
mPDM = nullptr;
return nullptr;
}
mPDM = aPDM;
nsresult rv = mDecoder->Init();
NS_ENSURE_SUCCESS(rv, nullptr);
}
@ -93,15 +105,25 @@ SharedDecoderManager::CreateVideoDecoder(
return proxy.forget();
}
void
SharedDecoderManager::DisableHardwareAcceleration()
{
MOZ_ASSERT(mPDM);
mPDM->DisableHardwareAcceleration();
}
bool
SharedDecoderManager::Recreate(PlatformDecoderModule* aPDM,
const mp4_demuxer::VideoDecoderConfig& aConfig,
SharedDecoderManager::Recreate(const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer)
{
mDecoder->Flush();
mDecoder->Shutdown();
mDecoder = aPDM->CreateVideoDecoder(aConfig, aLayersBackend, aImageContainer, mTaskQueue, mCallback);
mDecoder = mPDM->CreateVideoDecoder(aConfig,
aLayersBackend,
aImageContainer,
mTaskQueue,
mCallback);
if (!mDecoder) {
return false;
}
@ -168,6 +190,7 @@ SharedDecoderManager::Shutdown()
mDecoder->Shutdown();
mDecoder = nullptr;
}
mPDM = nullptr;
if (mTaskQueue) {
mTaskQueue->BeginShutdown();
mTaskQueue->AwaitShutdownAndIdle();
@ -175,13 +198,17 @@ SharedDecoderManager::Shutdown()
}
}
SharedDecoderProxy::SharedDecoderProxy(
SharedDecoderManager* aManager, MediaDataDecoderCallback* aCallback)
: mManager(aManager), mCallback(aCallback)
SharedDecoderProxy::SharedDecoderProxy(SharedDecoderManager* aManager,
MediaDataDecoderCallback* aCallback)
: mManager(aManager)
, mCallback(aCallback)
{
}
SharedDecoderProxy::~SharedDecoderProxy() { Shutdown(); }
SharedDecoderProxy::~SharedDecoderProxy()
{
Shutdown();
}
nsresult
SharedDecoderProxy::Init()

View File

@ -28,7 +28,8 @@ public:
PlatformDecoderModule* aPDM,
const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer, FlushableMediaTaskQueue* aVideoTaskQueue,
layers::ImageContainer* aImageContainer,
FlushableMediaTaskQueue* aVideoTaskQueue,
MediaDataDecoderCallback* aCallback);
void SetReader(MediaDecoderReader* aReader);
@ -40,15 +41,16 @@ public:
friend class SharedDecoderProxy;
friend class SharedDecoderCallback;
bool Recreate(PlatformDecoderModule* aPDM,
const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer);
void DisableHardwareAcceleration();
bool Recreate(const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer);
private:
virtual ~SharedDecoderManager();
void DrainComplete();
nsRefPtr<PlatformDecoderModule> mPDM;
nsRefPtr<MediaDataDecoder> mDecoder;
nsRefPtr<FlushableMediaTaskQueue> mTaskQueue;
SharedDecoderProxy* mActiveProxy;

View File

@ -299,12 +299,6 @@ AndroidDecoderModule::CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig&
}
nsresult AndroidDecoderModule::Shutdown()
{
return NS_OK;
}
MediaCodecDataDecoder::MediaCodecDataDecoder(MediaData::Type aType,
const char* aMimeType,
MediaFormat::Param aFormat,

View File

@ -19,8 +19,6 @@ typedef std::queue<mp4_demuxer::MP4Sample*> SampleQueue;
class AndroidDecoderModule : public PlatformDecoderModule {
public:
virtual nsresult Shutdown() override;
virtual already_AddRefed<MediaDataDecoder>
CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,

View File

@ -36,12 +36,46 @@ bool AppleDecoderModule::sIsVTHWAvailable = false;
bool AppleDecoderModule::sIsVDAAvailable = false;
bool AppleDecoderModule::sForceVDA = false;
class LinkTask : public nsRunnable {
public:
NS_IMETHOD Run() override {
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
MOZ_ASSERT(AppleDecoderModule::sInitialized);
if (AppleDecoderModule::sIsVDAAvailable) {
AppleVDALinker::Link();
}
if (AppleDecoderModule::sIsVTAvailable) {
AppleVTLinker::Link();
AppleCMLinker::Link();
}
return NS_OK;
}
};
class UnlinkTask : public nsRunnable {
public:
NS_IMETHOD Run() override {
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
MOZ_ASSERT(AppleDecoderModule::sInitialized);
if (AppleDecoderModule::sIsVDAAvailable) {
AppleVDALinker::Unlink();
}
if (AppleDecoderModule::sIsVTAvailable) {
AppleVTLinker::Unlink();
AppleCMLinker::Unlink();
}
return NS_OK;
}
};
AppleDecoderModule::AppleDecoderModule()
{
}
AppleDecoderModule::~AppleDecoderModule()
{
nsCOMPtr<nsIRunnable> task(new UnlinkTask());
NS_DispatchToMainThread(task);
}
/* static */
@ -104,22 +138,6 @@ AppleDecoderModule::CanDecode()
return (sIsVDAAvailable || sIsVTAvailable) ? NS_OK : NS_ERROR_NO_INTERFACE;
}
class LinkTask : public nsRunnable {
public:
NS_IMETHOD Run() override {
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
MOZ_ASSERT(AppleDecoderModule::sInitialized);
if (AppleDecoderModule::sIsVDAAvailable) {
AppleVDALinker::Link();
}
if (AppleDecoderModule::sIsVTAvailable) {
AppleVTLinker::Link();
AppleCMLinker::Link();
}
return NS_OK;
}
};
nsresult
AppleDecoderModule::Startup()
{
@ -133,30 +151,6 @@ AppleDecoderModule::Startup()
return NS_OK;
}
class UnlinkTask : public nsRunnable {
public:
NS_IMETHOD Run() override {
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
MOZ_ASSERT(AppleDecoderModule::sInitialized);
if (AppleDecoderModule::sIsVDAAvailable) {
AppleVDALinker::Unlink();
}
if (AppleDecoderModule::sIsVTAvailable) {
AppleVTLinker::Unlink();
AppleCMLinker::Unlink();
}
return NS_OK;
}
};
nsresult
AppleDecoderModule::Shutdown()
{
nsCOMPtr<nsIRunnable> task(new UnlinkTask());
NS_DispatchToMainThread(task);
return NS_OK;
}
already_AddRefed<MediaDataDecoder>
AppleDecoderModule::CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,

View File

@ -18,10 +18,6 @@ public:
virtual nsresult Startup() override;
// Called when the decoders have shutdown. Main thread only.
// Does this really need to be main thread only????
virtual nsresult Shutdown() override;
// Decode thread.
virtual already_AddRefed<MediaDataDecoder>
CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,

View File

@ -94,7 +94,11 @@ AppleVDALinker::Unlink()
MOZ_ASSERT(sRefCount > 0, "Unbalanced Unlink()");
--sRefCount;
if (sLink && sRefCount < 1) {
LOG("Unlinking VideoToolbox framework.");
LOG("Unlinking VideoDecodeAcceleration framework.");
#define LINK_FUNC(func) \
func = nullptr;
#include "AppleVDAFunctions.h"
#undef LINK_FUNC
dlclose(sLink);
sLink = nullptr;
skPropWidth = nullptr;

View File

@ -98,6 +98,10 @@ AppleVTLinker::Unlink()
--sRefCount;
if (sLink && sRefCount < 1) {
LOG("Unlinking VideoToolbox framework.");
#define LINK_FUNC(func) \
func = nullptr;
#include "AppleVTFunctions.h"
#undef LINK_FUNC
dlclose(sLink);
sLink = nullptr;
skPropEnableHWAccel = nullptr;

View File

@ -208,15 +208,6 @@ EMEDecoderModule::~EMEDecoderModule()
{
}
nsresult
EMEDecoderModule::Shutdown()
{
if (mPDM) {
return mPDM->Shutdown();
}
return NS_OK;
}
static already_AddRefed<MediaDataDecoderProxy>
CreateDecoderWrapper(MediaDataDecoderCallback* aCallback, CDMProxy* aProxy, FlushableMediaTaskQueue* aTaskQueue)
{

View File

@ -28,9 +28,6 @@ public:
virtual ~EMEDecoderModule();
// Called when the decoders have shutdown. Main thread only.
virtual nsresult Shutdown() override;
// Decode thread.
virtual already_AddRefed<MediaDataDecoder>
CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,

View File

@ -28,8 +28,6 @@ public:
FFmpegDecoderModule() {}
virtual ~FFmpegDecoderModule() {}
virtual nsresult Shutdown() override { return NS_OK; }
virtual already_AddRefed<MediaDataDecoder>
CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,

View File

@ -21,12 +21,6 @@ GMPDecoderModule::~GMPDecoderModule()
{
}
nsresult
GMPDecoderModule::Shutdown()
{
return NS_OK;
}
static already_AddRefed<MediaDataDecoderProxy>
CreateDecoderWrapper(MediaDataDecoderCallback* aCallback)
{

View File

@ -17,9 +17,6 @@ public:
virtual ~GMPDecoderModule();
// Called when the decoders have shutdown. Main thread only.
virtual nsresult Shutdown() override;
// Decode thread.
virtual already_AddRefed<MediaDataDecoder>
CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,

View File

@ -26,12 +26,6 @@ GonkDecoderModule::Init()
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
}
nsresult
GonkDecoderModule::Shutdown()
{
return NS_OK;
}
already_AddRefed<MediaDataDecoder>
GonkDecoderModule::CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
mozilla::layers::LayersBackend aLayersBackend,

View File

@ -16,9 +16,6 @@ public:
GonkDecoderModule();
virtual ~GonkDecoderModule();
// Called when the decoders have shutdown.
virtual nsresult Shutdown() override;
// Decode thread.
virtual already_AddRefed<MediaDataDecoder>
CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,

View File

@ -28,6 +28,8 @@ WMFDecoderModule::WMFDecoderModule()
WMFDecoderModule::~WMFDecoderModule()
{
DebugOnly<HRESULT> hr = wmf::MFShutdown();
NS_ASSERTION(SUCCEEDED(hr), "MFShutdown failed");
}
/* static */
@ -58,14 +60,6 @@ WMFDecoderModule::Startup()
return NS_OK;
}
nsresult
WMFDecoderModule::Shutdown()
{
DebugOnly<HRESULT> hr = wmf::MFShutdown();
NS_ASSERTION(SUCCEEDED(hr), "MFShutdown failed");
return NS_OK;
}
already_AddRefed<MediaDataDecoder>
WMFDecoderModule::CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,

View File

@ -19,9 +19,6 @@ public:
// Initializes the module, loads required dynamic libraries, etc.
virtual nsresult Startup() override;
// Called when the decoders have shutdown.
virtual nsresult Shutdown() override;
virtual already_AddRefed<MediaDataDecoder>
CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,

View File

@ -665,10 +665,11 @@ GeckoMediaPluginService::LoadFromEnvironment()
NS_IMETHODIMP
GeckoMediaPluginService::PathRunnable::Run()
{
if (mAdd) {
if (mOperation == ADD) {
mService->AddOnGMPThread(mPath);
} else {
mService->RemoveOnGMPThread(mPath);
mService->RemoveOnGMPThread(mPath,
mOperation == REMOVE_AND_DELETE_FROM_DISK);
}
return NS_OK;
}
@ -677,14 +678,26 @@ NS_IMETHODIMP
GeckoMediaPluginService::AddPluginDirectory(const nsAString& aDirectory)
{
MOZ_ASSERT(NS_IsMainThread());
return GMPDispatch(new PathRunnable(this, aDirectory, true));
return GMPDispatch(new PathRunnable(this, aDirectory,
PathRunnable::EOperation::ADD));
}
NS_IMETHODIMP
GeckoMediaPluginService::RemovePluginDirectory(const nsAString& aDirectory)
{
MOZ_ASSERT(NS_IsMainThread());
return GMPDispatch(new PathRunnable(this, aDirectory, false));
return GMPDispatch(new PathRunnable(this, aDirectory,
PathRunnable::EOperation::REMOVE));
}
NS_IMETHODIMP
GeckoMediaPluginService::RemoveAndDeletePluginDirectory(
const nsAString& aDirectory)
{
MOZ_ASSERT(NS_IsMainThread());
return GMPDispatch(
new PathRunnable(this, aDirectory,
PathRunnable::EOperation::REMOVE_AND_DELETE_FROM_DISK));
}
class DummyRunnable : public nsRunnable {
@ -915,7 +928,8 @@ GeckoMediaPluginService::AddOnGMPThread(const nsAString& aDirectory)
}
void
GeckoMediaPluginService::RemoveOnGMPThread(const nsAString& aDirectory)
GeckoMediaPluginService::RemoveOnGMPThread(const nsAString& aDirectory,
const bool aDeleteFromDisk)
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
LOGD(("%s::%s: %s", __CLASS__, __FUNCTION__, NS_LossyConvertUTF16toASCII(aDirectory).get()));
@ -931,7 +945,11 @@ GeckoMediaPluginService::RemoveOnGMPThread(const nsAString& aDirectory)
nsCOMPtr<nsIFile> pluginpath = mPlugins[i]->GetDirectory();
bool equals;
if (NS_SUCCEEDED(directory->Equals(pluginpath, &equals)) && equals) {
mPlugins[i]->AbortAsyncShutdown();
mPlugins[i]->CloseActive(true);
if (aDeleteFromDisk) {
pluginpath->Remove(true);
}
mPlugins.RemoveElementAt(i);
return;
}

View File

@ -71,8 +71,9 @@ private:
void LoadFromEnvironment();
void ProcessPossiblePlugin(nsIFile* aDir);
void AddOnGMPThread(const nsAString& aSearchDir);
void RemoveOnGMPThread(const nsAString& aSearchDir);
void AddOnGMPThread(const nsAString& aDirectory);
void RemoveOnGMPThread(const nsAString& aDirectory,
const bool aDeleteFromDisk);
nsresult SetAsyncShutdownTimeout();
@ -96,11 +97,17 @@ private:
class PathRunnable : public nsRunnable
{
public:
PathRunnable(GeckoMediaPluginService* service, const nsAString& path,
bool add)
: mService(service)
, mPath(path)
, mAdd(add)
enum EOperation {
ADD,
REMOVE,
REMOVE_AND_DELETE_FROM_DISK,
};
PathRunnable(GeckoMediaPluginService* aService, const nsAString& aPath,
EOperation aOperation)
: mService(aService)
, mPath(aPath)
, mOperation(aOperation)
{ }
NS_DECL_NSIRUNNABLE
@ -108,7 +115,7 @@ private:
private:
nsRefPtr<GeckoMediaPluginService> mService;
nsString mPath;
bool mAdd;
EOperation mOperation;
};
Mutex mMutex; // Protects mGMPThread and mShuttingDown and mPlugins

View File

@ -26,7 +26,7 @@ class GMPVideoHost;
[ptr] native GMPDecryptorProxy(GMPDecryptorProxy);
[ptr] native GMPAudioDecoderProxy(GMPAudioDecoderProxy);
[scriptable, uuid(56cc105f-dd27-4752-83e0-371908edba04)]
[scriptable, uuid(f71e6e57-5175-4cf3-8cc2-629273a75b67)]
interface mozIGeckoMediaPluginService : nsISupports
{
@ -97,6 +97,12 @@ interface mozIGeckoMediaPluginService : nsISupports
*/
void removePluginDirectory(in AString directory);
/**
* Remove a directory for gecko media plugins and delete it from disk.
* @note Main-thread API.
*/
void removeAndDeletePluginDirectory(in AString directory);
/**
* Gets the NodeId for a (origin, urlbarOrigin, isInprivateBrowsing) tuple.
*/

View File

@ -93,6 +93,25 @@ MediaSourceReader::IsWaitingMediaResources()
return !mHasEssentialTrackBuffers;
}
bool
MediaSourceReader::IsWaitingOnCDMResource()
{
#ifdef MOZ_EME
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
MOZ_ASSERT(!IsWaitingMediaResources());
for (auto& trackBuffer : mTrackBuffers) {
if (trackBuffer->IsWaitingOnCDMResource()) {
return true;
}
}
return mInfo.IsEncrypted() && !mCDMProxy;
#else
return false;
#endif
}
size_t
MediaSourceReader::SizeOfVideoQueueInFrames()
{
@ -1020,6 +1039,22 @@ MediaSourceReader::MaybeNotifyHaveData()
IsSeeking(), haveAudio, haveVideo);
}
static void
CombineEncryptionData(EncryptionInfo& aTo, const EncryptionInfo& aFrom)
{
if (!aFrom.mIsEncrypted) {
return;
}
aTo.mIsEncrypted = true;
if (!aTo.mType.IsEmpty() && !aTo.mType.Equals(aFrom.mType)) {
NS_WARNING("mismatched encryption types");
}
aTo.mType = aFrom.mType;
aTo.mInitData.AppendElements(aFrom.mInitData);
}
nsresult
MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
{
@ -1043,7 +1078,7 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
const MediaInfo& info = GetAudioReader()->GetMediaInfo();
MOZ_ASSERT(info.HasAudio());
mInfo.mAudio = info.mAudio;
mInfo.mIsEncrypted = mInfo.mIsEncrypted || info.mIsEncrypted;
CombineEncryptionData(mInfo.mCrypto, info.mCrypto);
MSE_DEBUG("audio reader=%p duration=%lld",
mAudioSourceDecoder.get(),
mAudioSourceDecoder->GetReader()->GetDecoder()->GetMediaDuration());
@ -1056,7 +1091,7 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
const MediaInfo& info = GetVideoReader()->GetMediaInfo();
MOZ_ASSERT(info.HasVideo());
mInfo.mVideo = info.mVideo;
mInfo.mIsEncrypted = mInfo.mIsEncrypted || info.mIsEncrypted;
CombineEncryptionData(mInfo.mCrypto, info.mCrypto);
MSE_DEBUG("video reader=%p duration=%lld",
GetVideoReader(),
GetVideoReader()->GetDecoder()->GetMediaDuration());

View File

@ -45,6 +45,7 @@ public:
void PrepareInitialization();
bool IsWaitingMediaResources() override;
bool IsWaitingOnCDMResource() override;
nsRefPtr<AudioDataPromise> RequestAudioData() override;
nsRefPtr<VideoDataPromise>

View File

@ -47,6 +47,7 @@ TrackBuffer::TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& a
, mLastStartTimestamp(0)
, mLastTimestampOffset(0)
, mAdjustedTimestamp(0)
, mIsWaitingOnCDM(false)
, mShutdown(false)
{
MOZ_COUNT_CTOR(TrackBuffer);
@ -588,6 +589,7 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
if (mCurrentDecoder != aDecoder) {
MSE_DEBUG("append was cancelled. Aborting initialization.");
RemoveDecoder(aDecoder);
// If we reached this point, the SourceBuffer would have disconnected
// the promise. So no need to reject it.
return;
@ -641,12 +643,12 @@ TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
}
if (mCurrentDecoder != aDecoder) {
MSE_DEBUG("append was cancelled. Aborting initialization.");
RemoveDecoder(aDecoder);
return;
}
if (NS_SUCCEEDED(rv) && reader->IsWaitingOnCDMResource()) {
mWaitingDecoders.AppendElement(aDecoder);
return;
mIsWaitingOnCDM = true;
}
aDecoder->SetTaskQueue(nullptr);
@ -693,6 +695,7 @@ TrackBuffer::CompleteInitializeDecoder(SourceBufferDecoder* aDecoder)
MSE_DEBUG("append was cancelled. Aborting initialization.");
// If we reached this point, the SourceBuffer would have disconnected
// the promise. So no need to reject it.
RemoveDecoder(aDecoder);
return;
}
@ -807,6 +810,12 @@ TrackBuffer::IsReady()
return mInfo.HasAudio() || mInfo.HasVideo();
}
bool
TrackBuffer::IsWaitingOnCDMResource()
{
return mIsWaitingOnCDM;
}
bool
TrackBuffer::ContainsTime(int64_t aTime, int64_t aTolerance)
{
@ -875,21 +884,12 @@ TrackBuffer::SetCDMProxy(CDMProxy* aProxy)
{
ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
nsresult rv = mDecoders[i]->SetCDMProxy(aProxy);
NS_ENSURE_SUCCESS(rv, rv);
for (auto& decoder : mDecoders) {
decoder->SetCDMProxy(aProxy);
}
for (uint32_t i = 0; i < mWaitingDecoders.Length(); ++i) {
CDMCaps::AutoLock caps(aProxy->Capabilites());
caps.CallOnMainThreadWhenCapsAvailable(
NS_NewRunnableMethodWithArg<SourceBufferDecoder*>(this,
&TrackBuffer::QueueInitializeDecoder,
mWaitingDecoders[i]));
}
mWaitingDecoders.Clear();
mIsWaitingOnCDM = false;
mParentDecoder->NotifyWaitingForResourcesStatusChanged();
return NS_OK;
}
#endif

View File

@ -76,6 +76,8 @@ public:
// segment has successfully initialized by setting mHas{Audio,Video}..
bool IsReady();
bool IsWaitingOnCDMResource();
// Returns true if any of the decoders managed by this track buffer
// contain aTime in their buffered ranges.
bool ContainsTime(int64_t aTime, int64_t aTolerance);
@ -185,10 +187,6 @@ private:
// Access protected by mParentDecoder's monitor.
nsTArray<nsRefPtr<SourceBufferDecoder>> mInitializedDecoders;
// Decoders which are waiting on a Content Decryption Module to be able to
// finish ReadMetadata.
nsTArray<nsRefPtr<SourceBufferDecoder>> mWaitingDecoders;
// The decoder that the owning SourceBuffer is currently appending data to.
// Modified on the main thread only.
nsRefPtr<SourceBufferDecoder> mCurrentDecoder;
@ -206,6 +204,9 @@ private:
int64_t mLastTimestampOffset;
int64_t mAdjustedTimestamp;
// True if at least one of our decoders has encrypted content.
bool mIsWaitingOnCDM;
// Set when the first decoder used by this TrackBuffer is initialized.
// Protected by mParentDecoder's monitor.
MediaInfo mInfo;

View File

@ -370,8 +370,6 @@ skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
[test_dormant_playback.html]
skip-if = (os == 'win' && os_version == '5.1') || (os != 'win' && toolkit != 'gonk')
[test_eme_access_control.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
[test_eme_canvas_blocked.html]
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s || (os == 'win' && !debug) # bug 1043403, bug 1057908, bug 1140675
[test_eme_non_mse_fails.html]

View File

@ -1,112 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test EME blocked cross-origin</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="manifest.js"></script>
<script type="text/javascript" src="eme.js"></script>
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
var manager = new MediaTestManager;
function TestNoCORS(test, token)
{
var token = token + "_nocors";
manager.started(token);
var v = document.createElement("video");
v.addEventListener("encrypted", function(ev) {
is(ev.initDataType, "", "initDataType should be empty for CORS cross-origin media");
is(ev.initData, null, "initData should be null for CORS cross-origin media");
manager.finished(token);
});
v.addEventListener("error", function() {
ok(false, "Should not receive error loading cross-origin media without crossorigin attribute");
});
v.src = test.uri;
}
function TestCORSFailure(test, token)
{
var token = token + "_corsfail";
manager.started(token);
var v = document.createElement("video");
v.crossOrigin = true;
v.addEventListener("error", function(ev) {
ok(true, "Should get error loading cross-origin media");
manager.finished(token);
});
v.addEventListener("encrypted", function() {
ok(false, "Should not receive encrypted event loading cross-origin media");
});
v.src = test.uri;
}
function TestCORSSuccess(test, token)
{
var token = token + "_corsok";
manager.started(token);
var v = document.createElement("video");
v.crossOrigin = true;
v.addEventListener("error", function(ev) {
ok(false, "Should not get error loading cross-origin media");
});
v.addEventListener("encrypted", function(ev) {
ok(ev.initData.byteLength > 0, "Should get encryption initData loading cross-origin media");
is(ev.initDataType, "cenc", "Should get correct encryption initDataType loading cross-origin media");
manager.finished(token);
});
v.src = test.uri;
}
function startTest(test, token)
{
test.uri = "http://test1.mochi.test:8888/tests/dom/media/test/" + test.name;
TestNoCORS(test, token);
TestCORSFailure(test, token);
test.uri = "http://test1.mochi.test:8888/tests/dom/media/test/allowed.sjs?" + test.name;
TestCORSSuccess(test, token);
}
function beginTest() {
manager.runTests(gEMETests.filter(t => t.crossOrigin), startTest);
}
var prefs = [
[ "media.mediasource.enabled", true ],
[ "media.mediasource.whitelist", false ],
[ "media.mediasource.mp4.enabled", true ],
];
if (/Linux/.test(navigator.userAgent) ||
!document.createElement('video').canPlayType("video/mp4")) {
// XXX remove once we have mp4 PlatformDecoderModules on all platforms.
prefs.push([ "media.fragmented-mp4.exposed", true ]);
prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
</script>
</pre>
</body>
</html>

View File

@ -40,17 +40,19 @@ function TestSetMediaKeys(test, token)
var v = document.createElement("video");
// XXX the encrypted event should never fire here after bug 1134434
v.addEventListener("encrypted", function() {
DoSetMediaKeys(v)
ok(false, token + " should not fire encrypted event");
});
.then(function() {
ok(false, token + " expected setMediaKeys to fail.");
manager.finished(token);
}, function(err) {
is(err.name, "NotSupportedError", token + " should return correct error");
manager.finished(token);
});
var loadedMetadata = false;
v.addEventListener("loadedmetadata", function() {
loadedMetadata = true;
});
v.addEventListener("error", function() {
ok(true, token + " expected error event");
ok(loadedMetadata, token + " expected loadedmetadata to have fired");
manager.finished(token);
});
v.src = test.name;

View File

@ -244,6 +244,9 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
if (NS_WARN_IF(!stsThread)) {
return;
}
// XXXnsm, Fix for Bug 1141332 means that if we decide to make this
// streaming at some point, we'll need a different solution to that bug.
rv = NS_AsyncCopy(body, responseBody, stsThread, NS_ASYNCCOPY_VIA_READSEGMENTS, 4096,
RespondWithCopyComplete, closure.forget());
if (NS_WARN_IF(NS_FAILED(rv))) {

View File

@ -0,0 +1,17 @@
function handleRequest(request, response) {
// The string "hello" repeated 10 times followed by newline. Compressed using gzip.
var bytes = [0x1f, 0x8b, 0x08, 0x08, 0x4d, 0xe2, 0xf9, 0x54, 0x00, 0x03, 0x68,
0x65, 0x6c, 0x6c, 0x6f, 0x00, 0xcb, 0x48, 0xcd, 0xc9, 0xc9, 0xcf,
0x20, 0x85, 0xe0, 0x02, 0x00, 0xf5, 0x4b, 0x38, 0xcf, 0x33, 0x00,
0x00, 0x00];
response.setHeader("Content-Encoding", "gzip", false);
response.setHeader("Content-Length", "" + bytes.length, false);
response.setHeader("Content-Type", "text/plain", false);
var bos = Components.classes["@mozilla.org/binaryoutputstream;1"]
.createInstance(Components.interfaces.nsIBinaryOutputStream);
bos.setOutputStream(response.bodyOutputStream);
bos.writeByteArray(bytes, bytes.length);
}

View File

@ -71,3 +71,35 @@ fetch('headers.txt', function(xhr) {
my_ok(xhr.responseText == "1", "request header checks should have passed");
finish();
}, null, [["X-Test1", "header1"], ["X-Test2", "header2"]]);
var expectedUncompressedResponse = "";
for (var i = 0; i < 10; ++i) {
expectedUncompressedResponse += "hello";
}
expectedUncompressedResponse += "\n";
// ServiceWorker does not intercept, at which point the network request should
// be correctly decoded.
fetch('deliver-gzip.sjs', function(xhr) {
my_ok(xhr.status == 200, "network gzip load should be successful");
my_ok(xhr.responseText == expectedUncompressedResponse, "network gzip load should have synthesized response.");
my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "network Content-Encoding should be gzip.");
my_ok(xhr.getResponseHeader("Content-Length") == "35", "network Content-Length should be of original gzipped file.");
finish();
});
fetch('hello.gz', function(xhr) {
my_ok(xhr.status == 200, "gzip load should be successful");
my_ok(xhr.responseText == expectedUncompressedResponse, "gzip load should have synthesized response.");
my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "Content-Encoding should be gzip.");
my_ok(xhr.getResponseHeader("Content-Length") == "35", "Content-Length should be of original gzipped file.");
finish();
});
fetch('hello-after-extracting.gz', function(xhr) {
my_ok(xhr.status == 200, "gzip load should be successful");
my_ok(xhr.responseText == expectedUncompressedResponse, "gzip load should have synthesized response.");
my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "Content-Encoding should be gzip.");
my_ok(xhr.getResponseHeader("Content-Length") == "35", "Content-Length should be of original gzipped file.");
finish();
});

View File

@ -96,4 +96,23 @@ onfetch = function(ev) {
new Response("check_intercepted_script();", {})
));
}
else if (ev.request.url.contains("deliver-gzip")) {
// Don't handle the request, this will make Necko perform a network request, at
// which point SetApplyConversion must be re-enabled, otherwise the request
// will fail.
return;
}
else if (ev.request.url.contains("hello.gz")) {
ev.respondWith(fetch("fetch/deliver-gzip.sjs"));
}
else if (ev.request.url.contains("hello-after-extracting.gz")) {
ev.respondWith(fetch("fetch/deliver-gzip.sjs").then(function(res) {
return res.text().then(function(body) {
return new Response(body, { status: res.status, statusText: res.statusText, headers: res.headers });
});
}));
}
}

View File

@ -25,6 +25,7 @@ support-files =
fetch/index.html
fetch/fetch_worker_script.js
fetch/fetch_tests.js
fetch/deliver-gzip.sjs
fetch/https/index.html
fetch/https/register.html
fetch/https/unregister.html

Some files were not shown because too many files have changed in this diff Show More