From 60056e5afe95d616de3a8e88681b5b6372119f17 Mon Sep 17 00:00:00 2001 From: Ben Kelly Date: Fri, 8 Apr 2016 15:43:11 -0700 Subject: [PATCH] Bug 1260591 Move ServiceWorkerInfo and ServiceWorkerRegistrationInfo into separate files. r=jdm --- dom/workers/ServiceWorkerInfo.cpp | 200 +++++++ dom/workers/ServiceWorkerInfo.h | 144 +++++ dom/workers/ServiceWorkerManager.cpp | 549 +----------------- dom/workers/ServiceWorkerManager.h | 235 +------- dom/workers/ServiceWorkerRegistrationInfo.cpp | 370 ++++++++++++ dom/workers/ServiceWorkerRegistrationInfo.h | 132 +++++ dom/workers/moz.build | 4 + 7 files changed, 852 insertions(+), 782 deletions(-) create mode 100644 dom/workers/ServiceWorkerInfo.cpp create mode 100644 dom/workers/ServiceWorkerInfo.h create mode 100644 dom/workers/ServiceWorkerRegistrationInfo.cpp create mode 100644 dom/workers/ServiceWorkerRegistrationInfo.h diff --git a/dom/workers/ServiceWorkerInfo.cpp b/dom/workers/ServiceWorkerInfo.cpp new file mode 100644 index 000000000000..622989e5fc54 --- /dev/null +++ b/dom/workers/ServiceWorkerInfo.cpp @@ -0,0 +1,200 @@ +/* -*- 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 "ServiceWorkerInfo.h" + +BEGIN_WORKERS_NAMESPACE + +NS_IMPL_ISUPPORTS(ServiceWorkerInfo, nsIServiceWorkerInfo) + +NS_IMETHODIMP +ServiceWorkerInfo::GetScriptSpec(nsAString& aScriptSpec) +{ + AssertIsOnMainThread(); + CopyUTF8toUTF16(mScriptSpec, aScriptSpec); + return NS_OK; +} + +NS_IMETHODIMP +ServiceWorkerInfo::GetCacheName(nsAString& aCacheName) +{ + AssertIsOnMainThread(); + aCacheName = mCacheName; + return NS_OK; +} + +NS_IMETHODIMP +ServiceWorkerInfo::GetDebugger(nsIWorkerDebugger** aResult) +{ + if (NS_WARN_IF(!aResult)) { + return NS_ERROR_FAILURE; + } + + return mServiceWorkerPrivate->GetDebugger(aResult); +} + +NS_IMETHODIMP +ServiceWorkerInfo::AttachDebugger() +{ + return mServiceWorkerPrivate->AttachDebugger(); +} + +NS_IMETHODIMP +ServiceWorkerInfo::DetachDebugger() +{ + return mServiceWorkerPrivate->DetachDebugger(); +} + +void +ServiceWorkerInfo::AppendWorker(ServiceWorker* aWorker) +{ + MOZ_ASSERT(aWorker); +#ifdef DEBUG + nsAutoString workerURL; + aWorker->GetScriptURL(workerURL); + MOZ_ASSERT(workerURL.Equals(NS_ConvertUTF8toUTF16(mScriptSpec))); +#endif + MOZ_ASSERT(!mInstances.Contains(aWorker)); + + mInstances.AppendElement(aWorker); + aWorker->SetState(State()); +} + +void +ServiceWorkerInfo::RemoveWorker(ServiceWorker* aWorker) +{ + MOZ_ASSERT(aWorker); +#ifdef DEBUG + nsAutoString workerURL; + aWorker->GetScriptURL(workerURL); + MOZ_ASSERT(workerURL.Equals(NS_ConvertUTF8toUTF16(mScriptSpec))); +#endif + MOZ_ASSERT(mInstances.Contains(aWorker)); + + mInstances.RemoveElement(aWorker); +} + +namespace { + +class ChangeStateUpdater final : public nsRunnable +{ +public: + ChangeStateUpdater(const nsTArray& aInstances, + ServiceWorkerState aState) + : mState(aState) + { + for (size_t i = 0; i < aInstances.Length(); ++i) { + mInstances.AppendElement(aInstances[i]); + } + } + + NS_IMETHODIMP Run() + { + // We need to update the state of all instances atomically before notifying + // them to make sure that the observed state for all instances inside + // statechange event handlers is correct. + for (size_t i = 0; i < mInstances.Length(); ++i) { + mInstances[i]->SetState(mState); + } + for (size_t i = 0; i < mInstances.Length(); ++i) { + mInstances[i]->DispatchStateChange(mState); + } + + return NS_OK; + } + +private: + AutoTArray, 1> mInstances; + ServiceWorkerState mState; +}; + +} + +void +ServiceWorkerInfo::UpdateState(ServiceWorkerState aState) +{ + AssertIsOnMainThread(); +#ifdef DEBUG + // Any state can directly transition to redundant, but everything else is + // ordered. + if (aState != ServiceWorkerState::Redundant) { + MOZ_ASSERT_IF(mState == ServiceWorkerState::EndGuard_, aState == ServiceWorkerState::Installing); + MOZ_ASSERT_IF(mState == ServiceWorkerState::Installing, aState == ServiceWorkerState::Installed); + MOZ_ASSERT_IF(mState == ServiceWorkerState::Installed, aState == ServiceWorkerState::Activating); + MOZ_ASSERT_IF(mState == ServiceWorkerState::Activating, aState == ServiceWorkerState::Activated); + } + // Activated can only go to redundant. + MOZ_ASSERT_IF(mState == ServiceWorkerState::Activated, aState == ServiceWorkerState::Redundant); +#endif + // Flush any pending functional events to the worker when it transitions to the + // activated state. + // TODO: Do we care that these events will race with the propagation of the + // state change? + if (aState == ServiceWorkerState::Activated && mState != aState) { + mServiceWorkerPrivate->Activated(); + } + mState = aState; + nsCOMPtr r = new ChangeStateUpdater(mInstances, mState); + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r.forget())); +} + +ServiceWorkerInfo::ServiceWorkerInfo(nsIPrincipal* aPrincipal, + const nsACString& aScope, + const nsACString& aScriptSpec, + const nsAString& aCacheName) + : mPrincipal(aPrincipal) + , mScope(aScope) + , mScriptSpec(aScriptSpec) + , mCacheName(aCacheName) + , mState(ServiceWorkerState::EndGuard_) + , mServiceWorkerID(GetNextID()) + , mServiceWorkerPrivate(new ServiceWorkerPrivate(this)) + , mSkipWaitingFlag(false) +{ + MOZ_ASSERT(mPrincipal); + MOZ_ASSERT(!mScope.IsEmpty()); + MOZ_ASSERT(!mScriptSpec.IsEmpty()); + MOZ_ASSERT(!mCacheName.IsEmpty()); +} + +ServiceWorkerInfo::~ServiceWorkerInfo() +{ + MOZ_ASSERT(mServiceWorkerPrivate); + mServiceWorkerPrivate->NoteDeadServiceWorkerInfo(); +} + +static uint64_t gServiceWorkerInfoCurrentID = 0; + +uint64_t +ServiceWorkerInfo::GetNextID() const +{ + return ++gServiceWorkerInfoCurrentID; +} + +already_AddRefed +ServiceWorkerInfo::GetOrCreateInstance(nsPIDOMWindowInner* aWindow) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aWindow); + + RefPtr ref; + + for (uint32_t i = 0; i < mInstances.Length(); ++i) { + MOZ_ASSERT(mInstances[i]); + if (mInstances[i]->GetOwner() == aWindow) { + ref = mInstances[i]; + break; + } + } + + if (!ref) { + ref = new ServiceWorker(aWindow, this); + } + + return ref.forget(); +} + +END_WORKERS_NAMESPACE diff --git a/dom/workers/ServiceWorkerInfo.h b/dom/workers/ServiceWorkerInfo.h new file mode 100644 index 000000000000..4529de159213 --- /dev/null +++ b/dom/workers/ServiceWorkerInfo.h @@ -0,0 +1,144 @@ +/* -*- 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_workers_serviceworkerinfo_h +#define mozilla_dom_workers_serviceworkerinfo_h + +#include "mozilla/dom/ServiceWorkerBinding.h" // For ServiceWorkerState +#include "nsIServiceWorkerManager.h" + +namespace mozilla { +namespace dom { +namespace workers { + +class ServiceWorker; +class ServiceWorkerPrivate; + +/* + * Wherever the spec treats a worker instance and a description of said worker + * as the same thing; i.e. "Resolve foo with + * _GetNewestWorker(serviceWorkerRegistration)", we represent the description + * by this class and spawn a ServiceWorker in the right global when required. + */ +class ServiceWorkerInfo final : public nsIServiceWorkerInfo +{ +private: + nsCOMPtr mPrincipal; + const nsCString mScope; + const nsCString mScriptSpec; + const nsString mCacheName; + ServiceWorkerState mState; + + // This id is shared with WorkerPrivate to match requests issued by service + // workers to their corresponding serviceWorkerInfo. + uint64_t mServiceWorkerID; + + // We hold rawptrs since the ServiceWorker constructor and destructor ensure + // addition and removal. + // There is a high chance of there being at least one ServiceWorker + // associated with this all the time. + AutoTArray mInstances; + + RefPtr mServiceWorkerPrivate; + bool mSkipWaitingFlag; + + ~ServiceWorkerInfo(); + + // Generates a unique id for the service worker, with zero being treated as + // invalid. + uint64_t + GetNextID() const; + +public: + NS_DECL_ISUPPORTS + NS_DECL_NSISERVICEWORKERINFO + + class ServiceWorkerPrivate* + WorkerPrivate() const + { + MOZ_ASSERT(mServiceWorkerPrivate); + return mServiceWorkerPrivate; + } + + nsIPrincipal* + GetPrincipal() const + { + return mPrincipal; + } + + const nsCString& + ScriptSpec() const + { + return mScriptSpec; + } + + const nsCString& + Scope() const + { + return mScope; + } + + bool SkipWaitingFlag() const + { + AssertIsOnMainThread(); + return mSkipWaitingFlag; + } + + void SetSkipWaitingFlag() + { + AssertIsOnMainThread(); + mSkipWaitingFlag = true; + } + + ServiceWorkerInfo(nsIPrincipal* aPrincipal, + const nsACString& aScope, + const nsACString& aScriptSpec, + const nsAString& aCacheName); + + ServiceWorkerState + State() const + { + return mState; + } + + const nsString& + CacheName() const + { + return mCacheName; + } + + uint64_t + ID() const + { + return mServiceWorkerID; + } + + void + UpdateState(ServiceWorkerState aState); + + // Only used to set initial state when loading from disk! + void + SetActivateStateUncheckedWithoutEvent(ServiceWorkerState aState) + { + AssertIsOnMainThread(); + mState = aState; + } + + void + AppendWorker(ServiceWorker* aWorker); + + void + RemoveWorker(ServiceWorker* aWorker); + + already_AddRefed + GetOrCreateInstance(nsPIDOMWindowInner* aWindow); +}; + +} // namespace workers +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_workers_serviceworkerinfo_h diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index 87b97d8b3038..4eb787914d40 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -64,6 +64,7 @@ #include "ServiceWorker.h" #include "ServiceWorkerClient.h" #include "ServiceWorkerContainer.h" +#include "ServiceWorkerInfo.h" #include "ServiceWorkerJobQueue.h" #include "ServiceWorkerManagerChild.h" #include "ServiceWorkerPrivate.h" @@ -213,180 +214,6 @@ private: } // namespace -void -ServiceWorkerRegistrationInfo::Clear() -{ - if (mInstallingWorker) { - mInstallingWorker->UpdateState(ServiceWorkerState::Redundant); - mInstallingWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo(); - mInstallingWorker = nullptr; - // FIXME(nsm): Abort any inflight requests from installing worker. - } - - if (mWaitingWorker) { - mWaitingWorker->UpdateState(ServiceWorkerState::Redundant); - - nsresult rv = serviceWorkerScriptCache::PurgeCache(mPrincipal, - mWaitingWorker->CacheName()); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to purge the waiting cache."); - } - - mWaitingWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo(); - mWaitingWorker = nullptr; - } - - if (mActiveWorker) { - mActiveWorker->UpdateState(ServiceWorkerState::Redundant); - - nsresult rv = serviceWorkerScriptCache::PurgeCache(mPrincipal, - mActiveWorker->CacheName()); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to purge the active cache."); - } - - mActiveWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo(); - mActiveWorker = nullptr; - } - - RefPtr swm = ServiceWorkerManager::GetInstance(); - MOZ_ASSERT(swm); - swm->InvalidateServiceWorkerRegistrationWorker(this, - WhichServiceWorker::INSTALLING_WORKER | - WhichServiceWorker::WAITING_WORKER | - WhichServiceWorker::ACTIVE_WORKER); -} - -ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(const nsACString& aScope, - nsIPrincipal* aPrincipal) - : mControlledDocumentsCounter(0) - , mUpdateState(NoUpdate) - , mLastUpdateCheckTime(0) - , mScope(aScope) - , mPrincipal(aPrincipal) - , mPendingUninstall(false) -{} - -ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo() -{ - if (IsControllingDocuments()) { - NS_WARNING("ServiceWorkerRegistrationInfo is still controlling documents. This can be a bug or a leak in ServiceWorker API or in any other API that takes the document alive."); - } -} - -NS_IMPL_ISUPPORTS(ServiceWorkerRegistrationInfo, nsIServiceWorkerRegistrationInfo) - -NS_IMETHODIMP -ServiceWorkerRegistrationInfo::GetPrincipal(nsIPrincipal** aPrincipal) -{ - AssertIsOnMainThread(); - NS_ADDREF(*aPrincipal = mPrincipal); - return NS_OK; -} - -NS_IMETHODIMP -ServiceWorkerRegistrationInfo::GetScope(nsAString& aScope) -{ - AssertIsOnMainThread(); - CopyUTF8toUTF16(mScope, aScope); - return NS_OK; -} - -NS_IMETHODIMP -ServiceWorkerRegistrationInfo::GetScriptSpec(nsAString& aScriptSpec) -{ - AssertIsOnMainThread(); - RefPtr newest = Newest(); - if (newest) { - CopyUTF8toUTF16(newest->ScriptSpec(), aScriptSpec); - } - return NS_OK; -} - -NS_IMETHODIMP -ServiceWorkerRegistrationInfo::GetInstallingWorker(nsIServiceWorkerInfo **aResult) -{ - AssertIsOnMainThread(); - nsCOMPtr info = do_QueryInterface(mInstallingWorker); - info.forget(aResult); - return NS_OK; -} - -NS_IMETHODIMP -ServiceWorkerRegistrationInfo::GetWaitingWorker(nsIServiceWorkerInfo **aResult) -{ - AssertIsOnMainThread(); - nsCOMPtr info = do_QueryInterface(mWaitingWorker); - info.forget(aResult); - return NS_OK; -} - -NS_IMETHODIMP -ServiceWorkerRegistrationInfo::GetActiveWorker(nsIServiceWorkerInfo **aResult) -{ - AssertIsOnMainThread(); - nsCOMPtr info = do_QueryInterface(mActiveWorker); - info.forget(aResult); - return NS_OK; -} - -NS_IMETHODIMP -ServiceWorkerRegistrationInfo::GetWorkerByID(uint64_t aID, nsIServiceWorkerInfo **aResult) -{ - AssertIsOnMainThread(); - MOZ_ASSERT(aResult); - - RefPtr info = GetServiceWorkerInfoById(aID); - // It is ok to return null for a missing service worker info. - info.forget(aResult); - return NS_OK; -} - -NS_IMETHODIMP -ServiceWorkerRegistrationInfo::AddListener( - nsIServiceWorkerRegistrationInfoListener *aListener) -{ - AssertIsOnMainThread(); - - if (!aListener || mListeners.Contains(aListener)) { - return NS_ERROR_INVALID_ARG; - } - - mListeners.AppendElement(aListener); - - return NS_OK; -} - -NS_IMETHODIMP -ServiceWorkerRegistrationInfo::RemoveListener( - nsIServiceWorkerRegistrationInfoListener *aListener) -{ - AssertIsOnMainThread(); - - if (!aListener || !mListeners.Contains(aListener)) { - return NS_ERROR_INVALID_ARG; - } - - mListeners.RemoveElement(aListener); - - return NS_OK; -} - -already_AddRefed -ServiceWorkerRegistrationInfo::GetServiceWorkerInfoById(uint64_t aId) -{ - RefPtr serviceWorker; - if (mInstallingWorker && mInstallingWorker->ID() == aId) { - serviceWorker = mInstallingWorker; - } else if (mWaitingWorker && mWaitingWorker->ID() == aId) { - serviceWorker = mWaitingWorker; - } else if (mActiveWorker && mActiveWorker->ID() == aId) { - serviceWorker = mActiveWorker; - } - - return serviceWorker.forget(); -} - ////////////////////////// // ServiceWorkerManager // ////////////////////////// @@ -909,93 +736,6 @@ ServiceWorkerManager::AppendPendingOperation(nsIRunnable* aRunnable) } } -void -ServiceWorkerRegistrationInfo::TryToActivateAsync() -{ - nsCOMPtr r = - NS_NewRunnableMethod(this, - &ServiceWorkerRegistrationInfo::TryToActivate); - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r)); -} - -/* - * TryToActivate should not be called directly, use TryToACtivateAsync instead. - */ -void -ServiceWorkerRegistrationInfo::TryToActivate() -{ - if (!IsControllingDocuments() || - // Waiting worker will be removed if the registration is removed - (mWaitingWorker && mWaitingWorker->SkipWaitingFlag())) { - Activate(); - } -} - -void -ServiceWorkerRegistrationInfo::PurgeActiveWorker() -{ - RefPtr exitingWorker = mActiveWorker.forget(); - if (!exitingWorker) - return; - - // FIXME(jaoo): Bug 1170543 - Wait for exitingWorker to finish and terminate it. - exitingWorker->UpdateState(ServiceWorkerState::Redundant); - nsresult rv = serviceWorkerScriptCache::PurgeCache(mPrincipal, - exitingWorker->CacheName()); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to purge the activating cache."); - } - RefPtr swm = ServiceWorkerManager::GetInstance(); - swm->InvalidateServiceWorkerRegistrationWorker(this, WhichServiceWorker::ACTIVE_WORKER); -} - -void -ServiceWorkerRegistrationInfo::Activate() -{ - RefPtr activatingWorker = mWaitingWorker; - if (!activatingWorker) { - return; - } - - PurgeActiveWorker(); - - RefPtr swm = ServiceWorkerManager::GetInstance(); - swm->InvalidateServiceWorkerRegistrationWorker(this, WhichServiceWorker::WAITING_WORKER); - - mActiveWorker = activatingWorker.forget(); - mWaitingWorker = nullptr; - mActiveWorker->UpdateState(ServiceWorkerState::Activating); - NotifyListenersOnChange(); - - // FIXME(nsm): Unlink appcache if there is one. - - swm->CheckPendingReadyPromises(); - - // "Queue a task to fire a simple event named controllerchange..." - nsCOMPtr controllerChangeRunnable = - NS_NewRunnableMethodWithArg>( - swm, &ServiceWorkerManager::FireControllerChange, this); - NS_DispatchToMainThread(controllerChangeRunnable); - - nsCOMPtr failRunnable = - NS_NewRunnableMethodWithArg(this, - &ServiceWorkerRegistrationInfo::FinishActivate, - false /* success */); - - nsMainThreadPtrHandle handle( - new nsMainThreadPtrHolder(this)); - RefPtr callback = new ContinueActivateRunnable(handle); - - ServiceWorkerPrivate* workerPrivate = mActiveWorker->WorkerPrivate(); - MOZ_ASSERT(workerPrivate); - nsresult rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("activate"), - callback, failRunnable); - if (NS_WARN_IF(NS_FAILED(rv))) { - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(failRunnable)); - return; - } -} - /* * Implements the async aspects of the getRegistrations algorithm. */ @@ -1842,104 +1582,6 @@ ServiceWorkerManager::HandleError(JSContext* aCx, aColumnNumber, aFlags); } -void -ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess) -{ - if (mPendingUninstall || !mActiveWorker || - mActiveWorker->State() != ServiceWorkerState::Activating) { - return; - } - - // Activation never fails, so aSuccess is ignored. - mActiveWorker->UpdateState(ServiceWorkerState::Activated); - RefPtr swm = ServiceWorkerManager::GetInstance(); - swm->StoreRegistration(mPrincipal, this); -} - -void -ServiceWorkerRegistrationInfo::RefreshLastUpdateCheckTime() -{ - AssertIsOnMainThread(); - mLastUpdateCheckTime = PR_IntervalNow() / PR_MSEC_PER_SEC; -} - -bool -ServiceWorkerRegistrationInfo::IsLastUpdateCheckTimeOverOneDay() const -{ - AssertIsOnMainThread(); - - // For testing. - if (Preferences::GetBool("dom.serviceWorkers.testUpdateOverOneDay")) { - return true; - } - - const uint64_t kSecondsPerDay = 86400; - const uint64_t now = PR_IntervalNow() / PR_MSEC_PER_SEC; - - if ((mLastUpdateCheckTime != 0) && - (now - mLastUpdateCheckTime > kSecondsPerDay)) { - return true; - } - return false; -} - -void -ServiceWorkerRegistrationInfo::NotifyListenersOnChange() -{ - nsTArray> listeners(mListeners); - for (size_t index = 0; index < listeners.Length(); ++index) { - listeners[index]->OnChange(); - } -} - -void -ServiceWorkerRegistrationInfo::MaybeScheduleTimeCheckAndUpdate() -{ - AssertIsOnMainThread(); - - RefPtr swm = ServiceWorkerManager::GetInstance(); - if (!swm) { - // shutting down, do nothing - return; - } - - if (mUpdateState == NoUpdate) { - mUpdateState = NeedTimeCheckAndUpdate; - } - - swm->ScheduleUpdateTimer(mPrincipal, mScope); -} - -void -ServiceWorkerRegistrationInfo::MaybeScheduleUpdate() -{ - AssertIsOnMainThread(); - - RefPtr swm = ServiceWorkerManager::GetInstance(); - if (!swm) { - // shutting down, do nothing - return; - } - - mUpdateState = NeedUpdate; - - swm->ScheduleUpdateTimer(mPrincipal, mScope); -} - -bool -ServiceWorkerRegistrationInfo::CheckAndClearIfUpdateNeeded() -{ - AssertIsOnMainThread(); - - bool result = mUpdateState == NeedUpdate || - (mUpdateState == NeedTimeCheckAndUpdate && - IsLastUpdateCheckTimeOverOneDay()); - - mUpdateState = NoUpdate; - - return result; -} - void ServiceWorkerManager::LoadRegistration( const ServiceWorkerRegistrationData& aRegistration) @@ -4125,193 +3767,4 @@ ServiceWorkerManager::MaybeSendUnregister(nsIPrincipal* aPrincipal, Unused << mActor->SendUnregister(principalInfo, NS_ConvertUTF8toUTF16(aScope)); } -NS_IMPL_ISUPPORTS(ServiceWorkerInfo, nsIServiceWorkerInfo) - -NS_IMETHODIMP -ServiceWorkerInfo::GetScriptSpec(nsAString& aScriptSpec) -{ - AssertIsOnMainThread(); - CopyUTF8toUTF16(mScriptSpec, aScriptSpec); - return NS_OK; -} - -NS_IMETHODIMP -ServiceWorkerInfo::GetCacheName(nsAString& aCacheName) -{ - AssertIsOnMainThread(); - aCacheName = mCacheName; - return NS_OK; -} - -NS_IMETHODIMP -ServiceWorkerInfo::GetDebugger(nsIWorkerDebugger** aResult) -{ - if (NS_WARN_IF(!aResult)) { - return NS_ERROR_FAILURE; - } - - return mServiceWorkerPrivate->GetDebugger(aResult); -} - -NS_IMETHODIMP -ServiceWorkerInfo::AttachDebugger() -{ - return mServiceWorkerPrivate->AttachDebugger(); -} - -NS_IMETHODIMP -ServiceWorkerInfo::DetachDebugger() -{ - return mServiceWorkerPrivate->DetachDebugger(); -} - -void -ServiceWorkerInfo::AppendWorker(ServiceWorker* aWorker) -{ - MOZ_ASSERT(aWorker); -#ifdef DEBUG - nsAutoString workerURL; - aWorker->GetScriptURL(workerURL); - MOZ_ASSERT(workerURL.Equals(NS_ConvertUTF8toUTF16(mScriptSpec))); -#endif - MOZ_ASSERT(!mInstances.Contains(aWorker)); - - mInstances.AppendElement(aWorker); - aWorker->SetState(State()); -} - -void -ServiceWorkerInfo::RemoveWorker(ServiceWorker* aWorker) -{ - MOZ_ASSERT(aWorker); -#ifdef DEBUG - nsAutoString workerURL; - aWorker->GetScriptURL(workerURL); - MOZ_ASSERT(workerURL.Equals(NS_ConvertUTF8toUTF16(mScriptSpec))); -#endif - MOZ_ASSERT(mInstances.Contains(aWorker)); - - mInstances.RemoveElement(aWorker); -} - -namespace { - -class ChangeStateUpdater final : public nsRunnable -{ -public: - ChangeStateUpdater(const nsTArray& aInstances, - ServiceWorkerState aState) - : mState(aState) - { - for (size_t i = 0; i < aInstances.Length(); ++i) { - mInstances.AppendElement(aInstances[i]); - } - } - - NS_IMETHODIMP Run() - { - // We need to update the state of all instances atomically before notifying - // them to make sure that the observed state for all instances inside - // statechange event handlers is correct. - for (size_t i = 0; i < mInstances.Length(); ++i) { - mInstances[i]->SetState(mState); - } - for (size_t i = 0; i < mInstances.Length(); ++i) { - mInstances[i]->DispatchStateChange(mState); - } - - return NS_OK; - } - -private: - AutoTArray, 1> mInstances; - ServiceWorkerState mState; -}; - -} - -void -ServiceWorkerInfo::UpdateState(ServiceWorkerState aState) -{ - AssertIsOnMainThread(); -#ifdef DEBUG - // Any state can directly transition to redundant, but everything else is - // ordered. - if (aState != ServiceWorkerState::Redundant) { - MOZ_ASSERT_IF(mState == ServiceWorkerState::EndGuard_, aState == ServiceWorkerState::Installing); - MOZ_ASSERT_IF(mState == ServiceWorkerState::Installing, aState == ServiceWorkerState::Installed); - MOZ_ASSERT_IF(mState == ServiceWorkerState::Installed, aState == ServiceWorkerState::Activating); - MOZ_ASSERT_IF(mState == ServiceWorkerState::Activating, aState == ServiceWorkerState::Activated); - } - // Activated can only go to redundant. - MOZ_ASSERT_IF(mState == ServiceWorkerState::Activated, aState == ServiceWorkerState::Redundant); -#endif - // Flush any pending functional events to the worker when it transitions to the - // activated state. - // TODO: Do we care that these events will race with the propagation of the - // state change? - if (aState == ServiceWorkerState::Activated && mState != aState) { - mServiceWorkerPrivate->Activated(); - } - mState = aState; - nsCOMPtr r = new ChangeStateUpdater(mInstances, mState); - MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r.forget())); -} - -ServiceWorkerInfo::ServiceWorkerInfo(nsIPrincipal* aPrincipal, - const nsACString& aScope, - const nsACString& aScriptSpec, - const nsAString& aCacheName) - : mPrincipal(aPrincipal) - , mScope(aScope) - , mScriptSpec(aScriptSpec) - , mCacheName(aCacheName) - , mState(ServiceWorkerState::EndGuard_) - , mServiceWorkerID(GetNextID()) - , mServiceWorkerPrivate(new ServiceWorkerPrivate(this)) - , mSkipWaitingFlag(false) -{ - MOZ_ASSERT(mPrincipal); - MOZ_ASSERT(!mScope.IsEmpty()); - MOZ_ASSERT(!mScriptSpec.IsEmpty()); - MOZ_ASSERT(!mCacheName.IsEmpty()); -} - -ServiceWorkerInfo::~ServiceWorkerInfo() -{ - MOZ_ASSERT(mServiceWorkerPrivate); - mServiceWorkerPrivate->NoteDeadServiceWorkerInfo(); -} - -static uint64_t gServiceWorkerInfoCurrentID = 0; - -uint64_t -ServiceWorkerInfo::GetNextID() const -{ - return ++gServiceWorkerInfoCurrentID; -} - -already_AddRefed -ServiceWorkerInfo::GetOrCreateInstance(nsPIDOMWindowInner* aWindow) -{ - AssertIsOnMainThread(); - MOZ_ASSERT(aWindow); - - RefPtr ref; - - for (uint32_t i = 0; i < mInstances.Length(); ++i) { - MOZ_ASSERT(mInstances[i]); - if (mInstances[i]->GetOwner() == aWindow) { - ref = mInstances[i]; - break; - } - } - - if (!ref) { - ref = new ServiceWorker(aWindow, this); - } - - return ref.forget(); -} - END_WORKERS_NAMESPACE diff --git a/dom/workers/ServiceWorkerManager.h b/dom/workers/ServiceWorkerManager.h index 30b115851a37..601fea9a768d 100644 --- a/dom/workers/ServiceWorkerManager.h +++ b/dom/workers/ServiceWorkerManager.h @@ -20,10 +20,10 @@ #include "mozilla/WeakPtr.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/Promise.h" -#include "mozilla/dom/ServiceWorkerBinding.h" // For ServiceWorkerState #include "mozilla/dom/ServiceWorkerCommon.h" #include "mozilla/dom/ServiceWorkerRegistrar.h" #include "mozilla/dom/ServiceWorkerRegistrarTypes.h" +#include "mozilla/dom/workers/ServiceWorkerRegistrationInfo.h" #include "mozilla/ipc/BackgroundUtils.h" #include "nsClassHashtable.h" #include "nsDataHashtable.h" @@ -44,125 +44,12 @@ class ServiceWorkerRegistrationListener; namespace workers { -class ServiceWorker; class ServiceWorkerClientInfo; class ServiceWorkerInfo; class ServiceWorkerJobQueue; class ServiceWorkerManagerChild; class ServiceWorkerPrivate; -class ServiceWorkerRegistrationInfo final - : public nsIServiceWorkerRegistrationInfo -{ - uint32_t mControlledDocumentsCounter; - - enum - { - NoUpdate, - NeedTimeCheckAndUpdate, - NeedUpdate - } mUpdateState; - - uint64_t mLastUpdateCheckTime; - - virtual ~ServiceWorkerRegistrationInfo(); - -public: - NS_DECL_ISUPPORTS - NS_DECL_NSISERVICEWORKERREGISTRATIONINFO - - nsCString mScope; - - nsCOMPtr mPrincipal; - - RefPtr mActiveWorker; - RefPtr mWaitingWorker; - RefPtr mInstallingWorker; - - nsTArray> mListeners; - - // When unregister() is called on a registration, it is not immediately - // removed since documents may be controlled. It is marked as - // pendingUninstall and when all controlling documents go away, removed. - bool mPendingUninstall; - - ServiceWorkerRegistrationInfo(const nsACString& aScope, - nsIPrincipal* aPrincipal); - - already_AddRefed - Newest() const - { - RefPtr newest; - if (mInstallingWorker) { - newest = mInstallingWorker; - } else if (mWaitingWorker) { - newest = mWaitingWorker; - } else { - newest = mActiveWorker; - } - - return newest.forget(); - } - - already_AddRefed - GetServiceWorkerInfoById(uint64_t aId); - - void - StartControllingADocument() - { - ++mControlledDocumentsCounter; - } - - void - StopControllingADocument() - { - MOZ_ASSERT(mControlledDocumentsCounter); - --mControlledDocumentsCounter; - } - - bool - IsControllingDocuments() const - { - return mActiveWorker && mControlledDocumentsCounter; - } - - void - Clear(); - - void - PurgeActiveWorker(); - - void - TryToActivateAsync(); - - void - TryToActivate(); - - void - Activate(); - - void - FinishActivate(bool aSuccess); - - void - RefreshLastUpdateCheckTime(); - - bool - IsLastUpdateCheckTimeOverOneDay() const; - - void - NotifyListenersOnChange(); - - void - MaybeScheduleTimeCheckAndUpdate(); - - void - MaybeScheduleUpdate(); - - bool - CheckAndClearIfUpdateNeeded(); -}; - class ServiceWorkerUpdateFinishCallback { protected: @@ -179,126 +66,6 @@ public: void UpdateFailed(ErrorResult& aStatus) = 0; }; -/* - * Wherever the spec treats a worker instance and a description of said worker - * as the same thing; i.e. "Resolve foo with - * _GetNewestWorker(serviceWorkerRegistration)", we represent the description - * by this class and spawn a ServiceWorker in the right global when required. - */ -class ServiceWorkerInfo final : public nsIServiceWorkerInfo -{ -private: - nsCOMPtr mPrincipal; - const nsCString mScope; - const nsCString mScriptSpec; - const nsString mCacheName; - ServiceWorkerState mState; - - // This id is shared with WorkerPrivate to match requests issued by service - // workers to their corresponding serviceWorkerInfo. - uint64_t mServiceWorkerID; - - // We hold rawptrs since the ServiceWorker constructor and destructor ensure - // addition and removal. - // There is a high chance of there being at least one ServiceWorker - // associated with this all the time. - AutoTArray mInstances; - - RefPtr mServiceWorkerPrivate; - bool mSkipWaitingFlag; - - ~ServiceWorkerInfo(); - - // Generates a unique id for the service worker, with zero being treated as - // invalid. - uint64_t - GetNextID() const; - -public: - NS_DECL_ISUPPORTS - NS_DECL_NSISERVICEWORKERINFO - - class ServiceWorkerPrivate* - WorkerPrivate() const - { - MOZ_ASSERT(mServiceWorkerPrivate); - return mServiceWorkerPrivate; - } - - nsIPrincipal* - GetPrincipal() const - { - return mPrincipal; - } - - const nsCString& - ScriptSpec() const - { - return mScriptSpec; - } - - const nsCString& - Scope() const - { - return mScope; - } - - bool SkipWaitingFlag() const - { - AssertIsOnMainThread(); - return mSkipWaitingFlag; - } - - void SetSkipWaitingFlag() - { - AssertIsOnMainThread(); - mSkipWaitingFlag = true; - } - - ServiceWorkerInfo(nsIPrincipal* aPrincipal, - const nsACString& aScope, - const nsACString& aScriptSpec, - const nsAString& aCacheName); - - ServiceWorkerState - State() const - { - return mState; - } - - const nsString& - CacheName() const - { - return mCacheName; - } - - uint64_t - ID() const - { - return mServiceWorkerID; - } - - void - UpdateState(ServiceWorkerState aState); - - // Only used to set initial state when loading from disk! - void - SetActivateStateUncheckedWithoutEvent(ServiceWorkerState aState) - { - AssertIsOnMainThread(); - mState = aState; - } - - void - AppendWorker(ServiceWorker* aWorker); - - void - RemoveWorker(ServiceWorker* aWorker); - - already_AddRefed - GetOrCreateInstance(nsPIDOMWindowInner* aWindow); -}; - #define NS_SERVICEWORKERMANAGER_IMPL_IID \ { /* f4f8755a-69ca-46e8-a65d-775745535990 */ \ 0xf4f8755a, \ diff --git a/dom/workers/ServiceWorkerRegistrationInfo.cpp b/dom/workers/ServiceWorkerRegistrationInfo.cpp new file mode 100644 index 000000000000..6c5496e52f6a --- /dev/null +++ b/dom/workers/ServiceWorkerRegistrationInfo.cpp @@ -0,0 +1,370 @@ +/* -*- 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 "ServiceWorkerRegistrationInfo.h" + +BEGIN_WORKERS_NAMESPACE + +void +ServiceWorkerRegistrationInfo::Clear() +{ + if (mInstallingWorker) { + mInstallingWorker->UpdateState(ServiceWorkerState::Redundant); + mInstallingWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo(); + mInstallingWorker = nullptr; + // FIXME(nsm): Abort any inflight requests from installing worker. + } + + if (mWaitingWorker) { + mWaitingWorker->UpdateState(ServiceWorkerState::Redundant); + + nsresult rv = serviceWorkerScriptCache::PurgeCache(mPrincipal, + mWaitingWorker->CacheName()); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to purge the waiting cache."); + } + + mWaitingWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo(); + mWaitingWorker = nullptr; + } + + if (mActiveWorker) { + mActiveWorker->UpdateState(ServiceWorkerState::Redundant); + + nsresult rv = serviceWorkerScriptCache::PurgeCache(mPrincipal, + mActiveWorker->CacheName()); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to purge the active cache."); + } + + mActiveWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo(); + mActiveWorker = nullptr; + } + + RefPtr swm = ServiceWorkerManager::GetInstance(); + MOZ_ASSERT(swm); + swm->InvalidateServiceWorkerRegistrationWorker(this, + WhichServiceWorker::INSTALLING_WORKER | + WhichServiceWorker::WAITING_WORKER | + WhichServiceWorker::ACTIVE_WORKER); +} + +ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(const nsACString& aScope, + nsIPrincipal* aPrincipal) + : mControlledDocumentsCounter(0) + , mUpdateState(NoUpdate) + , mLastUpdateCheckTime(0) + , mScope(aScope) + , mPrincipal(aPrincipal) + , mPendingUninstall(false) +{} + +ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo() +{ + if (IsControllingDocuments()) { + NS_WARNING("ServiceWorkerRegistrationInfo is still controlling documents. This can be a bug or a leak in ServiceWorker API or in any other API that takes the document alive."); + } +} + +NS_IMPL_ISUPPORTS(ServiceWorkerRegistrationInfo, nsIServiceWorkerRegistrationInfo) + +NS_IMETHODIMP +ServiceWorkerRegistrationInfo::GetPrincipal(nsIPrincipal** aPrincipal) +{ + AssertIsOnMainThread(); + NS_ADDREF(*aPrincipal = mPrincipal); + return NS_OK; +} + +NS_IMETHODIMP +ServiceWorkerRegistrationInfo::GetScope(nsAString& aScope) +{ + AssertIsOnMainThread(); + CopyUTF8toUTF16(mScope, aScope); + return NS_OK; +} + +NS_IMETHODIMP +ServiceWorkerRegistrationInfo::GetScriptSpec(nsAString& aScriptSpec) +{ + AssertIsOnMainThread(); + RefPtr newest = Newest(); + if (newest) { + CopyUTF8toUTF16(newest->ScriptSpec(), aScriptSpec); + } + return NS_OK; +} + +NS_IMETHODIMP +ServiceWorkerRegistrationInfo::GetInstallingWorker(nsIServiceWorkerInfo **aResult) +{ + AssertIsOnMainThread(); + nsCOMPtr info = do_QueryInterface(mInstallingWorker); + info.forget(aResult); + return NS_OK; +} + +NS_IMETHODIMP +ServiceWorkerRegistrationInfo::GetWaitingWorker(nsIServiceWorkerInfo **aResult) +{ + AssertIsOnMainThread(); + nsCOMPtr info = do_QueryInterface(mWaitingWorker); + info.forget(aResult); + return NS_OK; +} + +NS_IMETHODIMP +ServiceWorkerRegistrationInfo::GetActiveWorker(nsIServiceWorkerInfo **aResult) +{ + AssertIsOnMainThread(); + nsCOMPtr info = do_QueryInterface(mActiveWorker); + info.forget(aResult); + return NS_OK; +} + +NS_IMETHODIMP +ServiceWorkerRegistrationInfo::GetWorkerByID(uint64_t aID, nsIServiceWorkerInfo **aResult) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aResult); + + RefPtr info = GetServiceWorkerInfoById(aID); + // It is ok to return null for a missing service worker info. + info.forget(aResult); + return NS_OK; +} + +NS_IMETHODIMP +ServiceWorkerRegistrationInfo::AddListener( + nsIServiceWorkerRegistrationInfoListener *aListener) +{ + AssertIsOnMainThread(); + + if (!aListener || mListeners.Contains(aListener)) { + return NS_ERROR_INVALID_ARG; + } + + mListeners.AppendElement(aListener); + + return NS_OK; +} + +NS_IMETHODIMP +ServiceWorkerRegistrationInfo::RemoveListener( + nsIServiceWorkerRegistrationInfoListener *aListener) +{ + AssertIsOnMainThread(); + + if (!aListener || !mListeners.Contains(aListener)) { + return NS_ERROR_INVALID_ARG; + } + + mListeners.RemoveElement(aListener); + + return NS_OK; +} + +already_AddRefed +ServiceWorkerRegistrationInfo::GetServiceWorkerInfoById(uint64_t aId) +{ + RefPtr serviceWorker; + if (mInstallingWorker && mInstallingWorker->ID() == aId) { + serviceWorker = mInstallingWorker; + } else if (mWaitingWorker && mWaitingWorker->ID() == aId) { + serviceWorker = mWaitingWorker; + } else if (mActiveWorker && mActiveWorker->ID() == aId) { + serviceWorker = mActiveWorker; + } + + return serviceWorker.forget(); +} + +void +ServiceWorkerRegistrationInfo::TryToActivateAsync() +{ + nsCOMPtr r = + NS_NewRunnableMethod(this, + &ServiceWorkerRegistrationInfo::TryToActivate); + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r)); +} + +/* + * TryToActivate should not be called directly, use TryToACtivateAsync instead. + */ +void +ServiceWorkerRegistrationInfo::TryToActivate() +{ + if (!IsControllingDocuments() || + // Waiting worker will be removed if the registration is removed + (mWaitingWorker && mWaitingWorker->SkipWaitingFlag())) { + Activate(); + } +} + +void +ServiceWorkerRegistrationInfo::PurgeActiveWorker() +{ + RefPtr exitingWorker = mActiveWorker.forget(); + if (!exitingWorker) + return; + + // FIXME(jaoo): Bug 1170543 - Wait for exitingWorker to finish and terminate it. + exitingWorker->UpdateState(ServiceWorkerState::Redundant); + nsresult rv = serviceWorkerScriptCache::PurgeCache(mPrincipal, + exitingWorker->CacheName()); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to purge the activating cache."); + } + RefPtr swm = ServiceWorkerManager::GetInstance(); + swm->InvalidateServiceWorkerRegistrationWorker(this, WhichServiceWorker::ACTIVE_WORKER); +} + +void +ServiceWorkerRegistrationInfo::Activate() +{ + RefPtr activatingWorker = mWaitingWorker; + if (!activatingWorker) { + return; + } + + PurgeActiveWorker(); + + RefPtr swm = ServiceWorkerManager::GetInstance(); + swm->InvalidateServiceWorkerRegistrationWorker(this, WhichServiceWorker::WAITING_WORKER); + + mActiveWorker = activatingWorker.forget(); + mWaitingWorker = nullptr; + mActiveWorker->UpdateState(ServiceWorkerState::Activating); + NotifyListenersOnChange(); + + // FIXME(nsm): Unlink appcache if there is one. + + swm->CheckPendingReadyPromises(); + + // "Queue a task to fire a simple event named controllerchange..." + nsCOMPtr controllerChangeRunnable = + NS_NewRunnableMethodWithArg>( + swm, &ServiceWorkerManager::FireControllerChange, this); + NS_DispatchToMainThread(controllerChangeRunnable); + + nsCOMPtr failRunnable = + NS_NewRunnableMethodWithArg(this, + &ServiceWorkerRegistrationInfo::FinishActivate, + false /* success */); + + nsMainThreadPtrHandle handle( + new nsMainThreadPtrHolder(this)); + RefPtr callback = new ContinueActivateRunnable(handle); + + ServiceWorkerPrivate* workerPrivate = mActiveWorker->WorkerPrivate(); + MOZ_ASSERT(workerPrivate); + nsresult rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("activate"), + callback, failRunnable); + if (NS_WARN_IF(NS_FAILED(rv))) { + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(failRunnable)); + return; + } +} + +void +ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess) +{ + if (mPendingUninstall || !mActiveWorker || + mActiveWorker->State() != ServiceWorkerState::Activating) { + return; + } + + // Activation never fails, so aSuccess is ignored. + mActiveWorker->UpdateState(ServiceWorkerState::Activated); + RefPtr swm = ServiceWorkerManager::GetInstance(); + swm->StoreRegistration(mPrincipal, this); +} + +void +ServiceWorkerRegistrationInfo::RefreshLastUpdateCheckTime() +{ + AssertIsOnMainThread(); + mLastUpdateCheckTime = PR_IntervalNow() / PR_MSEC_PER_SEC; +} + +bool +ServiceWorkerRegistrationInfo::IsLastUpdateCheckTimeOverOneDay() const +{ + AssertIsOnMainThread(); + + // For testing. + if (Preferences::GetBool("dom.serviceWorkers.testUpdateOverOneDay")) { + return true; + } + + const uint64_t kSecondsPerDay = 86400; + const uint64_t now = PR_IntervalNow() / PR_MSEC_PER_SEC; + + if ((mLastUpdateCheckTime != 0) && + (now - mLastUpdateCheckTime > kSecondsPerDay)) { + return true; + } + return false; +} + +void +ServiceWorkerRegistrationInfo::NotifyListenersOnChange() +{ + nsTArray> listeners(mListeners); + for (size_t index = 0; index < listeners.Length(); ++index) { + listeners[index]->OnChange(); + } +} + +void +ServiceWorkerRegistrationInfo::MaybeScheduleTimeCheckAndUpdate() +{ + AssertIsOnMainThread(); + + RefPtr swm = ServiceWorkerManager::GetInstance(); + if (!swm) { + // shutting down, do nothing + return; + } + + if (mUpdateState == NoUpdate) { + mUpdateState = NeedTimeCheckAndUpdate; + } + + swm->ScheduleUpdateTimer(mPrincipal, mScope); +} + +void +ServiceWorkerRegistrationInfo::MaybeScheduleUpdate() +{ + AssertIsOnMainThread(); + + RefPtr swm = ServiceWorkerManager::GetInstance(); + if (!swm) { + // shutting down, do nothing + return; + } + + mUpdateState = NeedUpdate; + + swm->ScheduleUpdateTimer(mPrincipal, mScope); +} + +bool +ServiceWorkerRegistrationInfo::CheckAndClearIfUpdateNeeded() +{ + AssertIsOnMainThread(); + + bool result = mUpdateState == NeedUpdate || + (mUpdateState == NeedTimeCheckAndUpdate && + IsLastUpdateCheckTimeOverOneDay()); + + mUpdateState = NoUpdate; + + return result; +} + +END_WORKERS_NAMESPACE diff --git a/dom/workers/ServiceWorkerRegistrationInfo.h b/dom/workers/ServiceWorkerRegistrationInfo.h new file mode 100644 index 000000000000..169af898d400 --- /dev/null +++ b/dom/workers/ServiceWorkerRegistrationInfo.h @@ -0,0 +1,132 @@ +/* -*- 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_workers_serviceworkerregistrationinfo_h +#define mozilla_dom_workers_serviceworkerregistrationinfo_h + +#include "mozilla/dom/workers/ServiceWorkerInfo.h" + +namespace mozilla { +namespace dom { +namespace workers { + +class ServiceWorkerRegistrationInfo final + : public nsIServiceWorkerRegistrationInfo +{ + uint32_t mControlledDocumentsCounter; + + enum + { + NoUpdate, + NeedTimeCheckAndUpdate, + NeedUpdate + } mUpdateState; + + uint64_t mLastUpdateCheckTime; + + virtual ~ServiceWorkerRegistrationInfo(); + +public: + NS_DECL_ISUPPORTS + NS_DECL_NSISERVICEWORKERREGISTRATIONINFO + + nsCString mScope; + + nsCOMPtr mPrincipal; + + RefPtr mActiveWorker; + RefPtr mWaitingWorker; + RefPtr mInstallingWorker; + + nsTArray> mListeners; + + // When unregister() is called on a registration, it is not immediately + // removed since documents may be controlled. It is marked as + // pendingUninstall and when all controlling documents go away, removed. + bool mPendingUninstall; + + ServiceWorkerRegistrationInfo(const nsACString& aScope, + nsIPrincipal* aPrincipal); + + already_AddRefed + Newest() const + { + RefPtr newest; + if (mInstallingWorker) { + newest = mInstallingWorker; + } else if (mWaitingWorker) { + newest = mWaitingWorker; + } else { + newest = mActiveWorker; + } + + return newest.forget(); + } + + already_AddRefed + GetServiceWorkerInfoById(uint64_t aId); + + void + StartControllingADocument() + { + ++mControlledDocumentsCounter; + } + + void + StopControllingADocument() + { + MOZ_ASSERT(mControlledDocumentsCounter); + --mControlledDocumentsCounter; + } + + bool + IsControllingDocuments() const + { + return mActiveWorker && mControlledDocumentsCounter; + } + + void + Clear(); + + void + PurgeActiveWorker(); + + void + TryToActivateAsync(); + + void + TryToActivate(); + + void + Activate(); + + void + FinishActivate(bool aSuccess); + + void + RefreshLastUpdateCheckTime(); + + bool + IsLastUpdateCheckTimeOverOneDay() const; + + void + NotifyListenersOnChange(); + + void + MaybeScheduleTimeCheckAndUpdate(); + + void + MaybeScheduleUpdate(); + + bool + CheckAndClearIfUpdateNeeded(); +}; + +} // namespace workers +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_workers_serviceworkerregistrationinfo_h diff --git a/dom/workers/moz.build b/dom/workers/moz.build index bafc71b52ffc..6bf7f824b794 100644 --- a/dom/workers/moz.build +++ b/dom/workers/moz.build @@ -23,7 +23,9 @@ EXPORTS.mozilla.dom += [ EXPORTS.mozilla.dom.workers += [ 'RuntimeService.h', + 'ServiceWorkerInfo.h', 'ServiceWorkerManager.h', + 'ServiceWorkerRegistrationInfo.h', 'WorkerDebuggerManager.h', 'Workers.h', ] @@ -66,6 +68,7 @@ UNIFIED_SOURCES += [ 'ServiceWorkerClients.cpp', 'ServiceWorkerContainer.cpp', 'ServiceWorkerEvents.cpp', + 'ServiceWorkerInfo.cpp', 'ServiceWorkerJob.cpp', 'ServiceWorkerJobQueue.cpp', 'ServiceWorkerManager.cpp', @@ -77,6 +80,7 @@ UNIFIED_SOURCES += [ 'ServiceWorkerRegisterJob.cpp', 'ServiceWorkerRegistrar.cpp', 'ServiceWorkerRegistration.cpp', + 'ServiceWorkerRegistrationInfo.cpp', 'ServiceWorkerScriptCache.cpp', 'ServiceWorkerUnregisterJob.cpp', 'ServiceWorkerUpdateJob.cpp',