diff --git a/accessible/base/nsAccessibilityService.cpp b/accessible/base/nsAccessibilityService.cpp index 29bda6e86e..429855f395 100644 --- a/accessible/base/nsAccessibilityService.cpp +++ b/accessible/base/nsAccessibilityService.cpp @@ -1239,7 +1239,7 @@ nsAccessibilityService::Init() logging::CheckEnv(); #endif - if (XRE_GetProcessType() == GeckoProcessType_Default) + if (XRE_IsParentProcess()) gApplicationAccessible = new ApplicationAccessibleWrap(); else gApplicationAccessible = new ApplicationAccessible(); @@ -1254,7 +1254,7 @@ nsAccessibilityService::Init() gIsShutdown = false; // Now its safe to start platform accessibility. - if (XRE_GetProcessType() == GeckoProcessType_Default) + if (XRE_IsParentProcess()) PlatformInit(); return true; @@ -1297,7 +1297,7 @@ nsAccessibilityService::Shutdown() gIsShutdown = true; - if (XRE_GetProcessType() == GeckoProcessType_Default) + if (XRE_IsParentProcess()) PlatformShutdown(); gApplicationAccessible->Shutdown(); diff --git a/accessible/base/nsAccessibilityService.h b/accessible/base/nsAccessibilityService.h index 75091fc216..5c1a3c9633 100644 --- a/accessible/base/nsAccessibilityService.h +++ b/accessible/base/nsAccessibilityService.h @@ -282,7 +282,7 @@ IPCAccessibilityActive() #ifdef MOZ_B2G return false; #else - return XRE_GetProcessType() == GeckoProcessType_Content && + return XRE_IsContentProcess() && mozilla::Preferences::GetBool("accessibility.ipc_architecture.enabled", true); #endif } diff --git a/accessible/base/nsCoreUtils.cpp b/accessible/base/nsCoreUtils.cpp index 2b427e39ef..4e9b2001c4 100644 --- a/accessible/base/nsCoreUtils.cpp +++ b/accessible/base/nsCoreUtils.cpp @@ -425,7 +425,7 @@ nsCoreUtils::IsTabDocument(nsIDocument* aDocumentNode) treeItem->GetParent(getter_AddRefs(parentTreeItem)); // Tab document running in own process doesn't have parent. - if (XRE_GetProcessType() == GeckoProcessType_Content) + if (XRE_IsContentProcess()) return !parentTreeItem; // Parent of docshell for tab document running in chrome process is root. diff --git a/accessible/windows/msaa/nsWinUtils.cpp b/accessible/windows/msaa/nsWinUtils.cpp index c6eabf1f58..545c7c865b 100644 --- a/accessible/windows/msaa/nsWinUtils.cpp +++ b/accessible/windows/msaa/nsWinUtils.cpp @@ -64,7 +64,7 @@ nsWinUtils::MaybeStartWindowEmulation() if (Compatibility::IsJAWS() || Compatibility::IsWE() || Compatibility::IsDolphin() || - XRE_GetProcessType() == GeckoProcessType_Content) { + XRE_IsContentProcess()) { RegisterNativeWindow(kClassNameTabContent); sHWNDCache = new nsRefPtrHashtable, DocAccessible>(2); return true; diff --git a/b2g/components/test/unit/test_logshake_gonk.js b/b2g/components/test/unit/test_logshake_gonk.js index 887d237302..60cfddc397 100644 --- a/b2g/components/test/unit/test_logshake_gonk.js +++ b/b2g/components/test/unit/test_logshake_gonk.js @@ -52,8 +52,6 @@ add_test(function setup_sdcard() { let vol = volumeService.getVolumeByName(volName); ok(vol, "volume shouldn't be null"); equal(volName, vol.name, "name"); - - volumeService.SetFakeVolumeState(volName, Ci.nsIVolume.STATE_MOUNTED); equal(Ci.nsIVolume.STATE_MOUNTED, vol.state, "state"); run_next_test(); diff --git a/caps/DomainPolicy.cpp b/caps/DomainPolicy.cpp index a2ec3e34f6..e1a6a82fb6 100644 --- a/caps/DomainPolicy.cpp +++ b/caps/DomainPolicy.cpp @@ -22,7 +22,7 @@ static nsresult BroadcastDomainSetChange(DomainSetType aSetType, DomainSetChangeType aChangeType, nsIURI* aDomain = nullptr) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default, + MOZ_ASSERT(XRE_IsParentProcess(), "DomainPolicy should only be exposed to the chrome process."); nsTArray parents; @@ -45,7 +45,7 @@ DomainPolicy::DomainPolicy() : mBlacklist(new DomainSet(BLACKLIST)) , mWhitelist(new DomainSet(WHITELIST)) , mSuperWhitelist(new DomainSet(SUPER_WHITELIST)) { - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { BroadcastDomainSetChange(NO_TYPE, ACTIVATE_POLICY); } } @@ -112,7 +112,7 @@ DomainPolicy::Deactivate() if (ssm) { ssm->DeactivateDomainPolicy(); } - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { BroadcastDomainSetChange(NO_TYPE, DEACTIVATE_POLICY); } return NS_OK; @@ -170,7 +170,7 @@ DomainSet::Add(nsIURI* aDomain) nsCOMPtr clone = GetCanonicalClone(aDomain); NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE); mHashTable.PutEntry(clone); - if (XRE_GetProcessType() == GeckoProcessType_Default) + if (XRE_IsParentProcess()) return BroadcastDomainSetChange(mType, ADD_DOMAIN, aDomain); return NS_OK; @@ -182,7 +182,7 @@ DomainSet::Remove(nsIURI* aDomain) nsCOMPtr clone = GetCanonicalClone(aDomain); NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE); mHashTable.RemoveEntry(clone); - if (XRE_GetProcessType() == GeckoProcessType_Default) + if (XRE_IsParentProcess()) return BroadcastDomainSetChange(mType, REMOVE_DOMAIN, aDomain); return NS_OK; @@ -192,7 +192,7 @@ NS_IMETHODIMP DomainSet::Clear() { mHashTable.Clear(); - if (XRE_GetProcessType() == GeckoProcessType_Default) + if (XRE_IsParentProcess()) return BroadcastDomainSetChange(mType, CLEAR_DOMAINS); return NS_OK; diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp index 8e53183017..cafb5e4dc9 100644 --- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -1273,7 +1273,7 @@ nsScriptSecurityManager::~nsScriptSecurityManager(void) // ContentChild might hold a reference to the domain policy, // and it might release it only after the security manager is // gone. But we can still assert this for the main process. - MOZ_ASSERT_IF(XRE_GetProcessType() == GeckoProcessType_Default, + MOZ_ASSERT_IF(XRE_IsParentProcess(), !mDomainPolicy); } @@ -1490,7 +1490,7 @@ nsScriptSecurityManager::GetDomainPolicyActive(bool *aRv) NS_IMETHODIMP nsScriptSecurityManager::ActivateDomainPolicy(nsIDomainPolicy** aRv) { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return NS_ERROR_SERVICE_NOT_AVAILABLE; } diff --git a/db/sqlite3/src/sqlite.def b/db/sqlite3/src/sqlite.def index c9f741a579..09ec0a831f 100644 --- a/db/sqlite3/src/sqlite.def +++ b/db/sqlite3/src/sqlite.def @@ -51,6 +51,7 @@ EXPORTS sqlite3_create_function16 sqlite3_create_module sqlite3_data_count + sqlite3_db_filename sqlite3_db_handle sqlite3_db_mutex sqlite3_db_status diff --git a/docshell/base/nsDefaultURIFixup.cpp b/docshell/base/nsDefaultURIFixup.cpp index c03e05543d..4183c187b2 100644 --- a/docshell/base/nsDefaultURIFixup.cpp +++ b/docshell/base/nsDefaultURIFixup.cpp @@ -448,7 +448,7 @@ nsDefaultURIFixup::KeywordToURI(const nsACString& aKeyword, } keyword.Trim(" "); - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { dom::ContentChild* contentChild = dom::ContentChild::GetSingleton(); if (!contentChild) { return NS_ERROR_NOT_AVAILABLE; diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 09c2b1c79a..cdc4ca93e7 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -816,7 +816,7 @@ IncreasePrivateDocShellCount() { gNumberOfPrivateDocShells++; if (gNumberOfPrivateDocShells > 1 || - XRE_GetProcessType() != GeckoProcessType_Content) { + !XRE_IsContentProcess()) { return; } @@ -830,7 +830,7 @@ DecreasePrivateDocShellCount() MOZ_ASSERT(gNumberOfPrivateDocShells > 0); gNumberOfPrivateDocShells--; if (!gNumberOfPrivateDocShells) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { dom::ContentChild* cc = dom::ContentChild::GetSingleton(); cc->SendPrivateDocShellsExist(false); return; @@ -5028,7 +5028,7 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI* aURI, mInPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0; bool isStsHost = false; bool isPinnedHost = false; - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { nsCOMPtr sss = do_GetService(NS_SSSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); @@ -9487,7 +9487,7 @@ nsDocShell::CopyFavicon(nsIURI* aOldURI, nsIURI* aNewURI, bool aInPrivateBrowsing) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { dom::ContentChild* contentChild = dom::ContentChild::GetSingleton(); if (contentChild) { mozilla::ipc::URIParams oldURI, newURI; @@ -14199,7 +14199,7 @@ nsDocShell::MaybeNotifyKeywordSearchLoading(const nsString& aProvider, return; } - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { dom::ContentChild* contentChild = dom::ContentChild::GetSingleton(); if (contentChild) { contentChild->SendNotifyKeywordSearchLoading(aProvider, aKeyword); diff --git a/dom/asmjscache/AsmJSCache.cpp b/dom/asmjscache/AsmJSCache.cpp index 2376363f75..ef743e8c9d 100644 --- a/dom/asmjscache/AsmJSCache.cpp +++ b/dom/asmjscache/AsmJSCache.cpp @@ -17,7 +17,6 @@ #include "mozilla/dom/ContentChild.h" #include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/quota/Client.h" -#include "mozilla/dom/quota/OriginOrPatternString.h" #include "mozilla/dom/quota/QuotaManager.h" #include "mozilla/dom/quota/QuotaObject.h" #include "mozilla/dom/quota/UsageInfo.h" @@ -42,7 +41,6 @@ #define ASMJSCACHE_ENTRY_FILE_NAME_BASE "module" using mozilla::dom::quota::AssertIsOnIOThread; -using mozilla::dom::quota::OriginOrPatternString; using mozilla::dom::quota::PersistenceType; using mozilla::dom::quota::QuotaManager; using mozilla::dom::quota::QuotaObject; @@ -59,12 +57,6 @@ namespace asmjscache { namespace { -bool -IsMainProcess() -{ - return XRE_GetProcessType() == GeckoProcessType_Default; -} - // Anything smaller should compile fast enough that caching will just add // overhead. static const size_t sMinCachedModuleLength = 10000; @@ -298,8 +290,8 @@ public: } protected: - // This method must be called before AllowNextSynchronizedOp (which releases - // the lock protecting these resources). It is idempotent, so it is ok to call + // This method must be called before the directory lock is released (the lock + // is protecting these resources). It is idempotent, so it is ok to call // multiple times (or before the file has been fully opened). void Finish() @@ -482,8 +474,12 @@ private: // MainProcessRunnable is a base class shared by (Single|Parent)ProcessRunnable // that factors out the runnable state machine required to open a cache entry // that runs in the main process. -class MainProcessRunnable : public virtual FileDescriptorHolder +class MainProcessRunnable + : public virtual FileDescriptorHolder + , public quota::OpenDirectoryListener { + typedef mozilla::dom::quota::DirectoryLock DirectoryLock; + public: NS_DECL_NSIRUNNABLE @@ -496,20 +492,19 @@ public: : mPrincipal(aPrincipal), mOpenMode(aOpenMode), mWriteParams(aWriteParams), - mNeedAllowNextSynchronizedOp(false), mPersistence(quota::PERSISTENCE_TYPE_INVALID), mState(eInitial), mResult(JS::AsmJSCache_InternalError), mIsApp(false), mEnforcingQuota(true) { - MOZ_ASSERT(IsMainProcess()); + MOZ_ASSERT(XRE_IsParentProcess()); } virtual ~MainProcessRunnable() { MOZ_ASSERT(mState == eFinished); - MOZ_ASSERT(!mNeedAllowNextSynchronizedOp); + MOZ_ASSERT(!mDirectoryLock); } protected: @@ -637,16 +632,22 @@ private: } } + // OpenDirectoryListener overrides. + virtual void + DirectoryLockAcquired(DirectoryLock* aLock) override; + + virtual void + DirectoryLockFailed() override; + nsIPrincipal* const mPrincipal; const OpenMode mOpenMode; const WriteParams mWriteParams; // State initialized during eInitial: - bool mNeedAllowNextSynchronizedOp; quota::PersistenceType mPersistence; nsCString mGroup; nsCString mOrigin; - nsCString mStorageId; + nsRefPtr mDirectoryLock; // State initialized during eReadyToReadMetadata nsCOMPtr mDirectory; @@ -741,9 +742,6 @@ MainProcessRunnable::InitOnMainThread() mEnforcingQuota = QuotaManager::IsQuotaEnforced(mPersistence, mOrigin, mIsApp); - QuotaManager::GetStorageId(mPersistence, mOrigin, quota::Client::ASMJS, - NS_LITERAL_STRING("asmjs"), mStorageId); - return NS_OK; } @@ -918,18 +916,10 @@ MainProcessRunnable::FinishOnMainThread() MOZ_ASSERT(NS_IsMainThread()); // Per FileDescriptorHolder::Finish()'s comment, call before - // AllowNextSynchronizedOp. + // releasing the directory lock. FileDescriptorHolder::Finish(); - if (mNeedAllowNextSynchronizedOp) { - mNeedAllowNextSynchronizedOp = false; - QuotaManager* qm = QuotaManager::Get(); - if (qm) { - qm->AllowNextSynchronizedOp(OriginOrPatternString::FromOrigin(mOrigin), - Nullable(mPersistence), - mStorageId); - } - } + mDirectoryLock = nullptr; } NS_IMETHODIMP @@ -950,24 +940,16 @@ MainProcessRunnable::Run() } mState = eWaitingToOpenMetadata; - rv = QuotaManager::Get()->WaitForOpenAllowed( - OriginOrPatternString::FromOrigin(mOrigin), - Nullable(mPersistence), - mStorageId, this); - if (NS_FAILED(rv)) { - Fail(); - return NS_OK; - } - mNeedAllowNextSynchronizedOp = true; - return NS_OK; - } + // XXX The exclusive lock shouldn't be needed for read operations. + QuotaManager::Get()->OpenDirectory(mPersistence, + mGroup, + mOrigin, + mIsApp, + quota::Client::ASMJS, + /* aExclusive */ true, + this); - case eWaitingToOpenMetadata: { - MOZ_ASSERT(NS_IsMainThread()); - - mState = eReadyToReadMetadata; - DispatchToIOThread(); return NS_OK; } @@ -1053,6 +1035,7 @@ MainProcessRunnable::Run() return NS_OK; } + case eWaitingToOpenMetadata: case eWaitingToOpenCacheFileForRead: case eOpened: case eFinished: { @@ -1064,6 +1047,29 @@ MainProcessRunnable::Run() return NS_OK; } +void +MainProcessRunnable::DirectoryLockAcquired(DirectoryLock* aLock) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == eWaitingToOpenMetadata); + MOZ_ASSERT(!mDirectoryLock); + + mDirectoryLock = aLock; + + mState = eReadyToReadMetadata; + DispatchToIOThread(); +} + +void +MainProcessRunnable::DirectoryLockFailed() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == eWaitingToOpenMetadata); + MOZ_ASSERT(!mDirectoryLock); + + Fail(); +} + bool FindHashMatch(const Metadata& aMetadata, const ReadParams& aReadParams, unsigned* aModuleIndex) @@ -1113,6 +1119,8 @@ class SingleProcessRunnable final : public File, private MainProcessRunnable { public: + NS_DECL_ISUPPORTS_INHERITED + // In the single-process case, the calling JS compilation thread holds the // nsIPrincipal alive indirectly (via the global object -> compartment -> // principal) so we don't have to ref-count it here. This is fortunate since @@ -1125,7 +1133,7 @@ public: : MainProcessRunnable(aPrincipal, aOpenMode, aWriteParams), mReadParams(aReadParams) { - MOZ_ASSERT(IsMainProcess()); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(!NS_IsMainThread()); MOZ_COUNT_CTOR(SingleProcessRunnable); } @@ -1184,6 +1192,8 @@ private: ReadParams mReadParams; }; +NS_IMPL_ISUPPORTS_INHERITED0(SingleProcessRunnable, File) + // A runnable that executes in a parent process for a cache access originating // in the content process. This runnable gets registered as an IPDL subprotocol // actor so that it can communicate with the corresponding ChildProcessRunnable. @@ -1191,6 +1201,8 @@ class ParentProcessRunnable final : public PAsmJSCacheEntryParent, public MainProcessRunnable { public: + NS_DECL_ISUPPORTS_INHERITED + // The given principal comes from an IPC::Principal which will be dec-refed // at the end of the message, so we must ref-count it here. Fortunately, we // are on the main thread (where PContent messages are delivered). @@ -1203,7 +1215,7 @@ public: mOpened(false), mFinished(false) { - MOZ_ASSERT(IsMainProcess()); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); MOZ_COUNT_CTOR(ParentProcessRunnable); } @@ -1331,6 +1343,8 @@ private: bool mFinished; }; +NS_IMPL_ISUPPORTS_INHERITED0(ParentProcessRunnable, FileDescriptorHolder) + } // unnamed namespace PAsmJSCacheEntryParent* @@ -1380,7 +1394,7 @@ public: mActorDestroyed(false), mState(eInitial) { - MOZ_ASSERT(!IsMainProcess()); + MOZ_ASSERT(!XRE_IsParentProcess()); MOZ_ASSERT(!NS_IsMainThread()); MOZ_COUNT_CTOR(ChildProcessRunnable); } @@ -1512,8 +1526,8 @@ ChildProcessRunnable::Run() MOZ_ASSERT(NS_IsMainThread()); // Per FileDescriptorHolder::Finish()'s comment, call before - // AllowNextSynchronizedOp (which happens in the parent upon receipt of - // the Send__delete__ message). + // releasing the directory lock (which happens in the parent upon receipt + // of the Send__delete__ message). File::OnClose(); if (!mActorDestroyed) { @@ -1576,7 +1590,7 @@ OpenFile(nsIPrincipal* aPrincipal, // parent process to open the file and interact with the QuotaManager. The // child can then map the file into its address space to perform I/O. nsRefPtr file; - if (IsMainProcess()) { + if (XRE_IsParentProcess()) { file = new SingleProcessRunnable(aPrincipal, aOpenMode, aWriteParams, aReadParams); } else { @@ -1836,11 +1850,16 @@ public: { } virtual void - WaitForStoragesToComplete(nsTArray& aStorages, - nsIRunnable* aCallback) override - { - MOZ_ASSERT_UNREACHABLE("There are no storages"); - } + AbortOperations(const nsACString& aOrigin) override + { } + + virtual void + AbortOperationsForProcess(ContentParentId aContentParentId) override + { } + + virtual void + PerformIdleMaintenance() override + { } virtual void ShutdownWorkThreads() override diff --git a/dom/audiochannel/AudioChannelService.cpp b/dom/audiochannel/AudioChannelService.cpp index 61ce6de0d9..113099084c 100644 --- a/dom/audiochannel/AudioChannelService.cpp +++ b/dom/audiochannel/AudioChannelService.cpp @@ -57,7 +57,7 @@ AudioChannelService::GetAudioChannelService() { MOZ_ASSERT(NS_IsMainThread()); - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return AudioChannelServiceChild::GetAudioChannelService(); } @@ -71,7 +71,7 @@ AudioChannelService::GetOrCreateAudioChannelService() { MOZ_ASSERT(NS_IsMainThread()); - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return AudioChannelServiceChild::GetOrCreateAudioChannelService(); } @@ -91,7 +91,7 @@ AudioChannelService::GetOrCreateAudioChannelService() void AudioChannelService::Shutdown() { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return AudioChannelServiceChild::Shutdown(); } @@ -109,7 +109,7 @@ AudioChannelService::AudioChannelService() , mDisabled(false) , mDefChannelChildID(CONTENT_PROCESS_ID_UNKNOWN) { - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { nsCOMPtr obs = mozilla::services::GetObserverService(); if (obs) { obs->AddObserver(this, "ipc:content-shutdown", false); @@ -167,7 +167,7 @@ AudioChannelService::RegisterType(AudioChannel aChannel, uint64_t aChildID, AudioChannelInternalType type = GetInternalType(aChannel, true); mChannelCounters[type].AppendElement(aChildID); - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { // We must keep the childIds in order to decide which app is allowed to play // with then telephony channel. @@ -259,7 +259,7 @@ AudioChannelService::UnregisterType(AudioChannel aChannel, // There are two reasons to defer the decrease of telephony channel. // 1. User can have time to remove device from his ear before music resuming. // 2. Give BT SCO to be disconnected before starting to connect A2DP. - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { if (aChannel == AudioChannel::Telephony) { UnregisterTelephonyChild(aChildID); @@ -293,7 +293,7 @@ AudioChannelService::UnregisterTypeInternal(AudioChannel aChannel, // In order to avoid race conditions, it's safer to notify any existing // agent any time a new one is registered. - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { // No hidden content channel is playable if the original playable hidden // process does not need to play audio from background anymore. if (aChannel == AudioChannel::Content && @@ -518,7 +518,7 @@ AudioChannelService::SetDefaultVolumeControlChannelInternal(int32_t aChannel, bool aVisible, uint64_t aChildID) { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return; } @@ -561,7 +561,7 @@ AudioChannelService::SetDefaultVolumeControlChannelInternal(int32_t aChannel, void AudioChannelService::SendAudioChannelChangedNotification(uint64_t aChildID) { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return; } diff --git a/dom/base/Crypto.cpp b/dom/base/Crypto.cpp index 41f5d6ab1b..1559985673 100644 --- a/dom/base/Crypto.cpp +++ b/dom/base/Crypto.cpp @@ -91,7 +91,7 @@ Crypto::GetRandomValues(JSContext* aCx, const ArrayBufferView& aArray, uint8_t* data = aArray.Data(); - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { InfallibleTArray randomValues; // Tell the parent process to generate random values via PContent ContentChild* cc = ContentChild::GetSingleton(); diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index 2f3fa1661c..0e0b759b70 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -1479,7 +1479,7 @@ Navigator::GetFeature(const nsAString& aName, ErrorResult& aRv) #if defined(XP_LINUX) if (aName.EqualsLiteral("hardware.memory")) { // with seccomp enabled, fopen() should be in a non-sandboxed process - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { uint32_t memLevel = mozilla::hal::GetTotalSystemMemoryLevel(); if (memLevel == 0) { p->MaybeReject(NS_ERROR_NOT_AVAILABLE); @@ -2530,7 +2530,7 @@ Navigator::HasTVSupport(JSContext* aCx, JSObject* aGlobal) bool Navigator::IsE10sEnabled(JSContext* aCx, JSObject* aGlobal) { - return XRE_GetProcessType() == GeckoProcessType_Content; + return XRE_IsContentProcess(); } bool diff --git a/dom/base/nsCCUncollectableMarker.cpp b/dom/base/nsCCUncollectableMarker.cpp index 9a525229e1..616b9858ca 100644 --- a/dom/base/nsCCUncollectableMarker.cpp +++ b/dom/base/nsCCUncollectableMarker.cpp @@ -140,7 +140,7 @@ static void MarkMessageManagers() { // The global message manager only exists in the root process. - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return; } nsCOMPtr strongGlobalMM = diff --git a/dom/base/nsContentPermissionHelper.cpp b/dom/base/nsContentPermissionHelper.cpp index eb6a18acd3..2ca8dc9a85 100644 --- a/dom/base/nsContentPermissionHelper.cpp +++ b/dom/base/nsContentPermissionHelper.cpp @@ -335,7 +335,7 @@ nsContentPermissionUtils::AskPermission(nsIContentPermissionRequest* aRequest, n NS_ENSURE_STATE(aWindow && aWindow->IsCurrentInnerWindow()); // for content process - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { nsRefPtr req = new RemotePermissionRequest(aRequest, aWindow); diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index be8849eecb..540d0f8d45 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -24,6 +24,7 @@ #include "nsIObjectLoadingContent.h" #include "nsFrame.h" #include "mozilla/layers/ShadowLayers.h" +#include "mozilla/layers/APZCCallbackHelper.h" #include "ClientLayerManager.h" #include "nsQueryObject.h" #ifdef MOZ_FMP4 @@ -2487,6 +2488,34 @@ nsDOMWindowUtils::SetAsyncZoom(nsIDOMNode* aRootElement, float aValue) return NS_OK; } +NS_IMETHODIMP +nsDOMWindowUtils::FlushApzRepaints(bool* aOutResult) +{ + nsIWidget* widget = GetWidget(); + if (!widget) { + *aOutResult = false; + return NS_OK; + } + // If APZ is not enabled, this function is a no-op. + if (!widget->AsyncPanZoomEnabled()) { + *aOutResult = false; + return NS_OK; + } + LayerManager* manager = widget->GetLayerManager(); + if (!manager) { + *aOutResult = false; + return NS_OK; + } + ShadowLayerForwarder* forwarder = manager->AsShadowForwarder(); + if (!forwarder || !forwarder->HasShadowManager()) { + *aOutResult = false; + return NS_OK; + } + forwarder->GetShadowManager()->SendFlushApzRepaints(); + *aOutResult = true; + return NS_OK; +} + NS_IMETHODIMP nsDOMWindowUtils::ComputeAnimationDistance(nsIDOMElement* aElement, const nsAString& aProperty, @@ -2949,6 +2978,24 @@ nsDOMWindowUtils::GetFileReferences(const nsAString& aDatabaseName, int64_t aId, return NS_OK; } +NS_IMETHODIMP +nsDOMWindowUtils::FlushPendingFileDeletions() +{ + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); + + using mozilla::dom::indexedDB::IndexedDatabaseManager; + + nsRefPtr mgr = IndexedDatabaseManager::Get(); + if (mgr) { + nsresult rv = mgr->FlushPendingFileDeletions(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + return NS_OK; +} + NS_IMETHODIMP nsDOMWindowUtils::IsIncrementalGCEnabled(JSContext* cx, bool* aResult) { diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 0c4ff668a2..56c05d9734 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -10593,7 +10593,7 @@ PLDHashOperator UnlockEnumerator(imgIRequest* aKey, nsresult nsDocument::SetImageLockingState(bool aLocked) { - if (XRE_GetProcessType() == GeckoProcessType_Content && + if (XRE_IsContentProcess() && !Preferences::GetBool("image.mem.allow_locking_in_content_processes", true)) { return NS_OK; } diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp index 26f66ac719..b25bf7f1b4 100644 --- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -704,7 +704,7 @@ nsFocusManager::WindowRaised(nsIDOMWindow* aWindow) // If this is a parent or single process window, send the activate event. // Events for child process windows will be sent when ParentActivated // is called. - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { ActivateOrDeactivate(window, true); } @@ -767,7 +767,7 @@ nsFocusManager::WindowLowered(nsIDOMWindow* aWindow) // If this is a parent or single process window, send the deactivate event. // Events for child process windows will be sent when ParentActivated // is called. - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { ActivateOrDeactivate(window, false); } diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index 3c63d761e4..b6209614ee 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -1630,12 +1630,12 @@ nsFrameLoader::ShouldUseRemoteProcess() // Don't try to launch nested children if we don't have OMTC. // They won't render! - if (XRE_GetProcessType() == GeckoProcessType_Content && + if (XRE_IsContentProcess() && !CompositorChild::ChildProcessHasCompositor()) { return false; } - if (XRE_GetProcessType() == GeckoProcessType_Content && + if (XRE_IsContentProcess() && !(PR_GetEnv("MOZ_NESTED_OOP_TABS") || Preferences::GetBool("dom.ipc.tabs.nested.enabled", false))) { return false; @@ -2678,7 +2678,7 @@ nsFrameLoader::ResetPermissionManagerStatus() { // The resetting of the permissions status can run only // in the main process. - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { return; } diff --git a/dom/base/nsFrameMessageManager.cpp b/dom/base/nsFrameMessageManager.cpp index 0351ba9ccb..a62fc87d6c 100644 --- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -892,7 +892,7 @@ nsFrameMessageManager::Dump(const nsAString& aStr) NS_IMETHODIMP nsFrameMessageManager::PrivateNoteIntentionalCrash() { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { mozilla::NoteIntentionalCrash("tab"); return NS_OK; } else { @@ -1566,7 +1566,7 @@ MessageManagerReporter::CollectReports(nsIMemoryReporterCallback* aCb, { nsresult rv; - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { nsCOMPtr globalmm = do_GetService("@mozilla.org/globalmessagemanager;1"); if (globalmm) { @@ -1602,7 +1602,7 @@ MessageManagerReporter::CollectReports(nsIMemoryReporterCallback* aCb, nsresult NS_NewGlobalMessageManager(nsIMessageBroadcaster** aResult) { - NS_ENSURE_TRUE(XRE_GetProcessType() == GeckoProcessType_Default, + NS_ENSURE_TRUE(XRE_IsParentProcess(), NS_ERROR_NOT_AVAILABLE); nsRefPtr mm = new nsFrameMessageManager(nullptr, nullptr, @@ -2131,7 +2131,7 @@ NS_NewChildProcessMessageManager(nsISyncMessageSender** aResult) "Re-creating sChildProcessManager"); MessageManagerCallback* cb; - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { cb = new SameChildProcessMessageManagerCallback(); } else { cb = new ChildProcessMessageManagerCallback(); diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 80d9863b10..025f1e301f 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -9457,7 +9457,7 @@ nsGlobalWindow::ShowModalDialog(const nsAString& aUrl, nsIVariant* aArgument, (aUrl, aArgument, aOptions, aError), aError, nullptr); - if (!IsShowModalDialogEnabled() || XRE_GetProcessType() == GeckoProcessType_Content) { + if (!IsShowModalDialogEnabled() || XRE_IsContentProcess()) { aError.Throw(NS_ERROR_NOT_AVAILABLE); return nullptr; } @@ -11244,7 +11244,7 @@ nsGlobalWindow::ShowSlowScriptDialog() unsigned lineno; bool hasFrame = JS::DescribeScriptedCaller(cx, &filename, &lineno); - if (XRE_GetProcessType() == GeckoProcessType_Content && + if (XRE_IsContentProcess() && ProcessHangMonitor::Get()) { ProcessHangMonitor::SlowScriptAction action; nsRefPtr monitor = ProcessHangMonitor::Get(); diff --git a/dom/base/nsObjectLoadingContent.cpp b/dom/base/nsObjectLoadingContent.cpp index 516c70f79a..38409bd500 100644 --- a/dom/base/nsObjectLoadingContent.cpp +++ b/dom/base/nsObjectLoadingContent.cpp @@ -3197,7 +3197,7 @@ nsObjectLoadingContent::ShouldPlay(FallbackType &aReason, bool aIgnoreCurrentTyp sPrefsInitialized = true; } - if (XRE_GetProcessType() == GeckoProcessType_Default && + if (XRE_IsParentProcess() && BrowserTabsRemoteAutostart()) { // Plugins running OOP from the chrome process along with plugins running // OOP from the content process will hang. Let's prevent that situation. diff --git a/dom/base/nsWindowMemoryReporter.cpp b/dom/base/nsWindowMemoryReporter.cpp index 04c38ee84e..e5c09eb4c9 100644 --- a/dom/base/nsWindowMemoryReporter.cpp +++ b/dom/base/nsWindowMemoryReporter.cpp @@ -529,7 +529,7 @@ nsWindowMemoryReporter::CollectReports(nsIMemoryReporterCallback* aCb, // Collect window memory usage. nsWindowSizes windowTotalSizes(nullptr); nsCOMPtr addonManager; - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { // Only try to access the service from the main process. addonManager = do_GetService("@mozilla.org/addons/integration;1"); } diff --git a/dom/base/nsXMLHttpRequest.cpp b/dom/base/nsXMLHttpRequest.cpp index 5f16ae4c71..a4585e0fd3 100644 --- a/dom/base/nsXMLHttpRequest.cpp +++ b/dom/base/nsXMLHttpRequest.cpp @@ -3042,7 +3042,7 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable& aBody) if (scheme.LowerCaseEqualsLiteral("app") || scheme.LowerCaseEqualsLiteral("jar")) { mIsMappedArrayBuffer = true; - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { nsCOMPtr jarChannel = do_QueryInterface(mChannel); // For memory mapping from child process, we need to get file // descriptor of the JAR file opened remotely on the parent proess. diff --git a/dom/bluetooth/BluetoothUtils.cpp b/dom/bluetooth/BluetoothUtils.cpp index efa0bb1cdd..733c6829f4 100644 --- a/dom/bluetooth/BluetoothUtils.cpp +++ b/dom/bluetooth/BluetoothUtils.cpp @@ -377,10 +377,4 @@ DispatchStatusChangedEvent(const nsAString& aType, #endif } -bool -IsMainProcess() -{ - return XRE_GetProcessType() == GeckoProcessType_Default; -} - END_BLUETOOTH_NAMESPACE diff --git a/dom/bluetooth/BluetoothUtils.h b/dom/bluetooth/BluetoothUtils.h index 6c528c6801..14beabe11e 100644 --- a/dom/bluetooth/BluetoothUtils.h +++ b/dom/bluetooth/BluetoothUtils.h @@ -191,14 +191,6 @@ DispatchStatusChangedEvent(const nsAString& aType, const nsAString& aDeviceAddress, bool aStatus); -/** - * Check whether the caller runs at B2G process. - * - * @return true if the caller runs at B2G process, false otherwise. - */ -bool -IsMainProcess(); - END_BLUETOOTH_NAMESPACE #endif diff --git a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp index d7e7ab7f8e..4f1558f055 100644 --- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp +++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp @@ -238,7 +238,7 @@ bool BluetoothHfpManager::Init() { // The function must run at b2g process since it would access SettingsService. - MOZ_ASSERT(IsMainProcess()); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); diff --git a/dom/bluetooth/bluetooth1/BluetoothAdapter.cpp b/dom/bluetooth/bluetooth1/BluetoothAdapter.cpp index 337d98c361..cd3a95d595 100644 --- a/dom/bluetooth/bluetooth1/BluetoothAdapter.cpp +++ b/dom/bluetooth/bluetooth1/BluetoothAdapter.cpp @@ -784,7 +784,7 @@ BluetoothAdapter::SendFile(const nsAString& aDeviceAddress, return nullptr; } - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { // In-process transfer bs->SendFile(aDeviceAddress, &aBlob, results); } else { diff --git a/dom/bluetooth/bluetooth1/BluetoothService.cpp b/dom/bluetooth/bluetooth1/BluetoothService.cpp index 7f8edd85a5..7b2b03c053 100644 --- a/dom/bluetooth/bluetooth1/BluetoothService.cpp +++ b/dom/bluetooth/bluetooth1/BluetoothService.cpp @@ -98,12 +98,6 @@ StaticRefPtr sBluetoothService; bool sInShutdown = false; bool sToggleInProgress = false; -bool -IsMainProcess() -{ - return XRE_GetProcessType() == GeckoProcessType_Default; -} - void ShutdownTimeExceeded(nsITimer* aTimer, void* aClosure) { @@ -217,7 +211,7 @@ BluetoothService* BluetoothService::Create() { #if defined(MOZ_B2G_BT) - if (!IsMainProcess()) { + if (!XRE_IsParentProcess()) { return BluetoothServiceChildProcess::Create(); } @@ -251,7 +245,7 @@ BluetoothService::Init() } // Only the main process should observe bluetooth settings changes. - if (IsMainProcess() && + if (XRE_IsParentProcess() && NS_FAILED(obs->AddObserver(this, MOZSETTINGS_CHANGED_ID, false))) { BT_WARNING("Failed to add settings change observer!"); return false; diff --git a/dom/bluetooth/bluetooth2/BluetoothAdapter.cpp b/dom/bluetooth/bluetooth2/BluetoothAdapter.cpp index 879972f23b..e4c991f854 100644 --- a/dom/bluetooth/bluetooth2/BluetoothAdapter.cpp +++ b/dom/bluetooth/bluetooth2/BluetoothAdapter.cpp @@ -1272,7 +1272,7 @@ BluetoothAdapter::SendFile(const nsAString& aDeviceAddress, return nullptr; } - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { // In-process transfer bs->SendFile(aDeviceAddress, &aBlob, results); } else { diff --git a/dom/bluetooth/bluetooth2/BluetoothService.cpp b/dom/bluetooth/bluetooth2/BluetoothService.cpp index 8d1f33ce76..833cd96a4c 100644 --- a/dom/bluetooth/bluetooth2/BluetoothService.cpp +++ b/dom/bluetooth/bluetooth2/BluetoothService.cpp @@ -192,7 +192,7 @@ BluetoothService* BluetoothService::Create() { #if defined(MOZ_B2G_BT) - if (!IsMainProcess()) { + if (!XRE_IsParentProcess()) { return BluetoothServiceChildProcess::Create(); } @@ -226,7 +226,7 @@ BluetoothService::Init() } // Only the main process should observe bluetooth settings changes. - if (IsMainProcess() && + if (XRE_IsParentProcess() && NS_FAILED(obs->AddObserver(this, MOZSETTINGS_CHANGED_ID, false))) { BT_WARNING("Failed to add settings change observer!"); return false; @@ -268,7 +268,7 @@ BluetoothService::RegisterBluetoothSignalHandler( // Distribute pending pairing requests when pairing listener has been added // to signal observer table. - if (IsMainProcess() && + if (XRE_IsParentProcess() && !mPendingPairReqSignals.IsEmpty() && aNodeName.EqualsLiteral(KEY_PAIRING_LISTENER)) { for (uint32_t i = 0; i < mPendingPairReqSignals.Length(); ++i) { diff --git a/dom/bluetooth/bluez/BluetoothHfpManager.cpp b/dom/bluetooth/bluez/BluetoothHfpManager.cpp index 732af1906e..e0db0d0d75 100644 --- a/dom/bluetooth/bluez/BluetoothHfpManager.cpp +++ b/dom/bluetooth/bluez/BluetoothHfpManager.cpp @@ -443,7 +443,7 @@ bool BluetoothHfpManager::Init() { // The function must run at b2g process since it would access SettingsService. - MOZ_ASSERT(IsMainProcess()); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); nsCOMPtr obs = services::GetObserverService(); diff --git a/dom/cache/Context.cpp b/dom/cache/Context.cpp index 1d6697a19d..c07a2dd231 100644 --- a/dom/cache/Context.cpp +++ b/dom/cache/Context.cpp @@ -12,8 +12,6 @@ #include "mozilla/dom/cache/FileUtils.h" #include "mozilla/dom/cache/Manager.h" #include "mozilla/dom/cache/ManagerId.h" -#include "mozilla/dom/cache/OfflineStorage.h" -#include "mozilla/dom/quota/OriginOrPatternString.h" #include "mozilla/dom/quota/QuotaManager.h" #include "mozIStorageConnection.h" #include "nsIFile.h" @@ -23,39 +21,8 @@ namespace { -using mozilla::dom::Nullable; using mozilla::dom::cache::Action; using mozilla::dom::cache::QuotaInfo; -using mozilla::dom::quota::Client; -using mozilla::dom::quota::OriginOrPatternString; -using mozilla::dom::quota::QuotaManager; -using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT; -using mozilla::dom::quota::PersistenceType; - -// Release our lock on the QuotaManager directory asynchronously. -class QuotaReleaseRunnable final : public nsRunnable -{ -public: - explicit QuotaReleaseRunnable(const QuotaInfo& aQuotaInfo) - : mQuotaInfo(aQuotaInfo) - { } - - NS_IMETHOD Run() override - { - MOZ_ASSERT(NS_IsMainThread()); - QuotaManager* qm = QuotaManager::Get(); - MOZ_ASSERT(qm); - qm->AllowNextSynchronizedOp(OriginOrPatternString::FromOrigin(mQuotaInfo.mOrigin), - Nullable(PERSISTENCE_TYPE_DEFAULT), - mQuotaInfo.mStorageId); - return NS_OK; - } - -private: - ~QuotaReleaseRunnable() { } - - const QuotaInfo mQuotaInfo; -}; class NullAction final : public Action { @@ -80,7 +47,8 @@ namespace dom { namespace cache { using mozilla::DebugOnly; -using mozilla::dom::quota::OriginOrPatternString; +using mozilla::dom::quota::AssertIsOnIOThread; +using mozilla::dom::quota::OpenDirectoryListener; using mozilla::dom::quota::QuotaManager; using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT; using mozilla::dom::quota::PersistenceType; @@ -132,6 +100,7 @@ private: // the QuotaManager. This must be performed for each origin before any disk // IO occurrs. class Context::QuotaInitRunnable final : public nsIRunnable + , public OpenDirectoryListener { public: QuotaInitRunnable(Context* aContext, @@ -149,7 +118,6 @@ public: , mResult(NS_OK) , mState(STATE_INIT) , mCanceled(false) - , mNeedsQuotaRelease(false) { MOZ_ASSERT(mContext); MOZ_ASSERT(mManager); @@ -164,7 +132,7 @@ public: NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable); MOZ_ASSERT(mState == STATE_INIT); - mState = STATE_CALL_WAIT_FOR_OPEN_ALLOWED; + mState = STATE_OPEN_DIRECTORY; nsresult rv = NS_DispatchToMainThread(this, nsIThread::DISPATCH_NORMAL); if (NS_WARN_IF(NS_FAILED(rv))) { mState = STATE_COMPLETE; @@ -181,6 +149,13 @@ public: mInitAction->CancelOnInitiatingThread(); } + // OpenDirectoryListener methods + virtual void + DirectoryLockAcquired(DirectoryLock* aLock) override; + + virtual void + DirectoryLockFailed() override; + private: class SyncResolver final : public Action::Resolver { @@ -220,8 +195,8 @@ private: enum State { STATE_INIT, - STATE_CALL_WAIT_FOR_OPEN_ALLOWED, - STATE_WAIT_FOR_OPEN_ALLOWED, + STATE_OPEN_DIRECTORY, + STATE_WAIT_FOR_DIRECTORY_LOCK, STATE_ENSURE_ORIGIN_INITIALIZED, STATE_RUN_ON_TARGET, STATE_RUNNING, @@ -229,6 +204,18 @@ private: STATE_COMPLETE }; + void Complete(nsresult aResult) + { + MOZ_ASSERT(mState == STATE_RUNNING || NS_FAILED(aResult)); + + MOZ_ASSERT(NS_SUCCEEDED(mResult)); + mResult = aResult; + + mState = STATE_COMPLETING; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL))); + } + void Clear() { NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable); @@ -247,16 +234,52 @@ private: nsCOMPtr mInitiatingThread; nsresult mResult; QuotaInfo mQuotaInfo; - nsMainThreadPtrHandle mOfflineStorage; + nsMainThreadPtrHandle mDirectoryLock; State mState; Atomic mCanceled; - bool mNeedsQuotaRelease; public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIRUNNABLE }; +void +Context::QuotaInitRunnable::DirectoryLockAcquired(DirectoryLock* aLock) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == STATE_WAIT_FOR_DIRECTORY_LOCK); + MOZ_ASSERT(!mDirectoryLock); + + mDirectoryLock = new nsMainThreadPtrHolder(aLock); + + if (mCanceled) { + Complete(NS_ERROR_ABORT); + return; + } + + QuotaManager* qm = QuotaManager::Get(); + MOZ_ASSERT(qm); + + mState = STATE_ENSURE_ORIGIN_INITIALIZED; + nsresult rv = qm->IOThread()->Dispatch(this, nsIThread::DISPATCH_NORMAL); + if (NS_WARN_IF(NS_FAILED(rv))) { + Complete(rv); + return; + } +} + +void +Context::QuotaInitRunnable::DirectoryLockFailed() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == STATE_WAIT_FOR_DIRECTORY_LOCK); + MOZ_ASSERT(!mDirectoryLock); + + NS_WARNING("Failed to acquire a directory lock!"); + + Complete(NS_ERROR_FAILURE); +} + NS_IMPL_ISUPPORTS(mozilla::dom::cache::Context::QuotaInitRunnable, nsIRunnable); // The QuotaManager init state machine is represented in the following diagram: @@ -267,14 +290,14 @@ NS_IMPL_ISUPPORTS(mozilla::dom::cache::Context::QuotaInitRunnable, nsIRunnable); // +-------+-------+ | // | | // +----------v-----------+ | -// |CallWaitForOpenAllowed| Resolve(error) | +// | OpenDirectory | Resolve(error) | // | (Main Thread) +-----------------+ // +----------+-----------+ | // | | -// +--------v---------+ | -// |WaitForOpenAllowed| Resolve(error) | -// | (Main Thread) +-------------------+ -// +--------+---------+ | +// +----------v-----------+ | +// | WaitForDirectoryLock | Resolve(error) | +// | (Main Thread) +-----------------+ +// +----------+-----------+ | // | | // +----------v------------+ | // |EnsureOriginInitialized| Resolve(error) | @@ -307,7 +330,7 @@ Context::QuotaInitRunnable::Run() switch(mState) { // ----------------------------------- - case STATE_CALL_WAIT_FOR_OPEN_ALLOWED: + case STATE_OPEN_DIRECTORY: { MOZ_ASSERT(NS_IsMainThread()); @@ -333,59 +356,23 @@ Context::QuotaInitRunnable::Run() break; } - QuotaManager::GetStorageId(PERSISTENCE_TYPE_DEFAULT, - mQuotaInfo.mOrigin, - Client::DOMCACHE, - NS_LITERAL_STRING("cache"), - mQuotaInfo.mStorageId); - - // QuotaManager::WaitForOpenAllowed() will hold a reference to us as - // a callback. We will then get executed again on the main thread when - // it is safe to open the quota directory. - mState = STATE_WAIT_FOR_OPEN_ALLOWED; - rv = qm->WaitForOpenAllowed(OriginOrPatternString::FromOrigin(mQuotaInfo.mOrigin), - Nullable(PERSISTENCE_TYPE_DEFAULT), - mQuotaInfo.mStorageId, this); - if (NS_FAILED(rv)) { - resolver->Resolve(rv); - break; - } - break; - } - // ------------------------------ - case STATE_WAIT_FOR_OPEN_ALLOWED: - { - MOZ_ASSERT(NS_IsMainThread()); - - mNeedsQuotaRelease = true; - - if (mCanceled) { - resolver->Resolve(NS_ERROR_ABORT); - break; - } - - QuotaManager* qm = QuotaManager::Get(); - MOZ_ASSERT(qm); - - nsRefPtr offlineStorage = - OfflineStorage::Register(mThreadsafeHandle, mQuotaInfo); - mOfflineStorage = new nsMainThreadPtrHolder(offlineStorage); - - mState = STATE_ENSURE_ORIGIN_INITIALIZED; - nsresult rv = qm->IOThread()->Dispatch(this, nsIThread::DISPATCH_NORMAL); - if (NS_WARN_IF(NS_FAILED(rv))) { - resolver->Resolve(rv); - break; - } + // QuotaManager::OpenDirectory() will hold a reference to us as + // a listener. We will then get DirectoryLockAcquired() on the main + // thread when it is safe to access our storage directory. + mState = STATE_WAIT_FOR_DIRECTORY_LOCK; + qm->OpenDirectory(PERSISTENCE_TYPE_DEFAULT, + mQuotaInfo.mGroup, + mQuotaInfo.mOrigin, + mQuotaInfo.mIsApp, + quota::Client::DOMCACHE, + /* aExclusive */ false, + this); break; } // ---------------------------------- case STATE_ENSURE_ORIGIN_INITIALIZED: { - // Can't assert quota IO thread because its an idle thread that can get - // recreated. At least assert we're not on main thread or owning thread. - MOZ_ASSERT(!NS_IsMainThread()); - MOZ_ASSERT(_mOwningThread.GetThread() != PR_GetCurrentThread()); + AssertIsOnIOThread(); if (mCanceled) { resolver->Resolve(NS_ERROR_ABORT); @@ -438,21 +425,16 @@ Context::QuotaInitRunnable::Run() { NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable); mInitAction->CompleteOnInitiatingThread(mResult); - mContext->OnQuotaInit(mResult, mQuotaInfo, mOfflineStorage); + mContext->OnQuotaInit(mResult, mQuotaInfo, mDirectoryLock); mState = STATE_COMPLETE; - if (mNeedsQuotaRelease) { - // Unlock the quota dir if we locked it previously - nsCOMPtr runnable = new QuotaReleaseRunnable(mQuotaInfo); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); - } - // Explicitly cleanup here as the destructor could fire on any of // the threads we have bounced through. Clear(); break; } // ----- + case STATE_WAIT_FOR_DIRECTORY_LOCK: default: { MOZ_CRASH("unexpected state in QuotaInitRunnable"); @@ -460,14 +442,7 @@ Context::QuotaInitRunnable::Run() } if (resolver->Resolved()) { - MOZ_ASSERT(mState == STATE_RUNNING || NS_FAILED(resolver->Result())); - - MOZ_ASSERT(NS_SUCCEEDED(mResult)); - mResult = resolver->Result(); - - mState = STATE_COMPLETING; - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL))); + Complete(resolver->Result()); } return NS_OK; @@ -1007,7 +982,7 @@ Context::DispatchAction(Action* aAction, bool aDoomData) void Context::OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo, - nsMainThreadPtrHandle& aOfflineStorage) + nsMainThreadPtrHandle& aDirectoryLock) { NS_ASSERT_OWNINGTHREAD(Context); @@ -1016,10 +991,10 @@ Context::OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo, mQuotaInfo = aQuotaInfo; - // Always save the offline storage to ensure QuotaManager does not shutdown + // Always save the directory lock to ensure QuotaManager does not shutdown // before the Context has gone away. - MOZ_ASSERT(!mOfflineStorage); - mOfflineStorage = aOfflineStorage; + MOZ_ASSERT(!mDirectoryLock); + mDirectoryLock = aDirectoryLock; if (mState == STATE_CONTEXT_CANCELED || NS_FAILED(aRv)) { for (uint32_t i = 0; i < mPendingActions.Length(); ++i) { diff --git a/dom/cache/Context.h b/dom/cache/Context.h index 19856a7e16..156dc605f4 100644 --- a/dom/cache/Context.h +++ b/dom/cache/Context.h @@ -21,11 +21,17 @@ class nsIThread; namespace mozilla { namespace dom { + +namespace quota { + +class DirectoryLock; + +} // namespace quota + namespace cache { class Action; class Manager; -class OfflineStorage; // The Context class is RAII-style class for managing IO operations within the // Cache. @@ -41,8 +47,8 @@ class OfflineStorage; // 1) The Manager will call Context::AllowToClose() when all of the actors // have removed themselves as listener. This means an idle context with // no active DOM objects will close gracefully. -// 2) The QuotaManager invalidates the storage area so it can delete the -// files. In this case the OfflineStorage calls Cache::Invalidate() which +// 2) The QuotaManager aborts all operations so it can delete the files. +// In this case the QuotaManager calls Client::AbortOperations() which // in turn cancels all existing Action objects and then marks the Manager // as invalid. // 3) Browser shutdown occurs and the Manager calls Context::CancelAll(). @@ -59,6 +65,8 @@ class OfflineStorage; // via the code in ShutdownObserver.cpp. class Context final { + typedef mozilla::dom::quota::DirectoryLock DirectoryLock; + public: // Define a class allowing other threads to hold the Context alive. This also // allows these other threads to safely close or cancel the Context. @@ -182,7 +190,7 @@ private: void Start(); void DispatchAction(Action* aAction, bool aDoomData = false); void OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo, - nsMainThreadPtrHandle& aOfflineStorage); + nsMainThreadPtrHandle& aDirectoryLock); already_AddRefed CreateThreadsafeHandle(); @@ -212,7 +220,7 @@ private: // when ThreadsafeHandle::AllowToClose() is called. nsRefPtr mThreadsafeHandle; - nsMainThreadPtrHandle mOfflineStorage; + nsMainThreadPtrHandle mDirectoryLock; nsRefPtr mNextContext; public: diff --git a/dom/cache/DBSchema.cpp b/dom/cache/DBSchema.cpp index 3baf7e2796..2d80fff6b3 100644 --- a/dom/cache/DBSchema.cpp +++ b/dom/cache/DBSchema.cpp @@ -548,6 +548,62 @@ IsCacheOrphaned(mozIStorageConnection* aConn, CacheId aCacheId, return rv; } +nsresult +FindOrphanedCacheIds(mozIStorageConnection* aConn, + nsTArray& aOrphanedListOut) +{ + nsCOMPtr state; + nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING( + "SELECT id FROM caches " + "WHERE id NOT IN (SELECT cache_id from storage);" + ), getter_AddRefs(state)); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + bool hasMoreData = false; + while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) { + CacheId cacheId = INVALID_CACHE_ID; + rv = state->GetInt64(0, &cacheId); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + aOrphanedListOut.AppendElement(cacheId); + } + + return rv; +} + +nsresult +GetKnownBodyIds(mozIStorageConnection* aConn, nsTArray& aBodyIdListOut) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(aConn); + + nsCOMPtr state; + nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING( + "SELECT request_body_id, response_body_id FROM entries;" + ), getter_AddRefs(state)); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + bool hasMoreData = false; + while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) { + // extract 0 to 2 nsID structs per row + for (uint32_t i = 0; i < 2; ++i) { + bool isNull = false; + + rv = state->GetIsNull(i, &isNull); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + if (!isNull) { + nsID id; + rv = ExtractId(state, i, &id); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + aBodyIdListOut.AppendElement(id); + } + } + } + + return rv; +} + nsresult CacheMatch(mozIStorageConnection* aConn, CacheId aCacheId, const CacheRequest& aRequest, diff --git a/dom/cache/DBSchema.h b/dom/cache/DBSchema.h index 6f94deb362..86fa097bf2 100644 --- a/dom/cache/DBSchema.h +++ b/dom/cache/DBSchema.h @@ -48,6 +48,13 @@ nsresult IsCacheOrphaned(mozIStorageConnection* aConn, CacheId aCacheId, bool* aOrphanedOut); +nsresult +FindOrphanedCacheIds(mozIStorageConnection* aConn, + nsTArray& aOrphanedListOut); + +nsresult +GetKnownBodyIds(mozIStorageConnection* aConn, nsTArray& aBodyIdListOut); + nsresult CacheMatch(mozIStorageConnection* aConn, CacheId aCacheId, const CacheRequest& aRequest, const CacheQueryParams& aParams, diff --git a/dom/cache/FileUtils.cpp b/dom/cache/FileUtils.cpp index dfec03e200..77d8e8cdbc 100644 --- a/dom/cache/FileUtils.cpp +++ b/dom/cache/FileUtils.cpp @@ -320,8 +320,108 @@ BodyIdToFile(nsIFile* aBaseDir, const nsID& aId, BodyFileType aType, } // namespace nsresult -CreateMarkerFile(const QuotaInfo& aQuotaInfo) +BodyDeleteOrphanedFiles(nsIFile* aBaseDir, nsTArray& aKnownBodyIdList) { + MOZ_ASSERT(aBaseDir); + + // body files are stored in a directory structure like: + // + // /morgue/01/{01fdddb2-884d-4c3d-95ba-0c8062f6c325}.final + // /morgue/02/{02fdddb2-884d-4c3d-95ba-0c8062f6c325}.tmp + + nsCOMPtr dir; + nsresult rv = aBaseDir->Clone(getter_AddRefs(dir)); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + // Add the root morgue directory + rv = dir->Append(NS_LITERAL_STRING("morgue")); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + nsCOMPtr entries; + rv = dir->GetDirectoryEntries(getter_AddRefs(entries)); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + // Iterate over all the intermediate morgue subdirs + bool hasMore = false; + while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore) { + nsCOMPtr entry; + rv = entries->GetNext(getter_AddRefs(entry)); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + nsCOMPtr subdir = do_QueryInterface(entry); + + bool isDir = false; + rv = subdir->IsDirectory(&isDir); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + // If a file got in here somehow, try to remove it and move on + if (NS_WARN_IF(!isDir)) { + rv = subdir->Remove(false /* recursive */); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + continue; + } + + nsCOMPtr subEntries; + rv = subdir->GetDirectoryEntries(getter_AddRefs(subEntries)); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + // Now iterate over all the files in the subdir + bool subHasMore = false; + while(NS_SUCCEEDED(rv = subEntries->HasMoreElements(&subHasMore)) && + subHasMore) { + nsCOMPtr subEntry; + rv = subEntries->GetNext(getter_AddRefs(subEntry)); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + nsCOMPtr file = do_QueryInterface(subEntry); + + nsAutoCString leafName; + rv = file->GetNativeLeafName(leafName); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + // Delete all tmp files regardless of known bodies. These are + // all considered orphans. + if (StringEndsWith(leafName, NS_LITERAL_CSTRING(".tmp"))) { + // remove recursively in case its somehow a directory + rv = file->Remove(true /* recursive */); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + continue; + } + + nsCString suffix(NS_LITERAL_CSTRING(".final")); + + // Otherwise, it must be a .final file. If its not, then just + // skip it. + if (NS_WARN_IF(!StringEndsWith(leafName, suffix) || + leafName.Length() != NSID_LENGTH - 1 + suffix.Length())) { + continue; + } + + // Finally, parse the uuid out of the name. If its fails to parse, + // the ignore the file. + nsID id; + if (NS_WARN_IF(!id.Parse(leafName.BeginReading()))) { + continue; + } + + if (!aKnownBodyIdList.Contains(id)) { + // remove recursively in case its somehow a directory + rv = file->Remove(true /* recursive */); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + } + } + } + + return rv; +} + +namespace { + +nsresult +GetMarkerFileHandle(const QuotaInfo& aQuotaInfo, nsIFile** aFileOut) +{ + MOZ_ASSERT(aFileOut); + nsCOMPtr marker; nsresult rv = aQuotaInfo.mDir->Clone(getter_AddRefs(marker)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -332,6 +432,20 @@ CreateMarkerFile(const QuotaInfo& aQuotaInfo) rv = marker->Append(NS_LITERAL_STRING("context_open.marker")); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + marker.forget(aFileOut); + + return rv; +} + +} // namespace + +nsresult +CreateMarkerFile(const QuotaInfo& aQuotaInfo) +{ + nsCOMPtr marker; + nsresult rv = GetMarkerFileHandle(aQuotaInfo, getter_AddRefs(marker)); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + rv = marker->Create(nsIFile::NORMAL_FILE_TYPE, 0644); if (rv == NS_ERROR_FILE_ALREADY_EXISTS) { rv = NS_OK; @@ -350,13 +464,7 @@ nsresult DeleteMarkerFile(const QuotaInfo& aQuotaInfo) { nsCOMPtr marker; - nsresult rv = aQuotaInfo.mDir->Clone(getter_AddRefs(marker)); - if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - - rv = marker->Append(NS_LITERAL_STRING("cache")); - if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - - rv = marker->Append(NS_LITERAL_STRING("context_open.marker")); + nsresult rv = GetMarkerFileHandle(aQuotaInfo, getter_AddRefs(marker)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = marker->Remove(/* recursive = */ false); @@ -373,6 +481,20 @@ DeleteMarkerFile(const QuotaInfo& aQuotaInfo) return NS_OK; } +bool +MarkerFileExists(const QuotaInfo& aQuotaInfo) +{ + nsCOMPtr marker; + nsresult rv = GetMarkerFileHandle(aQuotaInfo, getter_AddRefs(marker)); + if (NS_WARN_IF(NS_FAILED(rv))) { return false; } + + bool exists = false; + rv = marker->Exists(&exists); + if (NS_WARN_IF(NS_FAILED(rv))) { return false; } + + return exists; +} + } // namespace cache } // namespace dom } // namespace mozilla diff --git a/dom/cache/FileUtils.h b/dom/cache/FileUtils.h index 38af32d3fd..e7389abeae 100644 --- a/dom/cache/FileUtils.h +++ b/dom/cache/FileUtils.h @@ -50,12 +50,18 @@ BodyOpen(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir, const nsID& aId, nsresult BodyDeleteFiles(nsIFile* aBaseDir, const nsTArray& aIdList); +nsresult +BodyDeleteOrphanedFiles(nsIFile* aBaseDir, nsTArray& aKnownBodyIdList); + nsresult CreateMarkerFile(const QuotaInfo& aQuotaInfo); nsresult DeleteMarkerFile(const QuotaInfo& aQuotaInfo); +bool +MarkerFileExists(const QuotaInfo& aQuotaInfo); + } // namespace cache } // namespace dom } // namespace mozilla diff --git a/dom/cache/Manager.cpp b/dom/cache/Manager.cpp index 468d069b9a..9e7a20dab8 100644 --- a/dom/cache/Manager.cpp +++ b/dom/cache/Manager.cpp @@ -30,15 +30,12 @@ #include "nsThreadUtils.h" #include "nsTObserverArray.h" -namespace { -using mozilla::unused; -using mozilla::dom::cache::Action; -using mozilla::dom::cache::BodyCreateDir; -using mozilla::dom::cache::BodyDeleteFiles; -using mozilla::dom::cache::QuotaInfo; -using mozilla::dom::cache::SyncDBAction; -using mozilla::dom::cache::db::CreateSchema; +namespace mozilla { +namespace dom { +namespace cache { + +namespace { // An Action that is executed when a Context is first created. It ensures that // the directory and database are setup properly. This lets other actions @@ -54,23 +51,56 @@ public: RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir, mozIStorageConnection* aConn) override { - // TODO: init maintainance marker (bug 1110446) - // TODO: perform maintainance if necessary (bug 1110446) - // TODO: find orphaned caches in database (bug 1110446) - // TODO: have Context create/delete marker files in constructor/destructor - // and only do expensive maintenance if that marker is present (bug 1110446) - nsresult rv = BodyCreateDir(aDBDir); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - mozStorageTransaction trans(aConn, false, - mozIStorageConnection::TRANSACTION_IMMEDIATE); + { + mozStorageTransaction trans(aConn, false, + mozIStorageConnection::TRANSACTION_IMMEDIATE); - rv = CreateSchema(aConn); - if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + rv = db::CreateSchema(aConn); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - rv = trans.Commit(); - if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + rv = trans.Commit(); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + } + + // If the Context marker file exists, then the last session was + // not cleanly shutdown. In these cases sqlite will ensure that + // the database is valid, but we might still orphan data. Both + // Cache objects and body files can be referenced by DOM objects + // after they are "removed" from their parent. So we need to + // look and see if any of these late access objects have been + // orphaned. + // + // Note, this must be done after any schema version updates to + // ensure our DBSchema methods work correctly. + if (MarkerFileExists(aQuotaInfo)) { + NS_WARNING("Cache not shutdown cleanly! Cleaning up stale data..."); + mozStorageTransaction trans(aConn, false, + mozIStorageConnection::TRANSACTION_IMMEDIATE); + + // Clean up orphaned Cache objects + nsAutoTArray orphanedCacheIdList; + nsresult rv = db::FindOrphanedCacheIds(aConn, orphanedCacheIdList); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + for (uint32_t i = 0; i < orphanedCacheIdList.Length(); ++i) { + nsAutoTArray deletedBodyIdList; + rv = db::DeleteCacheId(aConn, orphanedCacheIdList[i], deletedBodyIdList); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + rv = BodyDeleteFiles(aDBDir, deletedBodyIdList); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + } + + // Clean up orphaned body objects + nsAutoTArray knownBodyIdList; + rv = db::GetKnownBodyIds(aConn, knownBodyIdList); + + rv = BodyDeleteOrphanedFiles(aDBDir, knownBodyIdList); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + } return rv; } @@ -127,14 +157,6 @@ private: nsTArray mDeletedBodyIdList; }; -} // namespace - -namespace mozilla { -namespace dom { -namespace cache { - -namespace { - bool IsHeadRequest(CacheRequest aRequest, CacheQueryParams aParams) { return !aParams.ignoreMethod() && aRequest.method().LowerCaseEqualsLiteral("head"); @@ -229,6 +251,26 @@ public: MaybeDestroyInstance(); } + static void + StartAbortOnMainThread(const nsACString& aOrigin) + { + MOZ_ASSERT(NS_IsMainThread()); + + // Lock for sBackgroundThread. + StaticMutexAutoLock lock(sMutex); + + if (!sBackgroundThread) { + return; + } + + // Guaranteed to succeed because we should get abort only before the + // background thread is destroyed. + nsCOMPtr runnable = new AbortRunnable(aOrigin); + nsresult rv = sBackgroundThread->Dispatch(runnable, + nsIThread::DISPATCH_NORMAL); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv)); + } + static void StartShutdownAllOnMainThread() { @@ -338,6 +380,36 @@ private: sFactory = nullptr; } + static void + AbortOnBackgroundThread(const nsACString& aOrigin) + { + mozilla::ipc::AssertIsOnBackgroundThread(); + + // The factory was destroyed between when abort started on main thread and + // when we could start abort on the worker thread. Just declare abort + // complete. + if (!sFactory) { +#ifdef DEBUG + StaticMutexAutoLock lock(sMutex); + MOZ_ASSERT(!sBackgroundThread); +#endif + return; + } + + MOZ_ASSERT(!sFactory->mManagerList.IsEmpty()); + + { + ManagerList::ForwardIterator iter(sFactory->mManagerList); + while (iter.HasMore()) { + nsRefPtr manager = iter.GetNext(); + if (aOrigin.IsVoid() || + manager->mManagerId->ExtendedOrigin() == aOrigin) { + manager->Abort(); + } + } + } + } + static void ShutdownAllOnBackgroundThread() { @@ -374,6 +446,26 @@ private: MaybeDestroyInstance(); } + class AbortRunnable final : public nsRunnable + { + public: + explicit AbortRunnable(const nsACString& aOrigin) + : mOrigin(aOrigin) + { } + + NS_IMETHOD + Run() override + { + mozilla::ipc::AssertIsOnBackgroundThread(); + AbortOnBackgroundThread(mOrigin); + return NS_OK; + } + private: + ~AbortRunnable() { } + + const nsCString mOrigin; + }; + class ShutdownAllRunnable final : public nsRunnable { public: @@ -1463,6 +1555,15 @@ Manager::ShutdownAllOnMainThread() } } +// static +void +Manager::AbortOnMainThread(const nsACString& aOrigin) +{ + MOZ_ASSERT(NS_IsMainThread()); + + Factory::StartAbortOnMainThread(aOrigin); +} + void Manager::RemoveListener(Listener* aListener) { @@ -1838,6 +1939,22 @@ Manager::Shutdown() } } +void +Manager::Abort() +{ + NS_ASSERT_OWNINGTHREAD(Manager); + MOZ_ASSERT(mContext); + + // Note that we are closing to prevent any new requests from coming in and + // creating a new Context. We must ensure all Contexts and IO operations are + // complete before origin clear proceeds. + NoteClosing(); + + // Cancel and only note that we are done after the context is cleaned up. + nsRefPtr context = mContext; + context->CancelAll(); +} + Manager::ListenerId Manager::SaveListener(Listener* aListener) { diff --git a/dom/cache/Manager.h b/dom/cache/Manager.h index 51396f430a..7430a80ef3 100644 --- a/dom/cache/Manager.h +++ b/dom/cache/Manager.h @@ -134,6 +134,9 @@ public: // Synchronously shutdown from main thread. This spins the event loop. static void ShutdownAllOnMainThread(); + // Cancel actions for given origin or all actions if passed string is null. + static void AbortOnMainThread(const nsACString& aOrigin); + // Must be called by Listener objects before they are destroyed. void RemoveListener(Listener* aListener); @@ -197,7 +200,8 @@ private: ~Manager(); void Init(Manager* aOldManager); void Shutdown(); - already_AddRefed CurrentContext(); + + void Abort(); ListenerId SaveListener(Listener* aListener); Listener* GetListener(ListenerId aListenerId) const; diff --git a/dom/cache/ManagerId.cpp b/dom/cache/ManagerId.cpp index 19948bc67a..9863e00a4f 100644 --- a/dom/cache/ManagerId.cpp +++ b/dom/cache/ManagerId.cpp @@ -32,20 +32,15 @@ ManagerId::Create(nsIPrincipal* aPrincipal, ManagerId** aManagerIdOut) // // TODO: consider using QuotaManager's modified origin here (bug 1112071) - nsAutoCString origin; + nsCString origin; nsresult rv = aPrincipal->GetOrigin(origin); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - uint32_t appId; - rv = aPrincipal->GetAppId(&appId); + nsCString jarPrefix; + rv = aPrincipal->GetJarPrefix(jarPrefix); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - bool inBrowserElement; - rv = aPrincipal->GetIsInBrowserElement(&inBrowserElement); - if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - - nsRefPtr ref = new ManagerId(aPrincipal, origin, appId, - inBrowserElement); + nsRefPtr ref = new ManagerId(aPrincipal, origin, jarPrefix); ref.forget(aManagerIdOut); return NS_OK; @@ -60,11 +55,9 @@ ManagerId::Principal() const } ManagerId::ManagerId(nsIPrincipal* aPrincipal, const nsACString& aOrigin, - uint32_t aAppId, bool aInBrowserElement) + const nsACString& aJarPrefix) : mPrincipal(aPrincipal) - , mOrigin(aOrigin) - , mAppId(aAppId) - , mInBrowserElement(aInBrowserElement) + , mExtendedOrigin(aJarPrefix + aOrigin) { MOZ_ASSERT(mPrincipal); } diff --git a/dom/cache/ManagerId.h b/dom/cache/ManagerId.h index 8657826878..6b73533416 100644 --- a/dom/cache/ManagerId.h +++ b/dom/cache/ManagerId.h @@ -23,40 +23,22 @@ namespace cache { class ManagerId final { public: - // nsTArray comparator that compares by value instead of pointer values. - class MOZ_STACK_CLASS Comparator final - { - public: - bool Equals(ManagerId *aA, ManagerId* aB) const { return *aA == *aB; } - bool LessThan(ManagerId *aA, ManagerId* aB) const { return *aA < *aB; } - }; - // Main thread only static nsresult Create(nsIPrincipal* aPrincipal, ManagerId** aManagerIdOut); // Main thread only already_AddRefed Principal() const; - const nsACString& Origin() const { return mOrigin; } + const nsACString& ExtendedOrigin() const { return mExtendedOrigin; } bool operator==(const ManagerId& aOther) const { - return mOrigin == aOther.mOrigin && - mAppId == aOther.mAppId && - mInBrowserElement == aOther.mInBrowserElement; - } - - bool operator<(const ManagerId& aOther) const - { - return mOrigin < aOther.mOrigin || - (mOrigin == aOther.mOrigin && mAppId < aOther.mAppId) || - (mOrigin == aOther.mOrigin && mAppId == aOther.mAppId && - mInBrowserElement < aOther.mInBrowserElement); + return mExtendedOrigin == aOther.mExtendedOrigin; } private: ManagerId(nsIPrincipal* aPrincipal, const nsACString& aOrigin, - uint32_t aAppId, bool aInBrowserElement); + const nsACString& aJarPrefix); ~ManagerId(); ManagerId(const ManagerId&) = delete; @@ -66,9 +48,7 @@ private: nsCOMPtr mPrincipal; // immutable to allow threadsfe access - const nsCString mOrigin; - const uint32_t mAppId; - const bool mInBrowserElement; + const nsCString mExtendedOrigin; public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::cache::ManagerId) diff --git a/dom/cache/OfflineStorage.cpp b/dom/cache/OfflineStorage.cpp deleted file mode 100644 index 08e7cb41bd..0000000000 --- a/dom/cache/OfflineStorage.cpp +++ /dev/null @@ -1,135 +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 "mozilla/dom/cache/OfflineStorage.h" - -#include "mozilla/dom/cache/Context.h" -#include "mozilla/dom/cache/QuotaClient.h" -#include "mozilla/dom/quota/QuotaManager.h" -#include "nsThreadUtils.h" - -namespace mozilla { -namespace dom { -namespace cache { - -using mozilla::dom::quota::Client; -using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT; -using mozilla::dom::quota::QuotaManager; - -NS_IMPL_ISUPPORTS(OfflineStorage, nsIOfflineStorage); - -// static -already_AddRefed -OfflineStorage::Register(Context::ThreadsafeHandle* aContext, - const QuotaInfo& aQuotaInfo) -{ - MOZ_ASSERT(NS_IsMainThread()); - - QuotaManager* qm = QuotaManager::Get(); - if (NS_WARN_IF(!qm)) { - return nullptr; - } - - nsRefPtr client = qm->GetClient(Client::DOMCACHE); - - nsRefPtr storage = - new OfflineStorage(aContext, aQuotaInfo, client); - - if (NS_WARN_IF(!qm->RegisterStorage(storage))) { - return nullptr; - } - - return storage.forget(); -} - -void -OfflineStorage::AddDestroyCallback(nsIRunnable* aCallback) -{ - MOZ_ASSERT(aCallback); - MOZ_ASSERT(!mDestroyCallbacks.Contains(aCallback)); - mDestroyCallbacks.AppendElement(aCallback); -} - -OfflineStorage::OfflineStorage(Context::ThreadsafeHandle* aContext, - const QuotaInfo& aQuotaInfo, - Client* aClient) - : mContext(aContext) - , mQuotaInfo(aQuotaInfo) - , mClient(aClient) -{ - MOZ_ASSERT(mContext); - MOZ_ASSERT(mClient); - - mPersistenceType = PERSISTENCE_TYPE_DEFAULT; - mGroup = mQuotaInfo.mGroup; -} - -OfflineStorage::~OfflineStorage() -{ - MOZ_ASSERT(NS_IsMainThread()); - QuotaManager* qm = QuotaManager::Get(); - MOZ_ASSERT(qm); - qm->UnregisterStorage(this); - for (uint32_t i = 0; i < mDestroyCallbacks.Length(); ++i) { - mDestroyCallbacks[i]->Run(); - } -} - -NS_IMETHODIMP_(const nsACString&) -OfflineStorage::Id() -{ - MOZ_ASSERT(NS_IsMainThread()); - return mQuotaInfo.mStorageId; -} - -NS_IMETHODIMP_(Client*) -OfflineStorage::GetClient() -{ - MOZ_ASSERT(NS_IsMainThread()); - return mClient; -} - -NS_IMETHODIMP_(bool) -OfflineStorage::IsOwnedByProcess(ContentParent* aOwner) -{ - MOZ_ASSERT(NS_IsMainThread()); - // The Cache and Context can be shared by multiple client processes. They - // are not exclusively owned by a single process. - // - // As far as I can tell this is used by QuotaManager to shutdown storages - // when a particular process goes away. We definitely don't want this - // since we are shared. Also, the Cache actor code already properly - // handles asynchronous actor destruction when the child process dies. - // - // Therefore, always return false here. - return false; -} - -NS_IMETHODIMP_(const nsACString&) -OfflineStorage::Origin() -{ - MOZ_ASSERT(NS_IsMainThread()); - return mQuotaInfo.mOrigin; -} - -NS_IMETHODIMP_(nsresult) -OfflineStorage::Close() -{ - MOZ_ASSERT(NS_IsMainThread()); - mContext->AllowToClose(); - return NS_OK; -} - -NS_IMETHODIMP_(void) -OfflineStorage::Invalidate() -{ - MOZ_ASSERT(NS_IsMainThread()); - mContext->InvalidateAndAllowToClose(); -} - -} // namespace cache -} // namespace dom -} // namespace mozilla diff --git a/dom/cache/OfflineStorage.h b/dom/cache/OfflineStorage.h deleted file mode 100644 index 51291f5455..0000000000 --- a/dom/cache/OfflineStorage.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_cache_QuotaOfflineStorage_h -#define mozilla_dom_cache_QuotaOfflineStorage_h - -#include "nsISupportsImpl.h" -#include "mozilla/AlreadyAddRefed.h" -#include "mozilla/dom/cache/Context.h" -#include "nsIOfflineStorage.h" -#include "nsTArray.h" - -namespace mozilla { -namespace dom { -namespace cache { - -class OfflineStorage final : public nsIOfflineStorage -{ -public: - static already_AddRefed - Register(Context::ThreadsafeHandle* aContext, const QuotaInfo& aQuotaInfo); - - void - AddDestroyCallback(nsIRunnable* aCallback); - -private: - OfflineStorage(Context::ThreadsafeHandle* aContext, - const QuotaInfo& aQuotaInfo, - Client* aClient); - ~OfflineStorage(); - - nsRefPtr mContext; - const QuotaInfo mQuotaInfo; - nsRefPtr mClient; - nsTArray> mDestroyCallbacks; - - NS_DECL_ISUPPORTS - NS_DECL_NSIOFFLINESTORAGE -}; - -} // namespace cache -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_cache_QuotaOfflineStorage_h diff --git a/dom/cache/QuotaClient.cpp b/dom/cache/QuotaClient.cpp index 3428f91aee..726d0b351e 100644 --- a/dom/cache/QuotaClient.cpp +++ b/dom/cache/QuotaClient.cpp @@ -8,7 +8,6 @@ #include "mozilla/DebugOnly.h" #include "mozilla/dom/cache/Manager.h" -#include "mozilla/dom/cache/OfflineStorage.h" #include "mozilla/dom/quota/QuotaManager.h" #include "mozilla/dom/quota/UsageInfo.h" #include "nsIFile.h" @@ -18,8 +17,8 @@ namespace { using mozilla::DebugOnly; +using mozilla::dom::ContentParentId; using mozilla::dom::cache::Manager; -using mozilla::dom::cache::OfflineStorage; using mozilla::dom::quota::Client; using mozilla::dom::quota::PersistenceType; using mozilla::dom::quota::QuotaManager; @@ -62,41 +61,6 @@ GetBodyUsage(nsIFile* aDir, UsageInfo* aUsageInfo) return NS_OK; } -class StoragesDestroyedRunnable final : public nsRunnable -{ - uint32_t mExpectedCalls; - nsCOMPtr mCallback; - -public: - StoragesDestroyedRunnable(uint32_t aExpectedCalls, nsIRunnable* aCallback) - : mExpectedCalls(aExpectedCalls) - , mCallback(aCallback) - { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mExpectedCalls); - MOZ_ASSERT(mCallback); - } - - NS_IMETHOD Run() override - { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mExpectedCalls); - mExpectedCalls -= 1; - if (!mExpectedCalls) { - mCallback->Run(); - } - return NS_OK; - } - -private: - ~StoragesDestroyedRunnable() - { - // This is a callback runnable and not used for thread dispatch. It should - // always be destroyed on the main thread. - MOZ_ASSERT(NS_IsMainThread()); - } -}; - class CacheQuotaClient final : public Client { public: @@ -209,24 +173,30 @@ public: } virtual void - WaitForStoragesToComplete(nsTArray& aStorages, - nsIRunnable* aCallback) override + AbortOperations(const nsACString& aOrigin) override { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!aStorages.IsEmpty()); - nsCOMPtr callback = - new StoragesDestroyedRunnable(aStorages.Length(), aCallback); - - for (uint32_t i = 0; i < aStorages.Length(); ++i) { - MOZ_ASSERT(aStorages[i]->GetClient()); - MOZ_ASSERT(aStorages[i]->GetClient()->GetType() == Client::DOMCACHE); - nsRefPtr storage = - static_cast(aStorages[i]); - storage->AddDestroyCallback(callback); - } + Manager::AbortOnMainThread(aOrigin); } + virtual void + AbortOperationsForProcess(ContentParentId aContentParentId) override + { + // The Cache and Context can be shared by multiple client processes. They + // are not exclusively owned by a single process. + // + // As far as I can tell this is used by QuotaManager to abort operations + // when a particular process goes away. We definitely don't want this + // since we are shared. Also, the Cache actor code already properly + // handles asynchronous actor destruction when the child process dies. + // + // Therefore, do nothing here. + } + + virtual void + PerformIdleMaintenance() override + { } virtual void ShutdownWorkThreads() override diff --git a/dom/cache/Types.h b/dom/cache/Types.h index b67f73c06b..91e60fbe0c 100644 --- a/dom/cache/Types.h +++ b/dom/cache/Types.h @@ -33,7 +33,6 @@ struct QuotaInfo nsCOMPtr mDir; nsCString mGroup; nsCString mOrigin; - nsCString mStorageId; bool mIsApp; }; diff --git a/dom/cache/moz.build b/dom/cache/moz.build index ee6ed50f6d..b7d8624f02 100644 --- a/dom/cache/moz.build +++ b/dom/cache/moz.build @@ -30,7 +30,6 @@ EXPORTS.mozilla.dom.cache += [ 'IPCUtils.h', 'Manager.h', 'ManagerId.h', - 'OfflineStorage.h', 'PrincipalVerifier.h', 'QuotaClient.h', 'ReadStream.h', @@ -65,7 +64,6 @@ UNIFIED_SOURCES += [ 'FileUtils.cpp', 'Manager.cpp', 'ManagerId.cpp', - 'OfflineStorage.cpp', 'PrincipalVerifier.cpp', 'QuotaClient.cpp', 'ReadStream.cpp', diff --git a/dom/cache/test/mochitest/mochitest.ini b/dom/cache/test/mochitest/mochitest.ini index 3cfe425a5c..9f85fa5fb6 100644 --- a/dom/cache/test/mochitest/mochitest.ini +++ b/dom/cache/test/mochitest/mochitest.ini @@ -39,3 +39,5 @@ support-files = skip-if = buildapp == 'b2g' # bug 1162353 [test_cache_restart.html] [test_cache_shrink.html] +[test_cache_orphaned_cache.html] +[test_cache_orphaned_body.html] diff --git a/dom/cache/test/mochitest/test_cache_orphaned_body.html b/dom/cache/test/mochitest/test_cache_orphaned_body.html new file mode 100644 index 0000000000..f072c20c64 --- /dev/null +++ b/dom/cache/test/mochitest/test_cache_orphaned_body.html @@ -0,0 +1,178 @@ + + + + + Test Cache with QuotaManager Restart + + + + + + + + diff --git a/dom/cache/test/mochitest/test_cache_orphaned_cache.html b/dom/cache/test/mochitest/test_cache_orphaned_cache.html new file mode 100644 index 0000000000..2a4ed5c013 --- /dev/null +++ b/dom/cache/test/mochitest/test_cache_orphaned_cache.html @@ -0,0 +1,171 @@ + + + + + Test Cache with QuotaManager Restart + + + + + + + + diff --git a/dom/cellbroadcast/CellBroadcast.cpp b/dom/cellbroadcast/CellBroadcast.cpp index c6b5bf4a1c..c6a2dbe7e1 100644 --- a/dom/cellbroadcast/CellBroadcast.cpp +++ b/dom/cellbroadcast/CellBroadcast.cpp @@ -152,7 +152,7 @@ NS_CreateCellBroadcastService() { nsCOMPtr service; - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { service = new mozilla::dom::cellbroadcast::CellBroadcastIPCService(); #if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL) } else { diff --git a/dom/datastore/DataStoreService.cpp b/dom/datastore/DataStoreService.cpp index b7a4344d71..7a37c38690 100644 --- a/dom/datastore/DataStoreService.cpp +++ b/dom/datastore/DataStoreService.cpp @@ -51,8 +51,8 @@ #include "nsXULAppAPI.h" #define ASSERT_PARENT_PROCESS() \ - AssertIsInMainProcess(); \ - if (NS_WARN_IF(!IsMainProcess())) { \ + MOZ_ASSERT(XRE_IsParentProcess()); \ + if (NS_WARN_IF(!XRE_IsParentProcess())) { \ return NS_ERROR_FAILURE; \ } @@ -125,20 +125,6 @@ static uint64_t gCounterID = 0; typedef nsClassHashtable HashApp; -bool -IsMainProcess() -{ - static const bool isMainProcess = - XRE_GetProcessType() == GeckoProcessType_Default; - return isMainProcess; -} - -void -AssertIsInMainProcess() -{ - MOZ_ASSERT(IsMainProcess()); -} - void RejectPromise(nsPIDOMWindow* aWindow, Promise* aPromise, nsresult aRv) { @@ -161,8 +147,7 @@ void DeleteDatabase(const nsAString& aName, const nsAString& aManifestURL) { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); nsRefPtr db = new DataStoreDB(aManifestURL, aName); db->Delete(); @@ -174,8 +159,7 @@ DeleteDataStoresAppEnumerator( nsAutoPtr& aInfo, void* aUserData) { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); auto* appId = static_cast(aUserData); if (*appId != aAppId) { @@ -191,8 +175,7 @@ DeleteDataStoresEnumerator(const nsAString& aName, nsAutoPtr& aApps, void* aUserData) { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); aApps->Enumerate(DeleteDataStoresAppEnumerator, aUserData); return aApps->Count() ? PL_DHASH_NEXT : PL_DHASH_REMOVE; @@ -215,8 +198,7 @@ ResetPermission(uint32_t aAppId, const nsAString& aOriginURL, const nsAString& aPermission, bool aReadOnly) { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); nsresult rv; nsCOMPtr ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv)); @@ -347,8 +329,7 @@ GetDataStoreInfosEnumerator(const uint32_t& aAppId, DataStoreInfo* aInfo, void* aUserData) { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); auto* data = static_cast(aUserData); if (aAppId == data->mAppId) { @@ -384,8 +365,7 @@ GetAppManifestURLsEnumerator(const uint32_t& aAppId, DataStoreInfo* aInfo, void* aUserData) { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); auto* manifestURLs = static_cast(aUserData); nsCOMPtr manifestURL(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID)); @@ -417,8 +397,7 @@ AddPermissionsEnumerator(const uint32_t& aAppId, DataStoreInfo* aInfo, void* userData) { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); auto* data = static_cast(userData); @@ -457,8 +436,7 @@ AddAccessPermissionsEnumerator(const uint32_t& aAppId, DataStoreInfo* aInfo, void* userData) { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); auto* data = static_cast(userData); @@ -517,15 +495,13 @@ public: , mName(aName) , mManifestURL(aManifestURL) { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); } void Run(const nsAString& aRevisionId) { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); nsRefPtr service = DataStoreService::Get(); MOZ_ASSERT(service); @@ -553,15 +529,13 @@ public: , mName(aName) , mManifestURL(aManifestURL) { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); } void Run(DataStoreDB* aDb, RunStatus aStatus) override { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); MOZ_ASSERT(aDb); if (aStatus == Error) { @@ -642,8 +616,7 @@ public: NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) override { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); nsRefPtr request; request.swap(mRequest); @@ -830,7 +803,7 @@ DataStoreService::Shutdown() MOZ_ASSERT(NS_IsMainThread()); if (gDataStoreService) { - if (IsMainProcess()) { + if (XRE_IsParentProcess()) { nsCOMPtr obs = mozilla::services::GetObserverService(); if (obs) { obs->RemoveObserver(gDataStoreService, "webapps-clear-data"); @@ -863,7 +836,7 @@ DataStoreService::~DataStoreService() nsresult DataStoreService::Init() { - if (!IsMainProcess()) { + if (!XRE_IsParentProcess()) { return NS_OK; } @@ -973,7 +946,7 @@ DataStoreService::GetDataStores(nsIDOMWindow* aWindow, // If this request comes from the main process, we have access to the // window, so we can skip the ipc communication. - if (IsMainProcess()) { + if (XRE_IsParentProcess()) { uint32_t appId; nsresult rv = principal->GetAppId(&appId); if (NS_FAILED(rv)) { @@ -1132,8 +1105,7 @@ DataStoreService::GetDataStoreInfos(const nsAString& aName, nsIPrincipal* aPrincipal, nsTArray& aStores) { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); nsCOMPtr appsService = do_GetService("@mozilla.org/AppsService;1"); @@ -1242,8 +1214,7 @@ DataStoreService::CheckPermission(nsIPrincipal* aPrincipal, void DataStoreService::DeleteDataStores(uint32_t aAppId) { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); mStores.Enumerate(DeleteDataStoresEnumerator, &aAppId); mAccessStores.Enumerate(DeleteDataStoresEnumerator, &aAppId); @@ -1254,8 +1225,7 @@ DataStoreService::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); if (strcmp(aTopic, "webapps-clear-data")) { return NS_OK; @@ -1294,8 +1264,7 @@ DataStoreService::AddPermissions(uint32_t aAppId, const nsAString& aManifestURL, bool aReadOnly) { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); // This is the permission name. nsString permission; @@ -1327,8 +1296,7 @@ DataStoreService::AddAccessPermissions(uint32_t aAppId, const nsAString& aName, const nsAString& aManifestURL, bool aReadOnly) { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); // When an app wants to have access to a DataStore, the permissions must be // set. @@ -1349,8 +1317,7 @@ DataStoreService::CreateFirstRevisionId(uint32_t aAppId, const nsAString& aName, const nsAString& aManifestURL) { - AssertIsInMainProcess(); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); nsRefPtr db = new DataStoreDB(aManifestURL, aName); @@ -1380,7 +1347,7 @@ DataStoreService::EnableDataStore(uint32_t aAppId, const nsAString& aName, } // Notify the child processes. - if (IsMainProcess()) { + if (XRE_IsParentProcess()) { nsTArray children; ContentParent::GetAll(children); for (uint32_t i = 0; i < children.Length(); i++) { @@ -1447,8 +1414,7 @@ DataStoreService::GetDataStoresFromIPC(const nsAString& aName, nsIPrincipal* aPrincipal, nsTArray* aValue) { - MOZ_ASSERT(IsMainProcess()); - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(XRE_IsParentProcess() && NS_IsMainThread()); uint32_t appId; nsresult rv = aPrincipal->GetAppId(&appId); diff --git a/dom/devicestorage/nsDeviceStorage.cpp b/dom/devicestorage/nsDeviceStorage.cpp index 0837576351..4ef7f703bd 100644 --- a/dom/devicestorage/nsDeviceStorage.cpp +++ b/dom/devicestorage/nsDeviceStorage.cpp @@ -560,7 +560,7 @@ FileUpdateDispatcher::Observe(nsISupports* aSubject, return NS_OK; } - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { // Child process. Forward the notification to the parent. ContentChild::GetSingleton() ->SendFilePathUpdateNotify(dsf->mStorageType, @@ -691,7 +691,7 @@ DeviceStorageFile::Dump(const char* label) path = NS_LITERAL_STRING("(null)"); } const char* ptStr; - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { ptStr = "parent"; } else { ptStr = "child"; @@ -813,7 +813,7 @@ OverrideRootDir::Init() } if (sDirs->overrideRootDir) { - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { // Only the parent process can create directories. In testing, because // the preference is updated after startup, its entirely possible that // the preference updated notification will be received by a child @@ -935,7 +935,7 @@ InitDirs() } #endif - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { NS_GetSpecialDirectory("UAppData", getter_AddRefs(sDirs->crashes)); if (sDirs->crashes) { sDirs->crashes->Append(NS_LITERAL_STRING("Crash Reports")); @@ -2057,7 +2057,7 @@ ContinueCursorEvent::~ContinueCursorEvent() {} void ContinueCursorEvent::Continue() { - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { DebugOnly rv = NS_DispatchToMainThread(this); MOZ_ASSERT(NS_SUCCEEDED(rv)); return; @@ -2242,7 +2242,7 @@ nsDOMDeviceStorageCursor::Allow(JS::HandleValue aChoices) return NS_DispatchToMainThread(r); } - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(this, mFile); DeviceStorageEnumerationParams params(mFile->mStorageType, @@ -3011,7 +3011,7 @@ public: return NS_DispatchToCurrentThread(r); } - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { DeviceStorageCreateFdParams params; params.type() = mFile->mStorageType; @@ -3051,7 +3051,7 @@ public: return NS_DispatchToCurrentThread(r); } - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { BlobChild* actor = ContentChild::GetSingleton()->GetOrCreateActorForBlob( static_cast(mBlob.get())); @@ -3097,7 +3097,7 @@ public: return NS_DispatchToCurrentThread(r); } - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { BlobChild* actor = ContentChild::GetSingleton()->GetOrCreateActorForBlob( static_cast(mBlob.get())); @@ -3143,7 +3143,7 @@ public: return NS_DispatchToCurrentThread(r); } - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); DeviceStorageGetParams params(mFile->mStorageType, @@ -3177,7 +3177,7 @@ public: return NS_DispatchToCurrentThread(r); } - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); DeviceStorageDeleteParams params(mFile->mStorageType, @@ -3193,7 +3193,7 @@ public: case DEVICE_STORAGE_REQUEST_FREE_SPACE: { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); DeviceStorageFreeSpaceParams params(mFile->mStorageType, @@ -3208,7 +3208,7 @@ public: case DEVICE_STORAGE_REQUEST_USED_SPACE: { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); DeviceStorageUsedSpaceParams params(mFile->mStorageType, @@ -3229,7 +3229,7 @@ public: case DEVICE_STORAGE_REQUEST_AVAILABLE: { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); DeviceStorageAvailableParams params(mFile->mStorageType, @@ -3244,7 +3244,7 @@ public: case DEVICE_STORAGE_REQUEST_STATUS: { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); DeviceStorageStatusParams params(mFile->mStorageType, @@ -3265,7 +3265,7 @@ public: case DEVICE_STORAGE_REQUEST_FORMAT: { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); DeviceStorageFormatParams params(mFile->mStorageType, @@ -3280,7 +3280,7 @@ public: case DEVICE_STORAGE_REQUEST_MOUNT: { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); DeviceStorageMountParams params(mFile->mStorageType, @@ -3295,7 +3295,7 @@ public: case DEVICE_STORAGE_REQUEST_UNMOUNT: { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); DeviceStorageUnmountParams params(mFile->mStorageType, diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index 9376fb6632..135163ecc1 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -607,7 +607,7 @@ EventStateManager::PreHandleEvent(nsPresContext* aPresContext, // window edge) wont update the cursor if the cached value and the current // cursor match. So when the mouse exits a remote frame, clear the cached // widget cursor so a proper update will occur when the mouse re-enters. - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { ClearCachedWidgetCursor(mCurrentTarget); } diff --git a/dom/events/IMEStateManager.cpp b/dom/events/IMEStateManager.cpp index d3d581bd61..3ea48ffc20 100644 --- a/dom/events/IMEStateManager.cpp +++ b/dom/events/IMEStateManager.cpp @@ -892,7 +892,7 @@ IMEStateManager::SetIMEState(const IMEState& aState, // XXX I think that we should use nsContentUtils::IsCallerChrome() instead // of the process type. if (aAction.mCause == InputContextAction::CAUSE_UNKNOWN && - XRE_GetProcessType() != GeckoProcessType_Content) { + !XRE_IsContentProcess()) { aAction.mCause = InputContextAction::CAUSE_UNKNOWN_CHROME; } diff --git a/dom/filehandle/FileStreamWrappers.cpp b/dom/filehandle/FileStreamWrappers.cpp index b796691d8e..014ede2bab 100644 --- a/dom/filehandle/FileStreamWrappers.cpp +++ b/dom/filehandle/FileStreamWrappers.cpp @@ -242,7 +242,7 @@ void FileInputStreamWrapper::Serialize(InputStreamParams& aParams, FileDescriptorArray& /* aFDs */) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); nsCOMPtr thisStream = do_QueryObject(this); diff --git a/dom/filesystem/CreateDirectoryTask.cpp b/dom/filesystem/CreateDirectoryTask.cpp index 9891ed68f4..de3733ea1e 100644 --- a/dom/filesystem/CreateDirectoryTask.cpp +++ b/dom/filesystem/CreateDirectoryTask.cpp @@ -39,7 +39,7 @@ CreateDirectoryTask::CreateDirectoryTask( FileSystemRequestParent* aParent) : FileSystemTaskBase(aFileSystem, aParam, aParent) { - MOZ_ASSERT(FileSystemUtils::IsParentProcess(), + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); @@ -84,7 +84,7 @@ CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aVal nsresult CreateDirectoryTask::Work() { - MOZ_ASSERT(FileSystemUtils::IsParentProcess(), + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!"); diff --git a/dom/filesystem/CreateFileTask.cpp b/dom/filesystem/CreateFileTask.cpp index 7009fba6fa..ece56f2f9d 100644 --- a/dom/filesystem/CreateFileTask.cpp +++ b/dom/filesystem/CreateFileTask.cpp @@ -39,7 +39,7 @@ CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem, MOZ_ASSERT(aFileSystem); GetOutputBufferSize(); if (aBlobData) { - if (FileSystemUtils::IsParentProcess()) { + if (XRE_IsParentProcess()) { aBlobData->GetInternalStream(getter_AddRefs(mBlobStream), aRv); NS_WARN_IF(aRv.Failed()); } else { @@ -61,7 +61,7 @@ CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem, : FileSystemTaskBase(aFileSystem, aParam, aParent) , mReplace(false) { - MOZ_ASSERT(FileSystemUtils::IsParentProcess(), + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); @@ -168,7 +168,7 @@ CreateFileTask::Work() nsCOMPtr mStream; }; - MOZ_ASSERT(FileSystemUtils::IsParentProcess(), + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!"); @@ -324,7 +324,7 @@ CreateFileTask::GetPermissionAccessType(nsCString& aAccess) const void CreateFileTask::GetOutputBufferSize() const { - if (sOutputBufferSize || !FileSystemUtils::IsParentProcess()) { + if (sOutputBufferSize || !XRE_IsParentProcess()) { return; } sOutputBufferSize = diff --git a/dom/filesystem/DeviceStorageFileSystem.cpp b/dom/filesystem/DeviceStorageFileSystem.cpp index 3f1da032af..6e254d4d1f 100644 --- a/dom/filesystem/DeviceStorageFileSystem.cpp +++ b/dom/filesystem/DeviceStorageFileSystem.cpp @@ -47,7 +47,7 @@ DeviceStorageFileSystem::DeviceStorageFileSystem( // Get the local path of the file system root. // Since the child process is not allowed to access the file system, we only // do this from the parent process. - if (!FileSystemUtils::IsParentProcess()) { + if (!XRE_IsParentProcess()) { return; } nsCOMPtr rootFile; @@ -100,7 +100,7 @@ DeviceStorageFileSystem::GetWindow() const already_AddRefed DeviceStorageFileSystem::GetLocalFile(const nsAString& aRealPath) const { - MOZ_ASSERT(FileSystemUtils::IsParentProcess(), + MOZ_ASSERT(XRE_IsParentProcess(), "Should be on parent process!"); nsAutoString localPath; FileSystemUtils::NormalizedPathToLocalPath(aRealPath, localPath); @@ -116,7 +116,7 @@ DeviceStorageFileSystem::GetLocalFile(const nsAString& aRealPath) const bool DeviceStorageFileSystem::GetRealPath(BlobImpl* aFile, nsAString& aRealPath) const { - MOZ_ASSERT(FileSystemUtils::IsParentProcess(), + MOZ_ASSERT(XRE_IsParentProcess(), "Should be on parent process!"); MOZ_ASSERT(aFile, "aFile Should not be null."); @@ -141,7 +141,7 @@ DeviceStorageFileSystem::GetRootName() const bool DeviceStorageFileSystem::IsSafeFile(nsIFile* aFile) const { - MOZ_ASSERT(FileSystemUtils::IsParentProcess(), + MOZ_ASSERT(XRE_IsParentProcess(), "Should be on parent process!"); MOZ_ASSERT(aFile); diff --git a/dom/filesystem/FileSystemTaskBase.cpp b/dom/filesystem/FileSystemTaskBase.cpp index 97b19c3478..8f52f6b239 100644 --- a/dom/filesystem/FileSystemTaskBase.cpp +++ b/dom/filesystem/FileSystemTaskBase.cpp @@ -35,7 +35,7 @@ FileSystemTaskBase::FileSystemTaskBase(FileSystemBase* aFileSystem, , mFileSystem(aFileSystem) , mRequestParent(aParent) { - MOZ_ASSERT(FileSystemUtils::IsParentProcess(), + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem, "aFileSystem should not be null."); @@ -61,7 +61,7 @@ FileSystemTaskBase::Start() return; } - if (FileSystemUtils::IsParentProcess()) { + if (XRE_IsParentProcess()) { // Run in parent process. // Start worker thread. nsCOMPtr target @@ -121,7 +121,7 @@ FileSystemTaskBase::HandleResult() FileSystemResponseValue FileSystemTaskBase::GetRequestResult() const { - MOZ_ASSERT(FileSystemUtils::IsParentProcess(), + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); if (HasError()) { @@ -134,7 +134,7 @@ FileSystemTaskBase::GetRequestResult() const void FileSystemTaskBase::SetRequestResult(const FileSystemResponseValue& aValue) { - MOZ_ASSERT(!FileSystemUtils::IsParentProcess(), + MOZ_ASSERT(!XRE_IsParentProcess(), "Only call from child process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); if (aValue.type() == FileSystemResponseValue::TFileSystemErrorResponse) { @@ -156,7 +156,7 @@ FileSystemTaskBase::Recv__delete__(const FileSystemResponseValue& aValue) BlobParent* FileSystemTaskBase::GetBlobParent(BlobImpl* aFile) const { - MOZ_ASSERT(FileSystemUtils::IsParentProcess(), + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFile); diff --git a/dom/filesystem/FileSystemUtils.cpp b/dom/filesystem/FileSystemUtils.cpp index 6161cce7ce..cf175bcaa2 100644 --- a/dom/filesystem/FileSystemUtils.cpp +++ b/dom/filesystem/FileSystemUtils.cpp @@ -65,12 +65,5 @@ FileSystemUtils::IsDescendantPath(const nsAString& aPath, return true; } -// static -bool -FileSystemUtils::IsParentProcess() -{ - return XRE_GetProcessType() == GeckoProcessType_Default; -} - } // namespace dom } // namespace mozilla diff --git a/dom/filesystem/FileSystemUtils.h b/dom/filesystem/FileSystemUtils.h index 1203dfbb2d..abf30c3cf5 100644 --- a/dom/filesystem/FileSystemUtils.h +++ b/dom/filesystem/FileSystemUtils.h @@ -41,9 +41,6 @@ public: static bool IsDescendantPath(const nsAString& aPath, const nsAString& aDescendantPath); - static bool - IsParentProcess(); - static const char16_t kSeparatorChar = char16_t('/'); }; diff --git a/dom/filesystem/GetDirectoryListingTask.cpp b/dom/filesystem/GetDirectoryListingTask.cpp index 51b975dbad..9fdb120ab9 100644 --- a/dom/filesystem/GetDirectoryListingTask.cpp +++ b/dom/filesystem/GetDirectoryListingTask.cpp @@ -42,7 +42,7 @@ GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem, FileSystemRequestParent* aParent) : FileSystemTaskBase(aFileSystem, aParam, aParent) { - MOZ_ASSERT(FileSystemUtils::IsParentProcess(), + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); @@ -105,7 +105,7 @@ GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue& nsresult GetDirectoryListingTask::Work() { - MOZ_ASSERT(FileSystemUtils::IsParentProcess(), + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!"); @@ -222,7 +222,7 @@ GetDirectoryListingTask::HandlerCallback() path.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR); path.Append(name); #ifdef DEBUG - if (FileSystemUtils::IsParentProcess()) { + if (XRE_IsParentProcess()) { nsCOMPtr file = mFileSystem->GetLocalFile(path); bool exist; file->Exists(&exist); diff --git a/dom/filesystem/GetFileOrDirectoryTask.cpp b/dom/filesystem/GetFileOrDirectoryTask.cpp index 26150bc698..fcf96e9901 100644 --- a/dom/filesystem/GetFileOrDirectoryTask.cpp +++ b/dom/filesystem/GetFileOrDirectoryTask.cpp @@ -47,7 +47,7 @@ GetFileOrDirectoryTask::GetFileOrDirectoryTask( : FileSystemTaskBase(aFileSystem, aParam, aParent) , mIsDirectory(false) { - MOZ_ASSERT(FileSystemUtils::IsParentProcess(), + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); @@ -119,7 +119,7 @@ GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& a nsresult GetFileOrDirectoryTask::Work() { - MOZ_ASSERT(FileSystemUtils::IsParentProcess(), + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!"); diff --git a/dom/filesystem/RemoveTask.cpp b/dom/filesystem/RemoveTask.cpp index 4ab6ebe89d..78a66ce2b5 100644 --- a/dom/filesystem/RemoveTask.cpp +++ b/dom/filesystem/RemoveTask.cpp @@ -49,7 +49,7 @@ RemoveTask::RemoveTask(FileSystemBase* aFileSystem, , mRecursive(false) , mReturnValue(false) { - MOZ_ASSERT(FileSystemUtils::IsParentProcess(), + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); @@ -123,7 +123,7 @@ RemoveTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue) nsresult RemoveTask::Work() { - MOZ_ASSERT(FileSystemUtils::IsParentProcess(), + MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!"); MOZ_ASSERT(!NS_IsMainThread(), "Only call on worker thread!"); diff --git a/dom/fmradio/FMRadioService.cpp b/dom/fmradio/FMRadioService.cpp index c8d9798bda..89e9c86667 100644 --- a/dom/fmradio/FMRadioService.cpp +++ b/dom/fmradio/FMRadioService.cpp @@ -42,7 +42,7 @@ BEGIN_FMRADIO_NAMESPACE IFMRadioService* IFMRadioService::Singleton() { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return FMRadioChild::Singleton(); } else { return FMRadioService::Singleton(); @@ -1240,7 +1240,7 @@ FMRadioService::UpdateFrequency() FMRadioService* FMRadioService::Singleton() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); if (!sFMRadioService) { diff --git a/dom/fmradio/ipc/FMRadioChild.cpp b/dom/fmradio/ipc/FMRadioChild.cpp index 240c2e7e82..729504cd12 100644 --- a/dom/fmradio/ipc/FMRadioChild.cpp +++ b/dom/fmradio/ipc/FMRadioChild.cpp @@ -338,7 +338,7 @@ FMRadioChild::EnableAudio(bool aAudioEnabled) FMRadioChild* FMRadioChild::Singleton() { - MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default); + MOZ_ASSERT(!XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); if (!sFMRadioChild) { diff --git a/dom/gamepad/GamepadFunctions.cpp b/dom/gamepad/GamepadFunctions.cpp index 2a643cf619..c2e6289207 100644 --- a/dom/gamepad/GamepadFunctions.cpp +++ b/dom/gamepad/GamepadFunctions.cpp @@ -24,7 +24,7 @@ void NotifyGamepadChange(const T& aInfo) { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); GamepadChangeEvent e(aInfo); nsTArray t; ContentParent::GetAll(t); @@ -44,7 +44,7 @@ AddGamepad(const char* aID, uint32_t aNumButtons, uint32_t aNumAxes) { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); int index = gGamepadIndex; gGamepadIndex++; @@ -59,7 +59,7 @@ void RemoveGamepad(uint32_t aIndex) { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); GamepadRemoved a(aIndex); NotifyGamepadChange(a); } @@ -69,7 +69,7 @@ NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed, double aValue) { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); GamepadButtonInformation a(aIndex, aButton, aPressed, aValue); NotifyGamepadChange(a); } @@ -79,7 +79,7 @@ NewButtonEvent(uint32_t aIndex, uint32_t aButton, bool aPressed) { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); // When only a digital button is available the value will be synthesized. NewButtonEvent(aIndex, aButton, aPressed, aPressed ? 1.0L : 0.0L); } @@ -89,7 +89,7 @@ NewAxisMoveEvent(uint32_t aIndex, uint32_t aAxis, double aValue) { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); GamepadAxisInformation a(aIndex, aAxis, aValue); NotifyGamepadChange(a); } @@ -98,7 +98,7 @@ void ResetGamepadIndexes() { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); gGamepadIndex = 0; } diff --git a/dom/gamepad/GamepadMonitoring.cpp b/dom/gamepad/GamepadMonitoring.cpp index 05667dcd81..795a9d0916 100644 --- a/dom/gamepad/GamepadMonitoring.cpp +++ b/dom/gamepad/GamepadMonitoring.cpp @@ -17,7 +17,7 @@ void MaybeStopGamepadMonitoring() { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); nsTArray t; ContentParent::GetAll(t); for(uint32_t i = 0; i < t.Length(); ++i) { diff --git a/dom/gamepad/GamepadService.cpp b/dom/gamepad/GamepadService.cpp index b6fee91e6a..f42362c458 100644 --- a/dom/gamepad/GamepadService.cpp +++ b/dom/gamepad/GamepadService.cpp @@ -85,7 +85,7 @@ GamepadService::BeginShutdown() mTimer->Cancel(); } if (mStarted) { - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { MaybeStopGamepadMonitoring(); } else { ContentChild::GetSingleton()->SendGamepadListenerRemoved(); @@ -115,7 +115,7 @@ GamepadService::AddListener(nsGlobalWindow* aWindow) } if (!mStarted && mEnabled) { - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { StartGamepadMonitoring(); } else { ContentChild::GetSingleton()->SendGamepadListenerAdded(); @@ -500,7 +500,7 @@ GamepadService::TimeoutHandler(nsITimer* aTimer, void* aClosure) } if (self->mListeners.Length() == 0) { - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { MaybeStopGamepadMonitoring(); } else { ContentChild::GetSingleton()->SendGamepadListenerRemoved(); diff --git a/dom/geolocation/nsGeolocation.cpp b/dom/geolocation/nsGeolocation.cpp index 0e18dda3e9..5276775556 100644 --- a/dom/geolocation/nsGeolocation.cpp +++ b/dom/geolocation/nsGeolocation.cpp @@ -562,7 +562,7 @@ already_AddRefed nsGeolocationRequest::AdjustedLocation(nsIDOMGeoPosition *aPosition) { nsCOMPtr pos = aPosition; - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { GPSLOG("child process just copying position"); return pos.forget(); } @@ -756,7 +756,7 @@ nsresult nsGeolocationService::Init() return NS_ERROR_FAILURE; } - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { sGeoInitPending = false; return NS_OK; } @@ -1008,7 +1008,7 @@ nsGeolocationService::StartDevice(nsIPrincipal *aPrincipal) // inactivivity SetDisconnectTimer(); - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { ContentChild* cpc = ContentChild::GetSingleton(); cpc->SendAddGeolocationListener(IPC::Principal(aPrincipal), HighAccuracyRequested()); @@ -1071,7 +1071,7 @@ nsGeolocationService::UpdateAccuracy(bool aForceHigh) { bool highRequired = aForceHigh || HighAccuracyRequested(); - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { ContentChild* cpc = ContentChild::GetSingleton(); if (cpc->IsAlive()) { cpc->SendSetGeolocationHigherAccuracy(highRequired); @@ -1098,7 +1098,7 @@ nsGeolocationService::StopDevice() mDisconnectTimer = nullptr; } - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { ContentChild* cpc = ContentChild::GetSingleton(); cpc->SendRemoveGeolocationListener(); return; // bail early diff --git a/dom/geolocation/nsGeolocationSettings.cpp b/dom/geolocation/nsGeolocationSettings.cpp index f89e8fa1c1..f8a0ac19a2 100644 --- a/dom/geolocation/nsGeolocationSettings.cpp +++ b/dom/geolocation/nsGeolocationSettings.cpp @@ -49,7 +49,7 @@ already_AddRefed nsGeolocationSettings::GetGeolocationSettings() { // this singleton is only needed in the parent process... - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { return nullptr; } diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index a3470a163e..09da9dfdaf 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -1058,7 +1058,7 @@ UploadLastDir::FetchDirectoryAndDisplayPicker(nsIDocument* aDoc, new UploadLastDir::ContentPrefCallback(aFilePicker, aFpCallback); #ifdef MOZ_B2G - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { prefCallback->HandleCompletion(nsIContentPrefCallback2::COMPLETE_ERROR); return NS_OK; } @@ -1089,7 +1089,7 @@ UploadLastDir::StoreLastUsedDirectory(nsIDocument* aDoc, nsIFile* aDir) } #ifdef MOZ_B2G - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { return NS_OK; } #endif @@ -2417,7 +2417,7 @@ HTMLInputElement::MozSetFileArray(const Sequence>& aFiles) void HTMLInputElement::MozSetFileNameArray(const Sequence< nsString >& aFileNames, ErrorResult& aRv) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return; } diff --git a/dom/html/nsGenericHTMLFrameElement.cpp b/dom/html/nsGenericHTMLFrameElement.cpp index bf3ab36c0c..c59d0edefd 100644 --- a/dom/html/nsGenericHTMLFrameElement.cpp +++ b/dom/html/nsGenericHTMLFrameElement.cpp @@ -596,7 +596,7 @@ nsGenericHTMLFrameElement::GetAppManifestURL(nsAString& aOut) // Only allow content process to embed an app when nested content // process is enabled. - if (XRE_GetProcessType() != GeckoProcessType_Default && + if (!XRE_IsParentProcess() && !(GetBoolAttr(nsGkAtoms::Remote) && NestedEnabled())){ NS_WARNING("Can't embed-apps. Embed-apps is restricted to in-proc apps " "or content processes with nested pref enabled, see bug 1097479"); diff --git a/dom/icc/IccManager.cpp b/dom/icc/IccManager.cpp index 231f8d1dbb..057a929e9d 100644 --- a/dom/icc/IccManager.cpp +++ b/dom/icc/IccManager.cpp @@ -146,7 +146,7 @@ NS_CreateIccService() { nsCOMPtr service; - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { service = new mozilla::dom::icc::IccIPCService(); #if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL) } else { diff --git a/dom/indexedDB/ActorsChild.cpp b/dom/indexedDB/ActorsChild.cpp index 1dd55179d4..dc6547bcb7 100644 --- a/dom/indexedDB/ActorsChild.cpp +++ b/dom/indexedDB/ActorsChild.cpp @@ -818,7 +818,7 @@ public: : PermissionRequestBase(aElement, aPrincipal) , mChallenge(aChallenge) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aChallenge); } @@ -844,7 +844,7 @@ public: WorkerPermissionChallenge* aChallenge) : mChallenge(aChallenge) { - MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default); + MOZ_ASSERT(!XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aChallenge); } @@ -950,7 +950,7 @@ private: return true; } - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { nsCOMPtr ownerElement = do_QueryInterface(window->GetChromeEventHandler()); if (NS_WARN_IF(!ownerElement)) { @@ -1387,7 +1387,7 @@ BackgroundFactoryRequestChild::RecvPermissionChallenge( return false; } - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { nsCOMPtr window = mFactory->GetParentObject(); MOZ_ASSERT(window); diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index bfe12b0e70..340559af45 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -20,6 +20,7 @@ #include "mozilla/AppProcessChecker.h" #include "mozilla/AutoRestore.h" #include "mozilla/Endian.h" +#include "mozilla/Hal.h" #include "mozilla/LazyIdleThread.h" #include "mozilla/Maybe.h" #include "mozilla/Preferences.h" @@ -44,7 +45,6 @@ #include "mozilla/dom/ipc/BlobParent.h" #include "mozilla/dom/quota/Client.h" #include "mozilla/dom/quota/FileStreams.h" -#include "mozilla/dom/quota/OriginOrPatternString.h" #include "mozilla/dom/quota/QuotaManager.h" #include "mozilla/dom/quota/UsageInfo.h" #include "mozilla/ipc/BackgroundParent.h" @@ -64,12 +64,12 @@ #include "nsIEventTarget.h" #include "nsIFile.h" #include "nsIFileURL.h" +#include "nsIIdleService.h" #include "nsIInputStream.h" #include "nsIInterfaceRequestor.h" #include "nsInterfaceHashtable.h" #include "nsIObserver.h" #include "nsIObserverService.h" -#include "nsIOfflineStorage.h" #include "nsIOutputStream.h" #include "nsIPrincipal.h" #include "nsIScriptSecurityManager.h" @@ -83,10 +83,13 @@ #include "nsPrintfCString.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" +#include "prtime.h" #include "ReportInternalError.h" #include "snappy/snappy.h" @@ -122,7 +125,6 @@ class Database; struct DatabaseActorInfo; class DatabaseLoggingInfo; class DatabaseFile; -class DatabaseOfflineStorage; class Factory; class OpenDatabaseOp; class TransactionBase; @@ -200,11 +202,11 @@ static_assert(kMaxConnectionThreadCount >= kMaxIdleConnectionThreadCount, "Idle thread limit must be less than total thread limit!"); // The length of time that database connections will be held open after all -// transactions have completed. -const uint32_t kConnectionIdleCheckpointsMS = 2 * 1000; // 2 seconds +// transactions have completed before doing idle maintenance. +const uint32_t kConnectionIdleMaintenanceMS = 2 * 1000; // 2 seconds // The length of time that database connections will be held open after all -// transactions and checkpointing have completed. +// transactions and maintenance have completed. const uint32_t kConnectionIdleCloseMS = 10 * 1000; // 10 seconds // The length of time that idle threads will stay alive before being shut down. @@ -234,6 +236,8 @@ enum AppId { kUnknownAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID }; +const char kIdleServiceContractId[] = "@mozilla.org/widget/idleservice;1"; + #ifdef DEBUG const int32_t kDEBUGThreadPriority = nsISupportsPriority::PRIORITY_NORMAL; @@ -245,6 +249,14 @@ const uint32_t kDEBUGTransactionThreadSleepMS = 0; #endif +const bool kRunningXPCShellTests = +#ifdef ENABLE_TESTS + !!PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR") +#else + false +#endif + ; + struct FreeDeleter { void @@ -3531,6 +3543,7 @@ GetDatabaseFileURL(nsIFile* aDatabaseFile, PersistenceType aPersistenceType, const nsACString& aGroup, const nsACString& aOrigin, + uint32_t aTelemetryId, nsIFileURL** aResult) { MOZ_ASSERT(aDatabaseFile); @@ -3548,10 +3561,18 @@ GetDatabaseFileURL(nsIFile* aDatabaseFile, nsAutoCString type; PersistenceTypeToText(aPersistenceType, type); + nsAutoCString telemetryFilenameClause; + if (aTelemetryId) { + telemetryFilenameClause.AssignLiteral("&telemetryFilename=indexedDB-"); + telemetryFilenameClause.AppendInt(aTelemetryId); + telemetryFilenameClause.AppendLiteral(".sqlite"); + } + rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("persistenceType=") + type + NS_LITERAL_CSTRING("&group=") + aGroup + NS_LITERAL_CSTRING("&origin=") + aOrigin + - NS_LITERAL_CSTRING("&cache=private")); + NS_LITERAL_CSTRING("&cache=private") + + telemetryFilenameClause); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -3799,6 +3820,7 @@ CreateStorageConnection(nsIFile* aDBFile, PersistenceType aPersistenceType, const nsACString& aGroup, const nsACString& aOrigin, + uint32_t aTelemetryId, mozIStorageConnection** aConnection) { AssertIsOnIOThread(); @@ -3826,7 +3848,11 @@ CreateStorageConnection(nsIFile* aDBFile, } nsCOMPtr dbFileUrl; - rv = GetDatabaseFileURL(aDBFile, aPersistenceType, aGroup, aOrigin, + rv = GetDatabaseFileURL(aDBFile, + aPersistenceType, + aGroup, + aOrigin, + aTelemetryId, getter_AddRefs(dbFileUrl)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -4244,30 +4270,24 @@ GetFileForPath(const nsAString& aPath) } nsresult -GetStorageConnection(const nsAString& aDatabaseFilePath, +GetStorageConnection(nsIFile* aDatabaseFile, PersistenceType aPersistenceType, const nsACString& aGroup, const nsACString& aOrigin, + uint32_t aTelemetryId, mozIStorageConnection** aConnection) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(!IsOnBackgroundThread()); - MOZ_ASSERT(!aDatabaseFilePath.IsEmpty()); - MOZ_ASSERT(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite"))); + MOZ_ASSERT(aDatabaseFile); MOZ_ASSERT(aConnection); PROFILER_LABEL("IndexedDB", "GetStorageConnection", js::ProfileEntry::Category::STORAGE); - nsCOMPtr dbFile = GetFileForPath(aDatabaseFilePath); - if (NS_WARN_IF(!dbFile)) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - bool exists; - nsresult rv = dbFile->Exists(&exists); + nsresult rv = aDatabaseFile->Exists(&exists); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -4278,7 +4298,11 @@ GetStorageConnection(const nsAString& aDatabaseFilePath, } nsCOMPtr dbFileUrl; - rv = GetDatabaseFileURL(dbFile, aPersistenceType, aGroup, aOrigin, + rv = GetDatabaseFileURL(aDatabaseFile, + aPersistenceType, + aGroup, + aOrigin, + aTelemetryId, getter_AddRefs(dbFileUrl)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -4310,6 +4334,34 @@ GetStorageConnection(const nsAString& aDatabaseFilePath, return NS_OK; } +nsresult +GetStorageConnection(const nsAString& aDatabaseFilePath, + PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + uint32_t aTelemetryId, + mozIStorageConnection** aConnection) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(!aDatabaseFilePath.IsEmpty()); + MOZ_ASSERT(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite"))); + MOZ_ASSERT(aConnection); + + nsCOMPtr dbFile = GetFileForPath(aDatabaseFilePath); + if (NS_WARN_IF(!dbFile)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + return GetStorageConnection(dbFile, + aPersistenceType, + aGroup, + aOrigin, + aTelemetryId, + aConnection); +} + /******************************************************************************* * ConnectionPool declarations ******************************************************************************/ @@ -4318,6 +4370,13 @@ class DatabaseConnection final { friend class ConnectionPool; + enum CheckpointMode + { + CheckpointMode_Full, + CheckpointMode_Restart, + CheckpointMode_Truncate + }; + public: class AutoSavepoint; class CachedStatement; @@ -4329,6 +4388,7 @@ private: nsInterfaceHashtable mCachedStatements; nsRefPtr mUpdateRefcountFunction; + bool mInReadTransaction; bool mInWriteTransaction; #ifdef DEBUG @@ -4372,6 +4432,9 @@ public: nsresult BeginWriteTransaction(); + nsresult + CommitWriteTransaction(); + void RollbackWriteTransaction(); @@ -4388,7 +4451,15 @@ public: RollbackSavepoint(); nsresult - Checkpoint(bool aIdle); + Checkpoint() + { + AssertIsOnConnectionThread(); + + return CheckpointInternal(CheckpointMode_Full); + } + + void + DoIdleProcessing(bool aNeedsCheckpoint); void Close(); @@ -4401,6 +4472,19 @@ private: nsresult Init(); + + nsresult + CheckpointInternal(CheckpointMode aMode); + + nsresult + GetFreelistCount(CachedStatement& aCachedStatement, uint32_t* aFreelistCount); + + nsresult + ReclaimFreePagesWhileIdle(CachedStatement& aFreelistStatement, + CachedStatement& aRollbackStatement, + uint32_t aFreelistCount, + bool aNeedsCheckpoint, + bool* aFreedSomePages); }; class MOZ_STACK_CLASS DatabaseConnection::AutoSavepoint final @@ -4605,11 +4689,11 @@ public: private: class ConnectionRunnable; - class CheckpointConnectionRunnable; class CloseConnectionRunnable; struct DatabaseInfo; struct DatabasesCompleteCallback; class FinishCallbackWrapper; + class IdleConnectionRunnable; struct IdleDatabaseInfo; struct IdleResource; struct IdleThreadInfo; @@ -4623,6 +4707,7 @@ private: nsTArray mIdleThreads; nsTArray mIdleDatabases; + nsTArray mDatabasesPerformingIdleMaintenance; nsCOMPtr mIdleTimer; TimeStamp mTargetIdleTime; @@ -4733,7 +4818,7 @@ private: MaybeFireCallback(DatabasesCompleteCallback* aCallback); void - CheckpointDatabase(DatabaseInfo* aDatabaseInfo); + PerformIdleDatabaseMaintenance(DatabaseInfo* aDatabaseInfo); void CloseDatabase(DatabaseInfo* aDatabaseInfo); @@ -4757,19 +4842,21 @@ protected: { } }; -class ConnectionPool::CheckpointConnectionRunnable final +class ConnectionPool::IdleConnectionRunnable final : public ConnectionRunnable { + bool mNeedsCheckpoint; + public: - explicit - CheckpointConnectionRunnable(DatabaseInfo* aDatabaseInfo) + IdleConnectionRunnable(DatabaseInfo* aDatabaseInfo, bool aNeedsCheckpoint) : ConnectionRunnable(aDatabaseInfo) + , mNeedsCheckpoint(aNeedsCheckpoint) { } NS_DECL_ISUPPORTS_INHERITED private: - ~CheckpointConnectionRunnable() + ~IdleConnectionRunnable() { } NS_DECL_NSIRUNNABLE @@ -4821,6 +4908,7 @@ struct ConnectionPool::DatabaseInfo final uint32_t mReadTransactionCount; uint32_t mWriteTransactionCount; bool mNeedsCheckpoint; + bool mIdle; bool mCloseOnIdle; bool mClosing; @@ -5088,13 +5176,10 @@ public: AssertIsOnOwningThread() const { AssertIsOnBackgroundThread(); - -#ifdef DEBUG MOZ_ASSERT(mOwningThread); - bool current; + DebugOnly current; MOZ_ASSERT(NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(¤t))); MOZ_ASSERT(current); -#endif } void @@ -5114,7 +5199,8 @@ public: return mActorDestroyed; } - // May be called on any thread. + // 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 { @@ -5478,7 +5564,7 @@ private: ~WaitForTransactionsHelper() { MOZ_ASSERT(!mCallback); - MOZ_ASSERT(mState = State_Complete); + MOZ_ASSERT(mState == State_Complete); } void @@ -5499,6 +5585,27 @@ private: NS_DECL_NSIRUNNABLE }; +class UnlockDirectoryRunnable final + : public nsRunnable +{ + nsRefPtr mDirectoryLock; + +public: + explicit + UnlockDirectoryRunnable(already_AddRefed aDirectoryLock) + : mDirectoryLock(Move(aDirectoryLock)) + { } + +private: + ~UnlockDirectoryRunnable() + { + MOZ_ASSERT(!mDirectoryLock); + } + + NS_IMETHOD + Run() override; +}; + class Database final : public PBackgroundIDBDatabaseParent { @@ -5510,15 +5617,17 @@ private: nsRefPtr mFactory; nsRefPtr mMetadata; nsRefPtr mFileManager; - nsRefPtr mOfflineStorage; + nsRefPtr mDirectoryLock; nsTHashtable> mTransactions; nsRefPtr mConnection; const PrincipalInfo mPrincipalInfo; + const OptionalContentId mOptionalContentParentId; const nsCString mGroup; const nsCString mOrigin; const nsCString mId; const nsString mFilePath; uint32_t mFileHandleCount; + const uint32_t mTelemetryId; const PersistenceType mPersistenceType; const bool mChromeWriteAccessAllowed; bool mClosed; @@ -5531,11 +5640,13 @@ public: // Created by OpenDatabaseOp. Database(Factory* aFactory, const PrincipalInfo& aPrincipalInfo, + const OptionalContentId& aOptionalContentParentId, const nsACString& aGroup, const nsACString& aOrigin, + uint32_t aTelemetryId, FullDatabaseMetadata* aMetadata, FileManager* aFileManager, - already_AddRefed aOfflineStorage, + already_AddRefed aDirectoryLock, bool aChromeWriteAccessAllowed); void @@ -5564,6 +5675,16 @@ public: return mPrincipalInfo; } + bool + IsOwnedByProcess(ContentParentId aContentParentId) const + { + MOZ_ASSERT(mOptionalContentParentId.type() != OptionalContentId::T__None); + + return + mOptionalContentParentId.type() == OptionalContentId::TContentParentId && + mOptionalContentParentId.get_ContentParentId() == aContentParentId; + } + const nsCString& Group() const { @@ -5582,6 +5703,12 @@ public: return mId; } + uint32_t + TelemetryId() const + { + return mTelemetryId; + } + PersistenceType Type() const { @@ -6331,6 +6458,7 @@ private: class FactoryOp : public DatabaseOperationBase + , public OpenDirectoryListener , public PBackgroundIDBFactoryRequestParent { public: @@ -6340,24 +6468,33 @@ protected: enum State { // Just created on the PBackground thread, dispatched to the main thread. - // Next step is State_OpenPending. + // Next step is either State_SendingResults if permission is denied, + // State_PermissionChallenge if the permission is unknown, or + // State_DirectoryOpenPending if permission is granted. State_Initial, - // Waiting for open allowed on the main thread. The next step is either - // State_SendingResults if permission is denied, - // State_PermissionChallenge if the permission is unknown, or - // State_DatabaseWorkOpen if permission is granted. - State_OpenPending, - // Sending a permission challenge message to the child on the PBackground - // thread. Next step is State_PermissionRetryReady. + // thread. Next step is State_PermissionRetry. State_PermissionChallenge, // Retrying permission check after a challenge on the main thread. Next step // is either State_SendingResults if permission is denied or - // State_DatabaseWorkOpen if permission is granted. + // State_DirectoryOpenPending if permission is granted. State_PermissionRetry, + // Waiting for directory open allowed on the main thread. The next step is + // either State_SendingResults if directory lock failed to acquire, or + // State_DirectoryWorkOpen if directory lock is acquired. + State_DirectoryOpenPending, + + // Checking if database open needs to wait on the PBackground thread. + // The next step is State_DatabaseOpenPending. + State_DirectoryWorkOpen, + + // Waiting for database open allowed on the main thread. The next step is + // State_DatabaseWorkOpen. + State_DatabaseOpenPending, + // Waiting to do/doing work on the QuotaManager IO thread. Its next step is // either State_BeginVersionChange if the requested version doesn't match // the existing database version or State_SendingResults if the versions @@ -6369,7 +6506,7 @@ protected: // change is about to happen, and maybe tell the request that a version // change has been blocked. If databases are notified then the next step is // State_WaitingForOtherDatabasesToClose. Otherwise the next step is - // State_DispatchToWorkThread. + // State_WaitingForTransactionsToComplete. State_BeginVersionChange, // Waiting for other databases to close on the PBackground thread. This @@ -6389,13 +6526,9 @@ protected: State_DatabaseWorkVersionChange, // Waiting to send/sending results on the PBackground thread. Next step is - // State_UnblockingQuotaManager. + // State_Completed. State_SendingResults, - // Notifying the QuotaManager that it can proceed to the next operation on - // the main thread. Next step is State_Completed. - State_UnblockingQuotaManager, - // All done. State_Completed }; @@ -6406,6 +6539,10 @@ protected: // Must be released on the main thread! nsRefPtr mContentParent; + // Must be released on the main thread! + nsRefPtr mDirectoryLock; + + nsRefPtr mDelayedOp; nsTArray mMaybeBlockedDatabases; const CommonFactoryRequestParams mCommonParams; @@ -6416,7 +6553,7 @@ protected: bool mIsApp; bool mEnforcingQuota; const bool mDeleting; - bool mBlockedQuotaManager; + bool mBlockedDatabaseOpen; bool mChromeWriteAccessAllowed; public: @@ -6458,6 +6595,9 @@ protected: nsresult RetryCheckPermission(); + nsresult + DirectoryOpen(); + nsresult SendToIOThread(); @@ -6467,9 +6607,6 @@ protected: void FinishSendResults(); - void - UnblockQuotaManager(); - nsresult SendVersionChangeMessages(DatabaseActorInfo* aDatabaseActorInfo, Database* aOpeningDatabase, @@ -6478,7 +6615,7 @@ protected: // Methods that subclasses must implement. virtual nsresult - QuotaManagerOpen() = 0; + DatabaseOpen() = 0; virtual nsresult DoDatabaseWork() = 0; @@ -6492,10 +6629,19 @@ protected: virtual void SendResults() = 0; + NS_DECL_ISUPPORTS_INHERITED + // Common nsIRunnable implementation that subclasses may not override. NS_IMETHOD Run() final; + // OpenDirectoryListener overrides. + virtual void + DirectoryLockAcquired(DirectoryLock* aLock) override; + + virtual void + DirectoryLockFailed() override; + // IPDL methods. virtual void ActorDestroy(ActorDestroyReason aWhy) override; @@ -6517,6 +6663,10 @@ private: nsresult FinishOpen(); + + // Test whether this FactoryOp needs to wait for the given op. + bool + MustWaitFor(const FactoryOp& aExistingOp); }; struct FactoryOp::MaybeBlockedDatabaseInfo final @@ -6576,13 +6726,13 @@ class OpenDatabaseOp final nsRefPtr mDatabase; nsRefPtr mVersionChangeTransaction; - nsRefPtr mOfflineStorage; - // This is only set while a VersionChangeOp is live. It holds a strong // reference to its OpenDatabaseOp object so this is a weak pointer to avoid // cycles. VersionChangeOp* mVersionChangeOp; + uint32_t mTelemetryId; + public: OpenDatabaseOp(Factory* aFactory, already_AddRefed aContentParent, @@ -6633,7 +6783,7 @@ private: ActorDestroy(ActorDestroyReason aWhy) override; virtual nsresult - QuotaManagerOpen() override; + DatabaseOpen() override; virtual nsresult DoDatabaseWork() override; @@ -6720,7 +6870,7 @@ private: LoadPreviousVersion(nsIFile* aDatabaseFile); virtual nsresult - QuotaManagerOpen() override; + DatabaseOpen() override; virtual nsresult DoDatabaseWork() override; @@ -7694,19 +7844,60 @@ private: class QuotaClient final : public mozilla::dom::quota::Client + , public nsIObserver { - class ShutdownWorkThreadsRunnable; - friend class ShutdownWorkThreadsRunnable; + // The number of seconds we will wait after receiving the idle-daily + // notification before beginning maintenance. + static const uint32_t kIdleObserverTimeSec = 1; - class WaitForTransactionsRunnable; - friend class WaitForTransactionsRunnable; + // The minimum amount of time that has passed since the last vacuum before we + // will attempt to analyze the database for fragmentation. + static const PRTime kMinVacuumAge = + PRTime(PR_USEC_PER_SEC) * 60 * 60 * 24 * 7; + + // If the percent of database pages that are not in contiguous order is higher + // than this percentage we will attempt a vacuum. + static const int32_t kPercentUnorderedThreshold = 30; + + // If the percent of file size growth since the last vacuum is higher than + // this percentage we will attempt a vacuum. + static const int32_t kPercentFileSizeGrowthThreshold = 10; + + // The number of freelist pages beyond which we will favor an incremental + // vacuum over a full vacuum. + static const int32_t kMaxFreelistThreshold = 5; + + // If the percent of unused file bytes in the database exceeds this percentage + // then we will attempt a full vacuum. + static const int32_t kPercentUnusedThreshold = 20; + + class AbortOperationsRunnable; + class AutoProgressHandler; + class GetDirectoryLockListener; + struct MaintenanceInfoBase; + struct MultipleMaintenanceInfo; + class ShutdownWorkThreadsRunnable; + struct SingleMaintenanceInfo; + + typedef nsClassHashtable + MaintenanceInfoHashtable; + + enum MaintenanceAction { + MaintenanceAction_Nothing = 0, + MaintenanceAction_IncrementalVacuum, + MaintenanceAction_FullVacuum + }; static QuotaClient* sInstance; nsCOMPtr mBackgroundThread; nsRefPtr mShutdownRunnable; - + nsRefPtr mMaintenanceThreadPool; + PRTime mMaintenanceStartTime; + Atomic mMaintenanceRunId; + UniquePtr mMaintenanceInfoHashtable; bool mShutdownRequested; + bool mIdleObserverRegistered; public: QuotaClient(); @@ -7725,7 +7916,7 @@ public: MOZ_ASSERT(NS_IsMainThread()); if (sInstance) { - return sInstance->mShutdownRequested; + return sInstance->IsShuttingDown(); } return QuotaManager::IsShuttingDown(); @@ -7747,10 +7938,21 @@ public: return mShutdownRequested; } + bool + IdleMaintenanceMustEnd(uint32_t aRunId) const + { + if (mMaintenanceRunId != aRunId) { + MOZ_ASSERT(mMaintenanceRunId > aRunId); + return true; + } + + return false; + } + void NoteBackgroundThread(nsIEventTarget* aBackgroundThread); - NS_INLINE_DECL_REFCOUNTING(QuotaClient, override) + NS_DECL_THREADSAFE_ISUPPORTS virtual mozilla::dom::quota::Client::Type GetType() override; @@ -7776,8 +7978,13 @@ public: ReleaseIOThreadObjects() override; virtual void - WaitForStoragesToComplete(nsTArray& aStorages, - nsIRunnable* aCallback) override; + AbortOperations(const nsACString& aOrigin) override; + + virtual void + AbortOperationsForProcess(ContentParentId aContentParentId) override; + + virtual void + PerformIdleMaintenance() override; virtual void ShutdownWorkThreads() override; @@ -7794,19 +8001,251 @@ private: GetUsageForDirectoryInternal(nsIFile* aDirectory, UsageInfo* aUsageInfo, bool aDatabaseFiles); + + void + RemoveIdleObserver(); + + void + StartIdleMaintenance(); + + void + StopIdleMaintenance(); + + // Runs on mMaintenanceThreadPool. Once it finds databases it will queue a + // runnable that calls GetDirectoryLockForIdleMaintenance. + void + FindDatabasesForIdleMaintenance(uint32_t aRunId); + + // Runs on the main thread. Once QuotaManager has given a lock it will call + // ScheduleIdleMaintenance. + void + GetDirectoryLockForIdleMaintenance( + uint32_t aRunId, + MultipleMaintenanceInfo&& aMaintenanceInfo); + + // Runs on the main thread. It dispatches a runnable for each database that + // will call PerformIdleMaintenanceOnDatabase. + void + ScheduleIdleMaintenance(uint32_t aRunId, + const nsACString& aKey, + const MultipleMaintenanceInfo& aMaintenanceInfo); + + // Runs on mMaintenanceThreadPool. Does maintenance on one database and then + // dispatches a runnable back to the main thread to call + // MaybeReleaseDirectoryLockForIdleMaintenance. + void + PerformIdleMaintenanceOnDatabase(uint32_t aRunId, + const nsACString& aKey, + SingleMaintenanceInfo&& aMaintenanceInfo); + + // Runs on mMaintenanceThreadPool as part of PerformIdleMaintenanceOnDatabase. + void + PerformIdleMaintenanceOnDatabaseInternal( + uint32_t aRunId, + const SingleMaintenanceInfo& aMaintenanceInfo); + + // Runs on mMaintenanceThreadPool as part of PerformIdleMaintenanceOnDatabase. + nsresult + CheckIntegrity(mozIStorageConnection* aConnection, bool* aOk); + + // Runs on mMaintenanceThreadPool as part of PerformIdleMaintenanceOnDatabase. + nsresult + DetermineMaintenanceAction(mozIStorageConnection* aConnection, + nsIFile* aDatabaseFile, + MaintenanceAction* aMaintenanceAction); + + // Runs on mMaintenanceThreadPool as part of PerformIdleMaintenanceOnDatabase. + void + IncrementalVacuum(mozIStorageConnection* aConnection); + + // Runs on mMaintenanceThreadPool as part of PerformIdleMaintenanceOnDatabase. + void + FullVacuum(mozIStorageConnection* aConnection, + nsIFile* aDatabaseFile); + + // Runs on the main thread. Checks to see if all database maintenance has + // finished and then releases the directory lock. + void + MaybeReleaseDirectoryLockForIdleMaintenance( + const nsACString& aKey, + const nsAString& aDatabasePath); + + NS_DECL_NSIOBSERVER +}; + +class MOZ_STACK_CLASS QuotaClient::AutoProgressHandler final + : public mozIStorageProgressHandler +{ + QuotaClient* mQuotaClient; + mozIStorageConnection* mConnection; + uint32_t mRunId; + + NS_DECL_OWNINGTHREAD + + // This class is stack-based so we never actually allow AddRef/Release to do + // anything. But we need to know if any consumer *thinks* that they have a + // reference to this object so we track the reference countin DEBUG builds. + DebugOnly mDEBUGRefCnt; + +public: + AutoProgressHandler(QuotaClient* aQuotaClient, uint32_t aRunId) + : mQuotaClient(aQuotaClient) + , mConnection(nullptr) + , mRunId(aRunId) + , mDEBUGRefCnt(0) + { + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + NS_ASSERT_OWNINGTHREAD(QuotaClient::AutoProgressHandler); + MOZ_ASSERT(aQuotaClient); + } + + ~AutoProgressHandler() + { + NS_ASSERT_OWNINGTHREAD(QuotaClient::AutoProgressHandler); + + if (mConnection) { + Unregister(); + } + + MOZ_ASSERT(!mDEBUGRefCnt); + } + + nsresult + Register(mozIStorageConnection* aConnection); + + // We don't want the mRefCnt member but this class does not "inherit" + // nsISupports. + NS_DECL_ISUPPORTS_INHERITED + +private: + void + Unregister(); + + NS_DECL_MOZISTORAGEPROGRESSHANDLER + + // Not available for the heap! + void* + operator new(size_t) = delete; + void* + operator new[](size_t) = delete; + void + operator delete(void*) = delete; + void + operator delete[](void*) = delete; +}; + +struct QuotaClient::MaintenanceInfoBase +{ + const nsCString mGroup; + const nsCString mOrigin; + const PersistenceType mPersistenceType; + +protected: + MaintenanceInfoBase(const nsACString& aGroup, + const nsACString& aOrigin, + PersistenceType aPersistenceType) + : mGroup(aGroup) + , mOrigin(aOrigin) + , mPersistenceType(aPersistenceType) + { + MOZ_ASSERT(!aGroup.IsEmpty()); + MOZ_ASSERT(!aOrigin.IsEmpty()); + MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_INVALID); + + MOZ_COUNT_CTOR(QuotaClient::MaintenanceInfoBase); + } + + MaintenanceInfoBase(MaintenanceInfoBase&& aOther) + : mGroup(Move(aOther.mGroup)) + , mOrigin(Move(aOther.mOrigin)) + , mPersistenceType(Move(aOther.mPersistenceType)) + { + MOZ_COUNT_CTOR(QuotaClient::MaintenanceInfoBase); + } + + ~MaintenanceInfoBase() + { + MOZ_COUNT_DTOR(QuotaClient::MaintenanceInfoBase); + } + + MaintenanceInfoBase(const MaintenanceInfoBase& aOther) = delete; +}; + +struct QuotaClient::SingleMaintenanceInfo final + : public MaintenanceInfoBase +{ + const nsString mDatabasePath; + + SingleMaintenanceInfo(const nsACString& aGroup, + const nsACString& aOrigin, + PersistenceType aPersistenceType, + const nsAString& aDatabasePath) + : MaintenanceInfoBase(aGroup, aOrigin, aPersistenceType) + , mDatabasePath(aDatabasePath) + { + MOZ_ASSERT(!aDatabasePath.IsEmpty()); + } + + SingleMaintenanceInfo(SingleMaintenanceInfo&& aOther) + : MaintenanceInfoBase(Move(aOther)) + , mDatabasePath(Move(aOther.mDatabasePath)) + { + MOZ_ASSERT(!mDatabasePath.IsEmpty()); + } + + SingleMaintenanceInfo(const SingleMaintenanceInfo& aOther) = delete; +}; + +struct QuotaClient::MultipleMaintenanceInfo final + : public MaintenanceInfoBase +{ + nsTArray mDatabasePaths; + nsRefPtr mDirectoryLock; + const bool mIsApp; + + MultipleMaintenanceInfo(const nsACString& aGroup, + const nsACString& aOrigin, + PersistenceType aPersistenceType, + bool aIsApp, + nsTArray&& aDatabasePaths) + : MaintenanceInfoBase(aGroup, aOrigin, aPersistenceType) + , mDatabasePaths(Move(aDatabasePaths)) + , mIsApp(aIsApp) + { +#ifdef DEBUG + MOZ_ASSERT(!mDatabasePaths.IsEmpty()); + for (const nsString& databasePath : mDatabasePaths) { + MOZ_ASSERT(!databasePath.IsEmpty()); + } +#endif + } + + MultipleMaintenanceInfo(MultipleMaintenanceInfo&& aOther) + : MaintenanceInfoBase(Move(aOther)) + , mDatabasePaths(Move(aOther.mDatabasePaths)) + , mDirectoryLock(Move(aOther.mDirectoryLock)) + , mIsApp(Move(aOther.mIsApp)) + { +#ifdef DEBUG + MOZ_ASSERT(!mDatabasePaths.IsEmpty()); + for (const nsString& databasePath : mDatabasePaths) { + MOZ_ASSERT(!databasePath.IsEmpty()); + } +#endif + } + + MultipleMaintenanceInfo(const MultipleMaintenanceInfo& aOther) = delete; }; class QuotaClient::ShutdownWorkThreadsRunnable final : public nsRunnable { nsRefPtr mQuotaClient; - bool mHasRequestedShutDown; public: - explicit ShutdownWorkThreadsRunnable(QuotaClient* aQuotaClient) : mQuotaClient(aQuotaClient) - , mHasRequestedShutDown(false) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aQuotaClient); @@ -7825,158 +8264,82 @@ private: NS_DECL_NSIRUNNABLE }; -class QuotaClient::WaitForTransactionsRunnable final +class QuotaClient::AbortOperationsRunnable final : public nsRunnable { - nsRefPtr mQuotaClient; - nsTArray mDatabaseIds; - nsCOMPtr mCallback; + const ContentParentId mContentParentId; + const nsCString mOrigin; - enum - { - State_Initial = 0, - State_WaitingForTransactions, - State_DispatchToMainThread, - State_WaitingForFileHandles, - State_Complete - } mState; + nsTArray> mDatabases; public: - WaitForTransactionsRunnable(QuotaClient* aQuotaClient, - nsTArray& aDatabaseIds, - nsIRunnable* aCallback) - : mQuotaClient(aQuotaClient) - , mCallback(aCallback) - , mState(State_Initial) + explicit AbortOperationsRunnable(const nsACString& aOrigin) + : mOrigin(aOrigin) { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aQuotaClient); - MOZ_ASSERT(QuotaClient::GetInstance() == aQuotaClient); - MOZ_ASSERT(!aDatabaseIds.IsEmpty()); - MOZ_ASSERT(aCallback); + MOZ_ASSERT(!mOrigin.IsEmpty()); + } - mDatabaseIds.SwapElements(aDatabaseIds); + explicit AbortOperationsRunnable(ContentParentId aContentParentId) + : mContentParentId(aContentParentId) + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mOrigin.IsEmpty()); } NS_DECL_ISUPPORTS_INHERITED private: - ~WaitForTransactionsRunnable() - { - MOZ_ASSERT(!mQuotaClient); - MOZ_ASSERT(!mCallback); - MOZ_ASSERT(mState = State_Complete); - } + ~AbortOperationsRunnable() + { } - void - MaybeWaitForTransactions(); + static PLDHashOperator + MatchOrigin(const nsACString& aKey, + DatabaseActorInfo* aValue, + void* aClosure); - void - DispatchToMainThread(); - - void - MaybeWaitForFileHandles(); - - void - CallCallback(); + static PLDHashOperator + MatchContentParentId(const nsACString& aKey, + DatabaseActorInfo* aValue, + void* aClosure); NS_DECL_NSIRUNNABLE }; -class DatabaseOfflineStorage final - : public nsIOfflineStorage +class QuotaClient::GetDirectoryLockListener final + : public OpenDirectoryListener { - // Must be released on the main thread! - nsRefPtr mStrongQuotaClient; - - // Only used on the main thread. - QuotaClient* mWeakQuotaClient; - - // Only used on the background thread. - Database* mDatabase; - - const OptionalContentId mOptionalContentParentId; - const nsCString mOrigin; - const nsCString mId; - nsCOMPtr mOwningThread; - - bool mClosedOnMainThread; - bool mClosedOnOwningThread; - bool mInvalidatedOnMainThread; - bool mInvalidatedOnOwningThread; - - bool mRegisteredWithQuotaManager; + nsRefPtr mQuotaClient; + const uint32_t mRunId; + const nsCString mKey; public: - DatabaseOfflineStorage(QuotaClient* aQuotaClient, - const OptionalContentId& aOptionalContentParentId, - const nsACString& aGroup, - const nsACString& aOrigin, - const nsACString& aId, - PersistenceType aPersistenceType, - nsIEventTarget* aOwningThread); - - static void - UnregisterOnOwningThread( - already_AddRefed aOfflineStorage); - - void - SetDatabase(Database* aDatabase) - { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(aDatabase); - MOZ_ASSERT(!mDatabase); - - mDatabase = aDatabase; - } - - nsIEventTarget* - OwningThread() const - { - return mOwningThread; - } - - void - NoteRegisteredWithQuotaManager() + GetDirectoryLockListener(QuotaClient* aQuotaClient, + uint32_t aRunId, + const nsACString& aKey) + : mQuotaClient(aQuotaClient) + , mRunId(aRunId) + , mKey(aKey) { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mRegisteredWithQuotaManager); - - mRegisteredWithQuotaManager = true; + MOZ_ASSERT(aQuotaClient); + MOZ_ASSERT(QuotaClient::GetInstance() == aQuotaClient); } - void - CloseOnOwningThread(); - - void - AssertInvalidatedOnMainThread() const - { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mInvalidatedOnMainThread); - } - - NS_DECL_THREADSAFE_ISUPPORTS + NS_INLINE_DECL_REFCOUNTING(QuotaClient::GetDirectoryLockListener) private: - ~DatabaseOfflineStorage() + ~GetDirectoryLockListener() { - MOZ_ASSERT(!mDatabase); - MOZ_RELEASE_ASSERT(!mRegisteredWithQuotaManager); + MOZ_ASSERT(NS_IsMainThread()); } - void - CloseOnMainThread(); + // OpenDirectoryListener overrides. + virtual void + DirectoryLockAcquired(DirectoryLock* aLock) override; - void - InvalidateOnMainThread(); - - void - InvalidateOnOwningThread(); - - void - UnregisterOnMainThread(); - - NS_DECL_NSIOFFLINESTORAGE + virtual void + DirectoryLockFailed() override; }; #ifdef DEBUG @@ -8156,6 +8519,10 @@ ConvertBlobsToActors(PBackgroundParent* aBackgroundActor, * Globals ******************************************************************************/ +typedef nsTArray> FactoryOpArray; + +StaticAutoPtr gFactoryOps; + // Maps a database id to information about live database actors. typedef nsClassHashtable DatabaseActorHashtable; @@ -8169,12 +8536,102 @@ typedef nsDataHashtable StaticAutoPtr gLoggingInfoHashtable; +typedef nsDataHashtable TelemetryIdHashtable; + +StaticAutoPtr gTelemetryIdHashtable; + +// Protects all reads and writes to gTelemetryIdHashtable. +StaticAutoPtr gTelemetryIdMutex; + #ifdef DEBUG StaticRefPtr gDEBUGThreadSlower; #endif // DEBUG + +uint32_t +TelemetryIdForFile(nsIFile* aFile) +{ + // May be called on any thread! + + MOZ_ASSERT(aFile); + MOZ_ASSERT(gTelemetryIdMutex); + + // The storage directory is structured like this: + // + // /storage///idb/.sqlite + // + // For the purposes of this function we're only concerned with the + // , , and pieces. + + nsString filename; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aFile->GetLeafName(filename))); + + // Make sure we were given a database file. + NS_NAMED_LITERAL_STRING(sqliteExtension, ".sqlite"); + + MOZ_ASSERT(StringEndsWith(filename, sqliteExtension)); + + filename.Truncate(filename.Length() - sqliteExtension.Length()); + + // Get the "idb" directory. + nsCOMPtr idbDirectory; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aFile->GetParent(getter_AddRefs(idbDirectory)))); + + DebugOnly idbLeafName; + MOZ_ASSERT(NS_SUCCEEDED(idbDirectory->GetLeafName(idbLeafName))); + MOZ_ASSERT(static_cast(idbLeafName).EqualsLiteral("idb")); + + // Get the directory. + nsCOMPtr originDirectory; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + idbDirectory->GetParent(getter_AddRefs(originDirectory)))); + + nsString origin; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(originDirectory->GetLeafName(origin))); + + // Any databases in these directories are owned by the application and should + // not have their filenames masked. Hopefully they also appear in the + // Telemetry.cpp whitelist. + if (origin.EqualsLiteral("chrome") || + origin.EqualsLiteral("moz-safe-about+home")) { + return 0; + } + + // Get the directory. + nsCOMPtr persistenceDirectory; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + originDirectory->GetParent(getter_AddRefs(persistenceDirectory)))); + + nsString persistence; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(persistenceDirectory->GetLeafName(persistence))); + + NS_NAMED_LITERAL_STRING(separator, "*"); + + uint32_t hashValue = HashString(persistence + separator + + origin + separator + + filename); + + MutexAutoLock lock(*gTelemetryIdMutex); + + if (!gTelemetryIdHashtable) { + gTelemetryIdHashtable = new TelemetryIdHashtable(); + } + + uint32_t id; + if (!gTelemetryIdHashtable->Get(hashValue, &id)) { + static uint32_t sNextId = 1; + + // We're locked, no need for atomics. + id = sNextId++; + + gTelemetryIdHashtable->Put(hashValue, id); + } + + return id; +} + } // namespace /******************************************************************************* @@ -8288,6 +8745,7 @@ DatabaseConnection::DatabaseConnection( FileManager* aFileManager) : mStorageConnection(aStorageConnection) , mFileManager(aFileManager) + , mInReadTransaction(false) , mInWriteTransaction(false) #ifdef DEBUG , mDEBUGSavepointCount(0) @@ -8313,10 +8771,11 @@ nsresult DatabaseConnection::Init() { AssertIsOnConnectionThread(); + MOZ_ASSERT(!mInReadTransaction); MOZ_ASSERT(!mInWriteTransaction); CachedStatement stmt; - nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN"), &stmt); + nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN;"), &stmt); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -8326,6 +8785,8 @@ DatabaseConnection::Init() return rv; } + mInReadTransaction = true; + return NS_OK; } @@ -8375,6 +8836,7 @@ DatabaseConnection::BeginWriteTransaction() { AssertIsOnConnectionThread(); MOZ_ASSERT(mStorageConnection); + MOZ_ASSERT(mInReadTransaction); MOZ_ASSERT(!mInWriteTransaction); PROFILER_LABEL("IndexedDB", @@ -8383,7 +8845,8 @@ DatabaseConnection::BeginWriteTransaction() // Release our read locks. CachedStatement rollbackStmt; - nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("ROLLBACK"), &rollbackStmt); + nsresult rv = + GetCachedStatement(NS_LITERAL_CSTRING("ROLLBACK;"), &rollbackStmt); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -8393,6 +8856,8 @@ DatabaseConnection::BeginWriteTransaction() return rv; } + mInReadTransaction = false; + if (!mUpdateRefcountFunction) { MOZ_ASSERT(mFileManager); @@ -8411,7 +8876,7 @@ DatabaseConnection::BeginWriteTransaction() } CachedStatement beginStmt; - rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN IMMEDIATE"), &beginStmt); + rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN IMMEDIATE;"), &beginStmt); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -8445,10 +8910,38 @@ DatabaseConnection::BeginWriteTransaction() return NS_OK; } +nsresult +DatabaseConnection::CommitWriteTransaction() +{ + AssertIsOnConnectionThread(); + MOZ_ASSERT(mStorageConnection); + MOZ_ASSERT(!mInReadTransaction); + MOZ_ASSERT(mInWriteTransaction); + + PROFILER_LABEL("IndexedDB", + "DatabaseConnection::CommitWriteTransaction", + js::ProfileEntry::Category::STORAGE); + + CachedStatement stmt; + nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("COMMIT;"), &stmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mInWriteTransaction = false; + return NS_OK; +} + void DatabaseConnection::RollbackWriteTransaction() { AssertIsOnConnectionThread(); + MOZ_ASSERT(!mInReadTransaction); MOZ_ASSERT(mStorageConnection); PROFILER_LABEL("IndexedDB", @@ -8460,7 +8953,7 @@ DatabaseConnection::RollbackWriteTransaction() } DatabaseConnection::CachedStatement stmt; - nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("ROLLBACK"), &stmt); + nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("ROLLBACK;"), &stmt); if (NS_WARN_IF(NS_FAILED(rv))) { return; } @@ -8468,6 +8961,8 @@ DatabaseConnection::RollbackWriteTransaction() // This may fail if SQLite already rolled back the transaction so ignore any // errors. unused << stmt->Execute(); + + mInWriteTransaction = false; } void @@ -8475,6 +8970,8 @@ DatabaseConnection::FinishWriteTransaction() { AssertIsOnConnectionThread(); MOZ_ASSERT(mStorageConnection); + MOZ_ASSERT(!mInReadTransaction); + MOZ_ASSERT(!mInWriteTransaction); PROFILER_LABEL("IndexedDB", "DatabaseConnection::FinishWriteTransaction", @@ -8484,14 +8981,8 @@ DatabaseConnection::FinishWriteTransaction() mUpdateRefcountFunction->Reset(); } - if (!mInWriteTransaction) { - return; - } - - mInWriteTransaction = false; - CachedStatement stmt; - nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN"), &stmt); + nsresult rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN;"), &stmt); if (NS_WARN_IF(NS_FAILED(rv))) { return; } @@ -8500,6 +8991,8 @@ DatabaseConnection::FinishWriteTransaction() if (NS_WARN_IF(NS_FAILED(rv))) { return; } + + mInReadTransaction = true; } nsresult @@ -8601,23 +9094,46 @@ DatabaseConnection::RollbackSavepoint() } nsresult -DatabaseConnection::Checkpoint(bool aIdle) +DatabaseConnection::CheckpointInternal(CheckpointMode aMode) { AssertIsOnConnectionThread(); + MOZ_ASSERT(!mInReadTransaction); + MOZ_ASSERT(!mInWriteTransaction); PROFILER_LABEL("IndexedDB", - "DatabaseConnection::Checkpoint", + "DatabaseConnection::CheckpointInternal", js::ProfileEntry::Category::STORAGE); + nsAutoCString stmtString; + stmtString.AssignLiteral("PRAGMA wal_checkpoint("); + + switch (aMode) { + case CheckpointMode_Full: + // Ensures that the database is completely checkpointed and flushed to + // disk. + stmtString.AppendLiteral("FULL"); + break; + + case CheckpointMode_Restart: + // Like CheckpointMode_Full, but also ensures that the next write will + // start overwriting the existing WAL file rather than letting the WAL + // file grow. + stmtString.AppendLiteral("RESTART"); + break; + + case CheckpointMode_Truncate: + // Like CheckpointMode_Restart but also truncates the existing WAL file. + stmtString.AppendLiteral("TRUNCATE"); + break; + + default: + MOZ_CRASH("Unknown CheckpointMode!"); + } + + stmtString.AppendLiteral(");"); + CachedStatement stmt; - nsresult rv = - GetCachedStatement(aIdle ? - // When idle we want to reclaim disk space. - NS_LITERAL_CSTRING("PRAGMA wal_checkpoint(TRUNCATE)") : - // We're being called at the end of a READ_WRITE_FLUSH transaction so make - // sure that the database is completely checkpointed and flushed to disk. - NS_LITERAL_CSTRING("PRAGMA wal_checkpoint(FULL)"), - &stmt); + nsresult rv = GetCachedStatement(stmtString, &stmt); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -8630,6 +9146,246 @@ DatabaseConnection::Checkpoint(bool aIdle) return NS_OK; } +void +DatabaseConnection::DoIdleProcessing(bool aNeedsCheckpoint) +{ + AssertIsOnConnectionThread(); + MOZ_ASSERT(mInReadTransaction); + MOZ_ASSERT(!mInWriteTransaction); + + PROFILER_LABEL("IndexedDB", + "DatabaseConnection::DoIdleProcessing", + js::ProfileEntry::Category::STORAGE); + + DatabaseConnection::CachedStatement freelistStmt; + uint32_t freelistCount; + nsresult rv = GetFreelistCount(freelistStmt, &freelistCount); + if (NS_WARN_IF(NS_FAILED(rv))) { + freelistCount = 0; + } + + CachedStatement rollbackStmt; + CachedStatement beginStmt; + if (aNeedsCheckpoint || freelistCount) { + rv = GetCachedStatement(NS_LITERAL_CSTRING("ROLLBACK;"), &rollbackStmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN;"), &beginStmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + // Release the connection's normal transaction. It's possible that it could + // fail, but that isn't a problem here. + unused << rollbackStmt->Execute(); + + mInReadTransaction = false; + } + + bool freedSomePages = false; + + if (freelistCount) { + rv = ReclaimFreePagesWhileIdle(freelistStmt, + rollbackStmt, + freelistCount, + aNeedsCheckpoint, + &freedSomePages); + if (NS_WARN_IF(NS_FAILED(rv))) { + MOZ_ASSERT(!freedSomePages); + } + + // Make sure we didn't leave a transaction running. + MOZ_ASSERT(!mInReadTransaction); + MOZ_ASSERT(!mInWriteTransaction); + } + + // Truncate the WAL if we were asked to or if we managed to free some space. + if (aNeedsCheckpoint || freedSomePages) { + rv = CheckpointInternal(CheckpointMode_Truncate); + unused << NS_WARN_IF(NS_FAILED(rv)); + } + + // Finally try to restart the read transaction if we rolled it back earlier. + if (beginStmt) { + rv = beginStmt->Execute(); + if (NS_SUCCEEDED(rv)) { + mInReadTransaction = true; + } else { + NS_WARNING("Falied to restart read transaction!"); + } + } +} + +nsresult +DatabaseConnection::ReclaimFreePagesWhileIdle( + CachedStatement& aFreelistStatement, + CachedStatement& aRollbackStatement, + uint32_t aFreelistCount, + bool aNeedsCheckpoint, + bool* aFreedSomePages) +{ + AssertIsOnConnectionThread(); + MOZ_ASSERT(aFreelistStatement); + MOZ_ASSERT(aRollbackStatement); + MOZ_ASSERT(aFreelistCount); + MOZ_ASSERT(aFreedSomePages); + MOZ_ASSERT(!mInReadTransaction); + MOZ_ASSERT(!mInWriteTransaction); + + PROFILER_LABEL("IndexedDB", + "DatabaseConnection::ReclaimFreePagesWhileIdle", + js::ProfileEntry::Category::STORAGE); + + // Make sure we don't keep working if anything else needs this thread. + nsIThread* currentThread = NS_GetCurrentThread(); + MOZ_ASSERT(currentThread); + + if (NS_HasPendingEvents(currentThread)) { + *aFreedSomePages = false; + return NS_OK; + } + + // Only try to free 10% at a time so that we can bail out if this connection + // suddenly becomes active or if the thread is needed otherwise. + nsAutoCString stmtString; + stmtString.AssignLiteral("PRAGMA incremental_vacuum("); + stmtString.AppendInt(std::max(uint64_t(1), uint64_t(aFreelistCount / 10))); + stmtString.AppendLiteral(");"); + + // Make all the statements we'll need up front. + CachedStatement incrementalVacuumStmt; + nsresult rv = GetCachedStatement(stmtString, &incrementalVacuumStmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + CachedStatement beginImmediateStmt; + rv = GetCachedStatement(NS_LITERAL_CSTRING("BEGIN IMMEDIATE;"), + &beginImmediateStmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + CachedStatement commitStmt; + rv = GetCachedStatement(NS_LITERAL_CSTRING("COMMIT;"), &commitStmt); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (aNeedsCheckpoint) { + // Freeing pages is a journaled operation, so it will require additional WAL + // space. However, we're idle and are about to checkpoint anyway, so doing a + // RESTART checkpoint here should allow us to reuse any existing space. + rv = CheckpointInternal(CheckpointMode_Restart); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + // Start the write transaction. + rv = beginImmediateStmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + mInWriteTransaction = true; + + bool freedSomePages = false; + + while (aFreelistCount) { + if (NS_HasPendingEvents(currentThread)) { + // Something else wants to use the thread so roll back this transaction. + // It's ok if we never make progress here because the idle service should + // eventually reclaim this space. + rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + break; + } + + rv = incrementalVacuumStmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + break; + } + + freedSomePages = true; + + rv = GetFreelistCount(aFreelistStatement, &aFreelistCount); + if (NS_WARN_IF(NS_FAILED(rv))) { + break; + } + } + + if (NS_SUCCEEDED(rv) && freedSomePages) { + // Commit the write transaction. + rv = commitStmt->Execute(); + if (NS_SUCCEEDED(rv)) { + mInWriteTransaction = false; + } else { + NS_WARNING("Failed to commit!"); + } + } + + if (NS_FAILED(rv)) { + MOZ_ASSERT(mInWriteTransaction); + + // Something failed, make sure we roll everything back. + unused << aRollbackStatement->Execute(); + + mInWriteTransaction = false; + + return rv; + } + + *aFreedSomePages = freedSomePages; + return NS_OK; +} + +nsresult +DatabaseConnection::GetFreelistCount(CachedStatement& aCachedStatement, + uint32_t* aFreelistCount) +{ + AssertIsOnConnectionThread(); + MOZ_ASSERT(aFreelistCount); + + PROFILER_LABEL("IndexedDB", + "DatabaseConnection::GetFreelistCount", + js::ProfileEntry::Category::STORAGE); + + nsresult rv; + + if (!aCachedStatement) { + rv = GetCachedStatement(NS_LITERAL_CSTRING("PRAGMA freelist_count;"), + &aCachedStatement); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + bool hasResult; + rv = aCachedStatement->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(hasResult); + + // Make sure this statement is reset when leaving this function since we're + // not using the normal stack-based protection of CachedStatement. + mozStorageStatementScoper scoper(aCachedStatement); + + int32_t freelistCount; + rv = aCachedStatement->GetInt32(0, &freelistCount); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(freelistCount >= 0); + + *aFreelistCount = uint32_t(freelistCount); + return NS_OK; +} + void DatabaseConnection::Close() { @@ -9380,8 +10136,8 @@ ConnectionPool::IdleTimerCallback(nsITimer* aTimer, void* aClosure) IdleDatabaseInfo& info = self->mIdleDatabases[index]; if (now >= info.mIdleTime) { - if (info.mDatabaseInfo->mNeedsCheckpoint) { - self->CheckpointDatabase(info.mDatabaseInfo); + if (info.mDatabaseInfo->mIdle) { + self->PerformIdleDatabaseMaintenance(info.mDatabaseInfo); } else { self->CloseDatabase(info.mDatabaseInfo); } @@ -9446,6 +10202,7 @@ ConnectionPool::GetOrCreateConnection(const Database* aDatabase, aDatabase->Type(), aDatabase->Group(), aDatabase->Origin(), + aDatabase->TelemetryId(), getter_AddRefs(storageConnection)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -9859,13 +10616,19 @@ ConnectionPool::CloseIdleDatabases() js::ProfileEntry::Category::STORAGE); if (!mIdleDatabases.IsEmpty()) { - for (uint32_t count = mIdleDatabases.Length(), index = 0; - index < count; - index++) { - CloseDatabase(mIdleDatabases[index].mDatabaseInfo); + for (IdleDatabaseInfo& idleInfo : mIdleDatabases) { + CloseDatabase(idleInfo.mDatabaseInfo); } mIdleDatabases.Clear(); } + + if (!mDatabasesPerformingIdleMaintenance.IsEmpty()) { + for (DatabaseInfo* dbInfo : mDatabasesPerformingIdleMaintenance) { + MOZ_ASSERT(dbInfo); + CloseDatabase(dbInfo); + } + mDatabasesPerformingIdleMaintenance.Clear(); + } } void @@ -9902,6 +10665,8 @@ ConnectionPool::ScheduleTransaction(TransactionInfo* aTransactionInfo, DatabaseInfo* dbInfo = aTransactionInfo->mDatabaseInfo; MOZ_ASSERT(dbInfo); + dbInfo->mIdle = false; + if (dbInfo->mClosing) { MOZ_ASSERT(!mIdleDatabases.Contains(dbInfo)); MOZ_ASSERT( @@ -9936,6 +10701,23 @@ ConnectionPool::ScheduleTransaction(TransactionInfo* aTransactionInfo, } else { NS_WARNING("Failed to make new thread!"); } + } else if (!mDatabasesPerformingIdleMaintenance.IsEmpty()) { + // We need a thread right now so force all idle processing to stop by + // posting a dummy runnable to each thread that might be doing idle + // maintenance. + nsCOMPtr runnable = new nsRunnable(); + + for (uint32_t index = mDatabasesPerformingIdleMaintenance.Length(); + index > 0; + index--) { + DatabaseInfo* dbInfo = mDatabasesPerformingIdleMaintenance[index - 1]; + MOZ_ASSERT(dbInfo); + MOZ_ASSERT(dbInfo->mThreadInfo.mThread); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + dbInfo->mThreadInfo.mThread->Dispatch(runnable, + NS_DISPATCH_NORMAL))); + } } if (!created) { @@ -10103,6 +10885,9 @@ ConnectionPool::NoteFinishedTransaction(uint64_t aTransactionId) #endif if (!dbInfo->TotalTransactionCount()) { + MOZ_ASSERT(!dbInfo->mIdle); + dbInfo->mIdle = true; + NoteIdleDatabase(dbInfo); } } @@ -10315,21 +11100,26 @@ ConnectionPool::MaybeFireCallback(DatabasesCompleteCallback* aCallback) } void -ConnectionPool::CheckpointDatabase(DatabaseInfo* aDatabaseInfo) +ConnectionPool::PerformIdleDatabaseMaintenance(DatabaseInfo* aDatabaseInfo) { AssertIsOnOwningThread(); MOZ_ASSERT(aDatabaseInfo); MOZ_ASSERT(!aDatabaseInfo->TotalTransactionCount()); MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mThread); MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable); - MOZ_ASSERT(aDatabaseInfo->mNeedsCheckpoint); + MOZ_ASSERT(aDatabaseInfo->mIdle); MOZ_ASSERT(!aDatabaseInfo->mCloseOnIdle); MOZ_ASSERT(!aDatabaseInfo->mClosing); - - aDatabaseInfo->mNeedsCheckpoint = false; + MOZ_ASSERT(mIdleDatabases.Contains(aDatabaseInfo)); + MOZ_ASSERT(!mDatabasesPerformingIdleMaintenance.Contains(aDatabaseInfo)); nsCOMPtr runnable = - new CheckpointConnectionRunnable(aDatabaseInfo); + new IdleConnectionRunnable(aDatabaseInfo, aDatabaseInfo->mNeedsCheckpoint); + + aDatabaseInfo->mNeedsCheckpoint = false; + aDatabaseInfo->mIdle = false; + + mDatabasesPerformingIdleMaintenance.AppendElement(aDatabaseInfo); MOZ_ALWAYS_TRUE(NS_SUCCEEDED( aDatabaseInfo->mThreadInfo.mThread->Dispatch(runnable, @@ -10346,6 +11136,7 @@ ConnectionPool::CloseDatabase(DatabaseInfo* aDatabaseInfo) MOZ_ASSERT(aDatabaseInfo->mThreadInfo.mRunnable); MOZ_ASSERT(!aDatabaseInfo->mClosing); + aDatabaseInfo->mIdle = false; aDatabaseInfo->mNeedsCheckpoint = false; aDatabaseInfo->mClosing = true; @@ -10367,7 +11158,8 @@ ConnectionPool::CloseDatabaseWhenIdleInternal(const nsACString& aDatabaseId) js::ProfileEntry::Category::STORAGE); if (DatabaseInfo* dbInfo = mDatabases.Get(aDatabaseId)) { - if (mIdleDatabases.RemoveElement(dbInfo)) { + if (mIdleDatabases.RemoveElement(dbInfo) || + mDatabasesPerformingIdleMaintenance.RemoveElement(dbInfo)) { CloseDatabase(dbInfo); AdjustIdleTimer(); } else { @@ -10392,38 +11184,43 @@ ConnectionRunnable::ConnectionRunnable(DatabaseInfo* aDatabaseInfo) MOZ_ASSERT(mOwningThread); } -NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::CheckpointConnectionRunnable, +NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::IdleConnectionRunnable, ConnectionPool::ConnectionRunnable) NS_IMETHODIMP ConnectionPool:: -CheckpointConnectionRunnable::Run() +IdleConnectionRunnable::Run() { MOZ_ASSERT(mDatabaseInfo); + MOZ_ASSERT(!mDatabaseInfo->mIdle); - PROFILER_LABEL("IndexedDB", - "ConnectionPool::CheckpointConnectionRunnable::Run", - js::ProfileEntry::Category::STORAGE); + nsCOMPtr owningThread; + mOwningThread.swap(owningThread); - if (mOwningThread) { + if (owningThread) { mDatabaseInfo->AssertIsOnConnectionThread(); MOZ_ASSERT(mDatabaseInfo->mConnection); - - nsCOMPtr owningThread; - mOwningThread.swap(owningThread); - - mDatabaseInfo->mConnection->Checkpoint(/* aIdle */ true); + mDatabaseInfo->mConnection->DoIdleProcessing(mNeedsCheckpoint); MOZ_ALWAYS_TRUE(NS_SUCCEEDED( owningThread->Dispatch(this, NS_DISPATCH_NORMAL))); return NS_OK; } - if (!mDatabaseInfo->TotalTransactionCount()) { - nsRefPtr connectionPool = mDatabaseInfo->mConnectionPool; - MOZ_ASSERT(connectionPool); + nsRefPtr connectionPool = mDatabaseInfo->mConnectionPool; + MOZ_ASSERT(connectionPool); - connectionPool->NoteIdleDatabase(mDatabaseInfo); + if (mDatabaseInfo->mClosing) { + MOZ_ASSERT(!connectionPool-> + mDatabasesPerformingIdleMaintenance.Contains(mDatabaseInfo)); + } else { + MOZ_ALWAYS_TRUE( + connectionPool-> + mDatabasesPerformingIdleMaintenance.RemoveElement(mDatabaseInfo)); + + if (!mDatabaseInfo->TotalTransactionCount()) { + connectionPool->NoteIdleDatabase(mDatabaseInfo); + } } return NS_OK; @@ -10484,6 +11281,7 @@ DatabaseInfo::DatabaseInfo(ConnectionPool* aConnectionPool, , mReadTransactionCount(0) , mWriteTransactionCount(0) , mNeedsCheckpoint(false) + , mIdle(false) , mCloseOnIdle(false) , mClosing(false) #ifdef DEBUG @@ -10756,9 +11554,9 @@ IdleResource::~IdleResource() ConnectionPool:: IdleDatabaseInfo::IdleDatabaseInfo(DatabaseInfo* aDatabaseInfo) : IdleResource(TimeStamp::NowLoRes() + - (aDatabaseInfo->mNeedsCheckpoint ? - TimeDuration::FromMilliseconds(kConnectionIdleCheckpointsMS) : - TimeDuration::FromMilliseconds(kConnectionIdleCloseMS))) + (aDatabaseInfo->mIdle ? + TimeDuration::FromMilliseconds(kConnectionIdleMaintenanceMS) : + TimeDuration::FromMilliseconds(kConnectionIdleCloseMS))) , mDatabaseInfo(aDatabaseInfo) { AssertIsOnBackgroundThread(); @@ -11080,9 +11878,8 @@ Factory::Create(const LoggingInfo& aLoggingInfo) // If this is the first instance then we need to do some initialization. if (!sFactoryInstanceCount) { - if (!gConnectionPool) { - gConnectionPool = new ConnectionPool(); - } + MOZ_ASSERT(!gFactoryOps); + gFactoryOps = new FactoryOpArray(); MOZ_ASSERT(!gLiveDatabaseHashtable); gLiveDatabaseHashtable = new DatabaseActorHashtable(); @@ -11161,6 +11958,10 @@ Factory::ActorDestroy(ActorDestroyReason aWhy) MOZ_ASSERT(!gLiveDatabaseHashtable->Count()); gLiveDatabaseHashtable = nullptr; + MOZ_ASSERT(gFactoryOps); + MOZ_ASSERT(gFactoryOps->IsEmpty()); + gFactoryOps = nullptr; + #ifdef DEBUG if (kDEBUGThreadPriority != nsISupportsPriority::PRIORITY_NORMAL) { nsCOMPtr thread = @@ -11452,28 +12253,43 @@ WaitForTransactionsHelper::Run() return NS_OK; } +NS_IMETHODIMP +UnlockDirectoryRunnable::Run() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mDirectoryLock); + + mDirectoryLock = nullptr; + + return NS_OK; +} + /******************************************************************************* * Database ******************************************************************************/ Database::Database(Factory* aFactory, const PrincipalInfo& aPrincipalInfo, + const OptionalContentId& aOptionalContentParentId, const nsACString& aGroup, const nsACString& aOrigin, + uint32_t aTelemetryId, FullDatabaseMetadata* aMetadata, FileManager* aFileManager, - already_AddRefed aOfflineStorage, + already_AddRefed aDirectoryLock, bool aChromeWriteAccessAllowed) : mFactory(aFactory) , mMetadata(aMetadata) , mFileManager(aFileManager) - , mOfflineStorage(Move(aOfflineStorage)) + , mDirectoryLock(Move(aDirectoryLock)) , mPrincipalInfo(aPrincipalInfo) + , mOptionalContentParentId(aOptionalContentParentId) , mGroup(aGroup) , mOrigin(aOrigin) , mId(aMetadata->mDatabaseId) , mFilePath(aMetadata->mFilePath) , mFileHandleCount(0) + , mTelemetryId(aTelemetryId) , mPersistenceType(aMetadata->mCommonMetadata.persistenceType()) , mChromeWriteAccessAllowed(aChromeWriteAccessAllowed) , mClosed(false) @@ -11488,8 +12304,6 @@ Database::Database(Factory* aFactory, MOZ_ASSERT(aFileManager); MOZ_ASSERT_IF(aChromeWriteAccessAllowed, aPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo); - - mOfflineStorage->SetDatabase(this); } void @@ -11601,7 +12415,7 @@ Database::RegisterTransaction(TransactionBase* aTransaction) AssertIsOnBackgroundThread(); MOZ_ASSERT(aTransaction); MOZ_ASSERT(!mTransactions.GetEntry(aTransaction)); - MOZ_ASSERT(mOfflineStorage); + MOZ_ASSERT(mDirectoryLock); if (NS_WARN_IF(!mTransactions.PutEntry(aTransaction, fallible))) { return false; @@ -11666,10 +12480,6 @@ Database::CloseInternal() info->mWaitingFactoryOp->NoteDatabaseClosed(this); } - if (mOfflineStorage) { - mOfflineStorage->CloseOnOwningThread(); - } - MaybeCloseConnection(); return true; @@ -11683,7 +12493,7 @@ Database::MaybeCloseConnection() if (!mTransactions.Count() && !mFileHandleCount && IsClosed() && - mOfflineStorage) { + mDirectoryLock) { nsCOMPtr callback = NS_NewRunnableMethod(this, &Database::ConnectionClosedCallback); @@ -11701,8 +12511,11 @@ Database::ConnectionClosedCallback() MOZ_ASSERT(!mTransactions.Count()); MOZ_ASSERT(!mFileHandleCount); - if (mOfflineStorage) { - DatabaseOfflineStorage::UnregisterOnOwningThread(mOfflineStorage.forget()); + if (mDirectoryLock) { + nsRefPtr runnable = + new UnlockDirectoryRunnable(mDirectoryLock.forget()); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); } CleanupMetadata(); @@ -11915,6 +12728,10 @@ Database::RecvPBackgroundIDBTransactionConstructor( return true; } + if (!gConnectionPool) { + gConnectionPool = new ConnectionPool(); + } + auto* transaction = static_cast(aActor); nsRefPtr startOp = new StartTransactionOp(transaction); @@ -12020,7 +12837,7 @@ Database::RecvNewFileHandle() { AssertIsOnBackgroundThread(); - if (!mOfflineStorage) { + if (!mDirectoryLock) { ASSERT_UNLESS_FUZZING(); return false; } @@ -14116,6 +14933,7 @@ Cursor::RecvContinue(const CursorRequestParams& aParams) FileManager::FileManager(PersistenceType aPersistenceType, const nsACString& aGroup, const nsACString& aOrigin, + bool aIsApp, const nsAString& aDatabaseName, bool aEnforcingQuota) : mPersistenceType(aPersistenceType) @@ -14123,6 +14941,7 @@ FileManager::FileManager(PersistenceType aPersistenceType, , mOrigin(aOrigin) , mDatabaseName(aDatabaseName) , mLastFileId(0) + , mIsApp(aIsApp) , mEnforcingQuota(aEnforcingQuota) , mInvalidated(false) { } @@ -14392,7 +15211,8 @@ FileManager::InitDirectory(nsIFile* aDirectory, nsIFile* aDatabaseFile, PersistenceType aPersistenceType, const nsACString& aGroup, - const nsACString& aOrigin) + const nsACString& aOrigin, + uint32_t aTelemetryId) { AssertIsOnIOThread(); MOZ_ASSERT(aDirectory); @@ -14464,6 +15284,7 @@ FileManager::InitDirectory(nsIFile* aDirectory, aPersistenceType, aGroup, aOrigin, + aTelemetryId, getter_AddRefs(connection)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -14635,10 +15456,18 @@ FileManager::GetUsage(nsIFile* aDirectory, uint64_t* aUsage) QuotaClient* QuotaClient::sInstance = nullptr; QuotaClient::QuotaClient() - : mShutdownRequested(false) + : mMaintenanceStartTime(0) + , mMaintenanceRunId(0) + , mShutdownRequested(false) + , mIdleObserverRegistered(false) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!sInstance, "We expect this to be a singleton!"); + MOZ_ASSERT(!gTelemetryIdMutex); + + // Always create this so that later access to gTelemetryIdHashtable can be + // properly synchronized. + gTelemetryIdMutex = new Mutex("IndexedDB gTelemetryIdMutex"); sInstance = this; } @@ -14647,6 +15476,15 @@ QuotaClient::~QuotaClient() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!"); + MOZ_ASSERT(gTelemetryIdMutex); + MOZ_ASSERT(!mMaintenanceThreadPool); + MOZ_ASSERT_IF(mMaintenanceInfoHashtable, !mMaintenanceInfoHashtable->Count()); + MOZ_ASSERT(!mIdleObserverRegistered); + + // No one else should be able to touch gTelemetryIdHashtable now that the + // QuotaClient has gone away. + gTelemetryIdHashtable = nullptr; + gTelemetryIdMutex = nullptr; sInstance = nullptr; } @@ -14661,6 +15499,8 @@ QuotaClient::NoteBackgroundThread(nsIEventTarget* aBackgroundThread) mBackgroundThread = aBackgroundThread; } +NS_IMPL_ISUPPORTS(QuotaClient, nsIObserver) + mozilla::dom::quota::Client::Type QuotaClient::GetType() { @@ -14896,7 +15736,8 @@ QuotaClient::InitOrigin(PersistenceType aPersistenceType, initInfo.mDatabaseFile, aPersistenceType, aGroup, - aOrigin); + aOrigin, + TelemetryIdForFile(initInfo.mDatabaseFile)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -15008,52 +15849,85 @@ QuotaClient::ReleaseIOThreadObjects() } void -QuotaClient::WaitForStoragesToComplete(nsTArray& aStorages, - nsIRunnable* aCallback) +QuotaClient::AbortOperations(const nsACString& aOrigin) { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!aStorages.IsEmpty()); - MOZ_ASSERT(aCallback); + if (mBackgroundThread) { + nsRefPtr runnable = + new AbortOperationsRunnable(aOrigin); - nsCOMPtr backgroundThread; - nsTArray databaseIds; - - for (uint32_t count = aStorages.Length(), index = 0; index < count; index++) { - nsIOfflineStorage* storage = aStorages[index]; - MOZ_ASSERT(storage); - MOZ_ASSERT(storage->GetClient() == this); - - const nsACString& databaseId = storage->Id(); - - if (!databaseIds.Contains(databaseId)) { - databaseIds.AppendElement(databaseId); - - if (!backgroundThread) { - backgroundThread = - static_cast(storage)->OwningThread(); - MOZ_ASSERT(backgroundThread); - } -#ifdef DEBUG - else { - MOZ_ASSERT(backgroundThread == - static_cast(storage)-> - OwningThread()); - } -#endif + if (NS_FAILED(mBackgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) { + // This can happen if the thread has shut down already. + return; } } +} - if (databaseIds.IsEmpty()) { - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(aCallback))); +void +QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId) +{ + if (mBackgroundThread) { + nsRefPtr runnable = + new AbortOperationsRunnable(aContentParentId); + + if (NS_FAILED(mBackgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) { + // This can happen if the thread has shut down already. + return; + } + } +} + +void +QuotaClient::PerformIdleMaintenance() +{ + using namespace mozilla::hal; + + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mShutdownRequested); + + // If we're running on battery power then skip all idle maintenance since we + // would otherwise be doing lots of disk I/O. + BatteryInformation batteryInfo; + +#ifdef MOZ_WIDGET_ANDROID + // Android XPCShell doesn't load the AndroidBridge that is needed to make + // GetCurrentBatteryInformation work... + if (!kRunningXPCShellTests) +#endif + { + GetCurrentBatteryInformation(&batteryInfo); + } + + // If we're running XPCShell because we always want to be able to test this + // code so pretend that we're always charging. + if (kRunningXPCShellTests) { + batteryInfo.level() = 100; + batteryInfo.charging() = true; + } + + if (NS_WARN_IF(!batteryInfo.charging())) { return; } - MOZ_ASSERT(backgroundThread); + // Make sure that the IndexedDatabaseManager is running so that we can check + // for low disk space mode. + IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate(); + if (NS_WARN_IF(!mgr)) { + return; + } - nsCOMPtr runnable = - new WaitForTransactionsRunnable(this, databaseIds, aCallback); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - backgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL))); + if (kRunningXPCShellTests) { + // We don't want user activity to impact this code if we're running tests. + unused << Observe(nullptr, OBSERVER_TOPIC_IDLE, nullptr); + } else if (!mIdleObserverRegistered) { + nsCOMPtr idleService = + do_GetService(kIdleServiceContractId); + MOZ_ASSERT(idleService); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + idleService->AddIdleObserver(this, kIdleObserverTimeSec))); + + mIdleObserverRegistered = true; + } } void @@ -15063,28 +15937,35 @@ QuotaClient::ShutdownWorkThreads() MOZ_ASSERT(!mShutdownRunnable); MOZ_ASSERT(!mShutdownRequested); + StopIdleMaintenance(); + mShutdownRequested = true; if (mBackgroundThread) { nsRefPtr runnable = new ShutdownWorkThreadsRunnable(this); - if (NS_FAILED(mBackgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) { - // This can happen if the thread has shut down already. - return; + if (NS_SUCCEEDED(mBackgroundThread->Dispatch(runnable, + NS_DISPATCH_NORMAL))) { + mShutdownRunnable = Move(runnable); } + } + FileService::Shutdown(); + + if (mMaintenanceThreadPool) { + mMaintenanceThreadPool->Shutdown(); + mMaintenanceThreadPool = nullptr; + } + + if (mShutdownRunnable) { nsIThread* currentThread = NS_GetCurrentThread(); MOZ_ASSERT(currentThread); - mShutdownRunnable.swap(runnable); - while (mShutdownRunnable) { MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread)); } } - - FileService::Shutdown(); } nsresult @@ -15131,6 +16012,9 @@ QuotaClient::GetUsageForDirectoryInternal(nsIFile* aDirectory, return NS_OK; } + const NS_ConvertASCIItoUTF16 journalSuffix( + kSQLiteJournalSuffix, + LiteralStringLength(kSQLiteJournalSuffix)); const NS_ConvertASCIItoUTF16 shmSuffix(kSQLiteSHMSuffix, LiteralStringLength(kSQLiteSHMSuffix)); @@ -15153,12 +16037,19 @@ QuotaClient::GetUsageForDirectoryInternal(nsIFile* aDirectory, return rv; } - if (StringEndsWith(leafName, shmSuffix)) { + // Journal files and sqlite-shm files don't count towards usage. + if (StringEndsWith(leafName, journalSuffix) || + StringEndsWith(leafName, shmSuffix)) { continue; } bool isDirectory; rv = file->IsDirectory(&isDirectory); + if (rv == NS_ERROR_FILE_NOT_FOUND || + rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) { + continue; + } + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -15206,114 +16097,1012 @@ QuotaClient::GetUsageForDirectoryInternal(nsIFile* aDirectory, return NS_OK; } +void +QuotaClient::RemoveIdleObserver() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (mIdleObserverRegistered) { + nsCOMPtr idleService = + do_GetService(kIdleServiceContractId); + MOZ_ASSERT(idleService); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + idleService->RemoveIdleObserver(this, kIdleObserverTimeSec))); + + mIdleObserverRegistered = false; + } +} void -QuotaClient:: -WaitForTransactionsRunnable::MaybeWaitForTransactions() +QuotaClient::StartIdleMaintenance() { - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mState == State_Initial); - MOZ_ASSERT(mQuotaClient); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mShutdownRequested); - nsRefPtr connectionPool = gConnectionPool.get(); - if (connectionPool) { - // Have to copy here in case the file service needs a list too. - nsTArray databaseIds(mDatabaseIds); + if (!mMaintenanceThreadPool) { + nsRefPtr threadPool = new nsThreadPool(); - mState = State_WaitingForTransactions; + // PR_GetNumberOfProcessors() can return -1 on error, so make sure we + // don't set some huge number here. We add 2 in case some threads block on + // the disk I/O. + const uint32_t threadCount = + std::max(int32_t(PR_GetNumberOfProcessors()), int32_t(1)) + + 2; - connectionPool->WaitForDatabasesToComplete(Move(databaseIds), this); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + threadPool->SetThreadLimit(threadCount))); + + // Don't keep more than one idle thread. + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + threadPool->SetIdleThreadLimit(1))); + + // Don't keep idle threads alive very long. + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + threadPool->SetIdleThreadTimeout(5 * PR_MSEC_PER_SEC))); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + threadPool->SetName(NS_LITERAL_CSTRING("IndexedDB Mnt")))); + + mMaintenanceThreadPool = Move(threadPool); + } + + mMaintenanceStartTime = PR_Now(); + MOZ_ASSERT(mMaintenanceStartTime); + + if (!mMaintenanceInfoHashtable) { + mMaintenanceInfoHashtable = MakeUnique(); + } + + nsCOMPtr runnable = + NS_NewRunnableMethodWithArg( + this, + &QuotaClient::FindDatabasesForIdleMaintenance, + mMaintenanceRunId); + MOZ_ASSERT(runnable); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + mMaintenanceThreadPool->Dispatch(runnable, NS_DISPATCH_NORMAL))); +} + +void +QuotaClient::StopIdleMaintenance() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!mShutdownRequested); + + RemoveIdleObserver(); + + mMaintenanceRunId++; +} + +void +QuotaClient::FindDatabasesForIdleMaintenance(uint32_t aRunId) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(mMaintenanceThreadPool); + + // The storage directory is structured like this: + // + // /storage///idb/*.sqlite + // + // We have to find all database files that match any persistence type and any + // origin. We ignore anything out of the ordinary for now. + + if (IdleMaintenanceMustEnd(aRunId)) { return; } - DispatchToMainThread(); -} + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); -void -QuotaClient:: -WaitForTransactionsRunnable::DispatchToMainThread() -{ - AssertIsOnBackgroundThread(); - MOZ_ASSERT(mState == State_Initial || mState == State_WaitingForTransactions); + nsCOMPtr storageDir = GetFileForPath(quotaManager->GetStoragePath()); + MOZ_ASSERT(storageDir); - mState = State_DispatchToMainThread; - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); -} - -void -QuotaClient:: -WaitForTransactionsRunnable::MaybeWaitForFileHandles() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_DispatchToMainThread); - - FileService* service = FileService::Get(); - if (service) { - mState = State_WaitingForFileHandles; - - service->WaitForStoragesToComplete(mDatabaseIds, this); - - MOZ_ASSERT(mDatabaseIds.IsEmpty()); + bool exists; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(storageDir->Exists(&exists))); + if (!exists) { return; } - mDatabaseIds.Clear(); + bool isDirectory; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(storageDir->IsDirectory(&isDirectory))); + if (NS_WARN_IF(!isDirectory)) { + return; + } - mState = State_WaitingForFileHandles; + // There are currently only 3 persistence types, and we want to iterate them + // in this order: + static const PersistenceType kPersistenceTypes[] = { + PERSISTENCE_TYPE_PERSISTENT, + PERSISTENCE_TYPE_DEFAULT, + PERSISTENCE_TYPE_TEMPORARY + }; - CallCallback(); + static_assert((sizeof(kPersistenceTypes) / sizeof(kPersistenceTypes[0])) == + size_t(PERSISTENCE_TYPE_INVALID), + "Something changed with available persistence types!"); + + NS_NAMED_LITERAL_STRING(idbDirName, IDB_DIRECTORY_NAME); + NS_NAMED_LITERAL_STRING(sqliteExtension, ".sqlite"); + + for (const PersistenceType persistenceType : kPersistenceTypes) { + // Loop over "" directories. + if (IdleMaintenanceMustEnd(aRunId)) { + return; + } + + nsAutoCString persistenceTypeString; + if (persistenceType == PERSISTENCE_TYPE_PERSISTENT) { + // XXX This shouldn't be a special case... + persistenceTypeString.AssignLiteral("permanent"); + } else { + PersistenceTypeToText(persistenceType, persistenceTypeString); + } + + nsCOMPtr persistenceDir; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + storageDir->Clone(getter_AddRefs(persistenceDir)))); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + persistenceDir->Append(NS_ConvertASCIItoUTF16(persistenceTypeString)))); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(persistenceDir->Exists(&exists))); + if (!exists) { + continue; + } + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(persistenceDir->IsDirectory(&isDirectory))); + if (NS_WARN_IF(!isDirectory)) { + continue; + } + + nsCOMPtr persistenceDirEntries; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + persistenceDir->GetDirectoryEntries( + getter_AddRefs(persistenceDirEntries)))); + if (!persistenceDirEntries) { + continue; + } + + while (true) { + // Loop over "/idb" directories. + if (IdleMaintenanceMustEnd(aRunId)) { + return; + } + + bool persistenceDirHasMoreEntries; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + persistenceDirEntries->HasMoreElements(&persistenceDirHasMoreEntries))); + + if (!persistenceDirHasMoreEntries) { + break; + } + + nsCOMPtr persistenceDirEntry; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + persistenceDirEntries->GetNext(getter_AddRefs(persistenceDirEntry)))); + + nsCOMPtr originDir = do_QueryInterface(persistenceDirEntry); + MOZ_ASSERT(originDir); + + MOZ_ASSERT(NS_SUCCEEDED(originDir->Exists(&exists))); + MOZ_ASSERT(exists); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(originDir->IsDirectory(&isDirectory))); + if (!isDirectory) { + continue; + } + + nsCOMPtr idbDir; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(originDir->Clone(getter_AddRefs(idbDir)))); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(idbDir->Append(idbDirName))); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(idbDir->Exists(&exists))); + if (!exists) { + continue; + } + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(idbDir->IsDirectory(&isDirectory))); + if (NS_WARN_IF(!isDirectory)) { + continue; + } + + nsCOMPtr idbDirEntries; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + idbDir->GetDirectoryEntries(getter_AddRefs(idbDirEntries)))); + if (!idbDirEntries) { + continue; + } + + nsCString group; + nsCString origin; + bool isApp; + nsTArray databasePaths; + + while (true) { + // Loop over files in the "idb" directory. + if (IdleMaintenanceMustEnd(aRunId)) { + return; + } + + bool idbDirHasMoreEntries; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + idbDirEntries->HasMoreElements(&idbDirHasMoreEntries))); + + if (!idbDirHasMoreEntries) { + break; + } + + nsCOMPtr idbDirEntry; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + idbDirEntries->GetNext(getter_AddRefs(idbDirEntry)))); + + nsCOMPtr idbDirFile = do_QueryInterface(idbDirEntry); + MOZ_ASSERT(idbDirFile); + + nsString idbFilePath; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(idbDirFile->GetPath(idbFilePath))); + + if (!StringEndsWith(idbFilePath, sqliteExtension)) { + continue; + } + + MOZ_ASSERT(NS_SUCCEEDED(idbDirFile->Exists(&exists))); + MOZ_ASSERT(exists); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(idbDirFile->IsDirectory(&isDirectory))); + if (isDirectory) { + continue; + } + + // Found a database. + if (databasePaths.IsEmpty()) { + MOZ_ASSERT(group.IsEmpty()); + MOZ_ASSERT(origin.IsEmpty()); + + int64_t dummyTimeStamp; + if (NS_WARN_IF(NS_FAILED( + QuotaManager::GetDirectoryMetadata(originDir, + &dummyTimeStamp, + group, + origin, + &isApp)))) { + // Not much we can do here... + continue; + } + } + + MOZ_ASSERT(!databasePaths.Contains(idbFilePath)); + + databasePaths.AppendElement(idbFilePath); + } + + if (!databasePaths.IsEmpty()) { + nsCOMPtr runnable = + NS_NewRunnableMethodWithArgs( + this, + &QuotaClient::GetDirectoryLockForIdleMaintenance, + aRunId, + MultipleMaintenanceInfo(group, + origin, + persistenceType, + isApp, + Move(databasePaths))); + MOZ_ASSERT(runnable); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); + } + } + } } void -QuotaClient:: -WaitForTransactionsRunnable::CallCallback() +QuotaClient::GetDirectoryLockForIdleMaintenance( + uint32_t aRunId, + MultipleMaintenanceInfo&& aMaintenanceInfo) { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_WaitingForFileHandles); - nsRefPtr quotaClient; - mQuotaClient.swap(quotaClient); + if (IdleMaintenanceMustEnd(aRunId)) { + return; + } - nsCOMPtr callback; - mCallback.swap(callback); + MOZ_ASSERT(mMaintenanceInfoHashtable); - callback->Run(); + nsAutoCString key; + key.AppendInt(aMaintenanceInfo.mPersistenceType); + key.Append('*'); + key.Append(aMaintenanceInfo.mOrigin); - mState = State_Complete; + MOZ_ASSERT(!mMaintenanceInfoHashtable->Get(key)); + + MultipleMaintenanceInfo* maintenanceInfo = + new MultipleMaintenanceInfo(Move(aMaintenanceInfo)); + + mMaintenanceInfoHashtable->Put(key, maintenanceInfo); + + nsRefPtr listener = + new GetDirectoryLockListener(this, aRunId, key); + + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + quotaManager->OpenDirectory(maintenanceInfo->mPersistenceType, + maintenanceInfo->mGroup, + maintenanceInfo->mOrigin, + maintenanceInfo->mIsApp, + Client::IDB, + /* aExclusive */ false, + listener); } -NS_IMPL_ISUPPORTS_INHERITED0(QuotaClient::WaitForTransactionsRunnable, - nsRunnable) - -NS_IMETHODIMP -QuotaClient:: -WaitForTransactionsRunnable::Run() +void +QuotaClient::ScheduleIdleMaintenance(uint32_t aRunId, + const nsACString& aKey, + const MultipleMaintenanceInfo& aMaintenanceInfo) { - MOZ_ASSERT(mState != State_Complete); - MOZ_ASSERT(mCallback); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!aKey.IsEmpty()); - switch (mState) { - case State_Initial: - MaybeWaitForTransactions(); + MOZ_ASSERT(mMaintenanceThreadPool); + + for (const nsString& databasePath : aMaintenanceInfo.mDatabasePaths) { + nsCOMPtr runnable = + NS_NewRunnableMethodWithArgs( + this, + &QuotaClient::PerformIdleMaintenanceOnDatabase, + aRunId, + aKey, + SingleMaintenanceInfo(aMaintenanceInfo.mGroup, + aMaintenanceInfo.mOrigin, + aMaintenanceInfo.mPersistenceType, + databasePath)); + MOZ_ASSERT(runnable); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED( + mMaintenanceThreadPool->Dispatch(runnable, NS_DISPATCH_NORMAL))); + } +} + +void +QuotaClient::PerformIdleMaintenanceOnDatabase( + uint32_t aRunId, + const nsACString& aKey, + SingleMaintenanceInfo&& aMaintenanceInfo) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(mMaintenanceThreadPool); + MOZ_ASSERT(mMaintenanceStartTime); + MOZ_ASSERT(!aMaintenanceInfo.mDatabasePath.IsEmpty()); + MOZ_ASSERT(!aMaintenanceInfo.mGroup.IsEmpty()); + MOZ_ASSERT(!aMaintenanceInfo.mOrigin.IsEmpty()); + + PerformIdleMaintenanceOnDatabaseInternal(aRunId, aMaintenanceInfo); + + nsCOMPtr runnable = + NS_NewRunnableMethodWithArgs( + this, + &QuotaClient::MaybeReleaseDirectoryLockForIdleMaintenance, + aKey, + aMaintenanceInfo.mDatabasePath); + MOZ_ASSERT(runnable); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); +} + +void +QuotaClient::PerformIdleMaintenanceOnDatabaseInternal( + uint32_t aRunId, + const SingleMaintenanceInfo& aMaintenanceInfo) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(mMaintenanceThreadPool); + MOZ_ASSERT(mMaintenanceStartTime); + MOZ_ASSERT(!aMaintenanceInfo.mDatabasePath.IsEmpty()); + MOZ_ASSERT(!aMaintenanceInfo.mGroup.IsEmpty()); + MOZ_ASSERT(!aMaintenanceInfo.mOrigin.IsEmpty()); + + nsCOMPtr databaseFile = + GetFileForPath(aMaintenanceInfo.mDatabasePath); + MOZ_ASSERT(databaseFile); + + nsCOMPtr connection; + nsresult rv = GetStorageConnection(databaseFile, + aMaintenanceInfo.mPersistenceType, + aMaintenanceInfo.mGroup, + aMaintenanceInfo.mOrigin, + TelemetryIdForFile(databaseFile), + getter_AddRefs(connection)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + if (IdleMaintenanceMustEnd(aRunId)) { + return; + } + + AutoProgressHandler progressHandler(this, aRunId); + if (NS_WARN_IF(NS_FAILED(progressHandler.Register(connection)))) { + return; + } + + bool databaseIsOk; + rv = CheckIntegrity(connection, &databaseIsOk); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + if (NS_WARN_IF(!databaseIsOk)) { + // XXX Handle this somehow! Probably need to clear all storage for the + // origin. Needs followup. + MOZ_ASSERT(false, "Database corruption detected!"); + return; + } + + if (IdleMaintenanceMustEnd(aRunId)) { + return; + } + + MaintenanceAction maintenanceAction; + rv = DetermineMaintenanceAction(connection, databaseFile, &maintenanceAction); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + if (IdleMaintenanceMustEnd(aRunId)) { + return; + } + + switch (maintenanceAction) { + case MaintenanceAction_Nothing: break; - case State_WaitingForTransactions: - DispatchToMainThread(); + case MaintenanceAction_IncrementalVacuum: + IncrementalVacuum(connection); break; - case State_DispatchToMainThread: - MaybeWaitForFileHandles(); - break; - - case State_WaitingForFileHandles: - CallCallback(); + case MaintenanceAction_FullVacuum: + FullVacuum(connection, databaseFile); break; default: - MOZ_CRASH("Should never get here!"); + MOZ_CRASH("Unknown MaintenanceAction!"); + } +} + +nsresult +QuotaClient::CheckIntegrity(mozIStorageConnection* aConnection, bool* aOk) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(aConnection); + MOZ_ASSERT(aOk); + + nsresult rv; + + // First do a full integrity_check. Scope statements tightly here because + // later operations require zero live statements. + { + nsCOMPtr stmt; + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "PRAGMA integrity_check(1);" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(hasResult); + + nsString result; + rv = stmt->GetString(0, result); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (NS_WARN_IF(!result.EqualsLiteral("ok"))) { + *aOk = false; + return NS_OK; + } } + // Now enable and check for foreign key constraints. + { + int32_t foreignKeysWereEnabled; + { + nsCOMPtr stmt; + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "PRAGMA foreign_keys;" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(hasResult); + + rv = stmt->GetInt32(0, &foreignKeysWereEnabled); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + bool changedForeignKeys; + if (foreignKeysWereEnabled) { + changedForeignKeys = false; + } else { + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "PRAGMA foreign_keys = ON;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + changedForeignKeys = true; + } + + bool foreignKeyError; + { + nsCOMPtr stmt; + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "PRAGMA foreign_key_check;" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->ExecuteStep(&foreignKeyError); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + if (changedForeignKeys) { + nsAutoCString stmtSQL; + stmtSQL.AssignLiteral("PRAGMA foreign_keys = "); + if (foreignKeysWereEnabled) { + stmtSQL.AppendLiteral("ON"); + } else { + stmtSQL.AppendLiteral("OFF"); + } + stmtSQL.Append(';'); + + rv = aConnection->ExecuteSimpleSQL(stmtSQL); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + if (foreignKeyError) { + *aOk = false; + return NS_OK; + } + } + + *aOk = true; + return NS_OK; +} + +nsresult +QuotaClient::DetermineMaintenanceAction(mozIStorageConnection* aConnection, + nsIFile* aDatabaseFile, + MaintenanceAction* aMaintenanceAction) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(aConnection); + MOZ_ASSERT(aDatabaseFile); + MOZ_ASSERT(aMaintenanceAction); + + int32_t schemaVersion; + nsresult rv = aConnection->GetSchemaVersion(&schemaVersion); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Don't do anything if the schema version is less than 18; before that + // version no databases had |auto_vacuum == INCREMENTAL| set and we didn't + // track the values needed for the heuristics below. + if (schemaVersion < MakeSchemaVersion(18, 0)) { + *aMaintenanceAction = MaintenanceAction_Nothing; + return NS_OK; + } + + bool lowDiskSpace = IndexedDatabaseManager::InLowDiskSpaceMode(); + + if (kRunningXPCShellTests) { + // If we're running XPCShell then we want to test both the low disk space + // and normal disk space code paths so pick semi-randomly based on the + // current time. + lowDiskSpace = ((PR_Now() / PR_USEC_PER_MSEC) % 2) == 0; + } + + // If we're low on disk space then the best we can hope for is that an + // incremental vacuum might free some space. That is a journaled operation so + // it may not be possible even then. + if (lowDiskSpace) { + *aMaintenanceAction = MaintenanceAction_IncrementalVacuum; + return NS_OK; + } + + // This method shouldn't make any permanent changes to the database, so make + // sure everything gets rolled back when we leave. + mozStorageTransaction transaction(aConnection, + /* aCommitOnComplete */ false); + + // Check to see when we last vacuumed this database. + nsCOMPtr stmt; + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT last_vacuum_time, last_vacuum_size " + "FROM database;" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool hasResult; + rv = stmt->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(hasResult); + + PRTime lastVacuumTime; + rv = stmt->GetInt64(0, &lastVacuumTime); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + int64_t lastVacuumSize; + rv = stmt->GetInt64(1, &lastVacuumSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(lastVacuumSize > 0); + + // This shouldn't really be possible... + if (NS_WARN_IF(mMaintenanceStartTime <= lastVacuumTime)) { + *aMaintenanceAction = MaintenanceAction_Nothing; + return NS_OK; + } + + if (mMaintenanceStartTime - lastVacuumTime < kMinVacuumAge) { + *aMaintenanceAction = MaintenanceAction_IncrementalVacuum; + return NS_OK; + } + + // It has been more than a week since the database was vacuumed, so gather + // statistics on its usage to see if vacuuming is worthwhile. + rv = aConnection->EnableModule(NS_LITERAL_CSTRING("dbstat")); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Create a temporary copy of the dbstat table to speed up the queries that + // come later. + rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "CREATE VIRTUAL TABLE __stats__ USING dbstat;" + "CREATE TEMP TABLE __temp_stats__ AS SELECT * FROM __stats__;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Calculate the percentage of the database pages that are not in contiguous + // order. + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT SUM(__ts1__.pageno != __ts2__.pageno + 1) * 100.0 / COUNT(*) " + "FROM __temp_stats__ AS __ts1__, __temp_stats__ AS __ts2__ " + "WHERE __ts1__.name = __ts2__.name " + "AND __ts1__.rowid = __ts2__.rowid + 1;" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(hasResult); + + int32_t percentUnordered; + rv = stmt->GetInt32(0, &percentUnordered); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(percentUnordered >= 0); + MOZ_ASSERT(percentUnordered <= 100); + + if (percentUnordered >= kPercentUnorderedThreshold) { + *aMaintenanceAction = MaintenanceAction_FullVacuum; + return NS_OK; + } + + // Don't try a full vacuum if the file hasn't grown by 10%. + int64_t currentFileSize; + rv = aDatabaseFile->GetFileSize(¤tFileSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (currentFileSize <= lastVacuumSize || + (((currentFileSize - lastVacuumSize) * 100 / currentFileSize) < + kPercentFileSizeGrowthThreshold)) { + *aMaintenanceAction = MaintenanceAction_IncrementalVacuum; + return NS_OK; + } + + // See if there are any free pages that we can reclaim. + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "PRAGMA freelist_count;" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(hasResult); + + int32_t freelistCount; + rv = stmt->GetInt32(0, &freelistCount); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(freelistCount >= 0); + + // If we have too many free pages then we should try an incremental vacuum. If + // that causes too much fragmentation then we'll try a full vacuum later. + if (freelistCount > kMaxFreelistThreshold) { + *aMaintenanceAction = MaintenanceAction_IncrementalVacuum; + return NS_OK; + } + + // Calculate the percentage of unused bytes on pages in the database. + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "SELECT SUM(unused) * 100.0 / SUM(pgsize) " + "FROM __temp_stats__;" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = stmt->ExecuteStep(&hasResult); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(hasResult); + + int32_t percentUnused; + rv = stmt->GetInt32(0, &percentUnused); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(percentUnused >= 0); + MOZ_ASSERT(percentUnused <= 100); + + *aMaintenanceAction = percentUnused >= kPercentUnusedThreshold ? + MaintenanceAction_FullVacuum : + MaintenanceAction_IncrementalVacuum; + return NS_OK; +} + +void +QuotaClient::IncrementalVacuum(mozIStorageConnection* aConnection) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(aConnection); + + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "PRAGMA incremental_vacuum;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } +} + +void +QuotaClient::FullVacuum(mozIStorageConnection* aConnection, + nsIFile* aDatabaseFile) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(aConnection); + MOZ_ASSERT(aDatabaseFile); + + nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING( + "VACUUM;" + )); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + PRTime vacuumTime = PR_Now(); + MOZ_ASSERT(vacuumTime > 0); + + int64_t fileSize; + rv = aDatabaseFile->GetFileSize(&fileSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + MOZ_ASSERT(fileSize > 0); + + nsCOMPtr stmt; + rv = aConnection->CreateStatement(NS_LITERAL_CSTRING( + "UPDATE database " + "SET last_vacuum_time = :time" + ", last_vacuum_size = :size;" + ), getter_AddRefs(stmt)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("time"), vacuumTime); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("size"), fileSize); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + rv = stmt->Execute(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } +} + +void +QuotaClient::MaybeReleaseDirectoryLockForIdleMaintenance( + const nsACString& aKey, + const nsAString& aDatabasePath) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!aKey.IsEmpty()); + MOZ_ASSERT(!aDatabasePath.IsEmpty()); + MOZ_ASSERT(mMaintenanceInfoHashtable); + + MultipleMaintenanceInfo* maintenanceInfo; + MOZ_ALWAYS_TRUE(mMaintenanceInfoHashtable->Get(aKey, &maintenanceInfo)); + MOZ_ASSERT(maintenanceInfo); + + MOZ_ALWAYS_TRUE(maintenanceInfo->mDatabasePaths.RemoveElement(aDatabasePath)); + + if (maintenanceInfo->mDatabasePaths.IsEmpty()) { + // That's it! + maintenanceInfo->mDirectoryLock = nullptr; + + // This will delete |maintenanceInfo|. + mMaintenanceInfoHashtable->Remove(aKey); + } +} + +NS_IMETHODIMP +QuotaClient::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!strcmp(aTopic, OBSERVER_TOPIC_IDLE)) { + StartIdleMaintenance(); + return NS_OK; + } + + if (!strcmp(aTopic, OBSERVER_TOPIC_ACTIVE)) { + StopIdleMaintenance(); + return NS_OK; + } + + MOZ_ASSERT_UNREACHABLE("Should never get here!"); + return NS_OK; +} + +nsresult +QuotaClient:: +AutoProgressHandler::Register(mozIStorageConnection* aConnection) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(aConnection); + + // We want to quickly bail out of any operation if the user becomes active, so + // use a small granularity here since database performance isn't critical. + static const int32_t kProgressGranularity = 50; + + nsCOMPtr oldHandler; + nsresult rv = aConnection->SetProgressHandler(kProgressGranularity, + this, + getter_AddRefs(oldHandler)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + MOZ_ASSERT(!oldHandler); + mConnection = aConnection; + + return NS_OK; +} + +void +QuotaClient:: +AutoProgressHandler::Unregister() +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(!IsOnBackgroundThread()); + MOZ_ASSERT(mConnection); + + nsCOMPtr oldHandler; + nsresult rv = mConnection->RemoveProgressHandler(getter_AddRefs(oldHandler)); + unused << NS_WARN_IF(NS_FAILED(rv)); + + MOZ_ASSERT_IF(NS_SUCCEEDED(rv), oldHandler == this); +} + +NS_IMETHODIMP_(MozExternalRefCountType) +QuotaClient:: +AutoProgressHandler::AddRef() +{ + NS_ASSERT_OWNINGTHREAD(QuotaClient::AutoProgressHandler); + + mDEBUGRefCnt++; + return 2; +} + +NS_IMETHODIMP_(MozExternalRefCountType) +QuotaClient:: +AutoProgressHandler::Release() +{ + NS_ASSERT_OWNINGTHREAD(QuotaClient::AutoProgressHandler); + + mDEBUGRefCnt--; + return 1; +} + +NS_IMPL_QUERY_INTERFACE(QuotaClient::AutoProgressHandler, + mozIStorageProgressHandler) + +NS_IMETHODIMP +QuotaClient:: +AutoProgressHandler::OnProgress(mozIStorageConnection* aConnection, + bool* _retval) +{ + NS_ASSERT_OWNINGTHREAD(QuotaClient::AutoProgressHandler); + MOZ_ASSERT(aConnection); + MOZ_ASSERT(mConnection == aConnection); + MOZ_ASSERT(_retval); + + *_retval = mQuotaClient->IdleMaintenanceMustEnd(mRunId); return NS_OK; } @@ -15325,7 +17114,6 @@ QuotaClient:: ShutdownWorkThreadsRunnable::Run() { if (NS_IsMainThread()) { - MOZ_ASSERT(mHasRequestedShutDown); MOZ_ASSERT(QuotaClient::GetInstance() == mQuotaClient); MOZ_ASSERT(mQuotaClient->mShutdownRunnable == this); @@ -15337,16 +17125,11 @@ ShutdownWorkThreadsRunnable::Run() AssertIsOnBackgroundThread(); - if (!mHasRequestedShutDown) { - mHasRequestedShutDown = true; - - nsRefPtr connectionPool = gConnectionPool.get(); - if (connectionPool) { - connectionPool->Shutdown(); - - gConnectionPool = nullptr; - } + nsRefPtr connectionPool = gConnectionPool.get(); + if (connectionPool) { + connectionPool->Shutdown(); + gConnectionPool = nullptr; } MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); @@ -15354,201 +17137,118 @@ ShutdownWorkThreadsRunnable::Run() return NS_OK; } -/******************************************************************************* - * DatabaseOfflineStorage - ******************************************************************************/ - -DatabaseOfflineStorage::DatabaseOfflineStorage( - QuotaClient* aQuotaClient, - const OptionalContentId& aOptionalContentParentId, - const nsACString& aGroup, - const nsACString& aOrigin, - const nsACString& aId, - PersistenceType aPersistenceType, - nsIEventTarget* aOwningThread) - : mStrongQuotaClient(aQuotaClient) - , mWeakQuotaClient(aQuotaClient) - , mDatabase(nullptr) - , mOptionalContentParentId(aOptionalContentParentId) - , mOrigin(aOrigin) - , mId(aId) - , mOwningThread(aOwningThread) - , mClosedOnMainThread(false) - , mClosedOnOwningThread(false) - , mInvalidatedOnMainThread(false) - , mInvalidatedOnOwningThread(false) - , mRegisteredWithQuotaManager(false) +// static +PLDHashOperator +QuotaClient:: +AbortOperationsRunnable::MatchOrigin(const nsACString& aKey, + DatabaseActorInfo* aValue, + void* aClosure) { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aQuotaClient); - MOZ_ASSERT(aOwningThread); + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!aKey.IsEmpty()); + MOZ_ASSERT(aValue); + MOZ_ASSERT(aClosure); - DebugOnly current; - MOZ_ASSERT(NS_SUCCEEDED(aOwningThread->IsOnCurrentThread(¤t))); - MOZ_ASSERT(!current); + auto* closure = static_cast(aClosure); - mGroup = aGroup; - mPersistenceType = aPersistenceType; + for (Database* database : aValue->mLiveDatabases) { + if (closure->mOrigin.IsVoid() || closure->mOrigin == database->Origin()) { + closure->mDatabases.AppendElement(database); + } + } + + return PL_DHASH_NEXT; } // static -void -DatabaseOfflineStorage::UnregisterOnOwningThread( - already_AddRefed aOfflineStorage) +PLDHashOperator +QuotaClient:: +AbortOperationsRunnable::MatchContentParentId(const nsACString& aKey, + DatabaseActorInfo* aValue, + void* aClosure) +{ + AssertIsOnBackgroundThread(); + MOZ_ASSERT(!aKey.IsEmpty()); + MOZ_ASSERT(aValue); + MOZ_ASSERT(aClosure); + + auto* closure = static_cast(aClosure); + + for (Database* database : aValue->mLiveDatabases) { + if (database->IsOwnedByProcess(closure->mContentParentId)) { + closure->mDatabases.AppendElement(database); + } + } + + return PL_DHASH_NEXT; +} + +NS_IMPL_ISUPPORTS_INHERITED0(QuotaClient::AbortOperationsRunnable, nsRunnable) + +NS_IMETHODIMP +QuotaClient:: +AbortOperationsRunnable::Run() { AssertIsOnBackgroundThread(); - nsRefPtr offlineStorage = Move(aOfflineStorage); - MOZ_ASSERT(offlineStorage); - MOZ_ASSERT(offlineStorage->mClosedOnOwningThread); - - offlineStorage->mDatabase = nullptr; - - nsCOMPtr runnable = - NS_NewRunnableMethod(offlineStorage.get(), - &DatabaseOfflineStorage::UnregisterOnMainThread); - MOZ_ASSERT(runnable); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); -} - -void -DatabaseOfflineStorage::CloseOnOwningThread() -{ - AssertIsOnBackgroundThread(); - - if (mClosedOnOwningThread) { - return; + if (!gLiveDatabaseHashtable) { + return NS_OK; } - mClosedOnOwningThread = true; - - nsCOMPtr runnable = - NS_NewRunnableMethod(this, &DatabaseOfflineStorage::CloseOnMainThread); - MOZ_ASSERT(runnable); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); -} - -void -DatabaseOfflineStorage::CloseOnMainThread() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (mClosedOnMainThread) { - return; + if (mOrigin.IsEmpty()) { + gLiveDatabaseHashtable->EnumerateRead(MatchContentParentId, this); + } else { + gLiveDatabaseHashtable->EnumerateRead(MatchOrigin, this); } - mClosedOnMainThread = true; -} - -void -DatabaseOfflineStorage::InvalidateOnMainThread() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (mInvalidatedOnMainThread) { - return; - } - - mInvalidatedOnMainThread = true; - - nsCOMPtr runnable = - NS_NewRunnableMethod(this, - &DatabaseOfflineStorage::InvalidateOnOwningThread); - MOZ_ASSERT(runnable); - - nsCOMPtr owningThread = mOwningThread; - MOZ_ASSERT(owningThread); - - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(owningThread->Dispatch(runnable, - NS_DISPATCH_NORMAL))); -} - -void -DatabaseOfflineStorage::InvalidateOnOwningThread() -{ - AssertIsOnBackgroundThread(); - - if (mInvalidatedOnOwningThread) { - return; - } - - mInvalidatedOnOwningThread = true; - - if (nsRefPtr database = mDatabase) { - mDatabase = nullptr; - + for (Database* database : mDatabases) { database->Invalidate(); } -} -void -DatabaseOfflineStorage::UnregisterOnMainThread() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mOwningThread); - MOZ_ASSERT(mRegisteredWithQuotaManager); + mDatabases.Clear(); - QuotaManager* quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager); - - quotaManager->UnregisterStorage(this); - mRegisteredWithQuotaManager = false; - - mStrongQuotaClient = nullptr; - - mOwningThread = nullptr; -} - -NS_IMPL_ISUPPORTS(DatabaseOfflineStorage, nsIOfflineStorage) - -NS_IMETHODIMP_(const nsACString&) -DatabaseOfflineStorage::Id() -{ - return mId; -} - -NS_IMETHODIMP_(Client*) -DatabaseOfflineStorage::GetClient() -{ - MOZ_ASSERT(NS_IsMainThread()); - - return mWeakQuotaClient; -} - -NS_IMETHODIMP_(bool) -DatabaseOfflineStorage::IsOwnedByProcess(ContentParent* aOwner) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aOwner); - - return mOptionalContentParentId.type() == - OptionalContentId::TContentParentId && - mOptionalContentParentId.get_ContentParentId() == aOwner->ChildID(); -} - -NS_IMETHODIMP_(const nsACString&) -DatabaseOfflineStorage::Origin() -{ - return mOrigin; -} - -NS_IMETHODIMP_(nsresult) -DatabaseOfflineStorage::Close() -{ - MOZ_ASSERT(NS_IsMainThread()); - - InvalidateOnMainThread(); return NS_OK; } -NS_IMETHODIMP_(void) -DatabaseOfflineStorage::Invalidate() +void +QuotaClient:: +GetDirectoryLockListener::DirectoryLockAcquired(DirectoryLock* aLock) { MOZ_ASSERT(NS_IsMainThread()); - InvalidateOnMainThread(); + MultipleMaintenanceInfo* maintenanceInfo; + MOZ_ALWAYS_TRUE( + mQuotaClient->mMaintenanceInfoHashtable->Get(mKey, &maintenanceInfo)); + MOZ_ASSERT(maintenanceInfo); + MOZ_ASSERT(!maintenanceInfo->mDirectoryLock); + + if (mQuotaClient->IdleMaintenanceMustEnd(mRunId)) { +#ifdef DEBUG + maintenanceInfo->mDatabasePaths.Clear(); +#endif + + mQuotaClient->mMaintenanceInfoHashtable->Remove(mKey); + return; + } + + maintenanceInfo->mDirectoryLock = aLock; + + mQuotaClient->ScheduleIdleMaintenance(mRunId, mKey, *maintenanceInfo); +} + +void +QuotaClient:: +GetDirectoryLockListener::DirectoryLockFailed() +{ + MOZ_ASSERT(NS_IsMainThread()); + + DebugOnly maintenanceInfo; + MOZ_ASSERT( + mQuotaClient->mMaintenanceInfoHashtable->Get(mKey, &maintenanceInfo)); + MOZ_ASSERT(maintenanceInfo); + MOZ_ASSERT(!maintenanceInfo->mDirectoryLock); + + mQuotaClient->mMaintenanceInfoHashtable->Remove(mKey); } /******************************************************************************* @@ -16152,12 +17852,13 @@ DatabaseOperationBase::DeleteObjectStoreDataTableRowsWithIndexes( keyRangeClause); } - rv = aConnection->GetCachedStatement( - NS_LITERAL_CSTRING("SELECT index_data_values, key " - "FROM object_data " - "WHERE object_store_id = :") + objectStoreIdString + - keyRangeClause + - NS_LITERAL_CSTRING(";"), + rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING( + "SELECT index_data_values, key " + "FROM object_data " + "WHERE object_store_id = :") + + objectStoreIdString + + keyRangeClause + + NS_LITERAL_CSTRING(";"), &selectStmt); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -16427,7 +18128,7 @@ FactoryOp::FactoryOp(Factory* aFactory, , mIsApp(false) , mEnforcingQuota(true) , mDeleting(aDeleting) - , mBlockedQuotaManager(false) + , mBlockedDatabaseOpen(false) , mChromeWriteAccessAllowed(false) { AssertIsOnBackgroundThread(); @@ -16484,14 +18185,24 @@ FactoryOp::Open() } } + QuotaClient* quotaClient = QuotaClient::GetInstance(); + if (NS_WARN_IF(!quotaClient)) { + IDB_REPORT_INTERNAL_ERR(); + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + quotaClient->NoteBackgroundThread(mOwningThread); + const DatabaseMetadata& metadata = mCommonParams.metadata(); QuotaManager::GetStorageId(metadata.persistenceType(), mOrigin, Client::IDB, - metadata.name(), mDatabaseId); + mDatabaseId.Append('*'); + mDatabaseId.Append(NS_ConvertUTF16toUTF8(metadata.name())); + if (permission == PermissionRequestBase::kPermissionPrompt) { mState = State_PermissionChallenge; MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, @@ -16568,11 +18279,51 @@ FactoryOp::RetryCheckPermission() return NS_OK; } +nsresult +FactoryOp::DirectoryOpen() +{ + AssertIsOnOwningThread(); + MOZ_ASSERT(mState == State_DirectoryWorkOpen); + MOZ_ASSERT(mDirectoryLock); + + // gFactoryOps could be null here if the child process crashed or something + // and that cleaned up the last Factory actor. + if (!gFactoryOps) { + return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + // See if this FactoryOp needs to wait. + bool delayed = false; + for (uint32_t index = gFactoryOps->Length(); index > 0; index--) { + nsRefPtr& existingOp = (*gFactoryOps)[index - 1]; + if (MustWaitFor(*existingOp)) { + // Only one op can be delayed. + MOZ_ASSERT(!existingOp->mDelayedOp); + existingOp->mDelayedOp = this; + delayed = true; + break; + } + } + + // Adding this to the factory ops list will block any additional ops from + // proceeding until this one is done. + gFactoryOps->AppendElement(this); + + mBlockedDatabaseOpen = true; + + mState = State_DatabaseOpenPending; + if (!delayed) { + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); + } + + return NS_OK; +} + nsresult FactoryOp::SendToIOThread() { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_OpenPending); + MOZ_ASSERT(mState == State_DatabaseOpenPending); if (NS_WARN_IF(QuotaClient::IsShuttingDownOnMainThread()) || !OperationMayProceed()) { @@ -16625,33 +18376,14 @@ FactoryOp::FinishSendResults() nsRefPtr factory; mFactory.swap(factory); - if (mBlockedQuotaManager) { - // Must set mState before dispatching otherwise we will race with the main - // thread. - mState = State_UnblockingQuotaManager; + if (mBlockedDatabaseOpen) { + if (mDelayedOp) { + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(mDelayedOp))); + mDelayedOp = nullptr; + } - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); - } else { - mState = State_Completed; - } -} - -void -FactoryOp::UnblockQuotaManager() -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_UnblockingQuotaManager); - - Nullable persistenceType( - const_cast(mCommonParams.metadata().persistenceType())); - - if (QuotaManager* quotaManager = QuotaManager::Get()) { - quotaManager-> - AllowNextSynchronizedOp(OriginOrPatternString::FromOrigin(mOrigin), - persistenceType, - mDatabaseId); - } else { - NS_WARNING("QuotaManager went away before we could unblock it!"); + MOZ_ASSERT(gFactoryOps); + gFactoryOps->RemoveElement(this); } mState = State_Completed; @@ -16962,7 +18694,7 @@ FactoryOp::FinishOpen() MOZ_ASSERT(mState == State_Initial || mState == State_PermissionRetry); MOZ_ASSERT(!mOrigin.IsEmpty()); MOZ_ASSERT(!mDatabaseId.IsEmpty()); - MOZ_ASSERT(!mBlockedQuotaManager); + MOZ_ASSERT(!mDirectoryLock); MOZ_ASSERT(!mContentParent); MOZ_ASSERT(!QuotaClient::IsShuttingDownOnMainThread()); @@ -16972,24 +18704,32 @@ FactoryOp::FinishOpen() return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } - PersistenceType persistenceType = mCommonParams.metadata().persistenceType(); + mState = State_DirectoryOpenPending; - nsresult rv = - quotaManager-> - WaitForOpenAllowed(OriginOrPatternString::FromOrigin(mOrigin), - Nullable(persistenceType), - mDatabaseId, - this); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } + quotaManager->OpenDirectory(mCommonParams.metadata().persistenceType(), + mGroup, + mOrigin, + mIsApp, + Client::IDB, + /* aExclusive */ false, + this); - mBlockedQuotaManager = true; - - mState = State_OpenPending; return NS_OK; } +bool +FactoryOp::MustWaitFor(const FactoryOp& aExistingOp) +{ + AssertIsOnOwningThread(); + + // Things for the same persistence type, the same origin and the same + // database must wait. + return aExistingOp.mCommonParams.metadata().persistenceType() == + mCommonParams.metadata().persistenceType() && + aExistingOp.mOrigin == mOrigin && + aExistingOp.mDatabaseId == mDatabaseId; +} + void FactoryOp::NoteDatabaseBlocked(Database* aDatabase) { @@ -17021,6 +18761,8 @@ FactoryOp::NoteDatabaseBlocked(Database* aDatabase) } } +NS_IMPL_ISUPPORTS_INHERITED0(FactoryOp, DatabaseOperationBase) + NS_IMETHODIMP FactoryOp::Run() { @@ -17031,10 +18773,6 @@ FactoryOp::Run() rv = Open(); break; - case State_OpenPending: - rv = QuotaManagerOpen(); - break; - case State_PermissionChallenge: rv = ChallengePermission(); break; @@ -17043,6 +18781,14 @@ FactoryOp::Run() rv = RetryCheckPermission(); break; + case State_DirectoryWorkOpen: + rv = DirectoryOpen(); + break; + + case State_DatabaseOpenPending: + rv = DatabaseOpen(); + break; + case State_DatabaseWorkOpen: rv = DoDatabaseWork(); break; @@ -17059,10 +18805,6 @@ FactoryOp::Run() SendResults(); return NS_OK; - case State_UnblockingQuotaManager: - UnblockQuotaManager(); - return NS_OK; - default: MOZ_CRASH("Bad state!"); } @@ -17083,6 +18825,37 @@ FactoryOp::Run() return NS_OK; } +void +FactoryOp::DirectoryLockAcquired(DirectoryLock* aLock) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_DirectoryOpenPending); + MOZ_ASSERT(!mDirectoryLock); + + mDirectoryLock = aLock; + + mState = State_DirectoryWorkOpen; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, + NS_DISPATCH_NORMAL))); +} + +void +FactoryOp::DirectoryLockFailed() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_DirectoryOpenPending); + MOZ_ASSERT(!mDirectoryLock); + + if (NS_SUCCEEDED(mResultCode)) { + IDB_REPORT_INTERNAL_ERR(); + mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + + mState = State_SendingResults; + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mOwningThread->Dispatch(this, + NS_DISPATCH_NORMAL))); +} + void FactoryOp::ActorDestroy(ActorDestroyReason aWhy) { @@ -17113,6 +18886,7 @@ OpenDatabaseOp::OpenDatabaseOp(Factory* aFactory, , mMetadata(new FullDatabaseMetadata(aParams.metadata())) , mRequestedVersion(aParams.metadata().version()) , mVersionChangeOp(nullptr) + , mTelemetryId(0) { auto& optionalContentParentId = const_cast(mOptionalContentParentId); @@ -17131,53 +18905,18 @@ OpenDatabaseOp::ActorDestroy(ActorDestroyReason aWhy) { AssertIsOnOwningThread(); - if (mDatabase && aWhy != Deletion) { - mDatabase->Invalidate(); - } + FactoryOp::ActorDestroy(aWhy); if (mVersionChangeOp) { mVersionChangeOp->NoteActorDestroyed(); } - - FactoryOp::ActorDestroy(aWhy); } nsresult -OpenDatabaseOp::QuotaManagerOpen() +OpenDatabaseOp::DatabaseOpen() { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_OpenPending); - MOZ_ASSERT(!mOfflineStorage); - - QuotaClient* quotaClient = QuotaClient::GetInstance(); - if (NS_WARN_IF(!quotaClient) || - NS_WARN_IF(quotaClient->IsShuttingDown())) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - QuotaManager* quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager); - - nsRefPtr offlineStorage = - new DatabaseOfflineStorage(quotaClient, - mOptionalContentParentId, - mGroup, - mOrigin, - mDatabaseId, - mCommonParams.metadata().persistenceType(), - mOwningThread); - - if (NS_WARN_IF(!quotaManager->RegisterStorage(offlineStorage))) { - IDB_REPORT_INTERNAL_ERR(); - return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - - offlineStorage->NoteRegisteredWithQuotaManager(); - - quotaClient->NoteBackgroundThread(mOwningThread); - - mOfflineStorage.swap(offlineStorage); + MOZ_ASSERT(mState == State_DatabaseOpenPending); nsresult rv = SendToIOThread(); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -17260,6 +18999,8 @@ OpenDatabaseOp::DoDatabaseWork() return rv; } + mTelemetryId = TelemetryIdForFile(dbFile); + rv = dbFile->GetPath(mDatabaseFilePath); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -17285,6 +19026,7 @@ OpenDatabaseOp::DoDatabaseWork() persistenceType, mGroup, mOrigin, + mTelemetryId, getter_AddRefs(connection)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -17331,6 +19073,7 @@ OpenDatabaseOp::DoDatabaseWork() fileManager = new FileManager(persistenceType, mGroup, mOrigin, + mIsApp, databaseName, mEnforcingQuota); @@ -17652,7 +19395,6 @@ OpenDatabaseOp::BeginVersionChange() MOZ_ASSERT(!mVersionChangeTransaction); if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || - !OperationMayProceed() || IsActorDestroyed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; @@ -17770,7 +19512,8 @@ OpenDatabaseOp::DispatchToWorkThread() IDBTransaction::VERSION_CHANGE); MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); - if (IsActorDestroyed()) { + if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || + IsActorDestroyed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -17789,6 +19532,10 @@ OpenDatabaseOp::DispatchToWorkThread() return NS_ERROR_OUT_OF_MEMORY; } + if (!gConnectionPool) { + gConnectionPool = new ConnectionPool(); + } + nsRefPtr versionChangeOp = new VersionChangeOp(this); uint64_t transactionId = @@ -17818,7 +19565,6 @@ OpenDatabaseOp::SendUpgradeNeeded() MOZ_ASSERT_IF(!IsActorDestroyed(), mDatabase); if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || - !OperationMayProceed() || IsActorDestroyed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; @@ -17879,7 +19625,11 @@ OpenDatabaseOp::SendResults() mVersionChangeTransaction = nullptr; } - if (!IsActorDestroyed()) { + if (IsActorDestroyed()) { + if (NS_SUCCEEDED(mResultCode)) { + mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + } else { FactoryRequestResponse response; if (NS_SUCCEEDED(mResultCode)) { @@ -17914,9 +19664,16 @@ OpenDatabaseOp::SendResults() PBackgroundIDBFactoryRequestParent::Send__delete__(this, response); } - if (NS_FAILED(mResultCode) && mOfflineStorage) { - mOfflineStorage->CloseOnOwningThread(); + if (mDatabase) { + MOZ_ASSERT(!mDirectoryLock); + if (NS_FAILED(mResultCode)) { + mDatabase->Invalidate(); + } + + // Make sure to release the database on this thread. + mDatabase = nullptr; + } else if (mDirectoryLock) { nsCOMPtr callback = NS_NewRunnableMethod(this, &OpenDatabaseOp::ConnectionClosedCallback); @@ -17925,10 +19682,6 @@ OpenDatabaseOp::SendResults() helper->WaitForTransactions(); } - // Make sure to release the database on this thread. - nsRefPtr database; - mDatabase.swap(database); - FinishSendResults(); } @@ -17937,9 +19690,12 @@ OpenDatabaseOp::ConnectionClosedCallback() { AssertIsOnOwningThread(); MOZ_ASSERT(NS_FAILED(mResultCode)); - MOZ_ASSERT(mOfflineStorage); + MOZ_ASSERT(mDirectoryLock); - DatabaseOfflineStorage::UnregisterOnOwningThread(mOfflineStorage.forget()); + nsRefPtr runnable = + new UnlockDirectoryRunnable(mDirectoryLock.forget()); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); } void @@ -17973,11 +19729,13 @@ OpenDatabaseOp::EnsureDatabaseActor() mDatabase = new Database(factory, mCommonParams.principalInfo(), + mOptionalContentParentId, mGroup, mOrigin, + mTelemetryId, mMetadata, mFileManager, - mOfflineStorage.forget(), + mDirectoryLock.forget(), mChromeWriteAccessAllowed); if (info) { @@ -18270,9 +20028,9 @@ VersionChangeOp::DoDatabaseWork(DatabaseConnection* aConnection) } DatabaseConnection::CachedStatement updateStmt; - rv = aConnection->GetCachedStatement( - NS_LITERAL_CSTRING("UPDATE database " - "SET version = :version"), + rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING( + "UPDATE database " + "SET version = :version;"), &updateStmt); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; @@ -18421,10 +20179,10 @@ DeleteDatabaseOp::LoadPreviousVersion(nsIFile* aDatabaseFile) } nsresult -DeleteDatabaseOp::QuotaManagerOpen() +DeleteDatabaseOp::DatabaseOpen() { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mState == State_OpenPending); + MOZ_ASSERT(mState == State_DatabaseOpenPending); // Swap this to the stack now to ensure that we release it on this thread. nsRefPtr contentParent; @@ -18526,7 +20284,6 @@ DeleteDatabaseOp::BeginVersionChange() MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || - !OperationMayProceed() || IsActorDestroyed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; @@ -18566,7 +20323,6 @@ DeleteDatabaseOp::DispatchToWorkThread() MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty()); if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonMainThread()) || - !OperationMayProceed() || IsActorDestroyed()) { IDB_REPORT_INTERNAL_ERR(); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; @@ -18650,6 +20406,13 @@ DeleteDatabaseOp::SendResults() PBackgroundIDBFactoryRequestParent::Send__delete__(this, response); } + if (mDirectoryLock) { + nsRefPtr runnable = + new UnlockDirectoryRunnable(mDirectoryLock.forget()); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); + } + FinishSendResults(); } @@ -19324,7 +21087,8 @@ CommitOp::AssertForeignKeyConsistency(DatabaseConnection* aConnection) DatabaseConnection::CachedStatement pragmaStmt; MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - aConnection->GetCachedStatement(NS_LITERAL_CSTRING("PRAGMA foreign_keys;"), &pragmaStmt))); + aConnection->GetCachedStatement(NS_LITERAL_CSTRING("PRAGMA foreign_keys;"), + &pragmaStmt))); bool hasResult; MOZ_ALWAYS_TRUE(NS_SUCCEEDED(pragmaStmt->ExecuteStep(&hasResult))); @@ -19338,7 +21102,9 @@ CommitOp::AssertForeignKeyConsistency(DatabaseConnection* aConnection) DatabaseConnection::CachedStatement checkStmt; MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - aConnection->GetCachedStatement(NS_LITERAL_CSTRING("PRAGMA foreign_key_check;"), &checkStmt))); + aConnection->GetCachedStatement( + NS_LITERAL_CSTRING("PRAGMA foreign_key_check;"), + &checkStmt))); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(checkStmt->ExecuteStep(&hasResult))); @@ -19390,23 +21156,16 @@ CommitOp::Run() if (NS_SUCCEEDED(mResultCode)) { AssertForeignKeyConsistency(connection); - DatabaseConnection::CachedStatement stmt; - mResultCode = connection->GetCachedStatement(NS_LITERAL_CSTRING("COMMIT"), &stmt); - NS_WARN_IF_FALSE(NS_SUCCEEDED(mResultCode), - "Failed to get 'COMMIT' statement!"); + mResultCode = connection->CommitWriteTransaction(); + NS_WARN_IF_FALSE(NS_SUCCEEDED(mResultCode), "Commit failed!"); - if (NS_SUCCEEDED(mResultCode)) { - mResultCode = stmt->Execute(); - NS_WARN_IF_FALSE(NS_SUCCEEDED(mResultCode), "Commit failed!"); + if (NS_SUCCEEDED(mResultCode) && + mTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH) { + mResultCode = connection->Checkpoint(); + } - if (mTransaction->GetMode() == IDBTransaction::READ_WRITE_FLUSH && - NS_SUCCEEDED(mResultCode)) { - mResultCode = connection->Checkpoint(/* aIdle */ false); - } - - if (NS_SUCCEEDED(mResultCode) && fileRefcountFunction) { - fileRefcountFunction->DidCommit(); - } + if (NS_SUCCEEDED(mResultCode) && fileRefcountFunction) { + fileRefcountFunction->DidCommit(); } } } @@ -19566,7 +21325,7 @@ CreateObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection) DatabaseConnection::CachedStatement stmt; rv = aConnection->GetCachedStatement(NS_LITERAL_CSTRING( "INSERT INTO object_store (id, auto_increment, name, key_path) " - "VALUES (:id, :auto_increment, :name, :key_path)"), + "VALUES (:id, :auto_increment, :name, :key_path);"), &stmt); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; diff --git a/dom/indexedDB/FileManager.h b/dom/indexedDB/FileManager.h index 243185ae14..028edaf587 100644 --- a/dom/indexedDB/FileManager.h +++ b/dom/indexedDB/FileManager.h @@ -42,6 +42,7 @@ class FileManager final // Protected by IndexedDatabaseManager::FileMutex() nsDataHashtable mFileInfos; + const bool mIsApp; const bool mEnforcingQuota; bool mInvalidated; @@ -54,7 +55,8 @@ public: nsIFile* aDatabaseFile, PersistenceType aPersistenceType, const nsACString& aGroup, - const nsACString& aOrigin); + const nsACString& aOrigin, + uint32_t aTelemetryId); static nsresult GetUsage(nsIFile* aDirectory, uint64_t* aUsage); @@ -62,6 +64,7 @@ public: FileManager(PersistenceType aPersistenceType, const nsACString& aGroup, const nsACString& aOrigin, + bool aIsApp, const nsAString& aDatabaseName, bool aEnforcingQuota); @@ -83,6 +86,12 @@ public: return mOrigin; } + bool + IsApp() const + { + return mIsApp; + } + const nsAString& DatabaseName() const { diff --git a/dom/indexedDB/FileSnapshot.cpp b/dom/indexedDB/FileSnapshot.cpp index 0fd2d790da..d0a22faf42 100644 --- a/dom/indexedDB/FileSnapshot.cpp +++ b/dom/indexedDB/FileSnapshot.cpp @@ -83,7 +83,7 @@ BlobImplSnapshot::~BlobImplSnapshot() void BlobImplSnapshot::AssertSanity() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); } diff --git a/dom/indexedDB/IDBMutableFile.cpp b/dom/indexedDB/IDBMutableFile.cpp index c8855c0753..74ccb51937 100644 --- a/dom/indexedDB/IDBMutableFile.cpp +++ b/dom/indexedDB/IDBMutableFile.cpp @@ -168,15 +168,19 @@ IDBMutableFile::Create(IDBDatabase* aDatabase, const DatabaseSpec* spec = aDatabase->Spec(); MOZ_ASSERT(spec); - PersistenceType persistenceType = spec->metadata().persistenceType(); + const DatabaseMetadata& metadata = spec->metadata(); + + PersistenceType persistenceType = metadata.persistenceType(); nsCString storageId; QuotaManager::GetStorageId(persistenceType, origin, Client::IDB, - aDatabase->Name(), storageId); + storageId.Append('*'); + storageId.Append(NS_ConvertUTF16toUTF8(metadata.name())); + nsCOMPtr file = GetFileFor(fileInfo); if (NS_WARN_IF(!file)) { return nullptr; diff --git a/dom/indexedDB/IndexedDatabaseManager.cpp b/dom/indexedDB/IndexedDatabaseManager.cpp index 805fd5dc64..3724dd5ee1 100644 --- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -25,13 +25,7 @@ #include "mozilla/dom/DOMError.h" #include "mozilla/dom/ErrorEvent.h" #include "mozilla/dom/ErrorEventBinding.h" -#include "mozilla/dom/PBlobChild.h" -#include "mozilla/dom/quota/OriginOrPatternString.h" #include "mozilla/dom/quota/QuotaManager.h" -#include "mozilla/dom/quota/Utilities.h" -#include "mozilla/dom/TabContext.h" -#include "mozilla/ipc/BackgroundChild.h" -#include "mozilla/ipc/PBackgroundChild.h" #include "nsContentUtils.h" #include "nsGlobalWindow.h" #include "nsThreadUtils.h" @@ -121,6 +115,8 @@ namespace { NS_DEFINE_IID(kIDBRequestIID, PRIVATE_IDBREQUEST_IID); +const uint32_t kDeleteTimeoutMs = 1000; + #define IDB_PREF_BRANCH_ROOT "dom.indexedDB." const char kTestingPref[] = IDB_PREF_BRANCH_ROOT "testing"; @@ -146,19 +142,73 @@ Atomic gClosed(false); Atomic gTestingMode(false); Atomic gExperimentalFeaturesEnabled(false); -class AsyncDeleteFileRunnable final : public nsIRunnable +class DeleteFilesRunnable final + : public nsIRunnable + , public OpenDirectoryListener { + typedef mozilla::dom::quota::DirectoryLock DirectoryLock; + + enum State + { + // Just created on the main thread. Next step is State_DirectoryOpenPending. + State_Initial, + + // Waiting for directory open allowed on the main thread. The next step is + // State_DatabaseWorkOpen. + State_DirectoryOpenPending, + + // Waiting to do/doing work on the QuotaManager IO thread. The next step is + // State_UnblockingOpen. + State_DatabaseWorkOpen, + + // Notifying the QuotaManager that it can proceed to the next operation on + // the main thread. Next step is State_Completed. + State_UnblockingOpen, + + // All done. + State_Completed + }; + + nsRefPtr mFileManager; + nsTArray mFileIds; + + nsRefPtr mDirectoryLock; + + nsCOMPtr mDirectory; + nsCOMPtr mJournalDirectory; + + State mState; + public: + DeleteFilesRunnable(FileManager* aFileManager, + nsTArray& aFileIds); + NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSIRUNNABLE - AsyncDeleteFileRunnable(FileManager* aFileManager, int64_t aFileId); + virtual void + DirectoryLockAcquired(DirectoryLock* aLock) override; + + virtual void + DirectoryLockFailed() override; private: - ~AsyncDeleteFileRunnable() {} + ~DeleteFilesRunnable() {} - nsRefPtr mFileManager; - int64_t mFileId; + nsresult + Open(); + + nsresult + DeleteFile(int64_t aFileId); + + nsresult + DoDatabaseWork(); + + void + Finish(); + + void + UnblockOpen(); }; class GetFileReferencesHelper final : public nsIRunnable @@ -252,7 +302,7 @@ IndexedDatabaseManager::GetOrCreate() } if (!gDBManager) { - sIsMainProcess = XRE_GetProcessType() == GeckoProcessType_Default; + sIsMainProcess = XRE_IsParentProcess(); if (!sLoggingModule) { sLoggingModule = PR_NewLogModule("IndexedDB"); @@ -315,6 +365,9 @@ IndexedDatabaseManager::Init() nsresult rv = obs->AddObserver(this, DISKSPACEWATCHER_OBSERVER_TOPIC, false); NS_ENSURE_SUCCESS(rv, rv); + + mDeleteTimer = do_CreateInstance(NS_TIMER_CONTRACTID); + NS_ENSURE_STATE(mDeleteTimer); } Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback, @@ -353,6 +406,14 @@ IndexedDatabaseManager::Destroy() NS_ERROR("Shutdown more than once?!"); } + if (sIsMainProcess && mDeleteTimer) { + if (NS_FAILED(mDeleteTimer->Cancel())) { + NS_WARNING("Failed to cancel timer!"); + } + + mDeleteTimer = nullptr; + } + Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback, kTestingPref, &gTestingMode); @@ -515,25 +576,6 @@ IndexedDatabaseManager::CommonPostHandleEvent(EventChainPostVisitor& aVisitor, return NS_OK; } -// static -bool -IndexedDatabaseManager::TabContextMayAccessOrigin(const TabContext& aContext, - const nsACString& aOrigin) -{ - NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!"); - - // If aContext is for a browser element, it's allowed only to access other - // browser elements. But if aContext is not for a browser element, it may - // access both browser and non-browser elements. - nsAutoCString pattern; - QuotaManager::GetOriginPatternStringMaybeIgnoreBrowser( - aContext.OwnOrContainingAppId(), - aContext.IsBrowserElement(), - pattern); - - return PatternMatchesOrigin(pattern, aOrigin); -} - // static bool IndexedDatabaseManager::DefineIndexedDB(JSContext* aCx, @@ -597,7 +639,7 @@ IndexedDatabaseManager::IsMainProcess() { NS_ASSERTION(gDBManager, "IsMainProcess() called before indexedDB has been initialized!"); - NS_ASSERTION((XRE_GetProcessType() == GeckoProcessType_Default) == + NS_ASSERTION((XRE_IsParentProcess()) == sIsMainProcess, "XRE_GetProcessType changed its tune!"); return sIsMainProcess; } @@ -733,9 +775,8 @@ IndexedDatabaseManager::InvalidateAllFileManagers() } void -IndexedDatabaseManager::InvalidateFileManagers( - PersistenceType aPersistenceType, - const nsACString& aOrigin) +IndexedDatabaseManager::InvalidateFileManagers(PersistenceType aPersistenceType, + const nsACString& aOrigin) { AssertIsOnIOThread(); MOZ_ASSERT(!aOrigin.IsEmpty()); @@ -775,27 +816,30 @@ nsresult IndexedDatabaseManager::AsyncDeleteFile(FileManager* aFileManager, int64_t aFileId) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + MOZ_ASSERT(IsMainProcess()); + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aFileManager); + MOZ_ASSERT(aFileId > 0); + MOZ_ASSERT(mDeleteTimer); - NS_ENSURE_ARG_POINTER(aFileManager); - - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "Shouldn't be null!"); - - // See if we're currently clearing the storages for this origin. If so then - // we pretend that we've already deleted everything. - if (quotaManager->IsClearOriginPending( - aFileManager->Origin(), - Nullable(aFileManager->Type()))) { - return NS_OK; + nsresult rv = mDeleteTimer->Cancel(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; } - nsRefPtr runnable = - new AsyncDeleteFileRunnable(aFileManager, aFileId); + rv = mDeleteTimer->InitWithCallback(this, kDeleteTimeoutMs, + nsITimer::TYPE_ONE_SHOT); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } - nsresult rv = - quotaManager->IOThread()->Dispatch(runnable, NS_DISPATCH_NORMAL); - NS_ENSURE_SUCCESS(rv, rv); + nsTArray* array; + if (!mPendingDeleteInfos.Get(aFileManager, &array)) { + array = new nsTArray(); + mPendingDeleteInfos.Put(aFileManager, array); + } + + array->AppendElement(aFileId); return NS_OK; } @@ -811,6 +855,8 @@ IndexedDatabaseManager::BlockAndGetFileReferences( int32_t* aSliceRefCnt, bool* aResult) { + MOZ_ASSERT(NS_IsMainThread()); + if (NS_WARN_IF(!InTestingMode())) { return NS_ERROR_UNEXPECTED; } @@ -847,6 +893,39 @@ IndexedDatabaseManager::BlockAndGetFileReferences( return NS_OK; } +nsresult +IndexedDatabaseManager::FlushPendingFileDeletions() +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (NS_WARN_IF(!InTestingMode())) { + return NS_ERROR_UNEXPECTED; + } + + if (IsMainProcess()) { + nsresult rv = mDeleteTimer->Cancel(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = Notify(mDeleteTimer); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } else { + ContentChild* contentChild = ContentChild::GetSingleton(); + if (NS_WARN_IF(!contentChild)) { + return NS_ERROR_FAILURE; + } + + if (!contentChild->SendFlushPendingFileDeletions()) { + return NS_ERROR_FAILURE; + } + } + + return NS_OK; +} + // static void IndexedDatabaseManager::LoggingModePrefChangedCallback( @@ -887,7 +966,7 @@ IndexedDatabaseManager::LoggingModePrefChangedCallback( NS_IMPL_ADDREF(IndexedDatabaseManager) NS_IMPL_RELEASE_WITH_DESTROY(IndexedDatabaseManager, Destroy()) -NS_IMPL_QUERY_INTERFACE(IndexedDatabaseManager, nsIObserver) +NS_IMPL_QUERY_INTERFACE(IndexedDatabaseManager, nsIObserver, nsITimerCallback) NS_IMETHODIMP IndexedDatabaseManager::Observe(nsISupports* aSubject, const char* aTopic, @@ -916,7 +995,41 @@ IndexedDatabaseManager::Observe(nsISupports* aSubject, const char* aTopic, NS_NOTREACHED("Unknown topic!"); return NS_ERROR_UNEXPECTED; - } +} + +NS_IMETHODIMP +IndexedDatabaseManager::Notify(nsITimer* aTimer) +{ + MOZ_ASSERT(IsMainProcess()); + MOZ_ASSERT(NS_IsMainThread()); + + class MOZ_STACK_CLASS Helper final + { + public: + static PLDHashOperator + CreateAndDispatchRunnables(FileManager* aFileManager, + nsTArray* aValue, + void* aClosure) + { + MOZ_ASSERT(!aValue->IsEmpty()); + + nsRefPtr runnable = + new DeleteFilesRunnable(aFileManager, *aValue); + + MOZ_ASSERT(aValue->IsEmpty()); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); + + return PL_DHASH_NEXT; + } + }; + + mPendingDeleteInfos.EnumerateRead(Helper::CreateAndDispatchRunnables, + nullptr); + mPendingDeleteInfos.Clear(); + + return NS_OK; +} already_AddRefed FileManagerInfo::GetFileManager(PersistenceType aPersistenceType, @@ -1022,24 +1135,109 @@ FileManagerInfo::GetArray(PersistenceType aPersistenceType) } } -AsyncDeleteFileRunnable::AsyncDeleteFileRunnable(FileManager* aFileManager, - int64_t aFileId) -: mFileManager(aFileManager), mFileId(aFileId) +DeleteFilesRunnable::DeleteFilesRunnable(FileManager* aFileManager, + nsTArray& aFileIds) + : mFileManager(aFileManager) + , mState(State_Initial) { + mFileIds.SwapElements(aFileIds); } -NS_IMPL_ISUPPORTS(AsyncDeleteFileRunnable, - nsIRunnable) +NS_IMPL_ISUPPORTS(DeleteFilesRunnable, nsIRunnable) NS_IMETHODIMP -AsyncDeleteFileRunnable::Run() +DeleteFilesRunnable::Run() { - AssertIsOnIOThread(); + nsresult rv; - nsCOMPtr directory = mFileManager->GetDirectory(); - NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE); + switch (mState) { + case State_Initial: + rv = Open(); + break; - nsCOMPtr file = mFileManager->GetFileForId(directory, mFileId); + case State_DatabaseWorkOpen: + rv = DoDatabaseWork(); + break; + + case State_UnblockingOpen: + UnblockOpen(); + return NS_OK; + + case State_DirectoryOpenPending: + default: + MOZ_CRASH("Should never get here!"); + } + + if (NS_WARN_IF(NS_FAILED(rv)) && mState != State_UnblockingOpen) { + Finish(); + } + + return NS_OK; +} + +void +DeleteFilesRunnable::DirectoryLockAcquired(DirectoryLock* aLock) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_DirectoryOpenPending); + MOZ_ASSERT(!mDirectoryLock); + + mDirectoryLock = aLock; + + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + // Must set this before dispatching otherwise we will race with the IO thread + mState = State_DatabaseWorkOpen; + + nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL); + if (NS_WARN_IF(NS_FAILED(rv))) { + Finish(); + return; + } +} + +void +DeleteFilesRunnable::DirectoryLockFailed() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_DirectoryOpenPending); + MOZ_ASSERT(!mDirectoryLock); + + Finish(); +} + +nsresult +DeleteFilesRunnable::Open() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_Initial); + + QuotaManager* quotaManager = QuotaManager::Get(); + if (NS_WARN_IF(!quotaManager)) { + return NS_ERROR_FAILURE; + } + + mState = State_DirectoryOpenPending; + + quotaManager->OpenDirectory(mFileManager->Type(), + mFileManager->Group(), + mFileManager->Origin(), + mFileManager->IsApp(), + Client::IDB, + /* aExclusive */ false, + this); + + return NS_OK; +} + +nsresult +DeleteFilesRunnable::DeleteFile(int64_t aFileId) +{ + MOZ_ASSERT(mDirectory); + MOZ_ASSERT(mJournalDirectory); + + nsCOMPtr file = mFileManager->GetFileForId(mDirectory, aFileId); NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); nsresult rv; @@ -1062,10 +1260,7 @@ AsyncDeleteFileRunnable::Run() mFileManager->Origin(), fileSize); } - directory = mFileManager->GetJournalDirectory(); - NS_ENSURE_TRUE(directory, NS_ERROR_FAILURE); - - file = mFileManager->GetFileForId(directory, mFileId); + file = mFileManager->GetFileForId(mJournalDirectory, aFileId); NS_ENSURE_TRUE(file, NS_ERROR_FAILURE); rv = file->Remove(false); @@ -1074,6 +1269,58 @@ AsyncDeleteFileRunnable::Run() return NS_OK; } +nsresult +DeleteFilesRunnable::DoDatabaseWork() +{ + AssertIsOnIOThread(); + MOZ_ASSERT(mState == State_DatabaseWorkOpen); + + if (!mFileManager->Invalidated()) { + mDirectory = mFileManager->GetDirectory(); + if (NS_WARN_IF(!mDirectory)) { + return NS_ERROR_FAILURE; + } + + mJournalDirectory = mFileManager->GetJournalDirectory(); + if (NS_WARN_IF(!mJournalDirectory)) { + return NS_ERROR_FAILURE; + } + + for (int64_t fileId : mFileIds) { + if (NS_FAILED(DeleteFile(fileId))) { + NS_WARNING("Failed to delete file!"); + } + } + } + + Finish(); + + return NS_OK; +} + +void +DeleteFilesRunnable::Finish() +{ + // Must set mState before dispatching otherwise we will race with the main + // thread. + mState = State_UnblockingOpen; + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); +} + +void +DeleteFilesRunnable::UnblockOpen() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_UnblockingOpen); + + if (mDirectoryLock) { + mDirectoryLock = nullptr; + } + + mState = State_Completed; +} + nsresult GetFileReferencesHelper::DispatchAndReturnFileReferences(int32_t* aMemRefCnt, int32_t* aDBRefCnt, diff --git a/dom/indexedDB/IndexedDatabaseManager.h b/dom/indexedDB/IndexedDatabaseManager.h index a22073762c..5697f6ef92 100644 --- a/dom/indexedDB/IndexedDatabaseManager.h +++ b/dom/indexedDB/IndexedDatabaseManager.h @@ -14,7 +14,9 @@ #include "mozilla/dom/quota/PersistenceType.h" #include "mozilla/Mutex.h" #include "nsClassHashtable.h" +#include "nsCOMPtr.h" #include "nsHashKeys.h" +#include "nsITimer.h" struct PRLogModuleInfo; @@ -24,15 +26,15 @@ class EventChainPostVisitor; namespace dom { -class TabContext; - namespace indexedDB { class FileManager; class FileManagerInfo; class IDBFactory; -class IndexedDatabaseManager final : public nsIObserver +class IndexedDatabaseManager final + : public nsIObserver + , public nsITimerCallback { typedef mozilla::dom::quota::PersistenceType PersistenceType; @@ -48,6 +50,7 @@ public: NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER + NS_DECL_NSITIMERCALLBACK // Returns a non-owning reference. static IndexedDatabaseManager* @@ -152,6 +155,9 @@ public: int32_t* aSliceRefCnt, bool* aResult); + nsresult + FlushPendingFileDeletions(); + static mozilla::Mutex& FileMutex() { @@ -164,10 +170,6 @@ public: static nsresult CommonPostHandleEvent(EventChainPostVisitor& aVisitor, IDBFactory* aFactory); - static bool - TabContextMayAccessOrigin(const mozilla::dom::TabContext& aContext, - const nsACString& aOrigin); - static bool DefineIndexedDB(JSContext* aCx, JS::Handle aGlobal); @@ -184,10 +186,15 @@ private: static void LoggingModePrefChangedCallback(const char* aPrefName, void* aClosure); + nsCOMPtr mDeleteTimer; + // Maintains a list of all file managers per origin. This list isn't // protected by any mutex but it is only ever touched on the IO thread. nsClassHashtable mFileManagerInfos; + nsClassHashtable, + nsTArray> mPendingDeleteInfos; + // Lock protecting FileManager.mFileInfos and BlobImplBase.mFileInfos // It's s also used to atomically update FileInfo.mRefCnt, FileInfo.mDBRefCnt // and FileInfo.mSliceRefCnt diff --git a/dom/indexedDB/PermissionRequestBase.cpp b/dom/indexedDB/PermissionRequestBase.cpp index 84e669a35f..668869042f 100644 --- a/dom/indexedDB/PermissionRequestBase.cpp +++ b/dom/indexedDB/PermissionRequestBase.cpp @@ -43,7 +43,7 @@ const uint32_t kPermissionDefault = nsIPermissionManager::UNKNOWN_ACTION; void AssertSanity() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); } diff --git a/dom/indexedDB/moz.build b/dom/indexedDB/moz.build index 36f927a16d..4f2e6dfcd2 100644 --- a/dom/indexedDB/moz.build +++ b/dom/indexedDB/moz.build @@ -96,4 +96,5 @@ LOCAL_INCLUDES += [ '/dom/workers', '/ipc/glue', '/xpcom/build', + '/xpcom/threads', ] diff --git a/dom/indexedDB/test/file.js b/dom/indexedDB/test/file.js index ae63af38f9..8bbf35c4f1 100644 --- a/dom/indexedDB/test/file.js +++ b/dom/indexedDB/test/file.js @@ -220,3 +220,8 @@ function getFileDBRefCount(name, id) utils.getFileReferences(name, id, null, {}, count); return count.value; } + +function flushPendingFileDeletions() +{ + utils.flushPendingFileDeletions(); +} diff --git a/dom/indexedDB/test/test_file_os_delete.html b/dom/indexedDB/test/test_file_os_delete.html index 6403f3f514..94e520581e 100644 --- a/dom/indexedDB/test/test_file_os_delete.html +++ b/dom/indexedDB/test/test_file_os_delete.html @@ -90,6 +90,9 @@ request.onsuccess = grabEventAndContinueHandler; yield undefined; + // and also flush pending file deletions before checking usage. + flushPendingFileDeletions(); + getUsage(grabFileUsageAndContinueHandler); let endUsage = yield undefined; diff --git a/dom/indexedDB/test/unit/defaultStorageUpgrade_profile.zip b/dom/indexedDB/test/unit/defaultStorageUpgrade_profile.zip index c19a2ef5d6..1a7d61a4f6 100644 Binary files a/dom/indexedDB/test/unit/defaultStorageUpgrade_profile.zip and b/dom/indexedDB/test/unit/defaultStorageUpgrade_profile.zip differ diff --git a/dom/indexedDB/test/unit/metadataRestore_profile.zip b/dom/indexedDB/test/unit/metadataRestore_profile.zip new file mode 100644 index 0000000000..a01d49166e Binary files /dev/null and b/dom/indexedDB/test/unit/metadataRestore_profile.zip differ diff --git a/dom/indexedDB/test/unit/test_defaultStorageUpgrade.js b/dom/indexedDB/test/unit/test_defaultStorageUpgrade.js index f2d6e0a088..7f7ab312ed 100644 --- a/dom/indexedDB/test/unit/test_defaultStorageUpgrade.js +++ b/dom/indexedDB/test/unit/test_defaultStorageUpgrade.js @@ -63,6 +63,18 @@ function testSteps() // This one lives in storage/default/file++++c++ { url: "file:///c:/", dbName: "dbQ", dbVersion: 1 }, + // This one lives in storage/default/file++++Users+joe+c+++index.html + { url: "file:///Users/joe/c++/index.html", dbName: "dbR", dbVersion: 1 }, + + // This one lives in storage/default/file++++Users+joe+c+++index.html + { url: "file:///Users/joe/c///index.html", dbName: "dbR", dbVersion: 1 }, + + // This one lives in storage/default/file++++++index.html + { url: "file:///+/index.html", dbName: "dbS", dbVersion: 1 }, + + // This one lives in storage/default/file++++++index.html + { url: "file://///index.html", dbName: "dbS", dbVersion: 1 }, + // This one lives in storage/temporary/http+++localhost { url: "http://localhost", dbName: "dbZ", dbOptions: { version: 1, storage: "temporary" } } diff --git a/dom/indexedDB/test/unit/test_idle_maintenance.js b/dom/indexedDB/test/unit/test_idle_maintenance.js new file mode 100644 index 0000000000..3b347e4574 --- /dev/null +++ b/dom/indexedDB/test/unit/test_idle_maintenance.js @@ -0,0 +1,136 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + let uri = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService). + newURI("https://www.example.com", null, null); + let principal = Cc["@mozilla.org/scriptsecuritymanager;1"]. + getService(Ci.nsIScriptSecurityManager). + getNoAppCodebasePrincipal(uri); + + info("Setting permissions"); + + let permMgr = + Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager); + permMgr.add(uri, "indexedDB", Ci.nsIPermissionManager.ALLOW_ACTION); + + info("Setting idle preferences to prevent real 'idle-daily' notification"); + + let prefs = + Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); + prefs.setIntPref("idle.lastDailyNotification", (Date.now() / 1000) - 10); + + info("Activating real idle service"); + + do_get_idle(); + + info("Creating databases"); + + let quotaManager = + Cc["@mozilla.org/dom/quota/manager;1"].getService(Ci.nsIQuotaManager); + + let dbCount = 0; + + for (let persistence of ["persistent", "temporary", "default"]) { + for (let i = 1; i <= 5; i++) { + let dbName = "foo-" + i; + let dbPersistence = persistence; + let req = indexedDB.openForPrincipal(principal, + dbName, + { version: 1, + storage: dbPersistence }); + req.onerror = event => { + if (dbPersistence != "persistent") { + errorHandler(event); + return; + } + + // Explicit persistence is currently blocked on mobile. + info("Failed to create persistent database '" + dbPersistence + "/" + + dbName + "', hopefully this is on mobile!"); + + event.preventDefault(); + + if (!(--dbCount)) { + continueToNextStep(); + } + }; + req.onupgradeneeded = event => { + let db = event.target.result; + let objectStore = db.createObjectStore("foo"); + + // Add lots of data... + for (let j = 0; j < 100; j++) { + objectStore.add("abcdefghijklmnopqrstuvwxyz0123456789", j); + } + + // And then clear it so that maintenance has some space to reclaim. + objectStore.clear(); + }; + req.onsuccess = event => { + let db = event.target.result; + ok(db, "Created database '" + dbPersistence + "/" + dbName + "'"); + + db.close(); + + if (!(--dbCount)) { + continueToNextStep(); + } + }; + dbCount++; + } + } + yield undefined; + + info("Getting usage before maintenance"); + + let usageBeforeMaintenance; + + quotaManager.getUsageForURI(uri, (url, usage) => { + ok(usage > 0, "Usage is non-zero"); + usageBeforeMaintenance = usage; + continueToNextStep(); + }); + yield undefined; + + info("Sending fake 'idle-daily' notification to QuotaManager"); + + let observer = quotaManager.QueryInterface(Ci.nsIObserver); + observer.observe(null, "idle-daily", ""); + + info("Waiting for maintenance to start"); + + // This time is totally arbitrary. Most likely directory scanning will have + // completed, QuotaManager locks will be acquired, and maintenance tasks will + // be scheduled before this time has elapsed, so we will be testing the + // maintenance code. However, if something is slow then this will test + // shutting down in the middle of maintenance. + setTimeout(continueToNextStep, 10000); + yield undefined; + + info("Getting usage after maintenance"); + + let usageAfterMaintenance; + + quotaManager.getUsageForURI(uri, (url, usage) => { + ok(usage > 0, "Usage is non-zero"); + usageAfterMaintenance = usage; + continueToNextStep(); + }); + yield undefined; + + info("Usage before: " + usageBeforeMaintenance + ". " + + "Usage after: " + usageAfterMaintenance); + + ok(usageAfterMaintenance <= usageBeforeMaintenance, + "Maintenance decreased file sizes or left them the same"); + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/test_metadataRestore.js b/dom/indexedDB/test/unit/test_metadataRestore.js new file mode 100644 index 0000000000..24285740eb --- /dev/null +++ b/dom/indexedDB/test/unit/test_metadataRestore.js @@ -0,0 +1,109 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator = testSteps(); + +function testSteps() +{ + const openParams = [ + // This one lives in storage/permanent/chrome + { dbName: "dbA", + dbOptions: { version: 1, storage: "persistent" } }, + + // This one lives in storage/temporary/http+++localhost + { url: "http://localhost", dbName: "dbB", + dbOptions: { version: 1, storage: "temporary" } }, + + // This one lives in storage/default/http+++localhost+81 + { url: "http://localhost:81", dbName: "dbC", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+82 + { url: "http://localhost:82", dbName: "dbD", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+83 + { url: "http://localhost:83", dbName: "dbE", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+84 + { url: "http://localhost:84", dbName: "dbF", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+85 + { url: "http://localhost:85", dbName: "dbG", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+86 + { url: "http://localhost:86", dbName: "dbH", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+87 + { url: "http://localhost:87", dbName: "dbI", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+88 + { url: "http://localhost:88", dbName: "dbJ", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+89 + { url: "http://localhost:89", dbName: "dbK", + dbOptions: { version: 1, storage: "default" } }, + + // This one lives in storage/default/http+++localhost+90 + { url: "http://localhost:90", dbName: "dbL", + dbOptions: { version: 1, storage: "default" } } + ]; + + let ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"] + .getService(SpecialPowers.Ci.nsIIOService); + + let ssm = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(SpecialPowers.Ci.nsIScriptSecurityManager); + + function openDatabase(params) { + let request; + if ("url" in params) { + let uri = ios.newURI(params.url, null, null); + let principal = ssm.getNoAppCodebasePrincipal(uri); + request = indexedDB.openForPrincipal(principal, params.dbName, + params.dbOptions); + } else { + request = indexedDB.open(params.dbName, params.dbOptions); + } + return request; + } + + clearAllDatabases(continueToNextStepSync); + yield undefined; + + installPackagedProfile("metadataRestore_profile"); + + for (let params of openParams) { + let request = openDatabase(params); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Correct event type"); + } + + resetAllDatabases(continueToNextStepSync); + yield undefined; + + for (let params of openParams) { + let request = openDatabase(params); + request.onerror = errorHandler; + request.onupgradeneeded = unexpectedSuccessHandler; + request.onsuccess = grabEventAndContinueHandler; + let event = yield undefined; + + is(event.type, "success", "Correct event type"); + } + + finishTest(); + yield undefined; +} diff --git a/dom/indexedDB/test/unit/xpcshell-parent-process.ini b/dom/indexedDB/test/unit/xpcshell-parent-process.ini index e16aafc2f8..6760b752c6 100644 --- a/dom/indexedDB/test/unit/xpcshell-parent-process.ini +++ b/dom/indexedDB/test/unit/xpcshell-parent-process.ini @@ -15,6 +15,7 @@ support-files = GlobalObjectsComponent.manifest GlobalObjectsModule.jsm GlobalObjectsSandbox.js + metadataRestore_profile.zip schema18upgrade_profile.zip xpcshell-shared.ini @@ -25,10 +26,12 @@ support-files = [test_defaultStorageUpgrade.js] [test_globalObjects_ipc.js] skip-if = toolkit == 'android' +[test_idle_maintenance.js] [test_invalidate.js] # disabled for the moment. skip-if = true [test_lowDiskSpace.js] +[test_metadataRestore.js] [test_readwriteflush_disabled.js] [test_schema18upgrade.js] [test_temporary_storage.js] diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index e95649f23c..f255a22243 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -49,7 +49,7 @@ interface nsIJSRAIIHelper; interface nsIContentPermissionRequest; interface nsIObserver; -[scriptable, uuid(ec176f3b-2886-4090-938e-dded103c5f1c)] +[scriptable, uuid(e7b44320-8255-4ad1-bbe9-d78a8a1867c9)] interface nsIDOMWindowUtils : nsISupports { /** @@ -1438,6 +1438,16 @@ interface nsIDOMWindowUtils : nsISupports { */ void setAsyncZoom(in nsIDOMNode aRootElement, in float aValue); + /** + * Do a round-trip to the compositor to ensure any pending APZ repaint requests + * get flushed to the main thread. If the function returns true, the flush was + * triggered and an "apz-repaints-flushed" notification will be dispatched via + * the observer service once the flush is complete. If the function returns + * false, an error occurred or a flush is not needed, and the notification + * will not fire. This is intended to be used by test code only! + */ + bool flushApzRepaints(); + /** * Method for testing StyleAnimationValue::ComputeDistance. * @@ -1528,6 +1538,8 @@ interface nsIDOMWindowUtils : nsISupports { [optional] out long aDBRefCnt, [optional] out long aSliceRefCnt); + void flushPendingFileDeletions(); + /** * Return whether incremental GC has been disabled due to a binary add-on. */ diff --git a/dom/ipc/Blob.cpp b/dom/ipc/Blob.cpp index 82ab4bd94e..222630c038 100644 --- a/dom/ipc/Blob.cpp +++ b/dom/ipc/Blob.cpp @@ -3284,7 +3284,7 @@ BlobChild::AssertIsOnOwningThread() const void BlobChild::Startup(const FriendKey& /* aKey */) { - MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default); + MOZ_ASSERT(!XRE_IsParentProcess()); CommonStartup(); } @@ -3812,7 +3812,7 @@ BlobParent::AssertIsOnOwningThread() const void BlobParent::Startup(const FriendKey& /* aKey */) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); CommonStartup(); diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 2c62bfd6c6..d0ffb5dd78 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -2808,7 +2808,7 @@ uint64_t NextWindowID() { uint64_t processID = 0; - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { ContentChild* cc = ContentChild::GetSingleton(); processID = cc->GetID(); } diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 4172e775c6..2c921fb2ec 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -762,7 +762,7 @@ ContentParent::StartUp() // child process sCanLaunchSubprocesses = true; - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return; } @@ -1132,7 +1132,7 @@ ContentParent::CreateBrowserOrApp(const TabContext& aContext, } ProcessPriority initialPriority = GetInitialProcessPriority(aFrameElement); - bool isInContentProcess = (XRE_GetProcessType() != GeckoProcessType_Default); + bool isInContentProcess = !XRE_IsParentProcess(); TabId tabId; nsIDocShell* docShell = GetOpenerDocShellHelper(aFrameElement); @@ -1692,7 +1692,7 @@ ContentParent::ShutDownProcess(ShutDownMethod aMethod) using mozilla::dom::quota::QuotaManager; if (QuotaManager* quotaManager = QuotaManager::Get()) { - quotaManager->AbortCloseStoragesForProcess(this); + quotaManager->AbortOperationsForProcess(mChildID); } // If Close() fails with an error, we'll end up back in this function, but @@ -1810,7 +1810,7 @@ ContentParent::OnChannelError() void ContentParent::OnBeginSyncTransaction() { - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { nsCOMPtr console(do_GetService(NS_CONSOLESERVICE_CONTRACTID)); JSContext *cx = nsContentUtils::GetCurrentJSContext(); if (!sDisableUnsafeCPOWWarnings) { @@ -4798,6 +4798,26 @@ ContentParent::RecvGetFileReferences(const PersistenceType& aPersistenceType, return true; } +bool +ContentParent::RecvFlushPendingFileDeletions() +{ + nsRefPtr mgr = IndexedDatabaseManager::Get(); + if (NS_WARN_IF(!mgr)) { + return false; + } + + if (NS_WARN_IF(!mgr->IsMainProcess())) { + return false; + } + + nsresult rv = mgr->FlushPendingFileDeletions(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + return true; +} + bool ContentParent::IgnoreIPCPrincipal() { @@ -4834,7 +4854,7 @@ ContentParent::AllocateTabId(const TabId& aOpenerTabId, const ContentParentId& aCpId) { TabId tabId; - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { ContentProcessManager *cpm = ContentProcessManager::GetSingleton(); tabId = cpm->AllocateTabId(aOpenerTabId, aContext, aCpId); } @@ -4851,7 +4871,7 @@ ContentParent::AllocateTabId(const TabId& aOpenerTabId, ContentParent::DeallocateTabId(const TabId& aTabId, const ContentParentId& aCpId) { - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { ContentProcessManager::GetSingleton()->DeallocateTabId(aCpId, aTabId); } @@ -5050,7 +5070,7 @@ ContentParent::DeallocPContentPermissionRequestParent(PContentPermissionRequestP bool ContentParent::RecvGetBrowserConfiguration(const nsCString& aURI, BrowserConfiguration* aConfig) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); return GetBrowserConfiguration(aURI, *aConfig);; } @@ -5058,7 +5078,7 @@ ContentParent::RecvGetBrowserConfiguration(const nsCString& aURI, BrowserConfigu /*static*/ bool ContentParent::GetBrowserConfiguration(const nsCString& aURI, BrowserConfiguration& aConfig) { - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { nsRefPtr swr = ServiceWorkerRegistrar::Get(); MOZ_ASSERT(swr); diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 5722b75d1c..8152652728 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -841,6 +841,9 @@ private: int32_t* aSliceRefCnt, bool* aResult) override; + virtual bool + RecvFlushPendingFileDeletions() override; + virtual PWebrtcGlobalParent* AllocPWebrtcGlobalParent() override; virtual bool DeallocPWebrtcGlobalParent(PWebrtcGlobalParent *aActor) override; diff --git a/dom/ipc/ContentProcessManager.cpp b/dom/ipc/ContentProcessManager.cpp index e69d5fb782..51c3588c08 100644 --- a/dom/ipc/ContentProcessManager.cpp +++ b/dom/ipc/ContentProcessManager.cpp @@ -32,7 +32,7 @@ ContentProcessManager::sSingleton; /* static */ ContentProcessManager* ContentProcessManager::GetSingleton() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); if (!sSingleton) { sSingleton = new ContentProcessManager(); diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 52d23a0ba5..55387eba75 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -593,6 +593,7 @@ child: HandleSingleTap(CSSPoint aPoint, Modifiers aModifiers, ScrollableLayerGuid aGuid); HandleLongTap(CSSPoint point, Modifiers aModifiers, ScrollableLayerGuid aGuid, uint64_t aInputBlockId); NotifyAPZStateChange(ViewID aViewId, APZStateChange aChange, int aArg); + NotifyFlushComplete(); /** diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 50046f6d9c..a3ef2f074d 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -972,6 +972,9 @@ parent: returns (int32_t refCnt, int32_t dBRefCnt, int32_t sliceRefCnt, bool result); + // Use only for testing! + FlushPendingFileDeletions(); + /** * Tell the chrome process there is an creation of PBrowser. * return a system-wise unique Id. diff --git a/dom/ipc/ProcessHangMonitor.cpp b/dom/ipc/ProcessHangMonitor.cpp index 74c2e450e8..949443b34a 100644 --- a/dom/ipc/ProcessHangMonitor.cpp +++ b/dom/ipc/ProcessHangMonitor.cpp @@ -887,7 +887,7 @@ ProcessHangMonitor::ProcessHangMonitor() MOZ_COUNT_CTOR(ProcessHangMonitor); - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { nsCOMPtr obs = mozilla::services::GetObserverService(); obs->AddObserver(this, "xpcom-shutdown", false); } diff --git a/dom/ipc/ProcessPriorityManager.cpp b/dom/ipc/ProcessPriorityManager.cpp index 6ba01691f6..42833d4316 100644 --- a/dom/ipc/ProcessPriorityManager.cpp +++ b/dom/ipc/ProcessPriorityManager.cpp @@ -384,7 +384,7 @@ ProcessPriorityManagerImpl::StaticInit() } // The process priority manager is main-process only. - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { sInitialized = true; return; } @@ -427,7 +427,7 @@ ProcessPriorityManagerImpl::ProcessPriorityManagerImpl() , mBackgroundLRUPool(PROCESS_PRIORITY_BACKGROUND) , mBackgroundPerceivableLRUPool(PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); RegisterWakeLockObserver(this); } @@ -660,7 +660,7 @@ ParticularProcessPriorityManager::ParticularProcessPriorityManager( , mIsActivityOpener(false) , mFrozen(aFrozen) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); LOGP("Creating ParticularProcessPriorityManager."); } @@ -826,7 +826,7 @@ ParticularProcessPriorityManager::OnRemoteBrowserFrameShown(nsISupports* aSubjec TabParent* tp = TabParent::GetFrom(fl); NS_ENSURE_TRUE_VOID(tp); - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); if (tp->Manager() != mContentParent) { return; } @@ -850,7 +850,7 @@ ParticularProcessPriorityManager::OnTabParentDestroyed(nsISupports* aSubject) nsCOMPtr tp = do_QueryInterface(aSubject); NS_ENSURE_TRUE_VOID(tp); - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); if (TabParent::GetFrom(tp)->Manager() != mContentParent) { return; } @@ -873,7 +873,7 @@ ParticularProcessPriorityManager::OnFrameloaderVisibleChanged(nsISupports* aSubj return; } - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); if (tp->Manager() != mContentParent) { return; } @@ -1213,7 +1213,7 @@ NS_IMPL_ISUPPORTS(ProcessPriorityManagerChild, ProcessPriorityManagerChild::ProcessPriorityManagerChild() { - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { mCachedPriority = PROCESS_PRIORITY_MASTER; } else { mCachedPriority = PROCESS_PRIORITY_UNKNOWN; @@ -1225,7 +1225,7 @@ ProcessPriorityManagerChild::Init() { // The process priority should only be changed in child processes; don't even // bother listening for changes if we're in the main process. - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { nsCOMPtr os = services::GetObserverService(); NS_ENSURE_TRUE_VOID(os); os->AddObserver(this, "ipc:process-priority-changed", /* weak = */ false); diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index af81d1b6f2..21398d17cb 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -2157,6 +2157,13 @@ TabChild::RecvNotifyAPZStateChange(const ViewID& aViewId, return true; } +bool +TabChild::RecvNotifyFlushComplete() +{ + APZCCallbackHelper::NotifyFlushComplete(); + return true; +} + bool TabChild::RecvActivate() { diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index 85ea7d9412..cc88468e6d 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -345,6 +345,7 @@ public: virtual bool RecvNotifyAPZStateChange(const ViewID& aViewId, const APZStateChange& aChange, const int& aArg) override; + virtual bool RecvNotifyFlushComplete() override; virtual bool RecvActivate() override; virtual bool RecvDeactivate() override; virtual bool RecvMouseEvent(const nsString& aType, diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 8156d79577..7b63b65c6f 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -438,7 +438,7 @@ TabParent::Destroy() } mIsDestroyed = true; - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { Manager()->AsContentParent()->NotifyTabDestroying(this); } @@ -456,7 +456,7 @@ TabParent::Destroy() bool TabParent::Recv__delete__() { - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { Manager()->AsContentParent()->NotifyTabDestroyed(this, mMarkedDestroying); ContentParent::DeallocateTabId(mTabId, Manager()->AsContentParent()->ChildID()); @@ -1092,6 +1092,14 @@ TabParent::NotifyMouseScrollTestEvent(const ViewID& aScrollId, const nsString& a } } +void +TabParent::NotifyFlushComplete() +{ + if (!mIsDestroyed) { + unused << SendNotifyFlushComplete(); + } +} + void TabParent::Activate() { diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 5043caf804..c48bb6e9b6 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -281,6 +281,7 @@ public: APZStateChange aChange, int aArg); void NotifyMouseScrollTestEvent(const ViewID& aScrollId, const nsString& aEvent); + void NotifyFlushComplete(); void Activate(); void Deactivate(); diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index 23092082e1..b34762b058 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -1706,7 +1706,7 @@ MediaManager::NotifyRecordingStatusChange(nsPIDOMWindow* aWindow, // Forward recording events to parent process. // The events are gathered in chrome process and used for recording indicator - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { unused << dom::ContentChild::GetSingleton()->SendRecordingDeviceEvents(aMsg, requestURL, diff --git a/dom/media/gmp/GMPService.cpp b/dom/media/gmp/GMPService.cpp index 65982e951e..e93c2b10d3 100644 --- a/dom/media/gmp/GMPService.cpp +++ b/dom/media/gmp/GMPService.cpp @@ -104,7 +104,7 @@ private: MOZ_ASSERT(NS_IsMainThread()); if (!sSingletonService) { - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { nsRefPtr service = new GeckoMediaPluginServiceParent(); service->Init(); diff --git a/dom/media/gmp/GMPServiceChild.cpp b/dom/media/gmp/GMPServiceChild.cpp index 15f004d395..ef4e457926 100644 --- a/dom/media/gmp/GMPServiceChild.cpp +++ b/dom/media/gmp/GMPServiceChild.cpp @@ -25,7 +25,7 @@ namespace gmp { already_AddRefed GeckoMediaPluginServiceChild::GetSingleton() { - MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default); + MOZ_ASSERT(!XRE_IsParentProcess()); nsRefPtr service( GeckoMediaPluginService::GetGeckoMediaPluginService()); #ifdef DEBUG diff --git a/dom/media/gmp/GMPServiceParent.cpp b/dom/media/gmp/GMPServiceParent.cpp index e78560b8ca..20c738c690 100644 --- a/dom/media/gmp/GMPServiceParent.cpp +++ b/dom/media/gmp/GMPServiceParent.cpp @@ -55,7 +55,7 @@ static const uint32_t NodeIdSaltLength = 32; already_AddRefed GeckoMediaPluginServiceParent::GetSingleton() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); nsRefPtr service( GeckoMediaPluginServiceParent::GetGeckoMediaPluginService()); #ifdef DEBUG @@ -142,7 +142,7 @@ GeckoMediaPluginServiceParent::InitStorage() MOZ_ASSERT(NS_IsMainThread()); // GMP storage should be used in the chrome process only. - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return NS_OK; } diff --git a/dom/media/webspeech/synth/nsSpeechTask.cpp b/dom/media/webspeech/synth/nsSpeechTask.cpp index d94dd7c803..2ed52f7b9e 100644 --- a/dom/media/webspeech/synth/nsSpeechTask.cpp +++ b/dom/media/webspeech/synth/nsSpeechTask.cpp @@ -146,7 +146,7 @@ NS_IMETHODIMP nsSpeechTask::Setup(nsISpeechTaskCallback* aCallback, uint32_t aChannels, uint32_t aRate, uint8_t argc) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); LOG(PR_LOG_DEBUG, ("nsSpeechTask::Setup")); @@ -197,7 +197,7 @@ NS_IMETHODIMP nsSpeechTask::SendAudio(JS::Handle aData, JS::Handle aLandmarks, JSContext* aCx) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); if(NS_WARN_IF(!(mStream))) { return NS_ERROR_NOT_AVAILABLE; @@ -247,7 +247,7 @@ nsSpeechTask::SendAudio(JS::Handle aData, JS::Handle aLand NS_IMETHODIMP nsSpeechTask::SendAudioNative(int16_t* aData, uint32_t aDataLen) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); if(NS_WARN_IF(!(mStream))) { return NS_ERROR_NOT_AVAILABLE; @@ -508,7 +508,7 @@ nsSpeechTask::DispatchMarkImpl(const nsAString& aName, void nsSpeechTask::Pause() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); if (mCallback) { DebugOnly rv = mCallback->OnPause(); @@ -524,7 +524,7 @@ nsSpeechTask::Pause() void nsSpeechTask::Resume() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); if (mCallback) { DebugOnly rv = mCallback->OnResume(); @@ -540,7 +540,7 @@ nsSpeechTask::Resume() void nsSpeechTask::Cancel() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); LOG(PR_LOG_DEBUG, ("nsSpeechTask::Cancel")); diff --git a/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp b/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp index 50068cb8b2..338fda5b7b 100644 --- a/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp +++ b/dom/media/webspeech/synth/nsSynthVoiceRegistry.cpp @@ -101,7 +101,7 @@ NS_IMPL_ISUPPORTS(nsSynthVoiceRegistry, nsISynthVoiceRegistry) nsSynthVoiceRegistry::nsSynthVoiceRegistry() : mSpeechSynthChild(nullptr) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { mSpeechSynthChild = new SpeechSynthesisChild(); ContentChild::GetSingleton()->SendPSpeechSynthesisConstructor(mSpeechSynthChild); @@ -166,7 +166,7 @@ void nsSynthVoiceRegistry::Shutdown() { LOG(PR_LOG_DEBUG, ("[%s] nsSynthVoiceRegistry::Shutdown()", - (XRE_GetProcessType() == GeckoProcessType_Content) ? "Content" : "Default")); + (XRE_IsContentProcess()) ? "Content" : "Default")); gSynthVoiceRegistry = nullptr; } @@ -237,7 +237,7 @@ nsSynthVoiceRegistry::AddVoice(nsISpeechService* aService, NS_ConvertUTF16toUTF8(aLang).get(), aLocalService ? "true" : "false")); - if(NS_WARN_IF(XRE_GetProcessType() == GeckoProcessType_Content)) { + if(NS_WARN_IF(XRE_IsContentProcess())) { return NS_ERROR_NOT_AVAILABLE; } @@ -252,7 +252,7 @@ nsSynthVoiceRegistry::RemoveVoice(nsISpeechService* aService, LOG(PR_LOG_DEBUG, ("nsSynthVoiceRegistry::RemoveVoice uri='%s' (%s)", NS_ConvertUTF16toUTF8(aUri).get(), - (XRE_GetProcessType() == GeckoProcessType_Content) ? "child" : "parent")); + (XRE_IsContentProcess()) ? "child" : "parent")); bool found = false; VoiceData* retval = mUriVoiceMap.GetWeak(aUri, &found); @@ -297,7 +297,7 @@ nsSynthVoiceRegistry::SetDefaultVoice(const nsAString& aUri, mDefaultVoices.AppendElement(retval); } - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { nsTArray ssplist; GetAllSpeechSynthActors(ssplist); @@ -545,7 +545,7 @@ nsSynthVoiceRegistry::SpeakUtterance(SpeechSynthesisUtterance& aUtterance, } nsRefPtr task; - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { task = new SpeechTaskChild(&aUtterance); SpeechSynthesisRequestChild* actor = new SpeechSynthesisRequestChild(static_cast(task.get())); diff --git a/dom/media/webspeech/synth/pico/nsPicoService.cpp b/dom/media/webspeech/synth/pico/nsPicoService.cpp index 988cb3713c..76612b8a48 100644 --- a/dom/media/webspeech/synth/pico/nsPicoService.cpp +++ b/dom/media/webspeech/synth/pico/nsPicoService.cpp @@ -726,7 +726,7 @@ nsPicoService* nsPicoService::GetInstance() { MOZ_ASSERT(NS_IsMainThread()); - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { MOZ_ASSERT(false, "nsPicoService can only be started on main gecko process"); return nullptr; } diff --git a/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp b/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp index 60bd5208da..dd28bb47eb 100644 --- a/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp +++ b/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp @@ -328,7 +328,7 @@ nsFakeSynthServices* nsFakeSynthServices::GetInstance() { MOZ_ASSERT(NS_IsMainThread()); - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { MOZ_ASSERT(false, "nsFakeSynthServices can only be started on main gecko process"); return nullptr; } diff --git a/dom/mobileconnection/MobileConnectionArray.cpp b/dom/mobileconnection/MobileConnectionArray.cpp index 6377e6a54a..598232130c 100644 --- a/dom/mobileconnection/MobileConnectionArray.cpp +++ b/dom/mobileconnection/MobileConnectionArray.cpp @@ -101,7 +101,7 @@ NS_CreateMobileConnectionService() { nsCOMPtr service; - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { service = new mozilla::dom::mobileconnection::MobileConnectionIPCService(); } else { #if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL) diff --git a/dom/mobilemessage/MobileMessageManager.cpp b/dom/mobilemessage/MobileMessageManager.cpp index c8b97eb9f0..7b5b3e49fe 100644 --- a/dom/mobilemessage/MobileMessageManager.cpp +++ b/dom/mobilemessage/MobileMessageManager.cpp @@ -712,7 +712,7 @@ NS_CreateSmsService() { nsCOMPtr smsService; - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { smsService = SmsIPCService::GetSingleton(); } else { #ifdef MOZ_WIDGET_ANDROID @@ -729,7 +729,7 @@ already_AddRefed NS_CreateMobileMessageDatabaseService() { nsCOMPtr mobileMessageDBService; - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { mobileMessageDBService = SmsIPCService::GetSingleton(); } else { #ifdef MOZ_WIDGET_ANDROID @@ -748,7 +748,7 @@ NS_CreateMmsService() { nsCOMPtr mmsService; - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { mmsService = SmsIPCService::GetSingleton(); } else { #if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL) diff --git a/dom/network/UDPSocket.cpp b/dom/network/UDPSocket.cpp index 1b4a1f1943..8b538a3653 100644 --- a/dom/network/UDPSocket.cpp +++ b/dom/network/UDPSocket.cpp @@ -558,7 +558,7 @@ UDPSocket::Init(const nsString& aLocalAddress, } nsresult rv; - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { rv = mSocket->InitRemote(mSocket->mLocalAddress, localPort); } else { rv = mSocket->InitLocal(mSocket->mLocalAddress, localPort); diff --git a/dom/nfc/gonk/NfcService.cpp b/dom/nfc/gonk/NfcService.cpp index 706594c1c5..5b97e95304 100644 --- a/dom/nfc/gonk/NfcService.cpp +++ b/dom/nfc/gonk/NfcService.cpp @@ -309,7 +309,7 @@ NfcService::~NfcService() already_AddRefed NfcService::FactoryCreate() { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return nullptr; } diff --git a/dom/plugins/base/nsNPAPIPlugin.cpp b/dom/plugins/base/nsNPAPIPlugin.cpp index 6a4e9dcdc4..c24be0570d 100644 --- a/dom/plugins/base/nsNPAPIPlugin.cpp +++ b/dom/plugins/base/nsNPAPIPlugin.cpp @@ -247,7 +247,7 @@ nsNPAPIPlugin::PluginCrashed(const nsAString& pluginDumpID, bool nsNPAPIPlugin::RunPluginOOP(const nsPluginTag *aPluginTag) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { return true; } @@ -403,7 +403,7 @@ GetNewPluginLibrary(nsPluginTag *aPluginTag) return nullptr; } - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { return PluginModuleContentParent::LoadModule(aPluginTag->mId); } diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp index 8655ad03dd..f28654a5eb 100644 --- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -249,7 +249,7 @@ nsPluginHost::nsPluginHost() // good plugin list the first time it requests it. Normally we'd just // init this to 1, but due to the unique nature of our ctor we need to do // this manually. - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { IncrementChromeEpoch(); } @@ -1259,7 +1259,7 @@ nsresult nsPluginHost::EnsurePluginLoaded(nsPluginTag* aPluginTag) nsresult nsPluginHost::GetPluginForContentProcess(uint32_t aPluginId, nsNPAPIPlugin** aPlugin) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); // If plugins haven't been scanned yet, do so now LoadPlugins(); @@ -1317,7 +1317,7 @@ protected: void nsPluginHost::NotifyContentModuleDestroyed(uint32_t aPluginId) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); // This is called in response to a message from the plugin. Don't unload the // plugin until the message handler is off the stack. @@ -1823,7 +1823,7 @@ nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir, bool aCreatePluginList, bool *aPluginsChanged) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); NS_ENSURE_ARG_POINTER(aPluginsChanged); nsresult rv; @@ -2030,7 +2030,7 @@ nsresult nsPluginHost::ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum, bool aCreatePluginList, bool *aPluginsChanged) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); bool hasMore; while (NS_SUCCEEDED(dirEnum->HasMoreElements(&hasMore)) && hasMore) { @@ -2059,28 +2059,28 @@ nsresult nsPluginHost::ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum, void nsPluginHost::IncrementChromeEpoch() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); mPluginEpoch++; } uint32_t nsPluginHost::ChromeEpoch() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); return mPluginEpoch; } uint32_t nsPluginHost::ChromeEpochForContent() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content); + MOZ_ASSERT(XRE_IsContentProcess()); return mPluginEpoch; } void nsPluginHost::SetChromeEpochForContent(uint32_t aEpoch) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content); + MOZ_ASSERT(XRE_IsContentProcess()); mPluginEpoch = aEpoch; } @@ -2110,7 +2110,7 @@ WatchRegKey(uint32_t aRoot, nsCOMPtr& aKey) nsresult nsPluginHost::LoadPlugins() { #ifdef ANDROID - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { return NS_OK; } #endif @@ -2134,7 +2134,7 @@ nsresult nsPluginHost::LoadPlugins() // only if plugins have changed will we notify plugin-change observers if (pluginschanged) { - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { IncrementChromeEpoch(); } @@ -2150,7 +2150,7 @@ nsresult nsPluginHost::LoadPlugins() nsresult nsPluginHost::FindPluginsInContent(bool aCreatePluginList, bool* aPluginsChanged) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content); + MOZ_ASSERT(XRE_IsContentProcess()); dom::ContentChild* cp = dom::ContentChild::GetSingleton(); nsTArray plugins; @@ -2206,7 +2206,7 @@ nsresult nsPluginHost::FindPlugins(bool aCreatePluginList, bool * aPluginsChange *aPluginsChanged = false; - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { return FindPluginsInContent(aCreatePluginList, aPluginsChanged); } @@ -2373,7 +2373,7 @@ mozilla::plugins::FindPluginsForContent(uint32_t aPluginEpoch, nsTArray* aPlugins, uint32_t* aNewPluginEpoch) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); nsRefPtr host = nsPluginHost::GetInst(); host->FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch); @@ -2385,7 +2385,7 @@ nsPluginHost::FindPluginsForContent(uint32_t aPluginEpoch, nsTArray* aPlugins, uint32_t* aNewPluginEpoch) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); // Load plugins so that the epoch is correct. LoadPlugins(); @@ -2425,7 +2425,7 @@ nsPluginHost::FindPluginsForContent(uint32_t aPluginEpoch, void nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); ReadPluginInfo(); WritePluginInfo(); @@ -2511,7 +2511,7 @@ nsPluginHost::RegisterWithCategoryManager(nsCString &aMimeType, nsresult nsPluginHost::WritePluginInfo() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); nsresult rv = NS_OK; nsCOMPtr directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv)); @@ -2656,7 +2656,7 @@ nsPluginHost::WritePluginInfo() nsresult nsPluginHost::ReadPluginInfo() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); const long PLUGIN_REG_MIMETYPES_ARRAY_SIZE = 12; const long PLUGIN_REG_MAX_MIMETYPES = 1000; diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index ac53940f82..f9265eb14c 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -689,7 +689,7 @@ NS_IMETHODIMP nsPluginInstanceOwner::GetNetscapeWindow(void *value) // we only attempt to get the nearest window if this really is a "windowless" plugin so as not // to change any behaviour for the much more common windowed plugins, // though why this method would even be being called for a windowed plugin escapes me. - if (XRE_GetProcessType() != GeckoProcessType_Content && + if (!XRE_IsContentProcess() && mPluginWindow && mPluginWindow->type == NPWindowTypeDrawable) { // it turns out that flash also uses this window for determining focus, and is currently // unable to show a caret correctly if we return the enclosing window. Therefore for @@ -2884,7 +2884,7 @@ NS_IMETHODIMP nsPluginInstanceOwner::CreateWidget(void) parentWidget = nsContentUtils::WidgetForDocument(doc); #ifndef XP_MACOSX // If we're running in the content process, we need a remote widget created in chrome. - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { if (nsCOMPtr window = doc->GetWindow()) { if (nsCOMPtr topWindow = window->GetTop()) { dom::TabChild* tc = dom::TabChild::GetFrom(topWindow); @@ -3003,7 +3003,7 @@ void nsPluginInstanceOwner::FixUpPluginWindow(int32_t inPaintState) mPluginWindow->clipRect.bottom = mPluginWindow->clipRect.top; mPluginWindow->clipRect.right = mPluginWindow->clipRect.left; } - else if (XRE_GetProcessType() != GeckoProcessType_Default) + else if (!XRE_IsParentProcess()) { // For e10s we only support async windowless plugin. This means that // we're always going to allocate a full window for the plugin to draw @@ -3197,7 +3197,7 @@ nsPluginInstanceOwner::UpdateDocumentActiveState(bool aIsActive) // to be forwarded over after the active state is updated. If we // don't hide plugin widgets in hidden tabs, the native child window // in chrome will remain visible after a tab switch. - if (mWidget && XRE_GetProcessType() == GeckoProcessType_Content) { + if (mWidget && XRE_IsContentProcess()) { mWidget->Show(aIsActive); mWidget->Enable(aIsActive); } diff --git a/dom/plugins/base/nsPluginNativeWindowGtk.cpp b/dom/plugins/base/nsPluginNativeWindowGtk.cpp index 24f2385a40..32eb3f4abe 100644 --- a/dom/plugins/base/nsPluginNativeWindowGtk.cpp +++ b/dom/plugins/base/nsPluginNativeWindowGtk.cpp @@ -72,7 +72,7 @@ nsresult nsPluginNativeWindowGtk::CallSetWindow(nsRefPtr { if (aPluginInstance) { if (type == NPWindowTypeWindow && - XRE_GetProcessType() == GeckoProcessType_Content) { + XRE_IsContentProcess()) { // In this case, most of the initialization code here has already happened // in the chrome process. The window we have in content is the XID of the // socket widget we need to hand to plugins. diff --git a/dom/plugins/base/nsPluginNativeWindowWin.cpp b/dom/plugins/base/nsPluginNativeWindowWin.cpp index 500650a4a8..95c439a679 100644 --- a/dom/plugins/base/nsPluginNativeWindowWin.cpp +++ b/dom/plugins/base/nsPluginNativeWindowWin.cpp @@ -617,7 +617,7 @@ nsresult nsPluginNativeWindowWin::CallSetWindow(nsRefPtr // With e10s we execute in the content process and as such we don't // have access to native widgets. CallSetWindow and skip native widget // subclassing. - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { nsPluginNativeWindow::CallSetWindow(aPluginInstance); return NS_OK; } diff --git a/dom/plugins/base/nsPluginTags.cpp b/dom/plugins/base/nsPluginTags.cpp index 0878269a7e..b31fcc8e06 100644 --- a/dom/plugins/base/nsPluginTags.cpp +++ b/dom/plugins/base/nsPluginTags.cpp @@ -651,7 +651,7 @@ nsPluginTag::GetBlocklistState(uint32_t *aResult) return NS_OK; } - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { *aResult = nsIBlocklistService::STATE_BLOCKED; dom::ContentChild* cp = dom::ContentChild::GetSingleton(); if (!cp->SendGetBlocklistState(mId, aResult)) { diff --git a/dom/plugins/ipc/PluginInstanceParent.cpp b/dom/plugins/ipc/PluginInstanceParent.cpp index 142c0190b6..8f0bfad3bf 100644 --- a/dom/plugins/ipc/PluginInstanceParent.cpp +++ b/dom/plugins/ipc/PluginInstanceParent.cpp @@ -1843,7 +1843,7 @@ PluginInstanceParent::SubclassPluginWindow(HWND aWnd) return; } - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { if (!aWnd) { NS_WARNING("PluginInstanceParent::SubclassPluginWindow unexpected null window"); return; @@ -1873,7 +1873,7 @@ PluginInstanceParent::SubclassPluginWindow(HWND aWnd) void PluginInstanceParent::UnsubclassPluginWindow() { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { if (mPluginHWND) { // Remove 'this' from the plugin list safely nsAutoPtr tmp; diff --git a/dom/plugins/ipc/PluginModuleParent.cpp b/dom/plugins/ipc/PluginModuleParent.cpp index 5f86f66d2b..b73f0308a5 100755 --- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -338,7 +338,7 @@ bool PluginModuleMapping::sIsLoadModuleOnStack = false; void mozilla::plugins::TerminatePlugin(uint32_t aPluginId, const nsString& aBrowserDumpId) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); nsRefPtr host = nsPluginHost::GetInst(); nsPluginTag* pluginTag = host->PluginWithId(aPluginId); @@ -356,7 +356,7 @@ PluginModuleContentParent::LoadModule(uint32_t aPluginId) PluginModuleMapping::NotifyLoadingModule loadingModule; nsAutoPtr mapping(new PluginModuleMapping(aPluginId)); - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content); + MOZ_ASSERT(XRE_IsContentProcess()); /* * We send a LoadPlugin message to the chrome process using an intr diff --git a/dom/promise/PromiseDebugging.cpp b/dom/promise/PromiseDebugging.cpp index b1f5aef030..b1ce5bba64 100644 --- a/dom/promise/PromiseDebugging.cpp +++ b/dom/promise/PromiseDebugging.cpp @@ -96,7 +96,7 @@ PromiseDebugging::Init() // Generate a prefix for identifiers: "PromiseDebugging.$processid." sIDPrefix = NS_LITERAL_STRING("PromiseDebugging."); - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { sIDPrefix.AppendInt(ContentChild::GetSingleton()->GetID()); sIDPrefix.Append('.'); } else { diff --git a/dom/quota/ArrayCluster.h b/dom/quota/ArrayCluster.h deleted file mode 100644 index fa2994bc59..0000000000 --- a/dom/quota/ArrayCluster.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_quota_arraycluster_h__ -#define mozilla_dom_quota_arraycluster_h__ - -#include "mozilla/dom/quota/QuotaCommon.h" - -#include "Client.h" - -BEGIN_QUOTA_NAMESPACE - -template -class ArrayCluster -{ -public: - ArrayCluster() - { - mArrays.AppendElements(Length); - } - - nsTArray& - ArrayAt(uint32_t aIndex) - { - MOZ_ASSERT(aIndex < Length, "Bad index!"); - return mArrays[aIndex]; - } - - nsTArray& - operator[](uint32_t aIndex) - { - return ArrayAt(aIndex); - } - - bool - IsEmpty() - { - for (uint32_t index = 0; index < Length; index++) { - if (!mArrays[index].IsEmpty()) { - return false; - } - } - return true; - } - - template - void - AppendElementsTo(uint32_t aIndex, nsTArray& aArray) - { - NS_ASSERTION(aIndex < Length, "Bad index!"); - aArray.AppendElements(mArrays[aIndex]); - } - - template - void - AppendElementsTo(uint32_t aIndex, ArrayCluster& aArrayCluster) - { - NS_ASSERTION(aIndex < Length, "Bad index!"); - aArrayCluster[aIndex].AppendElements(mArrays[aIndex]); - } - - template - void - AppendElementsTo(nsTArray& aArray) - { - for (uint32_t index = 0; index < Length; index++) { - aArray.AppendElements(mArrays[index]); - } - } - - template - void - AppendElementsTo(ArrayCluster& aArrayCluster) - { - for (uint32_t index = 0; index < Length; index++) { - aArrayCluster[index].AppendElements(mArrays[index]); - } - } - - template - void - SwapElements(ArrayCluster& aArrayCluster) - { - for (uint32_t index = 0; index < Length; index++) { - mArrays[index].SwapElements(aArrayCluster.mArrays[index]); - } - } - - void - Clear() - { - for (uint32_t index = 0; index < Length; index++) { - mArrays[index].Clear(); - } - } - -private: - nsAutoTArray, Length> mArrays; -}; - -END_QUOTA_NAMESPACE - -#endif // mozilla_dom_quota_arraycluster_h__ diff --git a/dom/quota/Client.h b/dom/quota/Client.h index 5ee5dc9e45..9c929dd1f5 100644 --- a/dom/quota/Client.h +++ b/dom/quota/Client.h @@ -9,9 +9,10 @@ #include "mozilla/dom/quota/QuotaCommon.h" +#include "mozilla/dom/ipc/IdType.h" + #include "PersistenceType.h" -class nsIOfflineStorage; class nsIRunnable; #define IDB_DIRECTORY_NAME "idb" @@ -112,8 +113,13 @@ public: // Methods which are called on the main thred. virtual void - WaitForStoragesToComplete(nsTArray& aStorages, - nsIRunnable* aCallback) = 0; + AbortOperations(const nsACString& aOrigin) = 0; + + virtual void + AbortOperationsForProcess(ContentParentId aContentParentId) = 0; + + virtual void + PerformIdleMaintenance() = 0; virtual void ShutdownWorkThreads() = 0; diff --git a/dom/quota/OriginCollection.h b/dom/quota/OriginCollection.h deleted file mode 100644 index 9b0482961f..0000000000 --- a/dom/quota/OriginCollection.h +++ /dev/null @@ -1,64 +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_quota_origincollection_h__ -#define mozilla_dom_quota_origincollection_h__ - -#include "mozilla/dom/quota/QuotaCommon.h" - -#include "nsHashKeys.h" -#include "nsTHashtable.h" - -#include "Utilities.h" - -BEGIN_QUOTA_NAMESPACE - -class OriginCollection -{ -public: - bool - ContainsPattern(const nsACString& aPattern) - { - return mPatterns.Contains(aPattern); - } - - void - AddPattern(const nsACString& aPattern) - { - MOZ_ASSERT(!mOrigins.Count()); - if (!ContainsPattern(aPattern)) { - mPatterns.AppendElement(aPattern); - } - } - - bool - ContainsOrigin(const nsACString& aOrigin) - { - for (uint32_t index = 0; index < mPatterns.Length(); index++) { - if (PatternMatchesOrigin(mPatterns[index], aOrigin)) { - return true; - } - } - - return mOrigins.GetEntry(aOrigin); - } - - void - AddOrigin(const nsACString& aOrigin) - { - if (!ContainsOrigin(aOrigin)) { - mOrigins.PutEntry(aOrigin); - } - } - -private: - nsTArray mPatterns; - nsTHashtable mOrigins; -}; - -END_QUOTA_NAMESPACE - -#endif // mozilla_dom_quota_origincollection_h__ diff --git a/dom/quota/OriginOrPatternString.h b/dom/quota/OriginScope.h similarity index 54% rename from dom/quota/OriginOrPatternString.h rename to dom/quota/OriginScope.h index 6bea77bf4e..77e77599f8 100644 --- a/dom/quota/OriginOrPatternString.h +++ b/dom/quota/OriginScope.h @@ -11,61 +11,67 @@ BEGIN_QUOTA_NAMESPACE -class OriginOrPatternString : public nsCString +class OriginScope : public nsCString { public: - static OriginOrPatternString + enum Type + { + eOrigin, + ePattern, + eNull + }; + + static OriginScope FromOrigin(const nsACString& aOrigin) { - return OriginOrPatternString(aOrigin, true); + return OriginScope(aOrigin, eOrigin); } - static OriginOrPatternString + static OriginScope FromPattern(const nsACString& aPattern) { - return OriginOrPatternString(aPattern, false); + return OriginScope(aPattern, ePattern); } - static OriginOrPatternString + static OriginScope FromNull() { - return OriginOrPatternString(); + return OriginScope(NullCString(), eNull); } bool IsOrigin() const { - return mIsOrigin; + return mType == eOrigin; } bool IsPattern() const { - return mIsPattern; + return mType == ePattern; } bool IsNull() const { - return mIsNull; + return mType == eNull; + } + + Type + GetType() const + { + return mType; } private: - OriginOrPatternString(const nsACString& aOriginOrPattern, bool aIsOrigin) - : nsCString(aOriginOrPattern), - mIsOrigin(aIsOrigin), mIsPattern(!aIsOrigin), mIsNull(false) - { } - - OriginOrPatternString() - : mIsOrigin(false), mIsPattern(false), mIsNull(true) + OriginScope(const nsACString& aString, Type aType) + : nsCString(aString), mType(aType) { } bool - operator==(const OriginOrPatternString& aOther) = delete; + operator==(const OriginScope& aOther) = delete; - bool mIsOrigin; - bool mIsPattern; - bool mIsNull; + const Type mType; }; END_QUOTA_NAMESPACE diff --git a/dom/quota/PersistenceType.h b/dom/quota/PersistenceType.h index 7357a2f674..3e749e16ce 100644 --- a/dom/quota/PersistenceType.h +++ b/dom/quota/PersistenceType.h @@ -23,6 +23,12 @@ enum PersistenceType PERSISTENCE_TYPE_INVALID }; +static const PersistenceType kAllPersistenceTypes[] = { + PERSISTENCE_TYPE_PERSISTENT, + PERSISTENCE_TYPE_TEMPORARY, + PERSISTENCE_TYPE_DEFAULT +}; + inline void PersistenceTypeToText(PersistenceType aPersistenceType, nsACString& aText) { diff --git a/dom/quota/QuotaManager.cpp b/dom/quota/QuotaManager.cpp index d26ebc5559..8daa071467 100644 --- a/dom/quota/QuotaManager.cpp +++ b/dom/quota/QuotaManager.cpp @@ -10,8 +10,8 @@ #include "nsIBinaryInputStream.h" #include "nsIBinaryOutputStream.h" #include "nsIFile.h" +#include "nsIIdleService.h" #include "nsIObserverService.h" -#include "nsIOfflineStorage.h" #include "nsIPermissionManager.h" #include "nsIPrincipal.h" #include "nsIQuotaRequest.h" @@ -28,13 +28,16 @@ #include "GeckoProfiler.h" #include "mozilla/Atomics.h" #include "mozilla/CondVar.h" +#include "mozilla/dom/PContent.h" #include "mozilla/dom/asmjscache/AsmJSCache.h" #include "mozilla/dom/cache/QuotaClient.h" #include "mozilla/dom/indexedDB/ActorsParent.h" +#include "mozilla/IntegerRange.h" #include "mozilla/Mutex.h" #include "mozilla/LazyIdleThread.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" +#include "mozilla/TypeTraits.h" #include "nsAppDirectoryServiceDefs.h" #include "nsComponentManagerUtils.h" #include "nsAboutProtocolUtils.h" @@ -50,10 +53,8 @@ #include "nsXULAppAPI.h" #include "xpcpublic.h" -#include "OriginCollection.h" -#include "OriginOrPatternString.h" +#include "OriginScope.h" #include "QuotaObject.h" -#include "StorageMatcher.h" #include "UsageInfo.h" #include "Utilities.h" @@ -86,9 +87,15 @@ #define MB * 1024ULL KB #define GB * 1024ULL MB -USING_QUOTA_NAMESPACE -using namespace mozilla; -using namespace mozilla::dom; +namespace mozilla { +namespace dom { +namespace quota { + +namespace { + +/******************************************************************************* + * Constants + ******************************************************************************/ static_assert( static_cast(StorageType::Persistent) == @@ -105,8 +112,6 @@ static_assert( static_cast(PERSISTENCE_TYPE_DEFAULT), "Enum values should match."); -namespace { - const char kChromeOrigin[] = "chrome"; const char kAboutHomeOrigin[] = "moz-safe-about:home"; const char kIndexedDBOriginPrefix[] = "indexeddb://"; @@ -123,339 +128,718 @@ enum AppId { kUnknownAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID }; +/****************************************************************************** + * Quota manager class declarations + ******************************************************************************/ + } // namespace -BEGIN_QUOTA_NAMESPACE - -// A struct that contains the information corresponding to a pending or -// running operation that requires synchronization (e.g. opening a db, -// clearing dbs for an origin, etc). -struct SynchronizedOp +class DirectoryLockImpl final + : public DirectoryLock { - SynchronizedOp(const OriginOrPatternString& aOriginOrPattern, - Nullable aPersistenceType, - const nsACString& aId); + nsRefPtr mQuotaManager; - ~SynchronizedOp(); + const Nullable mPersistenceType; + const nsCString mGroup; + const OriginScope mOriginScope; + const Nullable mIsApp; + const Nullable mClientType; + nsRefPtr mOpenListener; + + nsTArray mBlocking; + nsTArray mBlockedOn; + + const bool mExclusive; + + // Internal quota manager operations use this flag to prevent directory lock + // registraction/unregistration from updating origin access time, etc. + const bool mInternal; + + bool mInvalidated; + +public: + DirectoryLockImpl(QuotaManager* aQuotaManager, + Nullable aPersistenceType, + const nsACString& aGroup, + const OriginScope& aOriginScope, + Nullable aIsApp, + Nullable aClientType, + bool aExclusive, + bool aInternal, + OpenDirectoryListener* aOpenListener); + + static bool + MatchOriginScopes(const OriginScope& aOriginScope1, + const OriginScope& aOriginScope2); + + const Nullable& + GetPersistenceType() const + { + return mPersistenceType; + } + + const nsACString& + GetGroup() const + { + return mGroup; + } + + const OriginScope& + GetOriginScope() const + { + return mOriginScope; + } + + const Nullable& + GetIsApp() const + { + return mIsApp; + } + + const Nullable& + GetClientType() const + { + return mClientType; + } - // Test whether this SynchronizedOp needs to wait for the given op. bool - MustWaitFor(const SynchronizedOp& aOp); + IsInternal() const + { + return mInternal; + } + + bool + ShouldUpdateLockTable() + { + return !mInternal && + mPersistenceType.Value() != PERSISTENCE_TYPE_PERSISTENT; + } + + // Test whether this DirectoryLock needs to wait for the given lock. + bool + MustWaitFor(const DirectoryLockImpl& aLock); void - DelayRunnable(nsIRunnable* aRunnable); + AddBlockingLock(DirectoryLockImpl* aLock) + { + MOZ_ASSERT(NS_IsMainThread()); + + mBlocking.AppendElement(aLock); + } + + const nsTArray& + GetBlockedOnLocks() + { + return mBlockedOn; + } void - DispatchDelayedRunnables(); + AddBlockedOnLock(DirectoryLockImpl* aLock) + { + MOZ_ASSERT(NS_IsMainThread()); - const OriginOrPatternString mOriginOrPattern; - Nullable mPersistenceType; - nsCString mId; - nsCOMPtr mRunnable; - nsTArray > mDelayedRunnables; + mBlockedOn.AppendElement(aLock); + } + + void + MaybeUnblock(DirectoryLockImpl* aLock) + { + MOZ_ASSERT(NS_IsMainThread()); + + mBlockedOn.RemoveElement(aLock); + if (mBlockedOn.IsEmpty()) { + NotifyOpenListener(); + } + } + + void + NotifyOpenListener(); + + void + Invalidate() + { + MOZ_ASSERT(NS_IsMainThread()); + + mInvalidated = true; + } + +private: + ~DirectoryLockImpl(); + + NS_DECL_ISUPPORTS }; -class CollectOriginsHelper final : public nsRunnable +namespace { + +/******************************************************************************* + * Local class declarations + ******************************************************************************/ + +} // namespace + +class OriginInfo final +{ + friend class GroupInfo; + friend class QuotaManager; + friend class QuotaObject; + +public: + OriginInfo(GroupInfo* aGroupInfo, const nsACString& aOrigin, bool aIsApp, + uint64_t aUsage, int64_t aAccessTime) + : mGroupInfo(aGroupInfo), mOrigin(aOrigin), mUsage(aUsage), + mAccessTime(aAccessTime), mIsApp(aIsApp) + { + MOZ_COUNT_CTOR(OriginInfo); + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OriginInfo) + + int64_t + AccessTime() const + { + return mAccessTime; + } + +private: + // Private destructor, to discourage deletion outside of Release(): + ~OriginInfo() + { + MOZ_COUNT_DTOR(OriginInfo); + + MOZ_ASSERT(!mQuotaObjects.Count()); + } + + void + LockedDecreaseUsage(int64_t aSize); + + void + LockedUpdateAccessTime(int64_t aAccessTime) + { + AssertCurrentThreadOwnsQuotaMutex(); + + mAccessTime = aAccessTime; + } + + nsDataHashtable mQuotaObjects; + + GroupInfo* mGroupInfo; + const nsCString mOrigin; + uint64_t mUsage; + int64_t mAccessTime; + const bool mIsApp; +}; + +class OriginInfoLRUComparator { public: - CollectOriginsHelper(mozilla::Mutex& aMutex, uint64_t aMinSizeToBeFreed); + bool + Equals(const OriginInfo* a, const OriginInfo* b) const + { + return + a && b ? a->AccessTime() == b->AccessTime() : !a && !b ? true : false; + } - NS_IMETHOD - Run(); + bool + LessThan(const OriginInfo* a, const OriginInfo* b) const + { + return a && b ? a->AccessTime() < b->AccessTime() : b ? true : false; + } +}; + +class GroupInfo final +{ + friend class GroupInfoPair; + friend class OriginInfo; + friend class QuotaManager; + friend class QuotaObject; + +public: + GroupInfo(GroupInfoPair* aGroupInfoPair, PersistenceType aPersistenceType, + const nsACString& aGroup) + : mGroupInfoPair(aGroupInfoPair), mPersistenceType(aPersistenceType), + mGroup(aGroup), mUsage(0) + { + MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT); + + MOZ_COUNT_CTOR(GroupInfo); + } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GroupInfo) + +private: + // Private destructor, to discourage deletion outside of Release(): + ~GroupInfo() + { + MOZ_COUNT_DTOR(GroupInfo); + } + + already_AddRefed + LockedGetOriginInfo(const nsACString& aOrigin); + + void + LockedAddOriginInfo(OriginInfo* aOriginInfo); + + void + LockedRemoveOriginInfo(const nsACString& aOrigin); + + void + LockedRemoveOriginInfos(); + + bool + LockedHasOriginInfos() + { + AssertCurrentThreadOwnsQuotaMutex(); + + return !mOriginInfos.IsEmpty(); + } + + nsTArray > mOriginInfos; + + GroupInfoPair* mGroupInfoPair; + PersistenceType mPersistenceType; + nsCString mGroup; + uint64_t mUsage; +}; + +class GroupInfoPair +{ + friend class QuotaManager; + friend class QuotaObject; + +public: + GroupInfoPair() + { + MOZ_COUNT_CTOR(GroupInfoPair); + } + + ~GroupInfoPair() + { + MOZ_COUNT_DTOR(GroupInfoPair); + } + +private: + already_AddRefed + LockedGetGroupInfo(PersistenceType aPersistenceType) + { + AssertCurrentThreadOwnsQuotaMutex(); + MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT); + + nsRefPtr groupInfo = + GetGroupInfoForPersistenceType(aPersistenceType); + return groupInfo.forget(); + } + + void + LockedSetGroupInfo(PersistenceType aPersistenceType, GroupInfo* aGroupInfo) + { + AssertCurrentThreadOwnsQuotaMutex(); + MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT); + + nsRefPtr& groupInfo = + GetGroupInfoForPersistenceType(aPersistenceType); + groupInfo = aGroupInfo; + } + + void + LockedClearGroupInfo(PersistenceType aPersistenceType) + { + AssertCurrentThreadOwnsQuotaMutex(); + MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT); + + nsRefPtr& groupInfo = + GetGroupInfoForPersistenceType(aPersistenceType); + groupInfo = nullptr; + } + + bool + LockedHasGroupInfos() + { + AssertCurrentThreadOwnsQuotaMutex(); + + return mTemporaryStorageGroupInfo || mDefaultStorageGroupInfo; + } + + nsRefPtr& + GetGroupInfoForPersistenceType(PersistenceType aPersistenceType); + + nsRefPtr mTemporaryStorageGroupInfo; + nsRefPtr mDefaultStorageGroupInfo; +}; + +namespace { + +class CollectOriginsHelper final + : public nsRunnable +{ + uint64_t mMinSizeToBeFreed; + + Mutex& mMutex; + CondVar mCondVar; + + // The members below are protected by mMutex. + nsTArray> mLocks; + uint64_t mSizeToBeFreed; + bool mWaiting; + +public: + CollectOriginsHelper(mozilla::Mutex& aMutex, + uint64_t aMinSizeToBeFreed); // Blocks the current thread until origins are collected on the main thread. // The returned value contains an aggregate size of those origins. int64_t - BlockAndReturnOriginsForEviction(nsTArray& aOriginInfos); + BlockAndReturnOriginsForEviction( + nsTArray>& aLocks); private: ~CollectOriginsHelper() { } - uint64_t mMinSizeToBeFreed; - - mozilla::Mutex& mMutex; - mozilla::CondVar mCondVar; - - // The members below are protected by mMutex. - nsTArray mOriginInfos; - uint64_t mSizeToBeFreed; - bool mWaiting; + NS_IMETHOD + Run(); }; -// Responsible for clearing the storage files for a particular origin on the -// IO thread. Created when nsIQuotaManager::ClearStoragesForURI is called. -// Runs three times, first on the main thread, next on the IO thread, and then -// finally again on the main thread. While on the IO thread the runnable will -// actually remove the origin's storage files and the directory that contains -// them before dispatching itself back to the main thread. When back on the main -// thread the runnable will notify the QuotaManager that the job has been -// completed. -class OriginClearRunnable final : public nsRunnable +class OriginOperationBase + : public nsRunnable { - enum CallbackState { +protected: + enum State { // Not yet run. - Pending = 0, + State_Initial, - // Running on the main thread in the callback for OpenAllowed. - OpenAllowed, + // Running on the main thread in the listener for OpenDirectory. + State_DirectoryOpenPending, // Running on the IO thread. - IO, + State_DirectoryWorkOpen, // Running on the main thread after all work is done. - Complete + State_UnblockingOpen, + + State_Complete }; -public: - NS_DECL_ISUPPORTS_INHERITED + State mState; + nsresult mResultCode; - OriginClearRunnable(const OriginOrPatternString& aOriginOrPattern, - Nullable aPersistenceType) - : mOriginOrPattern(aOriginOrPattern), - mPersistenceType(aPersistenceType), - mCallbackState(Pending) +protected: + OriginOperationBase() + : mState(State_Initial) + , mResultCode(NS_OK) { } - NS_IMETHOD - Run() override; + // Reference counted. + virtual ~OriginOperationBase() + { + MOZ_ASSERT(mState == State_Complete); + } void AdvanceState() { - switch (mCallbackState) { - case Pending: - mCallbackState = OpenAllowed; + switch (mState) { + case State_Initial: + mState = State_DirectoryOpenPending; return; - case OpenAllowed: - mCallbackState = IO; + case State_DirectoryOpenPending: + mState = State_DirectoryWorkOpen; return; - case IO: - mCallbackState = Complete; + case State_DirectoryWorkOpen: + mState = State_UnblockingOpen; + return; + case State_UnblockingOpen: + mState = State_Complete; return; default: - NS_NOTREACHED("Can't advance past Complete!"); + MOZ_CRASH("Bad state!"); } } - void - DeleteFiles(QuotaManager* aQuotaManager, - PersistenceType aPersistenceType); - -private: - ~OriginClearRunnable() {} - - OriginOrPatternString mOriginOrPattern; - Nullable mPersistenceType; - CallbackState mCallbackState; -}; - -// Responsible for calculating the amount of space taken up by storages of a -// certain origin. Created when nsIQuotaManager::GetUsageForURI is called. -// May be canceled with nsIQuotaRequest::Cancel. Runs three times, first -// on the main thread, next on the IO thread, and then finally again on the main -// thread. While on the IO thread the runnable will calculate the size of all -// files in the origin's directory before dispatching itself back to the main -// thread. When on the main thread the runnable will call the callback and then -// notify the QuotaManager that the job has been completed. -class AsyncUsageRunnable final : public UsageInfo, - public nsRunnable, - public nsIQuotaRequest -{ - enum CallbackState { - // Not yet run. - Pending = 0, - - // Running on the main thread in the callback for OpenAllowed. - OpenAllowed, - - // Running on the IO thread. - IO, - - // Running on the main thread after all work is done. - Complete, - - // Running on the main thread after skipping the work - Shortcut - }; - -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIQUOTAREQUEST - - AsyncUsageRunnable(uint32_t aAppId, - bool aInMozBrowserOnly, - const nsACString& aGroup, - const OriginOrPatternString& aOrigin, - bool aIsApp, - nsIURI* aURI, - nsIUsageCallback* aCallback); - - NS_IMETHOD - Run() override; - - void - AdvanceState() - { - switch (mCallbackState) { - case Pending: - mCallbackState = OpenAllowed; - return; - case OpenAllowed: - mCallbackState = IO; - return; - case IO: - mCallbackState = Complete; - return; - default: - NS_NOTREACHED("Can't advance past Complete!"); - } - } - - nsresult - TakeShortcut(); - -private: - ~AsyncUsageRunnable() {} - - // Run calls the RunInternal method and makes sure that we always dispatch - // to the main thread in case of an error. - inline nsresult - RunInternal(); - - nsresult - AddToUsage(QuotaManager* aQuotaManager, - PersistenceType aPersistenceType); - - nsCOMPtr mURI; - nsCOMPtr mCallback; - uint32_t mAppId; - nsCString mGroup; - OriginOrPatternString mOrigin; - CallbackState mCallbackState; - bool mInMozBrowserOnly; - const bool mIsApp; -}; - -class ResetOrClearRunnable final : public nsRunnable -{ - enum CallbackState { - // Not yet run. - Pending = 0, - - // Running on the main thread in the callback for OpenAllowed. - OpenAllowed, - - // Running on the IO thread. - IO, - - // Running on the main thread after all work is done. - Complete - }; - -public: - NS_DECL_ISUPPORTS_INHERITED - - explicit ResetOrClearRunnable(bool aClear) - : mCallbackState(Pending), - mClear(aClear) - { } - - NS_IMETHOD - Run() override; - - void - AdvanceState() - { - switch (mCallbackState) { - case Pending: - mCallbackState = OpenAllowed; - return; - case OpenAllowed: - mCallbackState = IO; - return; - case IO: - mCallbackState = Complete; - return; - default: - NS_NOTREACHED("Can't advance past Complete!"); - } - } - - void - DeleteFiles(QuotaManager* aQuotaManager); - -private: - ~ResetOrClearRunnable() {} - - CallbackState mCallbackState; - bool mClear; -}; - -// Responsible for finalizing eviction of certian origins (storage files have -// been already cleared, we just need to release IO thread only objects and -// allow next synchronized ops for evicted origins). Created when -// QuotaManager::FinalizeOriginEviction is called. Runs three times, first -// on the main thread, next on the IO thread, and then finally again on the main -// thread. While on the IO thread the runnable will release IO thread only -// objects before dispatching itself back to the main thread. When back on the -// main thread the runnable will call QuotaManager::AllowNextSynchronizedOp. -// The runnable can also run in a shortened mode (runs only twice). -class FinalizeOriginEvictionRunnable final : public nsRunnable -{ - enum CallbackState { - // Not yet run. - Pending = 0, - - // Running on the main thread in the callback for OpenAllowed. - OpenAllowed, - - // Running on the IO thread. - IO, - - // Running on the main thread after IO work is done. - Complete - }; - -public: - explicit FinalizeOriginEvictionRunnable(nsTArray& aOrigins) - : mCallbackState(Pending) - { - mOrigins.SwapElements(aOrigins); - } - NS_IMETHOD Run(); + virtual nsresult + Open() = 0; + + nsresult + DirectoryOpen(); + + virtual nsresult + DoDirectoryWork(QuotaManager* aQuotaManager) = 0; + void - AdvanceState() - { - switch (mCallbackState) { - case Pending: - mCallbackState = OpenAllowed; - return; - case OpenAllowed: - mCallbackState = IO; - return; - case IO: - mCallbackState = Complete; - return; - default: - MOZ_ASSERT_UNREACHABLE("Can't advance past Complete!"); - } - } + Finish(nsresult aResult); - nsresult - Dispatch(); - - nsresult - RunImmediately(); + virtual void + UnblockOpen() = 0; private: - CallbackState mCallbackState; - nsTArray mOrigins; + nsresult + DirectoryWork(); }; +class NormalOriginOperationBase + : public OriginOperationBase + , public OpenDirectoryListener +{ + nsRefPtr mDirectoryLock; + +protected: + Nullable mPersistenceType; + OriginScope mOriginScope; + const bool mExclusive; + +public: + void + RunImmediately() + { + MOZ_ASSERT(mState == State_Initial); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(this->Run())); + } + +protected: + NormalOriginOperationBase(Nullable aPersistenceType, + const OriginScope& aOriginScope, + bool aExclusive) + : mPersistenceType(aPersistenceType) + , mOriginScope(aOriginScope) + , mExclusive(aExclusive) + { + MOZ_ASSERT(NS_IsMainThread()); + } + + ~NormalOriginOperationBase() + { } + +private: + NS_DECL_ISUPPORTS_INHERITED + + virtual nsresult + Open() override; + + virtual void + UnblockOpen() override; + + // OpenDirectoryListener overrides. + virtual void + DirectoryLockAcquired(DirectoryLock* aLock) override; + + virtual void + DirectoryLockFailed() override; + + // Used to send results before unblocking open. + virtual void + SendResults() = 0; +}; + +class SaveOriginAccessTimeOp + : public NormalOriginOperationBase +{ + int64_t mTimestamp; + +public: + SaveOriginAccessTimeOp(PersistenceType aPersistenceType, + const nsACString& aOrigin, + int64_t aTimestamp) + : NormalOriginOperationBase(Nullable(aPersistenceType), + OriginScope::FromOrigin(aOrigin), + /* aExclusive */ false) + , mTimestamp(aTimestamp) + { + MOZ_ASSERT(NS_IsMainThread()); + } + +private: + ~SaveOriginAccessTimeOp() + { } + + virtual nsresult + DoDirectoryWork(QuotaManager* aQuotaManager) override; + + virtual void + SendResults() override + { } +}; + +class GetUsageOp + : public NormalOriginOperationBase + , public nsIQuotaRequest +{ + UsageInfo mUsageInfo; + + const nsCString mGroup; + nsCOMPtr mURI; + nsCOMPtr mCallback; + const uint32_t mAppId; + const bool mIsApp; + const bool mInMozBrowserOnly; + +public: + GetUsageOp(const nsACString& aGroup, + const nsACString& aOrigin, + bool aIsApp, + nsIURI* aURI, + nsIUsageCallback* aCallback, + uint32_t aAppId, + bool aInMozBrowserOnly); + +private: + ~GetUsageOp() + { } + + nsresult + AddToUsage(QuotaManager* aQuotaManager, + PersistenceType aPersistenceType); + + virtual nsresult + DoDirectoryWork(QuotaManager* aQuotaManager) override; + + virtual void + SendResults() override; + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIQUOTAREQUEST +}; + +class ResetOrClearOp + : public NormalOriginOperationBase +{ + bool mClear; + +public: + explicit ResetOrClearOp(bool aClear) + : NormalOriginOperationBase(Nullable(), + OriginScope::FromNull(), + /* aExclusive */ true) + , mClear(aClear) + { + MOZ_ASSERT(NS_IsMainThread()); + } + +private: + ~ResetOrClearOp() + { } + + void + DeleteFiles(QuotaManager* aQuotaManager); + + virtual nsresult + DoDirectoryWork(QuotaManager* aQuotaManager) override; + + virtual void + SendResults() override + { } +}; + +class OriginClearOp + : public NormalOriginOperationBase +{ +public: + OriginClearOp(Nullable aPersistenceType, + const OriginScope& aOriginScope) + : NormalOriginOperationBase(aPersistenceType, + aOriginScope, + /* aExclusive */ true) + { + MOZ_ASSERT(NS_IsMainThread()); + } + +private: + ~OriginClearOp() + { } + + void + DeleteFiles(QuotaManager* aQuotaManager, + PersistenceType aPersistenceType); + + virtual nsresult + DoDirectoryWork(QuotaManager* aQuotaManager) override; + + virtual void + SendResults() override + { } +}; + +class FinalizeOriginEvictionOp + : public OriginOperationBase +{ + nsTArray> mLocks; + +public: + explicit FinalizeOriginEvictionOp( + nsTArray>& aLocks) + { + MOZ_ASSERT(!NS_IsMainThread()); + + mLocks.SwapElements(aLocks); + } + + void + Dispatch(); + + void + RunOnIOThreadImmediately(); + +private: + ~FinalizeOriginEvictionOp() + { } + + virtual nsresult + Open() override; + + virtual nsresult + DoDirectoryWork(QuotaManager* aQuotaManager) override; + + virtual void + UnblockOpen() override; +}; + +/******************************************************************************* + * Helper Functions + ******************************************************************************/ + +template ::value> +struct IntChecker +{ + static void + Assert(T aInt) + { + static_assert(mozilla::IsIntegral::value, "Not an integer!"); + MOZ_ASSERT(aInt >= 0); + } +}; + +template +struct IntChecker +{ + static void + Assert(T aInt) + { + static_assert(mozilla::IsIntegral::value, "Not an integer!"); + } +}; + +template +void +AssertNoOverflow(uint64_t aDest, T aArg) +{ + IntChecker::Assert(aDest); + IntChecker::Assert(aArg); + MOZ_ASSERT(UINT64_MAX - aDest >= uint64_t(aArg)); +} + +template +void +AssertNoUnderflow(T aDest, U aArg) +{ + IntChecker::Assert(aDest); + IntChecker::Assert(aArg); + MOZ_ASSERT(uint64_t(aDest) >= uint64_t(aArg)); +} + +} // namespace + bool IsOnIOThread() { @@ -500,8 +884,6 @@ ReportInternalError(const char* aFile, uint32_t aLine, const char* aStr) "quota"); } -END_QUOTA_NAMESPACE - namespace { QuotaManager* gInstance = nullptr; @@ -515,53 +897,6 @@ uint32_t gChunkSizeKB = kDefaultChunkSizeKB; bool gTestingEnabled = false; -// A callback runnable used by the TransactionPool when it's safe to proceed -// with a SetVersion/DeleteDatabase/etc. -class WaitForTransactionsToFinishRunnable final : public nsRunnable -{ -public: - explicit WaitForTransactionsToFinishRunnable(SynchronizedOp* aOp) - : mOp(aOp), mCountdown(1) - { - NS_ASSERTION(mOp, "Why don't we have a runnable?"); - NS_ASSERTION(mOp->mRunnable, - "What are we supposed to do when we're done?"); - NS_ASSERTION(mCountdown, "Wrong countdown!"); - } - - NS_IMETHOD - Run(); - - void - AddRun() - { - mCountdown++; - } - -private: - // The QuotaManager holds this alive. - SynchronizedOp* mOp; - uint32_t mCountdown; -}; - -class SaveOriginAccessTimeRunnable final : public nsRunnable -{ -public: - SaveOriginAccessTimeRunnable(PersistenceType aPersistenceType, - const nsACString& aOrigin, - int64_t aTimestamp) - : mPersistenceType(aPersistenceType), mOrigin(aOrigin), mTimestamp(aTimestamp) - { } - - NS_IMETHOD - Run(); - -private: - PersistenceType mPersistenceType; - nsCString mOrigin; - int64_t mTimestamp; -}; - class StorageDirectoryHelper final : public nsRunnable { @@ -573,7 +908,8 @@ class StorageDirectoryHelper final mozilla::Mutex mMutex; mozilla::CondVar mCondVar; nsresult mMainThreadResultCode; - bool mPersistent; + const bool mPersistent; + bool mCreate; bool mWaiting; public: @@ -584,18 +920,28 @@ public: , mCondVar(mMutex, "StorageDirectoryHelper::mCondVar") , mMainThreadResultCode(NS_OK) , mPersistent(aPersistent) + , mCreate(true) , mWaiting(true) { AssertIsOnIOThread(); } nsresult - CreateOrUpgradeMetadataFiles(); + CreateOrUpgradeMetadataFiles(bool aCreate); + + nsresult + RestoreMetadataFile(); private: ~StorageDirectoryHelper() { } + nsresult + AddOriginDirectory(nsIFile* aDirectory); + + nsresult + ProcessOriginDirectories(bool aMove); + nsresult RunOnMainThread(); @@ -657,9 +1003,8 @@ class MOZ_STACK_CLASS OriginParser final eExpectingEmptyToken3, eExpectingHost, eExpectingPort, - eExpectingDriveLetterOrPathnameComponent, + eExpectingEmptyTokenOrDriveLetterOrPathnameComponent, eExpectingEmptyTokenOrPathnameComponent, - eExpectingPathnameComponent, eComplete, eHandledTrailingSeparator }; @@ -730,27 +1075,6 @@ public: } }; -struct MOZ_STACK_CLASS InactiveOriginsInfo -{ - InactiveOriginsInfo(OriginCollection& aPersistentCollection, - OriginCollection& aTemporaryCollection, - nsTArray& aOrigins) - : persistentCollection(aPersistentCollection), - temporaryCollection(aTemporaryCollection), - origins(aOrigins) - { } - - OriginCollection& persistentCollection; - OriginCollection& temporaryCollection; - nsTArray& origins; -}; - -bool -IsMainProcess() -{ - return XRE_GetProcessType() == GeckoProcessType_Default; -} - void SanitizeOriginString(nsCString& aOrigin) { @@ -1049,11 +1373,25 @@ CreateDirectoryMetadata(nsIFile* aDirectory, int64_t aTimestamp, return NS_OK; } +nsresult +RestoreDirectoryMetadata(nsIFile* aDirectory, bool aPersistent) +{ + nsRefPtr helper = + new StorageDirectoryHelper(aDirectory, aPersistent); + + nsresult rv = helper->RestoreMetadataFile(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + nsresult GetDirectoryMetadataInputStream(nsIFile* aDirectory, nsIBinaryInputStream** aStream) { - AssertIsOnIOThread(); + MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(aDirectory); MOZ_ASSERT(aStream); @@ -1084,11 +1422,39 @@ GetDirectoryMetadataInputStream(nsIFile* aDirectory, } nsresult -GetDirectoryMetadata(nsIFile* aDirectory, - int64_t* aTimestamp, - nsACString& aGroup, - nsACString& aOrigin, - bool* aIsApp) +GetDirectoryMetadataWithRestore(nsIFile* aDirectory, + bool aPersistent, + int64_t* aTimestamp, + nsACString& aGroup, + nsACString& aOrigin, + bool* aIsApp) +{ + nsresult rv = QuotaManager::GetDirectoryMetadata(aDirectory, + aTimestamp, + aGroup, + aOrigin, + aIsApp); + if (NS_WARN_IF(NS_FAILED(rv))) { + rv = RestoreDirectoryMetadata(aDirectory, aPersistent); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = QuotaManager::GetDirectoryMetadata(aDirectory, + aTimestamp, + aGroup, + aOrigin, + aIsApp); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + return NS_OK; +} + +nsresult +GetDirectoryMetadata(nsIFile* aDirectory, int64_t* aTimestamp) { AssertIsOnIOThread(); MOZ_ASSERT(aDirectory); @@ -1097,34 +1463,38 @@ GetDirectoryMetadata(nsIFile* aDirectory, nsCOMPtr binaryStream; nsresult rv = GetDirectoryMetadataInputStream(aDirectory, getter_AddRefs(binaryStream)); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } uint64_t timestamp; rv = binaryStream->Read64(×tamp); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } - nsCString group; - rv = binaryStream->ReadCString(group); - NS_ENSURE_SUCCESS(rv, rv); + *aTimestamp = timestamp; + return NS_OK; +} - nsCString origin; - rv = binaryStream->ReadCString(origin); - NS_ENSURE_SUCCESS(rv, rv); +nsresult +GetDirectoryMetadataWithRestore(nsIFile* aDirectory, + bool aPersistent, + int64_t* aTimestamp) +{ + nsresult rv = GetDirectoryMetadata(aDirectory, aTimestamp); + if (NS_WARN_IF(NS_FAILED(rv))) { + rv = RestoreDirectoryMetadata(aDirectory, aPersistent); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } - bool isApp; - if (aIsApp) { - rv = binaryStream->ReadBoolean(&isApp); + rv = GetDirectoryMetadata(aDirectory, aTimestamp); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } - *aTimestamp = timestamp; - aGroup = group; - aOrigin = origin; - if (aIsApp) { - *aIsApp = isApp; - } return NS_OK; } @@ -1234,6 +1604,380 @@ GetTemporaryStorageLimit(nsIFile* aDirectory, uint64_t aCurrentUsage, } // namespace +/******************************************************************************* + * Directory lock + ******************************************************************************/ + +DirectoryLockImpl::DirectoryLockImpl(QuotaManager* aQuotaManager, + Nullable aPersistenceType, + const nsACString& aGroup, + const OriginScope& aOriginScope, + Nullable aIsApp, + Nullable aClientType, + bool aExclusive, + bool aInternal, + OpenDirectoryListener* aOpenListener) + : mQuotaManager(aQuotaManager) + , mPersistenceType(aPersistenceType) + , mGroup(aGroup) + , mOriginScope(aOriginScope) + , mIsApp(aIsApp) + , mClientType(aClientType) + , mOpenListener(aOpenListener) + , mExclusive(aExclusive) + , mInternal(aInternal) + , mInvalidated(false) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aQuotaManager); + MOZ_ASSERT_IF(!aInternal, !aPersistenceType.IsNull()); + MOZ_ASSERT_IF(!aInternal, + aPersistenceType.Value() != PERSISTENCE_TYPE_INVALID); + MOZ_ASSERT_IF(!aInternal, !aGroup.IsEmpty()); + MOZ_ASSERT_IF(aInternal, !aOriginScope.IsEmpty() || aOriginScope.IsNull()); + MOZ_ASSERT_IF(!aInternal, aOriginScope.IsOrigin()); + MOZ_ASSERT_IF(!aInternal, !aOriginScope.IsEmpty()); + MOZ_ASSERT_IF(!aInternal, !aIsApp.IsNull()); + MOZ_ASSERT_IF(!aInternal, !aClientType.IsNull()); + MOZ_ASSERT_IF(!aInternal, aClientType.Value() != Client::TYPE_MAX); + MOZ_ASSERT_IF(!aInternal, aOpenListener); +} + +DirectoryLockImpl::~DirectoryLockImpl() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mQuotaManager); + + for (DirectoryLockImpl* blockingLock : mBlocking) { + blockingLock->MaybeUnblock(this); + } + + mBlocking.Clear(); + + mQuotaManager->UnregisterDirectoryLock(this); +} + +// static +bool +DirectoryLockImpl::MatchOriginScopes(const OriginScope& aOriginScope1, + const OriginScope& aOriginScope2) +{ + MOZ_ASSERT(NS_IsMainThread()); + + bool match; + + if (aOriginScope2.IsNull() || aOriginScope1.IsNull()) { + match = true; + } else if (aOriginScope2.IsOrigin()) { + if (aOriginScope1.IsOrigin()) { + match = aOriginScope2.Equals(aOriginScope1); + } else { + match = PatternMatchesOrigin(aOriginScope1, aOriginScope2); + } + } else if (aOriginScope1.IsOrigin()) { + match = PatternMatchesOrigin(aOriginScope2, aOriginScope1); + } else { + match = PatternMatchesOrigin(aOriginScope1, aOriginScope2) || + PatternMatchesOrigin(aOriginScope2, aOriginScope1); + } + + return match; +} + +bool +DirectoryLockImpl::MustWaitFor(const DirectoryLockImpl& aExistingLock) +{ + MOZ_ASSERT(NS_IsMainThread()); + + // Waiting is never required if the ops in comparison represent shared locks. + if (!aExistingLock.mExclusive && !mExclusive) { + return false; + } + + // If the persistence types don't overlap, the op can proceed. + if (!aExistingLock.mPersistenceType.IsNull() && !mPersistenceType.IsNull() && + aExistingLock.mPersistenceType.Value() != mPersistenceType.Value()) { + return false; + } + + // If the origin scopes don't overlap, the op can proceed. + bool match = MatchOriginScopes(mOriginScope, aExistingLock.mOriginScope); + if (!match) { + return false; + } + + // If the client types don't overlap, the op can proceed. + if (!aExistingLock.mClientType.IsNull() && !mClientType.IsNull() && + aExistingLock.mClientType.Value() != mClientType.Value()) { + return false; + } + + // Otherwise, when all attributes overlap (persistence type, origin scope and + // client type) the op must wait. + return true; +} + +void +DirectoryLockImpl::NotifyOpenListener() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mQuotaManager); + MOZ_ASSERT(mOpenListener); + + if (mInvalidated) { + mOpenListener->DirectoryLockFailed(); + } else { + mOpenListener->DirectoryLockAcquired(this); + } + + mOpenListener = nullptr; + + mQuotaManager->RemovePendingDirectoryLock(this); +} + +NS_IMPL_ISUPPORTS0(DirectoryLockImpl); + +/******************************************************************************* + * Quota object + ******************************************************************************/ + +void +QuotaObject::AddRef() +{ + QuotaManager* quotaManager = QuotaManager::Get(); + if (!quotaManager) { + NS_ERROR("Null quota manager, this shouldn't happen, possible leak!"); + + ++mRefCnt; + + return; + } + + MutexAutoLock lock(quotaManager->mQuotaMutex); + + ++mRefCnt; +} + +void +QuotaObject::Release() +{ + QuotaManager* quotaManager = QuotaManager::Get(); + if (!quotaManager) { + NS_ERROR("Null quota manager, this shouldn't happen, possible leak!"); + + nsrefcnt count = --mRefCnt; + if (count == 0) { + mRefCnt = 1; + delete this; + } + + return; + } + + { + MutexAutoLock lock(quotaManager->mQuotaMutex); + + --mRefCnt; + + if (mRefCnt > 0) { + return; + } + + if (mOriginInfo) { + mOriginInfo->mQuotaObjects.Remove(mPath); + } + } + + delete this; +} + +bool +QuotaObject::MaybeUpdateSize(int64_t aSize, bool aTruncate) +{ + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + MutexAutoLock lock(quotaManager->mQuotaMutex); + + if (mSize == aSize) { + return true; + } + + if (!mOriginInfo) { + mSize = aSize; + return true; + } + + GroupInfo* groupInfo = mOriginInfo->mGroupInfo; + MOZ_ASSERT(groupInfo); + + if (mSize > aSize) { + if (aTruncate) { + const int64_t delta = mSize - aSize; + + AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, delta); + quotaManager->mTemporaryStorageUsage -= delta; + + AssertNoUnderflow(groupInfo->mUsage, delta); + groupInfo->mUsage -= delta; + + AssertNoUnderflow(mOriginInfo->mUsage, delta); + mOriginInfo->mUsage -= delta; + + mSize = aSize; + } + return true; + } + + MOZ_ASSERT(mSize < aSize); + + nsRefPtr complementaryGroupInfo = + groupInfo->mGroupInfoPair->LockedGetGroupInfo( + ComplementaryPersistenceType(groupInfo->mPersistenceType)); + + uint64_t delta = aSize - mSize; + + AssertNoOverflow(mOriginInfo->mUsage, delta); + uint64_t newUsage = mOriginInfo->mUsage + delta; + + // Temporary storage has no limit for origin usage (there's a group and the + // global limit though). + + AssertNoOverflow(groupInfo->mUsage, delta); + uint64_t newGroupUsage = groupInfo->mUsage + delta; + + uint64_t groupUsage = groupInfo->mUsage; + if (complementaryGroupInfo) { + AssertNoOverflow(groupUsage, complementaryGroupInfo->mUsage); + groupUsage += complementaryGroupInfo->mUsage; + } + + // Temporary storage has a hard limit for group usage (20 % of the global + // limit). + AssertNoOverflow(groupUsage, delta); + if (groupUsage + delta > quotaManager->GetGroupLimit()) { + return false; + } + + AssertNoOverflow(quotaManager->mTemporaryStorageUsage, delta); + uint64_t newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage + + delta; + + if (newTemporaryStorageUsage > quotaManager->mTemporaryStorageLimit) { + // This will block the thread without holding the lock while waitting. + + nsAutoTArray, 10> locks; + + uint64_t sizeToBeFreed = + quotaManager->LockedCollectOriginsForEviction(delta, locks); + + if (!sizeToBeFreed) { + return false; + } + + NS_ASSERTION(sizeToBeFreed >= delta, "Huh?"); + + { + MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); + + for (nsRefPtr& lock : locks) { + MOZ_ASSERT(!lock->GetPersistenceType().IsNull()); + MOZ_ASSERT(lock->GetOriginScope().IsOrigin()); + MOZ_ASSERT(!lock->GetOriginScope().IsEmpty()); + + quotaManager->DeleteFilesForOrigin(lock->GetPersistenceType().Value(), + lock->GetOriginScope()); + } + } + + // Relocked. + + NS_ASSERTION(mOriginInfo, "How come?!"); + + for (DirectoryLockImpl* lock : locks) { + MOZ_ASSERT(!lock->GetPersistenceType().IsNull()); + MOZ_ASSERT(!lock->GetGroup().IsEmpty()); + MOZ_ASSERT(lock->GetOriginScope().IsOrigin()); + MOZ_ASSERT(!lock->GetOriginScope().IsEmpty()); + MOZ_ASSERT(lock->GetOriginScope() != mOriginInfo->mOrigin, + "Deleted itself!"); + + quotaManager->LockedRemoveQuotaForOrigin( + lock->GetPersistenceType().Value(), + lock->GetGroup(), + lock->GetOriginScope()); + } + + // We unlocked and relocked several times so we need to recompute all the + // essential variables and recheck the group limit. + + AssertNoUnderflow(aSize, mSize); + delta = aSize - mSize; + + AssertNoOverflow(mOriginInfo->mUsage, delta); + newUsage = mOriginInfo->mUsage + delta; + + AssertNoOverflow(groupInfo->mUsage, delta); + newGroupUsage = groupInfo->mUsage + delta; + + groupUsage = groupInfo->mUsage; + if (complementaryGroupInfo) { + AssertNoOverflow(groupUsage, complementaryGroupInfo->mUsage); + groupUsage += complementaryGroupInfo->mUsage; + } + + AssertNoOverflow(groupUsage, delta); + if (groupUsage + delta > quotaManager->GetGroupLimit()) { + // Unfortunately some other thread increased the group usage in the + // meantime and we are not below the group limit anymore. + + // However, the origin eviction must be finalized in this case too. + MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); + + quotaManager->FinalizeOriginEviction(locks); + + return false; + } + + AssertNoOverflow(quotaManager->mTemporaryStorageUsage, delta); + newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage + delta; + + NS_ASSERTION(newTemporaryStorageUsage <= + quotaManager->mTemporaryStorageLimit, "How come?!"); + + // Ok, we successfully freed enough space and the operation can continue + // without throwing the quota error. + mOriginInfo->mUsage = newUsage; + groupInfo->mUsage = newGroupUsage; + quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;; + + // Some other thread could increase the size in the meantime, but no more + // than this one. + MOZ_ASSERT(mSize < aSize); + mSize = aSize; + + // Finally, release IO thread only objects and allow next synchronized + // ops for the evicted origins. + MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); + + quotaManager->FinalizeOriginEviction(locks); + + return true; + } + + mOriginInfo->mUsage = newUsage; + groupInfo->mUsage = newGroupUsage; + quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage; + + mSize = aSize; + + return true; +} + +/******************************************************************************* + * Quota manager + ******************************************************************************/ + QuotaManager::QuotaManager() : mQuotaMutex("QuotaManager.mQuotaMutex"), mTemporaryStorageLimit(0), @@ -1309,11 +2053,341 @@ QuotaManager::IsShuttingDown() return gShutdown; } +auto +QuotaManager::CreateDirectoryLock(Nullable aPersistenceType, + const nsACString& aGroup, + const OriginScope& aOriginScope, + Nullable aIsApp, + Nullable aClientType, + bool aExclusive, + bool aInternal, + OpenDirectoryListener* aOpenListener) + -> already_AddRefed +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT_IF(!aInternal, !aPersistenceType.IsNull()); + MOZ_ASSERT_IF(!aInternal, + aPersistenceType.Value() != PERSISTENCE_TYPE_INVALID); + MOZ_ASSERT_IF(!aInternal, !aGroup.IsEmpty()); + MOZ_ASSERT_IF(aInternal, !aOriginScope.IsEmpty() || aOriginScope.IsNull()); + MOZ_ASSERT_IF(!aInternal, aOriginScope.IsOrigin()); + MOZ_ASSERT_IF(!aInternal, !aOriginScope.IsEmpty()); + MOZ_ASSERT_IF(!aInternal, !aIsApp.IsNull()); + MOZ_ASSERT_IF(!aInternal, !aClientType.IsNull()); + MOZ_ASSERT_IF(!aInternal, aClientType.Value() != Client::TYPE_MAX); + MOZ_ASSERT_IF(!aInternal, aOpenListener); + + nsRefPtr lock = new DirectoryLockImpl(this, + aPersistenceType, + aGroup, + aOriginScope, + aIsApp, + aClientType, + aExclusive, + aInternal, + aOpenListener); + + mPendingDirectoryLocks.AppendElement(lock); + + // See if this lock needs to wait. + bool blocked = false; + for (uint32_t index = mDirectoryLocks.Length(); index > 0; index--) { + DirectoryLockImpl* existingLock = mDirectoryLocks[index - 1]; + if (lock->MustWaitFor(*existingLock)) { + existingLock->AddBlockingLock(lock); + lock->AddBlockedOnLock(existingLock); + blocked = true; + } + } + + RegisterDirectoryLock(lock); + + // Otherwise, notify the open listener immediately. + if (!blocked) { + lock->NotifyOpenListener(); + } + + return lock.forget(); +} + +auto +QuotaManager::CreateDirectoryLockForEviction(PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + bool aIsApp) + -> already_AddRefed +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_INVALID); + MOZ_ASSERT(!aOrigin.IsEmpty()); + + nsRefPtr lock = + new DirectoryLockImpl(this, + Nullable(aPersistenceType), + aGroup, + OriginScope::FromOrigin(aOrigin), + Nullable(aIsApp), + Nullable(), + /* aExclusive */ true, + /* aInternal */ true, + nullptr); + +#ifdef DEBUG + for (uint32_t index = mDirectoryLocks.Length(); index > 0; index--) { + DirectoryLockImpl* existingLock = mDirectoryLocks[index - 1]; + MOZ_ASSERT(!lock->MustWaitFor(*existingLock)); + } +#endif + + RegisterDirectoryLock(lock); + + return lock.forget(); +} + +void +QuotaManager::RegisterDirectoryLock(DirectoryLockImpl* aLock) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aLock); + + mDirectoryLocks.AppendElement(aLock); + + if (aLock->ShouldUpdateLockTable()) { + const Nullable& persistenceType = + aLock->GetPersistenceType(); + const OriginScope& originScope = aLock->GetOriginScope(); + + MOZ_ASSERT(!persistenceType.IsNull()); + MOZ_ASSERT(!aLock->GetGroup().IsEmpty()); + MOZ_ASSERT(originScope.IsOrigin()); + MOZ_ASSERT(!originScope.IsEmpty()); + + DirectoryLockTable& directoryLockTable = + GetDirectoryLockTable(persistenceType.Value()); + + nsTArray* array; + if (!directoryLockTable.Get(originScope, &array)) { + array = new nsTArray(); + directoryLockTable.Put(originScope, array); + + if (!IsShuttingDown()) { + UpdateOriginAccessTime(persistenceType.Value(), + aLock->GetGroup(), + originScope); + } + } + array->AppendElement(aLock); + } +} + +void +QuotaManager::UnregisterDirectoryLock(DirectoryLockImpl* aLock) +{ + MOZ_ASSERT(NS_IsMainThread()); + + MOZ_ALWAYS_TRUE(mDirectoryLocks.RemoveElement(aLock)); + + if (aLock->ShouldUpdateLockTable()) { + const Nullable& persistenceType = + aLock->GetPersistenceType(); + const OriginScope& originScope = aLock->GetOriginScope(); + + MOZ_ASSERT(!persistenceType.IsNull()); + MOZ_ASSERT(!aLock->GetGroup().IsEmpty()); + MOZ_ASSERT(originScope.IsOrigin()); + MOZ_ASSERT(!originScope.IsEmpty()); + + DirectoryLockTable& directoryLockTable = + GetDirectoryLockTable(persistenceType.Value()); + + nsTArray* array; + MOZ_ALWAYS_TRUE(directoryLockTable.Get(originScope, &array)); + + MOZ_ALWAYS_TRUE(array->RemoveElement(aLock)); + if (array->IsEmpty()) { + directoryLockTable.Remove(originScope); + + if (!IsShuttingDown()) { + UpdateOriginAccessTime(persistenceType.Value(), + aLock->GetGroup(), + originScope); + } + } + } +} + +void +QuotaManager::RemovePendingDirectoryLock(DirectoryLockImpl* aLock) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aLock); + + MOZ_ALWAYS_TRUE(mPendingDirectoryLocks.RemoveElement(aLock)); +} + +uint64_t +QuotaManager::CollectOriginsForEviction( + uint64_t aMinSizeToBeFreed, + nsTArray>& aLocks) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aLocks.IsEmpty()); + + class MOZ_STACK_CLASS Closure final + { + nsTArray& mTemporaryStorageLocks; + nsTArray& mDefaultStorageLocks; + nsTArray& mInactiveOriginInfos; + + public: + Closure(nsTArray& aTemporaryStorageLocks, + nsTArray& aDefaultStorageLocks, + nsTArray& aInactiveOriginInfos) + : mTemporaryStorageLocks(aTemporaryStorageLocks) + , mDefaultStorageLocks(aDefaultStorageLocks) + , mInactiveOriginInfos(aInactiveOriginInfos) + { } + + static PLDHashOperator + GetInactiveTemporaryStorageOrigins(const nsACString& aKey, + GroupInfoPair* aValue, + void* aUserArg) + { + MOZ_ASSERT(!aKey.IsEmpty()); + MOZ_ASSERT(aValue); + MOZ_ASSERT(aUserArg); + + auto* closure = static_cast(aUserArg); + + nsRefPtr groupInfo = + aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY); + if (groupInfo) { + GetInactiveOriginInfos(groupInfo->mOriginInfos, + closure->mTemporaryStorageLocks, + closure->mInactiveOriginInfos); + } + + groupInfo = aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT); + if (groupInfo) { + GetInactiveOriginInfos(groupInfo->mOriginInfos, + closure->mDefaultStorageLocks, + closure->mInactiveOriginInfos); + } + + return PL_DHASH_NEXT; + } + + private: + static void + GetInactiveOriginInfos(nsTArray>& aOriginInfos, + nsTArray& aLocks, + nsTArray& aInactiveOriginInfos) + { + for (OriginInfo* originInfo : aOriginInfos) { + MOZ_ASSERT(IsTreatedAsTemporary(originInfo->mGroupInfo->mPersistenceType, + originInfo->mIsApp)); + + OriginScope originScope = OriginScope::FromOrigin(originInfo->mOrigin); + + bool match = false; + for (uint32_t j = aLocks.Length(); j > 0; j--) { + DirectoryLockImpl* lock = aLocks[j - 1]; + if (DirectoryLockImpl::MatchOriginScopes(originScope, + lock->GetOriginScope())) { + match = true; + break; + } + } + + if (!match) { + MOZ_ASSERT(!originInfo->mQuotaObjects.Count(), + "Inactive origin shouldn't have open files!"); + aInactiveOriginInfos.InsertElementSorted(originInfo, + OriginInfoLRUComparator()); + } + } + } + }; + + // Split locks into separate arrays and filter out locks for persistent + // storage, they can't block us. + nsTArray temporaryStorageLocks; + nsTArray defaultStorageLocks; + for (DirectoryLockImpl* lock : mDirectoryLocks) { + const Nullable& persistenceType = + lock->GetPersistenceType(); + + if (persistenceType.IsNull()) { + temporaryStorageLocks.AppendElement(lock); + defaultStorageLocks.AppendElement(lock); + } else if (persistenceType.Value() == PERSISTENCE_TYPE_TEMPORARY) { + temporaryStorageLocks.AppendElement(lock); + } else if (persistenceType.Value() == PERSISTENCE_TYPE_DEFAULT) { + defaultStorageLocks.AppendElement(lock); + } else { + MOZ_ASSERT(persistenceType.Value() == PERSISTENCE_TYPE_PERSISTENT); + + // Do nothing here, persistent origins don't need to be collected ever. + } + } + + nsTArray inactiveOrigins; + + Closure closure(temporaryStorageLocks, defaultStorageLocks, inactiveOrigins); + + // Enumerate and process inactive origins. This must be protected by the + // mutex. + MutexAutoLock lock(mQuotaMutex); + + mGroupInfoPairs.EnumerateRead(Closure::GetInactiveTemporaryStorageOrigins, + &closure); + +#ifdef DEBUG + // Make sure the array is sorted correctly. + for (uint32_t index = inactiveOrigins.Length(); index > 1; index--) { + MOZ_ASSERT(inactiveOrigins[index - 1]->mAccessTime >= + inactiveOrigins[index - 2]->mAccessTime); + } +#endif + + // Create a list of inactive and the least recently used origins + // whose aggregate size is greater or equals the minimal size to be freed. + uint64_t sizeToBeFreed = 0; + for(uint32_t count = inactiveOrigins.Length(), index = 0; + index < count; + index++) { + if (sizeToBeFreed >= aMinSizeToBeFreed) { + inactiveOrigins.TruncateLength(index); + break; + } + + sizeToBeFreed += inactiveOrigins[index]->mUsage; + } + + if (sizeToBeFreed >= aMinSizeToBeFreed) { + // Success, add directory locks for these origins, so any other + // operations for them will be delayed (until origin eviction is finalized). + + for (OriginInfo* originInfo : inactiveOrigins) { + nsRefPtr lock = + CreateDirectoryLockForEviction(originInfo->mGroupInfo->mPersistenceType, + originInfo->mGroupInfo->mGroup, + originInfo->mOrigin, + originInfo->mIsApp); + aLocks.AppendElement(lock.forget()); + } + + return sizeToBeFreed; + } + + return 0; +} + nsresult QuotaManager::Init() { nsresult rv; - if (IsMainProcess()) { + if (XRE_IsParentProcess()) { nsCOMPtr baseDir; rv = NS_GetSpecialDirectory(NS_APP_INDEXEDDB_PARENT_DIR, getter_AddRefs(baseDir)); @@ -1380,10 +2454,8 @@ QuotaManager::Init() NS_ASSERTION(mClients.Capacity() == Client::TYPE_MAX, "Should be using an auto array with correct capacity!"); - nsRefPtr idbClient = indexedDB::CreateQuotaClient(); - // Register clients. - mClients.AppendElement(idbClient); + mClients.AppendElement(indexedDB::CreateQuotaClient()); mClients.AppendElement(asmjscache::CreateClient()); mClients.AppendElement(cache::CreateQuotaClient()); @@ -1475,7 +2547,10 @@ QuotaManager::UpdateOriginAccessTime(PersistenceType aPersistenceType, MutexAutoUnlock autoUnlock(mQuotaMutex); - SaveOriginAccessTime(aPersistenceType, aOrigin, timestamp); + nsRefPtr op = + new SaveOriginAccessTimeOp(aPersistenceType, aOrigin, timestamp); + + op->RunImmediately(); } } @@ -1610,190 +2685,16 @@ QuotaManager::GetQuotaObject(PersistenceType aPersistenceType, return GetQuotaObject(aPersistenceType, aGroup, aOrigin, file); } -bool -QuotaManager::RegisterStorage(nsIOfflineStorage* aStorage) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aStorage, "Null pointer!"); - - // Don't allow any new storages to be created after shutdown. - if (IsShuttingDown()) { - return false; - } - - // Add this storage to its origin info if it exists, create it otherwise. - const nsACString& origin = aStorage->Origin(); - ArrayCluster* cluster; - if (!mLiveStorages.Get(origin, &cluster)) { - cluster = new ArrayCluster(); - mLiveStorages.Put(origin, cluster); - } - (*cluster)[aStorage->GetClient()->GetType()].AppendElement(aStorage); - - if (aStorage->Type() != PERSISTENCE_TYPE_PERSISTENT) { - LiveStorageTable& liveStorageTable = GetLiveStorageTable(aStorage->Type()); - - nsTArray* array; - if (!liveStorageTable.Get(origin, &array)) { - array = new nsTArray(); - liveStorageTable.Put(origin, array); - - UpdateOriginAccessTime(aStorage->Type(), aStorage->Group(), origin); - } - array->AppendElement(aStorage); - } - - return true; -} - void -QuotaManager::UnregisterStorage(nsIOfflineStorage* aStorage) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aStorage, "Null pointer!"); - - // Remove this storage from its origin array, maybe remove the array if it - // is then empty. - const nsACString& origin = aStorage->Origin(); - - ArrayCluster* cluster; - MOZ_ALWAYS_TRUE(mLiveStorages.Get(origin, &cluster)); - - MOZ_ALWAYS_TRUE( - (*cluster)[aStorage->GetClient()->GetType()].RemoveElement(aStorage)); - if (cluster->IsEmpty()) { - mLiveStorages.Remove(origin); - } - - if (aStorage->Type() != PERSISTENCE_TYPE_PERSISTENT) { - LiveStorageTable& liveStorageTable = GetLiveStorageTable(aStorage->Type()); - - nsTArray* array; - MOZ_ALWAYS_TRUE(liveStorageTable.Get(origin, &array)); - - MOZ_ALWAYS_TRUE(array->RemoveElement(aStorage)); - if (array->IsEmpty()) { - liveStorageTable.Remove(origin); - - UpdateOriginAccessTime(aStorage->Type(), aStorage->Group(), origin); - } - } -} - -void -QuotaManager::AbortCloseStoragesForProcess(ContentParent* aContentParent) +QuotaManager::AbortOperationsForProcess(ContentParentId aContentParentId) { MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aContentParent); - // FileHandle API is not yet supported in child processes, so we don't have - // to worry about aborting file handles for given child process. - - StorageMatcher> liveStorages; - liveStorages.Find(mLiveStorages); - - for (uint32_t i = 0; i < Client::TYPE_MAX; i++) { - nsTArray& array = liveStorages[i]; - for (uint32_t j = 0; j < array.Length(); j++) { - nsCOMPtr storage = array[j]; - - if (storage->IsOwnedByProcess(aContentParent)) { - if (NS_FAILED(storage->Close())) { - NS_WARNING("Failed to close storage for dying process!"); - } - } - } + for (nsRefPtr& client : mClients) { + client->AbortOperationsForProcess(aContentParentId); } } -nsresult -QuotaManager::WaitForOpenAllowed(const OriginOrPatternString& aOriginOrPattern, - Nullable aPersistenceType, - const nsACString& aId, nsIRunnable* aRunnable) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!aOriginOrPattern.IsEmpty() || aOriginOrPattern.IsNull(), - "Empty pattern!"); - NS_ASSERTION(aRunnable, "Null pointer!"); - - nsAutoPtr op(new SynchronizedOp(aOriginOrPattern, - aPersistenceType, aId)); - - // See if this runnable needs to wait. - bool delayed = false; - for (uint32_t index = mSynchronizedOps.Length(); index > 0; index--) { - nsAutoPtr& existingOp = mSynchronizedOps[index - 1]; - if (op->MustWaitFor(*existingOp)) { - existingOp->DelayRunnable(aRunnable); - delayed = true; - break; - } - } - - // Otherwise, dispatch it immediately. - if (!delayed) { - nsresult rv = NS_DispatchToCurrentThread(aRunnable); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Adding this to the synchronized ops list will block any additional - // ops from proceeding until this one is done. - mSynchronizedOps.AppendElement(op.forget()); - - return NS_OK; -} - -void -QuotaManager::AddSynchronizedOp(const OriginOrPatternString& aOriginOrPattern, - Nullable aPersistenceType) -{ - nsAutoPtr op(new SynchronizedOp(aOriginOrPattern, - aPersistenceType, - EmptyCString())); - -#ifdef DEBUG - for (uint32_t index = mSynchronizedOps.Length(); index > 0; index--) { - nsAutoPtr& existingOp = mSynchronizedOps[index - 1]; - NS_ASSERTION(!op->MustWaitFor(*existingOp), "What?"); - } -#endif - - mSynchronizedOps.AppendElement(op.forget()); -} - -void -QuotaManager::AllowNextSynchronizedOp( - const OriginOrPatternString& aOriginOrPattern, - Nullable aPersistenceType, - const nsACString& aId) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!aOriginOrPattern.IsEmpty() || aOriginOrPattern.IsNull(), - "Empty origin/pattern!"); - - uint32_t count = mSynchronizedOps.Length(); - for (uint32_t index = 0; index < count; index++) { - nsAutoPtr& op = mSynchronizedOps[index]; - if (op->mOriginOrPattern.IsOrigin() == aOriginOrPattern.IsOrigin() && - op->mOriginOrPattern == aOriginOrPattern && - op->mPersistenceType == aPersistenceType) { - if (op->mId == aId) { - op->DispatchDelayedRunnables(); - - mSynchronizedOps.RemoveElementAt(index); - return; - } - - // If one or the other is for an origin clear, we should have matched - // solely on origin. - NS_ASSERTION(!op->mId.IsEmpty() && !aId.IsEmpty(), - "Why didn't we match earlier?"); - } - } - - NS_NOTREACHED("Why didn't we find a SynchronizedOp?"); -} - nsresult QuotaManager::GetDirectoryForOrigin(PersistenceType aPersistenceType, const nsACString& aASCIIOrigin, @@ -1820,6 +2721,9 @@ QuotaManager::GetDirectoryForOrigin(PersistenceType aPersistenceType, nsresult QuotaManager::InitializeRepository(PersistenceType aPersistenceType) { + MOZ_ASSERT(aPersistenceType == PERSISTENCE_TYPE_TEMPORARY || + aPersistenceType == PERSISTENCE_TYPE_DEFAULT); + nsresult rv; nsCOMPtr directory = @@ -1883,8 +2787,12 @@ QuotaManager::InitializeRepository(PersistenceType aPersistenceType) nsCString group; nsCString origin; bool isApp; - rv = GetDirectoryMetadata(childDirectory, ×tamp, group, origin, - &isApp); + rv = GetDirectoryMetadataWithRestore(childDirectory, + /* aPersistent */ false, + ×tamp, + group, + origin, + &isApp); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -1906,6 +2814,37 @@ QuotaManager::InitializeRepository(PersistenceType aPersistenceType) return NS_OK; } +namespace { + +// The Cache API was creating top level morgue directories by accident for +// a short time in nightly. This unfortunately prevents all storage from +// working. So recover these profiles by removing these corrupt directories. +// This should be removed at some point in the future. +bool +MaybeRemoveCorruptDirectory(const nsAString& aLeafName, nsIFile* aDir) +{ +#ifdef NIGHTLY_BUILD + MOZ_ASSERT(aDir); + + if (aLeafName != NS_LITERAL_STRING("morgue")) { + return false; + } + + NS_WARNING("QuotaManager removing corrupt morgue directory!"); + + nsresult rv = aDir->Remove(true /* recursive */); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + return true; +#else + return false; +#endif // NIGHTLY_BUILD +} + +} // anonymous namespace + nsresult QuotaManager::InitializeOrigin(PersistenceType aPersistenceType, const nsACString& aGroup, @@ -1963,6 +2902,10 @@ QuotaManager::InitializeOrigin(PersistenceType aPersistenceType, return NS_ERROR_UNEXPECTED; } + if (MaybeRemoveCorruptDirectory(leafName, file)) { + continue; + } + Client::Type clientType; rv = Client::TypeFromText(leafName, clientType); if (NS_FAILED(rv)) { @@ -2119,7 +3062,7 @@ QuotaManager::MaybeUpgradePersistentStorageDirectory() nsRefPtr helper = new StorageDirectoryHelper(persistentStorageDir, /* aPersistent */ true); - rv = helper->CreateOrUpgradeMetadataFiles(); + rv = helper->CreateOrUpgradeMetadataFiles(/* aCreate */ true); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -2155,7 +3098,7 @@ QuotaManager::MaybeUpgradePersistentStorageDirectory() helper = new StorageDirectoryHelper(temporaryStorageDir, /* aPersistent */ false); - rv = helper->CreateOrUpgradeMetadataFiles(); + rv = helper->CreateOrUpgradeMetadataFiles(/* aCreate */ false); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -2194,6 +3137,103 @@ QuotaManager::MaybeUpgradeStorageArea() return NS_OK; } +void +QuotaManager::OpenDirectory(PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + bool aIsApp, + Client::Type aClientType, + bool aExclusive, + OpenDirectoryListener* aOpenListener) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsRefPtr lock = + CreateDirectoryLock(Nullable(aPersistenceType), + aGroup, + OriginScope::FromOrigin(aOrigin), + Nullable(aIsApp), + Nullable(aClientType), + aExclusive, + false, + aOpenListener); + MOZ_ASSERT(lock); +} + +void +QuotaManager::OpenDirectoryInternal(Nullable aPersistenceType, + const OriginScope& aOriginScope, + bool aExclusive, + OpenDirectoryListener* aOpenListener) +{ + MOZ_ASSERT(NS_IsMainThread()); + + class MOZ_STACK_CLASS Helper final + { + public: + static PLDHashOperator + Enumerate(nsCStringHashKey* aKey, void* aClosure) + { + auto* client = static_cast(aClosure); + MOZ_ASSERT(client); + + client->AbortOperations(aKey->GetKey()); + + return PL_DHASH_NEXT; + } + }; + + nsRefPtr lock = + CreateDirectoryLock(aPersistenceType, + EmptyCString(), + aOriginScope, + Nullable(), + Nullable(), + aExclusive, + true, + aOpenListener); + MOZ_ASSERT(lock); + + if (!aExclusive) { + return; + } + + // All the locks that block this new exclusive lock need to be invalidated. + // We also need to notify clients to abort operations for them. + nsAutoTArray>, + Client::TYPE_MAX> origins; + origins.SetLength(Client::TYPE_MAX); + + const nsTArray& blockedOnLocks = + lock->GetBlockedOnLocks(); + + for (DirectoryLockImpl* blockedOnLock : blockedOnLocks) { + blockedOnLock->Invalidate(); + + if (!blockedOnLock->IsInternal()) { + MOZ_ASSERT(!blockedOnLock->GetClientType().IsNull()); + Client::Type clientType = blockedOnLock->GetClientType().Value(); + MOZ_ASSERT(clientType < Client::TYPE_MAX); + + const OriginScope& originScope = blockedOnLock->GetOriginScope(); + MOZ_ASSERT(originScope.IsOrigin()); + MOZ_ASSERT(!originScope.IsEmpty()); + + nsAutoPtr>& origin = origins[clientType]; + if (!origin) { + origin = new nsTHashtable(); + } + origin->PutEntry(originScope); + } + } + + for (uint32_t index : MakeRange(uint32_t(Client::TYPE_MAX))) { + if (origins[index]) { + origins[index]->EnumerateEntries(Helper::Enumerate, mClients[index]); + } + } +} + nsresult QuotaManager::EnsureOriginIsInitialized(PersistenceType aPersistenceType, const nsACString& aGroup, @@ -2273,15 +3313,13 @@ QuotaManager::EnsureOriginIsInitialized(PersistenceType aPersistenceType, aIsApp); NS_ENSURE_SUCCESS(rv, rv); } else { - nsCOMPtr stream; - rv = GetDirectoryMetadataInputStream(directory, getter_AddRefs(stream)); - NS_ENSURE_SUCCESS(rv, rv); - - uint64_t ts; - rv = stream->Read64(&ts); - NS_ENSURE_SUCCESS(rv, rv); - - timestamp = ts; + bool persistent = aPersistenceType == PERSISTENCE_TYPE_PERSISTENT; + rv = GetDirectoryMetadataWithRestore(directory, + persistent, + ×tamp); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } MOZ_ASSERT(timestamp <= PR_Now()); } @@ -2335,11 +3373,13 @@ QuotaManager::ResetOrClearCompleted() ReleaseIOThreadObjects(); } -already_AddRefed +Client* QuotaManager::GetClient(Client::Type aClientType) { - nsRefPtr client = mClients.SafeElementAt(aClientType); - return client.forget(); + MOZ_ASSERT(aClientType >= Client::IDB); + MOZ_ASSERT(aClientType < Client::TYPE_MAX); + + return mClients.ElementAt(aClientType); } uint64_t @@ -2363,7 +3403,6 @@ void QuotaManager::GetStorageId(PersistenceType aPersistenceType, const nsACString& aOrigin, Client::Type aClientType, - const nsAString& aName, nsACString& aDatabaseId) { nsAutoCString str; @@ -2372,8 +3411,6 @@ QuotaManager::GetStorageId(PersistenceType aPersistenceType, str.Append(aOrigin); str.Append('*'); str.AppendInt(aClientType); - str.Append('*'); - AppendUTF16toUTF8(aName, str); aDatabaseId = str; } @@ -2634,6 +3671,52 @@ QuotaManager::ChromeOrigin(nsACString& aOrigin) aOrigin.AssignLiteral(kChromeOrigin); } +// static +nsresult +QuotaManager::GetDirectoryMetadata(nsIFile* aDirectory, + int64_t* aTimestamp, + nsACString& aGroup, + nsACString& aOrigin, + bool* aIsApp) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(aDirectory); + MOZ_ASSERT(aTimestamp); + + nsCOMPtr binaryStream; + nsresult rv = + GetDirectoryMetadataInputStream(aDirectory, getter_AddRefs(binaryStream)); + NS_ENSURE_SUCCESS(rv, rv); + + uint64_t timestamp; + rv = binaryStream->Read64(×tamp); + NS_ENSURE_SUCCESS(rv, rv); + + nsCString group; + rv = binaryStream->ReadCString(group); + NS_ENSURE_SUCCESS(rv, rv); + + nsCString origin; + rv = binaryStream->ReadCString(origin); + NS_ENSURE_SUCCESS(rv, rv); + + bool isApp; + if (aIsApp) { + rv = binaryStream->ReadBoolean(&isApp); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + *aTimestamp = timestamp; + aGroup = group; + aOrigin = origin; + if (aIsApp) { + *aIsApp = isApp; + } + return NS_OK; +} + NS_IMPL_ISUPPORTS(QuotaManager, nsIQuotaManager, nsIObserver) NS_IMETHODIMP @@ -2650,7 +3733,7 @@ QuotaManager::GetUsageForURI(nsIURI* aURI, NS_ENSURE_ARG_POINTER(aCallback); // This only works from the main process. - NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE); + NS_ENSURE_TRUE(XRE_IsParentProcess(), NS_ERROR_NOT_AVAILABLE); if (!aOptionalArgCount) { aAppId = nsIScriptSecurityManager::NO_APP_ID; @@ -2664,20 +3747,13 @@ QuotaManager::GetUsageForURI(nsIURI* aURI, &isApp); NS_ENSURE_SUCCESS(rv, rv); - OriginOrPatternString oops = OriginOrPatternString::FromOrigin(origin); + nsRefPtr op = + new GetUsageOp(group, origin, isApp, aURI, aCallback, aAppId, + aInMozBrowserOnly); - nsRefPtr runnable = - new AsyncUsageRunnable(aAppId, aInMozBrowserOnly, group, oops, isApp, aURI, - aCallback); + op->RunImmediately(); - // Put the computation runnable in the queue. - rv = WaitForOpenAllowed(oops, Nullable(), EmptyCString(), - runnable); - NS_ENSURE_SUCCESS(rv, rv); - - runnable->AdvanceState(); - - runnable.forget(_retval); + op.forget(_retval); return NS_OK; } @@ -2691,31 +3767,10 @@ QuotaManager::Clear() return NS_OK; } - OriginOrPatternString oops = OriginOrPatternString::FromNull(); + nsRefPtr op = new ResetOrClearOp(/* aClear */ true); - nsRefPtr runnable = new ResetOrClearRunnable(true); + op->RunImmediately(); - // Put the clear runnable in the queue. - nsresult rv = - WaitForOpenAllowed(oops, Nullable(), EmptyCString(), - runnable); - NS_ENSURE_SUCCESS(rv, rv); - - runnable->AdvanceState(); - - // Give the runnable some help by invalidating any storages in the way. - StorageMatcher > matches; - matches.Find(mLiveStorages); - - for (uint32_t index = 0; index < matches.Length(); index++) { - // We need to grab references to any live storages here to prevent them - // from dying while we invalidate them. - nsCOMPtr storage = matches[index]; - storage->Invalidate(); - } - - // After everything has been invalidated the helper should be dispatched to - // the end of the event queue. return NS_OK; } @@ -2738,7 +3793,7 @@ QuotaManager::ClearStoragesForURI(nsIURI* aURI, } // This only works from the main process. - NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE); + NS_ENSURE_TRUE(XRE_IsParentProcess(), NS_ERROR_NOT_AVAILABLE); if (!aOptionalArgCount) { aAppId = nsIScriptSecurityManager::NO_APP_ID; @@ -2753,39 +3808,11 @@ QuotaManager::ClearStoragesForURI(nsIURI* aURI, nsAutoCString pattern; GetOriginPatternString(aAppId, aInMozBrowserOnly, origin, pattern); - // If there is a pending or running clear operation for this origin, return - // immediately. - if (IsClearOriginPending(pattern, persistenceType)) { - return NS_OK; - } + nsRefPtr op = + new OriginClearOp(persistenceType, OriginScope::FromPattern(pattern)); - OriginOrPatternString oops = OriginOrPatternString::FromPattern(pattern); + op->RunImmediately(); - // Queue up the origin clear runnable. - nsRefPtr runnable = - new OriginClearRunnable(oops, persistenceType); - - rv = WaitForOpenAllowed(oops, persistenceType, EmptyCString(), runnable); - NS_ENSURE_SUCCESS(rv, rv); - - runnable->AdvanceState(); - - // Give the runnable some help by invalidating any storages in the way. - StorageMatcher > matches; - matches.Find(mLiveStorages, pattern); - - for (uint32_t index = 0; index < matches.Length(); index++) { - if (persistenceType.IsNull() || - matches[index]->Type() == persistenceType.Value()) { - // We need to grab references to any live storages here to prevent them - // from dying while we invalidate them. - nsCOMPtr storage = matches[index]; - storage->Invalidate(); - } - } - - // After everything has been invalidated the helper should be dispatched to - // the end of the event queue. return NS_OK; } @@ -2799,31 +3826,10 @@ QuotaManager::Reset() return NS_OK; } - OriginOrPatternString oops = OriginOrPatternString::FromNull(); + nsRefPtr op = new ResetOrClearOp(/* aClear */ false); - nsRefPtr runnable = new ResetOrClearRunnable(false); + op->RunImmediately(); - // Put the reset runnable in the queue. - nsresult rv = - WaitForOpenAllowed(oops, Nullable(), EmptyCString(), - runnable); - NS_ENSURE_SUCCESS(rv, rv); - - runnable->AdvanceState(); - - // Give the runnable some help by invalidating any storages in the way. - StorageMatcher > matches; - matches.Find(mLiveStorages); - - for (uint32_t index = 0; index < matches.Length(); index++) { - // We need to grab references to any live storages here to prevent them - // from dying while we invalidate them. - nsCOMPtr storage = matches[index]; - storage->Invalidate(); - } - - // After everything has been invalidated the helper should be dispatched to - // the end of the event queue. return NS_OK; } @@ -2841,7 +3847,7 @@ QuotaManager::Observe(nsISupports* aSubject, NS_ERROR("Shutdown more than once?!"); } - if (IsMainProcess()) { + if (XRE_IsParentProcess()) { // Kick off the shutdown timer. if (NS_FAILED(mShutdownTimer->Init(this, DEFAULT_SHUTDOWN_TIMER_MS, nsITimer::TYPE_ONE_SHOT))) { @@ -2874,27 +3880,24 @@ QuotaManager::Observe(nsISupports* aSubject, if (NS_FAILED(mIOThread->Shutdown())) { NS_WARNING("Failed to shutdown IO thread!"); } + + for (nsRefPtr& lock : mPendingDirectoryLocks) { + lock->Invalidate(); + } } return NS_OK; } if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) { - NS_ASSERTION(IsMainProcess(), "Should only happen in the main process!"); + NS_ASSERTION(XRE_IsParentProcess(), "Should only happen in the main process!"); NS_WARNING("Some storage operations are taking longer than expected " "during shutdown and will be aborted!"); - // Grab all live storages, for all origins. - StorageMatcher > liveStorages; - liveStorages.Find(mLiveStorages); - - // Invalidate them all. - if (!liveStorages.IsEmpty()) { - uint32_t count = liveStorages.Length(); - for (uint32_t index = 0; index < count; index++) { - liveStorages[index]->Invalidate(); - } + // Abort all operations. + for (nsRefPtr& client : mClients) { + client->AbortOperations(NullCString()); } return NS_OK; @@ -2919,14 +3922,21 @@ QuotaManager::Observe(nsISupports* aSubject, return NS_OK; } + if (!strcmp(aTopic, OBSERVER_TOPIC_IDLE_DAILY)) { + for (auto& client : mClients) { + client->PerformIdleMaintenance(); + } + return NS_OK; + } + NS_NOTREACHED("Unknown topic!"); return NS_ERROR_UNEXPECTED; } uint64_t QuotaManager::LockedCollectOriginsForEviction( - uint64_t aMinSizeToBeFreed, - nsTArray& aOriginInfos) + uint64_t aMinSizeToBeFreed, + nsTArray>& aLocks) { mQuotaMutex.AssertCurrentThreadOwns(); @@ -2940,12 +3950,10 @@ QuotaManager::LockedCollectOriginsForEviction( { MutexAutoUnlock autoUnlock(mQuotaMutex); - if (NS_FAILED(NS_DispatchToMainThread(helper))) { - NS_WARNING("Failed to dispatch to the main thread!"); - } + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(helper))); } - return helper->BlockAndReturnOriginsForEviction(aOriginInfos); + return helper->BlockAndReturnOriginsForEviction(aLocks); } void @@ -2977,89 +3985,6 @@ QuotaManager::LockedRemoveQuotaForOrigin(PersistenceType aPersistenceType, } } -nsresult -QuotaManager::AcquireExclusiveAccess(const nsACString& aPattern, - Nullable aPersistenceType, - nsIRunnable* aRunnable) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aRunnable, "Need a runnable!"); - - // Find the right SynchronizedOp. - SynchronizedOp* op = - FindSynchronizedOp(aPattern, aPersistenceType, EmptyCString()); - - NS_ASSERTION(op, "We didn't find a SynchronizedOp?"); - NS_ASSERTION(!op->mRunnable, "SynchronizedOp already has a runnable?!?"); - - ArrayCluster liveStorages; - - StorageMatcher > matches; - if (aPattern.IsVoid()) { - matches.Find(mLiveStorages); - } - else { - matches.Find(mLiveStorages, aPattern); - } - - // We want *all* storages that match the given persistence type, even those - // that are closed, when we're going to clear the origin. - if (!matches.IsEmpty()) { - for (uint32_t i = 0; i < Client::TYPE_MAX; i++) { - nsTArray& storages = matches.ArrayAt(i); - for (uint32_t j = 0; j < storages.Length(); j++) { - nsIOfflineStorage* storage = storages[j]; - if (aPersistenceType.IsNull() || - aPersistenceType.Value() == storage->Type()) { - storage->Invalidate(); - liveStorages[i].AppendElement(storage); - } - } - } - } - - op->mRunnable = aRunnable; - - nsRefPtr runnable = - new WaitForTransactionsToFinishRunnable(op); - - if (!liveStorages.IsEmpty()) { - // Ask each transaction service to call us back when they're done with this - // storage. - for (uint32_t index = 0; index < Client::TYPE_MAX; index++) { - nsRefPtr& client = mClients[index]; - if (!liveStorages[index].IsEmpty()) { - runnable->AddRun(); - - client->WaitForStoragesToComplete(liveStorages[index], runnable); - } - } - } - - nsresult rv = runnable->Run(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -SynchronizedOp* -QuotaManager::FindSynchronizedOp(const nsACString& aPattern, - Nullable aPersistenceType, - const nsACString& aId) -{ - for (uint32_t index = 0; index < mSynchronizedOps.Length(); index++) { - const nsAutoPtr& currentOp = mSynchronizedOps[index]; - if (PatternMatchesOrigin(aPattern, currentOp->mOriginOrPattern) && - (currentOp->mPersistenceType.IsNull() || - currentOp->mPersistenceType == aPersistenceType) && - (currentOp->mId.IsEmpty() || currentOp->mId == aId)) { - return currentOp; - } - } - - return nullptr; -} - nsresult QuotaManager::ClearStoragesForApp(uint32_t aAppId, bool aBrowserOnly) { @@ -3067,42 +3992,16 @@ QuotaManager::ClearStoragesForApp(uint32_t aAppId, bool aBrowserOnly) NS_ASSERTION(aAppId != kUnknownAppId, "Bad appId!"); // This only works from the main process. - NS_ENSURE_TRUE(IsMainProcess(), NS_ERROR_NOT_AVAILABLE); + NS_ENSURE_TRUE(XRE_IsParentProcess(), NS_ERROR_NOT_AVAILABLE); nsAutoCString pattern; GetOriginPatternStringMaybeIgnoreBrowser(aAppId, aBrowserOnly, pattern); - // Clear both temporary and persistent storages. - Nullable persistenceType; + nsRefPtr op = + new OriginClearOp(Nullable(), + OriginScope::FromPattern(pattern)); - // If there is a pending or running clear operation for this app, return - // immediately. - if (IsClearOriginPending(pattern, persistenceType)) { - return NS_OK; - } - - OriginOrPatternString oops = OriginOrPatternString::FromPattern(pattern); - - // Queue up the origin clear runnable. - nsRefPtr runnable = - new OriginClearRunnable(oops, persistenceType); - - nsresult rv = - WaitForOpenAllowed(oops, persistenceType, EmptyCString(), runnable); - NS_ENSURE_SUCCESS(rv, rv); - - runnable->AdvanceState(); - - // Give the runnable some help by invalidating any storages in the way. - StorageMatcher > matches; - matches.Find(mLiveStorages, pattern); - - for (uint32_t index = 0; index < matches.Length(); index++) { - // We need to grab references here to prevent the storage from dying while - // we invalidate it. - nsCOMPtr storage = matches[index]; - storage->Invalidate(); - } + op->RunImmediately(); return NS_OK; } @@ -3262,202 +4161,13 @@ QuotaManager::CheckTemporaryStorageLimits() } } - for (uint32_t index = 0; index < doomedOrigins.Length(); index++) { - const OriginParams& doomedOrigin = doomedOrigins[index]; - - OriginClearCompleted( - doomedOrigin.mPersistenceType, - OriginOrPatternString::FromOrigin(doomedOrigin.mOrigin), - doomedOrigin.mIsApp); + for (const OriginParams& doomedOrigin : doomedOrigins) { + OriginClearCompleted(doomedOrigin.mPersistenceType, + doomedOrigin.mOrigin, + doomedOrigin.mIsApp); } } -// static -PLDHashOperator -QuotaManager::AddLiveStorageOrigins(const nsACString& aKey, - nsTArray* aValue, - void* aUserArg) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!aKey.IsEmpty(), "Empty key!"); - NS_ASSERTION(aValue, "Null pointer!"); - NS_ASSERTION(aUserArg, "Null pointer!"); - - OriginCollection& collection = *static_cast(aUserArg); - - if (!collection.ContainsOrigin(aKey)) { - collection.AddOrigin(aKey); - } - - return PL_DHASH_NEXT; -} - -// static -PLDHashOperator -QuotaManager::GetInactiveTemporaryStorageOrigins(const nsACString& aKey, - GroupInfoPair* aValue, - void* aUserArg) -{ - NS_ASSERTION(!aKey.IsEmpty(), "Empty key!"); - NS_ASSERTION(aValue, "Null pointer!"); - NS_ASSERTION(aUserArg, "Null pointer!"); - - InactiveOriginsInfo* info = static_cast(aUserArg); - - nsRefPtr groupInfo = - aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_TEMPORARY); - if (groupInfo) { - nsTArray >& originInfos = groupInfo->mOriginInfos; - - for (uint32_t i = 0; i < originInfos.Length(); i++) { - OriginInfo* originInfo = originInfos[i]; - - MOZ_ASSERT(IsTreatedAsTemporary(originInfo->mGroupInfo->mPersistenceType, - originInfo->mIsApp)); - - if (!info->persistentCollection.ContainsOrigin(originInfo->mOrigin)) { - NS_ASSERTION(!originInfo->mQuotaObjects.Count(), - "Inactive origin shouldn't have open files!"); - info->origins.AppendElement(originInfo); - } - } - } - - groupInfo = aValue->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT); - if (groupInfo) { - nsTArray >& originInfos = groupInfo->mOriginInfos; - - for (uint32_t i = 0; i < originInfos.Length(); i++) { - OriginInfo* originInfo = originInfos[i]; - - MOZ_ASSERT(IsTreatedAsTemporary(originInfo->mGroupInfo->mPersistenceType, - originInfo->mIsApp)); - - if (!info->temporaryCollection.ContainsOrigin(originInfo->mOrigin)) { - NS_ASSERTION(!originInfo->mQuotaObjects.Count(), - "Inactive origin shouldn't have open files!"); - info->origins.AppendElement(originInfo); - } - } - } - - return PL_DHASH_NEXT; -} - -uint64_t -QuotaManager::CollectOriginsForEviction(uint64_t aMinSizeToBeFreed, - nsTArray& aOriginInfos) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - // Collect active origins first. - OriginCollection temporaryOriginCollection; - OriginCollection defaultOriginCollection; - - // Add patterns and origins that have running or pending synchronized ops. - // (add patterns first to reduce redundancy in the origin collection). - uint32_t index; - for (index = 0; index < mSynchronizedOps.Length(); index++) { - nsAutoPtr& op = mSynchronizedOps[index]; - - const OriginOrPatternString& originOrPattern = op->mOriginOrPattern; - - if (!originOrPattern.IsPattern()) { - continue; - } - - Nullable& persistenceType = op->mPersistenceType; - - if (persistenceType.IsNull()) { - temporaryOriginCollection.AddPattern(originOrPattern); - defaultOriginCollection.AddPattern(originOrPattern); - } else if (persistenceType.Value() == PERSISTENCE_TYPE_TEMPORARY) { - temporaryOriginCollection.AddPattern(originOrPattern); - } else if (persistenceType.Value() == PERSISTENCE_TYPE_DEFAULT) { - defaultOriginCollection.AddPattern(originOrPattern); - } - } - - for (index = 0; index < mSynchronizedOps.Length(); index++) { - nsAutoPtr& op = mSynchronizedOps[index]; - - const OriginOrPatternString& originOrPattern = op->mOriginOrPattern; - - if (!originOrPattern.IsOrigin()) { - continue; - } - - Nullable& persistenceType = op->mPersistenceType; - - if (persistenceType.IsNull()) { - temporaryOriginCollection.AddOrigin(originOrPattern); - defaultOriginCollection.AddOrigin(originOrPattern); - } else if (persistenceType.Value() == PERSISTENCE_TYPE_TEMPORARY) { - temporaryOriginCollection.AddOrigin(originOrPattern); - } else if (persistenceType.Value() == PERSISTENCE_TYPE_DEFAULT) { - defaultOriginCollection.AddOrigin(originOrPattern); - } - } - - // Add origins that have live temporary storages. - mTemporaryLiveStorageTable.EnumerateRead(AddLiveStorageOrigins, - &temporaryOriginCollection); - - // Add origins that have live persistent storages. - mDefaultLiveStorageTable.EnumerateRead(AddLiveStorageOrigins, - &defaultOriginCollection); - - // Enumerate inactive origins. This must be protected by the mutex. - nsTArray inactiveOrigins; - { - InactiveOriginsInfo info(temporaryOriginCollection, - defaultOriginCollection, - inactiveOrigins); - MutexAutoLock lock(mQuotaMutex); - mGroupInfoPairs.EnumerateRead(GetInactiveTemporaryStorageOrigins, &info); - } - - // We now have a list of all inactive origins. So it's safe to sort the list - // and calculate available size without holding the lock. - - // Sort by the origin access time. - inactiveOrigins.Sort(OriginInfoLRUComparator()); - - // Create a list of inactive and the least recently used origins - // whose aggregate size is greater or equals the minimal size to be freed. - uint64_t sizeToBeFreed = 0; - for(index = 0; index < inactiveOrigins.Length(); index++) { - if (sizeToBeFreed >= aMinSizeToBeFreed) { - inactiveOrigins.TruncateLength(index); - break; - } - - sizeToBeFreed += inactiveOrigins[index]->mUsage; - } - - if (sizeToBeFreed >= aMinSizeToBeFreed) { - // Success, add synchronized ops for these origins, so any other - // operations for them will be delayed (until origin eviction is finalized). - - for(index = 0; index < inactiveOrigins.Length(); index++) { - OriginInfo* inactiveOrigin = inactiveOrigins[index]; - - OriginOrPatternString oops = - OriginOrPatternString::FromOrigin(inactiveOrigin->mOrigin); - - Nullable persistenceType = - Nullable(inactiveOrigin->mGroupInfo->mPersistenceType); - - AddSynchronizedOp(oops, persistenceType); - } - - inactiveOrigins.SwapElements(aOriginInfos); - return sizeToBeFreed; - } - - return 0; -} - void QuotaManager::DeleteFilesForOrigin(PersistenceType aPersistenceType, const nsACString& aOrigin) @@ -3477,34 +4187,18 @@ QuotaManager::DeleteFilesForOrigin(PersistenceType aPersistenceType, } void -QuotaManager::FinalizeOriginEviction(nsTArray& aOrigins) +QuotaManager::FinalizeOriginEviction( + nsTArray>& aLocks) { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - nsRefPtr runnable = - new FinalizeOriginEvictionRunnable(aOrigins); + nsRefPtr op = + new FinalizeOriginEvictionOp(aLocks); - nsresult rv = IsOnIOThread() ? runnable->RunImmediately() - : runnable->Dispatch(); - NS_ENSURE_SUCCESS_VOID(rv); -} - -void -QuotaManager::SaveOriginAccessTime(PersistenceType aPersistenceType, - const nsACString& aOrigin, - int64_t aTimestamp) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (QuotaManager::IsShuttingDown()) { - return; - } - - nsRefPtr runnable = - new SaveOriginAccessTimeRunnable(aPersistenceType, aOrigin, aTimestamp); - - if (NS_FAILED(mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL))) { - NS_WARNING("Failed to dispatch runnable!"); + if (IsOnIOThread()) { + op->RunOnIOThreadImmediately(); + } else { + op->Dispatch(); } } @@ -3551,14 +4245,14 @@ QuotaManager::GetOriginPatternString(uint32_t aAppId, } auto -QuotaManager::GetLiveStorageTable(PersistenceType aPersistenceType) - -> LiveStorageTable& +QuotaManager::GetDirectoryLockTable(PersistenceType aPersistenceType) + -> DirectoryLockTable& { switch (aPersistenceType) { case PERSISTENCE_TYPE_TEMPORARY: - return mTemporaryLiveStorageTable; + return mTemporaryDirectoryLockTable; case PERSISTENCE_TYPE_DEFAULT: - return mDefaultLiveStorageTable; + return mDefaultDirectoryLockTable; case PERSISTENCE_TYPE_PERSISTENT: case PERSISTENCE_TYPE_INVALID: @@ -3567,99 +4261,121 @@ QuotaManager::GetLiveStorageTable(PersistenceType aPersistenceType) } } -SynchronizedOp::SynchronizedOp(const OriginOrPatternString& aOriginOrPattern, - Nullable aPersistenceType, - const nsACString& aId) -: mOriginOrPattern(aOriginOrPattern), mPersistenceType(aPersistenceType), - mId(aId) +/******************************************************************************* + * Local class implementations + ******************************************************************************/ + +void +OriginInfo::LockedDecreaseUsage(int64_t aSize) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - MOZ_COUNT_CTOR(SynchronizedOp); + AssertCurrentThreadOwnsQuotaMutex(); + + AssertNoUnderflow(mUsage, aSize); + mUsage -= aSize; + + AssertNoUnderflow(mGroupInfo->mUsage, aSize); + mGroupInfo->mUsage -= aSize; + + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, aSize); + quotaManager->mTemporaryStorageUsage -= aSize; } -SynchronizedOp::~SynchronizedOp() +already_AddRefed +GroupInfo::LockedGetOriginInfo(const nsACString& aOrigin) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - MOZ_COUNT_DTOR(SynchronizedOp); -} + AssertCurrentThreadOwnsQuotaMutex(); -bool -SynchronizedOp::MustWaitFor(const SynchronizedOp& aExistingOp) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - if (aExistingOp.mOriginOrPattern.IsNull() || mOriginOrPattern.IsNull()) { - return true; - } - - bool match; - - if (aExistingOp.mOriginOrPattern.IsOrigin()) { - if (mOriginOrPattern.IsOrigin()) { - match = aExistingOp.mOriginOrPattern.Equals(mOriginOrPattern); - } - else { - match = PatternMatchesOrigin(mOriginOrPattern, aExistingOp.mOriginOrPattern); + for (nsRefPtr& originInfo : mOriginInfos) { + if (originInfo->mOrigin == aOrigin) { + nsRefPtr result = originInfo; + return result.forget(); } } - else if (mOriginOrPattern.IsOrigin()) { - match = PatternMatchesOrigin(aExistingOp.mOriginOrPattern, mOriginOrPattern); - } - else { - match = PatternMatchesOrigin(mOriginOrPattern, aExistingOp.mOriginOrPattern) || - PatternMatchesOrigin(aExistingOp.mOriginOrPattern, mOriginOrPattern); - } - // If the origins don't match, the second can proceed. - if (!match) { - return false; - } - - // If the origins match but the persistence types are different, the second - // can proceed. - if (!aExistingOp.mPersistenceType.IsNull() && !mPersistenceType.IsNull() && - aExistingOp.mPersistenceType.Value() != mPersistenceType.Value()) { - return false; - } - - // If the origins and the ids match, the second must wait. - if (aExistingOp.mId == mId) { - return true; - } - - // Waiting is required if either one corresponds to an origin clearing - // (an empty Id). - if (aExistingOp.mId.IsEmpty() || mId.IsEmpty()) { - return true; - } - - // Otherwise, things for the same origin but different storages can proceed - // independently. - return false; + return nullptr; } void -SynchronizedOp::DelayRunnable(nsIRunnable* aRunnable) +GroupInfo::LockedAddOriginInfo(OriginInfo* aOriginInfo) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(mDelayedRunnables.IsEmpty() || mId.IsEmpty(), - "Only ClearOrigin operations can delay multiple runnables!"); + AssertCurrentThreadOwnsQuotaMutex(); - mDelayedRunnables.AppendElement(aRunnable); + NS_ASSERTION(!mOriginInfos.Contains(aOriginInfo), + "Replacing an existing entry!"); + mOriginInfos.AppendElement(aOriginInfo); + + AssertNoOverflow(mUsage, aOriginInfo->mUsage); + mUsage += aOriginInfo->mUsage; + + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + AssertNoOverflow(quotaManager->mTemporaryStorageUsage, aOriginInfo->mUsage); + quotaManager->mTemporaryStorageUsage += aOriginInfo->mUsage; } void -SynchronizedOp::DispatchDelayedRunnables() +GroupInfo::LockedRemoveOriginInfo(const nsACString& aOrigin) { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!mRunnable, "Any runnable should be gone by now!"); + AssertCurrentThreadOwnsQuotaMutex(); - uint32_t count = mDelayedRunnables.Length(); - for (uint32_t index = 0; index < count; index++) { - NS_DispatchToCurrentThread(mDelayedRunnables[index]); + for (uint32_t index = 0; index < mOriginInfos.Length(); index++) { + if (mOriginInfos[index]->mOrigin == aOrigin) { + AssertNoUnderflow(mUsage, mOriginInfos[index]->mUsage); + mUsage -= mOriginInfos[index]->mUsage; + + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, + mOriginInfos[index]->mUsage); + quotaManager->mTemporaryStorageUsage -= mOriginInfos[index]->mUsage; + + mOriginInfos.RemoveElementAt(index); + + return; + } } +} - mDelayedRunnables.Clear(); +void +GroupInfo::LockedRemoveOriginInfos() +{ + AssertCurrentThreadOwnsQuotaMutex(); + + QuotaManager* quotaManager = QuotaManager::Get(); + MOZ_ASSERT(quotaManager); + + for (uint32_t index = mOriginInfos.Length(); index > 0; index--) { + OriginInfo* originInfo = mOriginInfos[index - 1]; + + AssertNoUnderflow(mUsage, originInfo->mUsage); + mUsage -= originInfo->mUsage; + + AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, originInfo->mUsage); + quotaManager->mTemporaryStorageUsage -= originInfo->mUsage; + + mOriginInfos.RemoveElementAt(index - 1); + } +} + +nsRefPtr& +GroupInfoPair::GetGroupInfoForPersistenceType(PersistenceType aPersistenceType) +{ + switch (aPersistenceType) { + case PERSISTENCE_TYPE_TEMPORARY: + return mTemporaryStorageGroupInfo; + case PERSISTENCE_TYPE_DEFAULT: + return mDefaultStorageGroupInfo; + + case PERSISTENCE_TYPE_PERSISTENT: + case PERSISTENCE_TYPE_INVALID: + default: + MOZ_CRASH("Bad persistence type value!"); + } } CollectOriginsHelper::CollectOriginsHelper(mozilla::Mutex& aMutex, @@ -3676,7 +4392,7 @@ CollectOriginsHelper::CollectOriginsHelper(mozilla::Mutex& aMutex, int64_t CollectOriginsHelper::BlockAndReturnOriginsForEviction( - nsTArray& aOriginInfos) + nsTArray>& aLocks) { MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!"); mMutex.AssertCurrentThreadOwns(); @@ -3685,7 +4401,7 @@ CollectOriginsHelper::BlockAndReturnOriginsForEviction( mCondVar.Wait(); } - mOriginInfos.SwapElements(aOriginInfos); + mLocks.SwapElements(aLocks); return mSizeToBeFreed; } @@ -3699,15 +4415,15 @@ CollectOriginsHelper::Run() // We use extra stack vars here to avoid race detector warnings (the same // memory accessed with and without the lock held). - nsTArray originInfos; + nsTArray> locks; uint64_t sizeToBeFreed = - quotaManager->CollectOriginsForEviction(mMinSizeToBeFreed, originInfos); + quotaManager->CollectOriginsForEviction(mMinSizeToBeFreed, locks); MutexAutoLock lock(mMutex); NS_ASSERTION(mWaiting, "Huh?!"); - mOriginInfos.SwapElements(originInfos); + mLocks.SwapElements(locks); mSizeToBeFreed = sizeToBeFreed; mWaiting = false; mCondVar.Notify(); @@ -3715,291 +4431,241 @@ CollectOriginsHelper::Run() return NS_OK; } -void -OriginClearRunnable::DeleteFiles(QuotaManager* aQuotaManager, - PersistenceType aPersistenceType) +NS_IMETHODIMP +OriginOperationBase::Run() { - AssertIsOnIOThread(); - NS_ASSERTION(aQuotaManager, "Don't pass me null!"); - nsresult rv; - nsCOMPtr directory = - do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS_VOID(rv); - - rv = directory->InitWithPath(aQuotaManager->GetStoragePath(aPersistenceType)); - NS_ENSURE_SUCCESS_VOID(rv); - - nsCOMPtr entries; - if (NS_FAILED(directory->GetDirectoryEntries(getter_AddRefs(entries))) || - !entries) { - return; - } - - nsCString originSanitized(mOriginOrPattern); - SanitizeOriginString(originSanitized); - - bool hasMore; - while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) { - nsCOMPtr entry; - rv = entries->GetNext(getter_AddRefs(entry)); - NS_ENSURE_SUCCESS_VOID(rv); - - nsCOMPtr file = do_QueryInterface(entry); - NS_ASSERTION(file, "Don't know what this is!"); - - nsString leafName; - rv = file->GetLeafName(leafName); - NS_ENSURE_SUCCESS_VOID(rv); - - bool isDirectory; - rv = file->IsDirectory(&isDirectory); - NS_ENSURE_SUCCESS_VOID(rv); - - if (!isDirectory) { - if (!leafName.EqualsLiteral(DSSTORE_FILE_NAME)) { - NS_WARNING("Something in the IndexedDB directory that doesn't belong!"); - } - continue; + switch (mState) { + case State_Initial: { + rv = Open(); + break; } - // Skip storages for other apps. - if (!PatternMatchesOrigin(originSanitized, - NS_ConvertUTF16toUTF8(leafName))) { - continue; + case State_DirectoryOpenPending: { + rv = DirectoryOpen(); + break; } - int64_t timestamp; - nsCString group; - nsCString origin; - bool isApp; - rv = GetDirectoryMetadata(file, ×tamp, group, origin, &isApp); - if (NS_WARN_IF(NS_FAILED(rv))) { - return; + case State_DirectoryWorkOpen: { + rv = DirectoryWork(); + break; } - for (uint32_t index = 0; index < 10; index++) { - // We can't guarantee that this will always succeed on Windows... - if (NS_SUCCEEDED((rv = file->Remove(true)))) { - break; - } - - NS_WARNING("Failed to remove directory, retrying after a short delay."); - - PR_Sleep(PR_MillisecondsToInterval(200)); - } - - if (NS_FAILED(rv)) { - NS_WARNING("Failed to remove directory, giving up!"); - } - - if (aPersistenceType != PERSISTENCE_TYPE_PERSISTENT) { - aQuotaManager->RemoveQuotaForOrigin(aPersistenceType, group, origin); - } - - aQuotaManager->OriginClearCompleted(aPersistenceType, origin, isApp); - } -} - -NS_IMPL_ISUPPORTS_INHERITED0(OriginClearRunnable, nsRunnable) - -NS_IMETHODIMP -OriginClearRunnable::Run() -{ - PROFILER_LABEL("OriginClearRunnable", "Run", - js::ProfileEntry::Category::OTHER); - - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "This should never fail!"); - - switch (mCallbackState) { - case Pending: { - NS_NOTREACHED("Should never get here without being dispatched!"); - return NS_ERROR_UNEXPECTED; - } - - case OpenAllowed: { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - AdvanceState(); - - // Now we have to wait until the thread pool is done with all of the - // storages we care about. - nsresult rv = - quotaManager->AcquireExclusiveAccess(mOriginOrPattern, mPersistenceType, - this); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; - } - - case IO: { - AssertIsOnIOThread(); - - AdvanceState(); - - if (mPersistenceType.IsNull()) { - DeleteFiles(quotaManager, PERSISTENCE_TYPE_PERSISTENT); - DeleteFiles(quotaManager, PERSISTENCE_TYPE_TEMPORARY); - DeleteFiles(quotaManager, PERSISTENCE_TYPE_DEFAULT); - } else { - DeleteFiles(quotaManager, mPersistenceType.Value()); - } - - // Now dispatch back to the main thread. - if (NS_FAILED(NS_DispatchToMainThread(this))) { - NS_WARNING("Failed to dispatch to main thread!"); - return NS_ERROR_FAILURE; - } - - return NS_OK; - } - - case Complete: { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - // Tell the QuotaManager that we're done. - quotaManager->AllowNextSynchronizedOp(mOriginOrPattern, mPersistenceType, - EmptyCString()); - + case State_UnblockingOpen: { + UnblockOpen(); return NS_OK; } default: - NS_ERROR("Unknown state value!"); - return NS_ERROR_UNEXPECTED; + MOZ_CRASH("Bad state!"); } - NS_NOTREACHED("Should never get here!"); - return NS_ERROR_UNEXPECTED; -} + if (NS_WARN_IF(NS_FAILED(rv)) && mState != State_UnblockingOpen) { + Finish(rv); + } -AsyncUsageRunnable::AsyncUsageRunnable(uint32_t aAppId, - bool aInMozBrowserOnly, - const nsACString& aGroup, - const OriginOrPatternString& aOrigin, - bool aIsApp, - nsIURI* aURI, - nsIUsageCallback* aCallback) -: mURI(aURI), - mCallback(aCallback), - mAppId(aAppId), - mGroup(aGroup), - mOrigin(aOrigin), - mCallbackState(Pending), - mInMozBrowserOnly(aInMozBrowserOnly), - mIsApp(aIsApp) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aURI, "Null pointer!"); - NS_ASSERTION(!aGroup.IsEmpty(), "Empty group!"); - NS_ASSERTION(aOrigin.IsOrigin(), "Expect origin only here!"); - NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!"); - NS_ASSERTION(aCallback, "Null pointer!"); -} - -nsresult -AsyncUsageRunnable::TakeShortcut() -{ - NS_ASSERTION(mCallbackState == Pending, "Huh?"); - - nsresult rv = NS_DispatchToCurrentThread(this); - NS_ENSURE_SUCCESS(rv, rv); - - mCallbackState = Shortcut; return NS_OK; } nsresult -AsyncUsageRunnable::RunInternal() +OriginOperationBase::DirectoryOpen() { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_DirectoryOpenPending); + QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "This should never fail!"); - - nsresult rv; - - switch (mCallbackState) { - case Pending: { - NS_NOTREACHED("Should never get here without being dispatched!"); - return NS_ERROR_UNEXPECTED; - } - - case OpenAllowed: { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - AdvanceState(); - - rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to dispatch to the IO thread!"); - } - - return NS_OK; - } - - case IO: { - AssertIsOnIOThread(); - - AdvanceState(); - - // Add all the persistent storage files we care about. - rv = AddToUsage(quotaManager, PERSISTENCE_TYPE_PERSISTENT); - NS_ENSURE_SUCCESS(rv, rv); - - // Add all the temporary storage files we care about. - rv = AddToUsage(quotaManager, PERSISTENCE_TYPE_TEMPORARY); - NS_ENSURE_SUCCESS(rv, rv); - - // Add all the default storage files we care about. - rv = AddToUsage(quotaManager, PERSISTENCE_TYPE_DEFAULT); - NS_ENSURE_SUCCESS(rv, rv); - - // Run dispatches us back to the main thread. - return NS_OK; - } - - case Complete: // Fall through - case Shortcut: { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - // Call the callback unless we were canceled. - if (!mCanceled) { - mCallback->OnUsageResult(mURI, TotalUsage(), FileUsage(), mAppId, - mInMozBrowserOnly); - } - - // Clean up. - mURI = nullptr; - mCallback = nullptr; - - // And tell the QuotaManager that we're done. - if (mCallbackState == Complete) { - quotaManager->AllowNextSynchronizedOp(mOrigin, - Nullable(), - EmptyCString()); - } - - return NS_OK; - } - - default: - NS_ERROR("Unknown state value!"); - return NS_ERROR_UNEXPECTED; + if (NS_WARN_IF(!quotaManager)) { + return NS_ERROR_FAILURE; } - NS_NOTREACHED("Should never get here!"); - return NS_ERROR_UNEXPECTED; + // Must set this before dispatching otherwise we will race with the IO thread. + AdvanceState(); + + nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +void +OriginOperationBase::Finish(nsresult aResult) +{ + if (NS_SUCCEEDED(mResultCode)) { + mResultCode = aResult; + } + + // Must set mState before dispatching otherwise we will race with the main + // thread. + mState = State_UnblockingOpen; + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); } nsresult -AsyncUsageRunnable::AddToUsage(QuotaManager* aQuotaManager, - PersistenceType aPersistenceType) +OriginOperationBase::DirectoryWork() +{ + AssertIsOnIOThread(); + MOZ_ASSERT(mState == State_DirectoryWorkOpen); + + QuotaManager* quotaManager = QuotaManager::Get(); + if (NS_WARN_IF(!quotaManager)) { + return NS_ERROR_FAILURE; + } + + nsresult rv = DoDirectoryWork(quotaManager); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Must set mState before dispatching otherwise we will race with the main + // thread. + AdvanceState(); + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); + + return NS_OK; +} + +NS_IMPL_ISUPPORTS_INHERITED0(NormalOriginOperationBase, nsRunnable) + +nsresult +NormalOriginOperationBase::Open() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_Initial); + + if (QuotaManager::IsShuttingDown()) { + return NS_ERROR_FAILURE; + } + + QuotaManager* quotaManager = QuotaManager::Get(); + if (NS_WARN_IF(!quotaManager)) { + return NS_ERROR_FAILURE; + } + + AdvanceState(); + + quotaManager->OpenDirectoryInternal(mPersistenceType, + mOriginScope, + mExclusive, + this); + + return NS_OK; +} + +void +NormalOriginOperationBase::UnblockOpen() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_UnblockingOpen); + + SendResults(); + + mDirectoryLock = nullptr; + + AdvanceState(); +} + +void +NormalOriginOperationBase::DirectoryLockAcquired(DirectoryLock* aLock) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aLock); + MOZ_ASSERT(mState == State_DirectoryOpenPending); + MOZ_ASSERT(!mDirectoryLock); + + mDirectoryLock = aLock; + + nsresult rv = DirectoryOpen(); + if (NS_WARN_IF(NS_FAILED(rv))) { + Finish(rv); + return; + } +} + +void +NormalOriginOperationBase::DirectoryLockFailed() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_DirectoryOpenPending); + MOZ_ASSERT(!mDirectoryLock); + + Finish(NS_ERROR_FAILURE); +} + +nsresult +SaveOriginAccessTimeOp::DoDirectoryWork(QuotaManager* aQuotaManager) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(!mPersistenceType.IsNull()); + MOZ_ASSERT(mOriginScope.IsOrigin()); + + PROFILER_LABEL("Quota", "SaveOriginAccessTimeOp::DoDirectoryWork", + js::ProfileEntry::Category::OTHER); + + nsCOMPtr directory; + nsresult rv = + aQuotaManager->GetDirectoryForOrigin(mPersistenceType.Value(), + mOriginScope, + getter_AddRefs(directory)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr stream; + rv = GetDirectoryMetadataOutputStream(directory, kUpdateFileFlag, + getter_AddRefs(stream)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // The origin directory may not exist anymore. + if (stream) { + rv = stream->Write64(mTimestamp); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + return NS_OK; +} + +GetUsageOp::GetUsageOp(const nsACString& aGroup, + const nsACString& aOrigin, + bool aIsApp, + nsIURI* aURI, + nsIUsageCallback* aCallback, + uint32_t aAppId, + bool aInMozBrowserOnly) + : NormalOriginOperationBase(Nullable(), + OriginScope::FromOrigin(aOrigin), + /* aExclusive */ false) + , mGroup(aGroup) + , mURI(aURI) + , mCallback(aCallback) + , mAppId(aAppId) + , mIsApp(aIsApp) + , mInMozBrowserOnly(aInMozBrowserOnly) +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!aGroup.IsEmpty()); + MOZ_ASSERT(!aOrigin.IsEmpty()); + MOZ_ASSERT(aURI); + MOZ_ASSERT(aCallback); +} + +nsresult +GetUsageOp::AddToUsage(QuotaManager* aQuotaManager, + PersistenceType aPersistenceType) { AssertIsOnIOThread(); nsCOMPtr directory; - nsresult rv = aQuotaManager->GetDirectoryForOrigin(aPersistenceType, mOrigin, + nsresult rv = aQuotaManager->GetDirectoryForOrigin(aPersistenceType, + mOriginScope, getter_AddRefs(directory)); NS_ENSURE_SUCCESS(rv, rv); @@ -4009,15 +4675,14 @@ AsyncUsageRunnable::AddToUsage(QuotaManager* aQuotaManager, // If the directory exists then enumerate all the files inside, adding up // the sizes to get the final usage statistic. - if (exists && !mCanceled) { + if (exists && !mUsageInfo.Canceled()) { bool initialized; if (IsTreatedAsPersistent(aPersistenceType, mIsApp)) { - nsCString originKey = OriginKey(aPersistenceType, mOrigin); - initialized = aQuotaManager->mInitializedOrigins.Contains(originKey); - + nsCString originKey = OriginKey(aPersistenceType, mOriginScope); + initialized = aQuotaManager->IsOriginInitialized(originKey); } else { - initialized = aQuotaManager->mTemporaryStorageInitialized; + initialized = aQuotaManager->IsTemporaryStorageInitialized(); } if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT && !initialized) { @@ -4031,7 +4696,7 @@ AsyncUsageRunnable::AddToUsage(QuotaManager* aQuotaManager, bool hasMore; while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && - hasMore && !mCanceled) { + hasMore && !mUsageInfo.Canceled()) { nsCOMPtr entry; rv = entries->GetNext(getter_AddRefs(entry)); NS_ENSURE_SUCCESS(rv, rv); @@ -4059,6 +4724,10 @@ AsyncUsageRunnable::AddToUsage(QuotaManager* aQuotaManager, } } + if (MaybeRemoveCorruptDirectory(leafName, file)) { + continue; + } + Client::Type clientType; rv = Client::TypeFromText(leafName, clientType); if (NS_FAILED(rv)) { @@ -4069,13 +4738,16 @@ AsyncUsageRunnable::AddToUsage(QuotaManager* aQuotaManager, continue; } - nsRefPtr& client = aQuotaManager->mClients[clientType]; + Client* client = aQuotaManager->GetClient(clientType); + MOZ_ASSERT(client); if (initialized) { - rv = client->GetUsageForOrigin(aPersistenceType, mGroup, mOrigin, this); + rv = client->GetUsageForOrigin(aPersistenceType, mGroup, mOriginScope, + &mUsageInfo); } else { - rv = client->InitOrigin(aPersistenceType, mGroup, mOrigin, this); + rv = client->InitOrigin(aPersistenceType, mGroup, mOriginScope, + &mUsageInfo); } NS_ENSURE_SUCCESS(rv, rv); } @@ -4084,42 +4756,59 @@ AsyncUsageRunnable::AddToUsage(QuotaManager* aQuotaManager, return NS_OK; } -NS_IMPL_ISUPPORTS_INHERITED(AsyncUsageRunnable, nsRunnable, nsIQuotaRequest) - -NS_IMETHODIMP -AsyncUsageRunnable::Run() +nsresult +GetUsageOp::DoDirectoryWork(QuotaManager* aQuotaManager) { - PROFILER_LABEL("Quota", "AsyncUsageRunnable::Run", - js::ProfileEntry::Category::OTHER); + AssertIsOnIOThread(); - nsresult rv = RunInternal(); + PROFILER_LABEL("Quota", "GetUsageOp::DoDirectoryWork", + js::ProfileEntry::Category::OTHER); - if (!NS_IsMainThread()) { - if (NS_FAILED(rv)) { - ResetUsage(); + // Add all the persistent/temporary/default storage files we care about. + nsresult rv; + for (const PersistenceType type : kAllPersistenceTypes) { + rv = AddToUsage(aQuotaManager, type); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; } - - if (NS_FAILED(NS_DispatchToMainThread(this))) { - NS_WARNING("Failed to dispatch to main thread!"); - } - } - - return NS_OK; -} - -NS_IMETHODIMP -AsyncUsageRunnable::Cancel() -{ - if (mCanceled.exchange(true)) { - NS_WARNING("Canceled more than once?!"); - return NS_ERROR_UNEXPECTED; } return NS_OK; } void -ResetOrClearRunnable::DeleteFiles(QuotaManager* aQuotaManager) +GetUsageOp::SendResults() +{ + MOZ_ASSERT(NS_IsMainThread()); + + // Call the callback unless we were canceled. + if (!mUsageInfo.Canceled()) { + // XXX Implement better error reporting here, bug 1170019. + if (NS_FAILED(mResultCode)) { + mUsageInfo.ResetUsage(); + } + + mCallback->OnUsageResult(mURI, mUsageInfo.TotalUsage(), mUsageInfo.FileUsage(), mAppId, + mInMozBrowserOnly); + } + + // Clean up. + mURI = nullptr; + mCallback = nullptr; +} + +NS_IMPL_ISUPPORTS_INHERITED(GetUsageOp, + NormalOriginOperationBase, + nsIQuotaRequest) + +NS_IMETHODIMP +GetUsageOp::Cancel() +{ + return mUsageInfo.Cancel(); +} + +void +ResetOrClearOp::DeleteFiles(QuotaManager* aQuotaManager) { AssertIsOnIOThread(); NS_ASSERTION(aQuotaManager, "Don't pass me null!"); @@ -4142,236 +4831,216 @@ ResetOrClearRunnable::DeleteFiles(QuotaManager* aQuotaManager) } } -NS_IMPL_ISUPPORTS_INHERITED0(ResetOrClearRunnable, nsRunnable) - -NS_IMETHODIMP -ResetOrClearRunnable::Run() -{ - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "This should never fail!"); - - switch (mCallbackState) { - case Pending: { - NS_NOTREACHED("Should never get here without being dispatched!"); - return NS_ERROR_UNEXPECTED; - } - - case OpenAllowed: { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - AdvanceState(); - - // Now we have to wait until the thread pool is done with all of the - // storages we care about. - nsresult rv = - quotaManager->AcquireExclusiveAccess(NullCString(), - Nullable(), this); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; - } - - case IO: { - AssertIsOnIOThread(); - - AdvanceState(); - - if (mClear) { - DeleteFiles(quotaManager); - } - - quotaManager->RemoveQuota(); - quotaManager->ResetOrClearCompleted(); - - // Now dispatch back to the main thread. - if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) { - NS_WARNING("Failed to dispatch to main thread!"); - return NS_ERROR_FAILURE; - } - - return NS_OK; - } - - case Complete: { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - // Tell the QuotaManager that we're done. - quotaManager->AllowNextSynchronizedOp(OriginOrPatternString::FromNull(), - Nullable(), - EmptyCString()); - - return NS_OK; - } - - default: - NS_ERROR("Unknown state value!"); - return NS_ERROR_UNEXPECTED; - } - - NS_NOTREACHED("Should never get here!"); - return NS_ERROR_UNEXPECTED; -} - -NS_IMETHODIMP -FinalizeOriginEvictionRunnable::Run() -{ - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "This should never fail!"); - - nsresult rv; - - switch (mCallbackState) { - case Pending: { - NS_NOTREACHED("Should never get here without being dispatched!"); - return NS_ERROR_UNEXPECTED; - } - - case OpenAllowed: { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - AdvanceState(); - - rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to dispatch to the IO thread!"); - } - - return NS_OK; - } - - case IO: { - AssertIsOnIOThread(); - - AdvanceState(); - - for (uint32_t index = 0; index < mOrigins.Length(); index++) { - const OriginParams& origin = mOrigins[index]; - - quotaManager->OriginClearCompleted( - origin.mPersistenceType, - OriginOrPatternString::FromOrigin(origin.mOrigin), - origin.mIsApp); - } - - if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) { - NS_WARNING("Failed to dispatch to main thread!"); - return NS_ERROR_FAILURE; - } - - return NS_OK; - } - - case Complete: { - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - for (uint32_t index = 0; index < mOrigins.Length(); index++) { - const OriginParams& origin = mOrigins[index]; - - quotaManager->AllowNextSynchronizedOp( - OriginOrPatternString::FromOrigin(origin.mOrigin), - Nullable(origin.mPersistenceType), - EmptyCString()); - } - - return NS_OK; - } - - default: - NS_ERROR("Unknown state value!"); - return NS_ERROR_UNEXPECTED; - } - - NS_NOTREACHED("Should never get here!"); - return NS_ERROR_UNEXPECTED; -} - nsresult -FinalizeOriginEvictionRunnable::Dispatch() -{ - NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(mCallbackState == Pending, "Huh?"); - - mCallbackState = OpenAllowed; - return NS_DispatchToMainThread(this); -} - -nsresult -FinalizeOriginEvictionRunnable::RunImmediately() -{ - AssertIsOnIOThread(); - NS_ASSERTION(mCallbackState == Pending, "Huh?"); - - mCallbackState = IO; - return this->Run(); -} - -NS_IMETHODIMP -WaitForTransactionsToFinishRunnable::Run() -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(mOp, "Null op!"); - NS_ASSERTION(mOp->mRunnable, "Nothing to run!"); - NS_ASSERTION(mCountdown, "Wrong countdown!"); - - if (--mCountdown) { - return NS_OK; - } - - // Don't hold the runnable alive longer than necessary. - nsCOMPtr runnable; - runnable.swap(mOp->mRunnable); - - mOp = nullptr; - - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "This should never fail!"); - - nsresult rv = - quotaManager->IOThread()->Dispatch(runnable, NS_DISPATCH_NORMAL); - NS_ENSURE_SUCCESS(rv, rv); - - // The listener is responsible for calling - // QuotaManager::AllowNextSynchronizedOp. - return NS_OK; -} - -NS_IMETHODIMP -SaveOriginAccessTimeRunnable::Run() +ResetOrClearOp::DoDirectoryWork(QuotaManager* aQuotaManager) { AssertIsOnIOThread(); - QuotaManager* quotaManager = QuotaManager::Get(); - NS_ASSERTION(quotaManager, "This should never fail!"); + PROFILER_LABEL("Quota", "ResetOrClearOp::DoDirectoryWork", + js::ProfileEntry::Category::OTHER); - nsCOMPtr directory; - nsresult rv = - quotaManager->GetDirectoryForOrigin(mPersistenceType, mOrigin, - getter_AddRefs(directory)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr stream; - rv = GetDirectoryMetadataOutputStream(directory, kUpdateFileFlag, - getter_AddRefs(stream)); - NS_ENSURE_SUCCESS(rv, rv); - - // The origin directory may not exist anymore. - if (stream) { - rv = stream->Write64(mTimestamp); - NS_ENSURE_SUCCESS(rv, rv); + if (mClear) { + DeleteFiles(aQuotaManager); } + aQuotaManager->RemoveQuota(); + + aQuotaManager->ResetOrClearCompleted(); + return NS_OK; } -nsresult -StorageDirectoryHelper::CreateOrUpgradeMetadataFiles() +void +OriginClearOp::DeleteFiles(QuotaManager* aQuotaManager, + PersistenceType aPersistenceType) { AssertIsOnIOThread(); + MOZ_ASSERT(aQuotaManager); nsresult rv; + nsCOMPtr directory = + do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + rv = directory->InitWithPath(aQuotaManager->GetStoragePath(aPersistenceType)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + nsCOMPtr entries; - rv = mDirectory->GetDirectoryEntries(getter_AddRefs(entries)); + if (NS_WARN_IF(NS_FAILED( + directory->GetDirectoryEntries(getter_AddRefs(entries)))) || !entries) { + return; + } + + nsCString originSanitized(mOriginScope); + SanitizeOriginString(originSanitized); + + bool hasMore; + while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) { + nsCOMPtr entry; + rv = entries->GetNext(getter_AddRefs(entry)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + nsCOMPtr file = do_QueryInterface(entry); + MOZ_ASSERT(file); + + bool isDirectory; + rv = file->IsDirectory(&isDirectory); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + nsString leafName; + rv = file->GetLeafName(leafName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + if (!isDirectory) { + if (!leafName.EqualsLiteral(DSSTORE_FILE_NAME)) { + QM_WARNING("Something (%s) in the repository that doesn't belong!", + NS_ConvertUTF16toUTF8(leafName).get()); + } + continue; + } + + // Skip storages for other apps. + if (!PatternMatchesOrigin(originSanitized, + NS_ConvertUTF16toUTF8(leafName))) { + continue; + } + + bool persistent = aPersistenceType == PERSISTENCE_TYPE_PERSISTENT; + + int64_t timestamp; + nsCString group; + nsCString origin; + bool isApp; + rv = GetDirectoryMetadataWithRestore(file, + persistent, + ×tamp, + group, + origin, + &isApp); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + + for (uint32_t index = 0; index < 10; index++) { + // We can't guarantee that this will always succeed on Windows... + if (NS_SUCCEEDED((rv = file->Remove(true)))) { + break; + } + + NS_WARNING("Failed to remove directory, retrying after a short delay."); + + PR_Sleep(PR_MillisecondsToInterval(200)); + } + + if (NS_FAILED(rv)) { + NS_WARNING("Failed to remove directory, giving up!"); + } + + if (aPersistenceType != PERSISTENCE_TYPE_PERSISTENT) { + aQuotaManager->RemoveQuotaForOrigin(aPersistenceType, group, origin); + } + + aQuotaManager->OriginClearCompleted(aPersistenceType, origin, isApp); + } + +} + +nsresult +OriginClearOp::DoDirectoryWork(QuotaManager* aQuotaManager) +{ + AssertIsOnIOThread(); + + PROFILER_LABEL("Quota", "OriginClearOp::DoDirectoryWork", + js::ProfileEntry::Category::OTHER); + + if (mPersistenceType.IsNull()) { + for (const PersistenceType type : kAllPersistenceTypes) { + DeleteFiles(aQuotaManager, type); + } + } else { + DeleteFiles(aQuotaManager, mPersistenceType.Value()); + } + + return NS_OK; +} + +void +FinalizeOriginEvictionOp::Dispatch() +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(mState == State_Initial); + + mState = State_DirectoryOpenPending; + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); +} + +void +FinalizeOriginEvictionOp::RunOnIOThreadImmediately() +{ + AssertIsOnIOThread(); + MOZ_ASSERT(mState == State_Initial); + + mState = State_DirectoryWorkOpen; + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(this->Run())); +} + +nsresult +FinalizeOriginEvictionOp::Open() +{ + MOZ_CRASH("Shouldn't get here!"); +} + +nsresult +FinalizeOriginEvictionOp::DoDirectoryWork(QuotaManager* aQuotaManager) +{ + AssertIsOnIOThread(); + + PROFILER_LABEL("Quota", "FinalizeOriginEvictionOp::DoDirectoryWork", + js::ProfileEntry::Category::OTHER); + + for (nsRefPtr& lock : mLocks) { + aQuotaManager->OriginClearCompleted(lock->GetPersistenceType().Value(), + lock->GetOriginScope(), + lock->GetIsApp().Value()); + } + + return NS_OK; +} + +void +FinalizeOriginEvictionOp::UnblockOpen() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mState == State_UnblockingOpen); + + mLocks.Clear(); + + AdvanceState(); +} + +nsresult +StorageDirectoryHelper::CreateOrUpgradeMetadataFiles(bool aCreate) +{ + AssertIsOnIOThread(); + MOZ_ASSERT_IF(mPersistent, aCreate); + + mCreate = aCreate; + + nsCOMPtr entries; + nsresult rv = mDirectory->GetDirectoryEntries(getter_AddRefs(entries)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -4398,50 +5067,33 @@ StorageDirectoryHelper::CreateOrUpgradeMetadataFiles() if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - if (!isDirectory) { - if (!leafName.EqualsLiteral(DSSTORE_FILE_NAME)) { - NS_WARNING("Something in the storage directory that doesn't belong!"); - } - continue; - } - if (mPersistent) { - rv = MaybeUpgradeOriginDirectory(originDir); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - } + if (isDirectory) { + if (leafName.EqualsLiteral("moz-safe-about+++home")) { + // This directory was accidentally created by a buggy nightly and can + // be safely removed. - if (leafName.EqualsLiteral(kChromeOrigin)) { - OriginProps* originProps = mOriginProps.AppendElement(); - originProps->mDirectory = originDir; - originProps->mSpec = kChromeOrigin; - originProps->mType = OriginProps::eChrome; - } else { - nsCString spec; - uint32_t appId; - bool inMozBrowser; - if (NS_WARN_IF(!OriginParser::ParseOrigin(NS_ConvertUTF16toUTF8(leafName), - &appId, &inMozBrowser, spec))) { - return NS_ERROR_FAILURE; - } + QM_WARNING("Deleting accidental moz-safe-about+++home directory!"); - OriginProps* originProps = mOriginProps.AppendElement(); - originProps->mDirectory = originDir; - originProps->mSpec = spec; - originProps->mAppId = appId; - originProps->mType = OriginProps::eContent; - originProps->mInMozBrowser = inMozBrowser; - - if (mPersistent) { - int64_t timestamp = INT64_MIN; - rv = GetLastModifiedTime(originDir, ×tamp); + rv = originDir->Remove(/* aRecursive */ true); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - originProps->mTimestamp = timestamp; + continue; } + } else { + if (!leafName.EqualsLiteral(DSSTORE_FILE_NAME)) { + QM_WARNING("Something (%s) in the storage directory that doesn't belong!", + NS_ConvertUTF16toUTF8(leafName).get()); + + } + continue; + } + + rv = AddOriginDirectory(originDir); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; } } @@ -4449,6 +5101,94 @@ StorageDirectoryHelper::CreateOrUpgradeMetadataFiles() return NS_OK; } + rv = ProcessOriginDirectories(/* aMove */ true); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +StorageDirectoryHelper::RestoreMetadataFile() +{ + AssertIsOnIOThread(); + MOZ_ASSERT(mCreate); + + nsresult rv = AddOriginDirectory(mDirectory); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = ProcessOriginDirectories(/* aMove */ false); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} + +nsresult +StorageDirectoryHelper::AddOriginDirectory(nsIFile* aDirectory) +{ + MOZ_ASSERT(aDirectory); + + nsresult rv; + + if (mPersistent) { + rv = MaybeUpgradeOriginDirectory(aDirectory); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + nsString leafName; + rv = aDirectory->GetLeafName(leafName); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (leafName.EqualsLiteral(kChromeOrigin)) { + OriginProps* originProps = mOriginProps.AppendElement(); + originProps->mDirectory = aDirectory; + originProps->mSpec = kChromeOrigin; + originProps->mType = OriginProps::eChrome; + } else { + nsCString spec; + uint32_t appId; + bool inMozBrowser; + if (NS_WARN_IF(!OriginParser::ParseOrigin(NS_ConvertUTF16toUTF8(leafName), + &appId, &inMozBrowser, spec))) { + return NS_ERROR_FAILURE; + } + + OriginProps* originProps = mOriginProps.AppendElement(); + originProps->mDirectory = aDirectory; + originProps->mSpec = spec; + originProps->mAppId = appId; + originProps->mType = OriginProps::eContent; + originProps->mInMozBrowser = inMozBrowser; + + if (mCreate) { + int64_t timestamp = INT64_MIN; + rv = GetLastModifiedTime(aDirectory, ×tamp); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + originProps->mTimestamp = timestamp; + } + } + + return NS_OK; +} + +nsresult +StorageDirectoryHelper::ProcessOriginDirectories(bool aMove) +{ + AssertIsOnIOThread(); + MOZ_ASSERT(!mOriginProps.IsEmpty()); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this))); { @@ -4468,6 +5208,8 @@ StorageDirectoryHelper::CreateOrUpgradeMetadataFiles() return NS_ERROR_FAILURE; } + nsresult rv; + nsCOMPtr permanentStorageDir; for (uint32_t count = mOriginProps.Length(), index = 0; @@ -4475,7 +5217,7 @@ StorageDirectoryHelper::CreateOrUpgradeMetadataFiles() index++) { OriginProps& originProps = mOriginProps[index]; - if (mPersistent) { + if (mCreate) { rv = CreateDirectoryMetadata(originProps.mDirectory, originProps.mTimestamp, originProps.mGroup, @@ -4486,7 +5228,9 @@ StorageDirectoryHelper::CreateOrUpgradeMetadataFiles() } // Move whitelisted origins to new persistent storage. - if (QuotaManager::IsOriginWhitelistedForPersistentStorage( + if (mPersistent && + aMove && + QuotaManager::IsOriginWhitelistedForPersistentStorage( originProps.mSpec)) { if (!permanentStorageDir) { permanentStorageDir = @@ -4581,7 +5325,7 @@ StorageDirectoryHelper::RunOnMainThread() return rv; } - if (mPersistent) { + if (mCreate) { rv = QuotaManager::GetInfoFromPrincipal(principal, &originProps.mGroup, &originProps.mOrigin, @@ -4760,14 +5504,14 @@ void OriginParser::HandlePathnameComponent(const nsDependentCSubstring& aToken) { MOZ_ASSERT(!aToken.IsEmpty()); - MOZ_ASSERT(mState == eExpectingDriveLetterOrPathnameComponent || - mState == eExpectingEmptyTokenOrPathnameComponent || - mState == eExpectingPathnameComponent); + MOZ_ASSERT(mState == eExpectingEmptyTokenOrDriveLetterOrPathnameComponent || + mState == eExpectingEmptyTokenOrPathnameComponent); MOZ_ASSERT(mSchemaType == eFile); mPathnameComponents.AppendElement(aToken); - mState = mTokenizer.hasMoreTokens() ? eExpectingPathnameComponent : eComplete; + mState = mTokenizer.hasMoreTokens() ? eExpectingEmptyTokenOrPathnameComponent + : eComplete; } void @@ -4879,7 +5623,9 @@ OriginParser::HandleToken(const nsDependentCSubstring& aToken) return; } - mState = eExpectingDriveLetterOrPathnameComponent; + mState = mTokenizer.hasMoreTokens() + ? eExpectingEmptyTokenOrDriveLetterOrPathnameComponent + : eComplete; return; } @@ -4928,14 +5674,16 @@ OriginParser::HandleToken(const nsDependentCSubstring& aToken) return; } - case eExpectingDriveLetterOrPathnameComponent: { + case eExpectingEmptyTokenOrDriveLetterOrPathnameComponent: { MOZ_ASSERT(mSchemaType == eFile); if (aToken.IsEmpty()) { - QM_WARNING("Expected a drive letter or pathname component " - "(not an empty string)!"); + mPathnameComponents.AppendElement(EmptyCString()); + + mState = + mTokenizer.hasMoreTokens() ? eExpectingEmptyTokenOrPathnameComponent + : eComplete; - mError = true; return; } @@ -4959,28 +5707,22 @@ OriginParser::HandleToken(const nsDependentCSubstring& aToken) case eExpectingEmptyTokenOrPathnameComponent: { MOZ_ASSERT(mSchemaType == eFile); - if (mMaybeDriveLetter && aToken.IsEmpty()) { - MOZ_ASSERT(mPathnameComponents.Length() == 1); - - nsCString& pathnameComponent = mPathnameComponents[0]; - pathnameComponent.Append(':'); - - mState = mTokenizer.hasMoreTokens() ? eExpectingPathnameComponent - : eComplete; - - return; - } - - HandlePathnameComponent(aToken); - - return; - } - - case eExpectingPathnameComponent: { if (aToken.IsEmpty()) { - QM_WARNING("Expected a pathname component (not an empty string)!"); + if (mMaybeDriveLetter) { + MOZ_ASSERT(mPathnameComponents.Length() == 1); + + nsCString& pathnameComponent = mPathnameComponents[0]; + pathnameComponent.Append(':'); + + mMaybeDriveLetter = false; + } else { + mPathnameComponents.AppendElement(EmptyCString()); + } + + mState = + mTokenizer.hasMoreTokens() ? eExpectingEmptyTokenOrPathnameComponent + : eComplete; - mError = true; return; } @@ -4997,10 +5739,14 @@ OriginParser::HandleToken(const nsDependentCSubstring& aToken) void OriginParser::HandleTrailingSeparator() { - MOZ_ASSERT(mState = eComplete); + MOZ_ASSERT(mState == eComplete); MOZ_ASSERT(mSchemaType == eFile); mPathnameComponents.AppendElement(EmptyCString()); mState = eHandledTrailingSeparator; } + +} // namespace quota +} // namespace dom +} // namespace mozilla diff --git a/dom/quota/QuotaManager.h b/dom/quota/QuotaManager.h index eef0a16593..58c3a1fc85 100644 --- a/dom/quota/QuotaManager.h +++ b/dom/quota/QuotaManager.h @@ -13,18 +13,17 @@ #include "nsIQuotaManager.h" #include "mozilla/dom/Nullable.h" +#include "mozilla/dom/ipc/IdType.h" #include "mozilla/Mutex.h" #include "nsClassHashtable.h" #include "nsRefPtrHashtable.h" -#include "ArrayCluster.h" #include "Client.h" #include "PersistenceType.h" #define QUOTA_MANAGER_CONTRACTID "@mozilla.org/dom/quota/manager;1" -class nsIOfflineStorage; class nsIPrincipal; class nsIThread; class nsITimer; @@ -34,23 +33,58 @@ class nsIRunnable; namespace mozilla { namespace dom { -class ContentParent; +class OptionalContentId; } } BEGIN_QUOTA_NAMESPACE -class AsyncUsageRunnable; -class CollectOriginsHelper; -class FinalizeOriginEvictionRunnable; +class DirectoryLockImpl; class GroupInfo; class GroupInfoPair; -class OriginClearRunnable; class OriginInfo; -class OriginOrPatternString; +class OriginScope; class QuotaObject; -class ResetOrClearRunnable; -struct SynchronizedOp; + +class NS_NO_VTABLE RefCountedObject +{ +public: + NS_IMETHOD_(MozExternalRefCountType) + AddRef() = 0; + + NS_IMETHOD_(MozExternalRefCountType) + Release() = 0; +}; + +// nsISupports is needed for nsMainThreadPtrHandle +// XXX RemoveMe once bug 1164581 gets fixed. +class DirectoryLock + : public nsISupports +{ + friend class DirectoryLockImpl; + +private: + DirectoryLock() + { } + + ~DirectoryLock() + { } +}; + +class NS_NO_VTABLE OpenDirectoryListener + : public RefCountedObject +{ +public: + virtual void + DirectoryLockAcquired(DirectoryLock* aLock) = 0; + + virtual void + DirectoryLockFailed() = 0; + +protected: + virtual ~OpenDirectoryListener() + { } +}; struct OriginParams { @@ -70,16 +104,10 @@ struct OriginParams class QuotaManager final : public nsIQuotaManager, public nsIObserver { - friend class AsyncUsageRunnable; - friend class CollectOriginsHelper; - friend class FinalizeOriginEvictionRunnable; + friend class DirectoryLockImpl; friend class GroupInfo; - friend class OriginClearRunnable; friend class OriginInfo; friend class QuotaObject; - friend class ResetOrClearRunnable; - - typedef mozilla::dom::ContentParent ContentParent; enum MozBrowserPatternFlag { @@ -89,7 +117,7 @@ class QuotaManager final : public nsIQuotaManager, }; typedef nsClassHashtable> LiveStorageTable; + nsTArray> DirectoryLockTable; public: NS_DECL_ISUPPORTS @@ -111,6 +139,22 @@ public: // Returns true if we've begun the shutdown process. static bool IsShuttingDown(); + bool + IsOriginInitialized(const nsACString& aOrigin) const + { + AssertIsOnIOThread(); + + return mInitializedOrigins.Contains(aOrigin); + } + + bool + IsTemporaryStorageInitialized() const + { + AssertIsOnIOThread(); + + return mTemporaryStorageInitialized; + } + void InitQuotaForOrigin(PersistenceType aPersistenceType, const nsACString& aGroup, @@ -154,43 +198,52 @@ public: const nsACString& aOrigin, const nsAString& aPath); - // Called when a storage is created. - bool - RegisterStorage(nsIOfflineStorage* aStorage); - - // Called when a storage is being unlinked or destroyed. + // Called when a process is being shot down. Aborts any running operations + // for the given process. void - UnregisterStorage(nsIOfflineStorage* aStorage); - - // Called when a process is being shot down. Forces any live storage objects - // to close themselves and aborts any running transactions. - void - AbortCloseStoragesForProcess(ContentParent* aContentParent); - - // Waits for storages to be cleared and for version change transactions to - // complete before dispatching the given runnable. - nsresult - WaitForOpenAllowed(const OriginOrPatternString& aOriginOrPattern, - Nullable aPersistenceType, - const nsACString& aId, nsIRunnable* aRunnable); - - void - AllowNextSynchronizedOp(const OriginOrPatternString& aOriginOrPattern, - Nullable aPersistenceType, - const nsACString& aId); - - bool - IsClearOriginPending(const nsACString& aPattern, - Nullable aPersistenceType) - { - return !!FindSynchronizedOp(aPattern, aPersistenceType, EmptyCString()); - } + AbortOperationsForProcess(ContentParentId aContentParentId); nsresult GetDirectoryForOrigin(PersistenceType aPersistenceType, const nsACString& aASCIIOrigin, nsIFile** aDirectory) const; + // This is the main entry point into the QuotaManager API. + // Any storage API implementation (quota client) that participates in + // centralized quota and storage handling should call this method to get + // a directory lock which will protect client's files from being deleted + // while they are still in use. + // After a lock is acquired, client is notified via the open listener's + // method DirectoryLockAcquired. If the lock couldn't be acquired, client + // gets DirectoryLockFailed notification. + // A lock is a reference counted object and at the time DirectoryLockAcquired + // is called, quota manager holds just one strong reference to it which is + // then immediatelly cleared by quota manager. So it's up to client to add + // a new reference in order to keep the lock alive. + // Unlocking is simply done by dropping all references to the lock object. + // In other words, protection which the lock represents dies with the lock + // object itself. + void + OpenDirectory(PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + bool aIsApp, + Client::Type aClientType, + bool aExclusive, + OpenDirectoryListener* aOpenListener); + + // XXX RemoveMe once bug 1170279 gets fixed. + void + OpenDirectoryInternal(Nullable aPersistenceType, + const OriginScope& aOriginScope, + bool aExclusive, + OpenDirectoryListener* aOpenListener); + + // Collect inactive and the least recently used origins. + uint64_t + CollectOriginsForEviction(uint64_t aMinSizeToBeFreed, + nsTArray>& aLocks); + nsresult EnsureOriginIsInitialized(PersistenceType aPersistenceType, const nsACString& aGroup, @@ -219,7 +272,7 @@ public: return mIOThread; } - already_AddRefed + Client* GetClient(Client::Type aClientType); const nsString& @@ -251,7 +304,6 @@ public: GetStorageId(PersistenceType aPersistenceType, const nsACString& aOrigin, Client::Type aClientType, - const nsAString& aName, nsACString& aDatabaseId); static nsresult @@ -313,6 +365,13 @@ public: EmptyCString(), _retval); } + static nsresult + GetDirectoryMetadata(nsIFile* aDirectory, + int64_t* aTimestamp, + nsACString& aGroup, + nsACString& aOrigin, + bool* aIsApp); + private: QuotaManager(); @@ -321,29 +380,41 @@ private: nsresult Init(); + already_AddRefed + CreateDirectoryLock(Nullable aPersistenceType, + const nsACString& aGroup, + const OriginScope& aOriginScope, + Nullable aIsApp, + Nullable aClientType, + bool aExclusive, + bool aInternal, + OpenDirectoryListener* aOpenListener); + + already_AddRefed + CreateDirectoryLockForEviction(PersistenceType aPersistenceType, + const nsACString& aGroup, + const nsACString& aOrigin, + bool aIsApp); + + void + RegisterDirectoryLock(DirectoryLockImpl* aLock); + + void + UnregisterDirectoryLock(DirectoryLockImpl* aLock); + + void + RemovePendingDirectoryLock(DirectoryLockImpl* aLock); + uint64_t - LockedCollectOriginsForEviction(uint64_t aMinSizeToBeFreed, - nsTArray& aOriginInfos); + LockedCollectOriginsForEviction( + uint64_t aMinSizeToBeFreed, + nsTArray>& aLocks); void LockedRemoveQuotaForOrigin(PersistenceType aPersistenceType, const nsACString& aGroup, const nsACString& aOrigin); - nsresult - AcquireExclusiveAccess(const nsACString& aOrigin, - Nullable aPersistenceType, - nsIRunnable* aRunnable); - - void - AddSynchronizedOp(const OriginOrPatternString& aOriginOrPattern, - Nullable aPersistenceType); - - SynchronizedOp* - FindSynchronizedOp(const nsACString& aPattern, - Nullable aPersistenceType, - const nsACString& aId); - nsresult MaybeUpgradeIndexedDBDirectory(); @@ -370,22 +441,12 @@ private: void CheckTemporaryStorageLimits(); - // Collect inactive and the least recently used origins. - uint64_t - CollectOriginsForEviction(uint64_t aMinSizeToBeFreed, - nsTArray& aOriginInfos); - void DeleteFilesForOrigin(PersistenceType aPersistenceType, const nsACString& aOrigin); void - FinalizeOriginEviction(nsTArray& aOrigins); - - void - SaveOriginAccessTime(PersistenceType aPersistenceType, - const nsACString& aOrigin, - int64_t aTimestamp); + FinalizeOriginEviction(nsTArray>& aLocks); void ReleaseIOThreadObjects() @@ -397,8 +458,8 @@ private: } } - LiveStorageTable& - GetLiveStorageTable(PersistenceType aPersistenceType); + DirectoryLockTable& + GetDirectoryLockTable(PersistenceType aPersistenceType); static void GetOriginPatternString(uint32_t aAppId, @@ -421,29 +482,19 @@ private: GroupInfoPair* aValue, void* aUserArg); - static PLDHashOperator - AddLiveStorageOrigins(const nsACString& aKey, - nsTArray* aValue, - void* aUserArg); - - static PLDHashOperator - GetInactiveTemporaryStorageOrigins(const nsACString& aKey, - GroupInfoPair* aValue, - void* aUserArg); - mozilla::Mutex mQuotaMutex; nsClassHashtable mGroupInfoPairs; - // Maintains a list of live storages per origin. - nsClassHashtable > mLiveStorages; + // Maintains a list of directory locks that are queued. + nsTArray> mPendingDirectoryLocks; - LiveStorageTable mTemporaryLiveStorageTable; - LiveStorageTable mDefaultLiveStorageTable; + // Maintains a list of directory locks that are acquired or queued. + nsTArray mDirectoryLocks; - // Maintains a list of synchronized operatons that are in progress or queued. - nsAutoTArray, 5> mSynchronizedOps; + // Directory lock tables that are used to update origin access time. + DirectoryLockTable mTemporaryDirectoryLockTable; + DirectoryLockTable mDefaultDirectoryLockTable; // Thread on which IO is performed. nsCOMPtr mIOThread; diff --git a/dom/quota/QuotaObject.cpp b/dom/quota/QuotaObject.cpp deleted file mode 100644 index 0d2ba46048..0000000000 --- a/dom/quota/QuotaObject.cpp +++ /dev/null @@ -1,408 +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 "QuotaObject.h" - -#include "mozilla/TypeTraits.h" -#include "QuotaManager.h" -#include "Utilities.h" - -USING_QUOTA_NAMESPACE - -namespace { - -template ::value> -struct IntChecker -{ - static void - Assert(T aInt) - { - static_assert(mozilla::IsIntegral::value, "Not an integer!"); - MOZ_ASSERT(aInt >= 0); - } -}; - -template -struct IntChecker -{ - static void - Assert(T aInt) - { - static_assert(mozilla::IsIntegral::value, "Not an integer!"); - } -}; - -template -void -AssertNoOverflow(uint64_t aDest, T aArg) -{ - IntChecker::Assert(aDest); - IntChecker::Assert(aArg); - MOZ_ASSERT(UINT64_MAX - aDest >= uint64_t(aArg)); -} - -template -void -AssertNoUnderflow(T aDest, U aArg) -{ - IntChecker::Assert(aDest); - IntChecker::Assert(aArg); - MOZ_ASSERT(uint64_t(aDest) >= uint64_t(aArg)); -} - -} // anonymous namespace - -void -QuotaObject::AddRef() -{ - QuotaManager* quotaManager = QuotaManager::Get(); - if (!quotaManager) { - NS_ERROR("Null quota manager, this shouldn't happen, possible leak!"); - - ++mRefCnt; - - return; - } - - MutexAutoLock lock(quotaManager->mQuotaMutex); - - ++mRefCnt; -} - -void -QuotaObject::Release() -{ - QuotaManager* quotaManager = QuotaManager::Get(); - if (!quotaManager) { - NS_ERROR("Null quota manager, this shouldn't happen, possible leak!"); - - nsrefcnt count = --mRefCnt; - if (count == 0) { - mRefCnt = 1; - delete this; - } - - return; - } - - { - MutexAutoLock lock(quotaManager->mQuotaMutex); - - --mRefCnt; - - if (mRefCnt > 0) { - return; - } - - if (mOriginInfo) { - mOriginInfo->mQuotaObjects.Remove(mPath); - } - } - - delete this; -} - -bool -QuotaObject::MaybeUpdateSize(int64_t aSize, bool aTruncate) -{ - QuotaManager* quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager); - - MutexAutoLock lock(quotaManager->mQuotaMutex); - - if (mSize == aSize) { - return true; - } - - if (!mOriginInfo) { - mSize = aSize; - return true; - } - - GroupInfo* groupInfo = mOriginInfo->mGroupInfo; - MOZ_ASSERT(groupInfo); - - if (mSize > aSize) { - if (aTruncate) { - const int64_t delta = mSize - aSize; - - AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, delta); - quotaManager->mTemporaryStorageUsage -= delta; - - AssertNoUnderflow(groupInfo->mUsage, delta); - groupInfo->mUsage -= delta; - - AssertNoUnderflow(mOriginInfo->mUsage, delta); - mOriginInfo->mUsage -= delta; - - mSize = aSize; - } - return true; - } - - MOZ_ASSERT(mSize < aSize); - - nsRefPtr complementaryGroupInfo = - groupInfo->mGroupInfoPair->LockedGetGroupInfo( - ComplementaryPersistenceType(groupInfo->mPersistenceType)); - - uint64_t delta = aSize - mSize; - - AssertNoOverflow(mOriginInfo->mUsage, delta); - uint64_t newUsage = mOriginInfo->mUsage + delta; - - // Temporary storage has no limit for origin usage (there's a group and the - // global limit though). - - AssertNoOverflow(groupInfo->mUsage, delta); - uint64_t newGroupUsage = groupInfo->mUsage + delta; - - uint64_t groupUsage = groupInfo->mUsage; - if (complementaryGroupInfo) { - AssertNoOverflow(groupUsage, complementaryGroupInfo->mUsage); - groupUsage += complementaryGroupInfo->mUsage; - } - - // Temporary storage has a hard limit for group usage (20 % of the global - // limit). - AssertNoOverflow(groupUsage, delta); - if (groupUsage + delta > quotaManager->GetGroupLimit()) { - return false; - } - - AssertNoOverflow(quotaManager->mTemporaryStorageUsage, delta); - uint64_t newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage + - delta; - - if (newTemporaryStorageUsage > quotaManager->mTemporaryStorageLimit) { - // This will block the thread without holding the lock while waitting. - - nsAutoTArray originInfos; - uint64_t sizeToBeFreed = - quotaManager->LockedCollectOriginsForEviction(delta, originInfos); - - if (!sizeToBeFreed) { - return false; - } - - NS_ASSERTION(sizeToBeFreed >= delta, "Huh?"); - - { - MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); - - for (uint32_t i = 0; i < originInfos.Length(); i++) { - OriginInfo* originInfo = originInfos[i]; - - quotaManager->DeleteFilesForOrigin( - originInfo->mGroupInfo->mPersistenceType, - originInfo->mOrigin); - } - } - - // Relocked. - - NS_ASSERTION(mOriginInfo, "How come?!"); - - nsTArray origins; - for (uint32_t i = 0; i < originInfos.Length(); i++) { - OriginInfo* originInfo = originInfos[i]; - - NS_ASSERTION(originInfo != mOriginInfo, "Deleted itself!"); - - PersistenceType persistenceType = - originInfo->mGroupInfo->mPersistenceType; - nsCString group = originInfo->mGroupInfo->mGroup; - nsCString origin = originInfo->mOrigin; - bool isApp = originInfo->mIsApp; - quotaManager->LockedRemoveQuotaForOrigin(persistenceType, group, origin); - -#ifdef DEBUG - originInfos[i] = nullptr; -#endif - - origins.AppendElement(OriginParams(persistenceType, origin, isApp)); - } - - // We unlocked and relocked several times so we need to recompute all the - // essential variables and recheck the group limit. - - AssertNoUnderflow(aSize, mSize); - delta = aSize - mSize; - - AssertNoOverflow(mOriginInfo->mUsage, delta); - newUsage = mOriginInfo->mUsage + delta; - - AssertNoOverflow(groupInfo->mUsage, delta); - newGroupUsage = groupInfo->mUsage + delta; - - groupUsage = groupInfo->mUsage; - if (complementaryGroupInfo) { - AssertNoOverflow(groupUsage, complementaryGroupInfo->mUsage); - groupUsage += complementaryGroupInfo->mUsage; - } - - AssertNoOverflow(groupUsage, delta); - if (groupUsage + delta > quotaManager->GetGroupLimit()) { - // Unfortunately some other thread increased the group usage in the - // meantime and we are not below the group limit anymore. - - // However, the origin eviction must be finalized in this case too. - MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); - - quotaManager->FinalizeOriginEviction(origins); - - return false; - } - - AssertNoOverflow(quotaManager->mTemporaryStorageUsage, delta); - newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage + delta; - - NS_ASSERTION(newTemporaryStorageUsage <= - quotaManager->mTemporaryStorageLimit, "How come?!"); - - // Ok, we successfully freed enough space and the operation can continue - // without throwing the quota error. - mOriginInfo->mUsage = newUsage; - groupInfo->mUsage = newGroupUsage; - quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;; - - // Some other thread could increase the size in the meantime, but no more - // than this one. - MOZ_ASSERT(mSize < aSize); - mSize = aSize; - - // Finally, release IO thread only objects and allow next synchronized - // ops for the evicted origins. - MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); - - quotaManager->FinalizeOriginEviction(origins); - - return true; - } - - mOriginInfo->mUsage = newUsage; - groupInfo->mUsage = newGroupUsage; - quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage; - - mSize = aSize; - - return true; -} - -void -OriginInfo::LockedDecreaseUsage(int64_t aSize) -{ - AssertCurrentThreadOwnsQuotaMutex(); - - AssertNoUnderflow(mUsage, aSize); - mUsage -= aSize; - - AssertNoUnderflow(mGroupInfo->mUsage, aSize); - mGroupInfo->mUsage -= aSize; - - QuotaManager* quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager); - - AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, aSize); - quotaManager->mTemporaryStorageUsage -= aSize; -} - -already_AddRefed -GroupInfo::LockedGetOriginInfo(const nsACString& aOrigin) -{ - AssertCurrentThreadOwnsQuotaMutex(); - - for (uint32_t index = 0; index < mOriginInfos.Length(); index++) { - nsRefPtr& originInfo = mOriginInfos[index]; - - if (originInfo->mOrigin == aOrigin) { - nsRefPtr result = originInfo; - return result.forget(); - } - } - - return nullptr; -} - -void -GroupInfo::LockedAddOriginInfo(OriginInfo* aOriginInfo) -{ - AssertCurrentThreadOwnsQuotaMutex(); - - NS_ASSERTION(!mOriginInfos.Contains(aOriginInfo), - "Replacing an existing entry!"); - mOriginInfos.AppendElement(aOriginInfo); - - AssertNoOverflow(mUsage, aOriginInfo->mUsage); - mUsage += aOriginInfo->mUsage; - - QuotaManager* quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager); - - AssertNoOverflow(quotaManager->mTemporaryStorageUsage, aOriginInfo->mUsage); - quotaManager->mTemporaryStorageUsage += aOriginInfo->mUsage; -} - -void -GroupInfo::LockedRemoveOriginInfo(const nsACString& aOrigin) -{ - AssertCurrentThreadOwnsQuotaMutex(); - - for (uint32_t index = 0; index < mOriginInfos.Length(); index++) { - if (mOriginInfos[index]->mOrigin == aOrigin) { - AssertNoUnderflow(mUsage, mOriginInfos[index]->mUsage); - mUsage -= mOriginInfos[index]->mUsage; - - QuotaManager* quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager); - - AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, - mOriginInfos[index]->mUsage); - quotaManager->mTemporaryStorageUsage -= mOriginInfos[index]->mUsage; - - mOriginInfos.RemoveElementAt(index); - - return; - } - } -} - -void -GroupInfo::LockedRemoveOriginInfos() -{ - AssertCurrentThreadOwnsQuotaMutex(); - - QuotaManager* quotaManager = QuotaManager::Get(); - MOZ_ASSERT(quotaManager); - - for (uint32_t index = mOriginInfos.Length(); index > 0; index--) { - OriginInfo* originInfo = mOriginInfos[index - 1]; - - AssertNoUnderflow(mUsage, originInfo->mUsage); - mUsage -= originInfo->mUsage; - - AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, originInfo->mUsage); - quotaManager->mTemporaryStorageUsage -= originInfo->mUsage; - - mOriginInfos.RemoveElementAt(index - 1); - } -} - -nsRefPtr& -GroupInfoPair::GetGroupInfoForPersistenceType(PersistenceType aPersistenceType) -{ - switch (aPersistenceType) { - case PERSISTENCE_TYPE_TEMPORARY: - return mTemporaryStorageGroupInfo; - case PERSISTENCE_TYPE_DEFAULT: - return mDefaultStorageGroupInfo; - - case PERSISTENCE_TYPE_PERSISTENT: - case PERSISTENCE_TYPE_INVALID: - default: - MOZ_CRASH("Bad persistence type value!"); - } -} diff --git a/dom/quota/QuotaObject.h b/dom/quota/QuotaObject.h index 004bca3612..56f065eecc 100644 --- a/dom/quota/QuotaObject.h +++ b/dom/quota/QuotaObject.h @@ -15,8 +15,6 @@ BEGIN_QUOTA_NAMESPACE -class GroupInfo; -class GroupInfoPair; class OriginInfo; class QuotaManager; @@ -65,195 +63,6 @@ private: int64_t mSize; }; -class OriginInfo final -{ - friend class GroupInfo; - friend class QuotaManager; - friend class QuotaObject; - -public: - OriginInfo(GroupInfo* aGroupInfo, const nsACString& aOrigin, bool aIsApp, - uint64_t aUsage, int64_t aAccessTime) - : mGroupInfo(aGroupInfo), mOrigin(aOrigin), mUsage(aUsage), - mAccessTime(aAccessTime), mIsApp(aIsApp) - { - MOZ_COUNT_CTOR(OriginInfo); - } - - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OriginInfo) - - int64_t - AccessTime() const - { - return mAccessTime; - } - -private: - // Private destructor, to discourage deletion outside of Release(): - ~OriginInfo() - { - MOZ_COUNT_DTOR(OriginInfo); - - MOZ_ASSERT(!mQuotaObjects.Count()); - } - - void - LockedDecreaseUsage(int64_t aSize); - - void - LockedUpdateAccessTime(int64_t aAccessTime) - { - AssertCurrentThreadOwnsQuotaMutex(); - - mAccessTime = aAccessTime; - } - - nsDataHashtable mQuotaObjects; - - GroupInfo* mGroupInfo; - const nsCString mOrigin; - uint64_t mUsage; - int64_t mAccessTime; - const bool mIsApp; -}; - -class OriginInfoLRUComparator -{ -public: - bool - Equals(const OriginInfo* a, const OriginInfo* b) const - { - return - a && b ? a->AccessTime() == b->AccessTime() : !a && !b ? true : false; - } - - bool - LessThan(const OriginInfo* a, const OriginInfo* b) const - { - return a && b ? a->AccessTime() < b->AccessTime() : b ? true : false; - } -}; - -class GroupInfo final -{ - friend class GroupInfoPair; - friend class OriginInfo; - friend class QuotaManager; - friend class QuotaObject; - -public: - GroupInfo(GroupInfoPair* aGroupInfoPair, PersistenceType aPersistenceType, - const nsACString& aGroup) - : mGroupInfoPair(aGroupInfoPair), mPersistenceType(aPersistenceType), - mGroup(aGroup), mUsage(0) - { - MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT); - - MOZ_COUNT_CTOR(GroupInfo); - } - - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GroupInfo) - -private: - // Private destructor, to discourage deletion outside of Release(): - ~GroupInfo() - { - MOZ_COUNT_DTOR(GroupInfo); - } - - already_AddRefed - LockedGetOriginInfo(const nsACString& aOrigin); - - void - LockedAddOriginInfo(OriginInfo* aOriginInfo); - - void - LockedRemoveOriginInfo(const nsACString& aOrigin); - - void - LockedRemoveOriginInfos(); - - bool - LockedHasOriginInfos() - { - AssertCurrentThreadOwnsQuotaMutex(); - - return !mOriginInfos.IsEmpty(); - } - - nsTArray > mOriginInfos; - - GroupInfoPair* mGroupInfoPair; - PersistenceType mPersistenceType; - nsCString mGroup; - uint64_t mUsage; -}; - -class GroupInfoPair -{ - friend class QuotaManager; - friend class QuotaObject; - -public: - GroupInfoPair() - { - MOZ_COUNT_CTOR(GroupInfoPair); - } - - ~GroupInfoPair() - { - MOZ_COUNT_DTOR(GroupInfoPair); - } - -private: - already_AddRefed - LockedGetGroupInfo(PersistenceType aPersistenceType) - { - AssertCurrentThreadOwnsQuotaMutex(); - MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT); - - nsRefPtr groupInfo = - GetGroupInfoForPersistenceType(aPersistenceType); - return groupInfo.forget(); - } - - void - LockedSetGroupInfo(PersistenceType aPersistenceType, GroupInfo* aGroupInfo) - { - AssertCurrentThreadOwnsQuotaMutex(); - MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT); - - nsRefPtr& groupInfo = - GetGroupInfoForPersistenceType(aPersistenceType); - groupInfo = aGroupInfo; - } - - void - LockedClearGroupInfo(PersistenceType aPersistenceType) - { - AssertCurrentThreadOwnsQuotaMutex(); - MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT); - - nsRefPtr& groupInfo = - GetGroupInfoForPersistenceType(aPersistenceType); - groupInfo = nullptr; - } - - bool - LockedHasGroupInfos() - { - AssertCurrentThreadOwnsQuotaMutex(); - - return mTemporaryStorageGroupInfo || mDefaultStorageGroupInfo; - } - - nsRefPtr& - GetGroupInfoForPersistenceType(PersistenceType aPersistenceType); - - nsRefPtr mTemporaryStorageGroupInfo; - nsRefPtr mDefaultStorageGroupInfo; -}; - END_QUOTA_NAMESPACE #endif // mozilla_dom_quota_quotaobject_h__ diff --git a/dom/quota/StorageMatcher.h b/dom/quota/StorageMatcher.h deleted file mode 100644 index 0610f857c6..0000000000 --- a/dom/quota/StorageMatcher.h +++ /dev/null @@ -1,191 +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_quota_patternmatcher_h__ -#define mozilla_dom_quota_patternmatcher_h__ - -#include "mozilla/dom/quota/QuotaCommon.h" - -#include "ArrayCluster.h" -#include "Utilities.h" - -BEGIN_QUOTA_NAMESPACE - -template > -class StorageMatcher : public ValueType -{ - typedef StorageMatcher SelfType; - - struct Closure - { - explicit Closure(SelfType& aSelf) - : mSelf(aSelf), mPattern(EmptyCString()), mIndexes(nullptr) - { } - - Closure(SelfType& aSelf, const nsACString& aPattern) - : mSelf(aSelf), mPattern(aPattern), mIndexes(nullptr) - { } - - Closure(SelfType& aSelf, const nsTArray* aIndexes) - : mSelf(aSelf), mPattern(EmptyCString()), mIndexes(aIndexes) - { } - - Closure(SelfType& aSelf, const nsACString& aPattern, - const nsTArray* aIndexes) - : mSelf(aSelf), mPattern(aPattern), mIndexes(aIndexes) - { } - - SelfType& mSelf; - const nsACString& mPattern; - const nsTArray* mIndexes; - }; - -public: - template - void - Find(const nsBaseHashtable& aHashtable, - const nsACString& aPattern) - { - SelfType::Clear(); - - Closure closure(*this, aPattern); - aHashtable.EnumerateRead(SelfType::MatchPattern, &closure); - } - - template - void - Find(const nsBaseHashtable& aHashtable, - const nsTArray* aIndexes) - { - SelfType::Clear(); - - Closure closure(*this, aIndexes); - aHashtable.EnumerateRead(SelfType::MatchIndexes, &closure); - } - - template - void - Find(const nsBaseHashtable& aHashtable, - uint32_t aIndex) - { - nsAutoTArray indexes; - indexes.AppendElement(aIndex); - - Find(aHashtable, &indexes); - } - - template - void - Find(const nsBaseHashtable& aHashtable, - const nsACString& aPattern, - const nsTArray* aIndexes) - { - SelfType::Clear(); - - Closure closure(*this, aPattern, aIndexes); - aHashtable.EnumerateRead(SelfType::MatchPatternAndIndexes, &closure); - } - - template - void - Find(const nsBaseHashtable& aHashtable, - const nsACString& aPattern, - uint32_t aIndex) - { - nsAutoTArray indexes; - indexes.AppendElement(aIndex); - - Find(aHashtable, aPattern, &indexes); - } - - template - void - Find(const nsBaseHashtable& aHashtable) - { - SelfType::Clear(); - - Closure closure(*this); - aHashtable.EnumerateRead(SelfType::MatchAll, &closure); - } - -private: - static PLDHashOperator - MatchPattern(const nsACString& aKey, - BaseType* aValue, - void* aUserArg) - { - MOZ_ASSERT(!aKey.IsEmpty(), "Empty key!"); - MOZ_ASSERT(aValue, "Null pointer!"); - MOZ_ASSERT(aUserArg, "Null pointer!"); - - Closure* closure = static_cast(aUserArg); - - if (PatternMatchesOrigin(closure->mPattern, aKey)) { - aValue->AppendElementsTo(closure->mSelf); - } - - return PL_DHASH_NEXT; - } - - static PLDHashOperator - MatchIndexes(const nsACString& aKey, - BaseType* aValue, - void* aUserArg) - { - MOZ_ASSERT(!aKey.IsEmpty(), "Empty key!"); - MOZ_ASSERT(aValue, "Null pointer!"); - MOZ_ASSERT(aUserArg, "Null pointer!"); - - Closure* closure = static_cast(aUserArg); - - for (uint32_t index = 0; index < closure->mIndexes->Length(); index++) { - aValue->AppendElementsTo(closure->mIndexes->ElementAt(index), - closure->mSelf); - } - - return PL_DHASH_NEXT; - } - - static PLDHashOperator - MatchPatternAndIndexes(const nsACString& aKey, - BaseType* aValue, - void* aUserArg) - { - MOZ_ASSERT(!aKey.IsEmpty(), "Empty key!"); - MOZ_ASSERT(aValue, "Null pointer!"); - MOZ_ASSERT(aUserArg, "Null pointer!"); - - Closure* closure = static_cast(aUserArg); - - if (PatternMatchesOrigin(closure->mPattern, aKey)) { - for (uint32_t index = 0; index < closure->mIndexes->Length(); index++) { - aValue->AppendElementsTo(closure->mIndexes->ElementAt(index), - closure->mSelf); - } - } - - return PL_DHASH_NEXT; - } - - static PLDHashOperator - MatchAll(const nsACString& aKey, - BaseType* aValue, - void* aUserArg) - { - MOZ_ASSERT(!aKey.IsEmpty(), "Empty key!"); - MOZ_ASSERT(aValue, "Null pointer!"); - MOZ_ASSERT(aUserArg, "Null pointer!"); - - Closure* closure = static_cast(aUserArg); - aValue->AppendElementsTo(closure->mSelf); - - return PL_DHASH_NEXT; - } -}; - -END_QUOTA_NAMESPACE - -#endif // mozilla_dom_quota_patternmatcher_h__ diff --git a/dom/quota/UsageInfo.h b/dom/quota/UsageInfo.h index 5c32967810..c10413f62c 100644 --- a/dom/quota/UsageInfo.h +++ b/dom/quota/UsageInfo.h @@ -30,6 +30,16 @@ public: return mCanceled; } + nsresult + Cancel() + { + if (mCanceled.exchange(true)) { + NS_WARNING("Canceled more than once?!"); + return NS_ERROR_UNEXPECTED; + } + return NS_OK; + } + void AppendToDatabaseUsage(uint64_t aUsage) { diff --git a/dom/quota/moz.build b/dom/quota/moz.build index a602412439..d8988055a6 100644 --- a/dom/quota/moz.build +++ b/dom/quota/moz.build @@ -12,15 +12,9 @@ XPIDL_SOURCES += [ XPIDL_MODULE = 'dom_quota' -EXPORTS += [ - 'nsIOfflineStorage.h', -] - EXPORTS.mozilla.dom.quota += [ - 'ArrayCluster.h', 'Client.h', 'FileStreams.h', - 'OriginOrPatternString.h', 'PersistenceType.h', 'QuotaCommon.h', 'QuotaManager.h', @@ -32,7 +26,6 @@ EXPORTS.mozilla.dom.quota += [ UNIFIED_SOURCES += [ 'FileStreams.cpp', 'QuotaManager.cpp', - 'QuotaObject.cpp', ] FAIL_ON_WARNINGS = True diff --git a/dom/quota/nsIOfflineStorage.h b/dom/quota/nsIOfflineStorage.h deleted file mode 100644 index 83d4b1b257..0000000000 --- a/dom/quota/nsIOfflineStorage.h +++ /dev/null @@ -1,104 +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 nsIOfflineStorage_h__ -#define nsIOfflineStorage_h__ - -#include "mozilla/dom/quota/PersistenceType.h" - -#define NS_OFFLINESTORAGE_IID \ - {0x91c57bf2, 0x0eda, 0x4db6, {0x9f, 0xf6, 0xcb, 0x38, 0x26, 0x8d, 0xb3, 0x01}} - -namespace mozilla { -namespace dom { - -class ContentParent; - -namespace quota { - -class Client; - -} -} -} - -class nsIOfflineStorage : public nsISupports -{ -public: - typedef mozilla::dom::ContentParent ContentParent; - typedef mozilla::dom::quota::Client Client; - typedef mozilla::dom::quota::PersistenceType PersistenceType; - - NS_DECLARE_STATIC_IID_ACCESSOR(NS_OFFLINESTORAGE_IID) - - NS_IMETHOD_(const nsACString&) - Id() = 0; - - NS_IMETHOD_(Client*) - GetClient() = 0; - - NS_IMETHOD_(bool) - IsOwnedByProcess(ContentParent* aOwner) = 0; - - NS_IMETHOD_(PersistenceType) - Type() - { - return mPersistenceType; - } - - NS_IMETHOD_(const nsACString&) - Group() - { - return mGroup; - } - - NS_IMETHOD_(const nsACString&) - Origin() = 0; - - // Implementation of this method should close the storage (without aborting - // running operations nor discarding pending operations). - NS_IMETHOD_(nsresult) - Close() = 0; - - // Implementation of this method should close the storage, all running - // operations should be aborted and pending operations should be discarded. - NS_IMETHOD_(void) - Invalidate() = 0; - -protected: - nsIOfflineStorage() - : mPersistenceType(mozilla::dom::quota::PERSISTENCE_TYPE_INVALID) - { } - - virtual ~nsIOfflineStorage() - { } - - PersistenceType mPersistenceType; - nsCString mGroup; -}; - -NS_DEFINE_STATIC_IID_ACCESSOR(nsIOfflineStorage, NS_OFFLINESTORAGE_IID) - -#define NS_DECL_NSIOFFLINESTORAGE \ - NS_IMETHOD_(const nsACString&) \ - Id() override; \ - \ - NS_IMETHOD_(Client*) \ - GetClient() override; \ - \ - NS_IMETHOD_(bool) \ - IsOwnedByProcess(ContentParent* aOwner) override; \ - \ - NS_IMETHOD_(const nsACString&) \ - Origin() override; \ - \ - NS_IMETHOD_(nsresult) \ - Close() override; \ - \ - NS_IMETHOD_(void) \ - Invalidate() override; - -#endif // nsIOfflineStorage_h__ diff --git a/dom/speakermanager/SpeakerManagerService.cpp b/dom/speakermanager/SpeakerManagerService.cpp index 159d605fe7..118a92c8b1 100644 --- a/dom/speakermanager/SpeakerManagerService.cpp +++ b/dom/speakermanager/SpeakerManagerService.cpp @@ -30,7 +30,7 @@ SpeakerManagerService::GetOrCreateSpeakerManagerService() { MOZ_ASSERT(NS_IsMainThread()); - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return SpeakerManagerServiceChild::GetOrCreateSpeakerManagerService(); } @@ -52,7 +52,7 @@ SpeakerManagerService::GetSpeakerManagerService() { MOZ_ASSERT(NS_IsMainThread()); - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return SpeakerManagerServiceChild::GetSpeakerManagerService(); } @@ -62,7 +62,7 @@ SpeakerManagerService::GetSpeakerManagerService() void SpeakerManagerService::Shutdown() { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return SpeakerManagerServiceChild::Shutdown(); } @@ -182,7 +182,7 @@ SpeakerManagerService::SpeakerManagerService() mVisible(false) { MOZ_COUNT_CTOR(SpeakerManagerService); - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { nsCOMPtr obs = mozilla::services::GetObserverService(); if (obs) { obs->AddObserver(this, "ipc:content-shutdown", false); diff --git a/dom/storage/DOMStorageCache.cpp b/dom/storage/DOMStorageCache.cpp index fc3fca536c..d944f4fe1b 100644 --- a/dom/storage/DOMStorageCache.cpp +++ b/dom/storage/DOMStorageCache.cpp @@ -761,7 +761,7 @@ DOMStorageCache::StartDatabase() return sDatabase; } - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { nsAutoPtr db(new DOMStorageDBThread()); nsresult rv = db->Init(); @@ -803,7 +803,7 @@ DOMStorageCache::StopDatabase() sDatabaseDown = true; nsresult rv = sDatabase->Shutdown(); - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { delete sDatabase; } else { DOMStorageDBChild* child = static_cast(sDatabase); diff --git a/dom/storage/DOMStorageManager.cpp b/dom/storage/DOMStorageManager.cpp index 7fed58ac77..89e7354d32 100644 --- a/dom/storage/DOMStorageManager.cpp +++ b/dom/storage/DOMStorageManager.cpp @@ -592,7 +592,7 @@ DOMStorageManager::Observe(const char* aTopic, const nsACString& aScopePrefix) } if (!strcmp(aTopic, "test-flushed")) { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { nsCOMPtr obs = mozilla::services::GetObserverService(); if (obs) { obs->NotifyObservers(nullptr, "domstorage-test-flushed", nullptr); @@ -615,7 +615,7 @@ DOMLocalStorageManager::DOMLocalStorageManager() NS_ASSERTION(!sSelf, "Somebody is trying to do_CreateInstance(\"@mozilla/dom/localStorage-manager;1\""); sSelf = this; - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { // Do this only on the child process. The thread IPC bridge // is also used to communicate chrome observer notifications. // Note: must be called after we set sSelf @@ -633,7 +633,7 @@ DOMLocalStorageManager::~DOMLocalStorageManager() DOMSessionStorageManager::DOMSessionStorageManager() : DOMStorageManager(SessionStorage) { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { // Do this only on the child process. The thread IPC bridge // is also used to communicate chrome observer notifications. DOMStorageCache::StartDatabase(); diff --git a/dom/storage/DOMStorageObserver.cpp b/dom/storage/DOMStorageObserver.cpp index 0b09a36de5..29a20ed987 100644 --- a/dom/storage/DOMStorageObserver.cpp +++ b/dom/storage/DOMStorageObserver.cpp @@ -75,7 +75,7 @@ DOMStorageObserver::Init() #ifdef DOM_STORAGE_TESTS // Testing obs->AddObserver(sSelf, "domstorage-test-flush-force", true); - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { // Only to forward to child process. obs->AddObserver(sSelf, "domstorage-test-flushed", true); } diff --git a/dom/system/gonk/AudioManager.cpp b/dom/system/gonk/AudioManager.cpp index 2287f080b1..cfd1a21b39 100644 --- a/dom/system/gonk/AudioManager.cpp +++ b/dom/system/gonk/AudioManager.cpp @@ -546,7 +546,7 @@ already_AddRefed AudioManager::GetInstance() { // Avoid createing AudioManager from content process. - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { MOZ_CRASH("Non-chrome processes should not get here."); } diff --git a/dom/system/gonk/AutoMounter.cpp b/dom/system/gonk/AutoMounter.cpp index 871b00b3b5..f76294c9ea 100644 --- a/dom/system/gonk/AutoMounter.cpp +++ b/dom/system/gonk/AutoMounter.cpp @@ -1159,7 +1159,7 @@ ShutdownAutoMounterIOThread() static void SetAutoMounterModeIOThread(const int32_t& aMode) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); MOZ_ASSERT(sAutoMounter); @@ -1169,7 +1169,7 @@ SetAutoMounterModeIOThread(const int32_t& aMode) static void SetAutoMounterSharingModeIOThread(const nsCString& aVolumeName, const bool& aAllowSharing) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); MOZ_ASSERT(sAutoMounter); @@ -1179,7 +1179,7 @@ SetAutoMounterSharingModeIOThread(const nsCString& aVolumeName, const bool& aAll static void AutoMounterFormatVolumeIOThread(const nsCString& aVolumeName) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); MOZ_ASSERT(sAutoMounter); @@ -1189,7 +1189,7 @@ AutoMounterFormatVolumeIOThread(const nsCString& aVolumeName) static void AutoMounterMountVolumeIOThread(const nsCString& aVolumeName) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); MOZ_ASSERT(sAutoMounter); @@ -1199,7 +1199,7 @@ AutoMounterMountVolumeIOThread(const nsCString& aVolumeName) static void AutoMounterUnmountVolumeIOThread(const nsCString& aVolumeName) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); MOZ_ASSERT(sAutoMounter); diff --git a/dom/system/gonk/NetworkWorker.cpp b/dom/system/gonk/NetworkWorker.cpp index ed67d3182c..37af675744 100644 --- a/dom/system/gonk/NetworkWorker.cpp +++ b/dom/system/gonk/NetworkWorker.cpp @@ -134,7 +134,7 @@ NetworkWorker::~NetworkWorker() already_AddRefed NetworkWorker::FactoryCreate() { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return nullptr; } diff --git a/dom/system/gonk/SystemWorkerManager.cpp b/dom/system/gonk/SystemWorkerManager.cpp index 14e9a062d4..a5622f57c9 100644 --- a/dom/system/gonk/SystemWorkerManager.cpp +++ b/dom/system/gonk/SystemWorkerManager.cpp @@ -72,7 +72,7 @@ SystemWorkerManager::~SystemWorkerManager() nsresult SystemWorkerManager::Init() { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return NS_ERROR_NOT_AVAILABLE; } diff --git a/dom/system/gonk/Volume.cpp b/dom/system/gonk/Volume.cpp index fce564d403..772c16b099 100644 --- a/dom/system/gonk/Volume.cpp +++ b/dom/system/gonk/Volume.cpp @@ -233,7 +233,7 @@ Volume::SetConfig(const nsCString& aConfigName, const nsCString& aConfigValue) void Volume::SetMediaPresent(bool aMediaPresent) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); // mMediaPresent is slightly redunant to the state, however @@ -308,7 +308,7 @@ Volume::SetUnmountRequested(bool aUnmountRequested) void Volume::SetState(Volume::STATE aNewState) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); if (aNewState == mState) { return; @@ -380,7 +380,7 @@ Volume::SetState(Volume::STATE aNewState) void Volume::SetMountPoint(const nsCSubstring& aMountPoint) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); if (mMountPoint.Equals(aMountPoint)) { @@ -393,7 +393,7 @@ Volume::SetMountPoint(const nsCSubstring& aMountPoint) void Volume::StartMount(VolumeResponseCallback* aCallback) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); StartCommand(new VolumeActionCommand(this, "mount", "", aCallback)); @@ -402,7 +402,7 @@ Volume::StartMount(VolumeResponseCallback* aCallback) void Volume::StartUnmount(VolumeResponseCallback* aCallback) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); StartCommand(new VolumeActionCommand(this, "unmount", "force", aCallback)); @@ -411,7 +411,7 @@ Volume::StartUnmount(VolumeResponseCallback* aCallback) void Volume::StartFormat(VolumeResponseCallback* aCallback) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); StartCommand(new VolumeActionCommand(this, "format", "", aCallback)); @@ -420,7 +420,7 @@ Volume::StartFormat(VolumeResponseCallback* aCallback) void Volume::StartShare(VolumeResponseCallback* aCallback) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); StartCommand(new VolumeActionCommand(this, "share", "ums", aCallback)); @@ -429,7 +429,7 @@ Volume::StartShare(VolumeResponseCallback* aCallback) void Volume::StartUnshare(VolumeResponseCallback* aCallback) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); StartCommand(new VolumeActionCommand(this, "unshare", "ums", aCallback)); @@ -438,7 +438,7 @@ Volume::StartUnshare(VolumeResponseCallback* aCallback) void Volume::StartCommand(VolumeCommand* aCommand) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); VolumeManager::PostCommand(aCommand); @@ -448,7 +448,7 @@ Volume::StartCommand(VolumeCommand* aCommand) void Volume::RegisterVolumeObserver(Volume::EventObserver* aObserver, const char* aName) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); sEventObserverList.AddObserver(aObserver); @@ -468,7 +468,7 @@ Volume::RegisterVolumeObserver(Volume::EventObserver* aObserver, const char* aNa void Volume::UnregisterVolumeObserver(Volume::EventObserver* aObserver, const char* aName) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); sEventObserverList.RemoveObserver(aObserver); @@ -483,7 +483,7 @@ Volume::UpdateMountLock(const nsACString& aVolumeName, const int32_t& aMountGeneration, const bool& aMountLocked) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); RefPtr vol = VolumeManager::FindVolumeByName(aVolumeName); @@ -500,7 +500,7 @@ Volume::UpdateMountLock(const nsACString& aVolumeName, void Volume::HandleVoldResponse(int aResponseCode, nsCWhitespaceTokenizer& aTokenizer) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); // The volume name will have already been parsed, and the tokenizer will point diff --git a/dom/system/gonk/nsVolume.cpp b/dom/system/gonk/nsVolume.cpp index 41de770be9..138e2236c8 100644 --- a/dom/system/gonk/nsVolume.cpp +++ b/dom/system/gonk/nsVolume.cpp @@ -246,7 +246,7 @@ NS_IMETHODIMP nsVolume::GetIsHotSwappable(bool *aIsHotSwappable) NS_IMETHODIMP nsVolume::Format() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); XRE_GetIOMessageLoop()->PostTask( FROM_HERE, @@ -258,7 +258,7 @@ NS_IMETHODIMP nsVolume::Format() /* static */ void nsVolume::FormatVolumeIOThread(const nsCString& aVolume) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); if (VolumeManager::State() != VolumeManager::VOLUMES_READY) { @@ -270,7 +270,7 @@ void nsVolume::FormatVolumeIOThread(const nsCString& aVolume) NS_IMETHODIMP nsVolume::Mount() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); XRE_GetIOMessageLoop()->PostTask( FROM_HERE, @@ -282,7 +282,7 @@ NS_IMETHODIMP nsVolume::Mount() /* static */ void nsVolume::MountVolumeIOThread(const nsCString& aVolume) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); if (VolumeManager::State() != VolumeManager::VOLUMES_READY) { @@ -294,7 +294,7 @@ void nsVolume::MountVolumeIOThread(const nsCString& aVolume) NS_IMETHODIMP nsVolume::Unmount() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); XRE_GetIOMessageLoop()->PostTask( FROM_HERE, @@ -306,7 +306,7 @@ NS_IMETHODIMP nsVolume::Unmount() /* static */ void nsVolume::UnmountVolumeIOThread(const nsCString& aVolume) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); if (VolumeManager::State() != VolumeManager::VOLUMES_READY) { @@ -364,7 +364,7 @@ void nsVolume::Set(nsIVolume* aVolume) mMountGeneration = volMountGeneration; - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { // Child processes just track the state, not maintain it. aVolume->GetIsMountLocked(&mMountLocked); return; @@ -386,7 +386,7 @@ void nsVolume::Set(nsIVolume* aVolume) void nsVolume::UpdateMountLock(const nsAString& aMountLockState) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); // There are 3 states, unlocked, locked-background, and locked-foreground @@ -397,7 +397,7 @@ nsVolume::UpdateMountLock(const nsAString& aMountLockState) void nsVolume::UpdateMountLock(bool aMountLocked) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); if (aMountLocked == mMountLocked) { @@ -447,7 +447,7 @@ nsVolume::SetState(int32_t aState) { static int32_t sMountGeneration = 0; - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(IsFake()); diff --git a/dom/system/gonk/nsVolumeService.cpp b/dom/system/gonk/nsVolumeService.cpp index 80fa8c8714..033a97d175 100644 --- a/dom/system/gonk/nsVolumeService.cpp +++ b/dom/system/gonk/nsVolumeService.cpp @@ -68,7 +68,7 @@ nsVolumeService::Shutdown() if (!sSingleton) { return; } - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { sSingleton = nullptr; return; } @@ -92,7 +92,7 @@ nsVolumeService::nsVolumeService() { sSingleton = this; - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { // VolumeServiceIOThread and the WakeLock listener should only run in the // parent, so we return early. return; @@ -271,7 +271,7 @@ nsVolumeService::GetVolumeNames(nsIArray** aVolNames) void nsVolumeService::GetVolumesForIPC(nsTArray* aResult) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); MonitorAutoLock autoLock(mArrayMonitor); @@ -299,7 +299,7 @@ nsVolumeService::GetVolumesForIPC(nsTArray* aResult) void nsVolumeService::RecvVolumesFromParent(const nsTArray& aVolumes) { - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { // We are the parent. Therefore our volumes are already correct. return; } @@ -340,7 +340,7 @@ void nsVolumeService::CheckMountLock(const nsAString& aMountLockName, const nsAString& aMountLockState) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); nsRefPtr vol = FindVolumeByMountLockName(aMountLockName); @@ -438,8 +438,8 @@ nsVolumeService::UpdateVolume(nsIVolume* aVolume, bool aNotifyObservers) NS_IMETHODIMP nsVolumeService::CreateFakeVolume(const nsAString& name, const nsAString& path) { - if (XRE_GetProcessType() == GeckoProcessType_Default) { - nsRefPtr vol = new nsVolume(name, path, nsIVolume::STATE_MOUNTED, + if (XRE_IsParentProcess()) { + nsRefPtr vol = new nsVolume(name, path, nsIVolume::STATE_INIT, -1 /* mountGeneration */, true /* isMediaPresent */, false /* isSharing */, @@ -448,6 +448,7 @@ nsVolumeService::CreateFakeVolume(const nsAString& name, const nsAString& path) false /* isUnmounting */, false /* isRemovable */, false /* isHotSwappable */); + vol->SetState(nsIVolume::STATE_MOUNTED); vol->LogState(); UpdateVolume(vol.get()); return NS_OK; @@ -460,7 +461,7 @@ nsVolumeService::CreateFakeVolume(const nsAString& name, const nsAString& path) NS_IMETHODIMP nsVolumeService::SetFakeVolumeState(const nsAString& name, int32_t state) { - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { nsRefPtr vol; { MonitorAutoLock autoLock(mArrayMonitor); @@ -488,7 +489,7 @@ nsVolumeService::SetFakeVolumeState(const nsAString& name, int32_t state) NS_IMETHODIMP nsVolumeService::RemoveFakeVolume(const nsAString& name) { - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { SetFakeVolumeState(name, nsIVolume::STATE_NOMEDIA); RemoveVolumeByName(name); return NS_OK; @@ -511,7 +512,7 @@ nsVolumeService::RemoveVolumeByName(const nsAString& aName) } mVolumeArray.RemoveElement(vol); - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { nsCOMPtr obs = GetObserverService(); if (!obs) { return; diff --git a/dom/system/gonk/tests/marionette/test_fakevolume.js b/dom/system/gonk/tests/marionette/test_fakevolume.js index 5ac84d92ab..cafc1055c4 100644 --- a/dom/system/gonk/tests/marionette/test_fakevolume.js +++ b/dom/system/gonk/tests/marionette/test_fakevolume.js @@ -18,12 +18,8 @@ ok(vol, "volume shouldn't be null"); is(volName, vol.name, "name"); is(mountPoint, vol.mountPoint, "moutnPoint"); -is(Ci.nsIVolume.STATE_INIT, vol.state, "state"); - - -let oldMountGen = vol.mountGeneration; -volumeService.SetFakeVolumeState(volName, Ci.nsIVolume.STATE_MOUNTED); is(Ci.nsIVolume.STATE_MOUNTED, vol.state, "state"); -ok(vol.mountGeneration > oldMountGen, "mount generation should be incremented"); + +ok(vol.mountGeneration > 0, "mount generation should not be zero"); finish(); diff --git a/dom/telephony/Telephony.cpp b/dom/telephony/Telephony.cpp index fa31778198..8eb9cb83fb 100644 --- a/dom/telephony/Telephony.cpp +++ b/dom/telephony/Telephony.cpp @@ -739,7 +739,7 @@ NS_CreateTelephonyService() { nsCOMPtr service; - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { service = new mozilla::dom::telephony::TelephonyIPCService(); } else { #if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL) diff --git a/dom/voicemail/Voicemail.cpp b/dom/voicemail/Voicemail.cpp index 41097f1e0a..c9d729a4d8 100644 --- a/dom/voicemail/Voicemail.cpp +++ b/dom/voicemail/Voicemail.cpp @@ -250,7 +250,7 @@ NS_CreateVoicemailService() { nsCOMPtr service; - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { service = new mozilla::dom::voicemail::VoicemailIPCService(); } else { #if defined(MOZ_B2G_RIL) diff --git a/dom/wifi/WifiCertService.cpp b/dom/wifi/WifiCertService.cpp index 745e72ea45..80b61296b5 100644 --- a/dom/wifi/WifiCertService.cpp +++ b/dom/wifi/WifiCertService.cpp @@ -443,7 +443,7 @@ WifiCertService::~WifiCertService() already_AddRefed WifiCertService::FactoryCreate() { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return nullptr; } diff --git a/dom/wifi/WifiProxyService.cpp b/dom/wifi/WifiProxyService.cpp index 6efd7df7c0..50a4e08d85 100644 --- a/dom/wifi/WifiProxyService.cpp +++ b/dom/wifi/WifiProxyService.cpp @@ -153,7 +153,7 @@ WifiProxyService::~WifiProxyService() already_AddRefed WifiProxyService::FactoryCreate() { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return nullptr; } diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index 2c171a902c..3d7c7ebd46 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -403,7 +403,7 @@ ServiceWorkerManager::Init() MOZ_ASSERT(NS_SUCCEEDED(rv)); } - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { nsRefPtr swr = ServiceWorkerRegistrar::Get(); MOZ_ASSERT(swr); @@ -4374,7 +4374,7 @@ void ServiceWorkerManager::PropagateRemoveAll() { AssertIsOnMainThread(); - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); if (!mActor) { nsRefPtr runnable = new PropagateRemoveAllRunnable(); @@ -4431,21 +4431,21 @@ ServiceWorkerManager::Observe(nsISupports* aSubject, const char16_t* aData) { if (strcmp(aTopic, PURGE_SESSION_HISTORY) == 0) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); RemoveAll(); PropagateRemoveAll(); return NS_OK; } if (strcmp(aTopic, PURGE_DOMAIN_DATA) == 0) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); nsAutoString domain(aData); RemoveAndPropagate(NS_ConvertUTF16toUTF8(domain)); return NS_OK; } if (strcmp(aTopic, WEBAPPS_CLEAR_DATA) == 0) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); nsCOMPtr params = do_QueryInterface(aSubject); if (NS_WARN_IF(!params)) { @@ -4485,7 +4485,7 @@ ServiceWorkerManager::Observe(nsISupports* aSubject, if (obs) { obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { obs->RemoveObserver(this, PURGE_SESSION_HISTORY); obs->RemoveObserver(this, PURGE_DOMAIN_DATA); obs->RemoveObserver(this, WEBAPPS_CLEAR_DATA); diff --git a/dom/workers/ServiceWorkerPeriodicUpdater.cpp b/dom/workers/ServiceWorkerPeriodicUpdater.cpp index fa22a26113..8fea986a80 100644 --- a/dom/workers/ServiceWorkerPeriodicUpdater.cpp +++ b/dom/workers/ServiceWorkerPeriodicUpdater.cpp @@ -28,7 +28,7 @@ ServiceWorkerPeriodicUpdater::sPeriodicUpdatesEnabled = true; already_AddRefed ServiceWorkerPeriodicUpdater::GetSingleton() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); if (!sInstance) { sInstance = new ServiceWorkerPeriodicUpdater(); diff --git a/dom/workers/ServiceWorkerRegistrar.cpp b/dom/workers/ServiceWorkerRegistrar.cpp index 4ac667730b..4f4ae0d589 100644 --- a/dom/workers/ServiceWorkerRegistrar.cpp +++ b/dom/workers/ServiceWorkerRegistrar.cpp @@ -48,7 +48,7 @@ ServiceWorkerRegistrar::Initialize() { MOZ_ASSERT(!gServiceWorkerRegistrar); - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return; } @@ -70,7 +70,7 @@ ServiceWorkerRegistrar::Initialize() /* static */ already_AddRefed ServiceWorkerRegistrar::Get() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(gServiceWorkerRegistrar); nsRefPtr service = gServiceWorkerRegistrar.get(); diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 92b8f273cd..28711fde64 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -2596,7 +2596,7 @@ private: mAlreadyMappedToAddon = true; - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { // Only try to access the service from the main process. return; } diff --git a/embedding/browser/nsWebBrowser.cpp b/embedding/browser/nsWebBrowser.cpp index 95d9e518d7..e5935bed66 100644 --- a/embedding/browser/nsWebBrowser.cpp +++ b/embedding/browser/nsWebBrowser.cpp @@ -1174,7 +1174,7 @@ NS_IMETHODIMP nsWebBrowser::Create() } mDocShellAsNav->SetSessionHistory(mInitInfo->sessionHistory); - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { // Hook up global history. Do not fail if we can't - just warn. rv = EnableGlobalHistory(mShouldEnableHistory); NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "EnableGlobalHistory() failed"); diff --git a/embedding/components/windowwatcher/nsWindowWatcher.cpp b/embedding/components/windowwatcher/nsWindowWatcher.cpp index 79dbca4b54..1d8d4796bc 100644 --- a/embedding/components/windowwatcher/nsWindowWatcher.cpp +++ b/embedding/components/windowwatcher/nsWindowWatcher.cpp @@ -486,7 +486,7 @@ nsWindowWatcher::OpenWindowInternal(nsIDOMWindow* aParent, } MOZ_ASSERT_IF(openedFromRemoteTab, - XRE_GetProcessType() == GeckoProcessType_Default); + XRE_IsParentProcess()); NS_ENSURE_ARG_POINTER(aResult); *aResult = 0; diff --git a/extensions/cookie/nsPermissionManager.cpp b/extensions/cookie/nsPermissionManager.cpp index 94b58d4166..cbf461827f 100644 --- a/extensions/cookie/nsPermissionManager.cpp +++ b/extensions/cookie/nsPermissionManager.cpp @@ -46,7 +46,7 @@ using mozilla::unused; // ha! static bool IsChildProcess() { - return XRE_GetProcessType() == GeckoProcessType_Content; + return XRE_IsContentProcess(); } /** diff --git a/extensions/spellcheck/src/mozSpellChecker.cpp b/extensions/spellcheck/src/mozSpellChecker.cpp index 1da9118947..adc34f2898 100644 --- a/extensions/spellcheck/src/mozSpellChecker.cpp +++ b/extensions/spellcheck/src/mozSpellChecker.cpp @@ -49,7 +49,7 @@ mozSpellChecker::~mozSpellChecker() mPersonalDictionary = nullptr; if (mEngine) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content); + MOZ_ASSERT(XRE_IsContentProcess()); mEngine->Send__delete__(mEngine); MOZ_ASSERT(!mEngine); } @@ -59,7 +59,7 @@ nsresult mozSpellChecker::Init() { mSpellCheckingEngine = nullptr; - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { mozilla::dom::ContentChild* contentChild = mozilla::dom::ContentChild::GetSingleton(); MOZ_ASSERT(contentChild); mEngine = new RemoteSpellcheckEngineChild(this); @@ -130,7 +130,7 @@ mozSpellChecker::CheckWord(const nsAString &aWord, bool *aIsMisspelled, nsTArray nsresult result; bool correct; - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { nsString wordwrapped = nsString(aWord); bool rv; if (aSuggestions) { @@ -309,7 +309,7 @@ mozSpellChecker::GetPersonalDictionary(nsTArray *aWordList) NS_IMETHODIMP mozSpellChecker::GetDictionaryList(nsTArray *aDictionaryList) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { ContentChild *child = ContentChild::GetSingleton(); child->GetAvailableDictionaries(*aDictionaryList); return NS_OK; @@ -357,7 +357,7 @@ mozSpellChecker::GetDictionaryList(nsTArray *aDictionaryList) NS_IMETHODIMP mozSpellChecker::GetCurrentDictionary(nsAString &aDictionary) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { aDictionary = mCurrentDictionary; return NS_OK; } @@ -376,7 +376,7 @@ mozSpellChecker::GetCurrentDictionary(nsAString &aDictionary) NS_IMETHODIMP mozSpellChecker::SetCurrentDictionary(const nsAString &aDictionary) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { nsString wrappedDict = nsString(aDictionary); bool isSuccess; mEngine->SendSetDictionary(wrappedDict, &isSuccess); @@ -536,7 +536,7 @@ mozSpellChecker::GetCurrentBlockIndex(nsITextServicesDocument *aDoc, int32_t *ou nsresult mozSpellChecker::GetEngineList(nsCOMArray* aSpellCheckingEngines) { - MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Content); + MOZ_ASSERT(!XRE_IsContentProcess()); nsresult rv; bool hasMoreEngines; diff --git a/gfx/gl/GLScreenBuffer.cpp b/gfx/gl/GLScreenBuffer.cpp index 11ad348c4b..3601516ddf 100755 --- a/gfx/gl/GLScreenBuffer.cpp +++ b/gfx/gl/GLScreenBuffer.cpp @@ -40,7 +40,6 @@ GLScreenBuffer::Create(GLContext* gl, return Move(ret); } - layers::TextureFlags flags = layers::TextureFlags::ORIGIN_BOTTOM_LEFT; if (!caps.premultAlpha) { flags |= layers::TextureFlags::NON_PREMULTIPLIED; diff --git a/gfx/layers/apz/public/GeckoContentController.h b/gfx/layers/apz/public/GeckoContentController.h index 71ca8991d6..25c4ec50ab 100644 --- a/gfx/layers/apz/public/GeckoContentController.h +++ b/gfx/layers/apz/public/GeckoContentController.h @@ -148,6 +148,11 @@ public: virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent) {} + /** + * Notify content that the repaint requests have been flushed. + */ + virtual void NotifyFlushComplete() = 0; + GeckoContentController() {} virtual void Destroy() {} diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index 25210b8a54..009bd4098e 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -551,6 +551,20 @@ APZCTreeManager::UpdateHitTestingTree(TreeBuildingState& aState, return node; } +void +APZCTreeManager::FlushApzRepaints(uint64_t aLayersId) +{ + APZCTM_LOG("Flushing repaints for layers id %" PRIu64, aLayersId); + { // scope lock + MonitorAutoLock lock(mTreeLock); + FlushPendingRepaintRecursively(mRootNode, aLayersId); + } + const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId); + MOZ_ASSERT(state && state->mController); + NS_DispatchToMainThread(NS_NewRunnableMethod( + state->mController.get(), &GeckoContentController::NotifyFlushComplete)); +} + nsEventStatus APZCTreeManager::ReceiveInputEvent(InputData& aEvent, ScrollableLayerGuid* aOutTargetGuid, @@ -571,12 +585,26 @@ APZCTreeManager::ReceiveInputEvent(InputData& aEvent, result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId); break; } case SCROLLWHEEL_INPUT: { + FlushRepaintsToClearScreenToGeckoTransform(); + ScrollWheelInput& wheelInput = aEvent.AsScrollWheelInput(); nsRefPtr apzc = GetTargetAPZC(wheelInput.mOrigin, &hitResult); if (apzc) { MOZ_ASSERT(hitResult == HitLayer || hitResult == HitDispatchToContentRegion); + // For wheel events, the call to ReceiveInputEvent below may result in + // scrolling, which changes the async transform. However, the event we + // want to pass to gecko should be the pre-scroll event coordinates, + // transformed into the gecko space. (pre-scroll because the mouse + // cursor is stationary during wheel scrolling, unlike touchmove + // events). Also, since we just flushed the pending repaints the + // transform to gecko space is a no-op so we can just skip it. + MOZ_ASSERT( + (GetScreenToApzcTransform(apzc) * GetApzcToGeckoTransform(apzc)) + .NudgeToIntegersFixedEpsilon() + .IsIdentity()); + result = mInputQueue->ReceiveInputEvent( apzc, /* aTargetConfirmed = */ hitResult == HitLayer, @@ -584,10 +612,6 @@ APZCTreeManager::ReceiveInputEvent(InputData& aEvent, // Update the out-parameters so they are what the caller expects. apzc->GetGuid(aOutTargetGuid); - Matrix4x4 transformToGecko = GetScreenToApzcTransform(apzc) - * GetApzcToGeckoTransform(apzc); - wheelInput.mOrigin = - TransformTo(transformToGecko, wheelInput.mOrigin); } break; } case PANGESTURE_INPUT: { @@ -665,17 +689,7 @@ APZCTreeManager::GetTouchInputBlockAPZC(const MultiTouchInput& aEvent, return apzc.forget(); } - { // In this block we flush repaint requests for the entire APZ tree. We need to do this - // at the start of an input block for a number of reasons. One of the reasons is so that - // after we untransform the event into gecko space, it doesn't end up under something - // else. Another reason is that if we hit-test this event and end up on a layer's - // dispatch-to-content region we cannot be sure we actually got the correct layer. We - // have to fall back to the gecko hit-test to handle this case, but we can't untransform - // the event we send to gecko because we don't know the layer to untransform with - // respect to. - MonitorAutoLock lock(mTreeLock); - FlushRepaintsRecursively(mRootNode); - } + FlushRepaintsToClearScreenToGeckoTransform(); apzc = GetTargetAPZC(aEvent.mTouches[0].mScreenPoint, aOutHitResult); for (size_t i = 1; i < aEvent.mTouches.Length(); i++) { @@ -767,6 +781,12 @@ APZCTreeManager::ProcessTouchInput(MultiTouchInput& aInput, Matrix4x4 transformToApzc = GetScreenToApzcTransform(mApzcForInputBlock); Matrix4x4 transformToGecko = GetApzcToGeckoTransform(mApzcForInputBlock); Matrix4x4 outTransform = transformToApzc * transformToGecko; + if (aInput.mType == MultiTouchInput::MULTITOUCH_START) { + // For touch-start events we should have flushed all pending repaints + // above as part of the GetTouchInputBlockAPZC call, and so we expect + // the apzc-to-gecko transform to be empty. + MOZ_ASSERT(outTransform.NudgeToIntegersFixedEpsilon().IsIdentity()); + } for (size_t i = 0; i < aInput.mTouches.Length(); i++) { SingleTouchData& touchData = aInput.mTouches[i]; touchData.mScreenPoint = TransformTo( @@ -1058,6 +1078,32 @@ APZCTreeManager::UpdateZoomConstraintsRecursively(HitTestingTreeNode* aNode, } } +void +APZCTreeManager::FlushRepaintsToClearScreenToGeckoTransform() +{ + // As the name implies, we flush repaint requests for the entire APZ tree in + // order to clear the screen-to-gecko transform (aka the "untransform" applied + // to incoming input events before they can be passed on to Gecko). + // + // The primary reason we do this is to avoid the problem where input events, + // after being untransformed, end up hit-testing differently in Gecko. This + // might happen in cases where the input event lands on content that is async- + // scrolled into view, but Gecko still thinks it is out of view given the + // visible area of a scrollframe. + // + // Another reason we want to clear the untransform is that if our APZ hit-test + // hits a dispatch-to-content region then that's an ambiguous result and we + // need to ask Gecko what actually got hit. In order to do this we need to + // untransform the input event into Gecko space - but to do that we need to + // know which APZC got hit! This leads to a circular dependency; the only way + // to get out of it is to make sure that the untransform for all the possible + // matched APZCs is the same. It is simplest to ensure that by flushing the + // pending repaint requests, which makes all of the untransforms empty (and + // therefore equal). + MonitorAutoLock lock(mTreeLock); + FlushRepaintsRecursively(mRootNode); +} + void APZCTreeManager::FlushRepaintsRecursively(HitTestingTreeNode* aNode) { @@ -1072,6 +1118,23 @@ APZCTreeManager::FlushRepaintsRecursively(HitTestingTreeNode* aNode) } } +void +APZCTreeManager::FlushPendingRepaintRecursively(HitTestingTreeNode* aNode, uint64_t aLayersId) +{ + mTreeLock.AssertCurrentThreadOwns(); + + for (HitTestingTreeNode* node = aNode; node; node = node->GetPrevSibling()) { + if (node->IsPrimaryHolder()) { + AsyncPanZoomController* apzc = node->GetApzc(); + MOZ_ASSERT(apzc); + if (apzc->GetGuid().mLayersId == aLayersId) { + apzc->FlushRepaintIfPending(); + } + } + FlushPendingRepaintRecursively(node->GetLastChild(), aLayersId); + } +} + void APZCTreeManager::CancelAnimation(const ScrollableLayerGuid &aGuid) { diff --git a/gfx/layers/apz/src/APZCTreeManager.h b/gfx/layers/apz/src/APZCTreeManager.h index 8ac843ddad..ae3b678e26 100644 --- a/gfx/layers/apz/src/APZCTreeManager.h +++ b/gfx/layers/apz/src/APZCTreeManager.h @@ -130,6 +130,13 @@ public: uint64_t aOriginatingLayersId, uint32_t aPaintSequenceNumber); + /** + * Walk the tree of APZCs and flushes the repaint requests for all the APZCS + * corresponding to the given layers id. Finally, sends a flush complete + * notification to the GeckoContentController for the layers id. + */ + void FlushApzRepaints(uint64_t aLayersId); + /** * General handler for incoming input events. Manipulates the frame metrics * based on what type of input it is. For example, a PinchGestureEvent will @@ -435,7 +442,9 @@ private: void UpdateWheelTransaction(WidgetInputEvent& aEvent); void UpdateZoomConstraintsRecursively(HitTestingTreeNode* aNode, const ZoomConstraints& aConstraints); + void FlushRepaintsToClearScreenToGeckoTransform(); void FlushRepaintsRecursively(HitTestingTreeNode* aNode); + void FlushPendingRepaintRecursively(HitTestingTreeNode* aNode, uint64_t aLayersId); already_AddRefed RecycleOrCreateNode(TreeBuildingState& aState, AsyncPanZoomController* aApzc, diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index f16c2e4b25..9100eea797 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -1469,14 +1469,20 @@ AsyncPanZoomController::CanScroll(const ScrollWheelInput& aEvent) const return false; } - return CanScroll(delta.x, delta.y); + return CanScrollWithWheel(delta); } bool -AsyncPanZoomController::CanScroll(double aDeltaX, double aDeltaY) const +AsyncPanZoomController::CanScrollWithWheel(const LayoutDevicePoint& aDelta) const { ReentrantMonitorAutoEnter lock(mMonitor); - return mX.CanScroll(aDeltaX) || mY.CanScroll(aDeltaY); + if (mX.CanScroll(aDelta.x)) { + return true; + } + if (mY.CanScroll(aDelta.y) && mFrameMetrics.AllowVerticalScrollWithWheel()) { + return true; + } + return false; } bool @@ -1489,9 +1495,10 @@ AsyncPanZoomController::AllowScrollHandoffInWheelTransaction() const nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEvent) { LayoutDevicePoint delta = GetScrollWheelDelta(aEvent); + APZC_LOG("%p got a scroll-wheel with delta %s\n", this, Stringify(delta).c_str()); if ((delta.x || delta.y) && - !CanScroll(delta.x, delta.y) && + !CanScrollWithWheel(delta) && mInputQueue->GetCurrentWheelTransaction()) { // We can't scroll this apz anymore, so we simply drop the event. @@ -2426,9 +2433,18 @@ void AsyncPanZoomController::FlushRepaintForNewInputBlock() { UpdateSharedCompositorFrameMetrics(); } +void AsyncPanZoomController::FlushRepaintIfPending() { + // Just tell the paint throttler to send the pending repaint request if + // there is one. + ReentrantMonitorAutoEnter lock(mMonitor); + mPaintThrottler.TaskComplete(GetFrameTime()); +} + bool AsyncPanZoomController::SnapBackIfOverscrolled() { ReentrantMonitorAutoEnter lock(mMonitor); - if (IsOverscrolled()) { + // It's possible that we're already in the middle of an overscroll + // animation - if so, don't start a new one. + if (IsOverscrolled() && mState != OVERSCROLL_ANIMATION) { APZC_LOG("%p is overscrolled, starting snap-back\n", this); StartOverscrollAnimation(ParentLayerPoint(0, 0)); return true; diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index e14f6565b0..766b5dc04c 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -182,6 +182,11 @@ public: */ void NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint); + /** + * Flush any pending repaint request. + */ + void FlushRepaintIfPending(); + /** * The platform implementation must set the compositor parent so that we can * request composites. @@ -362,7 +367,7 @@ public: // Return whether or not a scroll delta will be able to scroll in either // direction. - bool CanScroll(double aDeltaX, double aDeltaY) const; + bool CanScrollWithWheel(const LayoutDevicePoint& aDelta) const; void NotifyMozMouseScrollEvent(const nsString& aString) const; diff --git a/gfx/layers/apz/util/APZCCallbackHelper.cpp b/gfx/layers/apz/util/APZCCallbackHelper.cpp index f0bce502ba..d0e77ef28f 100644 --- a/gfx/layers/apz/util/APZCCallbackHelper.cpp +++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp @@ -746,6 +746,15 @@ APZCCallbackHelper::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrol true, true); } +void +APZCCallbackHelper::NotifyFlushComplete() +{ + MOZ_ASSERT(NS_IsMainThread()); + nsCOMPtr observerService = mozilla::services::GetObserverService(); + MOZ_ASSERT(observerService); + observerService->NotifyObservers(nullptr, "apz-repaints-flushed", nullptr); +} + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/apz/util/APZCCallbackHelper.h b/gfx/layers/apz/util/APZCCallbackHelper.h index f48b72f088..0493127dd0 100644 --- a/gfx/layers/apz/util/APZCCallbackHelper.h +++ b/gfx/layers/apz/util/APZCCallbackHelper.h @@ -156,6 +156,9 @@ public: /* Notify content of a mouse scroll testing event. */ static void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent); + + /* Notify content that the repaint flush is complete. */ + static void NotifyFlushComplete(); }; } // namespace layers diff --git a/gfx/layers/apz/util/ChromeProcessController.cpp b/gfx/layers/apz/util/ChromeProcessController.cpp index 8cfd74596b..5bc5aa0568 100644 --- a/gfx/layers/apz/util/ChromeProcessController.cpp +++ b/gfx/layers/apz/util/ChromeProcessController.cpp @@ -200,3 +200,10 @@ ChromeProcessController::NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& a APZCCallbackHelper::NotifyMozMouseScrollEvent(aScrollId, aEvent); } + +void +ChromeProcessController::NotifyFlushComplete() +{ + MOZ_ASSERT(NS_IsMainThread()); + APZCCallbackHelper::NotifyFlushComplete(); +} diff --git a/gfx/layers/apz/util/ChromeProcessController.h b/gfx/layers/apz/util/ChromeProcessController.h index 5a4104f505..6580e1ec5b 100644 --- a/gfx/layers/apz/util/ChromeProcessController.h +++ b/gfx/layers/apz/util/ChromeProcessController.h @@ -56,6 +56,7 @@ public: int aArg) override; virtual void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent) override; + virtual void NotifyFlushComplete() override; private: nsCOMPtr mWidget; nsRefPtr mAPZEventState; diff --git a/gfx/layers/client/ClientCanvasLayer.cpp b/gfx/layers/client/ClientCanvasLayer.cpp index 090beb317f..e224cc8e49 100644 --- a/gfx/layers/client/ClientCanvasLayer.cpp +++ b/gfx/layers/client/ClientCanvasLayer.cpp @@ -88,8 +88,7 @@ ClientCanvasLayer::Initialize(const Data& aData) factory = MakeUnique(mGLContext, caps, forwarder, mFlags); #else if (mGLContext->GetContextType() == GLContextType::EGL) { - bool isCrossProcess = (XRE_GetProcessType() != GeckoProcessType_Default); - if (!isCrossProcess) { + if (XRE_IsParentProcess()) { factory = SurfaceFactory_EGLImage::Create(mGLContext, caps, forwarder, mFlags); } diff --git a/gfx/layers/client/ClientLayerManager.cpp b/gfx/layers/client/ClientLayerManager.cpp index 1d11bd4e28..f3ad29b67a 100644 --- a/gfx/layers/client/ClientLayerManager.cpp +++ b/gfx/layers/client/ClientLayerManager.cpp @@ -221,7 +221,7 @@ ClientLayerManager::BeginTransactionWithTarget(gfxContext* aTarget) // If we have a non-default target, we need to let our shadow manager draw // to it. This will happen at the end of the transaction. - if (aTarget && XRE_GetProcessType() == GeckoProcessType_Default) { + if (aTarget && XRE_IsParentProcess()) { mShadowTarget = aTarget; } else { NS_ASSERTION(!aTarget, @@ -370,7 +370,7 @@ ClientLayerManager::GetRemoteRenderer() CompositorChild* ClientLayerManager::GetCompositorChild() { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return CompositorChild::Get(); } return GetRemoteRenderer(); diff --git a/gfx/layers/ipc/CompositorChild.cpp b/gfx/layers/ipc/CompositorChild.cpp index ebbcaacc00..50b490839f 100644 --- a/gfx/layers/ipc/CompositorChild.cpp +++ b/gfx/layers/ipc/CompositorChild.cpp @@ -162,7 +162,7 @@ CompositorChild::OpenSameProcess(CompositorParent* aParent) CompositorChild::Get() { // This is only expected to be used in child processes. - MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default); + MOZ_ASSERT(!XRE_IsParentProcess()); return sCompositor; } diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index 7b5e1f59cd..38cd10b766 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -1347,6 +1347,19 @@ CompositorParent::RecvRequestOverfill() return true; } +void +CompositorParent::FlushApzRepaints(const LayerTransactionParent* aLayerTree) +{ + MOZ_ASSERT(mApzcTreeManager); + uint64_t layersId = aLayerTree->GetId(); + if (layersId == 0) { + // The request is coming from the parent-process layer tree, so we should + // use the compositor's root layer tree id. + layersId = mRootLayerTreeID; + } + mApzcTreeManager->FlushApzRepaints(layersId); +} + void CompositorParent::GetAPZTestData(const LayerTransactionParent* aLayerTree, APZTestData* aOutData) @@ -1758,6 +1771,7 @@ public: virtual void LeaveTestMode(LayerTransactionParent* aLayerTree) override; virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree) override; + virtual void FlushApzRepaints(const LayerTransactionParent* aLayerTree) override; virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree, APZTestData* aOutData) override; virtual void SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree, @@ -2172,6 +2186,21 @@ CrossProcessCompositorParent::ApplyAsyncProperties( state->mParent->ApplyAsyncProperties(aLayerTree); } +void +CrossProcessCompositorParent::FlushApzRepaints(const LayerTransactionParent* aLayerTree) +{ + uint64_t id = aLayerTree->GetId(); + MOZ_ASSERT(id != 0); + const CompositorParent::LayerTreeState* state = + CompositorParent::GetIndirectShadowTree(id); + if (!state) { + return; + } + + MOZ_ASSERT(state->mParent); + state->mParent->FlushApzRepaints(aLayerTree); +} + void CrossProcessCompositorParent::GetAPZTestData(const LayerTransactionParent* aLayerTree, APZTestData* aOutData) diff --git a/gfx/layers/ipc/CompositorParent.h b/gfx/layers/ipc/CompositorParent.h index 8f5c6a847d..dd616323bb 100644 --- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -270,6 +270,7 @@ public: virtual void LeaveTestMode(LayerTransactionParent* aLayerTree) override; virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree) override; + virtual void FlushApzRepaints(const LayerTransactionParent* aLayerTree) override; virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree, APZTestData* aOutData) override; virtual void SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree, diff --git a/gfx/layers/ipc/LayerTransactionParent.cpp b/gfx/layers/ipc/LayerTransactionParent.cpp index 988220f9db..967e61b5d2 100644 --- a/gfx/layers/ipc/LayerTransactionParent.cpp +++ b/gfx/layers/ipc/LayerTransactionParent.cpp @@ -808,6 +808,13 @@ LayerTransactionParent::RecvSetAsyncZoom(const FrameMetrics::ViewID& aScrollID, return true; } +bool +LayerTransactionParent::RecvFlushApzRepaints() +{ + mShadowLayersManager->FlushApzRepaints(this); + return true; +} + bool LayerTransactionParent::RecvGetAPZTestData(APZTestData* aOutData) { diff --git a/gfx/layers/ipc/LayerTransactionParent.h b/gfx/layers/ipc/LayerTransactionParent.h index c4f8e87f64..8e835f7d1d 100644 --- a/gfx/layers/ipc/LayerTransactionParent.h +++ b/gfx/layers/ipc/LayerTransactionParent.h @@ -135,6 +135,7 @@ protected: const int32_t& aX, const int32_t& aY) override; virtual bool RecvSetAsyncZoom(const FrameMetrics::ViewID& aId, const float& aValue) override; + virtual bool RecvFlushApzRepaints() override; virtual bool RecvGetAPZTestData(APZTestData* aOutData) override; virtual bool RecvRequestProperty(const nsString& aProperty, float* aValue) override; virtual bool RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId, diff --git a/gfx/layers/ipc/PLayerTransaction.ipdl b/gfx/layers/ipc/PLayerTransaction.ipdl index 249d79097c..764eb67370 100644 --- a/gfx/layers/ipc/PLayerTransaction.ipdl +++ b/gfx/layers/ipc/PLayerTransaction.ipdl @@ -94,6 +94,9 @@ parent: // Useful for testing rendering of async zooming. sync SetAsyncZoom(ViewID id, float zoom); + // Flush any pending APZ repaints to the main thread. + async FlushApzRepaints(); + // Drop any front buffers that might be retained on the compositor // side. async ClearCachedResources(); diff --git a/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp b/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp index 61f15bb5a7..33530ff36d 100644 --- a/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp +++ b/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp @@ -148,7 +148,7 @@ ParamTraits::Read(const Message* aMsg, aResult->mRef.mOwner = owner; aResult->mRef.mKey = index; - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { // If we are in chrome process, we can just get GraphicBuffer directly from // SharedBufferManagerParent. aResult->mGraphicBuffer = SharedBufferManagerParent::GetGraphicBuffer(aResult->mRef); @@ -215,7 +215,7 @@ GetGraphicBufferFrom(MaybeMagicGrallocBufferHandle aHandle) { if (aHandle.type() != MaybeMagicGrallocBufferHandle::TMagicGrallocBufferHandle) { if (aHandle.type() == MaybeMagicGrallocBufferHandle::TGrallocBufferRef) { - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { return SharedBufferManagerParent::GetGraphicBuffer(aHandle.get_GrallocBufferRef()); } return SharedBufferManagerChild::GetSingleton()->GetGraphicBuffer(aHandle.get_GrallocBufferRef().mKey); diff --git a/gfx/layers/ipc/ShadowLayersManager.h b/gfx/layers/ipc/ShadowLayersManager.h index 0c67c65910..f0240e0fe7 100644 --- a/gfx/layers/ipc/ShadowLayersManager.h +++ b/gfx/layers/ipc/ShadowLayersManager.h @@ -36,6 +36,7 @@ public: const TimeStamp& aTime) { return true; } virtual void LeaveTestMode(LayerTransactionParent* aLayerTree) { } virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree) = 0; + virtual void FlushApzRepaints(const LayerTransactionParent* aLayerTree) = 0; virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree, APZTestData* aOutData) { } virtual void SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree, diff --git a/gfx/layers/opengl/TextureClientOGL.cpp b/gfx/layers/opengl/TextureClientOGL.cpp index 0e6df0933a..fe23227255 100644 --- a/gfx/layers/opengl/TextureClientOGL.cpp +++ b/gfx/layers/opengl/TextureClientOGL.cpp @@ -29,7 +29,7 @@ EGLImageTextureClient::EGLImageTextureClient(ISurfaceAllocator* aAllocator, , mSize(aSize) , mIsLocked(false) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default, + MOZ_ASSERT(XRE_IsParentProcess(), "Can't pass an `EGLImage` between processes."); AddFlags(TextureFlags::DEALLOCATE_CLIENT); @@ -85,7 +85,7 @@ SurfaceTextureClient::SurfaceTextureClient(ISurfaceAllocator* aAllocator, , mSize(aSize) , mIsLocked(false) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default, + MOZ_ASSERT(XRE_IsParentProcess(), "Can't pass pointers between processes."); // Our data is always owned externally. diff --git a/gfx/tests/gtest/TestAsyncPanZoomController.cpp b/gfx/tests/gtest/TestAsyncPanZoomController.cpp index 95d7592ba9..2f1962a11a 100644 --- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp +++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp @@ -70,6 +70,7 @@ public: MOCK_METHOD3(SendAsyncScrollDOMEvent, void(bool aIsRoot, const CSSRect &aContentRect, const CSSSize &aScrollableSize)); MOCK_METHOD2(PostDelayedTask, void(Task* aTask, int aDelayMs)); MOCK_METHOD3(NotifyAPZStateChange, void(const ScrollableLayerGuid& aGuid, APZStateChange aChange, int aArg)); + MOCK_METHOD0(NotifyFlushComplete, void()); }; class MockContentControllerDelayed : public MockContentController { @@ -1697,6 +1698,8 @@ protected: layerBound.width, layerBound.height)); metrics.SetScrollableRect(aScrollableRect); metrics.SetScrollOffset(CSSPoint(0, 0)); + metrics.SetPageScrollAmount(LayoutDeviceIntSize(50, 100)); + metrics.SetAllowVerticalScrollWithWheel(); aLayer->SetFrameMetrics(metrics); aLayer->SetClipRect(Some(ViewAs(layerBound))); if (!aScrollableRect.IsEqualEdges(CSSRect(-1, -1, -1, -1))) { @@ -2222,6 +2225,37 @@ TEST_F(APZHitTestingTester, TestRepaintFlushOnNewInputBlock) { mcc->RunThroughDelayedTasks(); } +TEST_F(APZHitTestingTester, TestRepaintFlushOnWheelEvents) { + // The purpose of this test is to ensure that wheel events trigger a repaint + // flush as per bug 1166871, and that the wheel event untransform is a no-op. + + CreateSimpleScrollingLayer(); + ScopedLayerTreeRegistration registration(0, root, mcc); + manager->UpdateHitTestingTree(nullptr, root, false, 0, 0); + TestAsyncPanZoomController* apzcroot = ApzcOf(root); + + EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(3)); + ScreenPoint origin(100, 50); + for (int i = 0; i < 3; i++) { + ScrollWheelInput swi(MillisecondsSinceStartup(mTime), mTime, 0, + ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL, + origin, 0, 10); + EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr)); + EXPECT_EQ(origin, swi.mOrigin); + + ViewTransform viewTransform; + ParentLayerPoint point; + apzcroot->SampleContentTransformForFrame(mTime, &viewTransform, point); + EXPECT_EQ(0, point.x); + EXPECT_EQ((i + 1) * 10, point.y); + EXPECT_EQ(0, viewTransform.mTranslation.x); + EXPECT_EQ((i + 1) * -10, viewTransform.mTranslation.y); + + mTime += TimeDuration::FromMilliseconds(5); + } + mcc->RunThroughDelayedTasks(); +} + TEST_F(APZHitTestingTester, Bug1148350) { CreateBug1148350LayerTree(); ScopedLayerTreeRegistration registration(0, root, mcc); diff --git a/gfx/thebes/gfxAndroidPlatform.cpp b/gfx/thebes/gfxAndroidPlatform.cpp index 5fafca5f42..f05553cf2f 100644 --- a/gfx/thebes/gfxAndroidPlatform.cpp +++ b/gfx/thebes/gfxAndroidPlatform.cpp @@ -392,7 +392,7 @@ gfxAndroidPlatform::RequiresLinearZoom() // On B2G, we need linear zoom for the browser, but otherwise prefer // the improved glyph spacing that results from respecting the device // pixel resolution for glyph layout (see bug 816614). - return XRE_GetProcessType() == GeckoProcessType_Content && + return XRE_IsContentProcess() && ContentChild::GetSingleton()->IsForBrowser(); #endif diff --git a/gfx/thebes/gfxFT2FontList.cpp b/gfx/thebes/gfxFT2FontList.cpp index e9bb2be44d..66a1434eec 100644 --- a/gfx/thebes/gfxFT2FontList.cpp +++ b/gfx/thebes/gfxFT2FontList.cpp @@ -635,7 +635,7 @@ public: nullptr }; - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default, + MOZ_ASSERT(XRE_IsParentProcess(), "StartupCacheFontNameCache should only be used in chrome " "process"); mCache = mozilla::scache::StartupCache::GetSingleton(); @@ -1156,7 +1156,7 @@ gfxFT2FontList::FindFonts() mCodepointsWithNoFonts.SetRange(0,0x1f); // C0 controls mCodepointsWithNoFonts.SetRange(0x7f,0x9f); // C1 controls - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { // Content process: ask the Chrome process to give us the list InfallibleTArray fonts; mozilla::dom::ContentChild::GetSingleton()->SendReadFontList(&fonts); diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index c409b2bcd5..63f5ff1fcd 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -704,7 +704,7 @@ gfxPlatform::InitLayersIPC() AsyncTransactionTrackersHolder::Initialize(); - if (XRE_GetProcessType() == GeckoProcessType_Default) + if (XRE_IsParentProcess()) { mozilla::layers::CompositorParent::StartUp(); #ifndef MOZ_WIDGET_GONK @@ -726,7 +726,7 @@ gfxPlatform::ShutdownLayersIPC() } sLayersIPCIsUp = false; - if (XRE_GetProcessType() == GeckoProcessType_Default) + if (XRE_IsParentProcess()) { // This must happen after the shutdown of media and widgets, which // are triggered by the NS_XPCOM_SHUTDOWN_OBSERVER_ID notification. @@ -1060,7 +1060,7 @@ gfxPlatform::ComputeTileSize() { // The tile size should be picked in the parent processes // and sent to the child processes over IPDL GetTileSize. - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { NS_RUNTIMEABORT("wrong process."); } diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index d91faf976f..e7f3050a71 100644 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -633,7 +633,7 @@ gfxWindowsPlatform::CreateDevice(nsRefPtr &adapter1, mD2DDevice = cairo_d2d_create_device_from_d3d10device(device); // Setup a pref for future launch optimizaitons when in main process. - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { Preferences::SetInt(kFeatureLevelPref, featureLevelIndex); } } diff --git a/image/decoders/icon/android/nsIconChannel.cpp b/image/decoders/icon/android/nsIconChannel.cpp index c690c836e6..cbbbec8b9d 100644 --- a/image/decoders/icon/android/nsIconChannel.cpp +++ b/image/decoders/icon/android/nsIconChannel.cpp @@ -80,7 +80,7 @@ moz_icon_to_channel(nsIURI* aURI, const nsACString& aFileExt, *(out++) = height; nsresult rv; - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { rv = GetIconForExtension(aFileExt, aIconSize, out); } else { rv = CallRemoteGetIconForExtension(aFileExt, aIconSize, out); diff --git a/ipc/glue/BackgroundImpl.cpp b/ipc/glue/BackgroundImpl.cpp index 0084a01a7a..b783fc2866 100644 --- a/ipc/glue/BackgroundImpl.cpp +++ b/ipc/glue/BackgroundImpl.cpp @@ -51,7 +51,7 @@ #define CRASH_IN_CHILD_PROCESS(_msg) \ do { \ - if (IsMainProcess()) { \ + if (XRE_IsParentProcess()) { \ MOZ_ASSERT(false, _msg); \ } else { \ MOZ_CRASH(_msg); \ @@ -69,32 +69,17 @@ namespace { // Utility Functions // ----------------------------------------------------------------------------- -bool -IsMainProcess() -{ - static const bool isMainProcess = - XRE_GetProcessType() == GeckoProcessType_Default; - return isMainProcess; -} - -#ifdef DEBUG -bool -IsChildProcess() -{ - return !IsMainProcess(); -} -#endif void AssertIsInMainProcess() { - MOZ_ASSERT(IsMainProcess()); + MOZ_ASSERT(XRE_IsParentProcess()); } void AssertIsInChildProcess() { - MOZ_ASSERT(IsChildProcess()); + MOZ_ASSERT(!XRE_IsParentProcess()); } void @@ -2047,7 +2032,7 @@ ChildImpl::OpenProtocolOnMainThread(nsIEventTarget* aEventTarget) "shutdown has started!"); } - if (IsMainProcess()) { + if (XRE_IsParentProcess()) { nsRefPtr parentCallback = new ParentCreateCallback(aEventTarget); diff --git a/ipc/glue/BackgroundParentImpl.cpp b/ipc/glue/BackgroundParentImpl.cpp index dbf20d9aac..f4f94e4604 100644 --- a/ipc/glue/BackgroundParentImpl.cpp +++ b/ipc/glue/BackgroundParentImpl.cpp @@ -46,7 +46,7 @@ namespace { void AssertIsInMainProcess() { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); } void diff --git a/ipc/glue/InputStreamUtils.cpp b/ipc/glue/InputStreamUtils.cpp index 0da3128f4c..9e66cf723e 100644 --- a/ipc/glue/InputStreamUtils.cpp +++ b/ipc/glue/InputStreamUtils.cpp @@ -106,7 +106,7 @@ DeserializeInputStream(const InputStreamParams& aParams, // When the input stream already exists in this process, all we need to do // is retrieve the original instead of sending any data over the wire. case InputStreamParams::TRemoteInputStreamParams: { - if (NS_WARN_IF(XRE_GetProcessType() != GeckoProcessType_Default)) { + if (NS_WARN_IF(!XRE_IsParentProcess())) { return nullptr; } diff --git a/ipc/glue/ScopedXREEmbed.cpp b/ipc/glue/ScopedXREEmbed.cpp index 342decf42b..cf1753f2c8 100644 --- a/ipc/glue/ScopedXREEmbed.cpp +++ b/ipc/glue/ScopedXREEmbed.cpp @@ -67,7 +67,7 @@ ScopedXREEmbed::Start() NS_ENSURE_TRUE_VOID(localFile); #ifdef OS_MACOSX - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { // We're an XPCOM-using subprocess. Walk out of // [subprocess].app/Contents/MacOS to the real GRE dir. rv = localFile->GetParent(getter_AddRefs(parent)); diff --git a/js/xpconnect/loader/mozJSComponentLoader.cpp b/js/xpconnect/loader/mozJSComponentLoader.cpp index 5273490e21..ac60981c54 100644 --- a/js/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/xpconnect/loader/mozJSComponentLoader.cpp @@ -583,8 +583,9 @@ mozJSComponentLoader::PrepareObjectForLocation(JSContext* aCx, if (testFile) { *aRealFile = true; - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { RootedObject locationObj(aCx); + rv = xpc->WrapNative(aCx, obj, aComponentFile, NS_GET_IID(nsIFile), locationObj.address()); diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 352d63ceba..23aabadb50 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1322,7 +1322,7 @@ XPCJSRuntime::InterruptCallback(JSContext* cx) if (!nsContentUtils::IsInitialized()) return true; - bool contentProcess = XRE_GetProcessType() == GeckoProcessType_Content; + bool contentProcess = XRE_IsContentProcess(); // This is at least the second interrupt callback we've received since // returning to the event loop. See how long it's been, and what the limit @@ -2546,7 +2546,7 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats& rtStats, size_t* rtTotalOut) { nsCOMPtr am; - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { // Only try to access the service from the main process. am = do_GetService("@mozilla.org/addons/integration;1"); } @@ -2802,7 +2802,7 @@ JSReporter::CollectReports(WindowPaths* windowPaths, // stats seems like a bad idea. nsCOMPtr addonManager; - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { // Only try to access the service from the main process. addonManager = do_GetService("@mozilla.org/addons/integration;1"); } diff --git a/js/xpconnect/src/XPCWrappedNativeInfo.cpp b/js/xpconnect/src/XPCWrappedNativeInfo.cpp index b0dc92a3cf..ec807b19a8 100644 --- a/js/xpconnect/src/XPCWrappedNativeInfo.cpp +++ b/js/xpconnect/src/XPCWrappedNativeInfo.cpp @@ -236,10 +236,10 @@ XPCNativeInterface::NewInstance(nsIInterfaceInfo* aInfo) bool mainProcessScriptableOnly; if (NS_FAILED(aInfo->IsMainProcessScriptableOnly(&mainProcessScriptableOnly))) return nullptr; - if (mainProcessScriptableOnly && XRE_GetProcessType() != GeckoProcessType_Default) { + if (mainProcessScriptableOnly && !XRE_IsParentProcess()) { nsCOMPtr console(do_GetService(NS_CONSOLESERVICE_CONTRACTID)); if (console) { - char *intfNameChars; + char* intfNameChars; aInfo->GetName(&intfNameChars); nsPrintfCString errorMsg("Use of %s in content process is deprecated.", intfNameChars); diff --git a/js/xpconnect/wrappers/AccessCheck.cpp b/js/xpconnect/wrappers/AccessCheck.cpp index dfee72eae4..8b1c914f24 100644 --- a/js/xpconnect/wrappers/AccessCheck.cpp +++ b/js/xpconnect/wrappers/AccessCheck.cpp @@ -231,7 +231,7 @@ AccessCheck::checkPassToPrivilegedCode(JSContext* cx, HandleObject wrapper, Hand // pass any objects at all to CPOWs. if (mozilla::jsipc::IsWrappedCPOW(obj) && js::GetObjectCompartment(wrapper) == js::GetObjectCompartment(xpc::UnprivilegedJunkScope()) && - XRE_GetProcessType() == GeckoProcessType_Default) + XRE_IsParentProcess()) { return true; } diff --git a/js/xpconnect/wrappers/WrapperFactory.cpp b/js/xpconnect/wrappers/WrapperFactory.cpp index 9e35ea08c1..8e6da86bd3 100644 --- a/js/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/xpconnect/wrappers/WrapperFactory.cpp @@ -427,7 +427,7 @@ WrapperFactory::Rewrap(JSContext* cx, HandleObject existing, HandleObject obj) // here, but only in the content process. if ((IdentifyStandardInstance(obj) == JSProto_Function || (jsipc::IsCPOW(obj) && JS::IsCallable(obj) && - XRE_GetProcessType() == GeckoProcessType_Content))) + XRE_IsContentProcess()))) { wrapper = &FilteringWrapper::singleton; } diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index c892475622..2fabe9b448 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -1656,7 +1656,7 @@ already_AddRefed nsDisplayList::PaintRoot(nsDisplayListBuilder* aB // updates, so calculate that now before we call EndTransaction. if (rootPresContext && aBuilder->WillComputePluginGeometry() && - XRE_GetProcessType() == GeckoProcessType_Content) { + XRE_IsContentProcess()) { rootPresContext->ComputePluginGeometryUpdates(aBuilder->RootReferenceFrame(), aBuilder, this); rootPresContext->CollectPluginGeometryUpdates(layerManager); } diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index c41a9b8887..7c2413bf0c 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -3396,7 +3396,7 @@ nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFram // except requesting composition. Geometry updates were calculated and // shipped to the chrome process in nsDisplayList when the layer // transaction completed. - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { rootPresContext->ComputePluginGeometryUpdates(aFrame, &builder, &list); // We're not going to get a WillPaintWindow event here if we didn't do // widget invalidation, so just apply the plugin geometry update here diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index 95e095cd71..bbe1a35edb 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -2810,7 +2810,7 @@ nsPresContext::IsCrossProcessRootContentDocument() return false; } - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { return true; } @@ -2946,7 +2946,7 @@ nsRootPresContext::ComputePluginGeometryUpdates(nsIFrame* aFrame, // This is not happening during a paint event. ApplyPluginGeometryUpdates(); #else - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { InitApplyPluginGeometryTimer(); } #endif diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 904cf05382..04087e3831 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -4889,7 +4889,7 @@ PresShell::RenderDocument(const nsRect& aRect, uint32_t aFlags, // taking snapshots. if (layerManager && (!layerManager->AsClientLayerManager() || - XRE_GetProcessType() == GeckoProcessType_Default)) { + XRE_IsParentProcess())) { flags |= nsLayoutUtils::PAINT_WIDGET_LAYERS; } } @@ -5483,7 +5483,7 @@ void PresShell::UpdateCanvasBackground() if (!FrameConstructor()->GetRootElementFrame()) { mCanvasBackgroundColor = GetDefaultBackgroundColorToDraw(); } - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { if (TabChild* tabChild = TabChild::GetFrom(this)) { tabChild->SetBackgroundColor(mCanvasBackgroundColor); } @@ -6189,7 +6189,7 @@ public: } ~AutoUpdateHitRegion() { - if (XRE_GetProcessType() != GeckoProcessType_Content || + if (!XRE_IsContentProcess() || !mFrame || !mShell) { return; } @@ -11017,7 +11017,7 @@ nsIPresShell::RecomputeFontSizeInflationEnabled() mFontSizeInflationEnabled = false; return; } - } else if (XRE_GetProcessType() == GeckoProcessType_Default) { + } else if (XRE_IsParentProcess()) { // We're in the master process. Cancel inflation if it's been // explicitly disabled. if (FontSizeInflationDisabledInMasterProcess()) { diff --git a/layout/base/nsStyleSheetService.cpp b/layout/base/nsStyleSheetService.cpp index a09405ba55..4bbdb40ab2 100644 --- a/layout/base/nsStyleSheetService.cpp +++ b/layout/base/nsStyleSheetService.cpp @@ -101,7 +101,7 @@ nsStyleSheetService::Init() // SVGDocument::EnsureNonSVGUserAgentStyleSheetsLoaded should be updated too. // Child processes get their style sheets from the ContentParent. - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { return NS_OK; } @@ -156,7 +156,7 @@ nsStyleSheetService::LoadAndRegisterSheet(nsIURI *aSheetURI, serv->NotifyObservers(sheets[sheets.Count() - 1], message, nullptr); } - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { nsTArray children; dom::ContentParent::GetAll(children); @@ -265,7 +265,7 @@ nsStyleSheetService::UnregisterSheet(nsIURI *aSheetURI, uint32_t aSheetType) if (serv) serv->NotifyObservers(sheet, message, nullptr); - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { nsTArray children; dom::ContentParent::GetAll(children); diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index 9aa9b8163e..1cf5047be0 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -28,6 +28,7 @@ #include "nsIDocumentEncoder.h" #include "nsIFactory.h" #include "nsIFrameUtil.h" +#include "nsIIdleService.h" #include "nsHTMLStyleSheet.h" #include "nsILayoutDebugger.h" #include "nsNameSpaceManager.h" @@ -1331,6 +1332,7 @@ static const mozilla::Module::CategoryEntry kLayoutCategories[] = { { "net-channel-event-sinks", NS_MIXEDCONTENTBLOCKER_CONTRACTID, NS_MIXEDCONTENTBLOCKER_CONTRACTID }, { "app-startup", "Script Security Manager", "service," NS_SCRIPTSECURITYMANAGER_CONTRACTID }, { TOPIC_WEB_APP_CLEAR_DATA, "QuotaManager", "service," QUOTA_MANAGER_CONTRACTID }, + { OBSERVER_TOPIC_IDLE_DAILY, "QuotaManager", QUOTA_MANAGER_CONTRACTID }, #ifdef MOZ_WIDGET_GONK { "app-startup", "Volume Service", "service," NS_VOLUMESERVICE_CONTRACTID }, #endif diff --git a/layout/forms/nsListControlFrame.cpp b/layout/forms/nsListControlFrame.cpp index 34384bfab2..44c6fc7650 100644 --- a/layout/forms/nsListControlFrame.cpp +++ b/layout/forms/nsListControlFrame.cpp @@ -1763,7 +1763,7 @@ nsListControlFrame::GetIndexFromDOMEvent(nsIDOMEvent* aMouseEvent, static bool FireShowDropDownEvent(nsIContent* aContent) { - if (XRE_GetProcessType() == GeckoProcessType_Content && + if (XRE_IsContentProcess() && Preferences::GetBool("browser.tabs.remote.desktopbehavior", false)) { nsContentUtils::DispatchChromeEvent(aContent->OwnerDoc(), aContent, NS_LITERAL_STRING("mozshowdropdown"), true, diff --git a/layout/generic/nsPluginFrame.cpp b/layout/generic/nsPluginFrame.cpp index b11542a3bc..c8b36422c9 100644 --- a/layout/generic/nsPluginFrame.cpp +++ b/layout/generic/nsPluginFrame.cpp @@ -415,7 +415,7 @@ nsPluginFrame::GetWidgetConfiguration(nsTArray* aConfi configuration->mBounds = mNextConfigurationBounds; configuration->mClipRegion = mNextConfigurationClipRegion; #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { configuration->mWindowID = (uintptr_t)mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT); configuration->mVisible = mWidget->IsVisible(); } @@ -761,7 +761,7 @@ mozilla::LayoutDeviceIntPoint nsPluginFrame::GetRemoteTabChromeOffset() { LayoutDeviceIntPoint offset; - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { nsCOMPtr window = do_QueryInterface(GetContent()->OwnerDoc()->GetWindow()); if (window) { nsCOMPtr topWindow = window->GetTop(); diff --git a/layout/ipc/RenderFrameParent.cpp b/layout/ipc/RenderFrameParent.cpp index d1362c51cc..bb0ec932cb 100644 --- a/layout/ipc/RenderFrameParent.cpp +++ b/layout/ipc/RenderFrameParent.cpp @@ -261,6 +261,14 @@ public: } } + void NotifyFlushComplete() override { + MOZ_ASSERT(NS_IsMainThread()); + if (mRenderFrame) { + TabParent* browser = TabParent::GetFrom(mRenderFrame->Manager()); + browser->NotifyFlushComplete(); + } + } + // Methods used by RenderFrameParent to set fields stored here. void SetTouchSensitiveRegion(const nsRegion& aRegion) @@ -303,7 +311,7 @@ RenderFrameParent::RenderFrameParent(nsFrameLoader* aFrameLoader, *aTextureFactoryIdentifier = TextureFactoryIdentifier(); } - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { // Our remote frame will push layers updates to the compositor, // and we'll keep an indirect reference to that tree. *aId = mLayersId = CompositorParent::AllocateLayerTreeId(); @@ -316,7 +324,7 @@ RenderFrameParent::RenderFrameParent(nsFrameLoader* aFrameLoader, mContentController = new RemoteContentController(this); CompositorParent::SetControllerForLayerTree(mLayersId, mContentController); } - } else if (XRE_GetProcessType() == GeckoProcessType_Content) { + } else if (XRE_IsContentProcess()) { ContentChild::GetSingleton()->SendAllocateLayerTreeId(aId); mLayersId = *aId; CompositorChild::Get()->SendNotifyChildCreated(mLayersId); @@ -424,7 +432,7 @@ void RenderFrameParent::ActorDestroy(ActorDestroyReason why) { if (mLayersId != 0) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { ContentChild::GetSingleton()->SendDeallocateLayerTreeId(mLayersId); } else { CompositorParent::DeallocateLayerTreeId(mLayersId); diff --git a/media/mtransport/nr_socket_prsock.cpp b/media/mtransport/nr_socket_prsock.cpp index 998d2d7dbc..96bba56a1a 100644 --- a/media/mtransport/nr_socket_prsock.cpp +++ b/media/mtransport/nr_socket_prsock.cpp @@ -1313,7 +1313,7 @@ int nr_socket_local_create(void *obj, nr_transport_addr *addr, nr_socket **sockp RefPtr sock; // create IPC bridge for content process - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { sock = new NrSocket(); } else { sock = new NrSocketIpc(); diff --git a/media/webrtc/signaling/src/common/browser_logging/WebRtcLog.cpp b/media/webrtc/signaling/src/common/browser_logging/WebRtcLog.cpp index a0aca7c1e0..bee1857817 100644 --- a/media/webrtc/signaling/src/common/browser_logging/WebRtcLog.cpp +++ b/media/webrtc/signaling/src/common/browser_logging/WebRtcLog.cpp @@ -147,7 +147,7 @@ void ConfigWebRtcLog(uint32_t trace_mask, nsCString &aLogFile, nsCString &aAECLo } } #if !defined(MOZILLA_EXTERNAL_LINKAGE) - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { // Capture the final choices for the trace settings. mozilla::Preferences::SetCString("media.webrtc.debug.log_file", aLogFile); mozilla::Preferences::SetCString("media.webrtc.debug.aec_log_dir", aAECLogDir); diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp index d3dae374f6..08e0b87314 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp @@ -326,7 +326,7 @@ nsresult PeerConnectionCtx::Initialize() { mTelemetryTimer->InitWithFuncCallback(EverySecondTelemetryCallback_m, this, 1000, nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP); - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { WebrtcGlobalChild::Create(); } #endif // MOZILLA_INTERNAL_API diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index c5e12462be..0ac5fa4c91 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -263,7 +263,7 @@ static nsresult InitNSSInContent() { NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_SAME_THREAD); - if (XRE_GetProcessType() != GeckoProcessType_Content) { + if (!XRE_IsContentProcess()) { MOZ_ASSERT_UNREACHABLE("Must be called in content process"); return NS_ERROR_FAILURE; } @@ -589,7 +589,7 @@ PeerConnectionImpl::AddIceServer(const RTCIceServer &aServer, // Bug 1039655 - TURN TCP is not e10s ready if ((transport == kNrIceTransportTcp) && - (XRE_GetProcessType() != GeckoProcessType_Default)) { + (!XRE_IsParentProcess())) { continue; } @@ -636,7 +636,7 @@ PeerConnectionImpl::Initialize(PeerConnectionObserver& aObserver, // Initialize NSS if we are in content process. For chrome process, NSS should already // been initialized. - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { // This code interferes with the C++ unit test startup code. nsCOMPtr nssDummy = do_GetService("@mozilla.org/psm;1", &res); NS_ENSURE_SUCCESS(res, res); diff --git a/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp index 3bd2b32bee..b4fdd351b7 100644 --- a/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp +++ b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp @@ -261,7 +261,7 @@ OnStatsReport_m(WebrtcGlobalChild* aThisChild, } // This is the last stats report to be collected. (Must be the gecko process). - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); StatsRequest* request = StatsRequest::Get(aRequestId); @@ -332,7 +332,7 @@ static void OnGetLogging_m(WebrtcGlobalChild* aThisChild, } // This is the last log to be collected. (Must be the gecko process). - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); LogRequest* request = LogRequest::Get(aRequestId); @@ -441,7 +441,7 @@ WebrtcGlobalInformation::GetAllStats( return; } - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); // CallbackObject does not support threadsafe refcounting, and must be // destroyed on main. @@ -532,7 +532,7 @@ WebrtcGlobalInformation::GetLogging( return; } - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); // CallbackObject does not support threadsafe refcounting, and must be // destroyed on main. diff --git a/modules/libjar/nsJARProtocolHandler.cpp b/modules/libjar/nsJARProtocolHandler.cpp index eb789ee6ef..971c577bbb 100644 --- a/modules/libjar/nsJARProtocolHandler.cpp +++ b/modules/libjar/nsJARProtocolHandler.cpp @@ -32,7 +32,7 @@ static NS_DEFINE_CID(kZipReaderCacheCID, NS_ZIPREADERCACHE_CID); nsJARProtocolHandler *gJarHandler = nullptr; nsJARProtocolHandler::nsJARProtocolHandler() -: mIsMainProcess(XRE_GetProcessType() == GeckoProcessType_Default) +: mIsMainProcess(XRE_IsParentProcess()) { MOZ_ASSERT(NS_IsMainThread()); } diff --git a/modules/libpref/Preferences.cpp b/modules/libpref/Preferences.cpp index 14c0825b8b..a024527167 100644 --- a/modules/libpref/Preferences.cpp +++ b/modules/libpref/Preferences.cpp @@ -50,7 +50,7 @@ #ifdef DEBUG #define ENSURE_MAIN_PROCESS(message, pref) do { \ - if (MOZ_UNLIKELY(XRE_GetProcessType() != GeckoProcessType_Default)) { \ + if (MOZ_UNLIKELY(!XRE_IsParentProcess())) { \ nsPrintfCString msg("ENSURE_MAIN_PROCESS failed. %s %s", message, pref); \ NS_WARNING(msg.get()); \ return NS_ERROR_NOT_AVAILABLE; \ @@ -58,7 +58,7 @@ } while (0); #else #define ENSURE_MAIN_PROCESS(message, pref) \ - if (MOZ_UNLIKELY(XRE_GetProcessType() != GeckoProcessType_Default)) { \ + if (MOZ_UNLIKELY(!XRE_IsParentProcess())) { \ return NS_ERROR_NOT_AVAILABLE; \ } #endif @@ -530,7 +530,7 @@ Preferences::Init() NS_ENSURE_SUCCESS(rv, rv); using mozilla::dom::ContentChild; - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { InfallibleTArray prefs; ContentChild::GetSingleton()->SendReadPrefsArray(&prefs); @@ -581,7 +581,7 @@ NS_IMETHODIMP Preferences::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData) { - if (XRE_GetProcessType() == GeckoProcessType_Content) + if (XRE_IsContentProcess()) return NS_ERROR_NOT_AVAILABLE; nsresult rv = NS_OK; @@ -606,7 +606,7 @@ Preferences::Observe(nsISupports *aSubject, const char *aTopic, NS_IMETHODIMP Preferences::ReadUserPrefs(nsIFile *aFile) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { NS_ERROR("cannot load prefs from content process"); return NS_ERROR_NOT_AVAILABLE; } @@ -636,7 +636,7 @@ Preferences::ReadUserPrefs(nsIFile *aFile) NS_IMETHODIMP Preferences::ResetPrefs() { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { NS_ERROR("cannot reset prefs from content process"); return NS_ERROR_NOT_AVAILABLE; } @@ -653,7 +653,7 @@ Preferences::ResetPrefs() NS_IMETHODIMP Preferences::ResetUserPrefs() { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { NS_ERROR("cannot reset user prefs from content process"); return NS_ERROR_NOT_AVAILABLE; } @@ -665,7 +665,7 @@ Preferences::ResetUserPrefs() NS_IMETHODIMP Preferences::SavePrefFile(nsIFile *aFile) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { NS_ERROR("cannot save pref file from content process"); return NS_ERROR_NOT_AVAILABLE; } diff --git a/modules/libpref/nsPrefBranch.cpp b/modules/libpref/nsPrefBranch.cpp index 1923bbd73c..9a16ed9f6c 100644 --- a/modules/libpref/nsPrefBranch.cpp +++ b/modules/libpref/nsPrefBranch.cpp @@ -47,7 +47,7 @@ using mozilla::dom::ContentChild; static ContentChild* GetContentChild() { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { ContentChild* cpc = ContentChild::GetSingleton(); if (!cpc) { NS_RUNTIMEABORT("Content Protocol is NULL! We're going to crash!"); diff --git a/netwerk/base/nsBaseChannel.cpp b/netwerk/base/nsBaseChannel.cpp index 31252802d1..ae616d4de2 100644 --- a/netwerk/base/nsBaseChannel.cpp +++ b/netwerk/base/nsBaseChannel.cpp @@ -294,7 +294,7 @@ nsBaseChannel::ClassifyURI() { // For channels created in the child process, delegate to the parent to // classify URIs. - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return; } diff --git a/netwerk/base/nsChannelClassifier.cpp b/netwerk/base/nsChannelClassifier.cpp index bb139c52ef..0b4c2a4701 100644 --- a/netwerk/base/nsChannelClassifier.cpp +++ b/netwerk/base/nsChannelClassifier.cpp @@ -59,7 +59,7 @@ nsChannelClassifier::ShouldEnableTrackingProtection(nsIChannel *aChannel, bool *result) { // Should only be called in the parent process. - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); NS_ENSURE_ARG(result); *result = false; @@ -233,7 +233,7 @@ nsresult nsChannelClassifier::StartInternal() { // Should only be called in the parent process. - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); // Don't bother to run the classifier on a load that has already failed. // (this might happen after a redirect) @@ -346,7 +346,7 @@ void nsChannelClassifier::MarkEntryClassified(nsresult status) { // Should only be called in the parent process. - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); // Don't cache tracking classifications because we support allowlisting. if (status == NS_ERROR_TRACKING_URI || mIsAllowListed) { @@ -378,7 +378,7 @@ bool nsChannelClassifier::HasBeenClassified(nsIChannel *aChannel) { // Should only be called in the parent process. - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); nsCOMPtr cachingChannel = do_QueryInterface(aChannel); @@ -476,7 +476,7 @@ NS_IMETHODIMP nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode) { // Should only be called in the parent process. - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); LOG(("nsChannelClassifier[%p]:OnClassifyComplete %d", this, aErrorCode)); if (mSuspendedChannel) { diff --git a/netwerk/base/nsFileStreams.cpp b/netwerk/base/nsFileStreams.cpp index 54a601d17a..19b6f286f2 100644 --- a/netwerk/base/nsFileStreams.cpp +++ b/netwerk/base/nsFileStreams.cpp @@ -622,7 +622,7 @@ nsFileInputStream::Deserialize(const InputStreamParams& aParams, mBehaviorFlags = params.behaviorFlags(); - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { // A child process shouldn't close when it reads the end because it will // not be able to reopen the file later. mBehaviorFlags &= ~nsIFileInputStream::CLOSE_ON_EOF; diff --git a/netwerk/base/nsIOService.cpp b/netwerk/base/nsIOService.cpp index 762e463285..241b4657ac 100644 --- a/netwerk/base/nsIOService.cpp +++ b/netwerk/base/nsIOService.cpp @@ -277,7 +277,7 @@ nsIOService::InitializeNetworkLinkService() } // go into managed mode if we can, and chrome process - if (XRE_GetProcessType() == GeckoProcessType_Default) + if (XRE_IsParentProcess()) { mNetworkLinkService = do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID, &rv); } @@ -847,7 +847,7 @@ nsIOService::SetOffline(bool offline) NS_ASSERTION(observerService, "The observer service should not be null"); - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { if (observerService) { (void)observerService->NotifyObservers(nullptr, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC, offline ? @@ -936,7 +936,7 @@ nsIOService::SetConnectivity(bool aConnectivity) { // This should only be called from ContentChild to pass the connectivity // value from the chrome process to the content process. - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { return NS_ERROR_NOT_AVAILABLE; } return SetConnectivityInternal(aConnectivity); @@ -958,7 +958,7 @@ nsIOService::SetConnectivityInternal(bool aConnectivity) return NS_OK; } // This notification sends the connectivity to the child processes - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { observerService->NotifyObservers(nullptr, NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC, aConnectivity ? MOZ_UTF16("true") : diff --git a/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp index ac1c315194..f6c1d6185b 100644 --- a/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp +++ b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp @@ -26,7 +26,7 @@ StartService() property_set("ctl.start", "mdnsd"); } -void +inline void StopService() { char value[PROPERTY_VALUE_MAX] = { '\0' }; diff --git a/netwerk/ipc/NeckoCommon.h b/netwerk/ipc/NeckoCommon.h index c11d970e14..15bf5da5b1 100644 --- a/netwerk/ipc/NeckoCommon.h +++ b/netwerk/ipc/NeckoCommon.h @@ -95,7 +95,7 @@ IsNeckoChild() // TODO: Remove eventually when no longer supported (bug 571126) const char * e = PR_GetEnv("NECKO_SEPARATE_STACKS"); if (!e) - amChild = (XRE_GetProcessType() == GeckoProcessType_Content); + amChild = XRE_IsContentProcess(); didCheck = true; } return amChild; diff --git a/netwerk/ipc/NeckoParent.cpp b/netwerk/ipc/NeckoParent.cpp index 0e41e0354d..e7f3a4950a 100644 --- a/netwerk/ipc/NeckoParent.cpp +++ b/netwerk/ipc/NeckoParent.cpp @@ -790,7 +790,7 @@ NeckoParent::NestedFrameAuthPrompt::AsyncPromptAuth( nsIAuthInformation* aInfo, nsICancelable**) { static uint64_t callbackId = 0; - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); + MOZ_ASSERT(XRE_IsParentProcess()); nsCOMPtr uri; nsresult rv = aChannel->GetURI(getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, rv); diff --git a/netwerk/protocol/app/AppProtocolHandler.cpp b/netwerk/protocol/app/AppProtocolHandler.cpp index 2c19ebae4e..c6e0950583 100644 --- a/netwerk/protocol/app/AppProtocolHandler.cpp +++ b/netwerk/protocol/app/AppProtocolHandler.cpp @@ -441,7 +441,7 @@ AppProtocolHandler::NewChannel2(nsIURI* aUri, } bool noRemote = (appInfo->mIsCoreApp || - XRE_GetProcessType() == GeckoProcessType_Default); + XRE_IsParentProcess()); // In-parent and CoreApps can directly access files, so use jar:file:// nsAutoCString jarSpec(noRemote ? "jar:file://" diff --git a/netwerk/protocol/data/nsDataHandler.cpp b/netwerk/protocol/data/nsDataHandler.cpp index 02b3623617..3318d8a141 100644 --- a/netwerk/protocol/data/nsDataHandler.cpp +++ b/netwerk/protocol/data/nsDataHandler.cpp @@ -110,7 +110,7 @@ nsDataHandler::NewChannel2(nsIURI* uri, { NS_ENSURE_ARG_POINTER(uri); nsDataChannel* channel; - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { channel = new nsDataChannel(uri); } else { channel = new mozilla::net::DataChannelChild(uri); diff --git a/netwerk/protocol/device/AndroidCaptureProvider.cpp b/netwerk/protocol/device/AndroidCaptureProvider.cpp index cb7ab76b65..7bd6846b4b 100644 --- a/netwerk/protocol/device/AndroidCaptureProvider.cpp +++ b/netwerk/protocol/device/AndroidCaptureProvider.cpp @@ -38,7 +38,7 @@ AndroidCameraInputStream::~AndroidCameraInputStream() { NS_IMETHODIMP AndroidCameraInputStream::Init(nsACString& aContentType, nsCaptureParams* aParams) { - if (XRE_GetProcessType() != GeckoProcessType_Default) + if (!XRE_IsParentProcess()) return NS_ERROR_NOT_IMPLEMENTED; mContentType = aContentType; diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index 8d34e66dbc..5254cb30c5 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -2224,7 +2224,7 @@ HttpBaseChannel::AddCookiesToRequest() } bool useCookieService = - (XRE_GetProcessType() == GeckoProcessType_Default); + (XRE_IsParentProcess()); nsXPIDLCString cookie; if (useCookieService) { nsICookieService *cs = gHttpHandler->GetCookieService(); diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp index 8334a99570..afb7d1e31a 100644 --- a/netwerk/protocol/http/HttpChannelParent.cpp +++ b/netwerk/protocol/http/HttpChannelParent.cpp @@ -216,7 +216,7 @@ HttpChannelParent::GetInterface(const nsIID& aIID, void **result) } // Only support nsIAuthPromptProvider in Content process - if (XRE_GetProcessType() == GeckoProcessType_Default && + if (XRE_IsParentProcess() && aIID.Equals(NS_GET_IID(nsIAuthPromptProvider))) { *result = nullptr; return NS_OK; diff --git a/parser/html/nsHtml5StreamParser.cpp b/parser/html/nsHtml5StreamParser.cpp index 536f139ab8..b45614056f 100644 --- a/parser/html/nsHtml5StreamParser.cpp +++ b/parser/html/nsHtml5StreamParser.cpp @@ -947,7 +947,7 @@ nsHtml5StreamParser::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) if (NS_FAILED(rv)) { // for now skip warning if we're on child process, since we don't support // off-main thread delivery there yet. This will change with bug 1015466 - if (XRE_GetProcessType() != GeckoProcessType_Content) { + if (!XRE_IsContentProcess()) { NS_WARNING("Failed to retarget HTML data delivery to the parser thread."); } } diff --git a/security/manager/ssl/PSMContentListener.cpp b/security/manager/ssl/PSMContentListener.cpp index 83aa92c16d..f3991455a1 100644 --- a/security/manager/ssl/PSMContentListener.cpp +++ b/security/manager/ssl/PSMContentListener.cpp @@ -402,7 +402,7 @@ PSMContentListener::DoContent(const nsACString& aContentType, } if (type != UNKNOWN_TYPE) { nsCOMPtr downloader; - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { downloader = new PSMContentStreamListener(type); } else { downloader = static_cast( diff --git a/security/manager/ssl/TransportSecurityInfo.cpp b/security/manager/ssl/TransportSecurityInfo.cpp index 5c08f4366a..6dcd000e94 100644 --- a/security/manager/ssl/TransportSecurityInfo.cpp +++ b/security/manager/ssl/TransportSecurityInfo.cpp @@ -240,7 +240,7 @@ TransportSecurityInfo::formatErrorMessage(MutexAutoLock const & proofOfLock, return NS_OK; } - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return NS_ERROR_UNEXPECTED; } diff --git a/security/manager/ssl/nsNSSComponent.cpp b/security/manager/ssl/nsNSSComponent.cpp index 274c231909..97f45ae85f 100644 --- a/security/manager/ssl/nsNSSComponent.cpp +++ b/security/manager/ssl/nsNSSComponent.cpp @@ -69,7 +69,7 @@ bool nsPSMInitPanic::isPanic = false; bool EnsureNSSInitializedChromeOrContent() { nsresult rv; - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { nsCOMPtr nss = do_GetService(PSM_COMPONENT_CONTRACTID, &rv); if (NS_FAILED(rv)) { return false; diff --git a/security/manager/ssl/nsSiteSecurityService.cpp b/security/manager/ssl/nsSiteSecurityService.cpp index 6cd87d5f32..8079bb482b 100644 --- a/security/manager/ssl/nsSiteSecurityService.cpp +++ b/security/manager/ssl/nsSiteSecurityService.cpp @@ -229,7 +229,7 @@ nsresult nsSiteSecurityService::Init() { // Child processes are not allowed direct access to this. - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { MOZ_CRASH("Child process: no direct access to nsSiteSecurityService"); } diff --git a/security/sandbox/chromium-shim/sandbox/win/loggingCallbacks.h b/security/sandbox/chromium-shim/sandbox/win/loggingCallbacks.h index 75747744b9..fe1abd25d3 100644 --- a/security/sandbox/chromium-shim/sandbox/win/loggingCallbacks.h +++ b/security/sandbox/chromium-shim/sandbox/win/loggingCallbacks.h @@ -123,7 +123,7 @@ InitLoggingIfRequired() #if defined(MOZ_CONTENT_SANDBOX) && defined(MOZ_STACKWALKING) // We can only log the stack trace on process types where we know that the // sandbox won't prevent it. - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { Preferences::AddUintVarCache(&sStackTraceDepth, "security.sandbox.windows.log.stackTraceDepth"); } diff --git a/startupcache/StartupCache.cpp b/startupcache/StartupCache.cpp index 0936d281c2..4813695c1a 100644 --- a/startupcache/StartupCache.cpp +++ b/startupcache/StartupCache.cpp @@ -87,7 +87,7 @@ StartupCache* StartupCache::GetSingleton() { if (!gStartupCache) { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return nullptr; } #ifdef MOZ_DISABLE_STARTUPCACHE diff --git a/storage/VacuumManager.cpp b/storage/VacuumManager.cpp index cd3faa0344..f2322dacfd 100644 --- a/storage/VacuumManager.cpp +++ b/storage/VacuumManager.cpp @@ -315,7 +315,7 @@ VacuumManager * VacuumManager::getSingleton() { //Don't allocate it in the child Process. - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return nullptr; } diff --git a/storage/moz.build b/storage/moz.build index 35770b61a5..bafaf8bd8b 100644 --- a/storage/moz.build +++ b/storage/moz.build @@ -81,6 +81,8 @@ SOURCES += [ 'mozStorageConnection.cpp', ] +include('/ipc/chromium/chromium-config.mozbuild') + FAIL_ON_WARNINGS = True FINAL_LIBRARY = 'xul' diff --git a/storage/mozStorageConnection.cpp b/storage/mozStorageConnection.cpp index f94ad8eebf..8f26293b90 100644 --- a/storage/mozStorageConnection.cpp +++ b/storage/mozStorageConnection.cpp @@ -582,7 +582,13 @@ Connection::initialize() return convertResultCode(srv); } - return initializeInternal(nullptr); + // Do not set mDatabaseFile or mFileURL here since this is a "memory" + // database. + + nsresult rv = initializeInternal(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; } nsresult @@ -606,11 +612,13 @@ Connection::initialize(nsIFile *aDatabaseFile) return convertResultCode(srv); } - rv = initializeInternal(aDatabaseFile); - NS_ENSURE_SUCCESS(rv, rv); - + // Do not set mFileURL here since this is database does not have an associated + // URL. mDatabaseFile = aDatabaseFile; + rv = initializeInternal(); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; } @@ -636,19 +644,40 @@ Connection::initialize(nsIFileURL *aFileURL) return convertResultCode(srv); } - rv = initializeInternal(databaseFile); - NS_ENSURE_SUCCESS(rv, rv); - + // Set both mDatabaseFile and mFileURL here. mFileURL = aFileURL; mDatabaseFile = databaseFile; + rv = initializeInternal(); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; } - nsresult -Connection::initializeInternal(nsIFile* aDatabaseFile) +Connection::initializeInternal() { + MOZ_ASSERT(mDBConn); + + if (mFileURL) { + const char* dbPath = ::sqlite3_db_filename(mDBConn, "main"); + MOZ_ASSERT(dbPath); + + const char* telemetryFilename = + ::sqlite3_uri_parameter(dbPath, "telemetryFilename"); + if (telemetryFilename) { + if (NS_WARN_IF(*telemetryFilename == '\0')) { + return NS_ERROR_INVALID_ARG; + } + mTelemetryFilename = telemetryFilename; + } + } + + if (mTelemetryFilename.IsEmpty()) { + mTelemetryFilename = getFilename(); + MOZ_ASSERT(!mTelemetryFilename.IsEmpty()); + } + // Properly wrap the database handle's mutex. sharedDBMutex.initWithMutex(sqlite3_db_mutex(mDBConn)); @@ -660,11 +689,8 @@ Connection::initializeInternal(nsIFile* aDatabaseFile) if (PR_LOG_TEST(gStorageLog, PR_LOG_DEBUG)) { ::sqlite3_trace(mDBConn, tracefunc, this); - nsAutoCString leafName(":memory"); - if (aDatabaseFile) - (void)aDatabaseFile->GetNativeLeafName(leafName); PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Opening connection to '%s' (%p)", - leafName.get(), this)); + mTelemetryFilename.get(), this)); } int64_t pageSize = Service::getDefaultPageSize(); @@ -1006,7 +1032,7 @@ Connection::stepStatement(sqlite3 *aNativeConnection, sqlite3_stmt *aStatement) : Telemetry::kSlowSQLThresholdForHelperThreads; if (duration.ToMilliseconds() >= threshold) { nsDependentCString statementString(::sqlite3_sql(aStatement)); - Telemetry::RecordSlowSQLStatement(statementString, getFilename(), + Telemetry::RecordSlowSQLStatement(statementString, mTelemetryFilename, duration.ToMilliseconds()); } @@ -1092,7 +1118,7 @@ Connection::executeSql(sqlite3 *aNativeConnection, const char *aSqlString) : Telemetry::kSlowSQLThresholdForHelperThreads; if (duration.ToMilliseconds() >= threshold) { nsDependentCString statementString(aSqlString); - Telemetry::RecordSlowSQLStatement(statementString, getFilename(), + Telemetry::RecordSlowSQLStatement(statementString, mTelemetryFilename, duration.ToMilliseconds()); } diff --git a/storage/mozStorageConnection.h b/storage/mozStorageConnection.h index 91abed60af..4a16b8b2c3 100644 --- a/storage/mozStorageConnection.h +++ b/storage/mozStorageConnection.h @@ -225,7 +225,7 @@ public: private: ~Connection(); - nsresult initializeInternal(nsIFile *aDatabaseFile); + nsresult initializeInternal(); /** * Sets the database into a closed state so no further actions can be @@ -283,6 +283,12 @@ private: nsCOMPtr mFileURL; nsCOMPtr mDatabaseFile; + /** + * The filename that will be reported to telemetry for this connection. By + * default this will be the leaf of the path to the database file. + */ + nsCString mTelemetryFilename; + /** * Lazily created thread for asynchronous statement execution. Consumers * should use getAsyncExecutionTarget rather than directly accessing this diff --git a/toolkit/components/alerts/nsAlertsService.cpp b/toolkit/components/alerts/nsAlertsService.cpp index f494b0cf16..354d4cdbd5 100644 --- a/toolkit/components/alerts/nsAlertsService.cpp +++ b/toolkit/components/alerts/nsAlertsService.cpp @@ -72,7 +72,7 @@ NS_IMETHODIMP nsAlertsService::ShowAlertNotification(const nsAString & aImageUrl nsIPrincipal * aPrincipal, bool aInPrivateBrowsing) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { ContentChild* cpc = ContentChild::GetSingleton(); if (aAlertListener) @@ -128,7 +128,7 @@ NS_IMETHODIMP nsAlertsService::ShowAlertNotification(const nsAString & aImageUrl NS_IMETHODIMP nsAlertsService::CloseAlert(const nsAString& aAlertName, nsIPrincipal* aPrincipal) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { ContentChild* cpc = ContentChild::GetSingleton(); cpc->SendCloseAlert(nsAutoString(aAlertName), IPC::Principal(aPrincipal)); return NS_OK; diff --git a/toolkit/components/diskspacewatcher/DiskSpaceWatcher.cpp b/toolkit/components/diskspacewatcher/DiskSpaceWatcher.cpp index 6f0a8a7066..cb2b090e28 100644 --- a/toolkit/components/diskspacewatcher/DiskSpaceWatcher.cpp +++ b/toolkit/components/diskspacewatcher/DiskSpaceWatcher.cpp @@ -37,7 +37,7 @@ DiskSpaceWatcher::~DiskSpaceWatcher() already_AddRefed DiskSpaceWatcher::FactoryCreate() { - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return nullptr; } diff --git a/toolkit/components/places/Database.cpp b/toolkit/components/places/Database.cpp index 6d88774168..375461cf92 100644 --- a/toolkit/components/places/Database.cpp +++ b/toolkit/components/places/Database.cpp @@ -348,7 +348,7 @@ Database::Database() , mShuttingDown(false) , mClosed(false) { - MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Content, + MOZ_ASSERT(!XRE_IsContentProcess(), "Cannot instantiate Places in the content process"); // Attempting to create two instances of the service? MOZ_ASSERT(!gDatabase); diff --git a/toolkit/components/places/History.cpp b/toolkit/components/places/History.cpp index 03cf67a633..37bf6c4805 100644 --- a/toolkit/components/places/History.cpp +++ b/toolkit/components/places/History.cpp @@ -479,7 +479,7 @@ public: // If we are a content process, always remote the request to the // parent process. - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { URIParams uri; SerializeURI(aURI, uri); @@ -1999,7 +1999,7 @@ History::NotifyVisited(nsIURI* aURI) nsAutoScriptBlocker scriptBlocker; - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { nsTArray cplist; ContentParent::GetAll(cplist); @@ -2434,7 +2434,7 @@ History::VisitURI(nsIURI* aURI, return NS_OK; } - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { URIParams uri; SerializeURI(aURI, uri); @@ -2551,7 +2551,7 @@ History::RegisterVisitedCallback(nsIURI* aURI, Link* aLink) { NS_ASSERTION(aURI, "Must pass a non-null URI!"); - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { NS_PRECONDITION(aLink, "Must pass a non-null Link!"); } @@ -2586,7 +2586,7 @@ History::RegisterVisitedCallback(nsIURI* aURI, // ContentParent::RecvStartVisitedQuery. All of our code after this point // assumes aLink is non-nullptr, so we have to return now. else if (!aLink) { - NS_ASSERTION(XRE_GetProcessType() == GeckoProcessType_Default, + NS_ASSERTION(XRE_IsParentProcess(), "We should only ever get a null Link in the default process!"); return NS_OK; } @@ -2641,7 +2641,7 @@ History::SetURITitle(nsIURI* aURI, const nsAString& aTitle) return NS_OK; } - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { URIParams uri; SerializeURI(aURI, uri); @@ -2700,7 +2700,7 @@ History::AddDownload(nsIURI* aSource, nsIURI* aReferrer, return NS_OK; } - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { NS_ERROR("Cannot add downloads to history from content process!"); return NS_ERROR_NOT_AVAILABLE; } @@ -2756,7 +2756,7 @@ History::RemoveAllDownloads() return NS_OK; } - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { NS_ERROR("Cannot remove downloads to history from content process!"); return NS_ERROR_NOT_AVAILABLE; } diff --git a/toolkit/components/satchel/nsFormFillController.cpp b/toolkit/components/satchel/nsFormFillController.cpp index 586480e123..16472df7ca 100644 --- a/toolkit/components/satchel/nsFormFillController.cpp +++ b/toolkit/components/satchel/nsFormFillController.cpp @@ -746,7 +746,7 @@ void nsFormFillController::RevalidateDataList() return; } - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { nsCOMPtr controller(do_QueryInterface(mLastListener)); if (!controller) { return; diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp index daba4431cf..c00d4165c0 100644 --- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -725,9 +725,6 @@ private: static TelemetryImpl *sTelemetry; AutoHashtable mPrivateSQL; AutoHashtable mSanitizedSQL; - // This gets marked immutable in debug builds, so we can't use - // AutoHashtable here. - nsTHashtable mTrackedDBs; Mutex mHashMutex; HangReports mHangReports; Mutex mHangReportsMutex; @@ -1819,43 +1816,23 @@ TelemetryImpl::AsyncFetchTelemetryData(nsIFetchTelemetryDataCallback *aCallback) TelemetryImpl::TelemetryImpl(): mHistogramMap(Telemetry::HistogramCount), -mCanRecordBase(XRE_GetProcessType() == GeckoProcessType_Default || - XRE_GetProcessType() == GeckoProcessType_Content), -mCanRecordExtended(XRE_GetProcessType() == GeckoProcessType_Default || - XRE_GetProcessType() == GeckoProcessType_Content), +mCanRecordBase(XRE_IsParentProcess() || XRE_IsContentProcess()), +mCanRecordExtended(XRE_IsParentProcess() || XRE_IsContentProcess()), mHashMutex("Telemetry::mHashMutex"), mHangReportsMutex("Telemetry::mHangReportsMutex"), mCachedTelemetryData(false), mLastShutdownTime(0), mFailedLockCount(0) { - // A whitelist to prevent Telemetry reporting on Addon & Thunderbird DBs - const char *trackedDBs[] = { - "818200132aebmoouht.sqlite", // IndexedDB for about:home, see aboutHome.js - "addons.sqlite", - "content-prefs.sqlite", - "cookies.sqlite", - "downloads.sqlite", - "extensions.sqlite", - "formhistory.sqlite", - "healthreport.sqlite", - "index.sqlite", - "netpredictions.sqlite", - "permissions.sqlite", - "places.sqlite", - "reading-list.sqlite", - "search.sqlite", - "signons.sqlite", - "urlclassifier3.sqlite", - "webappsstore.sqlite" - }; - - for (size_t i = 0; i < ArrayLength(trackedDBs); i++) - mTrackedDBs.PutEntry(nsDependentCString(trackedDBs[i])); + // Populate the static histogram name->id cache. + // Note that the histogram names are statically allocated. + for (uint32_t i = 0; i < Telemetry::HistogramCount; i++) { + CharPtrEntryType *entry = mHistogramMap.PutEntry(gHistograms[i].id()); + entry->mData = (Telemetry::ID) i; + } #ifdef DEBUG - // Mark immutable to prevent asserts on simultaneous access from multiple threads - mTrackedDBs.MarkImmutable(); + mHistogramMap.MarkImmutable(); #endif // Create registered keyed histograms @@ -1989,21 +1966,8 @@ TelemetryImpl::GetHistogramEnumId(const char *name, Telemetry::ID *id) return NS_ERROR_FAILURE; } - // Cache names - // Note the histogram names are statically allocated - TelemetryImpl::HistogramMapType *map = &sTelemetry->mHistogramMap; - if (!map->Count()) { - for (uint32_t i = 0; i < Telemetry::HistogramCount; i++) { - CharPtrEntryType *entry = map->PutEntry(gHistograms[i].id()); - if (MOZ_UNLIKELY(!entry)) { - map->Clear(); - return NS_ERROR_OUT_OF_MEMORY; - } - entry->mData = (Telemetry::ID) i; - } - } - - CharPtrEntryType *entry = map->GetEntry(name); + const TelemetryImpl::HistogramMapType& map = sTelemetry->mHistogramMap; + CharPtrEntryType *entry = map.GetEntry(name); if (!entry) { return NS_ERROR_INVALID_ARG; } @@ -3173,6 +3137,57 @@ TelemetryImpl::SanitizeSQL(const nsACString &sql) { return output; } +// A whitelist mechanism to prevent Telemetry reporting on Addon & Thunderbird +// DBs. +struct TrackedDBEntry +{ + const char* mName; + const uint32_t mNameLength; + + // This struct isn't meant to be used beyond the static arrays below. + MOZ_CONSTEXPR + TrackedDBEntry(const char* aName, uint32_t aNameLength) + : mName(aName) + , mNameLength(aNameLength) + { } + + TrackedDBEntry() = delete; + TrackedDBEntry(TrackedDBEntry&) = delete; +}; + +#define TRACKEDDB_ENTRY(_name) { _name, (sizeof(_name) - 1) } + +// A whitelist of database names. If the database name exactly matches one of +// these then its SQL statements will always be recorded. +static MOZ_CONSTEXPR_VAR TrackedDBEntry kTrackedDBs[] = { + // IndexedDB for about:home, see aboutHome.js + TRACKEDDB_ENTRY("818200132aebmoouht.sqlite"), + TRACKEDDB_ENTRY("addons.sqlite"), + TRACKEDDB_ENTRY("content-prefs.sqlite"), + TRACKEDDB_ENTRY("cookies.sqlite"), + TRACKEDDB_ENTRY("downloads.sqlite"), + TRACKEDDB_ENTRY("extensions.sqlite"), + TRACKEDDB_ENTRY("formhistory.sqlite"), + TRACKEDDB_ENTRY("healthreport.sqlite"), + TRACKEDDB_ENTRY("index.sqlite"), + TRACKEDDB_ENTRY("netpredictions.sqlite"), + TRACKEDDB_ENTRY("permissions.sqlite"), + TRACKEDDB_ENTRY("places.sqlite"), + TRACKEDDB_ENTRY("reading-list.sqlite"), + TRACKEDDB_ENTRY("search.sqlite"), + TRACKEDDB_ENTRY("signons.sqlite"), + TRACKEDDB_ENTRY("urlclassifier3.sqlite"), + TRACKEDDB_ENTRY("webappsstore.sqlite") +}; + +// A whitelist of database name prefixes. If the database name begins with +// one of these prefixes then its SQL statements will always be recorded. +static const TrackedDBEntry kTrackedDBPrefixes[] = { + TRACKEDDB_ENTRY("indexedDB-") +}; + +#undef TRACKEDDB_ENTRY + // Slow SQL statements will be automatically // trimmed to kMaxSlowStatementLength characters. // This limit doesn't include the ellipsis and DB name, @@ -3184,11 +3199,36 @@ TelemetryImpl::RecordSlowStatement(const nsACString &sql, const nsACString &dbName, uint32_t delay) { + MOZ_ASSERT(!sql.IsEmpty()); + MOZ_ASSERT(!dbName.IsEmpty()); + if (!sTelemetry || !sTelemetry->mCanRecordExtended) return; - bool isFirefoxDB = sTelemetry->mTrackedDBs.Contains(dbName); - if (isFirefoxDB) { + bool recordStatement = false; + + for (const TrackedDBEntry& nameEntry : kTrackedDBs) { + MOZ_ASSERT(nameEntry.mNameLength); + const nsDependentCString name(nameEntry.mName, nameEntry.mNameLength); + if (dbName == name) { + recordStatement = true; + break; + } + } + + if (!recordStatement) { + for (const TrackedDBEntry& prefixEntry : kTrackedDBPrefixes) { + MOZ_ASSERT(prefixEntry.mNameLength); + const nsDependentCString prefix(prefixEntry.mName, + prefixEntry.mNameLength); + if (StringBeginsWith(dbName, prefix)) { + recordStatement = true; + break; + } + } + } + + if (recordStatement) { nsAutoCString sanitizedSQL(SanitizeSQL(sql)); if (sanitizedSQL.Length() > kMaxSlowStatementLength) { sanitizedSQL.SetLength(kMaxSlowStatementLength); @@ -3282,7 +3322,6 @@ TelemetryImpl::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) n += mPrivateSQL.SizeOfExcludingThis(aMallocSizeOf); n += mSanitizedSQL.SizeOfExcludingThis(aMallocSizeOf); } - n += mTrackedDBs.SizeOfExcludingThis(aMallocSizeOf); { // Scope for mHangReportsMutex lock MutexAutoLock lock(mHangReportsMutex); n += mHangReports.SizeOfExcludingThis(aMallocSizeOf); diff --git a/toolkit/mozapps/extensions/AddonPathService.cpp b/toolkit/mozapps/extensions/AddonPathService.cpp index 9240439047..926d94009f 100644 --- a/toolkit/mozapps/extensions/AddonPathService.cpp +++ b/toolkit/mozapps/extensions/AddonPathService.cpp @@ -193,7 +193,7 @@ ResolveURI(nsIURI* aURI, nsAString& out) JSAddonId* MapURIToAddonID(nsIURI* aURI) { - if (!NS_IsMainThread() || XRE_GetProcessType() != GeckoProcessType_Default) { + if (!NS_IsMainThread() || !XRE_IsParentProcess()) { return nullptr; } diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index f34969febf..3b19c6087d 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -637,7 +637,7 @@ NS_INTERFACE_MAP_BEGIN(nsXULAppInfo) NS_INTERFACE_MAP_ENTRY(nsIFinishDumpingCallback) #endif NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIXULAppInfo, gAppData || - XRE_GetProcessType() == GeckoProcessType_Content) + XRE_IsContentProcess()) NS_INTERFACE_MAP_END NS_IMETHODIMP_(MozExternalRefCountType) @@ -655,7 +655,7 @@ nsXULAppInfo::Release() NS_IMETHODIMP nsXULAppInfo::GetVendor(nsACString& aResult) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { ContentChild* cc = ContentChild::GetSingleton(); aResult = cc->GetAppInfo().vendor; return NS_OK; @@ -668,7 +668,7 @@ nsXULAppInfo::GetVendor(nsACString& aResult) NS_IMETHODIMP nsXULAppInfo::GetName(nsACString& aResult) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { ContentChild* cc = ContentChild::GetSingleton(); aResult = cc->GetAppInfo().name; return NS_OK; @@ -681,7 +681,7 @@ nsXULAppInfo::GetName(nsACString& aResult) NS_IMETHODIMP nsXULAppInfo::GetID(nsACString& aResult) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { ContentChild* cc = ContentChild::GetSingleton(); aResult = cc->GetAppInfo().ID; return NS_OK; @@ -694,7 +694,7 @@ nsXULAppInfo::GetID(nsACString& aResult) NS_IMETHODIMP nsXULAppInfo::GetVersion(nsACString& aResult) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { ContentChild* cc = ContentChild::GetSingleton(); aResult = cc->GetAppInfo().version; return NS_OK; @@ -715,7 +715,7 @@ nsXULAppInfo::GetPlatformVersion(nsACString& aResult) NS_IMETHODIMP nsXULAppInfo::GetAppBuildID(nsACString& aResult) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { ContentChild* cc = ContentChild::GetSingleton(); aResult = cc->GetAppInfo().buildID; return NS_OK; @@ -736,7 +736,7 @@ nsXULAppInfo::GetPlatformBuildID(nsACString& aResult) NS_IMETHODIMP nsXULAppInfo::GetUAName(nsACString& aResult) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { ContentChild* cc = ContentChild::GetSingleton(); aResult = cc->GetAppInfo().UAName; return NS_OK; @@ -902,7 +902,7 @@ nsXULAppInfo::GetIs64Bit(bool* aResult) NS_IMETHODIMP nsXULAppInfo::EnsureContentProcess() { - if (XRE_GetProcessType() != GeckoProcessType_Default) + if (!XRE_IsParentProcess()) return NS_ERROR_NOT_AVAILABLE; nsRefPtr unused = ContentParent::GetNewOrUsedBrowserProcess(); @@ -4040,6 +4040,12 @@ XRE_IsParentProcess() return XRE_GetProcessType() == GeckoProcessType_Default; } +bool +XRE_IsContentProcess() +{ + return XRE_GetProcessType() == GeckoProcessType_Content; +} + #ifdef E10S_TESTING_ONLY static void LogE10sBlockedReason(const char *reason) { diff --git a/toolkit/xre/nsSigHandlers.cpp b/toolkit/xre/nsSigHandlers.cpp index cb4b4e6808..d6846afbe9 100644 --- a/toolkit/xre/nsSigHandlers.cpp +++ b/toolkit/xre/nsSigHandlers.cpp @@ -259,7 +259,7 @@ void InstallSignalHandlers(const char *ProgramName) sigaction(SIGFPE, &sa, &osa); #endif - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { /* * If the user is debugging a Gecko parent process in gdb and hits ^C to * suspend, a SIGINT signal will be sent to the child. We ignore this signal diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp index a3a972bb51..582d744836 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -695,7 +695,7 @@ NS_IMETHODIMP nsExternalHelperAppService::DoContent(const nsACString& aMimeConte nsIInterfaceRequestor *aWindowContext, nsIStreamListener ** aStreamListener) { - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { return DoContentContentProcessHelper(aMimeContentType, aRequest, aContentContext, aForceSave, aWindowContext, aStreamListener); } @@ -951,7 +951,7 @@ nsExternalHelperAppService::LoadURI(nsIURI *aURI, { NS_ENSURE_ARG_POINTER(aURI); - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { URIParams uri; SerializeURI(aURI, uri); @@ -1257,7 +1257,7 @@ nsExternalAppHandler::~nsExternalAppHandler() void nsExternalAppHandler::DidDivertRequest(nsIRequest *request) { - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Content, "in child process"); + MOZ_ASSERT(XRE_IsContentProcess(), "in child process"); // Remove our request from the child loadGroup RetargetLoadNotifications(request); MaybeCloseWindow(); @@ -1611,7 +1611,7 @@ NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest *request, nsISuppo // At this point, the child process has done everything it can usefully do // for OnStartRequest. - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { return NS_OK; } @@ -1850,7 +1850,7 @@ void nsExternalAppHandler::SendStatusChange(ErrorType type, nsresult rv, nsIRequ mDialogProgressListener->OnStatusChange(nullptr, (type == kReadError) ? aRequest : nullptr, rv, msgText); } else if (mTransfer) { mTransfer->OnStatusChange(nullptr, (type == kReadError) ? aRequest : nullptr, rv, msgText); - } else if (XRE_GetProcessType() == GeckoProcessType_Default) { + } else if (XRE_IsParentProcess()) { // We don't have a listener. Simply show the alert ourselves. nsresult qiRv; nsCOMPtr prompter(do_GetInterface(GetDialogParent(), &qiRv)); diff --git a/widget/GfxInfoBase.cpp b/widget/GfxInfoBase.cpp index 886eb4dba4..42a485962a 100644 --- a/widget/GfxInfoBase.cpp +++ b/widget/GfxInfoBase.cpp @@ -652,7 +652,7 @@ GfxInfoBase::GetFeatureStatus(int32_t aFeature, int32_t* aStatus) if (GetPrefValueForFeature(aFeature, *aStatus)) return NS_OK; - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { // Delegate to the parent process. mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); bool success; diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp index 563bad2415..24e83eaca0 100644 --- a/widget/PuppetWidget.cpp +++ b/widget/PuppetWidget.cpp @@ -1125,7 +1125,7 @@ PuppetWidget::MemoryPressureObserver::Observe(nsISupports* aSubject, if (strcmp("memory-pressure", aTopic) == 0) { if (!mWidget->mVisible && mWidget->mLayerManager && - XRE_GetProcessType() == GeckoProcessType_Content) { + XRE_IsContentProcess()) { mWidget->mLayerManager->ClearCachedResources(); } } @@ -1146,7 +1146,7 @@ bool PuppetWidget::NeedsPaint() { // e10s popups are handled by the parent process, so never should be painted here - if (XRE_GetProcessType() == GeckoProcessType_Content && + if (XRE_IsContentProcess() && Preferences::GetBool("browser.tabs.remote.desktopbehavior", false) && mWindowType == eWindowType_popup) { NS_WARNING("Trying to paint an e10s popup in the child process!"); diff --git a/widget/cocoa/nsLookAndFeel.mm b/widget/cocoa/nsLookAndFeel.mm index bac6106d68..c4bceb53fe 100644 --- a/widget/cocoa/nsLookAndFeel.mm +++ b/widget/cocoa/nsLookAndFeel.mm @@ -690,7 +690,7 @@ nsLookAndFeel::RefreshImpl() // We should only clear the cache if we're in the main browser process. // Otherwise, we should wait for the parent to inform us of new values // to cache via LookAndFeel::SetIntCache. - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { mUseOverlayScrollbarsCached = false; mAllowOverlayScrollbarsOverlapCached = false; } diff --git a/widget/gonk/nsAppShell.cpp b/widget/gonk/nsAppShell.cpp index 90096e139c..f9f0f64559 100644 --- a/widget/gonk/nsAppShell.cpp +++ b/widget/gonk/nsAppShell.cpp @@ -882,7 +882,7 @@ nsAppShell::Init() InitGonkMemoryPressureMonitoring(); - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { printf("*****************************************************************\n"); printf("***\n"); printf("*** This is stdout. Most of the useful output will be in logcat.\n"); diff --git a/widget/gonk/nsClipboard.cpp b/widget/gonk/nsClipboard.cpp index 2aae6df49b..6b8f820fbe 100644 --- a/widget/gonk/nsClipboard.cpp +++ b/widget/gonk/nsClipboard.cpp @@ -31,7 +31,7 @@ nsClipboard::SetData(nsITransferable *aTransferable, return NS_ERROR_NOT_IMPLEMENTED; } - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { // Re-direct to the clipboard proxy. nsRefPtr clipboardProxy = new nsClipboardProxy(); return clipboardProxy->SetData(aTransferable, anOwner, aWhichClipboard); @@ -64,7 +64,7 @@ nsClipboard::GetData(nsITransferable *aTransferable, int32_t aWhichClipboard) return NS_ERROR_NOT_IMPLEMENTED; } - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { // Re-direct to the clipboard proxy. nsRefPtr clipboardProxy = new nsClipboardProxy(); return clipboardProxy->GetData(aTransferable, aWhichClipboard); @@ -104,7 +104,7 @@ nsClipboard::EmptyClipboard(int32_t aWhichClipboard) if (aWhichClipboard != kGlobalClipboard) { return NS_ERROR_NOT_IMPLEMENTED; } - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { mClipboard.Truncate(0); } else { ContentChild::GetSingleton()->SendEmptyClipboard(aWhichClipboard); @@ -122,7 +122,7 @@ nsClipboard::HasDataMatchingFlavors(const char **aFlavorList, if (aWhichClipboard != kGlobalClipboard) { return NS_ERROR_NOT_IMPLEMENTED; } - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { *aHasType = !mClipboard.IsEmpty(); } else { nsRefPtr clipboardProxy = new nsClipboardProxy(); diff --git a/widget/gonk/nsWidgetFactory.cpp b/widget/gonk/nsWidgetFactory.cpp index e11c918226..ddd116f566 100644 --- a/widget/gonk/nsWidgetFactory.cpp +++ b/widget/gonk/nsWidgetFactory.cpp @@ -72,7 +72,7 @@ NS_DEFINE_NAMED_CID(NS_CLIPBOARDHELPER_CID); static nsresult ScreenManagerConstructor(nsISupports *aOuter, REFNSIID aIID, void **aResult) { - return (XRE_GetProcessType() == GeckoProcessType_Default) ? + return (XRE_IsParentProcess()) ? nsScreenManagerGonkConstructor(aOuter, aIID, aResult) : PuppetScreenManagerConstructor(aOuter, aIID, aResult); } diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index e42381eaee..849264c1c2 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -2326,7 +2326,7 @@ public: static bool UsePuppetWidgets() { - return XRE_GetProcessType() == GeckoProcessType_Content; + return XRE_IsContentProcess(); } /** diff --git a/widget/nsIdleService.cpp b/widget/nsIdleService.cpp index efb2ccb2d2..7fbc9dde8b 100644 --- a/widget/nsIdleService.cpp +++ b/widget/nsIdleService.cpp @@ -398,7 +398,7 @@ nsIdleService::nsIdleService() : mCurrentlySetToTimeoutAt(TimeStamp()), sLog = PR_NewLogModule("idleService"); MOZ_ASSERT(!gIdleService); gIdleService = this; - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { mDailyIdle = new nsIdleServiceDaily(this); mDailyIdle->Init(); } @@ -425,7 +425,7 @@ nsIdleService::AddIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS) // high either - no more than ~136 years. NS_ENSURE_ARG_RANGE(aIdleTimeInS, 1, (UINT32_MAX / 10) - 1); - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { dom::ContentChild* cpc = dom::ContentChild::GetSingleton(); cpc->AddIdleObserver(aObserver, aIdleTimeInS); return NS_OK; @@ -484,7 +484,7 @@ nsIdleService::RemoveIdleObserver(nsIObserver* aObserver, uint32_t aTimeInS) NS_ENSURE_ARG_POINTER(aObserver); NS_ENSURE_ARG(aTimeInS); - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { dom::ContentChild* cpc = dom::ContentChild::GetSingleton(); cpc->RemoveIdleObserver(aObserver, aTimeInS); return NS_OK; diff --git a/widget/nsXPLookAndFeel.cpp b/widget/nsXPLookAndFeel.cpp index 0e59291ced..12ae452c30 100644 --- a/widget/nsXPLookAndFeel.cpp +++ b/widget/nsXPLookAndFeel.cpp @@ -457,7 +457,7 @@ nsXPLookAndFeel::Init() sUseNativeColors = val; } - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); diff --git a/widget/windows/AudioSession.cpp b/widget/windows/AudioSession.cpp index 52f12d4256..c69e575695 100644 --- a/widget/windows/AudioSession.cpp +++ b/widget/windows/AudioSession.cpp @@ -193,10 +193,10 @@ AudioSession::Start() mState = FAILED; // XXXkhuey implement this for content processes - if (XRE_GetProcessType() == GeckoProcessType_Content) + if (XRE_IsContentProcess()) return NS_ERROR_FAILURE; - MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default, + MOZ_ASSERT(XRE_IsParentProcess(), "Should only get here in a chrome process!"); nsCOMPtr bundleService = @@ -315,7 +315,7 @@ AudioSession::Stop() nsRefPtr kungFuDeathGrip; kungFuDeathGrip.swap(sService); - if (XRE_GetProcessType() != GeckoProcessType_Content) + if (!XRE_IsContentProcess()) StopInternal(); // At this point kungFuDeathGrip should be the only reference to AudioSession @@ -362,7 +362,7 @@ AudioSession::SetSessionData(const nsID& aID, { MOZ_ASSERT(mState == UNINITIALIZED, "State invariants violated"); - MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default, + MOZ_ASSERT(!XRE_IsParentProcess(), "Should never get here in a chrome process!"); mState = CLONED; diff --git a/widget/windows/nsNativeThemeWin.cpp b/widget/windows/nsNativeThemeWin.cpp index 426e54ecdd..eb3e6ce947 100644 --- a/widget/windows/nsNativeThemeWin.cpp +++ b/widget/windows/nsNativeThemeWin.cpp @@ -66,7 +66,7 @@ GetTopLevelWindowActiveState(nsIFrame *aFrame) // Used by window frame and button box rendering. We can end up in here in // the content process when rendering one of these moz styles freely in a // page. Bail in this case, there is no applicable window focus state. - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return mozilla::widget::themeconst::FS_INACTIVE; } // Get the widget. nsIFrame's GetNearestWidget walks up the view chain diff --git a/xpcom/base/nsDumpUtils.cpp b/xpcom/base/nsDumpUtils.cpp index 33b4bff303..35b0a3862b 100644 --- a/xpcom/base/nsDumpUtils.cpp +++ b/xpcom/base/nsDumpUtils.cpp @@ -262,7 +262,7 @@ FifoWatcher::MaybeCreate() { MOZ_ASSERT(NS_IsMainThread()); - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { // We want this to be main-process only, since two processes can't listen // to the same fifo. return false; diff --git a/xpcom/base/nsMemoryInfoDumper.cpp b/xpcom/base/nsMemoryInfoDumper.cpp index a9315d492e..2eddc3d87d 100644 --- a/xpcom/base/nsMemoryInfoDumper.cpp +++ b/xpcom/base/nsMemoryInfoDumper.cpp @@ -484,7 +484,7 @@ public: // of the form "$PROCESS_NAME (pid $PID)", or just "(pid $PID)" if we // don't have a process name. If we're the main process, we let // $PROCESS_NAME be "Main Process". - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { // We're the main process. process.AssignLiteral("Main Process"); } else if (ContentChild* cc = ContentChild::GetSingleton()) { diff --git a/xpcom/base/nsTraceRefcnt.cpp b/xpcom/base/nsTraceRefcnt.cpp index 959d317ec8..ea5e52c311 100644 --- a/xpcom/base/nsTraceRefcnt.cpp +++ b/xpcom/base/nsTraceRefcnt.cpp @@ -633,7 +633,7 @@ InitLog(const char* aEnvVar, const char* aMsg, FILE** aResult) } else { FILE* stream; nsAutoCString fname(value); - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { bool hasLogExtension = fname.RFind(".log", true, -1, 4) == kNotFound ? false : true; if (hasLogExtension) { diff --git a/xpcom/build/XPCOMInit.cpp b/xpcom/build/XPCOMInit.cpp index 905ce051e2..e7c2dc94b3 100644 --- a/xpcom/build/XPCOMInit.cpp +++ b/xpcom/build/XPCOMInit.cpp @@ -522,7 +522,7 @@ NS_InitXPCOM2(nsIServiceManager** aResult, sMessageLoop->set_hang_timeouts(128, 8192); } - if (XRE_GetProcessType() == GeckoProcessType_Default && + if (XRE_IsParentProcess() && !BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO)) { UniquePtr ioThread = MakeUnique(BrowserProcessSubThread::IO); @@ -713,7 +713,7 @@ NS_InitXPCOM2(nsIServiceManager** aResult, // We only want the SystemMemoryReporter running in one process, because it // profiles the entire system. The main process is the obvious place for // it. - if (XRE_GetProcessType() == GeckoProcessType_Default) { + if (XRE_IsParentProcess()) { mozilla::SystemMemoryReporter::Init(); } @@ -939,7 +939,7 @@ ShutdownXPCOM(nsIServiceManager* aServMgr) // On Windows XP debug, there are intermittent failures in // dom/media/tests/mochitest/test_peerConnection_basicH264Video.html // if we don't exit early in a child process. See bug 1073310. - if (XRE_GetProcessType() == GeckoProcessType_Content && !IsVistaOrLater()) { + if (XRE_IsContentProcess() && !IsVistaOrLater()) { NS_WARNING("Exiting child process early!"); exit(0); } @@ -1009,7 +1009,7 @@ ShutdownXPCOM(nsIServiceManager* aServMgr) // checking working on Linux. // On debug B2G, the child process crashes very late. Instead, just // give up so at least we exit cleanly. See bug 1071866. - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { NS_WARNING("Exiting child process early!"); exit(0); } diff --git a/xpcom/build/nsXULAppAPI.h b/xpcom/build/nsXULAppAPI.h index 0b07d7be75..b90e906acb 100644 --- a/xpcom/build/nsXULAppAPI.h +++ b/xpcom/build/nsXULAppAPI.h @@ -391,6 +391,9 @@ XRE_API(GeckoProcessType, XRE_API(bool, XRE_IsParentProcess, ()) +XRE_API(bool, + XRE_IsContentProcess, ()) + typedef void (*MainFunction)(void* aData); XRE_API(nsresult, diff --git a/xpcom/components/ManifestParser.cpp b/xpcom/components/ManifestParser.cpp index 0f33c6b02a..0d6029865e 100644 --- a/xpcom/components/ManifestParser.cpp +++ b/xpcom/components/ManifestParser.cpp @@ -585,7 +585,7 @@ ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf, } #endif - if (XRE_GetProcessType() == GeckoProcessType_Content) { + if (XRE_IsContentProcess()) { process = kContent; } else { process = kMain; diff --git a/xpcom/io/nsAnonymousTemporaryFile.cpp b/xpcom/io/nsAnonymousTemporaryFile.cpp index 991615d888..966a991ba8 100644 --- a/xpcom/io/nsAnonymousTemporaryFile.cpp +++ b/xpcom/io/nsAnonymousTemporaryFile.cpp @@ -274,7 +274,7 @@ CreateAnonTempFileRemover() // is a shutdown observer. We only create the temp file remover if we're running // in the main process; there's no point in doing the temp file removal multiple // times per startup. - if (XRE_GetProcessType() != GeckoProcessType_Default) { + if (!XRE_IsParentProcess()) { return NS_OK; } nsRefPtr tempRemover = new nsAnonTempFileRemover(); diff --git a/xpcom/libxpcomrt/XPCOMRTStubs.cpp b/xpcom/libxpcomrt/XPCOMRTStubs.cpp index 15b92f842d..0820c002b3 100644 --- a/xpcom/libxpcomrt/XPCOMRTStubs.cpp +++ b/xpcom/libxpcomrt/XPCOMRTStubs.cpp @@ -14,6 +14,18 @@ XRE_GetProcessType() return GeckoProcessType_Default; } +bool +XRE_IsParentProcess() +{ + return XRE_GetProcessType() == GeckoProcessType_Default; +} + +bool +XRE_IsContentProcess() +{ + return XRE_GetProcessType() == GeckoProcessType_Content; +} + #define PRINT_CALLED fprintf(stderr, "!!! ERROR: function %s defined in file %s should not be called, needs to be correctly implemented.\n", __FUNCTION__, __FILE__) class nsAString; diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp index 6122cc7881..8caa7c373a 100644 --- a/xpcom/threads/nsThread.cpp +++ b/xpcom/threads/nsThread.cpp @@ -687,7 +687,7 @@ public: static bool EventLatencyIsImportant() { - return NS_IsMainThread() && XRE_GetProcessType() == GeckoProcessType_Default; + return NS_IsMainThread() && XRE_IsParentProcess(); } };