Merge mozilla-central to fx-team

This commit is contained in:
Carsten "Tomcat" Book 2015-07-03 12:40:05 +02:00
commit a6385dd7a1
136 changed files with 1804 additions and 927 deletions

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="50b6104ed03f87f1207695f861ba475bd79aaf71"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="50b6104ed03f87f1207695f861ba475bd79aaf71"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<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="50b6104ed03f87f1207695f861ba475bd79aaf71"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="87a2d8ab9248540910e56921654367b78a587095"/>

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="50b6104ed03f87f1207695f861ba475bd79aaf71"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="49192a4e48d080e44a0d66f059e6897f07cf67f8"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="50b6104ed03f87f1207695f861ba475bd79aaf71"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="50b6104ed03f87f1207695f861ba475bd79aaf71"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<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="50b6104ed03f87f1207695f861ba475bd79aaf71"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="87a2d8ab9248540910e56921654367b78a587095"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="50b6104ed03f87f1207695f861ba475bd79aaf71"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "50b6104ed03f87f1207695f861ba475bd79aaf71",
"git_revision": "c2438f746a3236398735202c0d79fab28cd019ae",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "7f044c57386bfd12a5c2d52bcfccde76e11d6b68",
"revision": "0312e33fddf5c92fad3933fd16f39075d6e6190f",
"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="50b6104ed03f87f1207695f861ba475bd79aaf71"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="49192a4e48d080e44a0d66f059e6897f07cf67f8"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="50b6104ed03f87f1207695f861ba475bd79aaf71"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="c2438f746a3236398735202c0d79fab28cd019ae"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -229,6 +229,12 @@ Blob::IsFile() const
return mImpl->IsFile();
}
bool
Blob::IsDirectory() const
{
return mImpl->IsDirectory();
}
const nsTArray<nsRefPtr<BlobImpl>>*
Blob::GetSubBlobImpls() const
{
@ -421,10 +427,11 @@ File::Create(nsISupports* aParent, BlobImpl* aImpl)
/* static */ already_AddRefed<File>
File::Create(nsISupports* aParent, const nsAString& aName,
const nsAString& aContentType, uint64_t aLength,
int64_t aLastModifiedDate)
int64_t aLastModifiedDate, BlobDirState aDirState)
{
nsRefPtr<File> file = new File(aParent,
new BlobImplBase(aName, aContentType, aLength, aLastModifiedDate));
new BlobImplBase(aName, aContentType, aLength, aLastModifiedDate,
aDirState));
return file.forget();
}
@ -1056,6 +1063,17 @@ BlobImplFile::SetPath(const nsAString& aPath)
mPath = aPath;
}
void
BlobImplFile::LookupAndCacheIsDirectory()
{
MOZ_ASSERT(mIsFile,
"This should only be called when this object has been created "
"from an nsIFile to note that the nsIFile is a directory");
bool isDir;
mFile->IsDirectory(&isDir);
mDirState = isDir ? BlobDirState::eIsDir : BlobDirState::eIsNotDir;
}
////////////////////////////////////////////////////////////////////////////
// BlobImplMemory implementation

View File

@ -51,6 +51,18 @@ class BlobImpl;
class File;
class OwningArrayBufferOrArrayBufferViewOrBlobOrString;
/**
* Used to indicate when a Blob/BlobImpl that was created from an nsIFile
* (when IsFile() will return true) was from an nsIFile for which
* nsIFile::IsDirectory() returned true. This is a tri-state to enable us to
* assert that the state is always set when callers request it.
*/
enum BlobDirState : uint32_t {
eIsDir,
eIsNotDir,
eUnknownIfDir
};
class Blob : public nsIDOMBlob
, public nsIXHRSendable
, public nsIMutable
@ -95,6 +107,12 @@ public:
bool IsFile() const;
/**
* This may return true if the Blob was created from an nsIFile that is a
* directory.
*/
bool IsDirectory() const;
const nsTArray<nsRefPtr<BlobImpl>>* GetSubBlobImpls() const;
// This method returns null if this Blob is not a File; it returns
@ -102,6 +120,9 @@ public:
// otherwise it returns a new File object with the same BlobImpl.
already_AddRefed<File> ToFile();
// XXXjwatt Consider having a ToDirectory() method. The need for a FileSystem
// object complicates that though.
// This method creates a new File object with the given name and the same
// BlobImpl.
already_AddRefed<File> ToFile(const nsAString& aName) const;
@ -184,7 +205,7 @@ public:
static already_AddRefed<File>
Create(nsISupports* aParent, const nsAString& aName,
const nsAString& aContentType, uint64_t aLength,
int64_t aLastModifiedDate);
int64_t aLastModifiedDate, BlobDirState aDirState);
static already_AddRefed<File>
Create(nsISupports* aParent, const nsAString& aName,
@ -342,7 +363,8 @@ public:
virtual void SetLazyData(const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
int64_t aLastModifiedDate) = 0;
int64_t aLastModifiedDate,
BlobDirState aDirState) = 0;
virtual bool IsMemoryFile() const = 0;
@ -352,6 +374,27 @@ public:
virtual bool IsFile() const = 0;
/**
* Called when this BlobImpl was created from an nsIFile in order to call
* nsIFile::IsDirectory() and cache the result so that when the BlobImpl is
* copied to another process that informaton is available.
* nsIFile::IsDirectory() does synchronous I/O, and BlobImpl objects may be
* created on the main thread or in a non-chrome process (where I/O is not
* allowed). Do not call this on a non-chrome process, and preferably do not
* call it on the main thread.
*
* Not all creators of BlobImplFile will call this method, in which case
* calling IsDirectory will MOZ_ASSERT.
*/
virtual void LookupAndCacheIsDirectory() = 0;
virtual bool IsDirectory() const = 0;
/**
* Prefer IsDirectory(). This exists to help consumer code pass on state from
* one BlobImpl when creating another.
*/
virtual BlobDirState GetDirState() const = 0;
// True if this implementation can be sent to other threads.
virtual bool MayBeClonedToOtherThreads() const
{
@ -368,9 +411,11 @@ class BlobImplBase : public BlobImpl
{
public:
BlobImplBase(const nsAString& aName, const nsAString& aContentType,
uint64_t aLength, int64_t aLastModifiedDate)
uint64_t aLength, int64_t aLastModifiedDate,
BlobDirState aDirState = BlobDirState::eUnknownIfDir)
: mIsFile(true)
, mImmutable(false)
, mDirState(aDirState)
, mContentType(aContentType)
, mName(aName)
, mStart(0)
@ -386,6 +431,7 @@ public:
uint64_t aLength)
: mIsFile(true)
, mImmutable(false)
, mDirState(BlobDirState::eUnknownIfDir)
, mContentType(aContentType)
, mName(aName)
, mStart(0)
@ -400,6 +446,7 @@ public:
BlobImplBase(const nsAString& aContentType, uint64_t aLength)
: mIsFile(false)
, mImmutable(false)
, mDirState(BlobDirState::eUnknownIfDir)
, mContentType(aContentType)
, mStart(0)
, mLength(aLength)
@ -414,6 +461,7 @@ public:
uint64_t aLength)
: mIsFile(false)
, mImmutable(false)
, mDirState(BlobDirState::eUnknownIfDir)
, mContentType(aContentType)
, mStart(aStart)
, mLength(aLength)
@ -485,7 +533,8 @@ public:
virtual void
SetLazyData(const nsAString& aName, const nsAString& aContentType,
uint64_t aLength, int64_t aLastModifiedDate) override
uint64_t aLength, int64_t aLastModifiedDate,
BlobDirState aDirState) override
{
NS_ASSERTION(aLength, "must have length");
@ -511,6 +560,27 @@ public:
return mIsFile;
}
virtual void LookupAndCacheIsDirectory() override
{
MOZ_ASSERT(false, "Why is this being called on a non-BlobImplFile?");
}
/**
* Returns true if the nsIFile that this object wraps is a directory.
*/
virtual bool IsDirectory() const override
{
MOZ_ASSERT(mDirState != BlobDirState::eUnknownIfDir,
"Must only be used by callers for whom the code paths are "
"know to call LookupAndCacheIsDirectory()");
return mDirState == BlobDirState::eIsDir;
}
virtual BlobDirState GetDirState() const override
{
return mDirState;
}
virtual bool IsStoredFile() const
{
return false;
@ -552,6 +622,7 @@ protected:
bool mIsFile;
bool mImmutable;
BlobDirState mDirState;
nsString mContentType;
nsString mName;
@ -579,7 +650,8 @@ public:
BlobImplMemory(void* aMemoryBuffer, uint64_t aLength, const nsAString& aName,
const nsAString& aContentType, int64_t aLastModifiedDate)
: BlobImplBase(aName, aContentType, aLength, aLastModifiedDate)
: BlobImplBase(aName, aContentType, aLength, aLastModifiedDate,
BlobDirState::eIsNotDir)
, mDataOwner(new DataOwner(aMemoryBuffer, aLength))
{
NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
@ -828,6 +900,8 @@ public:
void SetPath(const nsAString& aFullPath);
virtual void LookupAndCacheIsDirectory() override;
protected:
virtual ~BlobImplFile() {
if (mFile && mIsTemporary) {

View File

@ -2308,35 +2308,6 @@ Navigator::MayResolve(jsid aId)
return nameSpaceManager->LookupNavigatorName(name);
}
struct NavigatorNameEnumeratorClosure
{
NavigatorNameEnumeratorClosure(JSContext* aCx, JSObject* aWrapper,
nsTArray<nsString>& aNames)
: mCx(aCx),
mWrapper(aCx, aWrapper),
mNames(aNames)
{
}
JSContext* mCx;
JS::Rooted<JSObject*> mWrapper;
nsTArray<nsString>& mNames;
};
static PLDHashOperator
SaveNavigatorName(const nsAString& aName,
const nsGlobalNameStruct& aNameStruct,
void* aClosure)
{
NavigatorNameEnumeratorClosure* closure =
static_cast<NavigatorNameEnumeratorClosure*>(aClosure);
if (!aNameStruct.mConstructorEnabled ||
aNameStruct.mConstructorEnabled(closure->mCx, closure->mWrapper)) {
closure->mNames.AppendElement(aName);
}
return PL_DHASH_NEXT;
}
void
Navigator::GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& aNames,
ErrorResult& aRv)
@ -2348,8 +2319,14 @@ Navigator::GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& aNames,
return;
}
NavigatorNameEnumeratorClosure closure(aCx, GetWrapper(), aNames);
nameSpaceManager->EnumerateNavigatorNames(SaveNavigatorName, &closure);
JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
for (auto i = nameSpaceManager->NavigatorNameIter(); !i.Done(); i.Next()) {
const GlobalNameMapEntry* entry = i.Get();
if (!entry->mGlobalName.mConstructorEnabled ||
entry->mGlobalName.mConstructorEnabled(aCx, wrapper)) {
aNames.AppendElement(entry->mKey);
}
}
}
JSObject*

View File

@ -4315,40 +4315,6 @@ nsGlobalWindow::MayResolve(jsid aId)
return name_struct;
}
struct GlobalNameEnumeratorClosure
{
GlobalNameEnumeratorClosure(JSContext* aCx, nsGlobalWindow* aWindow,
nsTArray<nsString>& aNames)
: mCx(aCx),
mWindow(aWindow),
mWrapper(aCx, aWindow->GetWrapper()),
mNames(aNames)
{
}
JSContext* mCx;
nsGlobalWindow* mWindow;
JS::Rooted<JSObject*> mWrapper;
nsTArray<nsString>& mNames;
};
static PLDHashOperator
EnumerateGlobalName(const nsAString& aName,
const nsGlobalNameStruct& aNameStruct,
void* aClosure)
{
GlobalNameEnumeratorClosure* closure =
static_cast<GlobalNameEnumeratorClosure*>(aClosure);
if (nsWindowSH::NameStructEnabled(closure->mCx, closure->mWindow, aName,
aNameStruct) &&
(!aNameStruct.mConstructorEnabled ||
aNameStruct.mConstructorEnabled(closure->mCx, closure->mWrapper))) {
closure->mNames.AppendElement(aName);
}
return PL_DHASH_NEXT;
}
void
nsGlobalWindow::GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& aNames,
ErrorResult& aRv)
@ -4358,8 +4324,16 @@ nsGlobalWindow::GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& aNames,
nsScriptNameSpaceManager* nameSpaceManager = GetNameSpaceManager();
if (nameSpaceManager) {
GlobalNameEnumeratorClosure closure(aCx, this, aNames);
nameSpaceManager->EnumerateGlobalNames(EnumerateGlobalName, &closure);
JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
for (auto i = nameSpaceManager->GlobalNameIter(); !i.Done(); i.Next()) {
const GlobalNameMapEntry* entry = i.Get();
if (nsWindowSH::NameStructEnabled(aCx, this, entry->mKey,
entry->mGlobalName) &&
(!entry->mGlobalName.mConstructorEnabled ||
entry->mGlobalName.mConstructorEnabled(aCx, wrapper))) {
aNames.AppendElement(entry->mKey);
}
}
}
}

View File

@ -35,23 +35,6 @@
using namespace mozilla;
// Our extended PLDHashEntryHdr
class GlobalNameMapEntry : public PLDHashEntryHdr
{
public:
// Our hash table ops don't care about the order of these members
nsString mKey;
nsGlobalNameStruct mGlobalName;
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) {
// Measurement of the following members may be added later if DMD finds it
// is worthwhile:
// - mGlobalName
return mKey.SizeOfExcludingThisMustBeUnshared(aMallocSizeOf);
}
};
static PLDHashNumber
GlobalNameHashHashKey(PLDHashTable *table, const void *key)
{
@ -742,36 +725,6 @@ nsScriptNameSpaceManager::RegisterNavigatorDOMConstructor(
}
}
struct NameClosure
{
nsScriptNameSpaceManager::NameEnumerator enumerator;
void* closure;
};
static PLDHashOperator
EnumerateName(PLDHashTable*, PLDHashEntryHdr *hdr, uint32_t, void* aClosure)
{
GlobalNameMapEntry *entry = static_cast<GlobalNameMapEntry *>(hdr);
NameClosure* closure = static_cast<NameClosure*>(aClosure);
return closure->enumerator(entry->mKey, entry->mGlobalName, closure->closure);
}
void
nsScriptNameSpaceManager::EnumerateGlobalNames(NameEnumerator aEnumerator,
void* aClosure)
{
NameClosure closure = { aEnumerator, aClosure };
PL_DHashTableEnumerate(&mGlobalNames, EnumerateName, &closure);
}
void
nsScriptNameSpaceManager::EnumerateNavigatorNames(NameEnumerator aEnumerator,
void* aClosure)
{
NameClosure closure = { aEnumerator, aClosure };
PL_DHashTableEnumerate(&mNavigatorNames, EnumerateName, &closure);
}
static size_t
SizeOfEntryExcludingThis(PLDHashEntryHdr *aHdr, MallocSizeOf aMallocSizeOf,
void *aArg)

