mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central to fx-team
This commit is contained in:
commit
a6385dd7a1
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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*
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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]
|
||||
|
97
dom/devicestorage/test/test_fs_getFilesAndDirectories.html
Normal file
97
dom/devicestorage/test/test_fs_getFilesAndDirectories.html
Normal 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>
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -157,7 +157,7 @@ public:
|
||||
* to dispatch events.
|
||||
*/
|
||||
static void OnCompositionEventDiscarded(
|
||||
const WidgetCompositionEvent* aCompositionEvent);
|
||||
WidgetCompositionEvent* aCompositionEvent);
|
||||
|
||||
/**
|
||||
* Get TextComposition from widget.
|
||||
|
@ -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?");
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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*
|
||||
|
@ -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)
|
||||
|
||||
|
249
dom/filesystem/GetDirectoryListingTask.cpp
Normal file
249
dom/filesystem/GetDirectoryListingTask.cpp
Normal 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
|
67
dom/filesystem/GetDirectoryListingTask.h
Normal file
67
dom/filesystem/GetDirectoryListingTask.h
Normal 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
|
@ -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;
|
||||
};
|
||||
|
@ -23,6 +23,7 @@ UNIFIED_SOURCES += [
|
||||
'FileSystemRequestParent.cpp',
|
||||
'FileSystemTaskBase.cpp',
|
||||
'FileSystemUtils.cpp',
|
||||
'GetDirectoryListingTask.cpp',
|
||||
'GetFileOrDirectoryTask.cpp',
|
||||
'RemoveTask.cpp',
|
||||
]
|
||||
|
@ -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 {
|
||||
|
@ -199,7 +199,7 @@ public:
|
||||
}
|
||||
|
||||
mozilla::net::ReferrerPolicy
|
||||
GetImageReferrerPolicy()
|
||||
GetImageReferrerPolicy() override
|
||||
{
|
||||
return GetReferrerPolicy();
|
||||
}
|
||||
|
@ -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()));
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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]
|
||||
|
101
dom/indexedDB/test/test_sandbox.html
Normal file
101
dom/indexedDB/test/test_sandbox.html
Normal 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>
|
78
dom/indexedDB/test/unit/test_sandbox.js
Normal file
78
dom/indexedDB/test/unit/test_sandbox.js
Normal 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);
|
||||
}
|
@ -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]
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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*
|
||||
|
@ -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:
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
23
dom/media/test/crashtests/doppler-1.html
Normal file
23
dom/media/test/crashtests/doppler-1.html
Normal 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>
|
@ -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]
|
||||
|
@ -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>
|
||||
|
109
dom/media/test/test_eme_request_notifications.html
Normal file
109
dom/media/test/test_eme_request_notifications.html
Normal 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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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() {
|
||||
|
@ -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!
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -1291,6 +1291,13 @@ public:
|
||||
return mPreferences[WORKERPREF_INTERCEPTION_ENABLED];
|
||||
}
|
||||
|
||||
bool
|
||||
OpaqueInterceptionEnabled() const
|
||||
{
|
||||
AssertIsOnWorkerThread();
|
||||
return mPreferences[WORKERPREF_INTERCEPTION_OPAQUE_ENABLED];
|
||||
}
|
||||
|
||||
bool
|
||||
DOMWorkerNotificationEnabled() const
|
||||
{
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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]
|
||||
|
1
dom/workers/test/serviceworkers/notify_loaded.js
Normal file
1
dom/workers/test/serviceworkers/notify_loaded.js
Normal file
@ -0,0 +1 @@
|
||||
parent.postMessage('SCRIPT_LOADED', '*');
|
25
dom/workers/test/serviceworkers/opaque_intercept_worker.js
Normal file
25
dom/workers/test/serviceworkers/opaque_intercept_worker.js
Normal 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);
|
||||
})
|
||||
);
|
||||
});
|
@ -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) {
|
||||
|
Binary file not shown.
@ -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);
|
||||
|
86
dom/workers/test/serviceworkers/test_opaque_intercept.html
Normal file
86
dom/workers/test/serviceworkers/test_opaque_intercept.html
Normal 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>
|
@ -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()) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user