mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-05 08:35:26 +00:00
3e3b6fb8c0
--HG-- extra : transplant_source : %B3%AA%CF%C5%05%409%D9%15Ly%FA%FF%E3%FA%5E%9B%3F%9F.
509 lines
14 KiB
C++
509 lines
14 KiB
C++
/* 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_serviceworkermanager_h
|
|
#define mozilla_dom_workers_serviceworkermanager_h
|
|
|
|
#include "nsIServiceWorkerManager.h"
|
|
#include "nsCOMPtr.h"
|
|
|
|
#include "mozilla/Attributes.h"
|
|
#include "mozilla/LinkedList.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/TypedEnumBits.h"
|
|
#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 "nsClassHashtable.h"
|
|
#include "nsDataHashtable.h"
|
|
#include "nsRefPtrHashtable.h"
|
|
#include "nsTArrayForwardDeclare.h"
|
|
#include "nsTObserverArray.h"
|
|
|
|
class nsIScriptError;
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
class ServiceWorkerRegistration;
|
|
|
|
namespace workers {
|
|
|
|
class ServiceWorker;
|
|
class ServiceWorkerInfo;
|
|
|
|
class ServiceWorkerJobQueue;
|
|
|
|
class ServiceWorkerJob : public nsISupports
|
|
{
|
|
// The queue keeps the jobs alive, so they can hold a rawptr back to the
|
|
// queue.
|
|
ServiceWorkerJobQueue* mQueue;
|
|
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
|
|
virtual void Start() = 0;
|
|
|
|
protected:
|
|
explicit ServiceWorkerJob(ServiceWorkerJobQueue* aQueue)
|
|
: mQueue(aQueue)
|
|
{
|
|
}
|
|
|
|
virtual ~ServiceWorkerJob()
|
|
{ }
|
|
|
|
void
|
|
Done(nsresult aStatus);
|
|
};
|
|
|
|
class ServiceWorkerJobQueue MOZ_FINAL
|
|
{
|
|
friend class ServiceWorkerJob;
|
|
|
|
nsTArray<nsRefPtr<ServiceWorkerJob>> mJobs;
|
|
|
|
public:
|
|
~ServiceWorkerJobQueue()
|
|
{
|
|
if (!mJobs.IsEmpty()) {
|
|
NS_WARNING("Pending/running jobs still around on shutdown!");
|
|
}
|
|
}
|
|
|
|
void
|
|
Append(ServiceWorkerJob* aJob)
|
|
{
|
|
MOZ_ASSERT(aJob);
|
|
MOZ_ASSERT(!mJobs.Contains(aJob));
|
|
bool wasEmpty = mJobs.IsEmpty();
|
|
mJobs.AppendElement(aJob);
|
|
if (wasEmpty) {
|
|
aJob->Start();
|
|
}
|
|
}
|
|
|
|
// Only used by HandleError, keep it that way!
|
|
ServiceWorkerJob*
|
|
Peek()
|
|
{
|
|
MOZ_ASSERT(!mJobs.IsEmpty());
|
|
return mJobs[0];
|
|
}
|
|
|
|
private:
|
|
void
|
|
Pop()
|
|
{
|
|
MOZ_ASSERT(!mJobs.IsEmpty());
|
|
mJobs.RemoveElementAt(0);
|
|
if (!mJobs.IsEmpty()) {
|
|
mJobs[0]->Start();
|
|
}
|
|
}
|
|
|
|
void
|
|
Done(ServiceWorkerJob* aJob)
|
|
{
|
|
MOZ_ASSERT(!mJobs.IsEmpty());
|
|
MOZ_ASSERT(mJobs[0] == aJob);
|
|
Pop();
|
|
}
|
|
};
|
|
|
|
// Needs to inherit from nsISupports because NS_ProxyRelease() does not support
|
|
// non-ISupports classes.
|
|
class ServiceWorkerRegistrationInfo MOZ_FINAL : public nsISupports
|
|
{
|
|
uint32_t mControlledDocumentsCounter;
|
|
|
|
virtual ~ServiceWorkerRegistrationInfo();
|
|
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
|
|
nsCString mScope;
|
|
// The scriptURL for the registration. This may be completely different from
|
|
// the URLs of the following three workers.
|
|
nsCString mScriptSpec;
|
|
|
|
nsRefPtr<ServiceWorkerInfo> mActiveWorker;
|
|
nsRefPtr<ServiceWorkerInfo> mWaitingWorker;
|
|
nsRefPtr<ServiceWorkerInfo> mInstallingWorker;
|
|
|
|
// 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;
|
|
bool mWaitingToActivate;
|
|
|
|
explicit ServiceWorkerRegistrationInfo(const nsACString& aScope);
|
|
|
|
already_AddRefed<ServiceWorkerInfo>
|
|
Newest()
|
|
{
|
|
nsRefPtr<ServiceWorkerInfo> newest;
|
|
if (mInstallingWorker) {
|
|
newest = mInstallingWorker;
|
|
} else if (mWaitingWorker) {
|
|
newest = mWaitingWorker;
|
|
} else {
|
|
newest = mActiveWorker;
|
|
}
|
|
|
|
return newest.forget();
|
|
}
|
|
|
|
void
|
|
StartControllingADocument()
|
|
{
|
|
++mControlledDocumentsCounter;
|
|
}
|
|
|
|
void
|
|
StopControllingADocument()
|
|
{
|
|
--mControlledDocumentsCounter;
|
|
}
|
|
|
|
bool
|
|
IsControllingDocuments() const
|
|
{
|
|
return mActiveWorker && mControlledDocumentsCounter > 0;
|
|
}
|
|
|
|
void
|
|
Clear();
|
|
|
|
void
|
|
TryToActivate();
|
|
|
|
void
|
|
Activate();
|
|
|
|
void
|
|
FinishActivate(bool aSuccess);
|
|
|
|
void
|
|
QueueStateChangeEvent(ServiceWorkerInfo* aInfo,
|
|
ServiceWorkerState aState) const;
|
|
};
|
|
|
|
/*
|
|
* 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 MOZ_FINAL
|
|
{
|
|
private:
|
|
const ServiceWorkerRegistrationInfo* mRegistration;
|
|
nsCString mScriptSpec;
|
|
ServiceWorkerState mState;
|
|
|
|
~ServiceWorkerInfo()
|
|
{ }
|
|
|
|
public:
|
|
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerInfo)
|
|
|
|
const nsCString&
|
|
ScriptSpec() const
|
|
{
|
|
return mScriptSpec;
|
|
}
|
|
|
|
explicit ServiceWorkerInfo(ServiceWorkerRegistrationInfo* aReg,
|
|
const nsACString& aScriptSpec)
|
|
: mRegistration(aReg)
|
|
, mScriptSpec(aScriptSpec)
|
|
, mState(ServiceWorkerState::EndGuard_)
|
|
{
|
|
MOZ_ASSERT(mRegistration);
|
|
}
|
|
|
|
ServiceWorkerState
|
|
State() const
|
|
{
|
|
return mState;
|
|
}
|
|
|
|
void
|
|
UpdateState(ServiceWorkerState aState)
|
|
{
|
|
#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
|
|
mState = aState;
|
|
mRegistration->QueueStateChangeEvent(this, mState);
|
|
}
|
|
};
|
|
|
|
#define NS_SERVICEWORKERMANAGER_IMPL_IID \
|
|
{ /* f4f8755a-69ca-46e8-a65d-775745535990 */ \
|
|
0xf4f8755a, \
|
|
0x69ca, \
|
|
0x46e8, \
|
|
{ 0xa6, 0x5d, 0x77, 0x57, 0x45, 0x53, 0x59, 0x90 } \
|
|
}
|
|
|
|
/*
|
|
* The ServiceWorkerManager is a per-process global that deals with the
|
|
* installation, querying and event dispatch of ServiceWorkers for all the
|
|
* origins in the process.
|
|
*/
|
|
class ServiceWorkerManager MOZ_FINAL : public nsIServiceWorkerManager
|
|
{
|
|
friend class ActivationRunnable;
|
|
friend class ServiceWorkerRegistrationInfo;
|
|
friend class ServiceWorkerRegisterJob;
|
|
friend class GetReadyPromiseRunnable;
|
|
friend class GetRegistrationsRunnable;
|
|
friend class GetRegistrationRunnable;
|
|
friend class QueueFireUpdateFoundRunnable;
|
|
friend class ServiceWorkerUnregisterJob;
|
|
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSISERVICEWORKERMANAGER
|
|
NS_DECLARE_STATIC_IID_ACCESSOR(NS_SERVICEWORKERMANAGER_IMPL_IID)
|
|
|
|
static ServiceWorkerManager* FactoryCreate()
|
|
{
|
|
AssertIsOnMainThread();
|
|
if (!Preferences::GetBool("dom.serviceWorkers.enabled")) {
|
|
return nullptr;
|
|
}
|
|
|
|
ServiceWorkerManager* res = new ServiceWorkerManager;
|
|
NS_ADDREF(res);
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* This struct is used for passive ServiceWorker management.
|
|
* Actively running ServiceWorkers use the SharedWorker infrastructure in
|
|
* RuntimeService for execution and lifetime management.
|
|
*/
|
|
struct ServiceWorkerDomainInfo
|
|
{
|
|
// Ordered list of scopes for glob matching.
|
|
// Each entry is an absolute URL representing the scope.
|
|
//
|
|
// An array is used for now since the number of controlled scopes per
|
|
// domain is expected to be relatively low. If that assumption was proved
|
|
// wrong this should be replaced with a better structure to avoid the
|
|
// memmoves associated with inserting stuff in the middle of the array.
|
|
nsTArray<nsCString> mOrderedScopes;
|
|
|
|
// Scope to registration.
|
|
nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistrationInfo> mServiceWorkerRegistrationInfos;
|
|
|
|
nsTObserverArray<ServiceWorkerRegistration*> mServiceWorkerRegistrations;
|
|
|
|
nsRefPtrHashtable<nsISupportsHashKey, ServiceWorkerRegistrationInfo> mControlledDocuments;
|
|
|
|
nsClassHashtable<nsCStringHashKey, ServiceWorkerJobQueue> mJobQueues;
|
|
|
|
nsDataHashtable<nsCStringHashKey, bool> mSetOfScopesBeingUpdated;
|
|
|
|
ServiceWorkerDomainInfo()
|
|
{ }
|
|
|
|
already_AddRefed<ServiceWorkerRegistrationInfo>
|
|
GetRegistration(const nsCString& aScope) const
|
|
{
|
|
nsRefPtr<ServiceWorkerRegistrationInfo> reg;
|
|
mServiceWorkerRegistrationInfos.Get(aScope, getter_AddRefs(reg));
|
|
return reg.forget();
|
|
}
|
|
|
|
ServiceWorkerRegistrationInfo*
|
|
CreateNewRegistration(const nsCString& aScope)
|
|
{
|
|
ServiceWorkerRegistrationInfo* registration =
|
|
new ServiceWorkerRegistrationInfo(aScope);
|
|
// From now on ownership of registration is with
|
|
// mServiceWorkerRegistrationInfos.
|
|
mServiceWorkerRegistrationInfos.Put(aScope, registration);
|
|
ServiceWorkerManager::AddScope(mOrderedScopes, aScope);
|
|
return registration;
|
|
}
|
|
|
|
void
|
|
RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
|
|
{
|
|
MOZ_ASSERT(mServiceWorkerRegistrationInfos.Contains(aRegistration->mScope));
|
|
ServiceWorkerManager::RemoveScope(mOrderedScopes, aRegistration->mScope);
|
|
mServiceWorkerRegistrationInfos.Remove(aRegistration->mScope);
|
|
}
|
|
|
|
ServiceWorkerJobQueue*
|
|
GetOrCreateJobQueue(const nsCString& aScope)
|
|
{
|
|
return mJobQueues.LookupOrAdd(aScope);
|
|
}
|
|
|
|
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerDomainInfo)
|
|
|
|
private:
|
|
~ServiceWorkerDomainInfo()
|
|
{ }
|
|
};
|
|
|
|
nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerDomainInfo> mDomainMap;
|
|
|
|
void
|
|
FinishFetch(ServiceWorkerRegistrationInfo* aRegistration);
|
|
|
|
// Returns true if the error was handled, false if normal worker error
|
|
// handling should continue.
|
|
bool
|
|
HandleError(JSContext* aCx,
|
|
const nsCString& aScope,
|
|
const nsString& aWorkerURL,
|
|
nsString aMessage,
|
|
nsString aFilename,
|
|
nsString aLine,
|
|
uint32_t aLineNumber,
|
|
uint32_t aColumnNumber,
|
|
uint32_t aFlags);
|
|
|
|
void
|
|
GetServicedClients(const nsCString& aScope,
|
|
nsTArray<uint64_t>* aControlledDocuments);
|
|
|
|
static already_AddRefed<ServiceWorkerManager>
|
|
GetInstance();
|
|
|
|
private:
|
|
ServiceWorkerManager();
|
|
~ServiceWorkerManager();
|
|
|
|
void
|
|
AbortCurrentUpdate(ServiceWorkerRegistrationInfo* aRegistration);
|
|
|
|
nsresult
|
|
Update(ServiceWorkerRegistrationInfo* aRegistration);
|
|
|
|
NS_IMETHOD
|
|
CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow,
|
|
const nsACString& aScriptSpec,
|
|
const nsACString& aScope,
|
|
ServiceWorker** aServiceWorker);
|
|
|
|
NS_IMETHOD
|
|
CreateServiceWorker(const nsACString& aScriptSpec,
|
|
const nsACString& aScope,
|
|
ServiceWorker** aServiceWorker);
|
|
|
|
static PLDHashOperator
|
|
CleanupServiceWorkerInformation(const nsACString& aDomain,
|
|
ServiceWorkerDomainInfo* aDomainInfo,
|
|
void *aUnused);
|
|
|
|
already_AddRefed<ServiceWorkerDomainInfo>
|
|
GetDomainInfo(nsIDocument* aDoc);
|
|
|
|
already_AddRefed<ServiceWorkerDomainInfo>
|
|
GetDomainInfo(nsIURI* aURI);
|
|
|
|
already_AddRefed<ServiceWorkerDomainInfo>
|
|
GetDomainInfo(const nsCString& aURL);
|
|
|
|
NS_IMETHODIMP
|
|
GetServiceWorkerForScope(nsIDOMWindow* aWindow,
|
|
const nsAString& aScope,
|
|
WhichServiceWorker aWhichWorker,
|
|
nsISupports** aServiceWorker);
|
|
|
|
void
|
|
InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
|
|
WhichServiceWorker aWhichOnes);
|
|
|
|
already_AddRefed<ServiceWorkerRegistrationInfo>
|
|
GetServiceWorkerRegistrationInfo(nsPIDOMWindow* aWindow);
|
|
|
|
already_AddRefed<ServiceWorkerRegistrationInfo>
|
|
GetServiceWorkerRegistrationInfo(nsIDocument* aDoc);
|
|
|
|
already_AddRefed<ServiceWorkerRegistrationInfo>
|
|
GetServiceWorkerRegistrationInfo(nsIURI* aURI);
|
|
|
|
static void
|
|
AddScope(nsTArray<nsCString>& aList, const nsACString& aScope);
|
|
|
|
static nsCString
|
|
FindScopeForPath(nsTArray<nsCString>& aList, const nsACString& aPath);
|
|
|
|
static void
|
|
RemoveScope(nsTArray<nsCString>& aList, const nsACString& aScope);
|
|
|
|
void
|
|
QueueFireEventOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration,
|
|
const nsAString& aName);
|
|
|
|
void
|
|
FireEventOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration,
|
|
const nsAString& aName);
|
|
|
|
void
|
|
FireUpdateFound(ServiceWorkerRegistrationInfo* aRegistration)
|
|
{
|
|
FireEventOnServiceWorkerRegistrations(aRegistration,
|
|
NS_LITERAL_STRING("updatefound"));
|
|
}
|
|
|
|
void
|
|
FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration);
|
|
|
|
void
|
|
StorePendingReadyPromise(nsPIDOMWindow* aWindow, nsIURI* aURI, Promise* aPromise);
|
|
|
|
void
|
|
CheckPendingReadyPromises();
|
|
|
|
bool
|
|
CheckReadyPromise(nsPIDOMWindow* aWindow, nsIURI* aURI, Promise* aPromise);
|
|
|
|
struct PendingReadyPromise
|
|
{
|
|
PendingReadyPromise(nsIURI* aURI, Promise* aPromise)
|
|
: mURI(aURI), mPromise(aPromise)
|
|
{ }
|
|
|
|
nsCOMPtr<nsIURI> mURI;
|
|
nsRefPtr<Promise> mPromise;
|
|
};
|
|
|
|
static PLDHashOperator
|
|
CheckPendingReadyPromisesEnumerator(nsISupports* aSupports,
|
|
nsAutoPtr<PendingReadyPromise>& aData,
|
|
void* aUnused);
|
|
|
|
nsClassHashtable<nsISupportsHashKey, PendingReadyPromise> mPendingReadyPromises;
|
|
};
|
|
|
|
NS_DEFINE_STATIC_IID_ACCESSOR(ServiceWorkerManager,
|
|
NS_SERVICEWORKERMANAGER_IMPL_IID);
|
|
|
|
} // namespace workers
|
|
} // namespace dom
|
|
} // namespace mozilla
|
|
|
|
#endif // mozilla_dom_workers_serviceworkermanager_h
|