View File

@ -79,6 +79,21 @@ struct nsGlobalNameStruct
mozilla::dom::ConstructorEnabled* mConstructorEnabled;
};
class GlobalNameMapEntry : public PLDHashEntryHdr
{
public:
// Our hash table ops don't care about the order of these members.
nsString mKey;
nsGlobalNameStruct mGlobalName;
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
// Measurement of the following members may be added later if DMD finds it
// is worthwhile:
// - mGlobalName
return mKey.SizeOfExcludingThisMustBeUnshared(aMallocSizeOf);
}
};
class nsICategoryManager;
class nsScriptNameSpaceManager : public nsIObserver,
@ -169,10 +184,27 @@ public:
const nsGlobalNameStruct& aGlobalNameStruct,
void* aClosure);
void EnumerateGlobalNames(NameEnumerator aEnumerator,
void* aClosure);
void EnumerateNavigatorNames(NameEnumerator aEnumerator,
void* aClosure);
class NameIterator : public PLDHashTable::Iterator
{
public:
typedef PLDHashTable::Iterator Base;
explicit NameIterator(PLDHashTable* aTable) : Base(aTable) {}
NameIterator(NameIterator&& aOther) : Base(mozilla::Move(aOther.mTable)) {}
const GlobalNameMapEntry* Get() const
{
return static_cast<const GlobalNameMapEntry*>(Base::Get());
}
private:
NameIterator() = delete;
NameIterator(const NameIterator&) = delete;
NameIterator& operator=(const NameIterator&) = delete;
NameIterator& operator=(const NameIterator&&) = delete;
};
NameIterator GlobalNameIter() { return NameIterator(&mGlobalNames); }
NameIterator NavigatorNameIter() { return NameIterator(&mNavigatorNames); }
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);

View File

@ -77,3 +77,44 @@ function reportErrorAndQuit(e) {
ok(false, "handleError was called : " + e.target.error.name);
devicestorage_cleanup();
}
function createTestFiles(storage, paths) {
function createTestFile(path) {
return new Promise(function(resolve, reject) {
function addNamed() {
var req = storage.addNamed(createRandomBlob("image/png"), path);
req.onsuccess = function() {
ok(true, path + " was created.");
resolve();
};
req.onerror = function(e) {
ok(false, "Failed to create " + path + ': ' + e.target.error.name);
reject();
};
}
// Bug 980136. Check if the file exists before we create.
var req = storage.get(path);
req.onsuccess = function() {
ok(true, path + " exists. Do not need to create.");
resolve();
};
req.onerror = function(e) {
ok(true, path + " does not exists: " + e.target.error.name);
addNamed();
};
});
}
var arr = [];
paths.forEach(function(path) {
arr.push(createTestFile(path));
});
return Promise.all(arr);
}

View File

@ -31,6 +31,7 @@ skip-if = (toolkit != 'gonk')
[test_fs_basic.html]
[test_fs_createDirectory.html]
[test_fs_get.html]
[test_fs_getFilesAndDirectories.html]
[test_fs_remove.html]
[test_fs_createFile.html]
[test_fs_appendFile.html]

View File

@ -0,0 +1,97 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html> <!--
https://bugzilla.mozilla.org/show_bug.cgi?id=XXX
-->
<head>
<title>Test Directory#getFilesAndDirectories of the FileSystem API for device storage</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="devicestorage_common.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=XXX">Mozilla Bug XXX</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
devicestorage_setup();
SimpleTest.requestCompleteLog();
// The root directory object.
var gRoot = null;
function checkContents1(contents) {
var expected = {
"sub2": "/sub",
"sub3": "/sub",
"a.png": "/sub",
"b.png": "/sub",
};
is(contents.length, Object.keys(expected).length,
"The sub-directory should contain four children");
var sub2;
for (var child of contents) {
if (child.name in expected) {
ok(true, "Found '" + child.name + "' in /sub");
if (child.name == "sub2") {
sub2 = child;
}
} else {
ok(false, "Did not expect '" + child.name + "' in /sub");
}
delete expected[child.name];
}
// 'expected' should now be "empty"
for (var missing in Object.keys(expected)) {
ok(false, "Expected '" + missing.name + "' in /sub");
}
sub2.getFilesAndDirectories().then(checkContents2, handleError);
}
function checkContents2(contents) {
is(contents[0].name, "c.png", "'sub2' should contain 'c.png'");
devicestorage_cleanup();
}
function handleError(e) {
ok(false, "Should not arrive at handleError! Error: " + e.name);
devicestorage_cleanup();
}
var gStorage = navigator.getDeviceStorage("pictures");
ok(gStorage, "Should have gotten a storage.");
function runTests() {
gStorage.getRoot().then(function(rootDir) {
gRoot = rootDir;
return rootDir.get("sub");
}).then(function(subDir) {
return subDir.getFilesAndDirectories();
}).then(checkContents1).catch(handleError);
}
createTestFiles(gStorage, ["sub/a.png", "sub/b.png", "sub/sub2/c.png", "sub/sub3/d.png"]).then(function() {
runTests();
}, function() {
ok(false, "Failed to created test files.");
devicestorage_cleanup();
});
</script>
</pre>
</body>
</html>

View File

@ -88,55 +88,9 @@ let gTestCases = [
}
];
function createTestFiles(storage, callback) {
function createTestFile(path) {
return new Promise(function(resolve, reject) {
function addNamed() {
var req = storage.addNamed(createRandomBlob("image/png"), path);
req.onsuccess = function() {
ok(true, path + " was created.");
resolve();
};
req.onerror = function(e) {
ok(false, "Failed to create " + path + ': ' + e.target.error.name);
reject();
};
}
// Bug 980136. Check if the file exists before we create.
var req = storage.get(path);
req.onsuccess = function() {
ok(true, path + " exists. Do not need to create.");
resolve();
};
req.onerror = function(e) {
ok(true, path + " does not exists: " + e.target.error.name);
addNamed();
};
});
}
let arr = [];
["sub1/sub2/a.png", "sub/b.png"].forEach(function(path) {
arr.push(createTestFile(path));
});
Promise.all(arr).then(function() {
callback();
}, function() {
ok(false, "Failed to created test files.");
devicestorage_cleanup();
});
}
function runTest() {
function runNextTests() {
gTestCount = 0;
createTestFiles(gStorage, function() {
function runTests() {
function cbError(e) {
ok(false, "Should not arrive at cbError! Error: " + e.name);
devicestorage_cleanup();
@ -165,6 +119,12 @@ function runTest() {
devicestorage_cleanup();
});
}, cbError);
};
createTestFiles(gStorage, ["sub1/sub2/a.png", "sub/b.png"]).then(function() {
runTests();
}, function() {
ok(false, "Failed to created test files.");
devicestorage_cleanup();
});
}
@ -192,7 +152,7 @@ function testNextRemove() {
if (gRemoveDeep) {
// Test "remove" after "removeDeep".
gRemoveDeep = false;
runTest();
runNextTests();
return;
}
@ -206,7 +166,7 @@ ok(gStorage, "Should have gotten a storage.");
// Test "removeDeep" first.
gRemoveDeep = true;
runTest();
runNextTests();
</script>
</pre>

View File

@ -82,12 +82,40 @@ ContentEventHandler::InitCommon()
return NS_ERROR_NOT_AVAILABLE;
}
// This shell doesn't support selection.
if (NS_WARN_IF(!mSelection->RangeCount())) {
return NS_ERROR_NOT_AVAILABLE;
}
mFirstSelectedRange = mSelection->GetRangeAt(0);
if (!mSelection->RangeCount()) {
// If there is no selection range, we should compute the selection root
// from ancestor limiter or root content of the document.
rv = mSelection->GetAncestorLimiter(getter_AddRefs(mRootContent));
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_FAILURE;
}
if (!mRootContent) {
mRootContent = mPresShell->GetDocument()->GetRootElement();
if (NS_WARN_IF(!mRootContent)) {
return NS_ERROR_NOT_AVAILABLE;
}
}
// Assume that there is selection at beginning of the root content.
rv = nsRange::CreateRange(mRootContent, 0, mRootContent, 0,
getter_AddRefs(mFirstSelectedRange));
if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!mFirstSelectedRange)) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
mFirstSelectedRange = mSelection->GetRangeAt(0);
if (NS_WARN_IF(!mFirstSelectedRange)) {
return NS_ERROR_UNEXPECTED;
}
// If there is a selection, we should retrieve the selection root from
// the range since when the window is inactivated, the ancestor limiter
// of mSelection was cleared by blur event handler of nsEditor but the
// selection range still keeps storing the nodes. If the active element of
// the deactive window is <input> or <textarea>, we can compute the selection
// root from them.
nsINode* startNode = mFirstSelectedRange->GetStartParent();
NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
nsINode* endNode = mFirstSelectedRange->GetEndParent();

View File

@ -842,21 +842,6 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
NS_ASSERTION(selectedText.mSucceeded, "Failed to get selected text");
compositionEvent->mData = selectedText.mReply.mString;
}
// through to compositionend handling
case NS_COMPOSITION_END:
case NS_COMPOSITION_CHANGE:
case NS_COMPOSITION_COMMIT_AS_IS:
case NS_COMPOSITION_COMMIT:
{
WidgetCompositionEvent* compositionEvent = aEvent->AsCompositionEvent();
if (IsTargetCrossProcess(compositionEvent)) {
// Will not be handled locally, remote the event
if (GetCrossProcessTarget()->SendCompositionEvent(*compositionEvent)) {
// Cancel local dispatching
aEvent->mFlags.mPropagationStopped = true;
}
}
}
break;
}
return NS_OK;

View File

