author | Wes Kocher <wkocher@mozilla.com> |
Wed, 09 Sep 2015 13:47:08 -0700 | |
changeset 261592 | dd2a1d737a64d9a3f23714ec5cc623ec8933b51f |
parent 261521 | 7c8e40e00bfe521953b69051e5d59551a684c346 (current diff) |
parent 261591 | 8480dd03b9c19cd3be93ac0b274bb83f29b1610a (diff) |
child 261593 | 18f5b185fb971492bcb820ec4881141939ea497b |
child 261594 | c638ef3e2f2077dde2c1d9a174bf4bddc2e20714 |
child 261618 | 89103605d401c6f6f0b2a702b289b40e2f135e69 |
child 261657 | b451aaf236d09fa4221544c30b7058782267ff95 |
push id | 29348 |
push user | kwierso@gmail.com |
push date | Wed, 09 Sep 2015 20:47:39 +0000 |
treeherder | mozilla-central@dd2a1d737a64 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 43.0a1 |
first release with | nightly linux32
dd2a1d737a64
/
43.0a1
/
20150910030225
/
files
nightly linux64
dd2a1d737a64
/
43.0a1
/
20150910030225
/
files
nightly mac
dd2a1d737a64
/
43.0a1
/
20150910030225
/
files
nightly win32
dd2a1d737a64
/
43.0a1
/
20150910030225
/
files
nightly win64
dd2a1d737a64
/
43.0a1
/
20150910030225
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
43.0a1
/
20150910030225
/
pushlog to previous
nightly linux64
43.0a1
/
20150910030225
/
pushlog to previous
nightly mac
43.0a1
/
20150910030225
/
pushlog to previous
nightly win32
43.0a1
/
20150910030225
/
pushlog to previous
nightly win64
43.0a1
/
20150910030225
/
pushlog to previous
|
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1293,16 +1293,17 @@ pref("services.sync.prefs.sync.privacy.c pref("services.sync.prefs.sync.privacy.clearOnShutdown.formdata", true); pref("services.sync.prefs.sync.privacy.clearOnShutdown.history", true); pref("services.sync.prefs.sync.privacy.clearOnShutdown.offlineApps", true); pref("services.sync.prefs.sync.privacy.clearOnShutdown.sessions", true); pref("services.sync.prefs.sync.privacy.clearOnShutdown.siteSettings", true); pref("services.sync.prefs.sync.privacy.donottrackheader.enabled", true); pref("services.sync.prefs.sync.privacy.sanitize.sanitizeOnShutdown", true); pref("services.sync.prefs.sync.privacy.trackingprotection.enabled", true); +pref("services.sync.prefs.sync.privacy.trackingprotection.pbmode.enabled", true); pref("services.sync.prefs.sync.security.OCSP.enabled", true); pref("services.sync.prefs.sync.security.OCSP.require", true); pref("services.sync.prefs.sync.security.default_personal_cert", true); pref("services.sync.prefs.sync.security.tls.version.min", true); pref("services.sync.prefs.sync.security.tls.version.max", true); pref("services.sync.prefs.sync.signon.rememberSignons", true); pref("services.sync.prefs.sync.spellchecker.dictionary", true); pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
--- a/build/gen_test_packages_manifest.py +++ b/build/gen_test_packages_manifest.py @@ -13,24 +13,26 @@ ALL_HARNESSES = [ 'mochitest', 'reftest', 'webapprt', 'xpcshell', 'cppunittest', 'jittest', 'mozbase', 'web-platform', + 'talos', ] PACKAGE_SPECIFIED_HARNESSES = [ 'cppunittest', 'mochitest', 'reftest', 'xpcshell', 'web-platform', + 'talos', ] def parse_args(): parser = ArgumentParser(description='Generate a test_packages.json file to tell automation which harnesses require which test packages.') parser.add_argument("--common", required=True, action="store", dest="tests_common", help="Name of the \"common\" archive, a package to be used by all harnesses.")
--- a/build/unix/stdc++compat/Makefile.in +++ b/build/unix/stdc++compat/Makefile.in @@ -1,10 +1,7 @@ # 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/. ENABLE_CLANG_PLUGIN := include $(topsrcdir)/config/rules.mk - -CXXFLAGS += -DMOZ_LIBSTDCXX_VERSION=$(MOZ_LIBSTDCXX_TARGET_VERSION) -HOST_CXXFLAGS += -DMOZ_LIBSTDCXX_VERSION=$(MOZ_LIBSTDCXX_HOST_VERSION)
--- a/build/unix/stdc++compat/moz.build +++ b/build/unix/stdc++compat/moz.build @@ -14,8 +14,11 @@ if CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']: 'stdc++compat.cpp', ] FORCE_STATIC_LIB = True NO_PGO = True DISABLE_STL_WRAPPING = True + +DEFINES['MOZ_LIBSTDCXX_VERSION'] = CONFIG['MOZ_LIBSTDCXX_TARGET_VERSION'] +HOST_DEFINES['MOZ_LIBSTDCXX_VERSION'] = CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']
--- a/config/Makefile.in +++ b/config/Makefile.in @@ -36,18 +36,16 @@ HEADERS_FILES = \ $(NULL) HEADERS_DEST := $(DIST)/include HEADERS_TARGET := export INSTALL_TARGETS += HEADERS endif include $(topsrcdir)/config/rules.mk -HOST_CFLAGS += -DUNICODE -D_UNICODE - ifndef JS_STANDALONE ifndef MOZ_PROFILE_USE # Generate a new buildid every time we "export" in config... that's only # supposed to be once per-build! export:: ifdef MOZ_BUILD_DATE printf '%s' $(MOZ_BUILD_DATE) > buildid else
--- a/config/config.mk +++ b/config/config.mk @@ -423,16 +423,19 @@ COMPILE_CXXFLAGS = $(if $(DISABLE_STL_WR COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS) $(MOZBUILD_CMFLAGS) COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS) $(MOZBUILD_CMMFLAGS) ASFLAGS += $(MOZBUILD_ASFLAGS) ifndef CROSS_COMPILE HOST_CFLAGS += $(RTL_FLAGS) endif +HOST_CFLAGS += $(HOST_DEFINES) $(MOZBUILD_HOST_CFLAGS) +HOST_CXXFLAGS += $(HOST_DEFINES) $(MOZBUILD_HOST_CXXFLAGS) + # # Name of the binary code directories # # Override defaults # Default location of include files ifndef LIBXUL_SDK IDL_PARSER_DIR = $(topsrcdir)/xpcom/idl-parser
--- a/config/moz.build +++ b/config/moz.build @@ -40,8 +40,13 @@ PYTHON_UNIT_TESTS += [ 'tests/unit-printprereleasesuffix.py', ] if CONFIG['GNU_CC'] and CONFIG['MOZ_OPTIMIZE']: CFLAGS += ['-O3'] # XXX: We should fix these warnings. ALLOW_COMPILER_WARNINGS = True + +HOST_DEFINES = { + 'UNICODE': True, + '_UNICODE': True, +}
--- a/configure.in +++ b/configure.in @@ -1478,17 +1478,16 @@ if test "$GNU_CC"; then fi # Turn on gcc/clang warnings: # https://gcc.gnu.org/onlinedocs/gcc-4.4.0/gcc/Warning-Options.html # # -Wall - turn on a lot of warnings # -Wchar-subscripts - catches array index using signed char # -Wcomment - catches nested comments - # -Wdeclaration-after-statement - MSVC doesn't like these # -Wempty-body - catches bugs, e.g. "if (c); foo();", few false positives # -Wendif-labels - catches `#else FOO` and `#endif FOO` not in comment # -Wenum-compare - catches comparison of different enum types # -Wignored-qualifiers - catches returns types with qualifiers like const # -Wint-to-pointer-cast - catches cast to pointer from integer of different size # -Wmultichar - catches multicharacter integer constants like 'THIS' # -Wnon-literal-null-conversion - catches expressions used as a null pointer constant # -Wnonnull - catches NULL used with functions arguments marked as non-null @@ -1498,17 +1497,16 @@ if test "$GNU_CC"; then # -Wreturn-type - catches missing returns, zero false positives # -Wsequence-point - catches undefined order behavior like `a = a++` # -Wsign-compare - catches comparison of signed and unsigned types # -Wtrigraphs - catches unlikely use of trigraphs # -Wtype-limits - catches overflow bugs, few false positives # -Wunknown-pragmas - catches unexpected #pragma directives # _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wall" - _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wdeclaration-after-statement" _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wempty-body" _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wpointer-to-int-cast" _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wsign-compare" _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wtype-limits" # Treat some warnings as errors if --enable-warnings-as-errors: if test "$MOZ_ENABLE_WARNINGS_AS_ERRORS"; then _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Werror=char-subscripts" @@ -1834,17 +1832,17 @@ case "$host" in HOST_AR=lib HOST_AR_FLAGS='-NOLOGO -OUT:$@' HOST_CFLAGS="$HOST_CFLAGS -TC -nologo" HOST_RANLIB='echo ranlib' else HOST_CFLAGS="$HOST_CFLAGS -mwindows" fi HOST_CFLAGS="$HOST_CFLAGS -DXP_WIN32 -DXP_WIN -DWIN32 -D_WIN32 -DNO_X11 -D_CRT_SECURE_NO_WARNINGS" - HOST_NSPR_MDCPUCFG='\"md/_winnt.cfg\"' + HOST_NSPR_MDCPUCFG='"md/_winnt.cfg"' HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O2}" HOST_BIN_SUFFIX=.exe case "$host" in *mingw*) PERL="/bin/sh ${_topsrcdir}/build/msys-perl-wrapper" ;; esac @@ -1860,23 +1858,23 @@ case "$host" in fi HOST_CFLAGS="$HOST_CFLAGS -D_AMD64_" ;; esac ;; *-darwin*) HOST_CFLAGS="$HOST_CFLAGS -DXP_UNIX -DXP_MACOSX -DNO_X11" - HOST_NSPR_MDCPUCFG='\"md/_darwin.cfg\"' + HOST_NSPR_MDCPUCFG='"md/_darwin.cfg"' HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O3}" ;; *-linux*|*-kfreebsd*-gnu|*-gnu*) HOST_CFLAGS="$HOST_CFLAGS -DXP_UNIX" - HOST_NSPR_MDCPUCFG='\"md/_linux.cfg\"' + HOST_NSPR_MDCPUCFG='"md/_linux.cfg"' HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O3}" ;; *) HOST_CFLAGS="$HOST_CFLAGS -DXP_UNIX" HOST_OPTIMIZE_FLAGS="${HOST_OPTIMIZE_FLAGS=-O2}" ;; esac
--- a/dom/base/File.cpp +++ b/dom/base/File.cpp @@ -375,28 +375,16 @@ Blob::Constructor( } int64_t Blob::GetFileId() { return mImpl->GetFileId(); } -void -Blob::AddFileInfo(indexedDB::FileInfo* aFileInfo) -{ - mImpl->AddFileInfo(aFileInfo); -} - -indexedDB::FileInfo* -Blob::GetFileInfo(indexedDB::FileManager* aFileManager) -{ - return mImpl->GetFileInfo(aFileManager); -} - bool Blob::IsMemoryFile() const { return mImpl->IsMemoryFile(); } void Blob::GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) @@ -457,46 +445,16 @@ File::CreateMemoryFile(nsISupports* aPar /* static */ already_AddRefed<File> File::CreateFromFile(nsISupports* aParent, nsIFile* aFile, bool aTemporary) { nsRefPtr<File> file = new File(aParent, new BlobImplFile(aFile, aTemporary)); return file.forget(); } /* static */ already_AddRefed<File> -File::CreateFromFile(nsISupports* aParent, const nsAString& aContentType, - uint64_t aLength, nsIFile* aFile, - indexedDB::FileInfo* aFileInfo) -{ - nsRefPtr<File> file = new File(aParent, - new BlobImplFile(aContentType, aLength, aFile, aFileInfo)); - return file.forget(); -} - -/* static */ already_AddRefed<File> -File::CreateFromFile(nsISupports* aParent, const nsAString& aName, - const nsAString& aContentType, - uint64_t aLength, nsIFile* aFile, - indexedDB::FileInfo* aFileInfo) -{ - nsRefPtr<File> file = new File(aParent, - new BlobImplFile(aName, aContentType, aLength, aFile, aFileInfo)); - return file.forget(); -} - -/* static */ already_AddRefed<File> -File::CreateFromFile(nsISupports* aParent, nsIFile* aFile, - indexedDB::FileInfo* aFileInfo) -{ - nsRefPtr<File> file = new File(aParent, - new BlobImplFile(aFile, aFileInfo)); - return file.forget(); -} - -/* static */ already_AddRefed<File> File::CreateFromFile(nsISupports* aParent, nsIFile* aFile, const nsAString& aName, const nsAString& aContentType) { nsRefPtr<File> file = new File(aParent, new BlobImplFile(aFile, aName, aContentType)); return file.forget(); } @@ -795,88 +753,17 @@ void BlobImplBase::SetLastModified(int64_t aLastModified) { mLastModificationDate = aLastModified * PR_USEC_PER_MSEC; } int64_t BlobImplBase::GetFileId() { - int64_t id = -1; - - if (IsStoredFile() && IsWholeFile() && !IsSnapshot()) { - if (!indexedDB::IndexedDatabaseManager::IsClosed()) { - indexedDB::IndexedDatabaseManager::FileMutex().Lock(); - } - - NS_ASSERTION(!mFileInfos.IsEmpty(), - "A stored file must have at least one file info!"); - - nsRefPtr<indexedDB::FileInfo>& fileInfo = mFileInfos.ElementAt(0); - if (fileInfo) { - id = fileInfo->Id(); - } - - if (!indexedDB::IndexedDatabaseManager::IsClosed()) { - indexedDB::IndexedDatabaseManager::FileMutex().Unlock(); - } - } - - return id; -} - -void -BlobImplBase::AddFileInfo(indexedDB::FileInfo* aFileInfo) -{ - if (indexedDB::IndexedDatabaseManager::IsClosed()) { - NS_ERROR("Shouldn't be called after shutdown!"); - return; - } - - nsRefPtr<indexedDB::FileInfo> fileInfo = aFileInfo; - - MutexAutoLock lock(indexedDB::IndexedDatabaseManager::FileMutex()); - - NS_ASSERTION(!mFileInfos.Contains(aFileInfo), - "Adding the same file info agan?!"); - - nsRefPtr<indexedDB::FileInfo>* element = mFileInfos.AppendElement(); - element->swap(fileInfo); -} - -indexedDB::FileInfo* -BlobImplBase::GetFileInfo(indexedDB::FileManager* aFileManager) -{ - if (indexedDB::IndexedDatabaseManager::IsClosed()) { - NS_ERROR("Shouldn't be called after shutdown!"); - return nullptr; - } - - // A slice created from a stored file must keep the file info alive. - // However, we don't support sharing of slices yet, so the slice must be - // copied again. That's why we have to ignore the first file info. - // Snapshots are handled in a similar way (they have to be copied). - uint32_t startIndex; - if (IsStoredFile() && (!IsWholeFile() || IsSnapshot())) { - startIndex = 1; - } - else { - startIndex = 0; - } - - MutexAutoLock lock(indexedDB::IndexedDatabaseManager::FileMutex()); - - for (uint32_t i = startIndex; i < mFileInfos.Length(); i++) { - nsRefPtr<indexedDB::FileInfo>& fileInfo = mFileInfos.ElementAt(i); - if (fileInfo->Manager() == aFileManager) { - return fileInfo; - } - } - - return nullptr; + return -1; } nsresult BlobImplBase::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength, nsACString& aContentType, nsACString& aCharset) { MOZ_ASSERT(aContentLength);
--- a/dom/base/File.h +++ b/dom/base/File.h @@ -3,25 +3,23 @@ /* 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_File_h #define mozilla_dom_File_h #include "mozilla/Attributes.h" - +#include "mozilla/ErrorResult.h" #include "mozilla/GuardObjects.h" #include "mozilla/LinkedList.h" #include "mozilla/StaticMutex.h" #include "mozilla/StaticPtr.h" +#include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/Date.h" -#include "mozilla/dom/indexedDB/FileInfo.h" -#include "mozilla/dom/indexedDB/FileManager.h" -#include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" #include "nsAutoPtr.h" #include "nsCycleCollectionParticipant.h" #include "nsCOMPtr.h" #include "nsIDOMBlob.h" #include "nsIFile.h" #include "nsIMutable.h" #include "nsIXMLHttpRequest.h" #include "nsString.h" @@ -34,20 +32,16 @@ class nsIInputStream; #define BLOBIMPL_IID \ { 0xbccb3275, 0x6778, 0x4ac5, \ { 0xaf, 0x03, 0x90, 0xed, 0x37, 0xad, 0xdf, 0x5d } } namespace mozilla { namespace dom { -namespace indexedDB { -class FileInfo; -} // namespace indexedDB - struct BlobPropertyBag; struct ChromeFilePropertyBag; struct FilePropertyBag; class BlobImpl; class File; class OwningArrayBufferOrArrayBufferViewOrBlobOrString; /** @@ -131,22 +125,16 @@ public: ErrorResult& aRv); void GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv); int64_t GetFileId(); - indexedDB::FileInfo* - GetFileInfo(indexedDB::FileManager* aFileManager); - - void - AddFileInfo(indexedDB::FileInfo* aFileInfo); - // WebIDL methods nsISupports* GetParentObject() const { return mParent; } bool IsMemoryFile() const; @@ -217,30 +205,16 @@ public: CreateMemoryFile(nsISupports* aParent, void* aMemoryBuffer, uint64_t aLength, const nsAString& aName, const nsAString& aContentType, int64_t aLastModifiedDate); static already_AddRefed<File> CreateFromFile(nsISupports* aParent, nsIFile* aFile, bool aTemporary = false); static already_AddRefed<File> - CreateFromFile(nsISupports* aParent, const nsAString& aContentType, - uint64_t aLength, nsIFile* aFile, - indexedDB::FileInfo* aFileInfo); - - static already_AddRefed<File> - CreateFromFile(nsISupports* aParent, const nsAString& aName, - const nsAString& aContentType, uint64_t aLength, - nsIFile* aFile, indexedDB::FileInfo* aFileInfo); - - static already_AddRefed<File> - CreateFromFile(nsISupports* aParent, nsIFile* aFile, - indexedDB::FileInfo* aFileInfo); - - static already_AddRefed<File> CreateFromFile(nsISupports* aParent, nsIFile* aFile, const nsAString& aName, const nsAString& aContentType); // WebIDL methods virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override; @@ -340,21 +314,16 @@ public: virtual const nsTArray<nsRefPtr<BlobImpl>>* GetSubBlobImpls() const = 0; virtual void GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) = 0; virtual int64_t GetFileId() = 0; - virtual void AddFileInfo(indexedDB::FileInfo* aFileInfo) = 0; - - virtual indexedDB::FileInfo* - GetFileInfo(indexedDB::FileManager* aFileManager) = 0; - virtual nsresult GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength, nsACString& aContentType, nsACString& aCharset) = 0; virtual nsresult GetMutable(bool* aMutable) const = 0; virtual nsresult SetMutable(bool aMutable) = 0; @@ -512,37 +481,30 @@ public: virtual void GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) override { aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); } virtual int64_t GetFileId() override; - virtual void AddFileInfo(indexedDB::FileInfo* aFileInfo) override; - - virtual indexedDB::FileInfo* - GetFileInfo(indexedDB::FileManager* aFileManager) override; - virtual nsresult GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength, nsACString& aContentType, nsACString& aCharset) override; virtual nsresult GetMutable(bool* aMutable) const override; virtual nsresult SetMutable(bool aMutable) override; virtual void SetLazyData(const nsAString& aName, const nsAString& aContentType, uint64_t aLength, int64_t aLastModifiedDate, BlobDirState aDirState) override { - NS_ASSERTION(aLength, "must have length"); - mName = aName; mContentType = aContentType; mLength = aLength; mLastModificationDate = aLastModifiedDate; mIsFile = !aName.IsVoid(); } virtual bool IsMemoryFile() const override @@ -585,72 +547,45 @@ public: return mDirState == BlobDirState::eIsDir; } virtual BlobDirState GetDirState() const override { return mDirState; } - virtual bool IsStoredFile() const - { - return false; - } - - virtual bool IsWholeFile() const - { - NS_NOTREACHED("Should only be called on dom blobs backed by files!"); - return false; - } - - virtual bool IsSnapshot() const - { - return false; - } - virtual bool IsSizeUnknown() const override { return mLength == UINT64_MAX; } protected: virtual ~BlobImplBase() {} /** * Returns a new, effectively-unique serial number. This should be used * by implementations to obtain a serial number for GetSerialNumber(). * The implementation is thread safe. */ static uint64_t NextSerialNumber(); - indexedDB::FileInfo* GetFileInfo() const - { - NS_ASSERTION(IsStoredFile(), "Should only be called on stored files!"); - NS_ASSERTION(!mFileInfos.IsEmpty(), "Must have at least one file info!"); - - return mFileInfos.ElementAt(0); - } - bool mIsFile; bool mImmutable; BlobDirState mDirState; nsString mContentType; nsString mName; nsString mPath; // The path relative to a directory chosen by the user uint64_t mStart; uint64_t mLength; int64_t mLastModificationDate; const uint64_t mSerialNumber; - - // Protected by IndexedDatabaseManager::FileMutex() - nsTArray<nsRefPtr<indexedDB::FileInfo>> mFileInfos; }; /** * This class may be used off the main thread, and in particular, its * constructor and destructor may not run on the same thread. Be careful! */ class BlobImplMemory final : public BlobImplBase { @@ -788,113 +723,65 @@ class BlobImplFile : public BlobImplBase public: NS_DECL_ISUPPORTS_INHERITED // Create as a file explicit BlobImplFile(nsIFile* aFile, bool aTemporary = false) : BlobImplBase(EmptyString(), EmptyString(), UINT64_MAX, INT64_MAX) , mFile(aFile) , mWholeFile(true) - , mStoredFile(false) , mIsTemporary(aTemporary) { NS_ASSERTION(mFile, "must have file"); // Lazily get the content type and size mContentType.SetIsVoid(true); mFile->GetLeafName(mName); } - BlobImplFile(nsIFile* aFile, indexedDB::FileInfo* aFileInfo) - : BlobImplBase(EmptyString(), EmptyString(), UINT64_MAX, INT64_MAX) - , mFile(aFile) - , mWholeFile(true) - , mStoredFile(true) - , mIsTemporary(false) - { - NS_ASSERTION(mFile, "must have file"); - NS_ASSERTION(aFileInfo, "must have file info"); - // Lazily get the content type and size - mContentType.SetIsVoid(true); - mFile->GetLeafName(mName); - - mFileInfos.AppendElement(aFileInfo); - } - // Create as a file BlobImplFile(const nsAString& aName, const nsAString& aContentType, uint64_t aLength, nsIFile* aFile) : BlobImplBase(aName, aContentType, aLength, UINT64_MAX) , mFile(aFile) , mWholeFile(true) - , mStoredFile(false) , mIsTemporary(false) { NS_ASSERTION(mFile, "must have file"); } BlobImplFile(const nsAString& aName, const nsAString& aContentType, uint64_t aLength, nsIFile* aFile, int64_t aLastModificationDate) : BlobImplBase(aName, aContentType, aLength, aLastModificationDate) , mFile(aFile) , mWholeFile(true) - , mStoredFile(false) , mIsTemporary(false) { NS_ASSERTION(mFile, "must have file"); } // Create as a file with custom name BlobImplFile(nsIFile* aFile, const nsAString& aName, const nsAString& aContentType) : BlobImplBase(aName, aContentType, UINT64_MAX, INT64_MAX) , mFile(aFile) , mWholeFile(true) - , mStoredFile(false) , mIsTemporary(false) { NS_ASSERTION(mFile, "must have file"); if (aContentType.IsEmpty()) { // Lazily get the content type and size mContentType.SetIsVoid(true); } } - // Create as a stored file - BlobImplFile(const nsAString& aName, const nsAString& aContentType, - uint64_t aLength, nsIFile* aFile, - indexedDB::FileInfo* aFileInfo) - : BlobImplBase(aName, aContentType, aLength, UINT64_MAX) - , mFile(aFile) - , mWholeFile(true) - , mStoredFile(true) - , mIsTemporary(false) - { - NS_ASSERTION(mFile, "must have file"); - mFileInfos.AppendElement(aFileInfo); - } - - // Create as a stored blob - BlobImplFile(const nsAString& aContentType, uint64_t aLength, - nsIFile* aFile, indexedDB::FileInfo* aFileInfo) - : BlobImplBase(aContentType, aLength) - , mFile(aFile) - , mWholeFile(true) - , mStoredFile(true) - , mIsTemporary(false) - { - NS_ASSERTION(mFile, "must have file"); - mFileInfos.AppendElement(aFileInfo); - } - // Create as a file to be later initialized BlobImplFile() : BlobImplBase(EmptyString(), EmptyString(), UINT64_MAX, INT64_MAX) , mWholeFile(true) - , mStoredFile(false) , mIsTemporary(false) { // Lazily get the content type and size mContentType.SetIsVoid(true); mName.SetIsVoid(true); } // Overrides @@ -927,55 +814,27 @@ protected: private: // Create slice BlobImplFile(const BlobImplFile* aOther, uint64_t aStart, uint64_t aLength, const nsAString& aContentType) : BlobImplBase(aContentType, aOther->mStart + aStart, aLength) , mFile(aOther->mFile) , mWholeFile(false) - , mStoredFile(aOther->mStoredFile) , mIsTemporary(false) { NS_ASSERTION(mFile, "must have file"); mImmutable = aOther->mImmutable; - - if (mStoredFile) { - indexedDB::FileInfo* fileInfo; - - using indexedDB::IndexedDatabaseManager; - - if (IndexedDatabaseManager::IsClosed()) { - fileInfo = aOther->GetFileInfo(); - } - else { - mozilla::MutexAutoLock lock(IndexedDatabaseManager::FileMutex()); - fileInfo = aOther->GetFileInfo(); - } - - mFileInfos.AppendElement(fileInfo); - } } virtual already_AddRefed<BlobImpl> CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType, ErrorResult& aRv) override; - virtual bool IsStoredFile() const override - { - return mStoredFile; - } - - virtual bool IsWholeFile() const override - { - return mWholeFile; - } - nsCOMPtr<nsIFile> mFile; bool mWholeFile; - bool mStoredFile; bool mIsTemporary; }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_File_h
--- a/dom/base/StructuredCloneHelper.h +++ b/dom/base/StructuredCloneHelper.h @@ -6,16 +6,20 @@ #ifndef mozilla_dom_StructuredCloneHelper_h #define mozilla_dom_StructuredCloneHelper_h #include "js/StructuredClone.h" #include "nsAutoPtr.h" #include "nsISupports.h" #include "nsTArray.h" +#ifdef DEBUG +#include "nsIThread.h" +#endif + namespace mozilla { class ErrorResult; namespace layers { class Image; } namespace dom { @@ -162,38 +166,16 @@ public: ErrorResult &aRv); // Sometimes, when IPC is involved, you must send a buffer after a Write(). // This method 'steals' the internal data from this helper class. // You should free this buffer with FreeBuffer(). void MoveBufferDataToArray(FallibleTArray<uint8_t>& aArray, ErrorResult& aRv); - // If you receive a buffer from IPC, you can use this method to retrieve a - // JS::Value. It can happen that you want to pre-populate the array of Blobs - // and/or the PortIdentifiers. - void ReadFromBuffer(nsISupports* aParent, - JSContext* aCx, - uint64_t* aBuffer, - size_t aBufferLength, - JS::MutableHandle<JS::Value> aValue, - ErrorResult &aRv); - - void ReadFromBuffer(nsISupports* aParent, - JSContext* aCx, - uint64_t* aBuffer, - size_t aBufferLength, - uint32_t aAlgorithmVersion, - JS::MutableHandle<JS::Value> aValue, - ErrorResult &aRv); - - // Use this method to free a buffer generated by MoveToBuffer(). - void FreeBuffer(uint64_t* aBuffer, - size_t aBufferLength); - bool HasClonedDOMObjects() const { return !mBlobImplArray.IsEmpty() || !mClonedImages.IsEmpty(); } nsTArray<nsRefPtr<BlobImpl>>& BlobImpls() { @@ -252,16 +234,38 @@ public: void** aContent, uint64_t* aExtraData) override; virtual void FreeTransferCallback(uint32_t aTag, JS::TransferableOwnership aOwnership, void* aContent, uint64_t aExtraData) override; protected: + // If you receive a buffer from IPC, you can use this method to retrieve a + // JS::Value. It can happen that you want to pre-populate the array of Blobs + // and/or the PortIdentifiers. + void ReadFromBuffer(nsISupports* aParent, + JSContext* aCx, + uint64_t* aBuffer, + size_t aBufferLength, + JS::MutableHandle<JS::Value> aValue, + ErrorResult &aRv); + + void ReadFromBuffer(nsISupports* aParent, + JSContext* aCx, + uint64_t* aBuffer, + size_t aBufferLength, + uint32_t aAlgorithmVersion, + JS::MutableHandle<JS::Value> aValue, + ErrorResult &aRv); + + // Use this method to free a buffer generated by MoveToBuffer(). + void FreeBuffer(uint64_t* aBuffer, + size_t aBufferLength); + bool mSupportsCloning; bool mSupportsTransferring; ContextSupport mContext; // Useful for the structured clone algorithm: nsTArray<nsRefPtr<BlobImpl>> mBlobImplArray;
--- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -758,17 +758,17 @@ nsFrameMessageManager::SendMessage(const return NS_ERROR_DOM_DATA_CLONE_ERR; } JS::Rooted<JSObject*> objects(aCx); if (aArgc >= 3 && aObjects.isObject()) { objects = &aObjects.toObject(); } - nsTArray<OwningSerializedStructuredCloneBuffer> retval; + nsTArray<StructuredCloneIPCHelper> retval; sSendingSyncMessage |= aIsSync; bool ok = mCallback->DoSendBlockingMessage(aCx, aMessageName, helper, objects, aPrincipal, &retval, aIsSync); if (aIsSync) { sSendingSyncMessage = false; } @@ -777,18 +777,19 @@ nsFrameMessageManager::SendMessage(const } uint32_t len = retval.Length(); JS::Rooted<JSObject*> dataArray(aCx, JS_NewArrayObject(aCx, len)); NS_ENSURE_TRUE(dataArray, NS_ERROR_OUT_OF_MEMORY); for (uint32_t i = 0; i < len; ++i) { JS::Rooted<JS::Value> ret(aCx); - if (!JS_ReadStructuredClone(aCx, retval[i].data, retval[i].dataLength, - JS_STRUCTURED_CLONE_VERSION, &ret, nullptr, nullptr)) { + ErrorResult rv; + retval[i].Read(aCx, &ret, rv); + if (rv.Failed()) { MOZ_ASSERT(false, "Unable to read structured clone in SendMessage"); return NS_ERROR_UNEXPECTED; } NS_ENSURE_TRUE(JS_DefineElement(aCx, dataArray, i, ret, JSPROP_ENUMERATE), NS_ERROR_OUT_OF_MEMORY); } @@ -1067,32 +1068,32 @@ public: nsresult nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader, const nsAString& aMessage, bool aIsSync, StructuredCloneIPCHelper* aCloneHelper, mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal, - nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal) + nsTArray<StructuredCloneIPCHelper>* aRetVal) { return ReceiveMessage(aTarget, aTargetFrameLoader, mClosed, aMessage, aIsSync, aCloneHelper, aCpows, aPrincipal, aRetVal); } nsresult nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader, bool aTargetClosed, const nsAString& aMessage, bool aIsSync, StructuredCloneIPCHelper* aCloneHelper, mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal, - nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal) + nsTArray<StructuredCloneIPCHelper>* aRetVal) { nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = mListeners.Get(aMessage); if (listeners) { MMListenerRemover lr(this); nsAutoTObserverArray<nsMessageListenerInfo, 1>::EndLimitedIterator @@ -1261,34 +1262,34 @@ nsFrameMessageManager::ReceiveMessage(ns return NS_ERROR_UNEXPECTED; } if (!JS_CallFunctionValue(cx, thisObject, funval, JS::HandleValueArray(argv), &rval)) { continue; } if (aRetVal) { - JSAutoStructuredCloneBuffer buffer; - if (!buffer.write(cx, rval)) { + ErrorResult rv; + StructuredCloneIPCHelper* helper = aRetVal->AppendElement(); + helper->Write(cx, rval, rv); + if (NS_WARN_IF(rv.Failed())) { + aRetVal->RemoveElementAt(aRetVal->Length() - 1); nsString msg = aMessage + NS_LITERAL_STRING(": message reply cannot be cloned. Are you trying to send an XPCOM object?"); nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID)); if (console) { nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID)); error->Init(msg, EmptyString(), EmptyString(), 0, 0, nsIScriptError::warningFlag, "chrome javascript"); console->LogMessage(error); } JS_ClearPendingException(cx); continue; } - - OwningSerializedStructuredCloneBuffer* data = aRetVal->AppendElement(); - buffer.steal(&data->data, &data->dataLength); } } } } nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = mParentManager; return mParentManager ? mParentManager->ReceiveMessage(aTarget, aTargetFrameLoader, aTargetClosed, aMessage, aIsSync, aCloneHelper, @@ -1989,17 +1990,17 @@ public: MOZ_COUNT_DTOR(ChildProcessMessageManagerCallback); } virtual bool DoSendBlockingMessage(JSContext* aCx, const nsAString& aMessage, StructuredCloneIPCHelper& aHelper, JS::Handle<JSObject *> aCpows, nsIPrincipal* aPrincipal, - nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal, + nsTArray<StructuredCloneIPCHelper>* aRetVal, bool aIsSync) override { mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); if (!cc) { return true; } ClonedMessageData data; @@ -2080,17 +2081,17 @@ public: MOZ_COUNT_DTOR(SameChildProcessMessageManagerCallback); } virtual bool DoSendBlockingMessage(JSContext* aCx, const nsAString& aMessage, StructuredCloneIPCHelper& aHelper, JS::Handle<JSObject *> aCpows, nsIPrincipal* aPrincipal, - nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal, + nsTArray<StructuredCloneIPCHelper>* aRetVal, bool aIsSync) override { SameProcessMessageQueue* queue = SameProcessMessageQueue::Get(); queue->Flush(); if (nsFrameMessageManager::sSameProcessParentManager) { SameProcessCpowHolder cpows(js::GetRuntime(aCx), aCpows); nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sSameProcessParentManager;
--- a/dom/base/nsFrameMessageManager.h +++ b/dom/base/nsFrameMessageManager.h @@ -29,19 +29,16 @@ #include "nsTObserverArray.h" #include "mozilla/dom/SameProcessMessageQueue.h" #include "mozilla/dom/StructuredCloneIPCHelper.h" #include "mozilla/jsipc/CpowHolder.h" class nsIFrameLoader; namespace mozilla { - -struct OwningSerializedStructuredCloneBuffer; - namespace dom { class nsIContentParent; class nsIContentChild; class ClonedMessageData; class MessageManagerReporter; namespace ipc { @@ -52,33 +49,30 @@ enum MessageManagerFlags { MM_GLOBAL = 2, MM_PROCESSMANAGER = 4, MM_BROADCASTER = 8, MM_OWNSCALLBACK = 16 }; class MessageManagerCallback { -protected: - typedef mozilla::OwningSerializedStructuredCloneBuffer OwningSerializedStructuredCloneBuffer; - public: virtual ~MessageManagerCallback() {} virtual bool DoLoadMessageManagerScript(const nsAString& aURL, bool aRunInGlobalScope) { return true; } virtual bool DoSendBlockingMessage(JSContext* aCx, const nsAString& aMessage, StructuredCloneIPCHelper& aHelper, JS::Handle<JSObject*> aCpows, nsIPrincipal* aPrincipal, - nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal, + nsTArray<StructuredCloneIPCHelper>* aRetVal, bool aIsSync) { return true; } virtual bool DoSendAsyncMessage(JSContext* aCx, const nsAString& aMessage, StructuredCloneIPCHelper& aHelper, @@ -165,17 +159,16 @@ private: class nsFrameMessageManager final : public nsIContentFrameMessageManager, public nsIMessageBroadcaster, public nsIFrameScriptLoader, public nsIGlobalProcessScriptLoader, public nsIProcessChecker { friend class mozilla::dom::MessageManagerReporter; typedef mozilla::dom::StructuredCloneIPCHelper StructuredCloneIPCHelper; - typedef mozilla::OwningSerializedStructuredCloneBuffer OwningSerializedStructuredCloneBuffer; public: nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback, nsFrameMessageManager* aParentManager, /* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags); private: ~nsFrameMessageManager(); @@ -196,17 +189,17 @@ public: static nsFrameMessageManager* NewProcessMessageManager(bool aIsRemote); nsresult ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader, const nsAString& aMessage, bool aIsSync, StructuredCloneIPCHelper* aCloneHelper, mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal, - nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal); + nsTArray<StructuredCloneIPCHelper>* aRetVal); void AddChildManager(nsFrameMessageManager* aManager); void RemoveChildManager(nsFrameMessageManager* aManager) { mChildManagers.RemoveObject(aManager); } void Disconnect(bool aRemoveFromParent = true); void Close(); @@ -264,17 +257,17 @@ private: uint8_t aArgc, JS::MutableHandle<JS::Value> aRetval, bool aIsSync); nsresult ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader, bool aTargetClosed, const nsAString& aMessage, bool aIsSync, StructuredCloneIPCHelper* aCloneHelper, mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal, - nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal); + nsTArray<StructuredCloneIPCHelper>* aRetVal); NS_IMETHOD LoadScript(const nsAString& aURL, bool aAllowDelayedLoad, bool aRunInGlobalScope); NS_IMETHOD RemoveDelayedScript(const nsAString& aURL); NS_IMETHOD GetDelayedScripts(JSContext* aCx, JS::MutableHandle<JS::Value> aList); protected:
--- a/dom/base/nsInProcessTabChildGlobal.cpp +++ b/dom/base/nsInProcessTabChildGlobal.cpp @@ -23,17 +23,17 @@ using namespace mozilla; using namespace mozilla::dom; bool nsInProcessTabChildGlobal::DoSendBlockingMessage(JSContext* aCx, const nsAString& aMessage, StructuredCloneIPCHelper& aHelper, JS::Handle<JSObject *> aCpows, nsIPrincipal* aPrincipal, - nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal, + nsTArray<StructuredCloneIPCHelper>* aRetVal, bool aIsSync) { SameProcessMessageQueue* queue = SameProcessMessageQueue::Get(); queue->Flush(); if (mChromeMessageManager) { SameProcessCpowHolder cpows(js::GetRuntime(aCx), aCpows); nsRefPtr<nsFrameMessageManager> mm = mChromeMessageManager;
--- a/dom/base/nsInProcessTabChildGlobal.h +++ b/dom/base/nsInProcessTabChildGlobal.h @@ -30,17 +30,17 @@ class EventChainPreVisitor; class nsInProcessTabChildGlobal : public mozilla::DOMEventTargetHelper, public nsMessageManagerScriptExecutor, public nsIInProcessContentFrameMessageManager, public nsIGlobalObject, public nsIScriptObjectPrincipal, public nsSupportsWeakReference, public mozilla::dom::ipc::MessageManagerCallback { - typedef mozilla::OwningSerializedStructuredCloneBuffer OwningSerializedStructuredCloneBuffer; + typedef mozilla::dom::StructuredCloneIPCHelper StructuredCloneIPCHelper; public: nsInProcessTabChildGlobal(nsIDocShell* aShell, nsIContent* aOwner, nsFrameMessageManager* aChrome); NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsInProcessTabChildGlobal, mozilla::DOMEventTargetHelper) @@ -78,24 +78,24 @@ public: NS_DECL_NSIINPROCESSCONTENTFRAMEMESSAGEMANAGER /** * MessageManagerCallback methods that we override. */ virtual bool DoSendBlockingMessage(JSContext* aCx, const nsAString& aMessage, - mozilla::dom::StructuredCloneIPCHelper& aHelper, + StructuredCloneIPCHelper& aHelper, JS::Handle<JSObject *> aCpows, nsIPrincipal* aPrincipal, - nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal, + nsTArray<StructuredCloneIPCHelper>* aRetVal, bool aIsSync) override; virtual bool DoSendAsyncMessage(JSContext* aCx, const nsAString& aMessage, - mozilla::dom::StructuredCloneIPCHelper& aHelper, + StructuredCloneIPCHelper& aHelper, JS::Handle<JSObject *> aCpows, nsIPrincipal* aPrincipal) override; virtual nsresult PreHandleEvent( mozilla::EventChainPreVisitor& aVisitor) override; NS_IMETHOD AddEventListener(const nsAString& aType, nsIDOMEventListener* aListener, bool aUseCapture)
--- a/dom/base/nsStructuredCloneContainer.cpp +++ b/dom/base/nsStructuredCloneContainer.cpp @@ -11,125 +11,104 @@ #include "nsIVariant.h" #include "nsIXPConnect.h" #include "nsServiceManagerUtils.h" #include "nsContentUtils.h" #include "jsapi.h" #include "xpcpublic.h" #include "mozilla/Base64.h" -#include "mozilla/dom/StructuredCloneHelper.h" #include "mozilla/dom/ScriptSettings.h" using namespace mozilla; using namespace mozilla::dom; NS_IMPL_ADDREF(nsStructuredCloneContainer) NS_IMPL_RELEASE(nsStructuredCloneContainer) NS_INTERFACE_MAP_BEGIN(nsStructuredCloneContainer) NS_INTERFACE_MAP_ENTRY(nsIStructuredCloneContainer) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END nsStructuredCloneContainer::nsStructuredCloneContainer() - : StructuredCloneHelper(CloningSupported, TransferringNotSupported, - DifferentProcess) - , mState(eNotInitialized) , mData(nullptr), mSize(0), mVersion(0) + : mVersion(0) { } nsStructuredCloneContainer::~nsStructuredCloneContainer() { - if (mData) { - free(mData); - } } NS_IMETHODIMP nsStructuredCloneContainer::InitFromJSVal(JS::Handle<JS::Value> aData, JSContext* aCx) { - if (mState != eNotInitialized) { + if (DataLength()) { return NS_ERROR_FAILURE; } ErrorResult rv; Write(aCx, aData, rv); if (NS_WARN_IF(rv.Failed())) { return rv.StealNSResult(); } - mState = eInitializedFromJSVal; + mVersion = JS_STRUCTURED_CLONE_VERSION; return NS_OK; } NS_IMETHODIMP nsStructuredCloneContainer::InitFromBase64(const nsAString &aData, uint32_t aFormatVersion, JSContext* aCx) { - if (mState != eNotInitialized) { + if (DataLength()) { return NS_ERROR_FAILURE; } NS_ConvertUTF16toUTF8 data(aData); nsAutoCString binaryData; nsresult rv = Base64Decode(data, binaryData); NS_ENSURE_SUCCESS(rv, rv); - // Copy the string's data into our own buffer. - mData = (uint64_t*) malloc(binaryData.Length()); - NS_ENSURE_STATE(mData); - memcpy(mData, binaryData.get(), binaryData.Length()); + if (!CopyExternalData(binaryData.get(), binaryData.Length())) { + return NS_ERROR_OUT_OF_MEMORY; + } - mSize = binaryData.Length(); mVersion = aFormatVersion; - - mState = eInitializedFromBase64; return NS_OK; } nsresult nsStructuredCloneContainer::DeserializeToJsval(JSContext* aCx, JS::MutableHandle<JS::Value> aValue) { aValue.setNull(); JS::Rooted<JS::Value> jsStateObj(aCx); - if (mState == eInitializedFromJSVal) { - ErrorResult rv; - Read(nullptr, aCx, &jsStateObj, rv); - if (NS_WARN_IF(rv.Failed())) { - return rv.StealNSResult(); - } - } else { - MOZ_ASSERT(mState == eInitializedFromBase64); - MOZ_ASSERT(mData); - - ErrorResult rv; - ReadFromBuffer(nullptr, aCx, mData, mSize, mVersion, &jsStateObj, rv); - if (NS_WARN_IF(rv.Failed())) { - return rv.StealNSResult(); - } + ErrorResult rv; + Read(aCx, &jsStateObj, rv); + if (NS_WARN_IF(rv.Failed())) { + return rv.StealNSResult(); } aValue.set(jsStateObj); return NS_OK; } NS_IMETHODIMP nsStructuredCloneContainer::DeserializeToVariant(JSContext* aCx, nsIVariant** aData) { NS_ENSURE_ARG_POINTER(aData); *aData = nullptr; - if (mState == eNotInitialized) { + if (!DataLength()) { return NS_ERROR_FAILURE; } // Deserialize to a JS::Value. JS::Rooted<JS::Value> jsStateObj(aCx); nsresult rv = DeserializeToJsval(aCx, &jsStateObj); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -146,83 +125,52 @@ nsStructuredCloneContainer::DeserializeT return NS_OK; } NS_IMETHODIMP nsStructuredCloneContainer::GetDataAsBase64(nsAString &aOut) { aOut.Truncate(); - if (mState == eNotInitialized) { + if (!DataLength()) { return NS_ERROR_FAILURE; } - uint64_t* data; - size_t size; - - if (mState == eInitializedFromJSVal) { - if (HasClonedDOMObjects()) { - return NS_ERROR_FAILURE; - } - - data = BufferData(); - size = BufferSize(); - } else { - MOZ_ASSERT(mState == eInitializedFromBase64); - MOZ_ASSERT(mData); - - data = mData; - size = mSize; + if (HasClonedDOMObjects()) { + return NS_ERROR_FAILURE; } - nsAutoCString binaryData(reinterpret_cast<char*>(data), size); + nsAutoCString binaryData(reinterpret_cast<char*>(Data()), DataLength()); nsAutoCString base64Data; nsresult rv = Base64Encode(binaryData, base64Data); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } CopyASCIItoUTF16(base64Data, aOut); return NS_OK; } NS_IMETHODIMP nsStructuredCloneContainer::GetSerializedNBytes(uint64_t* aSize) { NS_ENSURE_ARG_POINTER(aSize); - if (mState == eNotInitialized) { + if (!DataLength()) { return NS_ERROR_FAILURE; } - if (mState == eInitializedFromJSVal) { - *aSize = BufferSize(); - return NS_OK; - } - - MOZ_ASSERT(mState == eInitializedFromBase64); - - // mSize is a size_t, while aSize is a uint64_t. We rely on an implicit cast - // here so that we'll get a compile error if a size_t-to-uint64_t cast is - // narrowing. - *aSize = mSize; - + *aSize = DataLength(); return NS_OK; } NS_IMETHODIMP nsStructuredCloneContainer::GetFormatVersion(uint32_t* aFormatVersion) { NS_ENSURE_ARG_POINTER(aFormatVersion); - if (mState == eNotInitialized) { + if (!DataLength()) { return NS_ERROR_FAILURE; } - if (mState == eInitializedFromJSVal) { - *aFormatVersion = JS_STRUCTURED_CLONE_VERSION; - return NS_OK; - } - - MOZ_ASSERT(mState == eInitializedFromBase64); *aFormatVersion = mVersion; return NS_OK; }
--- a/dom/base/nsStructuredCloneContainer.h +++ b/dom/base/nsStructuredCloneContainer.h @@ -4,47 +4,37 @@ * 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 nsStructuredCloneContainer_h__ #define nsStructuredCloneContainer_h__ #include "nsIStructuredCloneContainer.h" #include "mozilla/Attributes.h" -#include "mozilla/dom/StructuredCloneHelper.h" +#include "mozilla/dom/StructuredCloneIPCHelper.h" #define NS_STRUCTUREDCLONECONTAINER_CONTRACTID \ "@mozilla.org/docshell/structured-clone-container;1" #define NS_STRUCTUREDCLONECONTAINER_CID \ { /* 38bd0634-0fd4-46f0-b85f-13ced889eeec */ \ 0x38bd0634, \ 0x0fd4, \ 0x46f0, \ {0xb8, 0x5f, 0x13, 0xce, 0xd8, 0x89, 0xee, 0xec} \ } class nsStructuredCloneContainer final : public nsIStructuredCloneContainer - , public mozilla::dom::StructuredCloneHelper + , public mozilla::dom::StructuredCloneIPCHelper { public: nsStructuredCloneContainer(); NS_DECL_ISUPPORTS NS_DECL_NSISTRUCTUREDCLONECONTAINER private: ~nsStructuredCloneContainer(); - enum { - eNotInitialized = 0, - eInitializedFromJSVal, - eInitializedFromBase64, - } mState; - - uint64_t* mData; - - // This needs to be size_t rather than a PR-type so it matches the JS API. - size_t mSize; uint32_t mVersion; }; #endif
--- a/dom/base/nsXMLHttpRequest.cpp +++ b/dom/base/nsXMLHttpRequest.cpp @@ -3099,18 +3099,23 @@ nsXMLHttpRequest::SetRequestHeader(const } } if (!mAlreadySetHeaders.Contains(header)) { // Case 2 above mergeHeaders = false; } - // Merge headers depending on what we decided above. - nsresult rv = httpChannel->SetRequestHeader(header, value, mergeHeaders); + nsresult rv; + if (value.IsEmpty()) { + rv = httpChannel->SetEmptyRequestHeader(header); + } else { + // Merge headers depending on what we decided above. + rv = httpChannel->SetRequestHeader(header, value, mergeHeaders); + } if (rv == NS_ERROR_INVALID_ARG) { return NS_ERROR_DOM_SYNTAX_ERR; } if (NS_SUCCEEDED(rv)) { // Remember that we've set this header, so subsequent set operations will merge values. mAlreadySetHeaders.PutEntry(nsCString(header)); // We'll want to duplicate this header for any replacement channels (eg. on redirect) @@ -3441,19 +3446,23 @@ nsXMLHttpRequest::OnRedirectVerifyCallba if (NS_SUCCEEDED(result)) { mChannel = mNewRedirectChannel; nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel)); if (httpChannel) { // Ensure all original headers are duplicated for the new channel (bug #553888) for (uint32_t i = mModifiedRequestHeaders.Length(); i > 0; ) { --i; - httpChannel->SetRequestHeader(mModifiedRequestHeaders[i].header, - mModifiedRequestHeaders[i].value, - false); + if (mModifiedRequestHeaders[i].value.IsEmpty()) { + httpChannel->SetEmptyRequestHeader(mModifiedRequestHeaders[i].header); + } else { + httpChannel->SetRequestHeader(mModifiedRequestHeaders[i].header, + mModifiedRequestHeaders[i].value, + false); + } } } } else { mErrorLoad = true; } mNewRedirectChannel = nullptr;
--- a/dom/broadcastchannel/BroadcastChannelChild.cpp +++ b/dom/broadcastchannel/BroadcastChannelChild.cpp @@ -6,17 +6,17 @@ #include "BroadcastChannelChild.h" #include "BroadcastChannel.h" #include "jsapi.h" #include "mozilla/dom/ipc/BlobChild.h" #include "mozilla/dom/File.h" #include "mozilla/dom/MessageEvent.h" #include "mozilla/dom/MessageEventBinding.h" -#include "mozilla/dom/StructuredCloneHelper.h" +#include "mozilla/dom/StructuredCloneIPCHelper.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/dom/WorkerScope.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/ipc/PBackgroundChild.h" #include "WorkerPrivate.h" namespace mozilla { @@ -80,29 +80,27 @@ BroadcastChannelChild::RecvNotify(const globalObject = workerPrivate->GlobalScope(); } if (!globalObject || !jsapi.Init(globalObject)) { NS_WARNING("Failed to initialize AutoJSAPI object."); return true; } - JSContext* cx = jsapi.cx(); - const SerializedStructuredCloneBuffer& buffer = aData.data(); - StructuredCloneHelper cloneHelper(StructuredCloneHelper::CloningSupported, - StructuredCloneHelper::TransferringNotSupported, - StructuredCloneHelper::DifferentProcess); - + StructuredCloneIPCHelper cloneHelper; cloneHelper.BlobImpls().AppendElements(blobs); + const SerializedStructuredCloneBuffer& buffer = aData.data(); + cloneHelper.UseExternalData(buffer.data, buffer.dataLength); + + JSContext* cx = jsapi.cx(); JS::Rooted<JS::Value> value(cx, JS::NullValue()); if (buffer.dataLength) { ErrorResult rv; - cloneHelper.ReadFromBuffer(mBC->GetParentObject(), cx, - buffer.data, buffer.dataLength, &value, rv); + cloneHelper.Read(cx, &value, rv); if (NS_WARN_IF(rv.Failed())) { return true; } } RootedDictionary<MessageEventInit> init(cx); init.mBubbles = false; init.mCancelable = false;
--- a/dom/fetch/FetchDriver.cpp +++ b/dom/fetch/FetchDriver.cpp @@ -472,17 +472,21 @@ FetchDriver::HttpFetch(bool aCORSFlag, b FailWithNetworkError(); return rv; } // Set the same headers. nsAutoTArray<InternalHeaders::Entry, 5> headers; mRequest->Headers()->GetEntries(headers); for (uint32_t i = 0; i < headers.Length(); ++i) { - httpChan->SetRequestHeader(headers[i].mName, headers[i].mValue, false /* merge */); + if (headers[i].mValue.IsEmpty()) { + httpChan->SetEmptyRequestHeader(headers[i].mName); + } else { + httpChan->SetRequestHeader(headers[i].mName, headers[i].mValue, false /* merge */); + } } // Step 2. Set the referrer. nsAutoString referrer; mRequest->GetReferrer(referrer); if (referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) { rv = nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal, mDocument,
new file mode 100644 --- /dev/null +++ b/dom/filehandle/ActorsChild.cpp @@ -0,0 +1,743 @@ +/* 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 "ActorsChild.h" + +#include "BackgroundChildImpl.h" +#include "FileHandleBase.h" +#include "FileRequestBase.h" +#include "js/Date.h" +#include "mozilla/dom/EncodingUtils.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/ipc/BlobChild.h" +#include "MutableFileBase.h" +#include "nsCOMPtr.h" +#include "nsContentUtils.h" +#include "nsString.h" +#include "xpcpublic.h" + +namespace mozilla { +namespace dom { + +/******************************************************************************* + * Helpers + ******************************************************************************/ + +namespace { + +class MOZ_STACK_CLASS AutoSetCurrentFileHandle final +{ + typedef mozilla::ipc::BackgroundChildImpl BackgroundChildImpl; + + FileHandleBase* const mFileHandle; + FileHandleBase* mPreviousFileHandle; + FileHandleBase** mThreadLocalSlot; + +public: + explicit AutoSetCurrentFileHandle(FileHandleBase* aFileHandle) + : mFileHandle(aFileHandle) + , mPreviousFileHandle(nullptr) + , mThreadLocalSlot(nullptr) + { + if (aFileHandle) { + BackgroundChildImpl::ThreadLocal* threadLocal = + BackgroundChildImpl::GetThreadLocalForCurrentThread(); + MOZ_ASSERT(threadLocal); + + // Hang onto this location for resetting later. + mThreadLocalSlot = &threadLocal->mCurrentFileHandle; + + // Save the current value. + mPreviousFileHandle = *mThreadLocalSlot; + + // Set the new value. + *mThreadLocalSlot = aFileHandle; + } + } + + ~AutoSetCurrentFileHandle() + { + MOZ_ASSERT_IF(mThreadLocalSlot, mFileHandle); + MOZ_ASSERT_IF(mThreadLocalSlot, *mThreadLocalSlot == mFileHandle); + + if (mThreadLocalSlot) { + // Reset old value. + *mThreadLocalSlot = mPreviousFileHandle; + } + } + + FileHandleBase* + FileHandle() const + { + return mFileHandle; + } +}; + +class MOZ_STACK_CLASS ResultHelper final + : public FileRequestBase::ResultCallback +{ + FileRequestBase* mFileRequest; + AutoSetCurrentFileHandle mAutoFileHandle; + + union + { + File* mFile; + const nsCString* mString; + const FileRequestMetadata* mMetadata; + const JS::Handle<JS::Value>* mJSValHandle; + } mResult; + + enum + { + ResultTypeFile, + ResultTypeString, + ResultTypeMetadata, + ResultTypeJSValHandle, + } mResultType; + +public: + ResultHelper(FileRequestBase* aFileRequest, + FileHandleBase* aFileHandle, + File* aResult) + : mFileRequest(aFileRequest) + , mAutoFileHandle(aFileHandle) + , mResultType(ResultTypeFile) + { + MOZ_ASSERT(aFileRequest); + MOZ_ASSERT(aFileHandle); + MOZ_ASSERT(aResult); + + mResult.mFile = aResult; + } + + ResultHelper(FileRequestBase* aFileRequest, + FileHandleBase* aFileHandle, + const nsCString* aResult) + : mFileRequest(aFileRequest) + , mAutoFileHandle(aFileHandle) + , mResultType(ResultTypeString) + { + MOZ_ASSERT(aFileRequest); + MOZ_ASSERT(aFileHandle); + MOZ_ASSERT(aResult); + + mResult.mString = aResult; + } + + ResultHelper(FileRequestBase* aFileRequest, + FileHandleBase* aFileHandle, + const FileRequestMetadata* aResult) + : mFileRequest(aFileRequest) + , mAutoFileHandle(aFileHandle) + , mResultType(ResultTypeMetadata) + { + MOZ_ASSERT(aFileRequest); + MOZ_ASSERT(aFileHandle); + MOZ_ASSERT(aResult); + + mResult.mMetadata = aResult; + } + + + ResultHelper(FileRequestBase* aFileRequest, + FileHandleBase* aFileHandle, + const JS::Handle<JS::Value>* aResult) + : mFileRequest(aFileRequest) + , mAutoFileHandle(aFileHandle) + , mResultType(ResultTypeJSValHandle) + { + MOZ_ASSERT(aFileRequest); + MOZ_ASSERT(aFileHandle); + MOZ_ASSERT(aResult); + + mResult.mJSValHandle = aResult; + } + + FileRequestBase* + FileRequest() const + { + return mFileRequest; + } + + FileHandleBase* + FileHandle() const + { + return mAutoFileHandle.FileHandle(); + } + + virtual nsresult + GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) override + { + MOZ_ASSERT(aCx); + MOZ_ASSERT(mFileRequest); + + switch (mResultType) { + case ResultTypeFile: + return GetResult(aCx, mResult.mFile, aResult); + + case ResultTypeString: + return GetResult(aCx, mResult.mString, aResult); + + case ResultTypeMetadata: + return GetResult(aCx, mResult.mMetadata, aResult); + + case ResultTypeJSValHandle: + aResult.set(*mResult.mJSValHandle); + return NS_OK; + + default: + MOZ_CRASH("Unknown result type!"); + } + + MOZ_CRASH("Should never get here!"); + } + +private: + nsresult + GetResult(JSContext* aCx, + File* aFile, + JS::MutableHandle<JS::Value> aResult) + { + bool ok = GetOrCreateDOMReflector(aCx, aFile, aResult); + if (NS_WARN_IF(!ok)) { + return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } + + return NS_OK; + } + + nsresult + GetResult(JSContext* aCx, + const nsCString* aString, + JS::MutableHandle<JS::Value> aResult) + { + const nsCString& data = *aString; + + nsresult rv; + + if (!mFileRequest->HasEncoding()) { + JS::Rooted<JSObject*> arrayBuffer(aCx); + rv = nsContentUtils::CreateArrayBuffer(aCx, data, arrayBuffer.address()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } + + aResult.setObject(*arrayBuffer); + return NS_OK; + } + + nsAutoCString encoding; + // The BOM sniffing is baked into the "decode" part of the Encoding + // Standard, which the File API references. + if (!nsContentUtils::CheckForBOM( + reinterpret_cast<const unsigned char *>(data.get()), + data.Length(), + encoding)) { + // BOM sniffing failed. Try the API argument. + if (!EncodingUtils::FindEncodingForLabel(mFileRequest->GetEncoding(), + encoding)) { + // API argument failed. Since we are dealing with a file system file, + // we don't have a meaningful type attribute for the blob available, + // so proceeding to the next step, which is defaulting to UTF-8. + encoding.AssignLiteral("UTF-8"); + } + } + + nsString tmpString; + rv = nsContentUtils::ConvertStringFromEncoding(encoding, data, tmpString); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } + + if (NS_WARN_IF(!xpc::StringToJsval(aCx, tmpString, aResult))) { + return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } + + return NS_OK; + } + + nsresult + GetResult(JSContext* aCx, + const FileRequestMetadata* aMetadata, + JS::MutableHandle<JS::Value> aResult) + { + JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx)); + if (NS_WARN_IF(!obj)) { + return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } + + const FileRequestSize& size = aMetadata->size(); + if (size.type() != FileRequestSize::Tvoid_t) { + MOZ_ASSERT(size.type() == FileRequestSize::Tuint64_t); + + JS::Rooted<JS::Value> number(aCx, JS_NumberValue(size.get_uint64_t())); + + if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "size", number, 0))) { + return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } + } + + const FileRequestLastModified& lastModified = aMetadata->lastModified(); + if (lastModified.type() != FileRequestLastModified::Tvoid_t) { + MOZ_ASSERT(lastModified.type() == FileRequestLastModified::Tint64_t); + + JS::Rooted<JSObject*> date(aCx, + JS::NewDateObject(aCx, JS::TimeClip(lastModified.get_int64_t()))); + if (NS_WARN_IF(!date)) { + return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } + + if (NS_WARN_IF(!JS_DefineProperty(aCx, obj, "lastModified", date, 0))) { + return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } + } + + aResult.setObject(*obj); + return NS_OK; + } +}; + +already_AddRefed<File> +ConvertActorToFile(FileHandleBase* aFileHandle, + const FileRequestGetFileResponse& aResponse) +{ + auto* actor = static_cast<BlobChild*>(aResponse.fileChild()); + + MutableFileBase* mutableFile = aFileHandle->MutableFile(); + MOZ_ASSERT(mutableFile); + + const FileRequestMetadata& metadata = aResponse.metadata(); + + const FileRequestSize& size = metadata.size(); + MOZ_ASSERT(size.type() == FileRequestSize::Tuint64_t); + + const FileRequestLastModified& lastModified = metadata.lastModified(); + MOZ_ASSERT(lastModified.type() == FileRequestLastModified::Tint64_t); + + actor->SetMysteryBlobInfo(mutableFile->Name(), + mutableFile->Type(), + size.get_uint64_t(), + lastModified.get_int64_t(), + BlobDirState::eUnknownIfDir); + + nsRefPtr<BlobImpl> blobImpl = actor->GetBlobImpl(); + MOZ_ASSERT(blobImpl); + + nsRefPtr<File> file = mutableFile->CreateFileFor(blobImpl, aFileHandle); + return file.forget(); +} + +void +HandleSuccess(ResultHelper* aResultHelper) +{ + MOZ_ASSERT(aResultHelper); + + nsRefPtr<FileRequestBase> fileRequest = aResultHelper->FileRequest(); + MOZ_ASSERT(fileRequest); + fileRequest->AssertIsOnOwningThread(); + + nsRefPtr<FileHandleBase> fileHandle = aResultHelper->FileHandle(); + MOZ_ASSERT(fileHandle); + + if (fileHandle->IsAborted()) { + fileRequest->SetError(NS_ERROR_DOM_FILEHANDLE_ABORT_ERR); + return; + } + + MOZ_ASSERT(fileHandle->IsOpen()); + + fileRequest->SetResultCallback(aResultHelper); + + MOZ_ASSERT(fileHandle->IsOpen() || fileHandle->IsAborted()); +} + +void +HandleError(FileRequestBase* aFileRequest, + nsresult aErrorCode, + FileHandleBase* aFileHandle) +{ + MOZ_ASSERT(aFileRequest); + aFileRequest->AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aErrorCode)); + MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_FILEHANDLE); + MOZ_ASSERT(aFileHandle); + + nsRefPtr<FileRequestBase> fileRequest = aFileRequest; + nsRefPtr<FileHandleBase> fileHandle = aFileHandle; + + AutoSetCurrentFileHandle ascfh(aFileHandle); + + fileRequest->SetError(aErrorCode); + + MOZ_ASSERT(fileHandle->IsOpen() || fileHandle->IsAborted()); +} + +} // anonymous namespace + +/******************************************************************************* + * BackgroundMutableFileChildBase + ******************************************************************************/ + +BackgroundMutableFileChildBase::BackgroundMutableFileChildBase( + DEBUGONLY(PRThread* aOwningThread)) + : ThreadObject(DEBUGONLY(aOwningThread)) + , mMutableFile(nullptr) +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(BackgroundMutableFileChildBase); +} + +BackgroundMutableFileChildBase::~BackgroundMutableFileChildBase() +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(BackgroundMutableFileChildBase); +} + +void +BackgroundMutableFileChildBase::EnsureDOMObject() +{ + AssertIsOnOwningThread(); + + if (mTemporaryStrongMutableFile) { + return; + } + + mTemporaryStrongMutableFile = CreateMutableFile(); + + MOZ_ASSERT(mTemporaryStrongMutableFile); + mTemporaryStrongMutableFile->AssertIsOnOwningThread(); + + mMutableFile = mTemporaryStrongMutableFile; +} + +void +BackgroundMutableFileChildBase::ReleaseDOMObject() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mTemporaryStrongMutableFile); + mTemporaryStrongMutableFile->AssertIsOnOwningThread(); + MOZ_ASSERT(mMutableFile == mTemporaryStrongMutableFile); + + mTemporaryStrongMutableFile = nullptr; +} + +void +BackgroundMutableFileChildBase::SendDeleteMeInternal() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!mTemporaryStrongMutableFile); + + if (mMutableFile) { + mMutableFile->ClearBackgroundActor(); + mMutableFile = nullptr; + + MOZ_ALWAYS_TRUE(PBackgroundMutableFileChild::SendDeleteMe()); + } +} + +void +BackgroundMutableFileChildBase::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + if (mMutableFile) { + mMutableFile->ClearBackgroundActor(); + DEBUGONLY(mMutableFile = nullptr;) + } +} + +PBackgroundFileHandleChild* +BackgroundMutableFileChildBase::AllocPBackgroundFileHandleChild( + const FileMode& aMode) +{ + MOZ_CRASH("PBackgroundFileHandleChild actors should be manually " + "constructed!"); +} + +bool +BackgroundMutableFileChildBase::DeallocPBackgroundFileHandleChild( + PBackgroundFileHandleChild* aActor) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aActor); + + delete static_cast<BackgroundFileHandleChild*>(aActor); + return true; +} + +/******************************************************************************* + * BackgroundFileHandleChild + ******************************************************************************/ + +BackgroundFileHandleChild::BackgroundFileHandleChild( + DEBUGONLY(PRThread* aOwningThread,) + FileHandleBase* aFileHandle) + : ThreadObject(DEBUGONLY(aOwningThread)) + , mTemporaryStrongFileHandle(aFileHandle) + , mFileHandle(aFileHandle) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aFileHandle); + aFileHandle->AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(BackgroundFileHandleChild); +} + +BackgroundFileHandleChild::~BackgroundFileHandleChild() +{ + AssertIsOnOwningThread(); + + MOZ_COUNT_DTOR(BackgroundFileHandleChild); +} + +void +BackgroundFileHandleChild::SendDeleteMeInternal() +{ + AssertIsOnOwningThread(); + + if (mFileHandle) { + NoteActorDestroyed(); + + MOZ_ALWAYS_TRUE(PBackgroundFileHandleChild::SendDeleteMe()); + } +} + +void +BackgroundFileHandleChild::NoteActorDestroyed() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT_IF(mTemporaryStrongFileHandle, mFileHandle); + + if (mFileHandle) { + mFileHandle->ClearBackgroundActor(); + + // Normally this would be DEBUG-only but NoteActorDestroyed is also called + // from SendDeleteMeInternal. In that case we're going to receive an actual + // ActorDestroy call later and we don't want to touch a dead object. + mTemporaryStrongFileHandle = nullptr; + mFileHandle = nullptr; + } +} + +void +BackgroundFileHandleChild::NoteComplete() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT_IF(mFileHandle, mTemporaryStrongFileHandle); + + mTemporaryStrongFileHandle = nullptr; +} + +void +BackgroundFileHandleChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + NoteActorDestroyed(); +} + +bool +BackgroundFileHandleChild::RecvComplete(const bool& aAborted) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mFileHandle); + + mFileHandle->HandleCompleteOrAbort(aAborted); + + NoteComplete(); + return true; +} + +PBackgroundFileRequestChild* +BackgroundFileHandleChild::AllocPBackgroundFileRequestChild( + const FileRequestParams& aParams) +{ + MOZ_CRASH("PBackgroundFileRequestChild actors should be manually " + "constructed!"); +} + +bool +BackgroundFileHandleChild::DeallocPBackgroundFileRequestChild( + PBackgroundFileRequestChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast<BackgroundFileRequestChild*>(aActor); + return true; +} + +/******************************************************************************* + * BackgroundFileRequestChild + ******************************************************************************/ + +BackgroundFileRequestChild::BackgroundFileRequestChild( + DEBUGONLY(PRThread* aOwningThread,) + FileRequestBase* aFileRequest) + : ThreadObject(DEBUGONLY(aOwningThread)) + , mFileRequest(aFileRequest) + , mFileHandle(aFileRequest->FileHandle()) + , mActorDestroyed(false) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aFileRequest); + aFileRequest->AssertIsOnOwningThread(); + MOZ_ASSERT(mFileHandle); + mFileHandle->AssertIsOnOwningThread(); + + MOZ_COUNT_CTOR(BackgroundFileRequestChild); +} + +BackgroundFileRequestChild::~BackgroundFileRequestChild() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!mFileHandle); + + MOZ_COUNT_DTOR(BackgroundFileRequestChild); +} + +void +BackgroundFileRequestChild::HandleResponse(nsresult aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aResponse)); + MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_FILEHANDLE); + MOZ_ASSERT(mFileHandle); + + HandleError(mFileRequest, aResponse, mFileHandle); +} + +void +BackgroundFileRequestChild::HandleResponse( + const FileRequestGetFileResponse& aResponse) +{ + AssertIsOnOwningThread(); + + nsRefPtr<File> file = ConvertActorToFile(mFileHandle, aResponse); + + ResultHelper helper(mFileRequest, mFileHandle, file); + + HandleSuccess(&helper); +} + +void +BackgroundFileRequestChild::HandleResponse(const nsCString& aResponse) +{ + AssertIsOnOwningThread(); + + ResultHelper helper(mFileRequest, mFileHandle, &aResponse); + + HandleSuccess(&helper); +} + +void +BackgroundFileRequestChild::HandleResponse(const FileRequestMetadata& aResponse) +{ + AssertIsOnOwningThread(); + + ResultHelper helper(mFileRequest, mFileHandle, &aResponse); + + HandleSuccess(&helper); +} + +void +BackgroundFileRequestChild::HandleResponse(JS::Handle<JS::Value> aResponse) +{ + AssertIsOnOwningThread(); + + ResultHelper helper(mFileRequest, mFileHandle, &aResponse); + + HandleSuccess(&helper); +} + +void +BackgroundFileRequestChild::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + MOZ_ASSERT(!mActorDestroyed); + + mActorDestroyed = true; + + if (mFileHandle) { + mFileHandle->AssertIsOnOwningThread(); + + mFileHandle->OnRequestFinished(/* aActorDestroyedNormally */ + aWhy == Deletion); + + DEBUGONLY(mFileHandle = nullptr;) + } +} + +bool +BackgroundFileRequestChild::Recv__delete__(const FileRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mFileRequest); + MOZ_ASSERT(mFileHandle); + + if (mFileHandle->IsAborted()) { + // Always handle an "error" with ABORT_ERR if the file handle was aborted, + // even if the request succeeded or failed with another error. + HandleResponse(NS_ERROR_DOM_FILEHANDLE_ABORT_ERR); + } else { + switch (aResponse.type()) { + case FileRequestResponse::Tnsresult: + HandleResponse(aResponse.get_nsresult()); + break; + + case FileRequestResponse::TFileRequestGetFileResponse: + HandleResponse(aResponse.get_FileRequestGetFileResponse()); + break; + + case FileRequestResponse::TFileRequestReadResponse: + HandleResponse(aResponse.get_FileRequestReadResponse().data()); + break; + + case FileRequestResponse::TFileRequestWriteResponse: + HandleResponse(JS::UndefinedHandleValue); + break; + + case FileRequestResponse::TFileRequestTruncateResponse: + HandleResponse(JS::UndefinedHandleValue); + break; + + case FileRequestResponse::TFileRequestFlushResponse: + HandleResponse(JS::UndefinedHandleValue); + break; + + case FileRequestResponse::TFileRequestGetMetadataResponse: + HandleResponse(aResponse.get_FileRequestGetMetadataResponse() + .metadata()); + break; + + default: + MOZ_CRASH("Unknown response type!"); + } + } + + mFileHandle->OnRequestFinished(/* aActorDestroyedNormally */ true); + + // Null this out so that we don't try to call OnRequestFinished() again in + // ActorDestroy. + mFileHandle = nullptr; + + return true; +} + +bool +BackgroundFileRequestChild::RecvProgress(const uint64_t& aProgress, + const uint64_t& aProgressMax) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mFileRequest); + + mFileRequest->OnProgress(aProgress, aProgressMax); + + return true; +} + +} // namespace dom +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/dom/filehandle/ActorsChild.h @@ -0,0 +1,175 @@ +/* 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_filehandle_ActorsChild_h +#define mozilla_dom_filehandle_ActorsChild_h + +#include "js/RootingAPI.h" +#include "mozilla/Attributes.h" +#include "mozilla/dom/FileHandleCommon.h" +#include "mozilla/dom/PBackgroundFileHandleChild.h" +#include "mozilla/dom/PBackgroundFileRequestChild.h" +#include "mozilla/dom/PBackgroundMutableFileChild.h" +#include "nsAutoPtr.h" + +class nsCString; + +namespace mozilla { +namespace dom { + +class FileHandleBase; +class FileRequestBase; +class MutableFileBase; + +class BackgroundMutableFileChildBase + : public ThreadObject + , public PBackgroundMutableFileChild +{ +protected: + friend class MutableFileBase; + + nsRefPtr<MutableFileBase> mTemporaryStrongMutableFile; + MutableFileBase* mMutableFile; + +public: + void + EnsureDOMObject(); + + MutableFileBase* + GetDOMObject() const + { + AssertIsOnOwningThread(); + return mMutableFile; + } + + void + ReleaseDOMObject(); + +protected: + BackgroundMutableFileChildBase(DEBUGONLY(PRThread* aOwningThread)); + + ~BackgroundMutableFileChildBase(); + + void + SendDeleteMeInternal(); + + virtual already_AddRefed<MutableFileBase> + CreateMutableFile() = 0; + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + virtual PBackgroundFileHandleChild* + AllocPBackgroundFileHandleChild(const FileMode& aMode) override; + + virtual bool + DeallocPBackgroundFileHandleChild(PBackgroundFileHandleChild* aActor) + override; + + bool + SendDeleteMe() = delete; +}; + +class BackgroundFileHandleChild + : public ThreadObject + , public PBackgroundFileHandleChild +{ + friend class BackgroundMutableFileChildBase; + friend class MutableFileBase; + + // mTemporaryStrongFileHandle is strong and is only valid until the end of + // NoteComplete() member function or until the NoteActorDestroyed() member + // function is called. + nsRefPtr<FileHandleBase> mTemporaryStrongFileHandle; + + // mFileHandle is weak and is valid until the NoteActorDestroyed() member + // function is called. + FileHandleBase* mFileHandle; + +public: + BackgroundFileHandleChild(DEBUGONLY(PRThread* aOwningThread,) + FileHandleBase* aFileHandle); + + void + SendDeleteMeInternal(); + +private: + ~BackgroundFileHandleChild(); + + void + NoteActorDestroyed(); + + void + NoteComplete(); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + bool + RecvComplete(const bool& aAborted) override; + + virtual PBackgroundFileRequestChild* + AllocPBackgroundFileRequestChild(const FileRequestParams& aParams) + override; + + virtual bool + DeallocPBackgroundFileRequestChild(PBackgroundFileRequestChild* aActor) + override; + + bool + SendDeleteMe() = delete; +}; + +class BackgroundFileRequestChild final + : public ThreadObject + , public PBackgroundFileRequestChild +{ + friend class BackgroundFileHandleChild; + friend class FileHandleBase; + + nsRefPtr<FileRequestBase> mFileRequest; + nsRefPtr<FileHandleBase> mFileHandle; + bool mActorDestroyed; + +private: + // Only created by FileHandleBase. + BackgroundFileRequestChild(DEBUGONLY(PRThread* aOwningThread,) + FileRequestBase* aFileRequest); + + // Only destroyed by BackgroundFileHandleChild. + ~BackgroundFileRequestChild(); + + void + HandleResponse(nsresult aResponse); + + void + HandleResponse(const FileRequestGetFileResponse& aResponse); + + void + HandleResponse(const nsCString& aResponse); + + void + HandleResponse(const FileRequestMetadata& aResponse); + + void + HandleResponse(JS::Handle<JS::Value> aResponse); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + virtual bool + Recv__delete__(const FileRequestResponse& aResponse) override; + + virtual bool + RecvProgress(const uint64_t& aProgress, + const uint64_t& aProgressMax) override; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_filehandle_ActorsChild_h
new file mode 100644 --- /dev/null +++ b/dom/filehandle/ActorsParent.cpp @@ -0,0 +1,2664 @@ +/* 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 "ActorsParent.h" + +#include "mozilla/Assertions.h" +#include "mozilla/Atomics.h" +#include "mozilla/Attributes.h" +#include "mozilla/unused.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/FileHandleCommon.h" +#include "mozilla/dom/PBackgroundFileHandleParent.h" +#include "mozilla/dom/PBackgroundFileRequestParent.h" +#include "mozilla/dom/indexedDB/ActorsParent.h" +#include "mozilla/dom/ipc/BlobParent.h" +#include "nsComponentManagerUtils.h" +#include "nsDebug.h" +#include "nsError.h" +#include "nsIEventTarget.h" +#include "nsIFileStreams.h" +#include "nsIInputStream.h" +#include "nsIOutputStream.h" +#include "nsIRunnable.h" +#include "nsISeekableStream.h" +#include "nsIThread.h" +#include "nsIThreadPool.h" +#include "nsNetUtil.h" +#include "nsStreamUtils.h" +#include "nsStringStream.h" +#include "nsTArray.h" +#include "nsThreadPool.h" +#include "nsThreadUtils.h" +#include "nsXPCOMCIDInternal.h" + +#define DISABLE_ASSERTS_FOR_FUZZING 0 + +#if DISABLE_ASSERTS_FOR_FUZZING +#define ASSERT_UNLESS_FUZZING(...) do { } while (0) +#else +#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__) +#endif + +namespace mozilla { +namespace dom { + +using namespace mozilla::ipc; + +namespace { + +/****************************************************************************** + * Constants + ******************************************************************************/ + +const uint32_t kThreadLimit = 5; +const uint32_t kIdleThreadLimit = 1; +const uint32_t kIdleThreadTimeoutMs = 30000; + +const uint32_t kStreamCopyBlockSize = 32768; + +} // namespace + +class FileHandleThreadPool::FileHandleQueue final + : public nsRunnable +{ + friend class FileHandleThreadPool; + + nsRefPtr<FileHandleThreadPool> mOwningFileHandleThreadPool; + nsRefPtr<FileHandle> mFileHandle; + nsTArray<nsRefPtr<FileHandleOp>> mQueue; + nsRefPtr<FileHandleOp> mCurrentOp; + bool mShouldFinish; + +public: + explicit + FileHandleQueue(FileHandleThreadPool* aFileHandleThreadPool, + FileHandle* aFileHandle); + + void + Enqueue(FileHandleOp* aFileHandleOp); + + void + Finish(); + + void + ProcessQueue(); + +private: + ~FileHandleQueue() {} + + NS_DECL_NSIRUNNABLE +}; + +struct FileHandleThreadPool::DelayedEnqueueInfo +{ + nsRefPtr<FileHandle> mFileHandle; + nsRefPtr<FileHandleOp> mFileHandleOp; + bool mFinish; +}; + +class FileHandleThreadPool::DirectoryInfo +{ + friend class FileHandleThreadPool; + + nsRefPtr<FileHandleThreadPool> mOwningFileHandleThreadPool; + nsTArray<nsRefPtr<FileHandleQueue>> mFileHandleQueues; + nsTArray<DelayedEnqueueInfo> mDelayedEnqueueInfos; + nsTHashtable<nsStringHashKey> mFilesReading; + nsTHashtable<nsStringHashKey> mFilesWriting; + +public: + FileHandleQueue* + CreateFileHandleQueue(FileHandle* aFileHandle); + + FileHandleQueue* + GetFileHandleQueue(FileHandle* aFileHandle); + + void + RemoveFileHandleQueue(FileHandle* aFileHandle); + + bool + HasRunningFileHandles() + { + return !mFileHandleQueues.IsEmpty(); + } + + DelayedEnqueueInfo* + CreateDelayedEnqueueInfo(FileHandle* aFileHandle, + FileHandleOp* aFileHandleOp, + bool aFinish); + + void + LockFileForReading(const nsAString& aFileName) + { + mFilesReading.PutEntry(aFileName); + } + + void + LockFileForWriting(const nsAString& aFileName) + { + mFilesWriting.PutEntry(aFileName); + } + + bool + IsFileLockedForReading(const nsAString& aFileName) + { + return mFilesReading.Contains(aFileName); + } + + bool + IsFileLockedForWriting(const nsAString& aFileName) + { + return mFilesWriting.Contains(aFileName); + } + +private: + explicit DirectoryInfo(FileHandleThreadPool* aFileHandleThreadPool) + : mOwningFileHandleThreadPool(aFileHandleThreadPool) + { } +}; + +struct FileHandleThreadPool::StoragesCompleteCallback final +{ + friend class nsAutoPtr<StoragesCompleteCallback>; + + nsTArray<nsCString> mDirectoryIds; + nsCOMPtr<nsIRunnable> mCallback; + + StoragesCompleteCallback(nsTArray<nsCString>&& aDatabaseIds, + nsIRunnable* aCallback); + +private: + ~StoragesCompleteCallback(); +}; + +/****************************************************************************** + * Actor class declarations + ******************************************************************************/ + +class FileHandle + : public PBackgroundFileHandleParent +{ + friend class BackgroundMutableFileParentBase; + + class FinishOp; + + nsRefPtr<BackgroundMutableFileParentBase> mMutableFile; + nsCOMPtr<nsISupports> mStream; + uint64_t mActiveRequestCount; + FileHandleStorage mStorage; + Atomic<bool> mInvalidatedOnAnyThread; + FileMode mMode; + bool mHasBeenActive; + bool mActorDestroyed; + bool mInvalidated; + bool mAborted; + bool mFinishOrAbortReceived; + bool mFinishedOrAborted; + bool mForceAborted; + + DEBUGONLY(nsCOMPtr<nsIEventTarget> mThreadPoolEventTarget;) + +public: + void + AssertIsOnThreadPool() const; + + bool + IsActorDestroyed() const + { + AssertIsOnBackgroundThread(); + + return mActorDestroyed; + } + + // Must be called on the background thread. + bool + IsInvalidated() const + { + MOZ_ASSERT(IsOnBackgroundThread(), "Use IsInvalidatedOnAnyThread()"); + MOZ_ASSERT_IF(mInvalidated, mAborted); + + return mInvalidated; + } + + // May be called on any thread, but is more expensive than IsInvalidated(). + bool + IsInvalidatedOnAnyThread() const + { + return mInvalidatedOnAnyThread; + } + + void + SetActive() + { + AssertIsOnBackgroundThread(); + + mHasBeenActive = true; + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::FileHandle) + + nsresult + GetOrCreateStream(nsISupports** aStream); + + void + Abort(bool aForce); + + FileHandleStorage + Storage() const + { + return mStorage; + } + + FileMode + Mode() const + { + return mMode; + } + + BackgroundMutableFileParentBase* + GetMutableFile() const + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mMutableFile); + + return mMutableFile; + } + + bool + IsAborted() const + { + AssertIsOnBackgroundThread(); + + return mAborted; + } + + PBackgroundParent* + GetBackgroundParent() const + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!IsActorDestroyed()); + + return GetMutableFile()->GetBackgroundParent(); + } + + void + NoteActiveRequest(); + + void + NoteFinishedRequest(); + + void + Invalidate(); + +private: + // This constructor is only called by BackgroundMutableFileParentBase. + FileHandle(BackgroundMutableFileParentBase* aMutableFile, + FileMode aMode); + + // Reference counted. + ~FileHandle(); + + void + MaybeFinishOrAbort() + { + AssertIsOnBackgroundThread(); + + // If we've already finished or aborted then there's nothing else to do. + if (mFinishedOrAborted) { + return; + } + + // If there are active requests then we have to wait for those requests to + // complete (see NoteFinishedRequest). + if (mActiveRequestCount) { + return; + } + + // If we haven't yet received a finish or abort message then there could be + // additional requests coming so we should wait unless we're being forced to + // abort. + if (!mFinishOrAbortReceived && !mForceAborted) { + return; + } + + FinishOrAbort(); + } + + void + SendCompleteNotification(bool aAborted); + + bool + VerifyRequestParams(const FileRequestParams& aParams) const; + + bool + VerifyRequestData(const FileRequestData& aData) const; + + void + FinishOrAbort(); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + virtual bool + RecvDeleteMe() override; + + virtual bool + RecvFinish() override; + + virtual bool + RecvAbort() override; + + virtual PBackgroundFileRequestParent* + AllocPBackgroundFileRequestParent(const FileRequestParams& aParams) override; + + virtual bool + RecvPBackgroundFileRequestConstructor(PBackgroundFileRequestParent* aActor, + const FileRequestParams& aParams) + override; + + virtual bool + DeallocPBackgroundFileRequestParent(PBackgroundFileRequestParent* aActor) + override; +}; + +class FileHandleOp +{ +protected: + nsCOMPtr<nsIEventTarget> mOwningThread; + nsRefPtr<FileHandle> mFileHandle; + +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileHandleOp) + + void + AssertIsOnOwningThread() const + { + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mOwningThread); + DebugOnly<bool> current; + MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t))); + MOZ_ASSERT(current); + } + + nsIEventTarget* + OwningThread() const + { + return mOwningThread; + } + + void + AssertIsOnThreadPool() const + { + MOZ_ASSERT(mFileHandle); + mFileHandle->AssertIsOnThreadPool(); + } + + void + Enqueue(); + + virtual void + RunOnThreadPool() = 0; + + virtual void + RunOnOwningThread() = 0; + +protected: + FileHandleOp(FileHandle* aFileHandle) + : mOwningThread(NS_GetCurrentThread()) + , mFileHandle(aFileHandle) + { + AssertIsOnOwningThread(); + MOZ_ASSERT(aFileHandle); + } + + virtual + ~FileHandleOp() + { } +}; + +class FileHandle::FinishOp + : public FileHandleOp +{ + friend class FileHandle; + + bool mAborted; + +private: + FinishOp(FileHandle* aFileHandle, + bool aAborted) + : FileHandleOp(aFileHandle) + , mAborted(aAborted) + { + MOZ_ASSERT(aFileHandle); + } + + ~FinishOp() + { } + + virtual void + RunOnThreadPool() override; + + virtual void + RunOnOwningThread() override; +}; + +class NormalFileHandleOp + : public FileHandleOp + , public PBackgroundFileRequestParent +{ + nsresult mResultCode; + Atomic<bool> mOperationMayProceed; + bool mActorDestroyed; + const bool mFileHandleIsAborted; + + DEBUGONLY(bool mResponseSent;) + +protected: + nsCOMPtr<nsISupports> mFileStream; + +public: + void + NoteActorDestroyed() + { + AssertIsOnOwningThread(); + + mActorDestroyed = true; + mOperationMayProceed = false; + } + + bool + IsActorDestroyed() const + { + AssertIsOnOwningThread(); + + return mActorDestroyed; + } + + // May be called on any thread, but you should call IsActorDestroyed() if + // you know you're on the background thread because it is slightly faster. + bool + OperationMayProceed() const + { + return mOperationMayProceed; + } + + // May be overridden by subclasses if they need to perform work on the + // background thread before being enqueued. Returning false will kill the + // child actors and prevent enqueue. + virtual bool + Init(FileHandle* aFileHandle); + + // This callback will be called on the background thread before releasing the + // final reference to this request object. Subclasses may perform any + // additional cleanup here but must always call the base class implementation. + virtual void + Cleanup(); + +protected: + NormalFileHandleOp(FileHandle* aFileHandle) + : FileHandleOp(aFileHandle) + , mResultCode(NS_OK) + , mOperationMayProceed(true) + , mActorDestroyed(false) + , mFileHandleIsAborted(aFileHandle->IsAborted()) + DEBUGONLY(, mResponseSent(false)) + { + MOZ_ASSERT(aFileHandle); + } + + virtual + ~NormalFileHandleOp(); + + // Must be overridden in subclasses. Called on the target thread to allow the + // subclass to perform necessary file operations. A successful return value + // will trigger a SendSuccessResult callback on the background thread while + // a failure value will trigger a SendFailureResult callback. + virtual nsresult + DoFileWork(FileHandle* aFileHandle) = 0; + + // Subclasses use this override to set the IPDL response value. + virtual void + GetResponse(FileRequestResponse& aResponse) = 0; + +private: + nsresult + SendSuccessResult(); + + bool + SendFailureResult(nsresult aResultCode); + + virtual void + RunOnThreadPool() override; + + virtual void + RunOnOwningThread() override; + + // IPDL methods. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; +}; + +class CopyFileHandleOp + : public NormalFileHandleOp +{ + class ProgressRunnable; + +protected: + nsCOMPtr<nsISupports> mBufferStream; + + uint64_t mOffset; + uint64_t mSize; + + bool mRead; + +protected: + CopyFileHandleOp(FileHandle* aFileHandle) + : NormalFileHandleOp(aFileHandle) + , mOffset(0) + , mSize(0) + , mRead(true) + { } + + virtual nsresult + DoFileWork(FileHandle* aFileHandle) override; + + virtual void + Cleanup() override; +}; + +class CopyFileHandleOp::ProgressRunnable final + : public nsRunnable +{ + nsRefPtr<CopyFileHandleOp> mCopyFileHandleOp; + uint64_t mProgress; + uint64_t mProgressMax; + +public: + ProgressRunnable(CopyFileHandleOp* aCopyFileHandleOp, + uint64_t aProgress, + uint64_t aProgressMax) + : mCopyFileHandleOp(aCopyFileHandleOp) + , mProgress(aProgress) + , mProgressMax(aProgressMax) + { } + +private: + ~ProgressRunnable() {} + + NS_DECL_NSIRUNNABLE +}; + +class GetMetadataOp + : public NormalFileHandleOp +{ + friend class FileHandle; + + const FileRequestGetMetadataParams mParams; + +protected: + FileRequestMetadata mMetadata; + +protected: + // Only created by FileHandle. + GetMetadataOp(FileHandle* aFileHandle, + const FileRequestParams& aParams); + + ~GetMetadataOp() + { } + + virtual nsresult + DoFileWork(FileHandle* aFileHandle) override; + + virtual void + GetResponse(FileRequestResponse& aResponse) override; +}; + +class ReadOp final + : public CopyFileHandleOp +{ + friend class FileHandle; + + class MemoryOutputStream; + + const FileRequestReadParams mParams; + +private: + // Only created by FileHandle. + ReadOp(FileHandle* aFileHandle, + const FileRequestParams& aParams); + + ~ReadOp() + { } + + virtual bool + Init(FileHandle* aFileHandle) override; + + virtual void + GetResponse(FileRequestResponse& aResponse) override; +}; + +class ReadOp::MemoryOutputStream final + : public nsIOutputStream +{ + nsCString mData; + uint64_t mOffset; + +public: + static already_AddRefed<MemoryOutputStream> + Create(uint64_t aSize); + + const nsCString& + Data() const + { + return mData; + } + +private: + MemoryOutputStream() + : mOffset(0) + { } + + virtual ~MemoryOutputStream() + { } + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIOUTPUTSTREAM +}; + +class WriteOp final + : public CopyFileHandleOp +{ + friend class FileHandle; + + const FileRequestWriteParams mParams; + +private: + // Only created by FileHandle. + WriteOp(FileHandle* aFileHandle, + const FileRequestParams& aParams); + + ~WriteOp() + { } + + virtual bool + Init(FileHandle* aFileHandle) override; + + virtual void + GetResponse(FileRequestResponse& aResponse) override; +}; + +class TruncateOp final + : public NormalFileHandleOp +{ + friend class FileHandle; + + const FileRequestTruncateParams mParams; + +private: + // Only created by FileHandle. + TruncateOp(FileHandle* aFileHandle, + const FileRequestParams& aParams); + + ~TruncateOp() + { } + + virtual nsresult + DoFileWork(FileHandle* aFileHandle) override; + + virtual void + GetResponse(FileRequestResponse& aResponse) override; +}; + +class FlushOp final + : public NormalFileHandleOp +{ + friend class FileHandle; + + const FileRequestFlushParams mParams; + +private: + // Only created by FileHandle. + FlushOp(FileHandle* aFileHandle, + const FileRequestParams& aParams); + + ~FlushOp() + { } + + virtual nsresult + DoFileWork(FileHandle* aFileHandle) override; + + virtual void + GetResponse(FileRequestResponse& aResponse) override; +}; + +class GetFileOp final + : public GetMetadataOp +{ + friend class FileHandle; + + PBackgroundParent* mBackgroundParent; + +private: + // Only created by FileHandle. + GetFileOp(FileHandle* aFileHandle, + const FileRequestParams& aParams); + + ~GetFileOp() + { } + + virtual void + GetResponse(FileRequestResponse& aResponse) override; +}; + +namespace { + +/******************************************************************************* + * Helper Functions + ******************************************************************************/ + +FileHandleThreadPool* +GetFileHandleThreadPoolFor(FileHandleStorage aStorage) +{ + switch (aStorage) { + case FILE_HANDLE_STORAGE_IDB: + return mozilla::dom::indexedDB::GetFileHandleThreadPool(); + + default: + MOZ_CRASH("Bad file handle storage value!"); + } +} + +} // namespace + +/******************************************************************************* + * FileHandleThreadPool implementation + ******************************************************************************/ + +FileHandleThreadPool::FileHandleThreadPool() + : mOwningThread(NS_GetCurrentThread()) + , mShutdownRequested(false) + , mShutdownComplete(false) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mOwningThread); + AssertIsOnOwningThread(); +} + +FileHandleThreadPool::~FileHandleThreadPool() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!mDirectoryInfos.Count()); + MOZ_ASSERT(mCompleteCallbacks.IsEmpty()); + MOZ_ASSERT(mShutdownRequested); + MOZ_ASSERT(mShutdownComplete); +} + +// static +already_AddRefed<FileHandleThreadPool> +FileHandleThreadPool::Create() +{ + AssertIsOnBackgroundThread(); + + nsRefPtr<FileHandleThreadPool> fileHandleThreadPool = + new FileHandleThreadPool(); + fileHandleThreadPool->AssertIsOnOwningThread(); + + if (NS_WARN_IF(NS_FAILED(fileHandleThreadPool->Init()))) { + return nullptr; + } + + return fileHandleThreadPool.forget(); +} + +#ifdef DEBUG + +void +FileHandleThreadPool::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mOwningThread); + + bool current; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t))); + MOZ_ASSERT(current); +} + +nsIEventTarget* +FileHandleThreadPool::GetThreadPoolEventTarget() const +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mThreadPool); + + return mThreadPool; +} + +#endif // DEBUG + +void +FileHandleThreadPool::Enqueue(FileHandle* aFileHandle, + FileHandleOp* aFileHandleOp, + bool aFinish) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aFileHandle); + MOZ_ASSERT(!mShutdownRequested); + + BackgroundMutableFileParentBase* mutableFile = aFileHandle->GetMutableFile(); + + const nsACString& directoryId = mutableFile->DirectoryId(); + const nsAString& fileName = mutableFile->FileName(); + bool modeIsWrite = aFileHandle->Mode() == FileMode::Readwrite; + + DirectoryInfo* directoryInfo; + if (!mDirectoryInfos.Get(directoryId, &directoryInfo)) { + nsAutoPtr<DirectoryInfo> newDirectoryInfo(new DirectoryInfo(this)); + + mDirectoryInfos.Put(directoryId, newDirectoryInfo); + + directoryInfo = newDirectoryInfo.forget(); + } + + FileHandleQueue* existingFileHandleQueue = + directoryInfo->GetFileHandleQueue(aFileHandle); + + if (existingFileHandleQueue) { + existingFileHandleQueue->Enqueue(aFileHandleOp); + if (aFinish) { + existingFileHandleQueue->Finish(); + } + return; + } + + bool lockedForReading = directoryInfo->IsFileLockedForReading(fileName); + bool lockedForWriting = directoryInfo->IsFileLockedForWriting(fileName); + + if (modeIsWrite) { + if (!lockedForWriting) { + directoryInfo->LockFileForWriting(fileName); + } + } + else { + if (!lockedForReading) { + directoryInfo->LockFileForReading(fileName); + } + } + + if (lockedForWriting || (lockedForReading && modeIsWrite)) { + directoryInfo->CreateDelayedEnqueueInfo(aFileHandle, + aFileHandleOp, + aFinish); + } + else { + FileHandleQueue* fileHandleQueue = + directoryInfo->CreateFileHandleQueue(aFileHandle); + + if (aFileHandleOp) { + fileHandleQueue->Enqueue(aFileHandleOp); + if (aFinish) { + existingFileHandleQueue->Finish(); + } + } + } +} + +void +FileHandleThreadPool::WaitForDirectoriesToComplete( + nsTArray<nsCString>&& aDirectoryIds, + nsIRunnable* aCallback) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!aDirectoryIds.IsEmpty()); + MOZ_ASSERT(aCallback); + + nsAutoPtr<StoragesCompleteCallback> callback( + new StoragesCompleteCallback(Move(aDirectoryIds), aCallback)); + + if (!MaybeFireCallback(callback)) { + mCompleteCallbacks.AppendElement(callback.forget()); + } +} + +void +FileHandleThreadPool::Shutdown() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!mShutdownRequested); + MOZ_ASSERT(!mShutdownComplete); + + mShutdownRequested = true; + + if (!mThreadPool) { + MOZ_ASSERT(!mDirectoryInfos.Count()); + MOZ_ASSERT(mCompleteCallbacks.IsEmpty()); + + mShutdownComplete = true; + return; + } + + if (!mDirectoryInfos.Count()) { + Cleanup(); + + MOZ_ASSERT(mShutdownComplete); + return; + } + + nsIThread* currentThread = NS_GetCurrentThread(); + MOZ_ASSERT(currentThread); + + while (!mShutdownComplete) { + MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread)); + } +} + +nsresult +FileHandleThreadPool::Init() +{ + AssertIsOnOwningThread(); + + mThreadPool = new nsThreadPool(); + + nsresult rv = mThreadPool->SetName(NS_LITERAL_CSTRING("FileHandles")); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = mThreadPool->SetThreadLimit(kThreadLimit); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = mThreadPool->SetIdleThreadLimit(kIdleThreadLimit); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = mThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +void +FileHandleThreadPool::Cleanup() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mThreadPool); + MOZ_ASSERT(mShutdownRequested); + MOZ_ASSERT(!mShutdownComplete); + MOZ_ASSERT(!mDirectoryInfos.Count()); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mThreadPool->Shutdown())); + + if (!mCompleteCallbacks.IsEmpty()) { + // Run all callbacks manually now. + for (uint32_t count = mCompleteCallbacks.Length(), index = 0; + index < count; + index++) { + nsAutoPtr<StoragesCompleteCallback> completeCallback( + mCompleteCallbacks[index].forget()); + MOZ_ASSERT(completeCallback); + MOZ_ASSERT(completeCallback->mCallback); + + unused << completeCallback->mCallback->Run(); + } + + mCompleteCallbacks.Clear(); + + // And make sure they get processed. + nsIThread* currentThread = NS_GetCurrentThread(); + MOZ_ASSERT(currentThread); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_ProcessPendingEvents(currentThread))); + } + + mShutdownComplete = true; +} + +void +FileHandleThreadPool::FinishFileHandle(FileHandle* aFileHandle) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aFileHandle); + + BackgroundMutableFileParentBase* mutableFile = aFileHandle->GetMutableFile(); + const nsACString& directoryId = mutableFile->DirectoryId(); + + DirectoryInfo* directoryInfo; + if (!mDirectoryInfos.Get(directoryId, &directoryInfo)) { + NS_ERROR("We don't know anyting about this directory?!"); + return; + } + + directoryInfo->RemoveFileHandleQueue(aFileHandle); + + if (!directoryInfo->HasRunningFileHandles()) { + mDirectoryInfos.Remove(directoryId); + + // See if we need to fire any complete callbacks. + uint32_t index = 0; + while (index < mCompleteCallbacks.Length()) { + if (MaybeFireCallback(mCompleteCallbacks[index])) { + mCompleteCallbacks.RemoveElementAt(index); + } + else { + index++; + } + } + + if (mShutdownRequested && !mDirectoryInfos.Count()) { + Cleanup(); + } + } +} + +bool +FileHandleThreadPool::MaybeFireCallback(StoragesCompleteCallback* aCallback) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aCallback); + MOZ_ASSERT(!aCallback->mDirectoryIds.IsEmpty()); + MOZ_ASSERT(aCallback->mCallback); + + for (uint32_t count = aCallback->mDirectoryIds.Length(), index = 0; + index < count; + index++) { + const nsCString& directoryId = aCallback->mDirectoryIds[index]; + MOZ_ASSERT(!directoryId.IsEmpty()); + + if (mDirectoryInfos.Get(directoryId, nullptr)) { + return false; + } + } + + aCallback->mCallback->Run(); + return true; +} + +FileHandleThreadPool:: +FileHandleQueue::FileHandleQueue(FileHandleThreadPool* aFileHandleThreadPool, + FileHandle* aFileHandle) + : mOwningFileHandleThreadPool(aFileHandleThreadPool) + , mFileHandle(aFileHandle) + , mShouldFinish(false) +{ + MOZ_ASSERT(aFileHandleThreadPool); + aFileHandleThreadPool->AssertIsOnOwningThread(); + MOZ_ASSERT(aFileHandle); +} + +void +FileHandleThreadPool:: +FileHandleQueue::Enqueue(FileHandleOp* aFileHandleOp) +{ + MOZ_ASSERT(!mShouldFinish, "Enqueue called after Finish!"); + + mQueue.AppendElement(aFileHandleOp); + + ProcessQueue(); +} + +void +FileHandleThreadPool:: +FileHandleQueue::Finish() +{ + MOZ_ASSERT(!mShouldFinish, "Finish called more than once!"); + + mShouldFinish = true; +} + +void +FileHandleThreadPool:: +FileHandleQueue::ProcessQueue() +{ + if (mCurrentOp) { + return; + } + + if (mQueue.IsEmpty()) { + if (mShouldFinish) { + mOwningFileHandleThreadPool->FinishFileHandle(mFileHandle); + + // Make sure this is released on this thread. + mOwningFileHandleThreadPool = nullptr; + } + + return; + } + + mCurrentOp = mQueue[0]; + mQueue.RemoveElementAt(0); + + nsCOMPtr<nsIThreadPool> threadPool = mOwningFileHandleThreadPool->mThreadPool; + MOZ_ASSERT(threadPool); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(threadPool->Dispatch(this, NS_DISPATCH_NORMAL))); +} + +NS_IMETHODIMP +FileHandleThreadPool:: +FileHandleQueue::Run() +{ + MOZ_ASSERT(mCurrentOp); + + if (IsOnBackgroundThread()) { + nsRefPtr<FileHandleOp> currentOp; + + mCurrentOp.swap(currentOp); + ProcessQueue(); + + currentOp->RunOnOwningThread(); + } else { + mCurrentOp->RunOnThreadPool(); + + nsCOMPtr<nsIEventTarget> backgroundThread = mCurrentOp->OwningThread(); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + backgroundThread->Dispatch(this, NS_DISPATCH_NORMAL))); + } + + return NS_OK; +} + +auto +FileHandleThreadPool:: +DirectoryInfo::CreateFileHandleQueue(FileHandle* aFileHandle) + -> FileHandleQueue* +{ + nsRefPtr<FileHandleQueue>* fileHandleQueue = + mFileHandleQueues.AppendElement(); + *fileHandleQueue = new FileHandleQueue(mOwningFileHandleThreadPool, + aFileHandle); + return fileHandleQueue->get(); +} + +auto +FileHandleThreadPool:: +DirectoryInfo::GetFileHandleQueue(FileHandle* aFileHandle) -> FileHandleQueue* +{ + uint32_t count = mFileHandleQueues.Length(); + for (uint32_t index = 0; index < count; index++) { + nsRefPtr<FileHandleQueue>& fileHandleQueue = mFileHandleQueues[index]; + if (fileHandleQueue->mFileHandle == aFileHandle) { + return fileHandleQueue; + } + } + return nullptr; +} + +void +FileHandleThreadPool:: +DirectoryInfo::RemoveFileHandleQueue(FileHandle* aFileHandle) +{ + for (uint32_t index = 0; index < mDelayedEnqueueInfos.Length(); index++) { + if (mDelayedEnqueueInfos[index].mFileHandle == aFileHandle) { + MOZ_ASSERT(!mDelayedEnqueueInfos[index].mFileHandleOp, "Should be null!"); + mDelayedEnqueueInfos.RemoveElementAt(index); + return; + } + } + + uint32_t fileHandleCount = mFileHandleQueues.Length(); + + // We can't just remove entries from lock hash tables, we have to rebuild + // them instead. Multiple FileHandle objects may lock the same file + // (one entry can represent multiple locks). + + mFilesReading.Clear(); + mFilesWriting.Clear(); + + for (uint32_t index = 0, count = fileHandleCount; index < count; index++) { + FileHandle* fileHandle = mFileHandleQueues[index]->mFileHandle; + if (fileHandle == aFileHandle) { + MOZ_ASSERT(count == fileHandleCount, "More than one match?!"); + + mFileHandleQueues.RemoveElementAt(index); + index--; + count--; + + continue; + } + + const nsAString& fileName = fileHandle->GetMutableFile()->FileName(); + + if (fileHandle->Mode() == FileMode::Readwrite) { + if (!IsFileLockedForWriting(fileName)) { + LockFileForWriting(fileName); + } + } + else { + if (!IsFileLockedForReading(fileName)) { + LockFileForReading(fileName); + } + } + } + + MOZ_ASSERT(mFileHandleQueues.Length() == fileHandleCount - 1, + "Didn't find the file handle we were looking for!"); + + nsTArray<DelayedEnqueueInfo> delayedEnqueueInfos; + delayedEnqueueInfos.SwapElements(mDelayedEnqueueInfos); + + for (uint32_t index = 0; index < delayedEnqueueInfos.Length(); index++) { + DelayedEnqueueInfo& delayedEnqueueInfo = delayedEnqueueInfos[index]; + mOwningFileHandleThreadPool->Enqueue(delayedEnqueueInfo.mFileHandle, + delayedEnqueueInfo.mFileHandleOp, + delayedEnqueueInfo.mFinish); + } +} + +auto +FileHandleThreadPool:: +DirectoryInfo::CreateDelayedEnqueueInfo(FileHandle* aFileHandle, + FileHandleOp* aFileHandleOp, + bool aFinish) -> DelayedEnqueueInfo* +{ + DelayedEnqueueInfo* info = mDelayedEnqueueInfos.AppendElement(); + info->mFileHandle = aFileHandle; + info->mFileHandleOp = aFileHandleOp; + info->mFinish = aFinish; + return info; +} + +FileHandleThreadPool:: +StoragesCompleteCallback::StoragesCompleteCallback( + nsTArray<nsCString>&& aDirectoryIds, + nsIRunnable* aCallback) + : mDirectoryIds(Move(aDirectoryIds)) + , mCallback(aCallback) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mDirectoryIds.IsEmpty()); + MOZ_ASSERT(aCallback); + + MOZ_COUNT_CTOR(FileHandleThreadPool::StoragesCompleteCallback); +} + +FileHandleThreadPool:: +StoragesCompleteCallback::~StoragesCompleteCallback() +{ + AssertIsOnBackgroundThread(); + + MOZ_COUNT_DTOR(FileHandleThreadPool::StoragesCompleteCallback); +} + +/******************************************************************************* + * BackgroundMutableFileParentBase + ******************************************************************************/ + +BackgroundMutableFileParentBase::BackgroundMutableFileParentBase( + FileHandleStorage aStorage, + const nsACString& aDirectoryId, + const nsAString& aFileName, + nsIFile* aFile) + : mDirectoryId(aDirectoryId) + , mFileName(aFileName) + , mStorage(aStorage) + , mInvalidated(false) + , mActorWasAlive(false) + , mActorDestroyed(false) + , mFile(aFile) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aStorage != FILE_HANDLE_STORAGE_MAX); + MOZ_ASSERT(!aDirectoryId.IsEmpty()); + MOZ_ASSERT(!aFileName.IsEmpty()); + MOZ_ASSERT(aFile); +} + +BackgroundMutableFileParentBase::~BackgroundMutableFileParentBase() +{ + MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed); +} + +void +BackgroundMutableFileParentBase::Invalidate() +{ + AssertIsOnBackgroundThread(); + + class MOZ_STACK_CLASS Helper final + { + public: + static bool + InvalidateFileHandles(nsTHashtable<nsPtrHashKey<FileHandle>>& aTable) + { + AssertIsOnBackgroundThread(); + + const uint32_t count = aTable.Count(); + if (!count) { + return true; + } + + FallibleTArray<nsRefPtr<FileHandle>> fileHandles; + if (NS_WARN_IF(!fileHandles.SetCapacity(count, fallible))) { + return false; + } + + for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) { + if (NS_WARN_IF(!fileHandles.AppendElement(iter.Get()->GetKey(), + fallible))) { + return false; + } + } + + if (count) { + for (uint32_t index = 0; index < count; index++) { + nsRefPtr<FileHandle> fileHandle = fileHandles[index].forget(); + MOZ_ASSERT(fileHandle); + + fileHandle->Invalidate(); + } + } + + return true; + } + }; + + if (mInvalidated) { + return; + } + + mInvalidated = true; + + if (!Helper::InvalidateFileHandles(mFileHandles)) { + NS_WARNING("Failed to abort all file handles!"); + } +} + +bool +BackgroundMutableFileParentBase::RegisterFileHandle(FileHandle* aFileHandle) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aFileHandle); + MOZ_ASSERT(!mFileHandles.GetEntry(aFileHandle)); + MOZ_ASSERT(!mInvalidated); + + if (NS_WARN_IF(!mFileHandles.PutEntry(aFileHandle, fallible))) { + return false; + } + + if (mFileHandles.Count() == 1) { + NoteActiveState(); + } + + return true; +} + +void +BackgroundMutableFileParentBase::UnregisterFileHandle(FileHandle* aFileHandle) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aFileHandle); + MOZ_ASSERT(mFileHandles.GetEntry(aFileHandle)); + + mFileHandles.RemoveEntry(aFileHandle); + + if (!mFileHandles.Count()) { + NoteInactiveState(); + } +} + +void +BackgroundMutableFileParentBase::SetActorAlive() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorWasAlive); + MOZ_ASSERT(!mActorDestroyed); + + mActorWasAlive = true; + + // This reference will be absorbed by IPDL and released when the actor is + // destroyed. + AddRef(); +} + +already_AddRefed<nsISupports> +BackgroundMutableFileParentBase::CreateStream(bool aReadOnly) +{ + AssertIsOnBackgroundThread(); + + nsresult rv; + + if (aReadOnly) { + nsCOMPtr<nsIInputStream> stream; + rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), mFile, -1, -1, + nsIFileInputStream::DEFER_OPEN); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + return stream.forget(); + } + + nsCOMPtr<nsIFileStream> stream; + rv = NS_NewLocalFileStream(getter_AddRefs(stream), mFile, -1, -1, + nsIFileStream::DEFER_OPEN); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + return stream.forget(); +} + +void +BackgroundMutableFileParentBase::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + mActorDestroyed = true; + + if (!IsInvalidated()) { + Invalidate(); + } +} + +PBackgroundFileHandleParent* +BackgroundMutableFileParentBase::AllocPBackgroundFileHandleParent( + const FileMode& aMode) +{ + AssertIsOnBackgroundThread(); + + if (NS_WARN_IF(aMode != FileMode::Readonly && + aMode != FileMode::Readwrite)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + nsRefPtr<FileHandle> fileHandle = new FileHandle(this, aMode); + + return fileHandle.forget().take(); +} + +bool +BackgroundMutableFileParentBase::RecvPBackgroundFileHandleConstructor( + PBackgroundFileHandleParent* aActor, + const FileMode& aMode) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(aMode == FileMode::Readonly || aMode == FileMode::Readwrite); + + FileHandleThreadPool* fileHandleThreadPool = + GetFileHandleThreadPoolFor(mStorage); + MOZ_ASSERT(fileHandleThreadPool); + + auto* fileHandle = static_cast<FileHandle*>(aActor); + + // Add a placeholder for this file handle immediately. + fileHandleThreadPool->Enqueue(fileHandle, nullptr, false); + + fileHandle->SetActive(); + + if (NS_WARN_IF(!RegisterFileHandle(fileHandle))) { + fileHandle->Abort(/* aForce */ false); + return true; + } + + return true; +} + +bool +BackgroundMutableFileParentBase::DeallocPBackgroundFileHandleParent( + PBackgroundFileHandleParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + nsRefPtr<FileHandle> fileHandle = + dont_AddRef(static_cast<FileHandle*>(aActor)); + return true; +} + +bool +BackgroundMutableFileParentBase::RecvDeleteMe() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mActorDestroyed); + + return PBackgroundMutableFileParent::Send__delete__(this); +} + +bool +BackgroundMutableFileParentBase::RecvGetFileId(int64_t* aFileId) +{ + AssertIsOnBackgroundThread(); + + *aFileId = -1; + return true; +} + +/******************************************************************************* + * FileHandle + ******************************************************************************/ + +FileHandle::FileHandle(BackgroundMutableFileParentBase* aMutableFile, + FileMode aMode) + : mMutableFile(aMutableFile) + , mActiveRequestCount(0) + , mStorage(aMutableFile->Storage()) + , mInvalidatedOnAnyThread(false) + , mMode(aMode) + , mHasBeenActive(false) + , mActorDestroyed(false) + , mInvalidated(false) + , mAborted(false) + , mFinishOrAbortReceived(false) + , mFinishedOrAborted(false) + , mForceAborted(false) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aMutableFile); + +#ifdef DEBUG + FileHandleThreadPool* fileHandleThreadPool = + GetFileHandleThreadPoolFor(mStorage); + MOZ_ASSERT(fileHandleThreadPool); + + mThreadPoolEventTarget = fileHandleThreadPool->GetThreadPoolEventTarget(); +#endif +} + +FileHandle::~FileHandle() +{ + MOZ_ASSERT(!mActiveRequestCount); + MOZ_ASSERT(mActorDestroyed); + MOZ_ASSERT_IF(mHasBeenActive, mFinishedOrAborted); +} + +void +FileHandle::AssertIsOnThreadPool() const +{ + MOZ_ASSERT(mThreadPoolEventTarget); + DebugOnly<bool> current; + MOZ_ASSERT(NS_SUCCEEDED(mThreadPoolEventTarget->IsOnCurrentThread(¤t))); + MOZ_ASSERT(current); +} + +nsresult +FileHandle::GetOrCreateStream(nsISupports** aStream) +{ + AssertIsOnBackgroundThread(); + + if (!mStream) { + nsCOMPtr<nsISupports> stream = + mMutableFile->CreateStream(mMode == FileMode::Readonly); + if (NS_WARN_IF(!stream)) { + return NS_ERROR_FAILURE; + } + + stream.swap(mStream); + } + + nsCOMPtr<nsISupports> stream(mStream); + stream.forget(aStream); + + return NS_OK; +} + +void +FileHandle::Abort(bool aForce) +{ + AssertIsOnBackgroundThread(); + + mAborted = true; + + if (aForce) { + mForceAborted = true; + } + + MaybeFinishOrAbort(); +} + +void +FileHandle::NoteActiveRequest() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mActiveRequestCount < UINT64_MAX); + + mActiveRequestCount++; +} + +void +FileHandle::NoteFinishedRequest() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mActiveRequestCount); + + mActiveRequestCount--; + + MaybeFinishOrAbort(); +} + +void +FileHandle::Invalidate() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(mInvalidated == mInvalidatedOnAnyThread); + + if (!mInvalidated) { + mInvalidated = true; + mInvalidatedOnAnyThread = true; + + Abort(/* aForce */ true); + } +} + +void +FileHandle::SendCompleteNotification(bool aAborted) +{ + AssertIsOnBackgroundThread(); + + if (!IsActorDestroyed()) { + unused << SendComplete(aAborted); + } +} + +bool +FileHandle::VerifyRequestParams(const FileRequestParams& aParams) const +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aParams.type() != FileRequestParams::T__None); + + switch (aParams.type()) { + case FileRequestParams::TFileRequestGetMetadataParams: { + const FileRequestGetMetadataParams& params = + aParams.get_FileRequestGetMetadataParams(); + + if (NS_WARN_IF(!params.size() && !params.lastModified())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + break; + } + + case FileRequestParams::TFileRequestReadParams: { + const FileRequestReadParams& params = + aParams.get_FileRequestReadParams(); + + if (NS_WARN_IF(params.offset() == UINT64_MAX)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(!params.size())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + break; + } + + case FileRequestParams::TFileRequestWriteParams: { + if (NS_WARN_IF(mMode != FileMode::Readwrite)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + const FileRequestWriteParams& params = + aParams.get_FileRequestWriteParams(); + + + if (NS_WARN_IF(!params.dataLength())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(!VerifyRequestData(params.data()))) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + break; + } + + case FileRequestParams::TFileRequestTruncateParams: { + if (NS_WARN_IF(mMode != FileMode::Readwrite)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + const FileRequestTruncateParams& params = + aParams.get_FileRequestTruncateParams(); + + if (NS_WARN_IF(params.offset() == UINT64_MAX)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + break; + } + + case FileRequestParams::TFileRequestFlushParams: { + if (NS_WARN_IF(mMode != FileMode::Readwrite)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + break; + } + + case FileRequestParams::TFileRequestGetFileParams: { + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + return true; +} + +bool +FileHandle::VerifyRequestData(const FileRequestData& aData) const +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aData.type() != FileRequestData::T__None); + + switch (aData.type()) { + case FileRequestData::TFileRequestStringData: { + const FileRequestStringData& data = + aData.get_FileRequestStringData(); + + if (NS_WARN_IF(data.string().IsEmpty())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + break; + } + + case FileRequestData::TFileRequestBlobData: { + const FileRequestBlobData& data = + aData.get_FileRequestBlobData(); + + if (NS_WARN_IF(data.blobChild())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + if (NS_WARN_IF(!data.blobParent())) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + return true; +} + +void +FileHandle::FinishOrAbort() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!mFinishedOrAborted); + + mFinishedOrAborted = true; + + if (!mHasBeenActive) { + return; + } + + nsRefPtr<FinishOp> finishOp = new FinishOp(this, mAborted); + + FileHandleThreadPool* fileHandleThreadPool = + GetFileHandleThreadPoolFor(mStorage); + MOZ_ASSERT(fileHandleThreadPool); + + fileHandleThreadPool->Enqueue(this, finishOp, true); +} + +void +FileHandle::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnBackgroundThread(); + + MOZ_ASSERT(!mActorDestroyed); + + mActorDestroyed = true; + + if (!mFinishedOrAborted) { + mAborted = true; + + mForceAborted = true; + + MaybeFinishOrAbort(); + } +} + +bool +FileHandle::RecvDeleteMe() +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!IsActorDestroyed()); + + return PBackgroundFileHandleParent::Send__delete__(this); +} + +bool +FileHandle::RecvFinish() +{ + AssertIsOnBackgroundThread(); + + if (NS_WARN_IF(mFinishOrAbortReceived)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + mFinishOrAbortReceived = true; + + MaybeFinishOrAbort(); + return true; +} + +bool +FileHandle::RecvAbort() +{ + AssertIsOnBackgroundThread(); + + if (NS_WARN_IF(mFinishOrAbortReceived)) { + ASSERT_UNLESS_FUZZING(); + return false; + } + + mFinishOrAbortReceived = true; + + Abort(/* aForce */ false); + return true; +} + +PBackgroundFileRequestParent* +FileHandle::AllocPBackgroundFileRequestParent(const FileRequestParams& aParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aParams.type() != FileRequestParams::T__None); + +#ifdef DEBUG + // Always verify parameters in DEBUG builds! + bool trustParams = false; +#else + PBackgroundParent* backgroundActor = GetBackgroundParent(); + MOZ_ASSERT(backgroundActor); + + bool trustParams = !BackgroundParent::IsOtherProcessActor(backgroundActor); +#endif + + if (NS_WARN_IF(!trustParams && !VerifyRequestParams(aParams))) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + if (NS_WARN_IF(mFinishOrAbortReceived)) { + ASSERT_UNLESS_FUZZING(); + return nullptr; + } + + nsRefPtr<NormalFileHandleOp> actor; + + switch (aParams.type()) { + case FileRequestParams::TFileRequestGetMetadataParams: + actor = new GetMetadataOp(this, aParams); + break; + + case FileRequestParams::TFileRequestReadParams: + actor = new ReadOp(this, aParams); + break; + + case FileRequestParams::TFileRequestWriteParams: + actor = new WriteOp(this, aParams); + break; + + case FileRequestParams::TFileRequestTruncateParams: + actor = new TruncateOp(this, aParams); + break; + + case FileRequestParams::TFileRequestFlushParams: + actor = new FlushOp(this, aParams); + break; + + case FileRequestParams::TFileRequestGetFileParams: + actor = new GetFileOp(this, aParams); + break; + + default: + MOZ_CRASH("Should never get here!"); + } + + MOZ_ASSERT(actor); + + // Transfer ownership to IPDL. + return actor.forget().take(); +} + +bool +FileHandle::RecvPBackgroundFileRequestConstructor( + PBackgroundFileRequestParent* aActor, + const FileRequestParams& aParams) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(aParams.type() != FileRequestParams::T__None); + + auto* op = static_cast<NormalFileHandleOp*>(aActor); + + if (NS_WARN_IF(!op->Init(this))) { + op->Cleanup(); + return false; + } + + op->Enqueue(); + return true; +} + +bool +FileHandle::DeallocPBackgroundFileRequestParent( + PBackgroundFileRequestParent* aActor) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(aActor); + + // Transfer ownership back from IPDL. + nsRefPtr<NormalFileHandleOp> actor = + dont_AddRef(static_cast<NormalFileHandleOp*>(aActor)); + return true; +} + +/******************************************************************************* + * Local class implementations + ******************************************************************************/ + +void +FileHandleOp::Enqueue() +{ + AssertIsOnOwningThread(); + + FileHandleThreadPool* fileHandleThreadPool = + GetFileHandleThreadPoolFor(mFileHandle->Storage()); + MOZ_ASSERT(fileHandleThreadPool); + + fileHandleThreadPool->Enqueue(mFileHandle, this, false); + + mFileHandle->NoteActiveRequest(); +} + +void +FileHandle:: +FinishOp::RunOnThreadPool() +{ + AssertIsOnThreadPool(); + MOZ_ASSERT(mFileHandle); + + nsCOMPtr<nsISupports>& stream = mFileHandle->mStream; + + if (!stream) { + return; + } + + nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(stream); + MOZ_ASSERT(inputStream); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(inputStream->Close())); + + stream = nullptr; +} + +void +FileHandle:: +FinishOp::RunOnOwningThread() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mFileHandle); + + mFileHandle->SendCompleteNotification(mAborted); + + mFileHandle->GetMutableFile()->UnregisterFileHandle(mFileHandle); + + mFileHandle = nullptr; +} + +NormalFileHandleOp::~NormalFileHandleOp() +{ + MOZ_ASSERT(!mFileHandle, + "NormalFileHandleOp::Cleanup() was not called by a subclass!"); +} + +bool +NormalFileHandleOp::Init(FileHandle* aFileHandle) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aFileHandle); + + nsresult rv = aFileHandle->GetOrCreateStream(getter_AddRefs(mFileStream)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + return true; +} + +void +NormalFileHandleOp::Cleanup() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mFileHandle); + MOZ_ASSERT_IF(!IsActorDestroyed(), mResponseSent); + + mFileHandle = nullptr; +} + +nsresult +NormalFileHandleOp::SendSuccessResult() +{ + AssertIsOnOwningThread(); + + if (!IsActorDestroyed()) { + FileRequestResponse response; + GetResponse(response); + + MOZ_ASSERT(response.type() != FileRequestResponse::T__None); + + if (response.type() == FileRequestResponse::Tnsresult) { + MOZ_ASSERT(NS_FAILED(response.get_nsresult())); + + return response.get_nsresult(); + } + + if (NS_WARN_IF(!PBackgroundFileRequestParent::Send__delete__(this, + response))) { + return NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } + } + + DEBUGONLY(mResponseSent = true;) + + return NS_OK; +} + +bool +NormalFileHandleOp::SendFailureResult(nsresult aResultCode) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(NS_FAILED(aResultCode)); + + bool result = false; + + if (!IsActorDestroyed()) { + result = + PBackgroundFileRequestParent::Send__delete__(this, aResultCode); + } + + DEBUGONLY(mResponseSent = true;) + + return result; +} + +void +NormalFileHandleOp::RunOnThreadPool() +{ + AssertIsOnThreadPool(); + MOZ_ASSERT(mFileHandle); + MOZ_ASSERT(NS_SUCCEEDED(mResultCode)); + + // There are several cases where we don't actually have to to any work here. + + if (mFileHandleIsAborted) { + // This transaction is already set to be aborted. + mResultCode = NS_ERROR_DOM_FILEHANDLE_ABORT_ERR; + } else if (mFileHandle->IsInvalidatedOnAnyThread()) { + // This file handle is being invalidated. + mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } else if (!OperationMayProceed()) { + // The operation was canceled in some way, likely because the child process + // has crashed. + mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } else { + nsresult rv = DoFileWork(mFileHandle); + if (NS_FAILED(rv)) { + mResultCode = rv; + } + } +} + +void +NormalFileHandleOp::RunOnOwningThread() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mFileHandle); + + if (NS_WARN_IF(IsActorDestroyed())) { + // Don't send any notifications if the actor was destroyed already. + if (NS_SUCCEEDED(mResultCode)) { + mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } + } else { + if (mFileHandle->IsInvalidated()) { + mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + } else if (mFileHandle->IsAborted()) { + // Aborted file handles always see their requests fail with ABORT_ERR, + // even if the request succeeded or failed with another error. + mResultCode = NS_ERROR_DOM_FILEHANDLE_ABORT_ERR; + } else if (NS_SUCCEEDED(mResultCode)) { + // This may release the IPDL reference. + mResultCode = SendSuccessResult(); + } + + if (NS_FAILED(mResultCode)) { + // This should definitely release the IPDL reference. + if (!SendFailureResult(mResultCode)) { + // Abort the file handle. + mFileHandle->Abort(/* aForce */ false); + } + } + } + + mFileHandle->NoteFinishedRequest(); + + Cleanup(); +} + +void +NormalFileHandleOp::ActorDestroy(ActorDestroyReason aWhy) +{ + AssertIsOnOwningThread(); + + NoteActorDestroyed(); +} + +nsresult +CopyFileHandleOp::DoFileWork(FileHandle* aFileHandle) +{ + AssertIsOnThreadPool(); + + nsCOMPtr<nsIInputStream> inputStream; + nsCOMPtr<nsIOutputStream> outputStream; + + if (mRead) { + inputStream = do_QueryInterface(mFileStream); + outputStream = do_QueryInterface(mBufferStream); + } else { + inputStream = do_QueryInterface(mBufferStream); + outputStream = do_QueryInterface(mFileStream); + } + + MOZ_ASSERT(inputStream); + MOZ_ASSERT(outputStream); + + nsCOMPtr<nsISeekableStream> seekableStream = + do_QueryInterface(mFileStream); + + nsresult rv; + + if (seekableStream) { + if (mOffset == UINT64_MAX) { + rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_END, 0); + } + else { + rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, mOffset); + } + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + mOffset = 0; + + do { + char copyBuffer[kStreamCopyBlockSize]; + + uint64_t max = mSize - mOffset; + if (max == 0) { + break; + } + + uint32_t count = sizeof(copyBuffer); + if (count > max) { + count = max; + } + + uint32_t numRead; + rv = inputStream->Read(copyBuffer, count, &numRead); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!numRead) { + break; + } + + uint32_t numWrite; + rv = outputStream->Write(copyBuffer, numRead, &numWrite); + if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) { + rv = NS_ERROR_DOM_FILEHANDLE_QUOTA_ERR; + } + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(numWrite != numRead)) { + return NS_ERROR_FAILURE; + } + + mOffset += numWrite; + + nsCOMPtr<nsIRunnable> runnable = + new ProgressRunnable(this, mOffset, mSize); + + mOwningThread->Dispatch(runnable, NS_DISPATCH_NORMAL); + } while (true); + + MOZ_ASSERT(mOffset == mSize); + + if (mRead) { + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(outputStream->Close())); + } else { + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(inputStream->Close())); + } + + return NS_OK; +} + +void +CopyFileHandleOp::Cleanup() +{ + AssertIsOnOwningThread(); + + mBufferStream = nullptr; + + NormalFileHandleOp::Cleanup(); +} + +NS_IMETHODIMP +CopyFileHandleOp:: +ProgressRunnable::Run() +{ + AssertIsOnBackgroundThread(); + + unused << mCopyFileHandleOp->SendProgress(mProgress, mProgressMax); + + mCopyFileHandleOp = nullptr; + + return NS_OK; +} + +GetMetadataOp::GetMetadataOp(FileHandle* aFileHandle, + const FileRequestParams& aParams) + : NormalFileHandleOp(aFileHandle) + , mParams(aParams.get_FileRequestGetMetadataParams()) +{ + MOZ_ASSERT(aParams.type() == + FileRequestParams::TFileRequestGetMetadataParams); +} + +nsresult +GetMetadataOp::DoFileWork(FileHandle* aFileHandle) +{ + AssertIsOnThreadPool(); + + nsresult rv; + + if (mFileHandle->Mode() == FileMode::Readwrite) { + // Force a flush (so all pending writes are flushed to the disk and file + // metadata is updated too). + + nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(mFileStream); + MOZ_ASSERT(ostream); + + rv = ostream->Flush(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + nsCOMPtr<nsIFileMetadata> metadata = do_QueryInterface(mFileStream); + MOZ_ASSERT(metadata); + + if (mParams.size()) { + int64_t size; + rv = metadata->GetSize(&size); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(size < 0)) { + return NS_ERROR_FAILURE; + } + + mMetadata.size() = uint64_t(size); + } else { + mMetadata.size() = void_t(); + } + + if (mParams.lastModified()) { + int64_t lastModified; + rv = metadata->GetLastModified(&lastModified); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mMetadata.lastModified() = lastModified; + } else { + mMetadata.lastModified() = void_t(); + } + + return NS_OK; +} + +void +GetMetadataOp::GetResponse(FileRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + + aResponse = FileRequestGetMetadataResponse(mMetadata); +} + +ReadOp::ReadOp(FileHandle* aFileHandle, + const FileRequestParams& aParams) + : CopyFileHandleOp(aFileHandle) + , mParams(aParams.get_FileRequestReadParams()) +{ + MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestReadParams); +} + +bool +ReadOp::Init(FileHandle* aFileHandle) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aFileHandle); + + if (NS_WARN_IF(!NormalFileHandleOp::Init(aFileHandle))) { + return false; + } + + mBufferStream = MemoryOutputStream::Create(mParams.size()); + if (NS_WARN_IF(!mBufferStream)) { + return false; + } + + mOffset = mParams.offset(); + mSize = mParams.size(); + mRead = true; + + return true; +} + +void +ReadOp::GetResponse(FileRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + + auto* stream = static_cast<MemoryOutputStream*>(mBufferStream.get()); + + aResponse = FileRequestReadResponse(stream->Data()); +} + +// static +already_AddRefed<ReadOp::MemoryOutputStream> +ReadOp:: +MemoryOutputStream::Create(uint64_t aSize) +{ + MOZ_ASSERT(aSize, "Passed zero size!"); + + if (NS_WARN_IF(aSize > UINT32_MAX)) { + return nullptr; + } + + nsRefPtr<MemoryOutputStream> stream = new MemoryOutputStream(); + + char* dummy; + uint32_t length = stream->mData.GetMutableData(&dummy, aSize, fallible); + if (NS_WARN_IF(length != aSize)) { + return nullptr; + } + + return stream.forget(); +} + +NS_IMPL_ISUPPORTS(ReadOp::MemoryOutputStream, nsIOutputStream) + +NS_IMETHODIMP +ReadOp:: +MemoryOutputStream::Close() +{ + mData.Truncate(mOffset); + return NS_OK; +} + +NS_IMETHODIMP +ReadOp:: +MemoryOutputStream::Write(const char* aBuf, uint32_t aCount, uint32_t* _retval) +{ + return WriteSegments(NS_CopySegmentToBuffer, (char*)aBuf, aCount, _retval); +} + +NS_IMETHODIMP +ReadOp:: +MemoryOutputStream::Flush() +{ + return NS_OK; +} + +NS_IMETHODIMP +ReadOp:: +MemoryOutputStream::WriteFrom(nsIInputStream* aFromStream, uint32_t aCount, + uint32_t* _retval) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +ReadOp:: +MemoryOutputStream::WriteSegments(nsReadSegmentFun aReader, void* aClosure, + uint32_t aCount, uint32_t* _retval) +{ + NS_ASSERTION(mData.Length() >= mOffset, "Bad stream state!"); + + uint32_t maxCount = mData.Length() - mOffset; + if (maxCount == 0) { + *_retval = 0; + return NS_OK; + } + + if (aCount > maxCount) { + aCount = maxCount; + } + + nsresult rv = aReader(this, aClosure, mData.BeginWriting() + mOffset, 0, + aCount, _retval); + if (NS_SUCCEEDED(rv)) { + NS_ASSERTION(*_retval <= aCount, + "Reader should not read more than we asked it to read!"); + mOffset += *_retval; + } + + return NS_OK; +} + +NS_IMETHODIMP +ReadOp:: +MemoryOutputStream::IsNonBlocking(bool* _retval) +{ + *_retval = false; + return NS_OK; +} + +WriteOp::WriteOp(FileHandle* aFileHandle, + const FileRequestParams& aParams) + : CopyFileHandleOp(aFileHandle) + , mParams(aParams.get_FileRequestWriteParams()) +{ + MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestWriteParams); +} + +bool +WriteOp::Init(FileHandle* aFileHandle) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aFileHandle); + + if (NS_WARN_IF(!NormalFileHandleOp::Init(aFileHandle))) { + return false; + } + + nsCOMPtr<nsIInputStream> inputStream; + + const FileRequestData& data = mParams.data(); + switch (data.type()) { + case FileRequestData::TFileRequestStringData: { + const FileRequestStringData& stringData = + data.get_FileRequestStringData(); + + const nsCString& string = stringData.string(); + + nsresult rv = + NS_NewCStringInputStream(getter_AddRefs(inputStream), string); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + break; + } + case FileRequestData::TFileRequestBlobData: { + const FileRequestBlobData& blobData = + data.get_FileRequestBlobData(); + + auto blobActor = static_cast<BlobParent*>(blobData.blobParent()); + + nsRefPtr<BlobImpl> blobImpl = blobActor->GetBlobImpl(); + + ErrorResult rv; + blobImpl->GetInternalStream(getter_AddRefs(inputStream), rv); + if (NS_WARN_IF(rv.Failed())) { + return false; + } + + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + mBufferStream = inputStream; + mOffset = mParams.offset(); + mSize = mParams.dataLength(); + mRead = false; + + return true; +} + +void +WriteOp::GetResponse(FileRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + aResponse = FileRequestWriteResponse(); +} + +TruncateOp::TruncateOp(FileHandle* aFileHandle, + const FileRequestParams& aParams) + : NormalFileHandleOp(aFileHandle) + , mParams(aParams.get_FileRequestTruncateParams()) +{ + MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestTruncateParams); +} + +nsresult +TruncateOp::DoFileWork(FileHandle* aFileHandle) +{ + AssertIsOnThreadPool(); + + nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(mFileStream); + MOZ_ASSERT(sstream); + + nsresult rv = sstream->Seek(nsISeekableStream::NS_SEEK_SET, mParams.offset()); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = sstream->SetEOF(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +void +TruncateOp::GetResponse(FileRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + aResponse = FileRequestTruncateResponse(); +} + +FlushOp::FlushOp(FileHandle* aFileHandle, + const FileRequestParams& aParams) + : NormalFileHandleOp(aFileHandle) + , mParams(aParams.get_FileRequestFlushParams()) +{ + MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestFlushParams); +} + +nsresult +FlushOp::DoFileWork(FileHandle* aFileHandle) +{ + AssertIsOnThreadPool(); + + nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(mFileStream); + MOZ_ASSERT(ostream); + + nsresult rv = ostream->Flush(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +void +FlushOp::GetResponse(FileRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + aResponse = FileRequestFlushResponse(); +} + +GetFileOp::GetFileOp(FileHandle* aFileHandle, + const FileRequestParams& aParams) + : GetMetadataOp(aFileHandle, + FileRequestGetMetadataParams(true, true)) + , mBackgroundParent(aFileHandle->GetBackgroundParent()) +{ + MOZ_ASSERT(aParams.type() == FileRequestParams::TFileRequestGetFileParams); + MOZ_ASSERT(mBackgroundParent); +} + +void +GetFileOp::GetResponse(FileRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + + nsRefPtr<BlobImpl> blobImpl = mFileHandle->GetMutableFile()->CreateBlobImpl(); + MOZ_ASSERT(blobImpl); + + PBlobParent* actor = + BackgroundParent::GetOrCreateActorForBlobImpl(mBackgroundParent, blobImpl); + if (NS_WARN_IF(!actor)) { + // This can only fail if the child has crashed. + aResponse = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; + return; + } + + FileRequestGetFileResponse response; + response.fileParent() = actor; + response.metadata() = mMetadata; + + aResponse = response; +} + +} // namespace dom +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/dom/filehandle/ActorsParent.h @@ -0,0 +1,221 @@ +/* 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_filehandle_ActorsParent_h +#define mozilla_dom_filehandle_ActorsParent_h + +#include "mozilla/dom/FileHandleStorage.h" +#include "mozilla/dom/PBackgroundMutableFileParent.h" +#include "mozilla/ipc/BackgroundParent.h" +#include "nsAutoPtr.h" +#include "nsClassHashtable.h" +#include "nsCOMPtr.h" +#include "nsHashKeys.h" +#include "nsString.h" +#include "nsTHashtable.h" + +template <class> struct already_AddRefed; +class nsIFile; +class nsIRunnable; +class nsIThreadPool; +template <class> class nsTArray; + +namespace mozilla { + +namespace ipc { + +class PBackgroundParent; + +} // namespace ipc + +namespace dom { + +class BlobImpl; +class FileHandle; +class FileHandleOp; + +class FileHandleThreadPool final +{ + class FileHandleQueue; + struct DelayedEnqueueInfo; + class DirectoryInfo; + struct StoragesCompleteCallback; + + nsCOMPtr<nsIThreadPool> mThreadPool; + nsCOMPtr<nsIEventTarget> mOwningThread; + + nsClassHashtable<nsCStringHashKey, DirectoryInfo> mDirectoryInfos; + + nsTArray<nsAutoPtr<StoragesCompleteCallback>> mCompleteCallbacks; + + bool mShutdownRequested; + bool mShutdownComplete; + +public: + static already_AddRefed<FileHandleThreadPool> + Create(); + +#ifdef DEBUG + void + AssertIsOnOwningThread() const; + + nsIEventTarget* + GetThreadPoolEventTarget() const; +#else + void + AssertIsOnOwningThread() const + { } +#endif + + void + Enqueue(FileHandle* aFileHandle, + FileHandleOp* aFileHandleOp, + bool aFinish); + + NS_INLINE_DECL_REFCOUNTING(FileHandleThreadPool) + + void + WaitForDirectoriesToComplete(nsTArray<nsCString>&& aDirectoryIds, + nsIRunnable* aCallback); + + void + Shutdown(); + +private: + FileHandleThreadPool(); + + // Reference counted. + ~FileHandleThreadPool(); + + nsresult + Init(); + + void + Cleanup(); + + void + FinishFileHandle(FileHandle* aFileHandle); + + bool + MaybeFireCallback(StoragesCompleteCallback* aCallback); +}; + +class BackgroundMutableFileParentBase + : public PBackgroundMutableFileParent +{ + nsTHashtable<nsPtrHashKey<FileHandle>> mFileHandles; + nsCString mDirectoryId; + nsString mFileName; + FileHandleStorage mStorage; + bool mInvalidated; + bool mActorWasAlive; + bool mActorDestroyed; + +protected: + nsCOMPtr<nsIFile> mFile; + +public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BackgroundMutableFileParentBase) + + void + Invalidate(); + + FileHandleStorage + Storage() const + { + return mStorage; + } + + const nsCString& + DirectoryId() const + { + return mDirectoryId; + } + + const nsString& + FileName() const + { + return mFileName; + } + + bool + RegisterFileHandle(FileHandle* aFileHandle); + + void + UnregisterFileHandle(FileHandle* aFileHandle); + + void + SetActorAlive(); + + bool + IsActorDestroyed() const + { + mozilla::ipc::AssertIsOnBackgroundThread(); + + return mActorWasAlive && mActorDestroyed; + } + + bool + IsInvalidated() const + { + mozilla::ipc::AssertIsOnBackgroundThread(); + + return mInvalidated; + } + + virtual void + NoteActiveState() + { } + + virtual void + NoteInactiveState() + { } + + virtual mozilla::ipc::PBackgroundParent* + GetBackgroundParent() const = 0; + + virtual already_AddRefed<nsISupports> + CreateStream(bool aReadOnly); + + virtual already_AddRefed<BlobImpl> + CreateBlobImpl() + { + return nullptr; + } + +protected: + BackgroundMutableFileParentBase(FileHandleStorage aStorage, + const nsACString& aDirectoryId, + const nsAString& aFileName, + nsIFile* aFile); + + // Reference counted. + ~BackgroundMutableFileParentBase(); + + // IPDL methods are only called by IPDL. + virtual void + ActorDestroy(ActorDestroyReason aWhy) override; + + virtual PBackgroundFileHandleParent* + AllocPBackgroundFileHandleParent(const FileMode& aMode) override; + + virtual bool + RecvPBackgroundFileHandleConstructor(PBackgroundFileHandleParent* aActor, + const FileMode& aMode) override; + + virtual bool + DeallocPBackgroundFileHandleParent(PBackgroundFileHandleParent* aActor) + override; + + virtual bool + RecvDeleteMe() override; + + virtual bool + RecvGetFileId(int64_t* aFileId) override; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_filehandle_ActorsParent_h
deleted file mode 100644 --- a/dom/filehandle/AsyncHelper.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* -*- 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 "AsyncHelper.h" - -#include "FileService.h" -#include "MainThreadUtils.h" -#include "nsDebug.h" -#include "nsIEventTarget.h" -#include "nsIRequestObserver.h" -#include "nsNetUtil.h" - -namespace mozilla { -namespace dom { - -NS_IMPL_ISUPPORTS(AsyncHelper, nsIRunnable, nsIRequest) - -nsresult -AsyncHelper::AsyncWork(nsIRequestObserver* aObserver, nsISupports* aCtxt) -{ - nsresult rv; - - if (aObserver) { - // build proxy for observer events - rv = NS_NewRequestObserverProxy(getter_AddRefs(mObserver), aObserver, aCtxt); - NS_ENSURE_SUCCESS(rv, rv); - } - - FileService* service = FileService::GetOrCreate(); - NS_ENSURE_TRUE(service, NS_ERROR_FAILURE); - - nsIEventTarget* target = service->ThreadPoolTarget(); - - rv = target->Dispatch(this, NS_DISPATCH_NORMAL); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -NS_IMETHODIMP -AsyncHelper::Run() -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - - if (mObserver) { - mObserver->OnStartRequest(this, nullptr); - } - - mStatus = DoStreamWork(mStream); - - if (mObserver) { - mObserver->OnStopRequest(this, nullptr, mStatus); - } - - return NS_OK; -} - -NS_IMETHODIMP -AsyncHelper::GetName(nsACString& aName) -{ - NS_WARNING("Shouldn't be called!"); - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -AsyncHelper::IsPending(bool* _retval) -{ - NS_WARNING("Shouldn't be called!"); - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -AsyncHelper::GetStatus(nsresult* aStatus) -{ - *aStatus = mStatus; - return NS_OK; -} - -NS_IMETHODIMP -AsyncHelper::Cancel(nsresult aStatus) -{ - return NS_OK; -} - -NS_IMETHODIMP -AsyncHelper::Suspend() -{ - NS_WARNING("Shouldn't be called!"); - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -AsyncHelper::Resume() -{ - NS_WARNING("Shouldn't be called!"); - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -AsyncHelper::GetLoadGroup(nsILoadGroup** aLoadGroup) -{ - NS_WARNING("Shouldn't be called!"); - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -AsyncHelper::SetLoadGroup(nsILoadGroup* aLoadGroup) -{ - NS_WARNING("Shouldn't be called!"); - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -AsyncHelper::GetLoadFlags(nsLoadFlags* aLoadFlags) -{ - NS_WARNING("Shouldn't be called!"); - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -AsyncHelper::SetLoadFlags(nsLoadFlags aLoadFlags) -{ - NS_WARNING("Shouldn't be called!"); - return NS_ERROR_NOT_IMPLEMENTED; -} - -} // namespace dom -} // namespace mozilla
deleted file mode 100644 --- a/dom/filehandle/AsyncHelper.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -*- 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_AsyncHelper_h -#define mozilla_dom_AsyncHelper_h - -#include "nsCOMPtr.h" -#include "nsIRequest.h" -#include "nsIRunnable.h" - -class nsIRequestObserver; - -namespace mozilla { -namespace dom { - -/** - * Must be subclassed. The subclass must implement DoStreamWork. - * Async operations that are not supported in necko (truncate, flush, etc.) - * should use this helper. Call AsyncWork to invoke the operation. - */ -class AsyncHelper : public nsIRunnable, - public nsIRequest -{ -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIRUNNABLE - NS_DECL_NSIREQUEST - - nsresult - AsyncWork(nsIRequestObserver* aObserver, nsISupports* aCtxt); - -protected: - explicit AsyncHelper(nsISupports* aStream) - : mStream(aStream), - mStatus(NS_OK) - { } - - virtual ~AsyncHelper() - { } - - virtual nsresult - DoStreamWork(nsISupports* aStream) = 0; - -private: - nsCOMPtr<nsISupports> mStream; - nsCOMPtr<nsIRequestObserver> mObserver; - - nsresult mStatus; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_AsyncHelper_h
rename from dom/filehandle/FileHandle.cpp rename to dom/filehandle/FileHandleBase.cpp --- a/dom/filehandle/FileHandle.cpp +++ b/dom/filehandle/FileHandleBase.cpp @@ -1,360 +1,237 @@ /* -*- 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 "FileHandle.h" +#include "FileHandleBase.h" -#include "AsyncHelper.h" -#include "FileHelper.h" -#include "FileRequest.h" -#include "FileService.h" -#include "FileStreamWrappers.h" -#include "MemoryStreams.h" -#include "mozilla/dom/EncodingUtils.h" +#include "ActorsChild.h" +#include "BackgroundChildImpl.h" +#include "FileRequestBase.h" +#include "mozilla/Assertions.h" +#include "mozilla/ErrorResult.h" #include "mozilla/dom/File.h" -#include "MutableFile.h" -#include "nsContentUtils.h" +#include "mozilla/dom/PBackgroundFileHandle.h" +#include "mozilla/dom/UnionConversions.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "MutableFileBase.h" +#include "nsCOMPtr.h" #include "nsDebug.h" #include "nsError.h" -#include "nsIEventTarget.h" -#include "nsISeekableStream.h" -#include "nsNetUtil.h" -#include "nsIAsyncStreamCopier.h" #include "nsString.h" -#include "nsStringStream.h" -#include "nsThreadUtils.h" -#include "xpcpublic.h" - -#define STREAM_COPY_BLOCK_SIZE 32768 namespace mozilla { namespace dom { -namespace { - -class ReadHelper : public FileHelper -{ -public: - ReadHelper(FileHandleBase* aFileHandle, - FileRequestBase* aFileRequest, - uint64_t aLocation, - uint64_t aSize) - : FileHelper(aFileHandle, aFileRequest), - mLocation(aLocation), mSize(aSize) - { - MOZ_ASSERT(mSize, "Passed zero size!"); - } - - nsresult - Init(); - - nsresult - DoAsyncRun(nsISupports* aStream) override; - - nsresult - GetSuccessResult(JSContext* aCx, - JS::MutableHandle<JS::Value> aVal) override; - -protected: - uint64_t mLocation; - uint64_t mSize; - - nsRefPtr<MemoryOutputStream> mStream; -}; - -class ReadTextHelper : public ReadHelper -{ -public: - ReadTextHelper(FileHandleBase* aFileHandle, - FileRequestBase* aFileRequest, - uint64_t aLocation, - uint64_t aSize, - const nsAString& aEncoding) - : ReadHelper(aFileHandle, aFileRequest, aLocation, aSize), - mEncoding(aEncoding) - { } - - nsresult - GetSuccessResult(JSContext* aCx, - JS::MutableHandle<JS::Value> aVal) override; - -private: - nsString mEncoding; -}; - -class WriteHelper : public FileHelper -{ -public: - WriteHelper(FileHandleBase* aFileHandle, - FileRequestBase* aFileRequest, - uint64_t aLocation, - nsIInputStream* aStream, - uint64_t aLength) - : FileHelper(aFileHandle, aFileRequest), - mLocation(aLocation), mStream(aStream), mLength(aLength) - { - MOZ_ASSERT(mLength, "Passed zero length!"); - } - - nsresult - DoAsyncRun(nsISupports* aStream); - -private: - uint64_t mLocation; - nsCOMPtr<nsIInputStream> mStream; - uint64_t mLength; -}; - -class TruncateHelper : public FileHelper -{ -public: - TruncateHelper(FileHandleBase* aFileHandle, - FileRequestBase* aFileRequest, - uint64_t aOffset) - : FileHelper(aFileHandle, aFileRequest), - mOffset(aOffset) - { } +using namespace mozilla::ipc; - nsresult - DoAsyncRun(nsISupports* aStream); - -private: - class AsyncTruncator : public AsyncHelper - { - public: - AsyncTruncator(nsISupports* aStream, int64_t aOffset) - : AsyncHelper(aStream), - mOffset(aOffset) - { } - protected: - nsresult - DoStreamWork(nsISupports* aStream) override; - - uint64_t mOffset; - }; - - uint64_t mOffset; -}; - -class FlushHelper : public FileHelper +FileHandleBase::FileHandleBase(DEBUGONLY(PRThread* aOwningThread,) + FileMode aMode) + : RefCountedThreadObject(DEBUGONLY(aOwningThread)) + , mBackgroundActor(nullptr) + , mLocation(0) + , mPendingRequestCount(0) + , mReadyState(INITIAL) + , mMode(aMode) + , mAborted(false) + , mCreating(false) + DEBUGONLY(, mSentFinishOrAbort(false)) + DEBUGONLY(, mFiredCompleteOrAbort(false)) { -public: - FlushHelper(FileHandleBase* aFileHandle, - FileRequestBase* aFileRequest) - : FileHelper(aFileHandle, aFileRequest) - { } - - nsresult - DoAsyncRun(nsISupports* aStream); - -private: - class AsyncFlusher : public AsyncHelper - { - public: - explicit AsyncFlusher(nsISupports* aStream) - : AsyncHelper(aStream) - { } - protected: - nsresult - DoStreamWork(nsISupports* aStream) override; - }; -}; - -class OpenStreamHelper : public FileHelper -{ -public: - OpenStreamHelper(FileHandleBase* aFileHandle, - bool aWholeFile, - uint64_t aStart, - uint64_t aLength) - : FileHelper(aFileHandle, nullptr), - mWholeFile(aWholeFile), mStart(aStart), mLength(aLength) - { } - - nsresult - DoAsyncRun(nsISupports* aStream); - - nsCOMPtr<nsIInputStream>& - Result() - { - return mStream; - } - -private: - bool mWholeFile; - uint64_t mStart; - uint64_t mLength; - - nsCOMPtr<nsIInputStream> mStream; -}; - -} // namespace - -FileHandleBase::FileHandleBase(FileMode aMode, - RequestMode aRequestMode) -: mReadyState(INITIAL), - mMode(aMode), - mRequestMode(aRequestMode), - mLocation(0), - mPendingRequests(0), - mAborted(false), - mCreating(false) -{ - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); } FileHandleBase::~FileHandleBase() { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); + MOZ_ASSERT(!mPendingRequestCount); + MOZ_ASSERT(!mCreating); + MOZ_ASSERT(mSentFinishOrAbort); + MOZ_ASSERT_IF(mBackgroundActor, mFiredCompleteOrAbort); + + if (mBackgroundActor) { + mBackgroundActor->SendDeleteMeInternal(); + + MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!"); + } +} + +// static +FileHandleBase* +FileHandleBase::GetCurrent() +{ + MOZ_ASSERT(BackgroundChild::GetForCurrentThread()); + + BackgroundChildImpl::ThreadLocal* threadLocal = + BackgroundChildImpl::GetThreadLocalForCurrentThread(); + MOZ_ASSERT(threadLocal); + + return threadLocal->mCurrentFileHandle; +} + +void +FileHandleBase::SetBackgroundActor(BackgroundFileHandleChild* aActor) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aActor); + MOZ_ASSERT(!mBackgroundActor); + + mBackgroundActor = aActor; +} + +void +FileHandleBase::StartRequest(FileRequestBase* aFileRequest, + const FileRequestParams& aParams) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(aFileRequest); + MOZ_ASSERT(aParams.type() != FileRequestParams::T__None); + + BackgroundFileRequestChild* actor = + new BackgroundFileRequestChild(DEBUGONLY(mBackgroundActor->OwningThread(),) + aFileRequest); + + mBackgroundActor->SendPBackgroundFileRequestConstructor(actor, aParams); + + // Balanced in BackgroundFileRequestChild::Recv__delete__(). + OnNewRequest(); } void FileHandleBase::OnNewRequest() { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); - if (!mPendingRequests) { - MOZ_ASSERT(mReadyState == INITIAL, "Reusing a file handle!"); + AssertIsOnOwningThread(); + + if (!mPendingRequestCount) { + MOZ_ASSERT(mReadyState == INITIAL); mReadyState = LOADING; } - ++mPendingRequests; + + ++mPendingRequestCount; } void -FileHandleBase::OnRequestFinished() +FileHandleBase::OnRequestFinished(bool aActorDestroyedNormally) { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); - MOZ_ASSERT(mPendingRequests, "Mismatched calls!"); - --mPendingRequests; - if (!mPendingRequests) { - MOZ_ASSERT(mAborted || mReadyState == LOADING, "Bad state!"); - mReadyState = FileHandleBase::FINISHING; - Finish(); - } -} + AssertIsOnOwningThread(); + MOZ_ASSERT(mPendingRequestCount); -nsresult -FileHandleBase::CreateParallelStream(nsISupports** aStream) -{ - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); - - MutableFileBase* mutableFile = MutableFile(); + --mPendingRequestCount; - if (mutableFile->IsInvalid()) { - return NS_ERROR_NOT_AVAILABLE; - } - - nsCOMPtr<nsISupports> stream = - mutableFile->CreateStream(mMode == FileMode::Readonly); - NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE); + if (!mPendingRequestCount && !MutableFile()->IsInvalidated()) { + mReadyState = FINISHING; - mParallelStreams.AppendElement(stream); - - stream.forget(aStream); - return NS_OK; -} - -nsresult -FileHandleBase::GetOrCreateStream(nsISupports** aStream) -{ - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); - - MutableFileBase* mutableFile = MutableFile(); - - if (mutableFile->IsInvalid()) { - return NS_ERROR_NOT_AVAILABLE; + if (aActorDestroyedNormally) { + if (!mAborted) { + SendFinish(); + } else { + SendAbort(); + } + } else { + // Don't try to send any more messages to the parent if the request actor + // was killed. +#ifdef DEBUG + MOZ_ASSERT(!mSentFinishOrAbort); + mSentFinishOrAbort = true; +#endif + } } - - if (!mStream) { - nsCOMPtr<nsISupports> stream = - mutableFile->CreateStream(mMode == FileMode::Readonly); - NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE); - - stream.swap(mStream); - } - - nsCOMPtr<nsISupports> stream(mStream); - stream.forget(aStream); - - return NS_OK; } bool FileHandleBase::IsOpen() const { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); // If we haven't started anything then we're open. if (mReadyState == INITIAL) { - MOZ_ASSERT(FileHelper::GetCurrentFileHandle() != this, - "This should be some other file handle (or null)!"); return true; } // If we've already started then we need to check to see if we still have the // mCreating flag set. If we do (i.e. we haven't returned to the event loop // from the time we were created) then we are open. Otherwise check the // currently running file handles to see if it's the same. We only allow other // requests to be made if this file handle is currently running. - if (mReadyState == LOADING) { - if (mCreating) { - return true; - } - - if (FileHelper::GetCurrentFileHandle() == this) { - return true; - } + if (mReadyState == LOADING && (mCreating || GetCurrent() == this)) { + return true; } return false; } +void +FileHandleBase::Abort() +{ + AssertIsOnOwningThread(); + + if (IsFinishingOrDone()) { + // Already started (and maybe finished) the finish or abort so there is + // nothing to do here. + return; + } + + const bool isInvalidated = MutableFile()->IsInvalidated(); + bool needToSendAbort = mReadyState == INITIAL && !isInvalidated; + +#ifdef DEBUG + if (isInvalidated) { + mSentFinishOrAbort = true; + } +#endif + + mAborted = true; + mReadyState = DONE; + + // Fire the abort event if there are no outstanding requests. Otherwise the + // abort event will be fired when all outstanding requests finish. + if (needToSendAbort) { + SendAbort(); + } +} + already_AddRefed<FileRequestBase> FileHandleBase::Read(uint64_t aSize, bool aHasEncoding, const nsAString& aEncoding, ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); // State and argument checking for read if (!CheckStateAndArgumentsForRead(aSize, aRv)) { return nullptr; } // Do nothing if the window is closed if (!CheckWindow()) { return nullptr; } - nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest(); + FileRequestReadParams params; + params.offset() = mLocation; + params.size() = aSize; - nsRefPtr<ReadHelper> helper; + nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest(); if (aHasEncoding) { - helper = new ReadTextHelper(this, fileRequest, mLocation, aSize, aEncoding); - } else { - helper = new ReadHelper(this, fileRequest, mLocation, aSize); + fileRequest->SetEncoding(aEncoding); } - if (NS_WARN_IF(NS_FAILED(helper->Init())) || - NS_WARN_IF(NS_FAILED(helper->Enqueue()))) { - aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); - return nullptr; - } + StartRequest(fileRequest, params); mLocation += aSize; return fileRequest.forget(); } already_AddRefed<FileRequestBase> FileHandleBase::Truncate(const Optional<uint64_t>& aSize, ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); // State checking for write if (!CheckStateForWrite(aRv)) { return nullptr; } // Getting location and additional state checking for truncate uint64_t location; @@ -370,137 +247,96 @@ FileHandleBase::Truncate(const Optional< location = mLocation; } // Do nothing if the window is closed if (!CheckWindow()) { return nullptr; } + FileRequestTruncateParams params; + params.offset() = location; + nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest(); - nsRefPtr<TruncateHelper> helper = - new TruncateHelper(this, fileRequest, location); - - if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) { - aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); - return nullptr; - } + StartRequest(fileRequest, params); if (aSize.WasPassed()) { mLocation = aSize.Value(); } return fileRequest.forget(); } already_AddRefed<FileRequestBase> FileHandleBase::Flush(ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); // State checking for write if (!CheckStateForWrite(aRv)) { return nullptr; } // Do nothing if the window is closed if (!CheckWindow()) { return nullptr; } + FileRequestFlushParams params; + nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest(); - nsRefPtr<FlushHelper> helper = new FlushHelper(this, fileRequest); - - if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) { - aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); - return nullptr; - } + StartRequest(fileRequest, params); return fileRequest.forget(); } void FileHandleBase::Abort(ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); // This method is special enough for not using generic state checking methods. - // We can't use IsOpen here since we need it to be possible to call Abort() - // even from outside of transaction callbacks. - if (mReadyState != FileHandleBase::INITIAL && - mReadyState != FileHandleBase::LOADING) { + if (IsFinishingOrDone()) { aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR); return; } - bool needToFinish = mReadyState == INITIAL; + Abort(); +} - mAborted = true; +void +FileHandleBase::HandleCompleteOrAbort(bool aAborted) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(!mFiredCompleteOrAbort); + mReadyState = DONE; - // Fire the abort event if there are no outstanding requests. Otherwise the - // abort event will be fired when all outstanding requests finish. - if (needToFinish) { - aRv = Finish(); - } + DEBUGONLY(mFiredCompleteOrAbort = true;) } void FileHandleBase::OnReturnToEventLoop() { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); // We're back at the event loop, no longer newborn. mCreating = false; - // Maybe set the readyState to DONE if there were no requests generated. + // Maybe finish if there were no requests generated. if (mReadyState == INITIAL) { mReadyState = DONE; - if (NS_FAILED(Finish())) { - NS_WARNING("Failed to finish!"); - } + SendFinish(); } } -nsresult -FileHandleBase::OpenInputStream(bool aWholeFile, uint64_t aStart, - uint64_t aLength, nsIInputStream** aResult) -{ - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); - MOZ_ASSERT(mRequestMode == PARALLEL, - "Don't call me in other than parallel mode!"); - - // Common state checking - ErrorResult error; - if (!CheckState(error)) { - return error.StealNSResult(); - } - - // Do nothing if the window is closed - if (!CheckWindow()) { - return NS_OK; - } - - nsRefPtr<OpenStreamHelper> helper = - new OpenStreamHelper(this, aWholeFile, aStart, aLength); - - nsresult rv = helper->Enqueue(); - NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); - - nsCOMPtr<nsIInputStream>& result = helper->Result(); - NS_ENSURE_TRUE(result, NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); - - result.forget(aResult); - return NS_OK; -} - bool FileHandleBase::CheckState(ErrorResult& aRv) { if (!IsOpen()) { aRv.Throw(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR); return false; } @@ -542,360 +378,259 @@ FileHandleBase::CheckStateForWrite(Error if (mMode != FileMode::Readwrite) { aRv.Throw(NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR); return false; } return true; } +bool +FileHandleBase::CheckStateForWriteOrAppend(bool aAppend, ErrorResult& aRv) +{ + // State checking for write + if (!CheckStateForWrite(aRv)) { + return false; + } + + // Additional state checking for write + if (!aAppend && mLocation == UINT64_MAX) { + aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR); + return false; + } + + return true; +} + already_AddRefed<FileRequestBase> -FileHandleBase::WriteInternal(nsIInputStream* aInputStream, - uint64_t aInputLength, bool aAppend, +FileHandleBase::WriteOrAppend( + const StringOrArrayBufferOrArrayBufferViewOrBlob& aValue, + bool aAppend, + ErrorResult& aRv) +{ + AssertIsOnOwningThread(); + + if (aValue.IsString()) { + return WriteOrAppend(aValue.GetAsString(), aAppend, aRv); + } + + if (aValue.IsArrayBuffer()) { + return WriteOrAppend(aValue.GetAsArrayBuffer(), aAppend, aRv); + } + + if (aValue.IsArrayBufferView()) { + return WriteOrAppend(aValue.GetAsArrayBufferView(), aAppend, aRv); + } + + MOZ_ASSERT(aValue.IsBlob()); + return WriteOrAppend(aValue.GetAsBlob(), aAppend, aRv); +} + +already_AddRefed<FileRequestBase> +FileHandleBase::WriteOrAppend(const nsAString& aValue, + bool aAppend, + ErrorResult& aRv) +{ + AssertIsOnOwningThread(); + + // State checking for write or append + if (!CheckStateForWriteOrAppend(aAppend, aRv)) { + return nullptr; + } + + NS_ConvertUTF16toUTF8 cstr(aValue); + + uint64_t dataLength = cstr.Length();; + if (!dataLength) { + return nullptr; + } + + FileRequestStringData stringData(cstr); + + // Do nothing if the window is closed + if (!CheckWindow()) { + return nullptr; + } + + return WriteInternal(stringData, dataLength, aAppend, aRv); +} + +already_AddRefed<FileRequestBase> +FileHandleBase::WriteOrAppend(const ArrayBuffer& aValue, + bool aAppend, ErrorResult& aRv) { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); + + // State checking for write or append + if (!CheckStateForWriteOrAppend(aAppend, aRv)) { + return nullptr; + } + + aValue.ComputeLengthAndData(); + + uint64_t dataLength = aValue.Length();; + if (!dataLength) { + return nullptr; + } + + const char* data = reinterpret_cast<const char*>(aValue.Data()); + + FileRequestStringData stringData; + if (NS_WARN_IF(!stringData.string().Assign(data, aValue.Length(), + fallible_t()))) { + aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); + return nullptr; + } + + // Do nothing if the window is closed + if (!CheckWindow()) { + return nullptr; + } + + return WriteInternal(stringData, dataLength, aAppend, aRv); +} + +already_AddRefed<FileRequestBase> +FileHandleBase::WriteOrAppend(const ArrayBufferView& aValue, + bool aAppend, + ErrorResult& aRv) +{ + AssertIsOnOwningThread(); + + // State checking for write or append + if (!CheckStateForWriteOrAppend(aAppend, aRv)) { + return nullptr; + } + + aValue.ComputeLengthAndData(); + + uint64_t dataLength = aValue.Length();; + if (!dataLength) { + return nullptr; + } + + const char* data = reinterpret_cast<const char*>(aValue.Data()); + + FileRequestStringData stringData; + if (NS_WARN_IF(!stringData.string().Assign(data, aValue.Length(), + fallible_t()))) { + aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); + return nullptr; + } + + // Do nothing if the window is closed + if (!CheckWindow()) { + return nullptr; + } + + return WriteInternal(stringData, dataLength, aAppend, aRv); +} + +already_AddRefed<FileRequestBase> +FileHandleBase::WriteOrAppend(Blob& aValue, + bool aAppend, + ErrorResult& aRv) +{ + AssertIsOnOwningThread(); + + // State checking for write or append + if (!CheckStateForWriteOrAppend(aAppend, aRv)) { + return nullptr; + } + + ErrorResult rv; + uint64_t dataLength = aValue.GetSize(rv); + if (NS_WARN_IF(rv.Failed())) { + aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); + return nullptr; + } + + if (!dataLength) { + return nullptr; + } + + PBackgroundChild* backgroundActor = BackgroundChild::GetForCurrentThread(); + MOZ_ASSERT(backgroundActor); + + PBlobChild* blobActor = + BackgroundChild::GetOrCreateActorForBlob(backgroundActor, &aValue); + if (NS_WARN_IF(!blobActor)) { + aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); + return nullptr; + } + + FileRequestBlobData blobData; + blobData.blobChild() = blobActor; + + // Do nothing if the window is closed + if (!CheckWindow()) { + return nullptr; + } + + return WriteInternal(blobData, dataLength, aAppend, aRv); +} + +already_AddRefed<FileRequestBase> +FileHandleBase::WriteInternal(const FileRequestData& aData, + uint64_t aDataLength, + bool aAppend, + ErrorResult& aRv) +{ + AssertIsOnOwningThread(); DebugOnly<ErrorResult> error; MOZ_ASSERT(CheckStateForWrite(error)); MOZ_ASSERT_IF(!aAppend, mLocation != UINT64_MAX); - MOZ_ASSERT(aInputStream); - MOZ_ASSERT(aInputLength); + MOZ_ASSERT(aDataLength); MOZ_ASSERT(CheckWindow()); + FileRequestWriteParams params; + params.offset() = aAppend ? UINT64_MAX : mLocation; + params.data() = aData; + params.dataLength() = aDataLength; + nsRefPtr<FileRequestBase> fileRequest = GenerateFileRequest(); - - uint64_t location = aAppend ? UINT64_MAX : mLocation; + MOZ_ASSERT(fileRequest); - nsRefPtr<WriteHelper> helper = - new WriteHelper(this, fileRequest, location, aInputStream, aInputLength); - - if (NS_WARN_IF(NS_FAILED(helper->Enqueue()))) { - aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR); - return nullptr; - } + StartRequest(fileRequest, params); if (aAppend) { mLocation = UINT64_MAX; } else { - mLocation += aInputLength; + mLocation += aDataLength; } return fileRequest.forget(); } -nsresult -FileHandleBase::Finish() -{ - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); - - nsRefPtr<FinishHelper> helper(new FinishHelper(this)); - - FileService* service = FileService::Get(); - MOZ_ASSERT(service, "This should never be null"); - - nsIEventTarget* target = service->ThreadPoolTarget(); - - nsresult rv = target->Dispatch(helper, NS_DISPATCH_NORMAL); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -// static -already_AddRefed<nsIInputStream> -FileHandleBase::GetInputStream(const ArrayBuffer& aValue, - uint64_t* aInputLength, ErrorResult& aRv) +void +FileHandleBase::SendFinish() { - aValue.ComputeLengthAndData(); - const char* data = reinterpret_cast<const char*>(aValue.Data()); - uint32_t length = aValue.Length(); - - nsCOMPtr<nsIInputStream> stream; - aRv = NS_NewByteInputStream(getter_AddRefs(stream), data, length, - NS_ASSIGNMENT_COPY); - if (aRv.Failed()) { - return nullptr; - } - - *aInputLength = length; - return stream.forget(); -} - -// static -already_AddRefed<nsIInputStream> -FileHandleBase::GetInputStream(const Blob& aValue, uint64_t* aInputLength, - ErrorResult& aRv) -{ - Blob& file = const_cast<Blob&>(aValue); - uint64_t length = file.GetSize(aRv); - if (aRv.Failed()) { - return nullptr; - } - - nsCOMPtr<nsIInputStream> stream; - file.GetInternalStream(getter_AddRefs(stream), aRv); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; - } - - *aInputLength = length; - return stream.forget(); -} - -// static -already_AddRefed<nsIInputStream> -FileHandleBase::GetInputStream(const nsAString& aValue, uint64_t* aInputLength, - ErrorResult& aRv) -{ - NS_ConvertUTF16toUTF8 cstr(aValue); + AssertIsOnOwningThread(); + MOZ_ASSERT(!mAborted); + MOZ_ASSERT(IsFinishingOrDone()); + MOZ_ASSERT(!mSentFinishOrAbort); + MOZ_ASSERT(!mPendingRequestCount); - nsCOMPtr<nsIInputStream> stream; - aRv = NS_NewCStringInputStream(getter_AddRefs(stream), cstr); - if (aRv.Failed()) { - return nullptr; - } - - *aInputLength = cstr.Length(); - return stream.forget(); -} - -FinishHelper::FinishHelper(FileHandleBase* aFileHandle) -: mFileHandle(aFileHandle), - mAborted(aFileHandle->mAborted) -{ - mParallelStreams.SwapElements(aFileHandle->mParallelStreams); - mStream.swap(aFileHandle->mStream); -} - -NS_IMPL_ISUPPORTS(FinishHelper, nsIRunnable) - -NS_IMETHODIMP -FinishHelper::Run() -{ - if (NS_IsMainThread()) { - mFileHandle->mReadyState = FileHandleBase::DONE; - - FileService* service = FileService::Get(); - if (service) { - service->NotifyFileHandleCompleted(mFileHandle); - } - - nsresult rv = mFileHandle->OnCompleteOrAbort(mAborted); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - mFileHandle = nullptr; + MOZ_ASSERT(mBackgroundActor); + mBackgroundActor->SendFinish(); - return NS_OK; - } - - if (mFileHandle->MutableFile()->IsInvalid()) { - mAborted = true; - } - - for (uint32_t index = 0; index < mParallelStreams.Length(); index++) { - nsCOMPtr<nsIInputStream> stream = - do_QueryInterface(mParallelStreams[index]); - - if (NS_FAILED(stream->Close())) { - NS_WARNING("Failed to close stream!"); - } - - mParallelStreams[index] = nullptr; - } - - if (mStream) { - nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mStream); - - if (NS_FAILED(stream->Close())) { - NS_WARNING("Failed to close stream!"); - } - - mStream = nullptr; - } - - return NS_DispatchToMainThread(this); -} - -nsresult -ReadHelper::Init() -{ - mStream = MemoryOutputStream::Create(mSize); - NS_ENSURE_TRUE(mStream, NS_ERROR_FAILURE); - - return NS_OK; + DEBUGONLY(mSentFinishOrAbort = true;) } -nsresult -ReadHelper::DoAsyncRun(nsISupports* aStream) -{ - MOZ_ASSERT(aStream, "Passed a null stream!"); - - uint32_t flags = FileStreamWrapper::NOTIFY_PROGRESS; - - nsCOMPtr<nsIInputStream> istream = - new FileInputStreamWrapper(aStream, this, mLocation, mSize, flags); - - FileService* service = FileService::Get(); - MOZ_ASSERT(service, "This should never be null"); - - nsIEventTarget* target = service->ThreadPoolTarget(); - - nsCOMPtr<nsIAsyncStreamCopier> copier; - nsresult rv = - NS_NewAsyncStreamCopier(getter_AddRefs(copier), istream, mStream, target, - false, true, STREAM_COPY_BLOCK_SIZE); - NS_ENSURE_SUCCESS(rv, rv); - - rv = copier->AsyncCopy(this, nullptr); - NS_ENSURE_SUCCESS(rv, rv); - - mRequest = do_QueryInterface(copier); - - return NS_OK; -} - -nsresult -ReadHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle<JS::Value> aVal) +void +FileHandleBase::SendAbort() { - JS::Rooted<JSObject*> arrayBuffer(aCx); - nsresult rv = - nsContentUtils::CreateArrayBuffer(aCx, mStream->Data(), arrayBuffer.address()); - NS_ENSURE_SUCCESS(rv, rv); - - aVal.setObject(*arrayBuffer); - return NS_OK; -} - -nsresult -ReadTextHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle<JS::Value> aVal) -{ - nsAutoCString encoding; - const nsCString& data = mStream->Data(); - // The BOM sniffing is baked into the "decode" part of the Encoding - // Standard, which the File API references. - if (!nsContentUtils::CheckForBOM( - reinterpret_cast<const unsigned char *>(data.get()), - data.Length(), - encoding)) { - // BOM sniffing failed. Try the API argument. - if (!EncodingUtils::FindEncodingForLabel(mEncoding, encoding)) { - // API argument failed. Since we are dealing with a file system file, - // we don't have a meaningful type attribute for the blob available, - // so proceeding to the next step, which is defaulting to UTF-8. - encoding.AssignLiteral("UTF-8"); - } - } - - nsString tmpString; - nsresult rv = nsContentUtils::ConvertStringFromEncoding(encoding, data, - tmpString); - NS_ENSURE_SUCCESS(rv, rv); - - if (!xpc::StringToJsval(aCx, tmpString, aVal)) { - NS_WARNING("Failed to convert string!"); - return NS_ERROR_FAILURE; - } - return NS_OK; -} - -nsresult -WriteHelper::DoAsyncRun(nsISupports* aStream) -{ - MOZ_ASSERT(aStream, "Passed a null stream!"); - - uint32_t flags = FileStreamWrapper::NOTIFY_PROGRESS; + AssertIsOnOwningThread(); + MOZ_ASSERT(mAborted); + MOZ_ASSERT(IsFinishingOrDone()); + MOZ_ASSERT(!mSentFinishOrAbort); - nsCOMPtr<nsIOutputStream> ostream = - new FileOutputStreamWrapper(aStream, this, mLocation, mLength, flags); - - FileService* service = FileService::Get(); - MOZ_ASSERT(service, "This should never be null"); - - nsIEventTarget* target = service->ThreadPoolTarget(); - - nsCOMPtr<nsIAsyncStreamCopier> copier; - nsresult rv = - NS_NewAsyncStreamCopier(getter_AddRefs(copier), mStream, ostream, target, - true, false, STREAM_COPY_BLOCK_SIZE); - NS_ENSURE_SUCCESS(rv, rv); - - rv = copier->AsyncCopy(this, nullptr); - NS_ENSURE_SUCCESS(rv, rv); - - mRequest = do_QueryInterface(copier); - - return NS_OK; -} - -nsresult -TruncateHelper::DoAsyncRun(nsISupports* aStream) -{ - MOZ_ASSERT(aStream, "Passed a null stream!"); - - nsRefPtr<AsyncTruncator> truncator = new AsyncTruncator(aStream, mOffset); - - nsresult rv = truncator->AsyncWork(this, nullptr); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -TruncateHelper::AsyncTruncator::DoStreamWork(nsISupports* aStream) -{ - nsCOMPtr<nsISeekableStream> sstream = do_QueryInterface(aStream); - - nsresult rv = sstream->Seek(nsISeekableStream::NS_SEEK_SET, mOffset); - NS_ENSURE_SUCCESS(rv, rv); + MOZ_ASSERT(mBackgroundActor); + mBackgroundActor->SendAbort(); - rv = sstream->SetEOF(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -FlushHelper::DoAsyncRun(nsISupports* aStream) -{ - MOZ_ASSERT(aStream, "Passed a null stream!"); - - nsRefPtr<AsyncFlusher> flusher = new AsyncFlusher(aStream); - - nsresult rv = flusher->AsyncWork(this, nullptr); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -FlushHelper::AsyncFlusher::DoStreamWork(nsISupports* aStream) -{ - nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(aStream); - - nsresult rv = ostream->Flush(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -OpenStreamHelper::DoAsyncRun(nsISupports* aStream) -{ - MOZ_ASSERT(aStream, "Passed a null stream!"); - - uint32_t flags = FileStreamWrapper::NOTIFY_CLOSE | - FileStreamWrapper::NOTIFY_DESTROY; - - mStream = mWholeFile ? - new FileInputStreamWrapper(aStream, this, 0, mLength, flags) : - new FileInputStreamWrapper(aStream, this, mStart, mLength, flags); - - return NS_OK; + DEBUGONLY(mSentFinishOrAbort = true;) } } // namespace dom } // namespace mozilla
rename from dom/filehandle/FileHandle.h rename to dom/filehandle/FileHandleBase.h --- a/dom/filehandle/FileHandle.h +++ b/dom/filehandle/FileHandleBase.h @@ -2,148 +2,161 @@ /* 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_FileHandle_h #define mozilla_dom_FileHandle_h -#include "MainThreadUtils.h" -#include "mozilla/Assertions.h" +#include "FileHandleCommon.h" #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/FileModeBinding.h" #include "mozilla/dom/Nullable.h" #include "mozilla/dom/TypedArray.h" -#include "mozilla/ErrorResult.h" -#include "nsAutoPtr.h" -#include "nsCOMPtr.h" -#include "nsIInputStream.h" -#include "nsIRunnable.h" -#include "nsTArray.h" +template <class> struct already_AddRefed; class nsAString; +struct PRThread; namespace mozilla { + +class ErrorResult; + namespace dom { +class BackgroundFileHandleChild; class Blob; -class FileHelper; class FileRequestBase; -class FileService; -class FinishHelper; -class MetadataHelper; +class FileRequestData; +class FileRequestParams; class MutableFileBase; +class StringOrArrayBufferOrArrayBufferViewOrBlob; /** * This class provides a base for FileHandle implementations. */ class FileHandleBase + : public RefCountedThreadObject { public: - enum RequestMode - { - NORMAL = 0, // Sequential - PARALLEL - }; - enum ReadyState { INITIAL = 0, LOADING, FINISHING, DONE }; private: - friend class FileHelper; - friend class FileService; - friend class FinishHelper; - friend class MetadataHelper; + BackgroundFileHandleChild* mBackgroundActor; + + uint64_t mLocation; + + uint32_t mPendingRequestCount; ReadyState mReadyState; FileMode mMode; - RequestMode mRequestMode; - uint64_t mLocation; - uint32_t mPendingRequests; - - nsTArray<nsCOMPtr<nsISupports>> mParallelStreams; - nsCOMPtr<nsISupports> mStream; bool mAborted; bool mCreating; + DEBUGONLY(bool mSentFinishOrAbort;) + DEBUGONLY(bool mFiredCompleteOrAbort;) + public: - NS_IMETHOD_(MozExternalRefCountType) - AddRef() = 0; + static FileHandleBase* + GetCurrent(); + + void + SetBackgroundActor(BackgroundFileHandleChild* aActor); - NS_IMETHOD_(MozExternalRefCountType) - Release() = 0; + void + ClearBackgroundActor() + { + AssertIsOnOwningThread(); + + mBackgroundActor = nullptr; + } - nsresult - CreateParallelStream(nsISupports** aStream); + void + StartRequest(FileRequestBase* aFileRequest, const FileRequestParams& aParams); - nsresult - GetOrCreateStream(nsISupports** aStream); + void + OnNewRequest(); + + void + OnRequestFinished(bool aActorDestroyedNormally); bool IsOpen() const; bool + IsFinishingOrDone() const + { + AssertIsOnOwningThread(); + + return mReadyState == FINISHING || mReadyState == DONE; + } + + bool + IsDone() const + { + AssertIsOnOwningThread(); + + return mReadyState == DONE; + } + + bool IsAborted() const { + AssertIsOnOwningThread(); return mAborted; } void SetCreating() { mCreating = true; } - virtual MutableFileBase* - MutableFile() const = 0; - - nsresult - OpenInputStream(bool aWholeFile, uint64_t aStart, uint64_t aLength, - nsIInputStream** aResult); + void + Abort(); // Shared WebIDL (IndexedDB FileHandle and FileSystem FileHandle) FileMode Mode() const { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); - + AssertIsOnOwningThread(); return mMode; } bool Active() const { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); - + AssertIsOnOwningThread(); return IsOpen(); } Nullable<uint64_t> GetLocation() const { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); if (mLocation == UINT64_MAX) { return Nullable<uint64_t>(); } return Nullable<uint64_t>(mLocation); } void SetLocation(const Nullable<uint64_t>& aLocation) { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + AssertIsOnOwningThread(); // Null means the end-of-file. if (aLocation.IsNull()) { mLocation = UINT64_MAX; } else { mLocation = aLocation.Value(); } } @@ -156,119 +169,78 @@ public: Truncate(const Optional<uint64_t>& aSize, ErrorResult& aRv); already_AddRefed<FileRequestBase> Flush(ErrorResult& aRv); void Abort(ErrorResult& aRv); + // Must be overridden in subclasses. + virtual MutableFileBase* + MutableFile() const = 0; + + // May be overridden in subclasses. + virtual void + HandleCompleteOrAbort(bool aAborted); + protected: - FileHandleBase(FileMode aMode, - RequestMode aRequestMode); + FileHandleBase(DEBUGONLY(PRThread* aOwningThread,) + FileMode aMode); + ~FileHandleBase(); void - OnNewRequest(); - - void - OnRequestFinished(); - - void OnReturnToEventLoop(); - virtual nsresult - OnCompleteOrAbort(bool aAborted) = 0; - bool CheckState(ErrorResult& aRv); bool CheckStateAndArgumentsForRead(uint64_t aSize, ErrorResult& aRv); bool CheckStateForWrite(ErrorResult& aRv); + bool + CheckStateForWriteOrAppend(bool aAppend, ErrorResult& aRv); + + already_AddRefed<FileRequestBase> + WriteOrAppend(const StringOrArrayBufferOrArrayBufferViewOrBlob& aValue, + bool aAppend, + ErrorResult& aRv); + + // Must be overridden in subclasses. virtual bool CheckWindow() = 0; + // Must be overridden in subclasses. virtual already_AddRefed<FileRequestBase> GenerateFileRequest() = 0; - template<class T> +private: already_AddRefed<FileRequestBase> - WriteOrAppend(const T& aValue, bool aAppend, ErrorResult& aRv) - { - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); - - // State checking for write - if (!CheckStateForWrite(aRv)) { - return nullptr; - } - - // Additional state checking for write - if (!aAppend && mLocation == UINT64_MAX) { - aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR); - return nullptr; - } + WriteOrAppend(const nsAString& aValue, bool aAppend, ErrorResult& aRv); - uint64_t length; - nsCOMPtr<nsIInputStream> stream = GetInputStream(aValue, &length, aRv); - if (aRv.Failed()) { - return nullptr; - } + already_AddRefed<FileRequestBase> + WriteOrAppend(const ArrayBuffer& aValue, bool aAppend, ErrorResult& aRv); - if (!length) { - return nullptr; - } - - // Do nothing if the window is closed - if (!CheckWindow()) { - return nullptr; - } - - return WriteInternal(stream, length, aAppend, aRv); - } + already_AddRefed<FileRequestBase> + WriteOrAppend(const ArrayBufferView& aValue, bool aAppend, ErrorResult& aRv); already_AddRefed<FileRequestBase> - WriteInternal(nsIInputStream* aInputStream, uint64_t aInputLength, + WriteOrAppend(Blob& aValue, bool aAppend, ErrorResult& aRv); + + already_AddRefed<FileRequestBase> + WriteInternal(const FileRequestData& aData, uint64_t aDataLength, bool aAppend, ErrorResult& aRv); - nsresult - Finish(); - - static already_AddRefed<nsIInputStream> - GetInputStream(const ArrayBuffer& aValue, uint64_t* aInputLength, - ErrorResult& aRv); - - static already_AddRefed<nsIInputStream> - GetInputStream(const Blob& aValue, uint64_t* aInputLength, - ErrorResult& aRv); - - static already_AddRefed<nsIInputStream> - GetInputStream(const nsAString& aValue, uint64_t* aInputLength, - ErrorResult& aRv); -}; + void + SendFinish(); -class FinishHelper final : public nsIRunnable -{ - friend class FileHandleBase; - -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIRUNNABLE - -private: - explicit FinishHelper(FileHandleBase* aFileHandle); - ~FinishHelper() - { } - - nsRefPtr<FileHandleBase> mFileHandle; - nsTArray<nsCOMPtr<nsISupports>> mParallelStreams; - nsCOMPtr<nsISupports> mStream; - - bool mAborted; + void + SendAbort(); }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_FileHandle_h
new file mode 100644 --- /dev/null +++ b/dom/filehandle/FileHandleCommon.cpp @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=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 "FileHandleCommon.h" + +#include "mozilla/Assertions.h" +#include "prthread.h" + +namespace mozilla { +namespace dom { + +#ifdef DEBUG + +void +ThreadObject::AssertIsOnOwningThread() const +{ + MOZ_ASSERT(mOwningThread); + MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread); +} + +PRThread* +ThreadObject::OwningThread() const +{ + MOZ_ASSERT(mOwningThread); + return mOwningThread; +} + +#endif // DEBUG + +} // namespace dom +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/dom/filehandle/FileHandleCommon.h @@ -0,0 +1,73 @@ +/* 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_FileHandleCommon_h +#define mozilla_dom_FileHandleCommon_h + +#include "nscore.h" + +#ifdef DEBUG +#define DEBUGONLY(...) __VA_ARGS__ +#else +#define DEBUGONLY(...) /* nothing */ +#endif + +struct PRThread; + +namespace mozilla { +namespace dom { + +class RefCountedObject +{ +public: + NS_IMETHOD_(MozExternalRefCountType) + AddRef() = 0; + + NS_IMETHOD_(MozExternalRefCountType) + Release() = 0; + +protected: + virtual ~RefCountedObject() + { } +}; + +class ThreadObject +{ + DEBUGONLY(PRThread* mOwningThread;) + +public: + explicit ThreadObject(DEBUGONLY(PRThread* aOwningThread)) + DEBUGONLY(: mOwningThread(aOwningThread)) + { } + + virtual ~ThreadObject() + { } + +#ifdef DEBUG + void + AssertIsOnOwningThread() const; + + PRThread* + OwningThread() const; +#else + void + AssertIsOnOwningThread() const + { } +#endif +}; + +class RefCountedThreadObject + : public RefCountedObject + , public ThreadObject +{ +public: + explicit RefCountedThreadObject(DEBUGONLY(PRThread* aOwningThread)) + : ThreadObject(DEBUGONLY(aOwningThread)) + { } +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_FileHandleCommon_h
new file mode 100644 --- /dev/null +++ b/dom/filehandle/FileHandleStorage.h @@ -0,0 +1,24 @@ +/* -*- 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_FileHandleStorage_h +#define mozilla_dom_FileHandleStorage_h + +namespace mozilla { +namespace dom { + +enum FileHandleStorage +{ + FILE_HANDLE_STORAGE_IDB = 0, + // A placeholder for bug 997471 + //FILE_HANDLE_STORAGE_FS + FILE_HANDLE_STORAGE_MAX +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_FileHandleStorage_h
deleted file mode 100644 --- a/dom/filehandle/FileHelper.cpp +++ /dev/null @@ -1,235 +0,0 @@ -/* -*- 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 "FileHelper.h" - -#include "FileHandle.h" -#include "FileRequest.h" -#include "FileService.h" -#include "js/Value.h" -#include "MainThreadUtils.h" -#include "mozilla/Assertions.h" -#include "MutableFile.h" -#include "nsDebug.h" -#include "nsError.h" -#include "nsIRequest.h" - -namespace mozilla { -namespace dom { - -namespace { - -FileHandleBase* gCurrentFileHandle = nullptr; - -} // namespace - -FileHelper::FileHelper(FileHandleBase* aFileHandle, - FileRequestBase* aFileRequest) -: mMutableFile(aFileHandle->MutableFile()), - mFileHandle(aFileHandle), - mFileRequest(aFileRequest), - mResultCode(NS_OK), - mFinished(false) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); -} - -FileHelper::~FileHelper() -{ - MOZ_ASSERT(!mMutableFile && !mFileHandle && !mFileRequest && !mListener && - !mRequest, "Should have cleared this!"); -} - -NS_IMPL_ISUPPORTS(FileHelper, nsIRequestObserver) - -nsresult -FileHelper::Enqueue() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - FileService* service = FileService::GetOrCreate(); - NS_ENSURE_TRUE(service, NS_ERROR_FAILURE); - - nsresult rv = service->Enqueue(mFileHandle, this); - NS_ENSURE_SUCCESS(rv, rv); - - if (mFileHandle) { - mFileHandle->OnNewRequest(); - } - - return NS_OK; -} - -nsresult -FileHelper::AsyncRun(FileHelperListener* aListener) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - // Assign the listener early, so we can use it synchronously if the code - // below fails. - mListener = aListener; - - nsresult rv; - - nsCOMPtr<nsISupports> stream; - if (mFileHandle->mRequestMode == FileHandleBase::PARALLEL) { - rv = mFileHandle->CreateParallelStream(getter_AddRefs(stream)); - } - else { - rv = mFileHandle->GetOrCreateStream(getter_AddRefs(stream)); - } - - if (NS_SUCCEEDED(rv)) { - NS_ASSERTION(stream, "This should never be null!"); - - rv = DoAsyncRun(stream); - } - - if (NS_FAILED(rv)) { - mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; - Finish(); - } - - return NS_OK; -} - -NS_IMETHODIMP -FileHelper::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - return NS_OK; -} - -NS_IMETHODIMP -FileHelper::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt, - nsresult aStatus) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (NS_FAILED(aStatus)) { - if (aStatus == NS_ERROR_FILE_NO_DEVICE_SPACE) { - mResultCode = NS_ERROR_DOM_FILEHANDLE_QUOTA_ERR; - } - else { - mResultCode = NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR; - } - } - - Finish(); - - return NS_OK; -} - -void -FileHelper::OnStreamProgress(uint64_t aProgress, uint64_t aProgressMax) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (mFileHandle->IsAborted()) { - NS_ASSERTION(mRequest, "Should have a request!\n"); - - nsresult rv = mRequest->Cancel(NS_BINDING_ABORTED); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to cancel the request!"); - } - - return; - } - - if (mFileRequest) { - mFileRequest->OnProgress(aProgress, aProgressMax); - } -} - -// static -FileHandleBase* -FileHelper::GetCurrentFileHandle() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - return gCurrentFileHandle; -} - -nsresult -FileHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle<JS::Value> aVal) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - aVal.setUndefined(); - return NS_OK; -} - -void -FileHelper::ReleaseObjects() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - mMutableFile = nullptr; - mFileHandle = nullptr; - mFileRequest = nullptr; - mListener = nullptr; - mRequest = nullptr; -} - -void -FileHelper::Finish() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (mFinished) { - return; - } - - mFinished = true; - - if (mFileHandle->IsAborted()) { - // Always fire a "error" event with ABORT_ERR if the transaction was - // aborted, even if the request succeeded or failed with another error. - mResultCode = NS_ERROR_DOM_FILEHANDLE_ABORT_ERR; - } - - FileHandleBase* oldFileHandle = gCurrentFileHandle; - gCurrentFileHandle = mFileHandle; - - if (mFileRequest) { - nsresult rv = mFileRequest->NotifyHelperCompleted(this); - if (NS_SUCCEEDED(mResultCode) && NS_FAILED(rv)) { - mResultCode = rv; - } - } - - MOZ_ASSERT(gCurrentFileHandle == mFileHandle, "Should be unchanged!"); - gCurrentFileHandle = oldFileHandle; - - mFileHandle->OnRequestFinished(); - - mListener->OnFileHelperComplete(this); - - ReleaseObjects(); - - MOZ_ASSERT(!(mMutableFile || mFileHandle || mFileRequest || mListener || - mRequest), "Subclass didn't call FileHelper::ReleaseObjects!"); - -} - -void -FileHelper::OnStreamClose() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - Finish(); -} - -void -FileHelper::OnStreamDestroy() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - Finish(); -} - -} // namespace dom -} // namespace mozilla
deleted file mode 100644 --- a/dom/filehandle/FileHelper.h +++ /dev/null @@ -1,106 +0,0 @@ -/* -*- 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_FileHelper_h -#define mozilla_dom_FileHelper_h - -#include "js/TypeDecls.h" -#include "nsAutoPtr.h" -#include "nsCOMPtr.h" -#include "nsIRequestObserver.h" - -namespace mozilla { -namespace dom { - -class FileHandleBase; -class FileHelper; -class FileRequestBase; -class FileOutputStreamWrapper; -class MutableFileBase; - -class FileHelperListener -{ -public: - NS_IMETHOD_(MozExternalRefCountType) - AddRef() = 0; - - NS_IMETHOD_(MozExternalRefCountType) - Release() = 0; - - virtual void - OnFileHelperComplete(FileHelper* aFileHelper) = 0; -}; - -/** - * Must be subclassed. The subclass must implement DoAsyncRun. It may then - * choose to implement GetSuccessResult to properly set the result of the - * success event. Call Enqueue to start the file operation. - */ -class FileHelper : public nsIRequestObserver -{ - friend class FileOutputStreamWrapper; - -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIREQUESTOBSERVER - - nsresult - ResultCode() const - { - return mResultCode; - } - - nsresult - Enqueue(); - - nsresult - AsyncRun(FileHelperListener* aListener); - - virtual nsresult - GetSuccessResult(JSContext* aCx, JS::MutableHandle<JS::Value> aVal); - - void - OnStreamProgress(uint64_t aProgress, uint64_t aProgressMax); - - void - OnStreamClose(); - - void - OnStreamDestroy(); - - static FileHandleBase* - GetCurrentFileHandle(); - -protected: - FileHelper(FileHandleBase* aFileHandle, FileRequestBase* aRequest); - - virtual ~FileHelper(); - - virtual nsresult - DoAsyncRun(nsISupports* aStream) = 0; - - virtual void - ReleaseObjects(); - - void - Finish(); - - nsRefPtr<MutableFileBase> mMutableFile; - nsRefPtr<FileHandleBase> mFileHandle; - nsRefPtr<FileRequestBase> mFileRequest; - - nsRefPtr<FileHelperListener> mListener; - nsCOMPtr<nsIRequest> mRequest; - -private: - nsresult mResultCode; - bool mFinished; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_FileHelper_h
deleted file mode 100644 --- a/dom/filehandle/FileRequest.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* -*- 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 "FileRequest.h" - -#include "MainThreadUtils.h" -#include "mozilla/Assertions.h" - -namespace mozilla { -namespace dom { - -FileRequestBase::FileRequestBase() -{ - MOZ_ASSERT(NS_IsMainThread()); -} - -FileRequestBase::~FileRequestBase() -{ - MOZ_ASSERT(NS_IsMainThread()); -} - -} // namespace dom -} // namespace mozilla
rename from dom/filehandle/FileRequest.h rename to dom/filehandle/FileRequestBase.h --- a/dom/filehandle/FileRequest.h +++ b/dom/filehandle/FileRequestBase.h @@ -2,43 +2,92 @@ /* 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_FileRequest_h #define mozilla_dom_FileRequest_h -#include "nscore.h" +#include "FileHandleCommon.h" +#include "js/TypeDecls.h" +#include "nsString.h" + +struct PRThread; namespace mozilla { namespace dom { -class FileHelper; +class FileHandleBase; /** * This class provides a base for FileRequest implementations. */ class FileRequestBase + : public RefCountedThreadObject { + nsString mEncoding; + + bool mHasEncoding; + public: - NS_IMETHOD_(MozExternalRefCountType) - AddRef() = 0; + class ResultCallback; + + void + SetEncoding(const nsAString& aEncoding) + { + mEncoding = aEncoding; + mHasEncoding = true; + } - NS_IMETHOD_(MozExternalRefCountType) - Release() = 0; + const nsAString& + GetEncoding() const + { + return mEncoding; + } + + bool + HasEncoding() const + { + return mHasEncoding; + } + + virtual FileHandleBase* + FileHandle() const = 0; virtual void OnProgress(uint64_t aProgress, uint64_t aProgressMax) = 0; - virtual nsresult - NotifyHelperCompleted(FileHelper* aFileHelper) = 0; + virtual void + SetResultCallback(ResultCallback* aCallback) = 0; + + virtual void + SetError(nsresult aError) = 0; protected: - FileRequestBase(); + FileRequestBase(DEBUGONLY(PRThread* aOwningThread)) + : RefCountedThreadObject(DEBUGONLY(aOwningThread)) + , mHasEncoding(false) + { + AssertIsOnOwningThread(); + } - virtual ~FileRequestBase(); + virtual ~FileRequestBase() + { + AssertIsOnOwningThread(); + } +}; + +class NS_NO_VTABLE FileRequestBase::ResultCallback +{ +public: + virtual nsresult + GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) = 0; + +protected: + ResultCallback() + { } }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_FileRequest_h
deleted file mode 100644 --- a/dom/filehandle/FileService.cpp +++ /dev/null @@ -1,479 +0,0 @@ -/* -*- 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 "FileService.h" - -#include "FileHandle.h" -#include "MainThreadUtils.h" -#include "mozilla/Assertions.h" -#include "MutableFile.h" -#include "nsError.h" -#include "nsIEventTarget.h" -#include "nsNetCID.h" -#include "nsServiceManagerUtils.h" -#include "nsThreadPool.h" -#include "nsThreadUtils.h" - -namespace mozilla { -namespace dom { - -namespace { - -const uint32_t kThreadLimit = 5; -const uint32_t kIdleThreadLimit = 1; -const uint32_t kIdleThreadTimeoutMs = 30000; - -StaticAutoPtr<FileService> gInstance; -bool gShutdown = false; - -} // namespace - -FileService::FileService() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!gInstance); -} - -FileService::~FileService() -{ - MOZ_ASSERT(NS_IsMainThread()); -} - -nsresult -FileService::Init() -{ - mThreadPool = new nsThreadPool(); - - nsresult rv = mThreadPool->SetName(NS_LITERAL_CSTRING("FileHandleTrans")); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = mThreadPool->SetThreadLimit(kThreadLimit); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = mThreadPool->SetIdleThreadLimit(kIdleThreadLimit); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = mThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} - -nsresult -FileService::Cleanup() -{ - MOZ_ASSERT(NS_IsMainThread()); - - nsIThread* thread = NS_GetCurrentThread(); - MOZ_ASSERT(thread); - - nsresult rv = mThreadPool->Shutdown(); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - // Make sure the service is still accessible while any generated callbacks - // are processed. - rv = NS_ProcessPendingEvents(thread); - NS_ENSURE_SUCCESS(rv, rv); - - if (!mCompleteCallbacks.IsEmpty()) { - // Run all callbacks manually now. - for (uint32_t index = 0; index < mCompleteCallbacks.Length(); index++) { - mCompleteCallbacks[index].mCallback->Run(); - } - mCompleteCallbacks.Clear(); - - // And make sure they get processed. - rv = NS_ProcessPendingEvents(thread); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - -// static -FileService* -FileService::GetOrCreate() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (gShutdown) { - NS_WARNING("Calling GetOrCreate() after shutdown!"); - return nullptr; - } - - if (!gInstance) { - nsAutoPtr<FileService> service(new FileService()); - - nsresult rv = service->Init(); - NS_ENSURE_SUCCESS(rv, nullptr); - - gInstance = service.forget(); - } - - return gInstance; -} - -// static -FileService* -FileService::Get() -{ - // Does not return an owning reference. - return gInstance; -} - -// static -void -FileService::Shutdown() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - gShutdown = true; - - if (gInstance) { - if (NS_FAILED(gInstance->Cleanup())) { - NS_WARNING("Failed to shutdown file service!"); - } - gInstance = nullptr; - } -} - -// static -bool -FileService::IsShuttingDown() -{ - return gShutdown; -} - -nsresult -FileService::Enqueue(FileHandleBase* aFileHandle, FileHelper* aFileHelper) -{ - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); - MOZ_ASSERT(aFileHandle, "Null pointer!"); - - MutableFileBase* mutableFile = aFileHandle->MutableFile(); - - if (mutableFile->IsInvalid()) { - return NS_ERROR_NOT_AVAILABLE; - } - - const nsACString& storageId = mutableFile->mStorageId; - const nsAString& fileName = mutableFile->mFileName; - bool modeIsWrite = aFileHandle->mMode == FileMode::Readwrite; - - StorageInfo* storageInfo; - if (!mStorageInfos.Get(storageId, &storageInfo)) { - nsAutoPtr<StorageInfo> newStorageInfo(new StorageInfo()); - - mStorageInfos.Put(storageId, newStorageInfo); - - storageInfo = newStorageInfo.forget(); - } - - FileHandleQueue* existingFileHandleQueue = - storageInfo->GetFileHandleQueue(aFileHandle); - - if (existingFileHandleQueue) { - existingFileHandleQueue->Enqueue(aFileHelper); - return NS_OK; - } - - bool lockedForReading = storageInfo->IsFileLockedForReading(fileName); - bool lockedForWriting = storageInfo->IsFileLockedForWriting(fileName); - - if (modeIsWrite) { - if (!lockedForWriting) { - storageInfo->LockFileForWriting(fileName); - } - } - else { - if (!lockedForReading) { - storageInfo->LockFileForReading(fileName); - } - } - - if (lockedForWriting || (lockedForReading && modeIsWrite)) { - storageInfo->CreateDelayedEnqueueInfo(aFileHandle, aFileHelper); - } - else { - FileHandleQueue* fileHandleQueue = - storageInfo->CreateFileHandleQueue(aFileHandle); - - if (aFileHelper) { - // Enqueue() will queue the file helper if there's already something - // running. That can't fail, so no need to eventually remove - // storageInfo from the hash table. - // - // If the file helper is free to run then AsyncRun() is called on the - // file helper. AsyncRun() is responsible for calling all necessary - // callbacks when something fails. We're propagating the error here, - // however there's no need to eventually remove storageInfo from - // the hash table. Code behind AsyncRun() will take care of it. The last - // item in the code path is NotifyFileHandleCompleted() which removes - // storageInfo from the hash table if there are no file handles for - // the file storage. - nsresult rv = fileHandleQueue->Enqueue(aFileHelper); - NS_ENSURE_SUCCESS(rv, rv); - } - } - - return NS_OK; -} - -void -FileService::NotifyFileHandleCompleted(FileHandleBase* aFileHandle) -{ - MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); - MOZ_ASSERT(aFileHandle, "Null pointer!"); - - MutableFileBase* mutableFile = aFileHandle->MutableFile(); - const nsACString& storageId = mutableFile->mStorageId; - - StorageInfo* storageInfo; - if (!mStorageInfos.Get(storageId, &storageInfo)) { - NS_ERROR("We don't know anyting about this file handle?!"); - return; - } - - storageInfo->RemoveFileHandleQueue(aFileHandle); - - if (!storageInfo->HasRunningFileHandles()) { - mStorageInfos.Remove(storageId); - - // See if we need to fire any complete callbacks. - uint32_t index = 0; - while (index < mCompleteCallbacks.Length()) { - if (MaybeFireCallback(mCompleteCallbacks[index])) { - mCompleteCallbacks.RemoveElementAt(index); - } - else { - index++; - } - } - } -} - -void -FileService::WaitForStoragesToComplete(nsTArray<nsCString>& aStorageIds, - nsIRunnable* aCallback) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!aStorageIds.IsEmpty()); - MOZ_ASSERT(aCallback); - - StoragesCompleteCallback* callback = mCompleteCallbacks.AppendElement(); - callback->mCallback = aCallback; - callback->mStorageIds.SwapElements(aStorageIds); - - if (MaybeFireCallback(*callback)) { - mCompleteCallbacks.RemoveElementAt(mCompleteCallbacks.Length() - 1); - } -} - -nsIEventTarget* -FileService::ThreadPoolTarget() const -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mThreadPool); - - return mThreadPool; -} - -bool -FileService::MaybeFireCallback(StoragesCompleteCallback& aCallback) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - for (uint32_t index = 0; index < aCallback.mStorageIds.Length(); index++) { - if (mStorageInfos.Get(aCallback.mStorageIds[index], nullptr)) { - return false; - } - } - - aCallback.mCallback->Run(); - return true; -} - -FileService::FileHandleQueue::FileHandleQueue(FileHandleBase* aFileHandle) -: mFileHandle(aFileHandle) -{ - MOZ_ASSERT(aFileHandle, "Null pointer!"); -} - -FileService::FileHandleQueue::~FileHandleQueue() -{ -} - -NS_IMPL_ADDREF(FileService::FileHandleQueue) -NS_IMPL_RELEASE(FileService::FileHandleQueue) - -nsresult -FileService::FileHandleQueue::Enqueue(FileHelper* aFileHelper) -{ - mQueue.AppendElement(aFileHelper); - - nsresult rv; - if (mFileHandle->mRequestMode == FileHandleBase::PARALLEL) { - rv = aFileHelper->AsyncRun(this); - } - else { - rv = ProcessQueue(); - } - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -void -FileService:: -FileHandleQueue::OnFileHelperComplete(FileHelper* aFileHelper) -{ - if (mFileHandle->mRequestMode == FileHandleBase::PARALLEL) { - int32_t index = mQueue.IndexOf(aFileHelper); - NS_ASSERTION(index != -1, "We don't know anything about this helper!"); - - mQueue.RemoveElementAt(index); - } - else { - NS_ASSERTION(mCurrentHelper == aFileHelper, "How can this happen?!"); - - mCurrentHelper = nullptr; - - nsresult rv = ProcessQueue(); - if (NS_FAILED(rv)) { - return; - } - } -} - -nsresult -FileService::FileHandleQueue::ProcessQueue() -{ - if (mQueue.IsEmpty() || mCurrentHelper) { - return NS_OK; - } - - mCurrentHelper = mQueue[0]; - mQueue.RemoveElementAt(0); - - nsresult rv = mCurrentHelper->AsyncRun(this); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -FileService::DelayedEnqueueInfo::DelayedEnqueueInfo() -{ -} - -FileService::DelayedEnqueueInfo::~DelayedEnqueueInfo() -{ -} - -FileService::FileHandleQueue* -FileService::StorageInfo::CreateFileHandleQueue(FileHandleBase* aFileHandle) -{ - nsRefPtr<FileHandleQueue>* fileHandleQueue = - mFileHandleQueues.AppendElement(); - *fileHandleQueue = new FileHandleQueue(aFileHandle); - return fileHandleQueue->get(); -} - -FileService::FileHandleQueue* -FileService::StorageInfo::GetFileHandleQueue(FileHandleBase* aFileHandle) -{ - uint32_t count = mFileHandleQueues.Length(); - for (uint32_t index = 0; index < count; index++) { - nsRefPtr<FileHandleQueue>& fileHandleQueue = mFileHandleQueues[index]; - if (fileHandleQueue->mFileHandle == aFileHandle) { - return fileHandleQueue; - } - } - return nullptr; -} - -void -FileService::StorageInfo::RemoveFileHandleQueue(FileHandleBase* aFileHandle) -{ - for (uint32_t index = 0; index < mDelayedEnqueueInfos.Length(); index++) { - if (mDelayedEnqueueInfos[index].mFileHandle == aFileHandle) { - MOZ_ASSERT(!mDelayedEnqueueInfos[index].mFileHelper, "Should be null!"); - mDelayedEnqueueInfos.RemoveElementAt(index); - return; - } - } - - uint32_t fileHandleCount = mFileHandleQueues.Length(); - - // We can't just remove entries from lock hash tables, we have to rebuild - // them instead. Multiple FileHandle objects may lock the same file - // (one entry can represent multiple locks). - - mFilesReading.Clear(); - mFilesWriting.Clear(); - - for (uint32_t index = 0, count = fileHandleCount; index < count; index++) { - FileHandleBase* fileHandle = mFileHandleQueues[index]->mFileHandle; - if (fileHandle == aFileHandle) { - MOZ_ASSERT(count == fileHandleCount, "More than one match?!"); - - mFileHandleQueues.RemoveElementAt(index); - index--; - count--; - - continue; - } - - const nsAString& fileName = fileHandle->MutableFile()->mFileName; - - if (fileHandle->mMode == FileMode::Readwrite) { - if (!IsFileLockedForWriting(fileName)) { - LockFileForWriting(fileName); - } - } - else { - if (!IsFileLockedForReading(fileName)) { - LockFileForReading(fileName); - } - } - } - - MOZ_ASSERT(mFileHandleQueues.Length() == fileHandleCount - 1, - "Didn't find the file handle we were looking for!"); - - nsTArray<DelayedEnqueueInfo> delayedEnqueueInfos; - delayedEnqueueInfos.SwapElements(mDelayedEnqueueInfos); - - for (uint32_t index = 0; index < delayedEnqueueInfos.Length(); index++) { - DelayedEnqueueInfo& delayedEnqueueInfo = delayedEnqueueInfos[index]; - if (NS_FAILED(gInstance->Enqueue(delayedEnqueueInfo.mFileHandle, - delayedEnqueueInfo.mFileHelper))) { - NS_WARNING("Enqueue failed!"); - } - } -} - -FileService::DelayedEnqueueInfo* -FileService::StorageInfo::CreateDelayedEnqueueInfo(FileHandleBase* aFileHandle, - FileHelper* aFileHelper) -{ - DelayedEnqueueInfo* info = mDelayedEnqueueInfos.AppendElement(); - info->mFileHandle = aFileHandle; - info->mFileHelper = aFileHelper; - return info; -} - -} // namespace dom -} // namespace mozilla
deleted file mode 100644 --- a/dom/filehandle/FileService.h +++ /dev/null @@ -1,193 +0,0 @@ -/* -*- 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_FileService_h -#define mozilla_dom_FileService_h - -#include "mozilla/Attributes.h" -#include "mozilla/dom/FileHelper.h" -#include "mozilla/StaticPtr.h" -#include "nsClassHashtable.h" -#include "nsAutoPtr.h" -#include "nsCOMPtr.h" -#include "nsDebug.h" -#include "nsHashKeys.h" -#include "nsTArray.h" -#include "nsTHashtable.h" - -class nsAString; -class nsIEventTarget; -class nsIRunnable; -class nsThreadPool; - -namespace mozilla { -namespace dom { - -class FileHandleBase; - -class FileService final -{ - friend class nsAutoPtr<FileService>; - friend class StaticAutoPtr<FileService>; - -public: - // Returns a non-owning reference! - static FileService* - GetOrCreate(); - - // Returns a non-owning reference! - static FileService* - Get(); - - static void - Shutdown(); - - // Returns true if we've begun the shutdown process. - static bool - IsShuttingDown(); - - nsresult - Enqueue(FileHandleBase* aFileHandle, FileHelper* aFileHelper); - - void - NotifyFileHandleCompleted(FileHandleBase* aFileHandle); - - void - WaitForStoragesToComplete(nsTArray<nsCString>& aStorageIds, - nsIRunnable* aCallback); - - nsIEventTarget* - ThreadPoolTarget() const; - -private: - class FileHandleQueue final : public FileHelperListener - { - friend class FileService; - - public: - NS_IMETHOD_(MozExternalRefCountType) - AddRef() override; - - NS_IMETHOD_(MozExternalRefCountType) - Release() override; - - inline nsresult - Enqueue(FileHelper* aFileHelper); - - virtual void - OnFileHelperComplete(FileHelper* aFileHelper) override; - - private: - inline - explicit FileHandleQueue(FileHandleBase* aFileHandle); - - ~FileHandleQueue(); - - nsresult - ProcessQueue(); - - ThreadSafeAutoRefCnt mRefCnt; - NS_DECL_OWNINGTHREAD - nsRefPtr<FileHandleBase> mFileHandle; - nsTArray<nsRefPtr<FileHelper> > mQueue; - nsRefPtr<FileHelper> mCurrentHelper; - }; - - struct DelayedEnqueueInfo - { - DelayedEnqueueInfo(); - ~DelayedEnqueueInfo(); - - nsRefPtr<FileHandleBase> mFileHandle; - nsRefPtr<FileHelper> mFileHelper; - }; - - class StorageInfo - { - friend class FileService; - - public: - inline FileHandleQueue* - CreateFileHandleQueue(FileHandleBase* aFileHandle); - - inline FileHandleQueue* - GetFileHandleQueue(FileHandleBase* aFileHandle); - - void - RemoveFileHandleQueue(FileHandleBase* aFileHandle); - - bool - HasRunningFileHandles() - { - return !mFileHandleQueues.IsEmpty(); - } - - inline DelayedEnqueueInfo* - CreateDelayedEnqueueInfo(FileHandleBase* aFileHandle, - FileHelper* aFileHelper); - - void - LockFileForReading(const nsAString& aFileName) - { - mFilesReading.PutEntry(aFileName); - } - - void - LockFileForWriting(const nsAString& aFileName) - { - mFilesWriting.PutEntry(aFileName); - } - - bool - IsFileLockedForReading(const nsAString& aFileName) - { - return mFilesReading.Contains(aFileName); - } - - bool - IsFileLockedForWriting(const nsAString& aFileName) - { - return mFilesWriting.Contains(aFileName); - } - - private: - StorageInfo() - { - } - - nsTArray<nsRefPtr<FileHandleQueue>> mFileHandleQueues; - nsTArray<DelayedEnqueueInfo> mDelayedEnqueueInfos; - nsTHashtable<nsStringHashKey> mFilesReading; - nsTHashtable<nsStringHashKey> mFilesWriting; - }; - - struct StoragesCompleteCallback - { - nsTArray<nsCString> mStorageIds; - nsCOMPtr<nsIRunnable> mCallback; - }; - - FileService(); - ~FileService(); - - nsresult - Init(); - - nsresult - Cleanup(); - - bool - MaybeFireCallback(StoragesCompleteCallback& aCallback); - - nsRefPtr<nsThreadPool> mThreadPool; - nsClassHashtable<nsCStringHashKey, StorageInfo> mStorageInfos; - nsTArray<StoragesCompleteCallback> mCompleteCallbacks; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_FileService_h
deleted file mode 100644 --- a/dom/filehandle/FileStreamWrappers.cpp +++ /dev/null @@ -1,415 +0,0 @@ -/* -*- 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 "FileStreamWrappers.h" - -#include "FileHelper.h" -#include "MainThreadUtils.h" -#include "mozilla/Assertions.h" -#include "mozilla/Attributes.h" -#include "mozilla/ipc/InputStreamParams.h" -#include "MutableFile.h" -#include "nsDebug.h" -#include "nsError.h" -#include "nsIRunnable.h" -#include "nsISeekableStream.h" -#include "nsThreadUtils.h" -#include "nsQueryObject.h" - -#ifdef DEBUG -#include "nsXULAppAPI.h" -#endif - -namespace mozilla { -namespace dom { - -namespace { - -class ProgressRunnable final : public nsIRunnable -{ -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIRUNNABLE - - ProgressRunnable(FileHelper* aFileHelper, - uint64_t aProgress, - uint64_t aProgressMax) - : mFileHelper(aFileHelper), - mProgress(aProgress), - mProgressMax(aProgressMax) - { - } - -private: - ~ProgressRunnable() {} - - nsRefPtr<FileHelper> mFileHelper; - uint64_t mProgress; - uint64_t mProgressMax; -}; - -class CloseRunnable final : public nsIRunnable -{ -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIRUNNABLE - - explicit CloseRunnable(FileHelper* aFileHelper) - : mFileHelper(aFileHelper) - { } - -private: - ~CloseRunnable() {} - - nsRefPtr<FileHelper> mFileHelper; -}; - -class DestroyRunnable final : public nsIRunnable -{ -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIRUNNABLE - - explicit DestroyRunnable(FileHelper* aFileHelper) - : mFileHelper(aFileHelper) - { } - -private: - ~DestroyRunnable() {} - - nsRefPtr<FileHelper> mFileHelper; -}; - -} // namespace - -FileStreamWrapper::FileStreamWrapper(nsISupports* aFileStream, - FileHelper* aFileHelper, - uint64_t aOffset, - uint64_t aLimit, - uint32_t aFlags) -: mFileStream(aFileStream), - mFileHelper(aFileHelper), - mOffset(aOffset), - mLimit(aLimit), - mFlags(aFlags), - mFirstTime(true) -{ - NS_ASSERTION(mFileStream, "Must have a file stream!"); - NS_ASSERTION(mFileHelper, "Must have a file helper!"); -} - -FileStreamWrapper::~FileStreamWrapper() -{ - if (mFlags & NOTIFY_DESTROY) { - if (NS_IsMainThread()) { - mFileHelper->OnStreamDestroy(); - } - else { - nsCOMPtr<nsIRunnable> runnable = - new DestroyRunnable(mFileHelper); - - nsresult rv = NS_DispatchToMainThread(runnable); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to dispatch to the main thread!"); - } - } - } -} - -NS_IMPL_ISUPPORTS0(FileStreamWrapper) - -FileInputStreamWrapper::FileInputStreamWrapper(nsISupports* aFileStream, - FileHelper* aFileHelper, - uint64_t aOffset, - uint64_t aLimit, - uint32_t aFlags) -: FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags) -{ - mInputStream = do_QueryInterface(mFileStream); - NS_ASSERTION(mInputStream, "This should always succeed!"); -} - -NS_IMPL_ISUPPORTS_INHERITED(FileInputStreamWrapper, - FileStreamWrapper, - nsIInputStream, - nsIIPCSerializableInputStream) - -NS_IMETHODIMP -FileInputStreamWrapper::Close() -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - - if (mFlags & NOTIFY_CLOSE) { - nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper); - - if (NS_FAILED(NS_DispatchToMainThread(runnable))) { - NS_WARNING("Failed to dispatch to the main thread!"); - } - } - - mOffset = 0; - mLimit = 0; - - return NS_OK; -} - -NS_IMETHODIMP -FileInputStreamWrapper::Available(uint64_t* _retval) -{ - // Performing sync IO on the main thread is generally not allowed. - // However, the input stream wrapper is also used to track reads performed by - // other APIs like FileReader, XHR, etc. - // In that case nsInputStreamChannel::OpenContentStream() calls Available() - // before setting the content length property. This property is not important - // to perform reads properly, so we can just return NS_BASE_STREAM_CLOSED - // here. It causes OpenContentStream() to set the content length property to - // zero. - - if (NS_IsMainThread()) { - return NS_BASE_STREAM_CLOSED; - } - - return mInputStream->Available(_retval); -} - -NS_IMETHODIMP -FileInputStreamWrapper::Read(char* aBuf, uint32_t aCount, uint32_t* _retval) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - - nsresult rv; - - if (mFirstTime) { - mFirstTime = false; - - if (mOffset != UINT64_MAX) { - nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream); - if (seekable) { - rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset); - NS_ENSURE_SUCCESS(rv, rv); - } - } - - mOffset = 0; - } - - uint64_t max = mLimit - mOffset; - if (max == 0) { - *_retval = 0; - return NS_OK; - } - - if (aCount > max) { - aCount = max; - } - - rv = mInputStream->Read(aBuf, aCount, _retval); - NS_ENSURE_SUCCESS(rv, rv); - - mOffset += *_retval; - - if (mFlags & NOTIFY_PROGRESS) { - nsCOMPtr<nsIRunnable> runnable = - new ProgressRunnable(mFileHelper, mOffset, mLimit); - - rv = NS_DispatchToMainThread(runnable); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to dispatch to the main thread!"); - } - } - - return NS_OK; -} - -NS_IMETHODIMP -FileInputStreamWrapper::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, - uint32_t aCount, uint32_t* _retval) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -FileInputStreamWrapper::IsNonBlocking(bool* _retval) -{ - *_retval = false; - return NS_OK; -} - -void -FileInputStreamWrapper::Serialize(InputStreamParams& aParams, - FileDescriptorArray& /* aFDs */) -{ - MOZ_ASSERT(XRE_IsParentProcess()); - MOZ_ASSERT(NS_IsMainThread()); - - nsCOMPtr<nsIInputStream> thisStream = do_QueryObject(this); - - aParams = mozilla::ipc::SameProcessInputStreamParams( - reinterpret_cast<intptr_t>(thisStream.forget().take())); -} - -bool -FileInputStreamWrapper::Deserialize(const InputStreamParams& /* aParams */, - const FileDescriptorArray& /* aFDs */) -{ - MOZ_CRASH("Should never get here!"); -} - -FileOutputStreamWrapper::FileOutputStreamWrapper(nsISupports* aFileStream, - FileHelper* aFileHelper, - uint64_t aOffset, - uint64_t aLimit, - uint32_t aFlags) -: FileStreamWrapper(aFileStream, aFileHelper, aOffset, aLimit, aFlags) -{ - mOutputStream = do_QueryInterface(mFileStream); - NS_ASSERTION(mOutputStream, "This should always succeed!"); -} - -NS_IMPL_ISUPPORTS_INHERITED(FileOutputStreamWrapper, - FileStreamWrapper, - nsIOutputStream) - -NS_IMETHODIMP -FileOutputStreamWrapper::Close() -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - - nsresult rv = NS_OK; - - if (mFlags & NOTIFY_CLOSE) { - nsCOMPtr<nsIRunnable> runnable = new CloseRunnable(mFileHelper); - - if (NS_FAILED(NS_DispatchToMainThread(runnable))) { - NS_WARNING("Failed to dispatch to the main thread!"); - } - } - - mOffset = 0; - mLimit = 0; - - return rv; -} - -NS_IMETHODIMP -FileOutputStreamWrapper::Write(const char* aBuf, uint32_t aCount, - uint32_t* _retval) -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - - nsresult rv; - - if (mFirstTime) { - mFirstTime = false; - - nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mOutputStream); - if (seekable) { - if (mOffset == UINT64_MAX) { - rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0); - } - else { - rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset); - } - NS_ENSURE_SUCCESS(rv, rv); - } - - mOffset = 0; - } - - uint64_t max = mLimit - mOffset; - if (max == 0) { - *_retval = 0; - return NS_OK; - } - - if (aCount > max) { - aCount = max; - } - - rv = mOutputStream->Write(aBuf, aCount, _retval); - NS_ENSURE_SUCCESS(rv, rv); - - mOffset += *_retval; - - if (mFlags & NOTIFY_PROGRESS) { - nsCOMPtr<nsIRunnable> runnable = - new ProgressRunnable(mFileHelper, mOffset, mLimit); - - NS_DispatchToMainThread(runnable); - } - - return NS_OK; -} - -NS_IMETHODIMP -FileOutputStreamWrapper::Flush() -{ - return NS_OK; -} - -NS_IMETHODIMP -FileOutputStreamWrapper::WriteFrom(nsIInputStream* aFromStream, - uint32_t aCount, uint32_t* _retval) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -FileOutputStreamWrapper::WriteSegments(nsReadSegmentFun aReader, - void* aClosure, uint32_t aCount, - uint32_t* _retval) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -FileOutputStreamWrapper::IsNonBlocking(bool* _retval) -{ - *_retval = false; - return NS_OK; -} - -NS_IMPL_ISUPPORTS(ProgressRunnable, nsIRunnable) - -NS_IMETHODIMP -ProgressRunnable::Run() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - mFileHelper->OnStreamProgress(mProgress, mProgressMax); - mFileHelper = nullptr; - - return NS_OK; -} - -NS_IMPL_ISUPPORTS(CloseRunnable, nsIRunnable) - -NS_IMETHODIMP -CloseRunnable::Run() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - mFileHelper->OnStreamClose(); - mFileHelper = nullptr; - - return NS_OK; -} - -NS_IMPL_ISUPPORTS(DestroyRunnable, nsIRunnable) - -NS_IMETHODIMP -DestroyRunnable::Run() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - mFileHelper->OnStreamDestroy(); - mFileHelper = nullptr; - - return NS_OK; -} - -} // namespace dom -} // namespace mozilla
deleted file mode 100644 --- a/dom/filehandle/FileStreamWrappers.h +++ /dev/null @@ -1,102 +0,0 @@ -/* -*- 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_FileStreamWrappers_h -#define mozilla_dom_FileStreamWrappers_h - -#include "nsAutoPtr.h" -#include "nsCOMPtr.h" -#include "nsIInputStream.h" -#include "nsIOutputStream.h" -#include "nsIIPCSerializableInputStream.h" - -namespace mozilla { -namespace ipc { -class InputStreamParams; -} // namespace ipc - -namespace dom { - -class FileHelper; - -class FileStreamWrapper : public nsISupports -{ -public: - NS_DECL_THREADSAFE_ISUPPORTS - - FileStreamWrapper(nsISupports* aFileStream, - FileHelper* aFileHelper, - uint64_t aOffset, - uint64_t aLimit, - uint32_t aFlags); - - enum { - NOTIFY_PROGRESS = 1 << 0, - NOTIFY_CLOSE = 1 << 1, - NOTIFY_DESTROY = 1 << 2 - }; - -protected: - virtual ~FileStreamWrapper(); - - nsCOMPtr<nsISupports> mFileStream; - nsRefPtr<FileHelper> mFileHelper; - uint64_t mOffset; - uint64_t mLimit; - uint32_t mFlags; - bool mFirstTime; -}; - -class FileInputStreamWrapper : public FileStreamWrapper, - public nsIInputStream, - public nsIIPCSerializableInputStream -{ - typedef mozilla::ipc::InputStreamParams InputStreamParams; - -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIINPUTSTREAM - NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM - - FileInputStreamWrapper(nsISupports* aFileStream, - FileHelper* aFileHelper, - uint64_t aOffset, - uint64_t aLimit, - uint32_t aFlags); - -protected: - virtual ~FileInputStreamWrapper() - { } - -private: - nsCOMPtr<nsIInputStream> mInputStream; -}; - -class FileOutputStreamWrapper : public FileStreamWrapper, - public nsIOutputStream -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIOUTPUTSTREAM - - FileOutputStreamWrapper(nsISupports* aFileStream, - FileHelper* aFileHelper, - uint64_t aOffset, - uint64_t aLimit, - uint32_t aFlags); - -protected: - virtual ~FileOutputStreamWrapper() - { } - -private: - nsCOMPtr<nsIOutputStream> mOutputStream; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_FileStreamWrappers_h
deleted file mode 100644 --- a/dom/filehandle/MemoryStreams.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* -*- 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 "MemoryStreams.h" - -#include "nsAutoPtr.h" -#include "nsCOMPtr.h" -#include "nsDebug.h" -#include "nsError.h" -#include "nsStreamUtils.h" - -namespace mozilla { -namespace dom { - -// static -already_AddRefed<MemoryOutputStream> -MemoryOutputStream::Create(uint64_t aSize) -{ - NS_ASSERTION(aSize, "Passed zero size!"); - - NS_ENSURE_TRUE(aSize <= UINT32_MAX, nullptr); - - nsRefPtr<MemoryOutputStream> stream = new MemoryOutputStream(); - - char* dummy; - uint32_t length = stream->mData.GetMutableData(&dummy, aSize, fallible); - NS_ENSURE_TRUE(length == aSize, nullptr); - - return stream.forget(); -} - -NS_IMPL_ISUPPORTS(MemoryOutputStream, nsIOutputStream) - -NS_IMETHODIMP -MemoryOutputStream::Close() -{ - mData.Truncate(mOffset); - return NS_OK; -} - -NS_IMETHODIMP -MemoryOutputStream::Write(const char* aBuf, uint32_t aCount, uint32_t* _retval) -{ - return WriteSegments(NS_CopySegmentToBuffer, (char*)aBuf, aCount, _retval); -} - -NS_IMETHODIMP -MemoryOutputStream::Flush() -{ - return NS_OK; -} - -NS_IMETHODIMP -MemoryOutputStream::WriteFrom(nsIInputStream* aFromStream, uint32_t aCount, - uint32_t* _retval) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -MemoryOutputStream::WriteSegments(nsReadSegmentFun aReader, void* aClosure, - uint32_t aCount, uint32_t* _retval) -{ - NS_ASSERTION(mData.Length() >= mOffset, "Bad stream state!"); - - uint32_t maxCount = mData.Length() - mOffset; - if (maxCount == 0) { - *_retval = 0; - return NS_OK; - } - - if (aCount > maxCount) { - aCount = maxCount; - } - - nsresult rv = aReader(this, aClosure, mData.BeginWriting() + mOffset, 0, - aCount, _retval); - if (NS_SUCCEEDED(rv)) { - NS_ASSERTION(*_retval <= aCount, - "Reader should not read more than we asked it to read!"); - mOffset += *_retval; - } - - return NS_OK; -} - -NS_IMETHODIMP -MemoryOutputStream::IsNonBlocking(bool* _retval) -{ - *_retval = false; - return NS_OK; -} - -} // namespace dom -} // namespace mozilla
deleted file mode 100644 --- a/dom/filehandle/MemoryStreams.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- 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_MemoryStreams_h -#define mozilla_dom_MemoryStreams_h - -#include "nsIOutputStream.h" -#include "nsString.h" - -template <class> struct already_AddRefed; - -namespace mozilla { -namespace dom { - -class MemoryOutputStream : public nsIOutputStream -{ -public: - NS_DECL_THREADSAFE_ISUPPORTS - NS_DECL_NSIOUTPUTSTREAM - - static already_AddRefed<MemoryOutputStream> - Create(uint64_t aSize); - - const nsCString& - Data() const - { - return mData; - } - -private: - MemoryOutputStream() - : mOffset(0) - { } - - virtual ~MemoryOutputStream() - { } - - nsCString mData; - uint64_t mOffset; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_MemoryStreams_h
deleted file mode 100644 --- a/dom/filehandle/MetadataHelper.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* -*- 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 "MetadataHelper.h" - -#include "FileHandle.h" -#include "js/Value.h" -#include "js/RootingAPI.h" -#include "jsapi.h" -#include "js/Date.h" -#include "mozilla/dom/FileModeBinding.h" -#include "nsDebug.h" -#include "nsIFileStreams.h" -#include "nsIOutputStream.h" - -namespace mozilla { -namespace dom { - -nsresult -MetadataHelper::DoAsyncRun(nsISupports* aStream) -{ - bool readWrite = mFileHandle->mMode == FileMode::Readwrite; - - nsRefPtr<AsyncMetadataGetter> getter = - new AsyncMetadataGetter(aStream, mParams, readWrite); - - nsresult rv = getter->AsyncWork(this, nullptr); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -MetadataHelper::GetSuccessResult(JSContext* aCx, - JS::MutableHandle<JS::Value> aVal) -{ - JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx)); - NS_ENSURE_TRUE(obj, NS_ERROR_OUT_OF_MEMORY); - - if (mParams->SizeRequested()) { - JS::Rooted<JS::Value> val(aCx, JS_NumberValue(mParams->Size())); - - if (!JS_DefineProperty(aCx, obj, "size", val, JSPROP_ENUMERATE)) { - return NS_ERROR_FAILURE; - } - } - - if (mParams->LastModifiedRequested()) { - double msec = mParams->LastModified(); - JSObject *date = JS::NewDateObject(aCx, JS::TimeClip(msec)); - NS_ENSURE_TRUE(date, NS_ERROR_OUT_OF_MEMORY); - - JS::Rooted<JS::Value> dateRoot(aCx, JS::ObjectValue(*date)); - if (!JS_DefineProperty(aCx, obj, "lastModified", dateRoot, - JSPROP_ENUMERATE)) { - return NS_ERROR_FAILURE; - } - } - - aVal.setObject(*obj); - return NS_OK; -} - -nsresult -MetadataHelper::AsyncMetadataGetter::DoStreamWork(nsISupports* aStream) -{ - nsresult rv; - - if (mReadWrite) { - // Force a flush (so all pending writes are flushed to the disk and file - // metadata is updated too). - - nsCOMPtr<nsIOutputStream> ostream = do_QueryInterface(aStream); - NS_ASSERTION(ostream, "This should always succeed!"); - - rv = ostream->Flush(); - NS_ENSURE_SUCCESS(rv, rv); - } - - nsCOMPtr<nsIFileMetadata> metadata = do_QueryInterface(aStream); - - if (mParams->SizeRequested()) { - int64_t size; - rv = metadata->GetSize(&size); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ENSURE_TRUE(size >= 0, NS_ERROR_FAILURE); - - mParams->mSize = uint64_t(size); - } - - if (mParams->LastModifiedRequested()) { - int64_t lastModified; - rv = metadata->GetLastModified(&lastModified); - NS_ENSURE_SUCCESS(rv, rv); - - mParams->mLastModified = lastModified; - } - - return NS_OK; -} - -} // namespace dom -} // namespace mozilla
deleted file mode 100644 --- a/dom/filehandle/MetadataHelper.h +++ /dev/null @@ -1,118 +0,0 @@ -/* -*- 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_MetadataHelper_h -#define mozilla_dom_MetadataHelper_h - -#include "FileHelper.h" -#include "js/TypeDecls.h" -#include "mozilla/Attributes.h" -#include "mozilla/dom/AsyncHelper.h" -#include "nsAutoPtr.h" - -namespace mozilla { -namespace dom { - -class MetadataHelper; - -class MetadataParameters final -{ - friend class MetadataHelper; - -public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MetadataParameters) - - MetadataParameters(bool aSizeRequested, bool aLastModifiedRequested) - : mSizeRequested(aSizeRequested) - , mLastModifiedRequested(aLastModifiedRequested) - { - } - - bool - IsConfigured() const - { - return mSizeRequested || mLastModifiedRequested; - } - - bool - SizeRequested() const - { - return mSizeRequested; - } - - bool - LastModifiedRequested() const - { - return mLastModifiedRequested; - } - - uint64_t - Size() const - { - return mSize; - } - - int64_t - LastModified() const - { - return mLastModified; - } - -private: - // Private destructor, to discourage deletion outside of Release(): - ~MetadataParameters() - { - } - - uint64_t mSize; - int64_t mLastModified; - bool mSizeRequested; - bool mLastModifiedRequested; -}; - -class MetadataHelper : public FileHelper -{ -public: - MetadataHelper(FileHandleBase* aFileHandle, - FileRequestBase* aFileRequest, - MetadataParameters* aParams) - : FileHelper(aFileHandle, aFileRequest), - mParams(aParams) - { } - - nsresult - DoAsyncRun(nsISupports* aStream) override; - - nsresult - GetSuccessResult(JSContext* aCx, - JS::MutableHandle<JS::Value> aVal) override; - -protected: - class AsyncMetadataGetter : public AsyncHelper - { - public: - AsyncMetadataGetter(nsISupports* aStream, MetadataParameters* aParams, - bool aReadWrite) - : AsyncHelper(aStream), - mParams(aParams), mReadWrite(aReadWrite) - { } - - protected: - nsresult - DoStreamWork(nsISupports* aStream) override; - - private: - nsRefPtr<MetadataParameters> mParams; - bool mReadWrite; - }; - - nsRefPtr<MetadataParameters> mParams; -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_MetadataHelper_h
rename from dom/filehandle/MutableFile.cpp rename to dom/filehandle/MutableFileBase.cpp --- a/dom/filehandle/MutableFile.cpp +++ b/dom/filehandle/MutableFileBase.cpp @@ -1,52 +1,36 @@ /* -*- 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 "MutableFile.h" +#include "MutableFileBase.h" -#include "nsDebug.h" -#include "nsIFile.h" -#include "nsIFileStreams.h" -#include "nsIInputStream.h" -#include "nsNetUtil.h" +#include "ActorsChild.h" +#include "mozilla/Assertions.h" +#include "prthread.h" namespace mozilla { namespace dom { -MutableFileBase::MutableFileBase() +MutableFileBase::MutableFileBase(DEBUGONLY(PRThread* aOwningThread,) + BackgroundMutableFileChildBase* aActor) + : RefCountedThreadObject(DEBUGONLY(aOwningThread)) + , mBackgroundActor(aActor) { + AssertIsOnOwningThread(); + MOZ_ASSERT(aActor); } MutableFileBase::~MutableFileBase() { -} - -// virtual -already_AddRefed<nsISupports> -MutableFileBase::CreateStream(bool aReadOnly) -{ - nsresult rv; + AssertIsOnOwningThread(); - if (aReadOnly) { - nsCOMPtr<nsIInputStream> stream; - rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), mFile, -1, -1, - nsIFileInputStream::DEFER_OPEN); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } - return stream.forget(); + if (mBackgroundActor) { + mBackgroundActor->SendDeleteMeInternal(); + MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!"); } - - nsCOMPtr<nsIFileStream> stream; - rv = NS_NewLocalFileStream(getter_AddRefs(stream), mFile, -1, -1, - nsIFileStream::DEFER_OPEN); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } - return stream.forget(); } } // namespace dom } // namespace mozilla
rename from dom/filehandle/MutableFile.h rename to dom/filehandle/MutableFileBase.h --- a/dom/filehandle/MutableFile.h +++ b/dom/filehandle/MutableFileBase.h @@ -2,65 +2,75 @@ /* 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_MutableFile_h #define mozilla_dom_MutableFile_h -#include "nsCOMPtr.h" -#include "nsString.h" +#include "mozilla/dom/FileHandleCommon.h" +#include "nscore.h" -class nsIFile; -class nsIOfflineStorage; +template <class> struct already_AddRefed; +class nsISupports; +class nsString; +struct PRThread; namespace mozilla { namespace dom { -class FileService; +class BackgroundMutableFileChildBase; +class BlobImpl; +class File; +class FileHandleBase; /** * This class provides a base for MutableFile implementations. - * The subclasses can override implementation of IsInvalid and CreateStream. * (for example IDBMutableFile provides IndexedDB specific implementation). */ class MutableFileBase + : public RefCountedThreadObject { - friend class FileService; +protected: + BackgroundMutableFileChildBase* mBackgroundActor; public: - NS_IMETHOD_(MozExternalRefCountType) - AddRef() = 0; + BackgroundMutableFileChildBase* + GetBackgroundActor() const + { + AssertIsOnOwningThread(); - NS_IMETHOD_(MozExternalRefCountType) - Release() = 0; - - virtual bool - IsInvalid() - { - return false; + return mBackgroundActor; } - // A temporary method that will be removed along with nsIOfflineStorage - // interface. - virtual nsIOfflineStorage* - Storage() = 0; + void + ClearBackgroundActor() + { + AssertIsOnOwningThread(); + + mBackgroundActor = nullptr; + } - virtual already_AddRefed<nsISupports> - CreateStream(bool aReadOnly); + virtual const nsString& + Name() const = 0; + + virtual const nsString& + Type() const = 0; + virtual bool + IsInvalidated() = 0; + + virtual already_AddRefed<File> + CreateFileFor(BlobImpl* aBlobImpl, + FileHandleBase* aFileHandle) = 0; protected: - MutableFileBase(); + MutableFileBase(DEBUGONLY(PRThread* aOwningThread,) + BackgroundMutableFileChildBase* aActor); virtual ~MutableFileBase(); - - nsCOMPtr<nsIFile> mFile; - - nsCString mStorageId; - nsString mFileName; }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_MutableFile_h
new file mode 100644 --- /dev/null +++ b/dom/filehandle/PBackgroundFileHandle.ipdl @@ -0,0 +1,91 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PBackgroundFileRequest; +include protocol PBackgroundMutableFile; +include protocol PBlob; + +namespace mozilla { +namespace dom { + +struct FileRequestGetMetadataParams +{ + bool size; + bool lastModified; +}; + +struct FileRequestReadParams +{ + uint64_t offset; + uint64_t size; +}; + +struct FileRequestStringData +{ + nsCString string; +}; + +struct FileRequestBlobData +{ + PBlob blob; +}; + +union FileRequestData +{ + FileRequestStringData; + FileRequestBlobData; +}; + +struct FileRequestWriteParams +{ + uint64_t offset; + FileRequestData data; + uint64_t dataLength; +}; + +struct FileRequestTruncateParams +{ + uint64_t offset; +}; + +struct FileRequestFlushParams +{ +}; + +struct FileRequestGetFileParams +{ +}; + +union FileRequestParams +{ + FileRequestGetMetadataParams; + FileRequestReadParams; + FileRequestWriteParams; + FileRequestTruncateParams; + FileRequestFlushParams; + FileRequestGetFileParams; +}; + +protocol PBackgroundFileHandle +{ + manager PBackgroundMutableFile; + + manages PBackgroundFileRequest; + +parent: + DeleteMe(); + + Finish(); + Abort(); + + PBackgroundFileRequest(FileRequestParams params); + +child: + __delete__(); + + Complete(bool aborted); +}; + +} // namespace dom +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/dom/filehandle/PBackgroundFileRequest.ipdl @@ -0,0 +1,83 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PBackgroundFileHandle; +include protocol PBlob; + +using struct mozilla::void_t + from "ipc/IPCMessageUtils.h"; + +namespace mozilla { +namespace dom { + +union FileRequestSize +{ + void_t; + uint64_t; +}; + +union FileRequestLastModified +{ + void_t; + int64_t; +}; + +struct FileRequestMetadata +{ + FileRequestSize size; + FileRequestLastModified lastModified; +}; + +struct FileRequestGetMetadataResponse +{ + FileRequestMetadata metadata; +}; + +struct FileRequestReadResponse +{ + nsCString data; +}; + +struct FileRequestWriteResponse +{ +}; + +struct FileRequestTruncateResponse +{ +}; + +struct FileRequestFlushResponse +{ +}; + +struct FileRequestGetFileResponse +{ + PBlob file; + FileRequestMetadata metadata; +}; + +union FileRequestResponse +{ + nsresult; + FileRequestGetMetadataResponse; + FileRequestReadResponse; + FileRequestWriteResponse; + FileRequestTruncateResponse; + FileRequestFlushResponse; + FileRequestGetFileResponse; +}; + +protocol PBackgroundFileRequest +{ + manager PBackgroundFileHandle; + +child: + __delete__(FileRequestResponse response); + + Progress(uint64_t progress, + uint64_t progressMax); +}; + +} // namespace dom +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/dom/filehandle/PBackgroundMutableFile.ipdl @@ -0,0 +1,36 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PBackgroundFileHandle; +include protocol PBackgroundIDBDatabase; + +include "mozilla/dom/filehandle/SerializationHelpers.h"; + +using mozilla::dom::FileMode + from "mozilla/dom/FileModeBinding.h"; + +namespace mozilla { +namespace dom { + +sync protocol PBackgroundMutableFile +{ + manager PBackgroundIDBDatabase; + + manages PBackgroundFileHandle; + +parent: + DeleteMe(); + + PBackgroundFileHandle(FileMode mode); + + // Use only for testing! + sync GetFileId() + returns (int64_t fileId); + +child: + __delete__(); +}; + +} // namespace dom +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/dom/filehandle/SerializationHelpers.h @@ -0,0 +1,23 @@ +/* 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_filehandle_SerializationHelpers_h +#define mozilla_dom_filehandle_SerializationHelpers_h + +#include "ipc/IPCMessageUtils.h" + +#include "mozilla/dom/FileModeBinding.h" + +namespace IPC { + +template <> +struct ParamTraits<mozilla::dom::FileMode> : + public ContiguousEnumSerializer<mozilla::dom::FileMode, + mozilla::dom::FileMode::Readonly, + mozilla::dom::FileMode::EndGuard_> +{ }; + +} // namespace IPC + +#endif // mozilla_dom_filehandle_SerializationHelpers_h
--- a/dom/filehandle/moz.build +++ b/dom/filehandle/moz.build @@ -1,34 +1,40 @@ # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # 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/. +EXPORTS.mozilla.dom.filehandle += [ + 'ActorsChild.h', + 'ActorsParent.h', + 'SerializationHelpers.h', +] + EXPORTS.mozilla.dom += [ - 'AsyncHelper.h', - 'FileHandle.h', - 'FileHelper.h', - 'FileRequest.h', - 'FileService.h', - 'MetadataHelper.h', - 'MutableFile.h', + 'FileHandleBase.h', + 'FileHandleCommon.h', + 'FileHandleStorage.h', + 'FileRequestBase.h', + 'MutableFileBase.h', ] UNIFIED_SOURCES += [ - 'AsyncHelper.cpp', - 'FileHandle.cpp', - 'FileHelper.cpp', - 'FileRequest.cpp', - 'FileService.cpp', - 'FileStreamWrappers.cpp', - 'MemoryStreams.cpp', - 'MetadataHelper.cpp', - 'MutableFile.cpp', + 'ActorsChild.cpp', + 'ActorsParent.cpp', + 'FileHandleBase.cpp', + 'FileHandleCommon.cpp', + 'MutableFileBase.cpp', +] + +IPDL_SOURCES += [ + 'PBackgroundFileHandle.ipdl', + 'PBackgroundFileRequest.ipdl', + 'PBackgroundMutableFile.ipdl', ] include('/ipc/chromium/chromium-config.mozbuild') LOCAL_INCLUDES += [ '../base', ]
--- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -373,18 +373,17 @@ HTMLInputElement::nsFilePickerShownCallb return NS_OK; } int16_t mode; mFilePicker->GetMode(&mode); // Collect new selected filenames nsTArray<nsRefPtr<File>> newFiles; - if (mode == static_cast<int16_t>(nsIFilePicker::modeOpenMultiple) || - mode == static_cast<int16_t>(nsIFilePicker::modeGetFolder)) { + if (mode == static_cast<int16_t>(nsIFilePicker::modeOpenMultiple)) { nsCOMPtr<nsISimpleEnumerator> iter; nsresult rv = mFilePicker->GetDomfiles(getter_AddRefs(iter)); NS_ENSURE_SUCCESS(rv, rv); if (!iter) { return NS_OK; } @@ -396,17 +395,18 @@ HTMLInputElement::nsFilePickerShownCallb nsCOMPtr<nsIDOMBlob> domBlob = do_QueryInterface(tmp); MOZ_ASSERT(domBlob, "Null file object from FilePicker's file enumerator?"); if (domBlob) { newFiles.AppendElement(static_cast<File*>(domBlob.get())); } } } else { - MOZ_ASSERT(mode == static_cast<int16_t>(nsIFilePicker::modeOpen)); + MOZ_ASSERT(mode == static_cast<int16_t>(nsIFilePicker::modeOpen) || + mode == static_cast<int16_t>(nsIFilePicker::modeGetFolder)); nsCOMPtr<nsISupports> tmp; nsresult rv = mFilePicker->GetDomfile(getter_AddRefs(tmp)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(tmp); if (blob) { nsRefPtr<File> file = static_cast<Blob*>(blob.get())->ToFile(); newFiles.AppendElement(file);
--- a/dom/indexedDB/ActorsChild.cpp +++ b/dom/indexedDB/ActorsChild.cpp @@ -2,21 +2,21 @@ /* 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 "ActorsChild.h" #include "BackgroundChildImpl.h" -#include "FileManager.h" #include "IDBDatabase.h" #include "IDBEvents.h" #include "IDBFactory.h" #include "IDBIndex.h" +#include "IDBMutableFile.h" #include "IDBObjectStore.h" #include "IDBMutableFile.h" #include "IDBRequest.h" #include "IDBTransaction.h" #include "IndexedDatabase.h" #include "IndexedDatabaseInlines.h" #include "mozilla/BasicEvents.h" #include "mozilla/Maybe.h" @@ -592,52 +592,93 @@ protected: void ConvertActorsToBlobs(IDBDatabase* aDatabase, const SerializedStructuredCloneReadInfo& aCloneReadInfo, nsTArray<StructuredCloneFile>& aFiles) { MOZ_ASSERT(aFiles.IsEmpty()); - const nsTArray<PBlobChild*>& blobs = aCloneReadInfo.blobsChild(); - const nsTArray<intptr_t>& fileInfos = aCloneReadInfo.fileInfos(); - - MOZ_ASSERT_IF(IndexedDatabaseManager::IsMainProcess(), - blobs.Length() == fileInfos.Length()); - MOZ_ASSERT_IF(!IndexedDatabaseManager::IsMainProcess(), fileInfos.IsEmpty()); + const nsTArray<BlobOrMutableFile>& blobs = aCloneReadInfo.blobs(); if (!blobs.IsEmpty()) { const uint32_t count = blobs.Length(); aFiles.SetCapacity(count); for (uint32_t index = 0; index < count; index++) { - BlobChild* actor = static_cast<BlobChild*>(blobs[index]); - - nsRefPtr<BlobImpl> blobImpl = actor->GetBlobImpl(); - MOZ_ASSERT(blobImpl); - - nsRefPtr<Blob> blob = Blob::Create(aDatabase->GetOwner(), blobImpl); - - nsRefPtr<FileInfo> fileInfo; - if (!fileInfos.IsEmpty()) { - fileInfo = dont_AddRef(reinterpret_cast<FileInfo*>(fileInfos[index])); - - MOZ_ASSERT(fileInfo); - MOZ_ASSERT(fileInfo->Id() > 0); - - blob->AddFileInfo(fileInfo); + const BlobOrMutableFile& blobOrMutableFile = blobs[index]; + + switch (blobOrMutableFile.type()) { + case BlobOrMutableFile::TPBlobChild: { + auto* actor = + static_cast<BlobChild*>(blobOrMutableFile.get_PBlobChild()); + + nsRefPtr<BlobImpl> blobImpl = actor->GetBlobImpl(); + MOZ_ASSERT(blobImpl); + + nsRefPtr<Blob> blob = Blob::Create(aDatabase->GetOwner(), blobImpl); + + aDatabase->NoteReceivedBlob(blob); + + StructuredCloneFile* file = aFiles.AppendElement(); + MOZ_ASSERT(file); + + file->mMutable = false; + file->mBlob.swap(blob); + + break; + } + + case BlobOrMutableFile::TNullableMutableFile: { + const NullableMutableFile& nullableMutableFile = + blobOrMutableFile.get_NullableMutableFile(); + + switch (nullableMutableFile.type()) { + case NullableMutableFile::Tnull_t: { + StructuredCloneFile* file = aFiles.AppendElement(); + MOZ_ASSERT(file); + + file->mMutable = true; + + break; + } + + case NullableMutableFile::TPBackgroundMutableFileChild: { + auto* actor = + static_cast<BackgroundMutableFileChild*>( + nullableMutableFile.get_PBackgroundMutableFileChild()); + MOZ_ASSERT(actor); + + actor->EnsureDOMObject(); + + auto* mutableFile = + static_cast<IDBMutableFile*>(actor->GetDOMObject()); + MOZ_ASSERT(mutableFile); + + StructuredCloneFile* file = aFiles.AppendElement(); + MOZ_ASSERT(file); + + file->mMutable = true; + file->mMutableFile = mutableFile; + + actor->ReleaseDOMObject(); + + break; + } + + default: + MOZ_CRASH("Should never get here!"); + } + + break; + } + + default: + MOZ_CRASH("Should never get here!"); } - - aDatabase->NoteReceivedBlob(blob); - - StructuredCloneFile* file = aFiles.AppendElement(); - MOZ_ASSERT(file); - - file->mBlob.swap(blob); - file->mFileInfo.swap(fileInfo); } } } void DispatchErrorEvent(IDBRequest* aRequest, nsresult aErrorCode, IDBTransaction* aTransaction = nullptr, @@ -1120,16 +1161,23 @@ BackgroundFactoryChild::AssertIsOnOwning { MOZ_ASSERT(mOwningThread); bool current; MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t))); MOZ_ASSERT(current); } +nsIEventTarget* +BackgroundFactoryChild::OwningThread() const +{ + MOZ_ASSERT(mOwningThread); + return mOwningThread; +} + #endif // DEBUG void BackgroundFactoryChild::SendDeleteMeInternal() { AssertIsOnOwningThread(); if (mFactory) { @@ -1521,17 +1569,17 @@ BackgroundDatabaseChild::EnsureDOMObject if (mTemporaryStrongDatabase) { MOZ_ASSERT(!mSpec); return; } MOZ_ASSERT(mSpec); - auto request = mOpenRequestActor->GetDOMObject(); + auto request = mOpenRequestActor->GetOpenDBRequest(); MOZ_ASSERT(request); auto factory = static_cast<BackgroundFactoryChild*>(Manager())->GetDOMObject(); MOZ_ASSERT(factory); mTemporaryStrongDatabase = IDBDatabase::Create(request, factory, this, mSpec); @@ -1588,16 +1636,34 @@ BackgroundDatabaseChild::DeallocPBackgro { AssertIsOnOwningThread(); MOZ_ASSERT(aActor); delete aActor; return true; } +PBackgroundIDBDatabaseRequestChild* +BackgroundDatabaseChild::AllocPBackgroundIDBDatabaseRequestChild( + const DatabaseRequestParams& aParams) +{ + MOZ_CRASH("PBackgroundIDBDatabaseRequestChild actors should be manually " + "constructed!"); +} + +bool +BackgroundDatabaseChild::DeallocPBackgroundIDBDatabaseRequestChild( + PBackgroundIDBDatabaseRequestChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast<BackgroundDatabaseRequestChild*>(aActor); + return true; +} + PBackgroundIDBTransactionChild* BackgroundDatabaseChild::AllocPBackgroundIDBTransactionChild( const nsTArray<nsString>& aObjectStoreNames, const Mode& aMode) { MOZ_CRASH("PBackgroundIDBTransactionChild actors should be manually " "constructed!"); } @@ -1693,16 +1759,44 @@ BackgroundDatabaseChild::DeallocPBackgro PBackgroundIDBVersionChangeTransactionChild* aActor) { MOZ_ASSERT(aActor); delete static_cast<BackgroundVersionChangeTransactionChild*>(aActor); return true; } +PBackgroundMutableFileChild* +BackgroundDatabaseChild::AllocPBackgroundMutableFileChild(const nsString& aName, + const nsString& aType) +{ + AssertIsOnOwningThread(); + +#ifdef DEBUG + nsCOMPtr<nsIThread> owningThread = do_QueryInterface(OwningThread()); + + PRThread* owningPRThread; + owningThread->GetPRThread(&owningPRThread); +#endif + + return new BackgroundMutableFileChild(DEBUGONLY(owningPRThread,) + aName, + aType); +} + +bool +BackgroundDatabaseChild::DeallocPBackgroundMutableFileChild( + PBackgroundMutableFileChild* aActor) +{ + MOZ_ASSERT(aActor); + + delete static_cast<BackgroundMutableFileChild*>(aActor); + return true; +} + bool BackgroundDatabaseChild::RecvVersionChange(const uint64_t& aOldVersion, const NullableVersion& aNewVersion) { AssertIsOnOwningThread(); MaybeCollectGarbageOnIPCMessage(); @@ -1786,16 +1880,101 @@ BackgroundDatabaseChild::RecvInvalidate( if (mDatabase) { mDatabase->Invalidate(); } return true; } /******************************************************************************* + * BackgroundDatabaseRequestChild + ******************************************************************************/ + +BackgroundDatabaseRequestChild::BackgroundDatabaseRequestChild( + IDBDatabase* aDatabase, + IDBRequest* aRequest) + : BackgroundRequestChildBase(aRequest) + , mDatabase(aDatabase) +{ + // Can't assert owning thread here because IPDL has not yet set our manager! + MOZ_ASSERT(aDatabase); + aDatabase->AssertIsOnOwningThread(); + MOZ_ASSERT(aRequest); + + MOZ_COUNT_CTOR(indexedDB::BackgroundDatabaseRequestChild); +} + +BackgroundDatabaseRequestChild::~BackgroundDatabaseRequestChild() +{ + MOZ_COUNT_DTOR(indexedDB::BackgroundDatabaseRequestChild); +} + +bool +BackgroundDatabaseRequestChild::HandleResponse(nsresult aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(NS_FAILED(aResponse)); + MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB); + + mRequest->Reset(); + + DispatchErrorEvent(mRequest, aResponse); + + return true; +} + +bool +BackgroundDatabaseRequestChild::HandleResponse( + const CreateFileRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + + mRequest->Reset(); + + auto mutableFileActor = + static_cast<BackgroundMutableFileChild*>(aResponse.mutableFileChild()); + MOZ_ASSERT(mutableFileActor); + + mutableFileActor->EnsureDOMObject(); + + auto mutableFile = + static_cast<IDBMutableFile*>(mutableFileActor->GetDOMObject()); + MOZ_ASSERT(mutableFile); + + ResultHelper helper(mRequest, nullptr, mutableFile); + + DispatchSuccessEvent(&helper); + + mutableFileActor->ReleaseDOMObject(); + + return true; +} + +bool +BackgroundDatabaseRequestChild::Recv__delete__( + const DatabaseRequestResponse& aResponse) +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mRequest); + + switch (aResponse.type()) { + case DatabaseRequestResponse::Tnsresult: + return HandleResponse(aResponse.get_nsresult()); + + case DatabaseRequestResponse::TCreateFileRequestResponse: + return HandleResponse(aResponse.get_CreateFileRequestResponse()); + + default: + MOZ_CRASH("Unknown response type!"); + } + + MOZ_CRASH("Should never get here!"); +} + +/******************************************************************************* * BackgroundTransactionBase ******************************************************************************/ BackgroundTransactionBase::BackgroundTransactionBase() : mTransaction(nullptr) { MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionBase); } @@ -2090,16 +2269,50 @@ BackgroundVersionChangeTransactionChild: { MOZ_ASSERT(aActor); delete static_cast<BackgroundCursorChild*>(aActor); return true; } /******************************************************************************* + * BackgroundMutableFileChild + ******************************************************************************/ + +BackgroundMutableFileChild::BackgroundMutableFileChild( + DEBUGONLY(PRThread* aOwningThread,) + const nsAString& aName, + const nsAString& aType) + : BackgroundMutableFileChildBase(DEBUGONLY(aOwningThread)) + , mName(aName) + , mType(aType) +{ + // Can't assert owning thread here because IPDL has not yet set our manager! + MOZ_COUNT_CTOR(indexedDB::BackgroundMutableFileChild); +} + +BackgroundMutableFileChild::~BackgroundMutableFileChild() +{ + MOZ_COUNT_DTOR(indexedDB::BackgroundMutableFileChild); +} + +already_AddRefed<MutableFileBase> +BackgroundMutableFileChild::CreateMutableFile() +{ + auto database = + static_cast<BackgroundDatabaseChild*>(Manager())->GetDOMObject(); + MOZ_ASSERT(database); + + nsRefPtr<IDBMutableFile> mutableFile = + new IDBMutableFile(database, this, mName, mType); + + return mutableFile.forget(); +} + +/******************************************************************************* * BackgroundRequestChild ******************************************************************************/ BackgroundRequestChild::BackgroundRequestChild(IDBRequest* aRequest) : BackgroundRequestChildBase(aRequest) , mTransaction(aRequest->GetTransaction()) { MOZ_ASSERT(mTransaction); @@ -2112,26 +2325,16 @@ BackgroundRequestChild::~BackgroundReque { AssertIsOnOwningThread(); MOZ_ASSERT(!mTransaction); MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChild); } void -BackgroundRequestChild::HoldFileInfosUntilComplete( - nsTArray<nsRefPtr<FileInfo>>& aFileInfos) -{ - AssertIsOnOwningThread(); - MOZ_ASSERT(mFileInfos.IsEmpty()); - - mFileInfos.SwapElements(aFileInfos); -} - -void BackgroundRequestChild::HandleResponse(nsresult aResponse) { AssertIsOnOwningThread(); MOZ_ASSERT(NS_FAILED(aResponse)); MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB); MOZ_ASSERT(mTransaction); DispatchErrorEvent(mRequest, aResponse, mTransaction); @@ -2163,17 +2366,16 @@ BackgroundRequestChild::HandleResponse( { AssertIsOnOwningThread(); // XXX Fix this somehow... auto& serializedCloneInfo = const_cast<SerializedStructuredCloneReadInfo&>(aResponse); StructuredCloneReadInfo cloneReadInfo(Move(serializedCloneInfo)); - cloneReadInfo.mDatabase = mTransaction->Database(); ConvertActorsToBlobs(mTransaction->Database(), aResponse, cloneReadInfo.mFiles); ResultHelper helper(mRequest, mTransaction, &cloneReadInfo); DispatchSuccessEvent(&helper); @@ -2198,18 +2400,16 @@ BackgroundRequestChild::HandleResponse( // XXX Fix this somehow... auto& serializedCloneInfo = const_cast<SerializedStructuredCloneReadInfo&>(aResponse[index]); StructuredCloneReadInfo* cloneReadInfo = cloneReadInfos.AppendElement(); *cloneReadInfo = Move(serializedCloneInfo); - cloneReadInfo->mDatabase = mTransaction->Database(); - ConvertActorsToBlobs(database, serializedCloneInfo, cloneReadInfo->mFiles); } } ResultHelper helper(mRequest, mTransaction, &cloneReadInfos); @@ -2809,36 +3009,16 @@ BackgroundCursorChild::RecvResponse(cons MOZ_CRASH("Should never get here!"); } mTransaction->OnRequestFinished(/* aActorDestroyedNormally */ true); return true; } -// XXX This doesn't belong here. However, we're not yet porting MutableFile -// stuff to PBackground so this is necessary for the time being. -void -DispatchMutableFileResult(IDBRequest* aRequest, - nsresult aResultCode, - IDBMutableFile* aMutableFile) -{ - MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess()); - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aRequest); - MOZ_ASSERT_IF(NS_SUCCEEDED(aResultCode), aMutableFile); - - if (NS_SUCCEEDED(aResultCode)) { - ResultHelper helper(aRequest, nullptr, aMutableFile); - DispatchSuccessEvent(&helper); - } else { - DispatchErrorEvent(aRequest, aResultCode); - } -} - NS_IMPL_ISUPPORTS(BackgroundCursorChild::DelayedActionRunnable, nsIRunnable, nsICancelableRunnable) NS_IMETHODIMP BackgroundCursorChild:: DelayedActionRunnable::Run() {
--- a/dom/indexedDB/ActorsChild.h +++ b/dom/indexedDB/ActorsChild.h @@ -5,18 +5,20 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_indexeddb_actorschild_h__ #define mozilla_dom_indexeddb_actorschild_h__ #include "IDBTransaction.h" #include "js/RootingAPI.h" #include "mozilla/Attributes.h" +#include "mozilla/dom/filehandle/ActorsChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBCursorChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseChild.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseRequestChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBRequestChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" #include "mozilla/dom/indexedDB/PBackgroundIDBTransactionChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionChild.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" @@ -31,17 +33,16 @@ namespace ipc { class BackgroundChildImpl; } // namespace ipc namespace dom { namespace indexedDB { -class FileInfo; class IDBCursor; class IDBDatabase; class IDBFactory; class IDBMutableFile; class IDBOpenDBRequest; class IDBRequest; class Key; class PermissionRequestChild; @@ -150,21 +151,25 @@ class BackgroundFactoryChild final IDBFactory* mFactory; #ifdef DEBUG nsCOMPtr<nsIEventTarget> mOwningThread; #endif public: +#ifdef DEBUG + void + AssertIsOnOwningThread() const; + + nsIEventTarget* + OwningThread() const; +#else void AssertIsOnOwningThread() const -#ifdef DEBUG - ; -#else { } #endif IDBFactory* GetDOMObject() const { AssertIsOnOwningThread(); return mFactory; @@ -316,16 +321,24 @@ class BackgroundDatabaseChild final public: void AssertIsOnOwningThread() const { static_cast<BackgroundFactoryChild*>(Manager())->AssertIsOnOwningThread(); } +#ifdef DEBUG + nsIEventTarget* + OwningThread() const + { + return static_cast<BackgroundFactoryChild*>(Manager())->OwningThread(); + } +#endif + const DatabaseSpec* Spec() const { AssertIsOnOwningThread(); return mSpec; } IDBDatabase* @@ -360,16 +373,25 @@ private: AllocPBackgroundIDBDatabaseFileChild(PBlobChild* aBlobChild) override; virtual bool DeallocPBackgroundIDBDatabaseFileChild( PBackgroundIDBDatabaseFileChild* aActor) override; + virtual PBackgroundIDBDatabaseRequestChild* + AllocPBackgroundIDBDatabaseRequestChild(const DatabaseRequestParams& aParams) + override; + + virtual bool + DeallocPBackgroundIDBDatabaseRequestChild( + PBackgroundIDBDatabaseRequestChild* aActor) + override; + virtual PBackgroundIDBTransactionChild* AllocPBackgroundIDBTransactionChild( const nsTArray<nsString>& aObjectStoreNames, const Mode& aMode) override; virtual bool DeallocPBackgroundIDBTransactionChild(PBackgroundIDBTransactionChild* aActor) @@ -392,28 +414,64 @@ private: const int64_t& aNextIndexId) override; virtual bool DeallocPBackgroundIDBVersionChangeTransactionChild( PBackgroundIDBVersionChangeTransactionChild* aActor) override; + virtual PBackgroundMutableFileChild* + AllocPBackgroundMutableFileChild(const nsString& aName, + const nsString& aType) override; + + virtual bool + DeallocPBackgroundMutableFileChild(PBackgroundMutableFileChild* aActor) + override; + virtual bool RecvVersionChange(const uint64_t& aOldVersion, const NullableVersion& aNewVersion) override; virtual bool RecvInvalidate() override; bool SendDeleteMe() = delete; }; +class BackgroundDatabaseRequestChild final + : public BackgroundRequestChildBase + , public PBackgroundIDBDatabaseRequestChild +{ + friend class BackgroundDatabaseChild; + friend class IDBDatabase; + + nsRefPtr<IDBDatabase> mDatabase; + +private: + // Only created by IDBDatabase. + BackgroundDatabaseRequestChild(IDBDatabase* aDatabase, + IDBRequest* aRequest); + + // Only destroyed by BackgroundDatabaseChild. + ~BackgroundDatabaseRequestChild(); + + bool + HandleResponse(nsresult aResponse); + + bool + HandleResponse(const CreateFileRequestResponse& aResponse); + + // IPDL methods are only called by IPDL. + virtual bool + Recv__delete__(const DatabaseRequestResponse& aResponse) override; +}; + class BackgroundVersionChangeTransactionChild; class BackgroundTransactionBase { friend class BackgroundVersionChangeTransactionChild; // mTemporaryStrongTransaction is strong and is only valid until the end of // NoteComplete() member function or until the NoteActorDestroyed() member @@ -560,30 +618,47 @@ private: virtual bool DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor) override; bool SendDeleteMe() = delete; }; +class BackgroundMutableFileChild final + : public mozilla::dom::BackgroundMutableFileChildBase +{ + friend class BackgroundDatabaseChild; + + nsString mName; + nsString mType; + +private: + // Only constructed by BackgroundDatabaseChild. + BackgroundMutableFileChild(DEBUGONLY(PRThread* aOwningThread,) + const nsAString& aName, + const nsAString& aType); + + // Only destroyed by BackgroundDatabaseChild. + ~BackgroundMutableFileChild(); + + // BackgroundMutableFileChildBase + virtual already_AddRefed<MutableFileBase> + CreateMutableFile() override; +}; + class BackgroundRequestChild final : public BackgroundRequestChildBase , public PBackgroundIDBRequestChild { friend class BackgroundTransactionChild; friend class BackgroundVersionChangeTransactionChild; friend class IDBTransaction; nsRefPtr<IDBTransaction> mTransaction; - nsTArray<nsRefPtr<FileInfo>> mFileInfos; - -public: - void - HoldFileInfosUntilComplete(nsTArray<nsRefPtr<FileInfo>>& aFileInfos); private: // Only created by IDBTransaction. explicit BackgroundRequestChild(IDBRequest* aRequest); // Only destroyed by BackgroundTransactionChild or // BackgroundVersionChangeTransactionChild. @@ -749,20 +824,13 @@ private: // Force callers to use SendContinueInternal. bool SendContinue(const CursorRequestParams& aParams, const Key& aKey) = delete; bool SendDeleteMe() = delete; }; -// XXX This doesn't belong here. However, we're not yet porting MutableFile -// stuff to PBackground so this is necessary for the time being. -void -DispatchMutableFileResult(IDBRequest* aRequest, - nsresult aResultCode, - IDBMutableFile* aMutableFile); - } // namespace indexedDB } // namespace dom } // namespace mozilla #endif // mozilla_dom_indexeddb_actorschild_h__
--- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -27,22 +27,23 @@ #include "mozilla/Maybe.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" #include "mozilla/storage.h" #include "mozilla/unused.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/File.h" -#include "mozilla/dom/FileService.h" #include "mozilla/dom/StructuredCloneTags.h" #include "mozilla/dom/TabParent.h" +#include "mozilla/dom/filehandle/ActorsParent.h" #include "mozilla/dom/indexedDB/PBackgroundIDBCursorParent.h" #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h" #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileParent.h" +#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseRequestParent.h" #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryParent.h" #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestParent.h" #include "mozilla/dom/indexedDB/PBackgroundIDBRequestParent.h" #include "mozilla/dom/indexedDB/PBackgroundIDBTransactionParent.h" #include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionParent.h" #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestParent.h" #include "mozilla/dom/ipc/BlobParent.h" #include "mozilla/dom/quota/Client.h" @@ -79,16 +80,17 @@ #include "nsISupports.h" #include "nsISupportsImpl.h" #include "nsISupportsPriority.h" #include "nsIThread.h" #include "nsITimer.h" #include "nsIURI.h" #include "nsNetUtil.h" #include "nsPrintfCString.h" +#include "nsQueryObject.h" #include "nsRefPtrHashtable.h" #include "nsString.h" #include "nsThreadPool.h" #include "nsThreadUtils.h" #include "nsXPCOMCID.h" #include "PermissionRequestBase.h" #include "ProfilerHelpers.h" #include "prsystem.h" @@ -108,48 +110,52 @@ MOZ_LOG(IndexedDatabaseManager::GetLoggingModule(), \ LogLevel::Debug, \ _args ) #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK) #define IDB_MOBILE #endif +#define BLOB_IMPL_STORED_FILE_IID \ + {0x6b505c84, 0x2c60, 0x4ffb, {0x8b, 0x91, 0xfe, 0x22, 0xb1, 0xec, 0x75, 0xe2}} + namespace mozilla { namespace dom { namespace indexedDB { using namespace mozilla::dom::quota; using namespace mozilla::ipc; namespace { class ConnectionPool; class Cursor; class Database; struct DatabaseActorInfo; class DatabaseLoggingInfo; class DatabaseFile; class Factory; +class MutableFile; class OpenDatabaseOp; class TransactionBase; class TransactionDatabaseOperationBase; class VersionChangeTransaction; /******************************************************************************* * Constants ******************************************************************************/ // If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major // schema version.