diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /dom/workers/ServiceWorkerRegistrationInfo.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/workers/ServiceWorkerRegistrationInfo.cpp')
-rw-r--r-- | dom/workers/ServiceWorkerRegistrationInfo.cpp | 546 |
1 files changed, 546 insertions, 0 deletions
diff --git a/dom/workers/ServiceWorkerRegistrationInfo.cpp b/dom/workers/ServiceWorkerRegistrationInfo.cpp new file mode 100644 index 000000000..26ad74bda --- /dev/null +++ b/dom/workers/ServiceWorkerRegistrationInfo.cpp @@ -0,0 +1,546 @@ +/* -*- 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 + +namespace { + +class ContinueActivateRunnable final : public LifeCycleEventCallback +{ + nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration; + bool mSuccess; + +public: + explicit ContinueActivateRunnable(const nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration) + : mRegistration(aRegistration) + , mSuccess(false) + { + AssertIsOnMainThread(); + } + + void + SetResult(bool aResult) override + { + mSuccess = aResult; + } + + NS_IMETHOD + Run() override + { + AssertIsOnMainThread(); + mRegistration->FinishActivate(mSuccess); + mRegistration = nullptr; + return NS_OK; + } +}; + +} // anonymous namespace + +void +ServiceWorkerRegistrationInfo::Clear() +{ + if (mEvaluatingWorker) { + mEvaluatingWorker = nullptr; + } + + 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); + mWaitingWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo(); + mWaitingWorker = nullptr; + } + + if (mActiveWorker) { + mActiveWorker->UpdateState(ServiceWorkerState::Redundant); + mActiveWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo(); + mActiveWorker = nullptr; + } + + NotifyListenersOnChange(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<ServiceWorkerInfo> newest = Newest(); + if (newest) { + CopyUTF8toUTF16(newest->ScriptSpec(), aScriptSpec); + } + return NS_OK; +} + +NS_IMETHODIMP +ServiceWorkerRegistrationInfo::GetInstallingWorker(nsIServiceWorkerInfo **aResult) +{ + AssertIsOnMainThread(); + nsCOMPtr<nsIServiceWorkerInfo> info = do_QueryInterface(mInstallingWorker); + info.forget(aResult); + return NS_OK; +} + +NS_IMETHODIMP +ServiceWorkerRegistrationInfo::GetWaitingWorker(nsIServiceWorkerInfo **aResult) +{ + AssertIsOnMainThread(); + nsCOMPtr<nsIServiceWorkerInfo> info = do_QueryInterface(mWaitingWorker); + info.forget(aResult); + return NS_OK; +} + +NS_IMETHODIMP +ServiceWorkerRegistrationInfo::GetActiveWorker(nsIServiceWorkerInfo **aResult) +{ + AssertIsOnMainThread(); + nsCOMPtr<nsIServiceWorkerInfo> info = do_QueryInterface(mActiveWorker); + info.forget(aResult); + return NS_OK; +} + +NS_IMETHODIMP +ServiceWorkerRegistrationInfo::GetWorkerByID(uint64_t aID, nsIServiceWorkerInfo **aResult) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aResult); + + RefPtr<ServiceWorkerInfo> 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<ServiceWorkerInfo> +ServiceWorkerRegistrationInfo::GetServiceWorkerInfoById(uint64_t aId) +{ + AssertIsOnMainThread(); + + RefPtr<ServiceWorkerInfo> serviceWorker; + if (mEvaluatingWorker && mEvaluatingWorker->ID() == aId) { + serviceWorker = mEvaluatingWorker; + } else 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() +{ + MOZ_ALWAYS_SUCCEEDS( + NS_DispatchToMainThread(NewRunnableMethod(this, + &ServiceWorkerRegistrationInfo::TryToActivate))); +} + +/* + * TryToActivate should not be called directly, use TryToActivateAsync instead. + */ +void +ServiceWorkerRegistrationInfo::TryToActivate() +{ + AssertIsOnMainThread(); + bool controlling = IsControllingDocuments(); + bool skipWaiting = mWaitingWorker && mWaitingWorker->SkipWaitingFlag(); + bool idle = IsIdle(); + if (idle && (!controlling || skipWaiting)) { + Activate(); + } +} + +void +ServiceWorkerRegistrationInfo::Activate() +{ + if (!mWaitingWorker) { + return; + } + + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + if (!swm) { + // browser shutdown began during async activation step + return; + } + + TransitionWaitingToActive(); + + // FIXME(nsm): Unlink appcache if there is one. + + swm->CheckPendingReadyPromises(); + + // "Queue a task to fire a simple event named controllerchange..." + nsCOMPtr<nsIRunnable> controllerChangeRunnable = + NewRunnableMethod<RefPtr<ServiceWorkerRegistrationInfo>>( + swm, &ServiceWorkerManager::FireControllerChange, this); + NS_DispatchToMainThread(controllerChangeRunnable); + + nsCOMPtr<nsIRunnable> failRunnable = + NewRunnableMethod<bool>(this, + &ServiceWorkerRegistrationInfo::FinishActivate, + false /* success */); + + nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> handle( + new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(this)); + RefPtr<LifeCycleEventCallback> 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<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + if (!swm) { + // browser shutdown started during async activation completion step + return; + } + 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 ((now - mLastUpdateCheckTime) > kSecondsPerDay) { + return true; + } + return false; +} + +void +ServiceWorkerRegistrationInfo::NotifyListenersOnChange(WhichServiceWorker aChangedWorkers) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aChangedWorkers & (WhichServiceWorker::INSTALLING_WORKER | + WhichServiceWorker::WAITING_WORKER | + WhichServiceWorker::ACTIVE_WORKER)); + + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + if (!swm) { + // browser shutdown started + return; + } + + swm->InvalidateServiceWorkerRegistrationWorker(this, aChangedWorkers); + + nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> listeners(mListeners); + for (size_t index = 0; index < listeners.Length(); ++index) { + listeners[index]->OnChange(); + } +} + +void +ServiceWorkerRegistrationInfo::MaybeScheduleTimeCheckAndUpdate() +{ + AssertIsOnMainThread(); + + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + if (!swm) { + // shutting down, do nothing + return; + } + + if (mUpdateState == NoUpdate) { + mUpdateState = NeedTimeCheckAndUpdate; + } + + swm->ScheduleUpdateTimer(mPrincipal, mScope); +} + +void +ServiceWorkerRegistrationInfo::MaybeScheduleUpdate() +{ + AssertIsOnMainThread(); + + RefPtr<ServiceWorkerManager> 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; +} + +ServiceWorkerInfo* +ServiceWorkerRegistrationInfo::GetEvaluating() const +{ + AssertIsOnMainThread(); + return mEvaluatingWorker; +} + +ServiceWorkerInfo* +ServiceWorkerRegistrationInfo::GetInstalling() const +{ + AssertIsOnMainThread(); + return mInstallingWorker; +} + +ServiceWorkerInfo* +ServiceWorkerRegistrationInfo::GetWaiting() const +{ + AssertIsOnMainThread(); + return mWaitingWorker; +} + +ServiceWorkerInfo* +ServiceWorkerRegistrationInfo::GetActive() const +{ + AssertIsOnMainThread(); + return mActiveWorker; +} + +void +ServiceWorkerRegistrationInfo::SetEvaluating(ServiceWorkerInfo* aServiceWorker) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aServiceWorker); + MOZ_ASSERT(!mEvaluatingWorker); + MOZ_ASSERT(!mInstallingWorker); + MOZ_ASSERT(mWaitingWorker != aServiceWorker); + MOZ_ASSERT(mActiveWorker != aServiceWorker); + + mEvaluatingWorker = aServiceWorker; +} + +void +ServiceWorkerRegistrationInfo::ClearEvaluating() +{ + AssertIsOnMainThread(); + + if (!mEvaluatingWorker) { + return; + } + + mEvaluatingWorker->UpdateState(ServiceWorkerState::Redundant); + mEvaluatingWorker = nullptr; +} + +void +ServiceWorkerRegistrationInfo::ClearInstalling() +{ + AssertIsOnMainThread(); + + if (!mInstallingWorker) { + return; + } + + mInstallingWorker->UpdateState(ServiceWorkerState::Redundant); + mInstallingWorker = nullptr; + NotifyListenersOnChange(WhichServiceWorker::INSTALLING_WORKER); +} + +void +ServiceWorkerRegistrationInfo::TransitionEvaluatingToInstalling() +{ + AssertIsOnMainThread(); + MOZ_ASSERT(mEvaluatingWorker); + MOZ_ASSERT(!mInstallingWorker); + + mInstallingWorker = mEvaluatingWorker.forget(); + mInstallingWorker->UpdateState(ServiceWorkerState::Installing); + NotifyListenersOnChange(WhichServiceWorker::INSTALLING_WORKER); +} + +void +ServiceWorkerRegistrationInfo::TransitionInstallingToWaiting() +{ + AssertIsOnMainThread(); + MOZ_ASSERT(mInstallingWorker); + + if (mWaitingWorker) { + MOZ_ASSERT(mInstallingWorker->CacheName() != mWaitingWorker->CacheName()); + mWaitingWorker->UpdateState(ServiceWorkerState::Redundant); + } + + mWaitingWorker = mInstallingWorker.forget(); + mWaitingWorker->UpdateState(ServiceWorkerState::Installed); + NotifyListenersOnChange(WhichServiceWorker::INSTALLING_WORKER | + WhichServiceWorker::WAITING_WORKER); + + RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); + if (!swm) { + // browser shutdown began + return; + } + swm->StoreRegistration(mPrincipal, this); +} + +void +ServiceWorkerRegistrationInfo::SetActive(ServiceWorkerInfo* aServiceWorker) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aServiceWorker); + + // TODO: Assert installing, waiting, and active are nullptr once the SWM + // moves to the parent process. After that happens this code will + // only run for browser initialization and not for cross-process + // overrides. + MOZ_ASSERT(mInstallingWorker != aServiceWorker); + MOZ_ASSERT(mWaitingWorker != aServiceWorker); + MOZ_ASSERT(mActiveWorker != aServiceWorker); + + if (mActiveWorker) { + MOZ_ASSERT(aServiceWorker->CacheName() != mActiveWorker->CacheName()); + mActiveWorker->UpdateState(ServiceWorkerState::Redundant); + } + + // The active worker is being overriden due to initial load or + // another process activating a worker. Move straight to the + // Activated state. + mActiveWorker = aServiceWorker; + mActiveWorker->SetActivateStateUncheckedWithoutEvent(ServiceWorkerState::Activated); + NotifyListenersOnChange(WhichServiceWorker::ACTIVE_WORKER); +} + +void +ServiceWorkerRegistrationInfo::TransitionWaitingToActive() +{ + AssertIsOnMainThread(); + MOZ_ASSERT(mWaitingWorker); + + if (mActiveWorker) { + MOZ_ASSERT(mWaitingWorker->CacheName() != mActiveWorker->CacheName()); + mActiveWorker->UpdateState(ServiceWorkerState::Redundant); + } + + // We are transitioning from waiting to active normally, so go to + // the activating state. + mActiveWorker = mWaitingWorker.forget(); + mActiveWorker->UpdateState(ServiceWorkerState::Activating); + NotifyListenersOnChange(WhichServiceWorker::WAITING_WORKER | + WhichServiceWorker::ACTIVE_WORKER); +} + +bool +ServiceWorkerRegistrationInfo::IsIdle() const +{ + return !mActiveWorker || mActiveWorker->WorkerPrivate()->IsIdle(); +} + +END_WORKERS_NAMESPACE |