@ -1125,16 +1125,20 @@ IMEStateManager::DispatchCompositionEvent(
EventDispatchingCallback* aCallBack,
bool aIsSynthesized)
{
nsRefPtr<TabParent> tabParent =
aEventTargetNode->IsContent() ?
TabParent::GetFrom(aEventTargetNode->AsContent()) : nullptr;
MOZ_LOG(sISMLog, LogLevel::Info,
("ISM: IMEStateManager::DispatchCompositionEvent(aNode=0x%p, "
"aPresContext=0x%p, aCompositionEvent={ message=%s, "
"mFlags={ mIsTrusted=%s, mPropagationStopped=%s } }, "
"aIsSynthesized=%s)",
"aIsSynthesized=%s), tabParent=%p",
aEventTargetNode, aPresContext,
GetEventMessageName(aCompositionEvent->message),
GetBoolName(aCompositionEvent->mFlags.mIsTrusted),
GetBoolName(aCompositionEvent->mFlags.mPropagationStopped),
GetBoolName(aIsSynthesized)));
GetBoolName(aIsSynthesized), tabParent.get()));
if (!aCompositionEvent->mFlags.mIsTrusted ||
aCompositionEvent->mFlags.mPropagationStopped) {
@ -1159,7 +1163,8 @@ IMEStateManager::DispatchCompositionEvent(
"adding new TextComposition to the array"));
MOZ_ASSERT(aCompositionEvent->message == NS_COMPOSITION_START);
composition =
new TextComposition(aPresContext, aEventTargetNode, aCompositionEvent);
new TextComposition(aPresContext, aEventTargetNode, tabParent,
aCompositionEvent);
sTextCompositions->AppendElement(composition);
}
#ifdef DEBUG
@ -1203,7 +1208,7 @@ IMEStateManager::DispatchCompositionEvent(
// static
void
IMEStateManager::OnCompositionEventDiscarded(
const WidgetCompositionEvent* aCompositionEvent)
WidgetCompositionEvent* aCompositionEvent)
{
// Note that this method is never called for synthesized events for emulating
// commit or cancel composition.

View File

@ -157,7 +157,7 @@ public:
* to dispatch events.
*/
static void OnCompositionEventDiscarded(
const WidgetCompositionEvent* aCompositionEvent);
WidgetCompositionEvent* aCompositionEvent);
/**
* Get TextComposition from widget.

View File

@ -17,6 +17,8 @@
#include "mozilla/Preferences.h"
#include "mozilla/TextComposition.h"
#include "mozilla/TextEvents.h"
#include "mozilla/unused.h"
#include "mozilla/dom/TabParent.h"
using namespace mozilla::widget;
@ -30,9 +32,11 @@ namespace mozilla {
TextComposition::TextComposition(nsPresContext* aPresContext,
nsINode* aNode,
TabParent* aTabParent,
WidgetCompositionEvent* aCompositionEvent)
: mPresContext(aPresContext)
, mNode(aNode)
, mTabParent(aTabParent)
, mNativeContext(
aCompositionEvent->widget->GetInputContext().mNativeIMEContext)
, mCompositionStartOffset(0)
@ -55,6 +59,7 @@ TextComposition::Destroy()
{
mPresContext = nullptr;
mNode = nullptr;
mTabParent = nullptr;
// TODO: If the editor is still alive and this is held by it, we should tell
// this being destroyed for cleaning up the stuff.
}
@ -77,6 +82,8 @@ bool
TextComposition::MaybeDispatchCompositionUpdate(
const WidgetCompositionEvent* aCompositionEvent)
{
MOZ_RELEASE_ASSERT(!mTabParent);
if (!IsValidStateForComposition(aCompositionEvent->widget)) {
return false;
}
@ -95,6 +102,8 @@ TextComposition::CloneAndDispatchAs(
nsEventStatus* aStatus,
EventDispatchingCallback* aCallBack)
{
MOZ_RELEASE_ASSERT(!mTabParent);
MOZ_ASSERT(IsValidStateForComposition(aCompositionEvent->widget),
"Should be called only when it's safe to dispatch an event");
@ -118,7 +127,7 @@ TextComposition::CloneAndDispatchAs(
void
TextComposition::OnCompositionEventDiscarded(
const WidgetCompositionEvent* aCompositionEvent)
WidgetCompositionEvent* aCompositionEvent)
{
// Note that this method is never called for synthesized events for emulating
// commit or cancel composition.
@ -126,6 +135,11 @@ TextComposition::OnCompositionEventDiscarded(
MOZ_ASSERT(aCompositionEvent->mFlags.mIsTrusted,
"Shouldn't be called with untrusted event");
if (mTabParent) {
// The composition event should be discarded in the child process too.
unused << mTabParent->SendCompositionEvent(*aCompositionEvent);
}
// XXX If composition events are discarded, should we dispatch them with
// runnable event? However, even if we do so, it might make native IME
// confused due to async modification. Especially when native IME is
@ -198,6 +212,21 @@ TextComposition::DispatchCompositionEvent(
EventDispatchingCallback* aCallBack,
bool aIsSynthesized)
{
// If the content is a container of TabParent, composition should be in the
// remote process.
if (mTabParent) {
unused << mTabParent->SendCompositionEvent(*aCompositionEvent);
aCompositionEvent->mFlags.mPropagationStopped = true;
if (aCompositionEvent->CausesDOMTextEvent()) {
mLastData = aCompositionEvent->mData;
// Although, the composition event hasn't been actually handled yet,
// emulate an editor to be handling the composition event.
EditorWillHandleCompositionChangeEvent(aCompositionEvent);
EditorDidHandleCompositionChangeEvent();
}
return;
}
if (!mAllowControlCharacters) {
RemoveControlCharactersFrom(aCompositionEvent->mData,
aCompositionEvent->mRanges);
@ -349,6 +378,8 @@ void
TextComposition::NotityUpdateComposition(
const WidgetCompositionEvent* aCompositionEvent)
{
MOZ_RELEASE_ASSERT(!mTabParent);
nsEventStatus status;
// When compositon start, notify the rect of first offset character.
@ -465,6 +496,8 @@ TextComposition::EditorWillHandleCompositionChangeEvent(
void
TextComposition::OnEditorDestroyed()
{
MOZ_RELEASE_ASSERT(!mTabParent);
MOZ_ASSERT(!mIsEditorHandlingEvent,
"The editor should have stopped listening events");
nsCOMPtr<nsIWidget> widget = GetWidget();
@ -487,6 +520,8 @@ TextComposition::EditorDidHandleCompositionChangeEvent()
void
TextComposition::StartHandlingComposition(nsIEditor* aEditor)
{
MOZ_RELEASE_ASSERT(!mTabParent);
MOZ_ASSERT(!HasEditor(), "There is a handling editor already");
mEditorWeak = do_GetWeakReference(aEditor);
}
@ -494,6 +529,8 @@ TextComposition::StartHandlingComposition(nsIEditor* aEditor)
void
TextComposition::EndHandlingComposition(nsIEditor* aEditor)
{
MOZ_RELEASE_ASSERT(!mTabParent);
#ifdef DEBUG
nsCOMPtr<nsIEditor> editor = GetEditor();
MOZ_ASSERT(editor == aEditor, "Another editor handled the composition?");

View File

@ -17,6 +17,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/EventForwards.h"
#include "mozilla/TextRange.h"
#include "mozilla/dom/TabParent.h"
class nsIEditor;
@ -38,8 +39,11 @@ class TextComposition final
NS_INLINE_DECL_REFCOUNTING(TextComposition)
public:
typedef dom::TabParent TabParent;
TextComposition(nsPresContext* aPresContext,
nsINode* aNode,
TabParent* aTabParent,
WidgetCompositionEvent* aCompositionEvent);
bool Destroyed() const { return !mPresContext; }
@ -173,6 +177,7 @@ private:
// this instance.
nsPresContext* mPresContext;
nsCOMPtr<nsINode> mNode;
nsRefPtr<TabParent> mTabParent;
// This is the clause and caret range information which is managed by
// the focused editor. This may be null if there is no clauses or caret.
@ -314,8 +319,7 @@ private:
* compositionupdate, compositionend or compositionchange event due to not
* safe to dispatch event.
*/
void OnCompositionEventDiscarded(
const WidgetCompositionEvent* aCompositionEvent);
void OnCompositionEventDiscarded(WidgetCompositionEvent* aCompositionEvent);
/**
* Calculate composition offset then notify composition update to widget

View File

@ -9,6 +9,7 @@
#include "CreateDirectoryTask.h"
#include "CreateFileTask.h"
#include "FileSystemPermissionRequest.h"
#include "GetDirectoryListingTask.h"
#include "GetFileOrDirectoryTask.h"
#include "RemoveTask.h"
@ -99,7 +100,7 @@ Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions,
ErrorResult& aRv)
{
nsresult error = NS_OK;
nsString realPath;
nsAutoString realPath;
nsRefPtr<Blob> blobData;
InfallibleTArray<uint8_t> arrayData;
bool replace = (aOptions.mIfExists == CreateIfExistsMode::Replace);
@ -142,7 +143,7 @@ already_AddRefed<Promise>
Directory::CreateDirectory(const nsAString& aPath, ErrorResult& aRv)
{
nsresult error = NS_OK;
nsString realPath;
nsAutoString realPath;
if (!DOMPathToRealPath(aPath, realPath)) {
error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
}
@ -160,7 +161,7 @@ already_AddRefed<Promise>
Directory::Get(const nsAString& aPath, ErrorResult& aRv)
{
nsresult error = NS_OK;
nsString realPath;
nsAutoString realPath;
if (!DOMPathToRealPath(aPath, realPath)) {
error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
}
@ -191,7 +192,7 @@ Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive,
ErrorResult& aRv)
{
nsresult error = NS_OK;
nsString realPath;
nsAutoString realPath;
nsRefPtr<BlobImpl> blob;
// Check and get the target path.
@ -222,6 +223,34 @@ Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive,
return task->GetPromise();
}
void
Directory::GetPath(nsAString& aRetval) const
{
if (mPath.IsEmpty()) {
// The Directory ctor removes any trailing '/'; this is the root directory.
aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR);
} else {
aRetval = Substring(mPath, 0,
mPath.RFindChar(FileSystemUtils::kSeparatorChar) + 1);
}
}
already_AddRefed<Promise>
Directory::GetFilesAndDirectories()
{
nsresult error = NS_OK;
nsString realPath;
ErrorResult rv;
nsRefPtr<GetDirectoryListingTask> task =
new GetDirectoryListingTask(mFileSystem, mPath, rv);
if (NS_WARN_IF(rv.Failed())) {
return nullptr;
}
task->SetError(error);
FileSystemPermissionRequest::RequestForTask(task);
return task->GetPromise();
}
FileSystemBase*
Directory::GetFileSystem() const
{

View File

@ -77,6 +77,14 @@ public:
already_AddRefed<Promise>
RemoveDeep(const StringOrFileOrDirectory& aPath, ErrorResult& aRv);
// From https://microsoftedge.github.io/directory-upload/proposal.html#directory-interface :
void
GetPath(nsAString& aRetval) const;
already_AddRefed<Promise>
GetFilesAndDirectories();
// =========== End WebIDL bindings.============
FileSystemBase*

View File

@ -7,6 +7,7 @@
#include "CreateDirectoryTask.h"
#include "CreateFileTask.h"
#include "GetDirectoryListingTask.h"
#include "GetFileOrDirectoryTask.h"
#include "RemoveTask.h"
@ -42,6 +43,7 @@ FileSystemRequestParent::Dispatch(ContentParent* aParent,
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateDirectory)
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateFile)
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetDirectoryListing)
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(GetFileOrDirectory)
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(Remove)

View File

@ -0,0 +1,249 @@
/* -*- 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 "GetDirectoryListingTask.h"
#include "js/Value.h"
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/DOMError.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ipc/BlobChild.h"
#include "mozilla/dom/ipc/BlobParent.h"
#include "nsIFile.h"
#include "nsStringGlue.h"
namespace mozilla {
namespace dom {
GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem,
const nsAString& aTargetPath,
ErrorResult& aRv)
: FileSystemTaskBase(aFileSystem)
, mTargetRealPath(aTargetPath)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
nsCOMPtr<nsIGlobalObject> globalObject =
do_QueryInterface(aFileSystem->GetWindow());
if (!globalObject) {
return;
}
mPromise = Promise::Create(globalObject, aRv);
}
GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem,
const FileSystemGetDirectoryListingParams& aParam,
FileSystemRequestParent* aParent)
: FileSystemTaskBase(aFileSystem, aParam, aParent)
{
MOZ_ASSERT(FileSystemUtils::IsParentProcess(),
"Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
mTargetRealPath = aParam.realPath();
}
GetDirectoryListingTask::~GetDirectoryListingTask()
{
MOZ_ASSERT(!mPromise || NS_IsMainThread(),
"mPromise should be released on main thread!");
}
already_AddRefed<Promise>
GetDirectoryListingTask::GetPromise()
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
return nsRefPtr<Promise>(mPromise).forget();
}
FileSystemParams
GetDirectoryListingTask::GetRequestParams(const nsString& aFileSystem) const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
return FileSystemGetDirectoryListingParams(aFileSystem, mTargetRealPath);
}
FileSystemResponseValue
GetDirectoryListingTask::GetSuccessRequestResult() const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
InfallibleTArray<PBlobParent*> blobs;
for (unsigned i = 0; i < mTargetBlobImpls.Length(); i++) {
BlobParent* blobParent = GetBlobParent(mTargetBlobImpls[i]);
if (blobParent) {
blobs.AppendElement(blobParent);
}
}
FileSystemDirectoryListingResponse response;
response.blobsParent().SwapElements(blobs);
return response;
}
void
GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aValue.type() ==
FileSystemResponseValue::TFileSystemDirectoryListingResponse);
FileSystemDirectoryListingResponse r = aValue;
nsTArray<PBlobChild*>& blobs = r.blobsChild();
for (unsigned i = 0; i < blobs.Length(); i++) {
mTargetBlobImpls.AppendElement(static_cast<BlobChild*>(blobs[i])->GetBlobImpl());
}
}
nsresult
GetDirectoryListingTask::Work()
{
MOZ_ASSERT(FileSystemUtils::IsParentProcess(),
"Only call from parent process!");
MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!");
if (mFileSystem->IsShutdown()) {
return NS_ERROR_FAILURE;
}
// Whether we want to get the root directory.
bool getRoot = mTargetRealPath.IsEmpty();
nsCOMPtr<nsIFile> dir = mFileSystem->GetLocalFile(mTargetRealPath);
if (!dir) {
return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
}
bool exists;
nsresult rv = dir->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!exists) {
if (!getRoot) {
return NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
}
// If the root directory doesn't exit, create it.
rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0777);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
// Get isDirectory.
bool isDir;
rv = dir->IsDirectory(&isDir);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!isDir) {
return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
}
nsCOMPtr<nsISimpleEnumerator> entries;
rv = dir->GetDirectoryEntries(getter_AddRefs(entries));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
for (;;) {
bool hasMore = false;
if (NS_WARN_IF(NS_FAILED(entries->HasMoreElements(&hasMore))) || !hasMore) {
break;
}
nsCOMPtr<nsISupports> supp;
if (NS_WARN_IF(NS_FAILED(entries->GetNext(getter_AddRefs(supp))))) {
break;
}
nsCOMPtr<nsIFile> currFile = do_QueryInterface(supp);
MOZ_ASSERT(currFile);
bool isLink, isSpecial, isFile;
if (NS_WARN_IF(NS_FAILED(currFile->IsSymlink(&isLink)) ||
NS_FAILED(currFile->IsSpecial(&isSpecial))) ||
isLink || isSpecial) {
continue;
};
if (NS_WARN_IF(NS_FAILED(currFile->IsFile(&isFile)) ||
NS_FAILED(currFile->IsDirectory(&isDir))) ||
!(isFile || isDir)) {
continue;
}
BlobImplFile* impl = new BlobImplFile(currFile);
impl->LookupAndCacheIsDirectory();
mTargetBlobImpls.AppendElement(impl);
}
return NS_OK;
}
void
GetDirectoryListingTask::HandlerCallback()
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
if (mFileSystem->IsShutdown()) {
mPromise = nullptr;
return;
}
if (HasError()) {
nsRefPtr<DOMError> domError = new DOMError(mFileSystem->GetWindow(),
mErrorValue);
mPromise->MaybeRejectBrokenly(domError);
mPromise = nullptr;
return;
}
size_t count = mTargetBlobImpls.Length();
Sequence<OwningFileOrDirectory> listing;
if (!listing.SetLength(count, mozilla::fallible_t())) {
mPromise->MaybeReject(NS_ERROR_FAILURE);
mPromise = nullptr;
return;
}
for (unsigned i = 0; i < count; i++) {
if (mTargetBlobImpls[i]->IsDirectory()) {
nsAutoString name;
mTargetBlobImpls[i]->GetName(name);
nsAutoString path(mTargetRealPath);
path.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR);
path.Append(name);
#ifdef DEBUG
if (FileSystemUtils::IsParentProcess()) {
nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(path);
bool exist;
file->Exists(&exist);
MOZ_ASSERT(exist);
}
#endif
listing[i].SetAsDirectory() = new Directory(mFileSystem, path);
} else {
listing[i].SetAsFile() = File::Create(mFileSystem->GetWindow(), mTargetBlobImpls[i]);
}
}
mPromise->MaybeResolve(listing);
mPromise = nullptr;
}
void
GetDirectoryListingTask::GetPermissionAccessType(nsCString& aAccess) const
{
aAccess.AssignLiteral("read");
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,67 @@
/* -*- 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_GetDirectoryListing_h
#define mozilla_dom_GetDirectoryListing_h
#include "mozilla/dom/FileSystemTaskBase.h"
#include "mozilla/ErrorResult.h"
#include "nsAutoPtr.h"
namespace mozilla {
namespace dom {
class BlobImpl;
class GetDirectoryListingTask final
: public FileSystemTaskBase
{
public:
// If aDirectoryOnly is set, we should ensure that the target is a directory.
GetDirectoryListingTask(FileSystemBase* aFileSystem,
const nsAString& aTargetPath,
ErrorResult& aRv);
GetDirectoryListingTask(FileSystemBase* aFileSystem,
const FileSystemGetDirectoryListingParams& aParam,
FileSystemRequestParent* aParent);
virtual
~GetDirectoryListingTask();
already_AddRefed<Promise>
GetPromise();
virtual void
GetPermissionAccessType(nsCString& aAccess) const override;
protected:
virtual FileSystemParams
GetRequestParams(const nsString& aFileSystem) const override;
virtual FileSystemResponseValue
GetSuccessRequestResult() const override;
virtual void
SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
virtual nsresult
Work() override;
virtual void
HandlerCallback() override;
private:
nsRefPtr<Promise> mPromise;
nsString mTargetRealPath;
// We cannot store File or Directory objects bacause this object is created
// on a different thread and File and Directory are not thread-safe.
nsTArray<nsRefPtr<BlobImpl>> mTargetBlobImpls;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_GetDirectoryListing_h

View File

@ -20,6 +20,11 @@ struct FileSystemDirectoryResponse
nsString realPath;
};
struct FileSystemDirectoryListingResponse
{
PBlob[] blobs;
};
struct FileSystemErrorResponse
{
nsresult error;
@ -34,6 +39,7 @@ union FileSystemResponseValue
{
FileSystemBooleanResponse;
FileSystemDirectoryResponse;
FileSystemDirectoryListingResponse;
FileSystemFileResponse;
FileSystemErrorResponse;
};

View File

@ -23,6 +23,7 @@ UNIFIED_SOURCES += [
'FileSystemRequestParent.cpp',
'FileSystemTaskBase.cpp',
'FileSystemUtils.cpp',
'GetDirectoryListingTask.cpp',
'GetFileOrDirectoryTask.cpp',
'RemoveTask.cpp',
]

View File

@ -27,7 +27,7 @@ NS_NewHTMLExtAppElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
already_AddRefed<mozilla::dom::NodeInfo> aarni = ni.forget();
if (!permissionManager) {
return new HTMLUnknownElement(aarni);
return new mozilla::dom::HTMLUnknownElement(aarni);
}
uint32_t perm = nsIPermissionManager::UNKNOWN_ACTION;
@ -35,10 +35,10 @@ NS_NewHTMLExtAppElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
"external-app",
&perm);
if (perm != nsIPermissionManager::ALLOW_ACTION) {
return new HTMLUnknownElement(aarni);
return new mozilla::dom::HTMLUnknownElement(aarni);
}
return new HTMLExtAppElement(aarni);
return new mozilla::dom::HTMLExtAppElement(aarni);
}
namespace mozilla {

View File

@ -199,7 +199,7 @@ public:
}
mozilla::net::ReferrerPolicy
GetImageReferrerPolicy()
GetImageReferrerPolicy() override
{
return GetReferrerPolicy();
}

View File

@ -387,8 +387,8 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
iter->GetNext(getter_AddRefs(tmp));
nsCOMPtr<nsIDOMBlob> domBlob = do_QueryInterface(tmp);
NS_WARN_IF_FALSE(domBlob,
"Null file object from FilePicker's file enumerator?");
MOZ_ASSERT(domBlob,
"Null file object from FilePicker's file enumerator?");
if (domBlob) {
newFiles.AppendElement(static_cast<File*>(domBlob.get()));
}

View File

@ -217,7 +217,7 @@ public:
// Called by the media decoder and the video frame to get the
// ImageContainer containing the video data.
virtual VideoFrameContainer* GetVideoFrameContainer() final override;
B2G_ACL_EXPORT virtual VideoFrameContainer* GetVideoFrameContainer() final override;
layers::ImageContainer* GetImageContainer();
// Dispatch events

View File

@ -9,6 +9,7 @@
#include "IDBFileHandle.h"
#include "MainThreadUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/MetadataHelper.h"
#ifdef DEBUG

View File

@ -180,18 +180,26 @@ IDBFactory::CreateForWindow(nsPIDOMWindow* aWindow,
// static
nsresult
IDBFactory::CreateForChromeJS(JSContext* aCx,
JS::Handle<JSObject*> aOwningObject,
IDBFactory** aFactory)
IDBFactory::CreateForMainThreadJS(JSContext* aCx,
JS::Handle<JSObject*> aOwningObject,
IDBFactory** aFactory)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(nsContentUtils::IsCallerChrome());
nsAutoPtr<PrincipalInfo> principalInfo(
new PrincipalInfo(SystemPrincipalInfo()));
nsAutoPtr<PrincipalInfo> principalInfo(new PrincipalInfo());
nsIPrincipal* principal = nsContentUtils::ObjectPrincipal(aOwningObject);
MOZ_ASSERT(principal);
bool isSystem;
if (!AllowedForPrincipal(principal, &isSystem)) {
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
nsresult rv =
CreateForMainThreadJSInternal(aCx, aOwningObject, principalInfo, aFactory);
nsresult rv = PrincipalToPrincipalInfo(principal, principalInfo);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = CreateForMainThreadJSInternal(aCx, aOwningObject, principalInfo, aFactory);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}

View File

@ -86,9 +86,9 @@ public:
IDBFactory** aFactory);
static nsresult
CreateForChromeJS(JSContext* aCx,
JS::Handle<JSObject*> aOwningObject,
IDBFactory** aFactory);
CreateForMainThreadJS(JSContext* aCx,
JS::Handle<JSObject*> aOwningObject,
IDBFactory** aFactory);
static nsresult
CreateForDatastore(JSContext* aCx,

View File

@ -439,7 +439,8 @@ ResolveMysteryFile(BlobImpl* aImpl,
BlobChild* actor = ActorFromRemoteBlobImpl(aImpl);
if (actor) {
return actor->SetMysteryBlobInfo(aName, aContentType,
aSize, aLastModifiedDate);
aSize, aLastModifiedDate,
BlobDirState::eUnknownIfDir);
}
return true;
}

View File

@ -582,7 +582,6 @@ IndexedDatabaseManager::DefineIndexedDB(JSContext* aCx,
JS::Handle<JSObject*> aGlobal)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(nsContentUtils::IsCallerChrome(), "Only for chrome!");
MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL,
"Passed object is not a global object!");
@ -609,9 +608,9 @@ IndexedDatabaseManager::DefineIndexedDB(JSContext* aCx,
}
nsRefPtr<IDBFactory> factory;
if (NS_FAILED(IDBFactory::CreateForChromeJS(aCx,
aGlobal,
getter_AddRefs(factory)))) {
if (NS_FAILED(IDBFactory::CreateForMainThreadJS(aCx,
aGlobal,
getter_AddRefs(factory)))) {
return false;
}
@ -940,7 +939,7 @@ IndexedDatabaseManager::LoggingModePrefChangedCallback(
return;
}
bool useProfiler =
bool useProfiler =
#if defined(DEBUG) || defined(MOZ_ENABLE_PROFILER_SPS)
Preferences::GetBool(kPrefLoggingProfiler);
#if !defined(MOZ_ENABLE_PROFILER_SPS)

View File

@ -332,6 +332,8 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || (buildapp == 'mulet') # Bug 931116 # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.
[test_request_readyState.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_sandbox.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_setVersion.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
[test_setVersion_abort.html]

View File

@ -0,0 +1,101 @@
<!doctype html>
<html>
<head>
<title>indexedDB in JS Sandbox</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"></link>
</head>
<body>
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
// This runs inside a same-origin sandbox.
// The intent being to show that the data store is the same.
function storeValue() {
function createDB_inner() {
var op = indexedDB.open('db');
op.onupgradeneeded = e => {
var db = e.target.result;
db.createObjectStore('store');
};
return new Promise(resolve => {
op.onsuccess = e => resolve(e.target.result);
});
}
function add(k, v) {
return createDB_inner().then(db => {
var tx = db.transaction('store', 'readwrite');
var store = tx.objectStore('store');
var op = store.add(v, k);
return new Promise((resolve, reject) => {
op.onsuccess = e => resolve(e.target.result);
op.onerror = _ => reject(op.error);
tx.onabort = _ => reject(tx.error);
});
});
}
return add('x', [ 10, {} ])
.then(_ => step_done(),
_ => ok(false, 'failed to store'));
}
function createDB_outer() {
var op = indexedDB.open('db');
op.onupgradeneeded = e => {
ok(false, 'upgrade should not be needed');
var db = e.target.result;
db.createObjectStore('store');
};
return new Promise(resolve => {
op.onsuccess = e => resolve(e.target.result);
});
}
function get(k) {
return createDB_outer().then(db => {
var tx = db.transaction('store', 'readonly');
var store = tx.objectStore('store');
var op = store.get(k);
return new Promise((resolve, reject) => {
op.onsuccess = e => resolve(e.target.result);
op.onerror = _ => reject(op.error);
tx.onabort = _ => reject(tx.error);
});
});
}
function runInSandbox(sandbox, testFunc) {
is(typeof testFunc, 'function');
var resolvePromise;
var testPromise = new Promise(r => resolvePromise = r);
SpecialPowers.Cu.exportFunction(_ => resolvePromise(), sandbox,
{ defineAs: 'step_done' });
SpecialPowers.Cu.evalInSandbox('(' + testFunc.toSource() + ')()' +
'.then(step_done);', sandbox);
return testPromise;
}
// Use the window principal for the sandbox; location.origin is not sufficient.
var sb = new SpecialPowers.Cu.Sandbox(window,
{ wantGlobalProperties: ['indexedDB'] });
sb.ok = SpecialPowers.Cu.exportFunction(ok, sb);
Promise.resolve()
.then(_ => runInSandbox(sb, storeValue))
.then(_ => get('x'))
.then(x => {
ok(x, 'a value should be present');
is(x.length, 2);
is(x[0], 10);
is(typeof x[1], 'object');
is(Object.keys(x[1]).length, 0);
})
.then(_ => SimpleTest.finish());
</script>
</body>
</html>

View File

@ -0,0 +1,78 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function exerciseInterface() {
function DB(name, store) {
this.name = name;
this.store = store;
this._db = this._create();
}
DB.prototype = {
_create: function() {
var op = indexedDB.open(this.name);
op.onupgradeneeded = e => {
var db = e.target.result;
db.createObjectStore(this.store);
};
return new Promise(resolve => {
op.onsuccess = e => resolve(e.target.result);
});
},
_result: function(tx, op) {
return new Promise((resolve, reject) => {
op.onsuccess = e => resolve(e.target.result);
op.onerror = () => reject(op.error);
tx.onabort = () => reject(tx.error);
});
},
get: function(k) {
return this._db.then(db => {
var tx = db.transaction(this.store, 'readonly');
var store = tx.objectStore(this.store);
return this._result(tx, store.get(k));
});
},
add: function(k, v) {
return this._db.then(db => {
var tx = db.transaction(this.store, 'readwrite');
var store = tx.objectStore(this.store);
return this._result(tx, store.add(v, k));
});
}
};
var db = new DB('data', 'base');
return db.add('x', [ 10, {} ])
.then(_ => db.get('x'))
.then(x => {
equal(x.length, 2);
equal(x[0], 10);
equal(typeof x[1], 'object');
equal(Object.keys(x[1]).length, 0);
});
}
function run_test() {
do_get_profile();
let Cu = Components.utils;
let sb = new Cu.Sandbox('https://www.example.com',
{ wantGlobalProperties: ['indexedDB'] });
sb.equal = equal;
var innerPromise = new Promise((resolve, reject) => {
sb.test_done = resolve;
sb.test_error = reject;
});
Cu.evalInSandbox('(' + exerciseInterface.toSource() + ')()' +
'.then(test_done, test_error);', sb);
Cu.importGlobalProperties(['indexedDB']);
do_test_pending();
Promise.all([innerPromise, exerciseInterface()])
.then(do_test_finished);
}

View File

@ -59,6 +59,7 @@ skip-if = toolkit == 'android' # bug 1079278
[test_remove_index.js]
[test_remove_objectStore.js]
[test_request_readyState.js]
[test_sandbox.js]
[test_setVersion.js]
[test_setVersion_abort.js]
[test_setVersion_events.js]

View File

@ -670,7 +670,8 @@ public:
EmptyBlobImpl(const nsAString& aName,
const nsAString& aContentType,
int64_t aLastModifiedDate)
: BlobImplBase(aName, aContentType, 0, aLastModifiedDate)
: BlobImplBase(aName, aContentType, 0, aLastModifiedDate,
BlobDirState::eIsNotDir)
{
mImmutable = true;
}
@ -733,7 +734,8 @@ public:
uint64_t aLength,
int64_t aLastModifiedDate,
nsIInputStream* aInputStream)
: BlobImplBase(aName, aContentType, aLength, aLastModifiedDate)
: BlobImplBase(aName, aContentType, aLength, aLastModifiedDate,
BlobDirState::eIsNotDir)
, mInputStream(aInputStream)
{
MOZ_ASSERT(aLength != UINT64_MAX);
@ -772,12 +774,14 @@ struct MOZ_STACK_CLASS CreateBlobImplMetadata final
nsString mName;
uint64_t mLength;
int64_t mLastModifiedDate;
BlobDirState mDirState;
bool mHasRecursed;
const bool mIsSameProcessActor;
explicit CreateBlobImplMetadata(bool aIsSameProcessActor)
: mLength(0)
, mLastModifiedDate(0)
, mDirState(BlobDirState::eUnknownIfDir)
, mHasRecursed(false)
, mIsSameProcessActor(aIsSameProcessActor)
{
@ -1054,6 +1058,7 @@ CreateBlobImpl(const ParentBlobConstructorParams& aParams,
metadata.mName = params.name();
metadata.mLength = params.length();
metadata.mLastModifiedDate = params.modDate();
metadata.mDirState = BlobDirState(params.dirState());
}
nsRefPtr<BlobImpl> blobImpl =
@ -1855,7 +1860,8 @@ public:
const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
int64_t aModDate);
int64_t aModDate,
BlobDirState aDirState);
// For Blob.
RemoteBlobImpl(BlobChild* aActor,
@ -1868,7 +1874,8 @@ public:
const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
int64_t aModDate);
int64_t aModDate,
BlobDirState aDirState);
// For same-process blobs.
RemoteBlobImpl(BlobChild* aActor,
@ -2129,7 +2136,8 @@ public:
SetLazyData(const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
int64_t aLastModifiedDate) override;
int64_t aLastModifiedDate,
BlobDirState aDirState) override;
virtual bool
IsMemoryFile() const override;
@ -2143,6 +2151,15 @@ public:
virtual bool
IsFile() const override;
virtual void
LookupAndCacheIsDirectory() override;
virtual bool
IsDirectory() const override;
virtual BlobDirState
GetDirState() const override;
virtual bool
MayBeClonedToOtherThreads() const override;
@ -2172,8 +2189,9 @@ RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
int64_t aModDate)
: BlobImplBase(aName, aContentType, aLength, aModDate)
int64_t aModDate,
BlobDirState aDirState)
: BlobImplBase(aName, aContentType, aLength, aModDate, aDirState)
, mIsSlice(false)
{
CommonInit(aActor);
@ -2195,8 +2213,9 @@ RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
int64_t aModDate)
: BlobImplBase(aName, aContentType, aLength, aModDate)
int64_t aModDate,
BlobDirState aDirState)
: BlobImplBase(aName, aContentType, aLength, aModDate, aDirState)
, mSameProcessBlobImpl(aSameProcessBlobImpl)
, mIsSlice(false)
{
@ -2884,7 +2903,8 @@ BlobParent::
RemoteBlobImpl::SetLazyData(const nsAString& aName,
const nsAString& aContentType,
uint64_t aLength,
int64_t aLastModifiedDate)
int64_t aLastModifiedDate,
BlobDirState aDirState)
{
MOZ_CRASH("This should never be called!");
}
@ -2917,6 +2937,27 @@ RemoteBlobImpl::IsFile() const
return mBlobImpl->IsFile();
}
void
BlobParent::
RemoteBlobImpl::LookupAndCacheIsDirectory()
{
return mBlobImpl->LookupAndCacheIsDirectory();
}
bool
BlobParent::
RemoteBlobImpl::IsDirectory() const
{
return mBlobImpl->IsDirectory();
}
BlobDirState
BlobParent::
RemoteBlobImpl::GetDirState() const
{
return mBlobImpl->GetDirState();
}
bool
BlobParent::
RemoteBlobImpl::MayBeClonedToOtherThreads() const
@ -3107,7 +3148,8 @@ BlobChild::CommonInit(BlobChild* aOther, BlobImpl* aBlobImpl)
int64_t modDate = otherImpl->GetLastModified(rv);
MOZ_ASSERT(!rv.Failed());
remoteBlob = new RemoteBlobImpl(this, name, contentType, length, modDate);
remoteBlob = new RemoteBlobImpl(this, name, contentType, length, modDate,
otherImpl->GetDirState());
} else {
remoteBlob = new RemoteBlobImpl(this, contentType, length);
}
@ -3149,7 +3191,8 @@ BlobChild::CommonInit(const ChildBlobConstructorParams& aParams)
params.name(),
params.contentType(),
params.length(),
params.modDate());
params.modDate(),
BlobDirState(params.dirState()));
break;
}
@ -3183,7 +3226,8 @@ BlobChild::CommonInit(const ChildBlobConstructorParams& aParams)
name,
contentType,
size,
lastModifiedDate);
lastModifiedDate,
blobImpl->GetDirState());
} else {
remoteBlob = new RemoteBlobImpl(this, blobImpl, contentType, size);
}
@ -3368,7 +3412,8 @@ BlobChild::GetOrCreateFromImpl(ChildManagerType* aManager,
MOZ_ASSERT(!rv.Failed());
blobParams =
FileBlobConstructorParams(name, contentType, length, modDate, blobData);
FileBlobConstructorParams(name, contentType, length, modDate,
aBlobImpl->GetDirState(), blobData);
} else {
blobParams = NormalBlobConstructorParams(contentType, length, blobData);
}
@ -3541,19 +3586,22 @@ bool
BlobChild::SetMysteryBlobInfo(const nsString& aName,
const nsString& aContentType,
uint64_t aLength,
int64_t aLastModifiedDate)
int64_t aLastModifiedDate,
BlobDirState aDirState)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mBlobImpl);
MOZ_ASSERT(mRemoteBlobImpl);
MOZ_ASSERT(aLastModifiedDate != INT64_MAX);
mBlobImpl->SetLazyData(aName, aContentType, aLength, aLastModifiedDate);
mBlobImpl->SetLazyData(aName, aContentType, aLength, aLastModifiedDate,
aDirState);
FileBlobConstructorParams params(aName,
aContentType,
aLength,
aLastModifiedDate,
aDirState,
void_t() /* optionalBlobData */);
return SendResolveMystery(params);
}
@ -3568,7 +3616,8 @@ BlobChild::SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength)
nsString voidString;
voidString.SetIsVoid(true);
mBlobImpl->SetLazyData(voidString, aContentType, aLength, INT64_MAX);
mBlobImpl->SetLazyData(voidString, aContentType, aLength, INT64_MAX,
BlobDirState::eUnknownIfDir);
NormalBlobConstructorParams params(aContentType,
aLength,
@ -3910,7 +3959,8 @@ BlobParent::GetOrCreateFromImpl(ParentManagerType* aManager,
MOZ_ASSERT(!rv.Failed());
blobParams =
FileBlobConstructorParams(name, contentType, length, modDate, void_t());
FileBlobConstructorParams(name, contentType, length, modDate,
aBlobImpl->GetDirState(), void_t());
} else {
blobParams = NormalBlobConstructorParams(contentType, length, void_t());
}
@ -4388,7 +4438,8 @@ BlobParent::RecvResolveMystery(const ResolveMysteryParams& aParams)
mBlobImpl->SetLazyData(voidString,
params.contentType(),
params.length(),
INT64_MAX);
INT64_MAX,
BlobDirState::eUnknownIfDir);
return true;
}
@ -4413,7 +4464,8 @@ BlobParent::RecvResolveMystery(const ResolveMysteryParams& aParams)
mBlobImpl->SetLazyData(params.name(),
params.contentType(),
params.length(),
params.modDate());
params.modDate(),
BlobDirState(params.dirState()));
return true;
}

