/* 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/dom/BindingUtils.h" #include "mozilla/dom/Promise.h" #include "nsClassHashtable.h" #include "nsDataHashtable.h" #include "nsRefPtrHashtable.h" #include "nsTArrayForwardDeclare.h" #include "nsTWeakRef.h" namespace mozilla { namespace dom { namespace workers { class ServiceWorker; /* * 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 { const nsCString mScriptSpec; public: bool IsValid() const { return !mScriptSpec.IsVoid(); } const nsCString& GetScriptSpec() const { MOZ_ASSERT(IsValid()); return mScriptSpec; } ServiceWorkerInfo() { } explicit ServiceWorkerInfo(const nsACString& aScriptSpec) : mScriptSpec(aScriptSpec) { } }; class ServiceWorkerRegistration { private: ~ServiceWorkerRegistration() {} public: NS_INLINE_DECL_REFCOUNTING(ServiceWorkerRegistration) nsCString mScope; // The scriptURL for the registration. This may be completely different from // the URLs of the following three workers. nsCString mScriptSpec; ServiceWorkerInfo mCurrentWorker; ServiceWorkerInfo mWaitingWorker; ServiceWorkerInfo mInstallingWorker; bool mHasUpdatePromise; void AddUpdatePromiseObserver(Promise* aPromise) { // FIXME(nsm): Same bug, different patch. } bool HasUpdatePromise() { return mHasUpdatePromise; } // 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; explicit ServiceWorkerRegistration(const nsACString& aScope) : mScope(aScope), mHasUpdatePromise(false), mPendingUninstall(false) { } ServiceWorkerInfo Newest() const { if (mInstallingWorker.IsValid()) { return mInstallingWorker; } else if (mWaitingWorker.IsValid()) { return mWaitingWorker; } else { return mCurrentWorker; } } }; #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 RegisterRunnable; public: NS_DECL_ISUPPORTS NS_DECL_NSISERVICEWORKERMANAGER NS_DECLARE_STATIC_IID_ACCESSOR(NS_SERVICEWORKERMANAGER_IMPL_IID) static ServiceWorkerManager* FactoryCreate() { 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 { // Scope to registration. nsRefPtrHashtable mServiceWorkerRegistrations; ServiceWorkerDomainInfo() { } already_AddRefed GetRegistration(const nsCString& aScope) const { nsRefPtr reg; mServiceWorkerRegistrations.Get(aScope, getter_AddRefs(reg)); return reg.forget(); } ServiceWorkerRegistration* CreateNewRegistration(const nsCString& aScope) { ServiceWorkerRegistration* registration = new ServiceWorkerRegistration(aScope); // From now on ownership of registration is with // mServiceWorkerRegistrations. mServiceWorkerRegistrations.Put(aScope, registration); return registration; } }; nsClassHashtable mDomainMap; // FIXME(nsm): What do we do if a page calls for register("/foo_worker.js", { scope: "/*" // }) and then another page calls register("/bar_worker.js", { scope: "/*" }) // while the install is in progress. The async install steps for register // bar_worker.js could finish before foo_worker.js, but bar_worker still has // to be the winning controller. // FIXME(nsm): Move this into per domain? static already_AddRefed GetInstance(); private: ServiceWorkerManager(); ~ServiceWorkerManager(); NS_IMETHOD Update(ServiceWorkerRegistration* aRegistration, nsPIDOMWindow* aWindow); NS_IMETHODIMP CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow, const nsACString& aScriptSpec, const nsACString& aScope, ServiceWorker** aServiceWorker); static PLDHashOperator CleanupServiceWorkerInformation(const nsACString& aDomain, ServiceWorkerDomainInfo* aDomainInfo, void *aUnused); }; NS_DEFINE_STATIC_IID_ACCESSOR(ServiceWorkerManager, NS_SERVICEWORKERMANAGER_IMPL_IID); } // namespace workers } // namespace dom } // namespace mozilla #endif // mozilla_dom_workers_serviceworkermanager_h