View File

@ -25,11 +25,14 @@ class PBackgroundChild;
namespace dom {
class Blob;
class BlobImpl;
class ContentChild;
class nsIContentChild;
class PBlobStreamChild;
enum BlobDirState : uint32_t;
class BlobChild final
: public PBlobChild
{
@ -114,7 +117,8 @@ public:
SetMysteryBlobInfo(const nsString& aName,
const nsString& aContentType,
uint64_t aLength,
int64_t aLastModifiedDate);
int64_t aLastModifiedDate,
BlobDirState aDirState);
// Use this for non-file blobs.
bool

View File

@ -67,6 +67,7 @@ struct FileBlobConstructorParams
nsString contentType;
uint64_t length;
int64_t modDate;
uint32_t dirState;
// This must be of type BlobData in a child->parent message, and will always
// be of type void_t in a parent->child message.

View File

@ -272,6 +272,12 @@ struct FileSystemCreateFileParams
bool replace;
};
struct FileSystemGetDirectoryListingParams
{
nsString filesystem;
nsString realPath;
};
struct FileSystemGetFileOrDirectoryParams
{
nsString filesystem;
@ -296,6 +302,7 @@ union FileSystemParams
{
FileSystemCreateDirectoryParams;
FileSystemCreateFileParams;
FileSystemGetDirectoryListingParams;
FileSystemGetFileOrDirectoryParams;
FileSystemRemoveParams;
};

View File

@ -128,6 +128,7 @@ PRLogModuleInfo* gMediaSampleLog;
void
MediaDecoder::InitStatics()
{
MOZ_ASSERT(NS_IsMainThread());
AbstractThread::InitStatics();
SharedThreadPool::InitStatics();
@ -240,6 +241,7 @@ void MediaDecoder::UpdateDormantState(bool aDormantTimeout, bool aActivity)
void MediaDecoder::DormantTimerExpired(nsITimer* aTimer, void* aClosure)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aClosure);
MediaDecoder* decoder = static_cast<MediaDecoder*>(aClosure);
ReentrantMonitorAutoEnter mon(decoder->GetReentrantMonitor());
@ -249,6 +251,7 @@ void MediaDecoder::DormantTimerExpired(nsITimer* aTimer, void* aClosure)
void MediaDecoder::StartDormantTimer()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mIsHeuristicDormantSupported) {
return;
}
@ -274,6 +277,7 @@ void MediaDecoder::StartDormantTimer()
void MediaDecoder::CancelDormantTimer()
{
MOZ_ASSERT(NS_IsMainThread());
if (mDormantTimer) {
mDormantTimer->Cancel();
}
@ -508,7 +512,7 @@ nsresult MediaDecoder::InitializeStateMachine(MediaDecoder* aCloneDonor)
void MediaDecoder::SetStateMachineParameters()
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
MOZ_ASSERT(NS_IsMainThread());
if (mMinimizePreroll) {
mDecoderStateMachine->DispatchMinimizePrerollUntilPlaybackStarts();
}
@ -516,8 +520,8 @@ void MediaDecoder::SetStateMachineParameters()
void MediaDecoder::SetMinimizePrerollUntilPlaybackStarts()
{
DECODER_LOG("SetMinimizePrerollUntilPlaybackStarts()");
MOZ_ASSERT(NS_IsMainThread());
DECODER_LOG("SetMinimizePrerollUntilPlaybackStarts()");
mMinimizePreroll = true;
// This needs to be called before we init the state machine, otherwise it will
@ -595,6 +599,7 @@ nsresult MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
void MediaDecoder::CallSeek(const SeekTarget& aTarget)
{
MOZ_ASSERT(NS_IsMainThread());
mSeekRequest.DisconnectIfExists();
mSeekRequest.Begin(ProxyMediaCall(mDecoderStateMachine->TaskQueue(),
mDecoderStateMachine.get(), __func__,
@ -674,6 +679,7 @@ void MediaDecoder::MetadataLoaded(nsAutoPtr<MediaInfo> aInfo,
const char*
MediaDecoder::PlayStateStr()
{
MOZ_ASSERT(NS_IsMainThread());
switch (mPlayState) {
case PLAY_STATE_START: return "PLAY_STATE_START";
case PLAY_STATE_LOADING: return "PLAY_STATE_LOADING";
@ -766,6 +772,7 @@ void MediaDecoder::DecodeError()
void MediaDecoder::UpdateSameOriginStatus(bool aSameOrigin)
{
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mSameOriginMedia = aSameOrigin;
}
@ -790,6 +797,7 @@ bool MediaDecoder::IsEndedOrShutdown() const
bool MediaDecoder::IsEnded() const
{
MOZ_ASSERT(NS_IsMainThread());
return mPlayState == PLAY_STATE_ENDED ||
(mWasEndedWhenEnteredDormant && (mPlayState != PLAY_STATE_SHUTDOWN));
}
@ -863,7 +871,7 @@ double MediaDecoder::ComputePlaybackRate(bool* aReliable)
void MediaDecoder::UpdatePlaybackRate()
{
MOZ_ASSERT(NS_IsMainThread() || OnStateMachineTaskQueue());
MOZ_ASSERT(NS_IsMainThread());
GetReentrantMonitor().AssertCurrentThreadIn();
if (!mResource)
return;
@ -933,6 +941,7 @@ void MediaDecoder::NotifyDownloadEnded(nsresult aStatus)
void MediaDecoder::NotifyPrincipalChanged()
{
MOZ_ASSERT(NS_IsMainThread());
if (mOwner) {
mOwner->NotifyDecoderPrincipalChanged();
}
@ -1139,6 +1148,7 @@ bool MediaDecoder::IsMediaSeekable()
media::TimeIntervals MediaDecoder::GetSeekable()
{
MOZ_ASSERT(NS_IsMainThread());
// We can seek in buffered range if the media is seekable. Also, we can seek
// in unbuffered ranges if the transport level is seekable (local file or the
// server supports range requests, etc.)
@ -1225,6 +1235,7 @@ bool MediaDecoder::OnStateMachineTaskQueue() const
void MediaDecoder::SetPlaybackRate(double aPlaybackRate)
{
MOZ_ASSERT(NS_IsMainThread());
mPlaybackRate = aPlaybackRate;
if (mPlaybackRate == 0.0) {
mPausedForPlaybackRateNull = true;
@ -1242,6 +1253,7 @@ void MediaDecoder::SetPlaybackRate(double aPlaybackRate)
void MediaDecoder::SetPreservesPitch(bool aPreservesPitch)
{
MOZ_ASSERT(NS_IsMainThread());
mPreservesPitch = aPreservesPitch;
}
@ -1253,6 +1265,7 @@ bool MediaDecoder::OnDecodeTaskQueue() const {
void
MediaDecoder::SetStateMachine(MediaDecoderStateMachine* aStateMachine)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT_IF(aStateMachine, !mDecoderStateMachine);
mDecoderStateMachine = aStateMachine;
@ -1297,10 +1310,12 @@ void MediaDecoder::Invalidate()
// Constructs the time ranges representing what segments of the media
// are buffered and playable.
media::TimeIntervals MediaDecoder::GetBuffered() {
MOZ_ASSERT(NS_IsMainThread());
return mBuffered.Ref();
}
size_t MediaDecoder::SizeOfVideoQueue() {
MOZ_ASSERT(NS_IsMainThread());
if (mDecoderStateMachine) {
return mDecoderStateMachine->SizeOfVideoQueue();
}
@ -1308,6 +1323,7 @@ size_t MediaDecoder::SizeOfVideoQueue() {
}
size_t MediaDecoder::SizeOfAudioQueue() {
MOZ_ASSERT(NS_IsMainThread());
if (mDecoderStateMachine) {
return mDecoderStateMachine->SizeOfAudioQueue();
}
@ -1360,6 +1376,7 @@ MediaDecoderOwner* MediaDecoder::GetMediaOwner() const
void MediaDecoder::FireTimeUpdate()
{
MOZ_ASSERT(NS_IsMainThread());
if (!mOwner)
return;
mOwner->FireTimeUpdate(true);
@ -1367,6 +1384,7 @@ void MediaDecoder::FireTimeUpdate()
void MediaDecoder::PinForSeek()
{
MOZ_ASSERT(NS_IsMainThread());
MediaResource* resource = GetResource();
if (!resource || mPinnedForSeek) {
return;
@ -1377,6 +1395,7 @@ void MediaDecoder::PinForSeek()
void MediaDecoder::UnpinForSeek()
{
MOZ_ASSERT(NS_IsMainThread());
MediaResource* resource = GetResource();
if (!resource || !mPinnedForSeek) {
return;
@ -1425,8 +1444,8 @@ bool MediaDecoder::CanPlayThrough()
nsresult
MediaDecoder::SetCDMProxy(CDMProxy* aProxy)
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mProxy = aProxy;
// Awaken any readers waiting for the proxy.
NotifyWaitingForResourcesStatusChanged();

View File

@ -339,7 +339,7 @@ public:
}
void SetResource(MediaResource* aResource)
{
NS_ASSERTION(NS_IsMainThread(), "Should only be called on main thread");
MOZ_ASSERT(NS_IsMainThread());
mResource = aResource;
}
@ -559,17 +559,21 @@ public:
virtual void UpdatePlaybackRate();
// Used to estimate rates of data passing through the decoder's channel.
// Records activity stopping on the channel. The monitor must be held.
virtual void NotifyPlaybackStarted() {
GetReentrantMonitor().AssertCurrentThreadIn();
mPlaybackStatistics->Start();
// Records activity stopping on the channel.
void DispatchPlaybackStarted() {
nsRefPtr<MediaDecoder> self = this;
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableFunction([self] () { self->mPlaybackStatistics->Start(); });
AbstractThread::MainThread()->Dispatch(r.forget());
}
// Used to estimate rates of data passing through the decoder's channel.
// Records activity stopping on the channel. The monitor must be held.
virtual void NotifyPlaybackStopped() {
GetReentrantMonitor().AssertCurrentThreadIn();
mPlaybackStatistics->Stop();
// Records activity stopping on the channel.
void DispatchPlaybackStopped() {
nsRefPtr<MediaDecoder> self = this;
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableFunction([self] () { self->mPlaybackStatistics->Stop(); });
AbstractThread::MainThread()->Dispatch(r.forget());
}
// The actual playback rate computation. The monitor must be held.
@ -640,6 +644,7 @@ public:
void OnSeekRejected()
{
MOZ_ASSERT(NS_IsMainThread());
mSeekRequest.Complete();
mLogicallySeeking = false;
}
@ -650,7 +655,11 @@ public:
void SeekingStarted(MediaDecoderEventVisibility aEventVisibility = MediaDecoderEventVisibility::Observable);
void UpdateLogicalPosition(MediaDecoderEventVisibility aEventVisibility);
void UpdateLogicalPosition() { UpdateLogicalPosition(MediaDecoderEventVisibility::Observable); }
void UpdateLogicalPosition()
{
MOZ_ASSERT(NS_IsMainThread());
UpdateLogicalPosition(MediaDecoderEventVisibility::Observable);
}
// Find the end of the cached data starting at the current decoder
// position.

View File

@ -1267,7 +1267,7 @@ void MediaDecoderStateMachine::StopPlayback()
AssertCurrentThreadInMonitor();
mDecoder->NotifyPlaybackStopped();
mDecoder->DispatchPlaybackStopped();
if (IsPlaying()) {
mPlayDuration = GetClock();
@ -1308,7 +1308,7 @@ void MediaDecoderStateMachine::MaybeStartPlayback()
DECODER_LOG("MaybeStartPlayback() starting playback");
mDecoder->NotifyPlaybackStarted();
mDecoder->DispatchPlaybackStarted();
SetPlayStartTime(TimeStamp::Now());
MOZ_ASSERT(IsPlaying());

View File

@ -779,10 +779,13 @@ MediaRecorder::MediaRecorder(AudioNode& aSrcAudioNode,
mPipeStream = ctx->Graph()->CreateAudioNodeStream(engine,
MediaStreamGraph::EXTERNAL_STREAM,
ctx->SampleRate());
mInputPort = mPipeStream->AllocateInputPort(aSrcAudioNode.Stream(),
MediaInputPort::FLAG_BLOCK_INPUT,
0,
aSrcOutput);
AudioNodeStream* ns = aSrcAudioNode.GetStream();
if (ns) {
mInputPort = mPipeStream->AllocateInputPort(aSrcAudioNode.GetStream(),
MediaInputPort::FLAG_BLOCK_INPUT,
0,
aSrcOutput);
}
}
mAudioNode = &aSrcAudioNode;
if (!gMediaRecorderLog) {
@ -1140,7 +1143,7 @@ MediaRecorder::GetSourceMediaStream()
return mDOMStream->GetStream();
}
MOZ_ASSERT(mAudioNode != nullptr);
return mPipeStream != nullptr ? mPipeStream : mAudioNode->Stream();
return mPipeStream ? mPipeStream.get() : mAudioNode->GetStream();
}
nsIPrincipal*

View File

@ -34,7 +34,7 @@ class ImageContainer;
* confusing.
*/
class VideoFrameContainer {
~VideoFrameContainer();
B2G_ACL_EXPORT ~VideoFrameContainer();
public:
typedef layers::ImageContainer ImageContainer;
@ -46,7 +46,7 @@ public:
already_AddRefed<ImageContainer> aContainer);
// Call on any thread
void SetCurrentFrame(const gfxIntSize& aIntrinsicSize, Image* aImage,
B2G_ACL_EXPORT void SetCurrentFrame(const gfxIntSize& aIntrinsicSize, Image* aImage,
TimeStamp aTargetTime);
void ClearCurrentFrame(bool aResetSize = false);
// Reset the VideoFrameContainer
@ -61,8 +61,8 @@ public:
INVALIDATE_FORCE
};
void Invalidate() { InvalidateWithFlags(INVALIDATE_DEFAULT); }
void InvalidateWithFlags(uint32_t aFlags);
ImageContainer* GetImageContainer();
B2G_ACL_EXPORT void InvalidateWithFlags(uint32_t aFlags);
B2G_ACL_EXPORT ImageContainer* GetImageContainer();
void ForgetElement() { mElement = nullptr; }
protected:

View File

@ -349,6 +349,7 @@ MediaKeySystemAccess::NotifyObservers(nsIDOMWindow* aWindow,
data.mStatus = aStatus;
nsAutoString json;
data.ToJSON(json);
EME_LOG("MediaKeySystemAccess::NotifyObservers() %s", NS_ConvertUTF16toUTF8(json).get());
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (obs) {
obs->NotifyObservers(aWindow, "mediakeys-request", json.get());

View File

@ -18,13 +18,13 @@ public:
, mShutdownBit(false)
{}
virtual MediaDecoder* Clone() {
virtual MediaDecoder* Clone() override {
if (!IsOggEnabled()) {
return nullptr;
}
return new OggDecoder();
}
virtual MediaDecoderStateMachine* CreateStateMachine();
virtual MediaDecoderStateMachine* CreateStateMachine() override;
// For yucky legacy reasons, the ogg decoder needs to do a cross-thread read
// to check for shutdown while it hogs its own task queue. We don't want to

View File

@ -70,6 +70,7 @@ load 990794.html
load 1015662.html
skip-if(Android||B2G) test-pref(media.navigator.permission.disabled,true) load 1028458.html # bug 1048863
load buffer-source-ended-1.html
load doppler-1.html
HTTP load media-element-source-seek-1.html
load offline-buffer-source-ended-1.html
load oscillator-ended-1.html

View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html class="reftest-wait">
<script>
var context = new window.AudioContext();
var source = context.createBufferSource();
source.buffer = context.createBuffer(1, 1, context.sampleRate);
source.onended =
function(e) {
setTimeout(
function() {
var panner = context.createPanner();
source.connect(panner);
panner.setVelocity(1.0, 0.0, 0.0);
setTimeout(
function() {
document.documentElement.removeAttribute("class");
},
0);
},
0);
};
source.start(0);
</script>

View File

@ -618,11 +618,8 @@ skip-if = (os == 'win' && os_version == '5.1') || (os != 'win' && toolkit != 'go
skip-if = toolkit == 'android' || (os == 'win' && !debug) # bug 1043403, bug 1140675
[test_eme_non_mse_fails.html]
skip-if = toolkit == 'android' # bug 1043403
#[test_eme_obs_notification.html]
#skip-if = buildapp == 'b2g' || toolkit == 'android' # bug 1043403
# Disabled (bug 1140778) since this test fails and we don't want to remove the
# functionality being tested by this test. We should still test other observers
# in future however, so I'm not removing the test, just disabling it.
[test_eme_request_notifications.html]
skip-if = toolkit == 'android' # bug 1043403
[test_eme_persistent_sessions.html]
skip-if = toolkit == 'android' # bug 1043403
[test_eme_playback.html]

View File

@ -1,68 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test Encrypted Media Extensions</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;
var videos = new Set();
var observedVideos = new Set();
var observer = function(subject) {
ok(videos.has(subject), "Video should be known to us");
videos.delete(subject);
observedVideos.add(subject);
};
SpecialPowers.Services.obs.addObserver(observer, "media-eme-metadataloaded", false);
// When the test manager finishes, these sets should all be empty again:
manager.onFinished = function() {
is(videos.size, 0, "video set should be empty");
is(observedVideos.size, 0, "observed video set should be empty");
};
// ... but even if they're not, we should clear them out when we finish:
SimpleTest.registerCleanupFunction(function() {
SpecialPowers.Services.obs.removeObserver(observer, "media-eme-metadataloaded");
videos.clear();
observedVideos.clear();
});
function startTest(test, token)
{
manager.started(token);
var sessions = [];
var v = SetupEME(test, token);
videos.add(v);
v.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
v.addEventListener("loadeddata", function(ev) {
ok(observedVideos.has(ev.target), "Should have been told about eme video through observer as well.");
observedVideos.delete(ev.target);
manager.finished(token);
});
LoadTestWithManagedLoadToken(test, v, manager, token,
{ onlyLoadFirstFragments:2, noEndOfStream:true });
}
function beginTest() {
manager.runTests(gEMETests, startTest);
}
SimpleTest.waitForExplicitFinish();
SetupEMEPref(beginTest);
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,109 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test Encrypted Media Extensions</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;
var observedStatus = "nothing";
var observer = function(subject, topic, data) {
observedStatus = JSON.parse(data).status;
info("Observed: " + observedStatus);
};
SpecialPowers.Services.obs.addObserver(observer, "mediakeys-request", false);
SimpleTest.registerCleanupFunction(function() {
SpecialPowers.Services.obs.removeObserver(observer, "mediakeys-request");
});
function SetPrefs(prefs) {
return new Promise(function(resolve, reject) {
SpecialPowers.pushPrefEnv({"set": prefs}, function() { resolve(); });
});
}
function Test(test) {
var p = test.prefs ? SetPrefs(test.prefs) : Promise.resolve();
observedStatus = "nothing";
var name = "'" + test.keySystem + "'";
return p.then(function() {
return new Promise(function(resolve, reject) {
navigator.requestMediaKeySystemAccess(test.keySystem)
.then(
function(keySystemAccess) {
return keySystemAccess.createMediaKeys();
})
.then(
function(mediaKeys) {
ok(test.shouldPass, name + " passed and was expected to " + (test.shouldPass ? "pass" : "fail"));
is(observedStatus, test.expectedStatus, name + " observer service result");
resolve();
}
)
.catch(
function() {
ok(!test.shouldPass, name + " failed and was expected to " + (test.shouldPass ? "pass" : "fail"));
is(observedStatus, test.expectedStatus, name + " observer service result");
resolve();
}
);
});
});
}
const CLEARKEY_ID = 'org.w3.clearkey';
var tests = [
{
keySystem: CLEARKEY_ID,
shouldPass: false,
expectedStatus: 'api-disabled',
prefs: [["media.eme.enabled", false], ["media.eme.clearkey.enabled", true]]
},
{
keySystem: CLEARKEY_ID,
shouldPass: false,
expectedStatus: 'cdm-disabled',
prefs: [["media.eme.enabled", true], ["media.eme.clearkey.enabled", false]]
},
{
keySystem: 'unsupported-keysystem',
shouldPass: false,
expectedStatus: 'cdm-not-supported'
},
{
keySystem: CLEARKEY_ID + '.10000' , // A stupendously high min CDM version, presumably not installed.
shouldPass: false,
expectedStatus: 'cdm-insufficient-version',
prefs: [["media.eme.enabled", true], ["media.eme.clearkey.enabled", true]]
},
{
keySystem: CLEARKEY_ID,
shouldPass: true,
expectedStatus: 'cdm-created',
prefs: [["media.eme.enabled", true], ["media.eme.clearkey.enabled", true]]
},
];
SetupEMEPref(function() {
tests.reduce(function(p,c,i,array) {
return p.then(function() { return Test(c); });
}, Promise.resolve()).then(SimpleTest.finish);
});
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

View File

@ -19,27 +19,7 @@
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_CLASS(AudioBufferSourceNode)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioBufferSourceNode)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBuffer)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlaybackRate)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDetune)
if (tmp->Context()) {
// AudioNode's Unlink implementation disconnects us from the graph
// too, but we need to do this right here to make sure that
// UnregisterAudioBufferSourceNode can properly untangle us from
// the possibly connected PannerNodes.
tmp->DisconnectFromGraph();
tmp->Context()->UnregisterAudioBufferSourceNode(tmp);
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(AudioNode)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AudioBufferSourceNode, AudioNode)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBuffer)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlaybackRate)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDetune)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_INHERITED(AudioBufferSourceNode, AudioNode, mBuffer, mPlaybackRate, mDetune)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioBufferSourceNode)
NS_INTERFACE_MAP_END_INHERITING(AudioNode)
@ -66,7 +46,7 @@ public:
mLoopStart(0), mLoopEnd(0),
mBufferSampleRate(0), mBufferPosition(0), mChannels(0),
mDopplerShift(1.0f),
mDestination(static_cast<AudioNodeStream*>(aDestination->Stream())),
mDestination(aDestination->Stream()),
mPlaybackRateTimeline(1.0f),
mDetuneTimeline(0.0f),
mLoop(false)
@ -565,13 +545,23 @@ AudioBufferSourceNode::AudioBufferSourceNode(AudioContext* aContext)
{
AudioBufferSourceNodeEngine* engine = new AudioBufferSourceNodeEngine(this, aContext->Destination());
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::SOURCE_STREAM);
engine->SetSourceStream(static_cast<AudioNodeStream*>(mStream.get()));
engine->SetSourceStream(mStream);
mStream->AddMainThreadListener(this);
}
AudioBufferSourceNode::~AudioBufferSourceNode()
{
if (Context()) {
}
void
AudioBufferSourceNode::DestroyMediaStream()
{
bool hadStream = mStream;
if (hadStream) {
mStream->RemoveMainThreadListener(this);
}
AudioNode::DestroyMediaStream();
if (hadStream && Context()) {
Context()->UnregisterAudioBufferSourceNode(this);
}
}
@ -617,7 +607,7 @@ AudioBufferSourceNode::Start(double aWhen, double aOffset,
}
mStartCalled = true;
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
AudioNodeStream* ns = mStream;
if (!ns) {
// Nothing to play, or we're already dead for some reason
return;
@ -642,8 +632,8 @@ AudioBufferSourceNode::Start(double aWhen, double aOffset,
void
AudioBufferSourceNode::SendBufferParameterToStream(JSContext* aCx)
{
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
if (!mStream) {
AudioNodeStream* ns = mStream;
if (!ns) {
return;
}
@ -701,7 +691,7 @@ AudioBufferSourceNode::Stop(double aWhen, ErrorResult& aRv)
return;
}
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
AudioNodeStream* ns = mStream;
if (!ns || !Context()) {
// We've already stopped and had our stream shut down
return;
@ -758,6 +748,9 @@ void
AudioBufferSourceNode::SendDetuneToStream(AudioNode* aNode)
{
AudioBufferSourceNode* This = static_cast<AudioBufferSourceNode*>(aNode);
if (!This->mStream) {
return;
}
SendTimelineParameterToStream(This, DETUNE, *This->mDetune);
}

View File

@ -21,13 +21,8 @@ class AudioBufferSourceNode final : public AudioNode,
public:
explicit AudioBufferSourceNode(AudioContext* aContext);
virtual void DestroyMediaStream() override
{
if (mStream) {
mStream->RemoveMainThreadListener(this);
}
AudioNode::DestroyMediaStream();
}
virtual void DestroyMediaStream() override;
virtual uint16_t NumberOfInputs() const final override
{
return 0;

View File

@ -369,7 +369,7 @@ AudioDestinationNode::AudioDestinationNode(AudioContext* aContext,
mStream->AddAudioOutput(&gWebAudioOutputKey);
if (!aIsOffline) {
graph->NotifyWhenGraphStarted(mStream->AsAudioNodeStream());
graph->NotifyWhenGraphStarted(mStream);
}
if (aChannel != AudioChannel::Normal) {
@ -438,9 +438,8 @@ AudioDestinationNode::NotifyMainThreadStreamFinished()
void
AudioDestinationNode::FireOfflineCompletionEvent()
{
AudioNodeStream* stream = static_cast<AudioNodeStream*>(Stream());
OfflineDestinationNodeEngine* engine =
static_cast<OfflineDestinationNodeEngine*>(stream->Engine());
static_cast<OfflineDestinationNodeEngine*>(Stream()->Engine());
engine->FireOfflineCompletionEvent(this);
}

View File

@ -51,6 +51,9 @@ public:
virtual void SetChannelCount(uint32_t aChannelCount,
ErrorResult& aRv) override;
// Returns the stream or null after unlink.
AudioNodeStream* Stream() { return mStream; }
void Mute();
void Unmute();

View File

@ -217,15 +217,13 @@ AudioNode::Connect(AudioNode& aDestination, uint32_t aOutput,
input->mInputNode = this;
input->mInputPort = aInput;
input->mOutputPort = aOutput;
if (aDestination.mStream) {
AudioNodeStream* destinationStream = aDestination.mStream;
if (mStream && destinationStream) {
// Connect streams in the MediaStreamGraph
MOZ_ASSERT(aDestination.mStream->AsProcessedStream());
ProcessedMediaStream* ps =
static_cast<ProcessedMediaStream*>(aDestination.mStream.get());
MOZ_ASSERT(aInput <= UINT16_MAX, "Unexpected large input port number");
MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number");
input->mStreamPort =
ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT,
input->mStreamPort = destinationStream->
AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT,
static_cast<uint16_t>(aInput),
static_cast<uint16_t>(aOutput));
}
@ -263,43 +261,41 @@ AudioNode::Connect(AudioParam& aDestination, uint32_t aOutput,
MediaStream* stream = aDestination.Stream();
MOZ_ASSERT(stream->AsProcessedStream());
ProcessedMediaStream* ps = static_cast<ProcessedMediaStream*>(stream);
// Setup our stream as an input to the AudioParam's stream
MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number");
input->mStreamPort = ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT,
0, static_cast<uint16_t>(aOutput));
if (mStream) {
// Setup our stream as an input to the AudioParam's stream
MOZ_ASSERT(aOutput <= UINT16_MAX, "Unexpected large output port number");
input->mStreamPort =
ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT,
0, static_cast<uint16_t>(aOutput));
}
}
void
AudioNode::SendDoubleParameterToStream(uint32_t aIndex, double aValue)
{
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
MOZ_ASSERT(ns, "How come we don't have a stream here?");
ns->SetDoubleParameter(aIndex, aValue);
MOZ_ASSERT(mStream, "How come we don't have a stream here?");
mStream->SetDoubleParameter(aIndex, aValue);
}
void
AudioNode::SendInt32ParameterToStream(uint32_t aIndex, int32_t aValue)
{
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
MOZ_ASSERT(ns, "How come we don't have a stream here?");
ns->SetInt32Parameter(aIndex, aValue);
MOZ_ASSERT(mStream, "How come we don't have a stream here?");
mStream->SetInt32Parameter(aIndex, aValue);
}
void
AudioNode::SendThreeDPointParameterToStream(uint32_t aIndex, const ThreeDPoint& aValue)
{
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
MOZ_ASSERT(ns, "How come we don't have a stream here?");
ns->SetThreeDPointParameter(aIndex, aValue);
MOZ_ASSERT(mStream, "How come we don't have a stream here?");
mStream->SetThreeDPointParameter(aIndex, aValue);
}
void
AudioNode::SendChannelMixingParametersToStream()
{
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
MOZ_ASSERT(ns, "How come we don't have a stream here?");
ns->SetChannelMixingParameters(mChannelCount, mChannelCountMode,
MOZ_ASSERT(mStream, "How come we don't have a stream here?");
mStream->SetChannelMixingParameters(mChannelCount, mChannelCountMode,
mChannelInterpretation);
}
@ -307,7 +303,7 @@ void
AudioNode::SendTimelineParameterToStream(AudioNode* aNode, uint32_t aIndex,
const AudioParamTimeline& aValue)
{
AudioNodeStream* ns = static_cast<AudioNodeStream*>(aNode->mStream.get());
AudioNodeStream* ns = aNode->mStream;
MOZ_ASSERT(ns, "How come we don't have a stream here?");
ns->SetTimelineParameter(aIndex, aValue);
}
@ -391,7 +387,7 @@ AudioNode::DestroyMediaStream()
// hold the lock when the stream gets destroyed, because that will
// cause the engine to be destroyed as well, and we don't want to
// be holding the lock as we're trying to destroy it!
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
AudioNodeStream* ns = mStream;
MutexAutoLock lock(ns->Engine()->NodeMutex());
MOZ_ASSERT(ns, "How come we don't have a stream here?");
MOZ_ASSERT(ns->Engine()->Node() == this, "Invalid node reference");
@ -431,9 +427,8 @@ AudioNode::SetPassThrough(bool aPassThrough)
{
MOZ_ASSERT(NumberOfInputs() <= 1 && NumberOfOutputs() == 1);
mPassThrough = aPassThrough;
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
MOZ_ASSERT(ns, "How come we don't have a stream here?");
ns->SetPassThrough(mPassThrough);
MOZ_ASSERT(mStream, "How come we don't have a stream here?");
mStream->SetPassThrough(mPassThrough);
}
}

View File

@ -167,7 +167,8 @@ public:
uint32_t mOutputPort;
};
MediaStream* Stream() { return mStream; }
// Returns the stream, if any.
AudioNodeStream* GetStream() { return mStream; }
const nsTArray<InputNode>& InputNodes() const
{
@ -221,8 +222,7 @@ private:
protected:
// Must be set in the constructor. Must not be null unless finished.
// If MaxNumberOfInputs() is > 0, then mStream must be a ProcessedMediaStream.
nsRefPtr<MediaStream> mStream;
nsRefPtr<AudioNodeStream> mStream;
private:
// For every InputNode, there is a corresponding entry in mOutputNodes of the

View File

@ -113,10 +113,11 @@ AudioParam::Stream()
mStream = stream.forget();
// Setup the AudioParam's stream as an input to the owner AudioNode's stream
MediaStream* nodeStream = mNode->Stream();
MOZ_ASSERT(nodeStream->AsProcessedStream());
ProcessedMediaStream* ps = static_cast<ProcessedMediaStream*>(nodeStream);
mNodeStreamPort = ps->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT);
AudioNodeStream* nodeStream = mNode->GetStream();
if (nodeStream) {
mNodeStreamPort =
nodeStream->AllocateInputPort(mStream, MediaInputPort::FLAG_BLOCK_INPUT);
}
// Let the MSG's copy of AudioParamTimeline know about the change in the stream
mCallback(mNode);

View File

@ -79,7 +79,7 @@ public:
BiquadFilterNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
: AudioNodeEngine(aNode)
, mSource(nullptr)
, mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
, mDestination(aDestination->Stream())
// Keep the default values in sync with the default values in
// BiquadFilterNode::BiquadFilterNode
, mType(BiquadFilterType::Lowpass)
@ -251,7 +251,7 @@ BiquadFilterNode::BiquadFilterNode(AudioContext* aContext)
{
BiquadFilterNodeEngine* engine = new BiquadFilterNodeEngine(this, aContext->Destination());
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
engine->SetSourceStream(mStream);
}
BiquadFilterNode::~BiquadFilterNode()

View File

@ -241,7 +241,7 @@ ConvolverNode::SetBuffer(JSContext* aCx, AudioBuffer* aBuffer, ErrorResult& aRv)
mBuffer = aBuffer;
// Send the buffer to the stream
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
AudioNodeStream* ns = mStream;
MOZ_ASSERT(ns, "Why don't we have a stream here?");
if (mBuffer) {
uint32_t length = mBuffer->Length();

View File

@ -33,7 +33,7 @@ public:
double aMaxDelayTicks)
: AudioNodeEngine(aNode)
, mSource(nullptr)
, mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
, mDestination(aDestination->Stream())
// Keep the default value in sync with the default value in DelayNode::DelayNode.
, mDelay(0.f)
// Use a smoothing range of 20ms
@ -199,7 +199,7 @@ DelayNode::DelayNode(AudioContext* aContext, double aMaxDelay)
new DelayNodeEngine(this, aContext->Destination(),
aContext->SampleRate() * aMaxDelay);
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
engine->SetSourceStream(mStream);
}
DelayNode::~DelayNode()

View File

@ -37,7 +37,7 @@ public:
AudioDestinationNode* aDestination)
: AudioNodeEngine(aNode)
, mSource(nullptr)
, mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
, mDestination(aDestination->Stream())
// Keep the default value in sync with the default value in
// DynamicsCompressorNode::DynamicsCompressorNode.
, mThreshold(-24.f)
@ -210,7 +210,7 @@ DynamicsCompressorNode::DynamicsCompressorNode(AudioContext* aContext)
{
DynamicsCompressorNodeEngine* engine = new DynamicsCompressorNodeEngine(this, aContext->Destination());
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
engine->SetSourceStream(mStream);
}
DynamicsCompressorNode::~DynamicsCompressorNode()

View File

@ -29,7 +29,7 @@ public:
GainNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
: AudioNodeEngine(aNode)
, mSource(nullptr)
, mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
, mDestination(aDestination->Stream())
// Keep the default value in sync with the default value in GainNode::GainNode.
, mGain(1.f)
{
@ -130,7 +130,7 @@ GainNode::GainNode(AudioContext* aContext)
{
GainNodeEngine* engine = new GainNodeEngine(this, aContext->Destination());
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
engine->SetSourceStream(mStream);
}
GainNode::~GainNode()

View File

@ -29,7 +29,7 @@ public:
OscillatorNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination)
: AudioNodeEngine(aNode)
, mSource(nullptr)
, mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
, mDestination(aDestination->Stream())
, mStart(-1)
, mStop(STREAM_TIME_MAX)
// Keep the default values in sync with OscillatorNode::OscillatorNode.
@ -387,7 +387,7 @@ OscillatorNode::OscillatorNode(AudioContext* aContext)
{
OscillatorNodeEngine* engine = new OscillatorNodeEngine(this, aContext->Destination());
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::SOURCE_STREAM);
engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
engine->SetSourceStream(mStream);
mStream->AddMainThreadListener(this);
}
@ -421,6 +421,15 @@ OscillatorNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
return OscillatorNodeBinding::Wrap(aCx, this, aGivenProto);
}
void
OscillatorNode::DestroyMediaStream()
{
if (mStream) {
mStream->RemoveMainThreadListener(this);
}
AudioNode::DestroyMediaStream();
}
void
OscillatorNode::SendFrequencyToStream(AudioNode* aNode)
{
@ -458,14 +467,13 @@ void OscillatorNode::SendPeriodicWaveToStream()
{
NS_ASSERTION(mType == OscillatorType::Custom,
"Sending custom waveform to engine thread with non-custom type");
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
MOZ_ASSERT(ns, "Missing node stream.");
MOZ_ASSERT(mStream, "Missing node stream.");
MOZ_ASSERT(mPeriodicWave, "Send called without PeriodicWave object.");
SendInt32ParameterToStream(OscillatorNodeEngine::PERIODICWAVE,
mPeriodicWave->DataLength());
nsRefPtr<ThreadSharedFloatArrayBufferList> data =
mPeriodicWave->GetThreadSharedBuffer();
ns->SetBuffer(data.forget());
mStream->SetBuffer(data.forget());
}
void
@ -482,15 +490,14 @@ OscillatorNode::Start(double aWhen, ErrorResult& aRv)
}
mStartCalled = true;
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
if (!ns) {
if (!mStream) {
// Nothing to play, or we're already dead for some reason
return;
}
// TODO: Perhaps we need to do more here.
ns->SetStreamTimeParameter(OscillatorNodeEngine::START,
Context(), aWhen);
mStream->SetStreamTimeParameter(OscillatorNodeEngine::START,
Context(), aWhen);
MarkActive();
}
@ -508,15 +515,14 @@ OscillatorNode::Stop(double aWhen, ErrorResult& aRv)
return;
}
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
if (!ns || !Context()) {
if (!mStream || !Context()) {
// We've already stopped and had our stream shut down
return;
}
// TODO: Perhaps we need to do more here.
ns->SetStreamTimeParameter(OscillatorNodeEngine::STOP,
Context(), std::max(0.0, aWhen));
mStream->SetStreamTimeParameter(OscillatorNodeEngine::STOP,
Context(), std::max(0.0, aWhen));
}
void

View File

@ -29,13 +29,8 @@ public:
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
virtual void DestroyMediaStream() override
{
if (mStream) {
mStream->RemoveMainThreadListener(this);
}
AudioNode::DestroyMediaStream();
}
virtual void DestroyMediaStream() override;
virtual uint16_t NumberOfInputs() const final override
{
return 0;

View File

@ -540,9 +540,10 @@ PannerNode::FindConnectedSources(AudioNode* aNode,
// Recurse
FindConnectedSources(inputNodes[i].mInputNode, aSources, aNodesSeen);
// Check if this node is an AudioBufferSourceNode
// Check if this node is an AudioBufferSourceNode that still have a stream,
// which means it has not finished playing.
AudioBufferSourceNode* node = inputNodes[i].mInputNode->AsAudioBufferSourceNode();
if (node) {
if (node && node->GetStream()) {
aSources.AppendElement(node);
}
}

View File

@ -249,7 +249,7 @@ public:
: AudioNodeEngine(aNode)
, mSharedBuffers(aNode->GetSharedBuffers())
, mSource(nullptr)
, mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
, mDestination(aDestination->Stream())
, mBufferSize(aBufferSize)
, mInputWriteIndex(0)
, mSeenNonSilenceInput(false)
@ -490,7 +490,7 @@ ScriptProcessorNode::ScriptProcessorNode(AudioContext* aContext,
BufferSize(),
aNumberOfInputChannels);
mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
engine->SetSourceStream(mStream);
}
ScriptProcessorNode::~ScriptProcessorNode()

View File

@ -34,7 +34,7 @@ public:
AudioDestinationNode* aDestination)
: AudioNodeEngine(aNode)
, mSource(nullptr)
, mDestination(static_cast<AudioNodeStream*>(aDestination->Stream()))
, mDestination(aDestination->Stream())
// Keep the default value in sync with the default value in
// StereoPannerNode::StereoPannerNode.
, mPan(0.f)
@ -183,7 +183,7 @@ StereoPannerNode::StereoPannerNode(AudioContext* aContext)
StereoPannerNodeEngine* engine = new StereoPannerNodeEngine(this, aContext->Destination());
mStream = aContext->Graph()->CreateAudioNodeStream(engine,
MediaStreamGraph::INTERNAL_STREAM);
engine->SetSourceStream(static_cast<AudioNodeStream*>(mStream.get()));
engine->SetSourceStream(mStream);
}
StereoPannerNode::~StereoPannerNode()

View File

@ -326,7 +326,7 @@ WaveShaperNode::SetCurve(const Nullable<Float32Array>& aCurve)
mCurve = nullptr;
}
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
AudioNodeStream* ns = mStream;
MOZ_ASSERT(ns, "Why don't we have a stream here?");
ns->SetRawArrayData(curve);
}

View File

@ -115,6 +115,11 @@ GonkGPSGeolocationProvider::LocationCallback(GpsLocation* location)
MOZ_ASSERT(location);
const float kImpossibleAccuracy_m = 0.001;
if (location->accuracy < kImpossibleAccuracy_m) {
return;
}
nsRefPtr<nsGeoPosition> somewhere = new nsGeoPosition(location->latitude,
location->longitude,
location->altitude,

View File

@ -11,6 +11,7 @@ function testScript(script) {
SpecialPowers.pushPrefEnv({
"set": [["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.interception.opaque.enabled", true],
["dom.serviceWorkers.testing.enabled", true],
["dom.serviceWorkers.exemptFromPerDomainMax", true]]
}, function() {

View File

@ -374,6 +374,8 @@ var interfaceNamesInGlobalScope =
{ name: "DeviceStorage", desktop: false},
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "DeviceStorageChangeEvent", desktop: false},
// IMPORTANT: Do not change this list without review from a DOM peer!
"Directory",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "DisplayPortInputPort", b2g: true, permission: ["inputport"]},
// IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -11,8 +11,11 @@
* segment of ".." or ".". So the paths aren't allowed to walk up the directory
* tree. For example, paths like "../foo", "..", "/foo/bar" or "foo/../bar" are
* not allowed.
*
* http://w3c.github.io/filesystem-api/#idl-def-Directory
* https://microsoftedge.github.io/directory-upload/proposal.html#directory-interface
*/
[NoInterfaceObject]
[Exposed=Window]
interface Directory {
/*
* The leaf name of the directory.
@ -35,7 +38,7 @@ interface Directory {
* @return If succeeds, the promise is resolved with the new created
* File object. Otherwise, rejected with a DOM error.
*/
[NewObject]
[Pref="device.storage.enabled", NewObject]
Promise<File> createFile(DOMString path, optional CreateFileOptions options);
/*
@ -47,7 +50,7 @@ interface Directory {
* @return If succeeds, the promise is resolved with the new created
* Directory object. Otherwise, rejected with a DOM error.
*/
[NewObject]
[Pref="device.storage.enabled", NewObject]
Promise<Directory> createDirectory(DOMString path);
/*
@ -58,7 +61,7 @@ interface Directory {
* with a File or Directory object, depending on the entry's type. Otherwise,
* rejected with a DOM error.
*/
[NewObject]
[Pref="device.storage.enabled", NewObject]
Promise<(File or Directory)> get(DOMString path);
/*
@ -72,7 +75,7 @@ interface Directory {
* exist, the promise is resolved with boolean false. If the target did exist
* and was successfully deleted, the promise is resolved with boolean true.
*/
[NewObject]
[Pref="device.storage.enabled", NewObject]
Promise<boolean> remove((DOMString or File or Directory) path);
/*
@ -86,10 +89,26 @@ interface Directory {
* resolved with boolean false. If the target did exist and was successfully
* deleted, the promise is resolved with boolean true.
*/
[NewObject]
[Pref="device.storage.enabled", NewObject]
Promise<boolean> removeDeep((DOMString or File or Directory) path);
};
[Exposed=Window]
partial interface Directory {
// Already defined in the main interface declaration:
//readonly attribute DOMString name;
/*
* The base name of the directory (a relative path excluding the leaf name).
*/
readonly attribute DOMString path;
/*
* Getter for the immediate children of this directory.
*/
Promise<sequence<(File or Directory)>> getFilesAndDirectories();
};
enum CreateIfExistsMode { "replace", "fail" };
dictionary CreateFileOptions {

View File

@ -165,6 +165,7 @@ static_assert(MAX_WORKERS_PER_DOMAIN >= 1,
#define PREF_SERVICEWORKERS_ENABLED "dom.serviceWorkers.enabled"
#define PREF_SERVICEWORKERS_TESTING_ENABLED "dom.serviceWorkers.testing.enabled"
#define PREF_INTERCEPTION_ENABLED "dom.serviceWorkers.interception.enabled"
#define PREF_INTERCEPTION_OPAQUE_ENABLED "dom.serviceWorkers.interception.opaque.enabled"
namespace {
@ -1668,7 +1669,7 @@ RuntimeService::UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
}
}
if (!domainInfo->ActiveWorkerCount()) {
if (domainInfo->HasNoWorkers()) {
MOZ_ASSERT(domainInfo->mQueuedWorkers.IsEmpty());
mDomainMap.Remove(domain);
}
@ -1932,6 +1933,10 @@ RuntimeService::Init()
WorkerPrefChanged,
PREF_INTERCEPTION_ENABLED,
reinterpret_cast<void *>(WORKERPREF_INTERCEPTION_ENABLED))) ||
NS_FAILED(Preferences::RegisterCallbackAndCall(
WorkerPrefChanged,
PREF_INTERCEPTION_OPAQUE_ENABLED,
reinterpret_cast<void *>(WORKERPREF_INTERCEPTION_OPAQUE_ENABLED))) ||
NS_FAILED(Preferences::RegisterCallbackAndCall(
WorkerPrefChanged,
PREF_DOM_CACHES_TESTING_ENABLED,
@ -2143,6 +2148,10 @@ RuntimeService::Cleanup()
WorkerPrefChanged,
PREF_DOM_CACHES_TESTING_ENABLED,
reinterpret_cast<void *>(WORKERPREF_DOM_CACHES_TESTING))) ||
NS_FAILED(Preferences::UnregisterCallback(
WorkerPrefChanged,
PREF_INTERCEPTION_OPAQUE_ENABLED,
reinterpret_cast<void *>(WORKERPREF_INTERCEPTION_OPAQUE_ENABLED))) ||
NS_FAILED(Preferences::UnregisterCallback(
WorkerPrefChanged,
PREF_INTERCEPTION_ENABLED,
@ -2704,6 +2713,7 @@ RuntimeService::WorkerPrefChanged(const char* aPrefName, void* aClosure)
case WORKERPREF_DUMP:
#endif
case WORKERPREF_INTERCEPTION_ENABLED:
case WORKERPREF_INTERCEPTION_OPAQUE_ENABLED:
case WORKERPREF_SERVICEWORKERS:
case WORKERPREF_SERVICEWORKERS_TESTING:
sDefaultPreferences[key] = Preferences::GetBool(aPrefName, false);

View File

@ -56,9 +56,21 @@ class RuntimeService final : public nsIObserver
ActiveWorkerCount() const
{
return mActiveWorkers.Length() +
mActiveServiceWorkers.Length() +
mChildWorkerCount;
}
uint32_t
ActiveServiceWorkerCount() const
{
return mActiveServiceWorkers.Length();
}
bool
HasNoWorkers() const
{
return ActiveWorkerCount() == 0 &&
ActiveServiceWorkerCount() == 0;
}
};
struct IdleThreadInfo;

View File

@ -232,6 +232,17 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
return;
}
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
worker->AssertIsOnWorkerThread();
// Allow opaque response interception to be disabled until we can ensure the
// security implications are not a complete disaster.
if (response->Type() == ResponseType::Opaque &&
!worker->OpaqueInterceptionEnabled()) {
return;
}
// Section 4.2, step 2.2 "If either response's type is "opaque" and request's
// mode is not "no-cors" or response's type is error, return a network error."
if (((response->Type() == ResponseType::Opaque) && (mRequestMode != RequestMode::No_cors)) ||
@ -248,14 +259,10 @@ RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValu
return;
}
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(worker);
worker->AssertIsOnWorkerThread();
nsAutoPtr<RespondWithClosure> closure(
new RespondWithClosure(mInterceptedChannel, ir, worker->GetChannelInfo()));
nsCOMPtr<nsIInputStream> body;
response->GetBody(getter_AddRefs(body));
ir->GetInternalBody(getter_AddRefs(body));
// Errors and redirects may not have a body.
if (body) {
response->SetBodyUsed();

View File

@ -1291,6 +1291,13 @@ public:
return mPreferences[WORKERPREF_INTERCEPTION_ENABLED];
}
bool
OpaqueInterceptionEnabled() const
{
AssertIsOnWorkerThread();
return mPreferences[WORKERPREF_INTERCEPTION_OPAQUE_ENABLED];
}
bool
DOMWorkerNotificationEnabled() const
{

View File

@ -202,6 +202,7 @@ enum WorkerPreference
WORKERPREF_DOM_WORKERNOTIFICATION, // dom.webnotifications.workers.enabled
WORKERPREF_DOM_CACHES_TESTING, // dom.caches.testing.enabled
WORKERPREF_SERVICEWORKERS_TESTING, // dom.serviceWorkers.testing.enabled
WORKERPREF_INTERCEPTION_OPAQUE_ENABLED, // dom.serviceWorkers.interception.opaque.enabled
WORKERPREF_COUNT
};

View File

@ -144,6 +144,8 @@ support-files =
sw_clients/navigator.html
eval_worker.js
test_eval_not_allowed.html^headers^
opaque_intercept_worker.js
notify_loaded.js
[test_app_protocol.html]
[test_bug1151916.html]
@ -201,3 +203,4 @@ skip-if = toolkit == 'android' # Bug 1163410
[test_workerUnregister.html]
[test_workerUpdate.html]
[test_workerupdatefoundevent.html]
[test_opaque_intercept.html]

View File

@ -0,0 +1 @@
parent.postMessage('SCRIPT_LOADED', '*');

View File

@ -0,0 +1,25 @@
var name = 'opaqueInterceptCache';
// Cross origin request to ensure that an opaque response is used
var prefix = 'http://example.com/tests/dom/workers/test/serviceworkers/'
self.addEventListener('install', function(event) {
var request = new Request(prefix + 'notify_loaded.js', { mode: 'no-cors' });
event.waitUntil(
Promise.all([caches.open(name), fetch(request)]).then(function(results) {
var cache = results[0];
var response = results[1];
return cache.put('./sw_clients/does_not_exist.js', response);
})
);
});
self.addEventListener('fetch', function (event) {
event.respondWith(
caches.open(name).then(function(cache) {
return cache.match(event.request);
}).then(function(response) {
return response || fetch(event.request);
})
);
});

View File

@ -6,8 +6,9 @@
<html>
<head>
<title>Bug 982726 - test match_all not crashing</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<!-- some tests will intercept this bogus script request -->
<script type="text/javascript" src="does_not_exist.js"></script>
</head>
<body>
<p id="display"></p>
@ -16,7 +17,7 @@
<script class="testbody" type="text/javascript">
if (!parent) {
info("sw_clients/simple.html shouldn't be launched directly!");
dump("sw_clients/simple.html shouldn't be launched directly!");
}
window.addEventListener("message", function(event) {

View File

@ -74,6 +74,7 @@
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.interception.enabled", true],
["dom.serviceWorkers.interception.opaque.enabled", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true],
]}, runTest);

View File

@ -0,0 +1,86 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>
<head>
<title>Bug 982726 - Test service worker post message </title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<script class="testbody" type="text/javascript">
var registration;
function start() {
return navigator.serviceWorker.register("opaque_intercept_worker.js",
{ scope: "./sw_clients/" })
.then((swr) => registration = swr);
}
function unregister() {
return registration.unregister().then(function(result) {
ok(result, "Unregister should return true.");
}, function(e) {
dump("Unregistering the SW failed with " + e + "\n");
});
}
function testOpaqueIntercept(swr) {
var p = new Promise(function(res, rej) {
var ready = false;
var scriptLoaded = false;
window.onmessage = function(e) {
if (e.data === "READY") {
ok(!ready, "ready message should only be received once");
ok(!scriptLoaded, "ready message should be received before script loaded");
if (ready) {
res();
return;
}
ready = true;
iframe.contentWindow.postMessage("REFRESH", "*");
} else if (e.data === "SCRIPT_LOADED") {
ok(ready, "script loaded should be received after ready");
ok(!scriptLoaded, "script loaded message should be received only once");
scriptLoaded = true;
res();
}
}
});
var content = document.getElementById("content");
ok(content, "Parent exists.");
iframe = document.createElement("iframe");
iframe.setAttribute('src', "sw_clients/refresher.html");
content.appendChild(iframe);
return p.then(() => content.removeChild(iframe));
}
function runTest() {
start()
.then(testOpaqueIntercept)
.then(unregister)
.catch(function(e) {
ok(false, "Some test failed with error " + e);
}).then(SimpleTest.finish);
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [
["dom.serviceWorkers.exemptFromPerDomainMax", true],
["dom.serviceWorkers.enabled", true],
["dom.serviceWorkers.testing.enabled", true],
["dom.serviceWorkers.interception.opaque.enabled", true],
["dom.caches.enabled", true],
]}, runTest);
</script>
</pre>
</body>
</html>

View File

@ -34,6 +34,7 @@ int32_t GrallocImage::sColorIdMap[] = {
HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED, HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED,
HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS, HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS,
HAL_PIXEL_FORMAT_YV12, OMX_COLOR_FormatYUV420Planar,
HAL_PIXEL_FORMAT_RGBA_8888, -1,
0, 0
};
@ -361,6 +362,21 @@ ConvertOmxYUVFormatToRGB565(android::sp<GraphicBuffer>& aBuffer,
return OK;
}
if (format == HAL_PIXEL_FORMAT_RGBA_8888) {
uint32_t* src = (uint32_t*)(buffer);
uint16_t* dest = (uint16_t*)(aMappedSurface->mData);
// Convert RGBA8888 to RGB565
for (size_t i = 0; i < width * height; i++) {
uint32_t r = ((*src >> 0 ) & 0xFF);
uint32_t g = ((*src >> 8 ) & 0xFF);
uint32_t b = ((*src >> 16) & 0xFF);
*dest++ = ((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3) << 0);
src++;
}
return OK;
}
android::ColorConverter colorConverter((OMX_COLOR_FORMATTYPE)omxFormat,
OMX_COLOR_Format16bitRGB565);
if (!colorConverter.isValid()) {

View File

@ -303,7 +303,7 @@ public:
* Can be called on any thread. This method takes mReentrantMonitor
* when accessing thread-shared state.
*/
already_AddRefed<Image> CreateImage(ImageFormat aFormat);
B2G_ACL_EXPORT already_AddRefed<Image> CreateImage(ImageFormat aFormat);
/**
* Set an Image as the current image to display. The Image must have
@ -523,7 +523,7 @@ private:
typedef mozilla::ReentrantMonitor ReentrantMonitor;
// Private destructor, to discourage deletion outside of Release():
~ImageContainer();
B2G_ACL_EXPORT ~ImageContainer();
void SetCurrentImageInternal(Image* aImage);

View File

@ -485,7 +485,7 @@ private:
* Here goes the shut-down code that uses virtual methods.
* Must only be called by Release().
*/
void Finalize();
B2G_ACL_EXPORT void Finalize();
friend class AtomicRefCountedWithFinalize<TextureClient>;

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