diff options
Diffstat (limited to 'dom/workers/loader/CacheLoadHandler.h')
-rw-r--r-- | dom/workers/loader/CacheLoadHandler.h | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/dom/workers/loader/CacheLoadHandler.h b/dom/workers/loader/CacheLoadHandler.h new file mode 100644 index 0000000000..88744c4b93 --- /dev/null +++ b/dom/workers/loader/CacheLoadHandler.h @@ -0,0 +1,219 @@ +/* -*- 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_CacheLoadHandler_h__ +#define mozilla_dom_workers_CacheLoadHandler_h__ + +#include "nsIContentPolicy.h" +#include "nsIInputStreamPump.h" +#include "nsIStreamLoader.h" +#include "nsStringFwd.h" +#include "nsStreamUtils.h" + +#include "mozilla/StaticPrefs_browser.h" +#include "mozilla/dom/CacheBinding.h" +#include "mozilla/dom/ChannelInfo.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/PromiseNativeHandler.h" +#include "mozilla/dom/ScriptLoadHandler.h" +#include "mozilla/dom/cache/Cache.h" +#include "mozilla/dom/cache/CacheStorage.h" +#include "mozilla/dom/WorkerCommon.h" +#include "mozilla/dom/WorkerRef.h" + +#include "mozilla/dom/workerinternals/ScriptLoader.h" + +using mozilla::dom::cache::Cache; +using mozilla::dom::cache::CacheStorage; +using mozilla::ipc::PrincipalInfo; + +namespace mozilla::dom { + +class WorkerLoadContext; + +namespace workerinternals::loader { + +/* + * [DOMDOC] CacheLoadHandler for Workers + * + * A LoadHandler is a ScriptLoader helper class that reacts to an + * nsIStreamLoader's events for loading JS scripts. It is primarily responsible + * for decoding the stream into UTF8 or UTF16. Additionally, it takes care of + * any work that needs to follow the completion of a stream. Every LoadHandler + * also manages additional tasks for the type of load that it is doing. + * + * CacheLoadHandler is a specialized LoadHandler used by ServiceWorkers to + * implement the installation model used by ServiceWorkers to support running + * offline. When a ServiceWorker is installed, its main script is evaluated and + * all script resources that are loaded are saved. The spec does not specify the + * storage mechanism for this, but we chose to reuse the Cache API[1] mechanism + * that we expose to content to also store the script and its dependencies. We + * store the script resources in a special chrome namespace CacheStorage that is + * not visible to content. Each distinct ServiceWorker installation gets its own + * Cache keyed by a randomly-generated UUID. + * + * In terms of specification, this class implements step 4 of + * https://w3c.github.io/ServiceWorker/#importscripts + * + * Relationship to NetworkLoadHandler + * + * During ServiceWorker installation, the CacheLoadHandler falls back on the + * NetworkLoadHandler by calling `mLoader->LoadScript(...)`. If a script has not + * been seen before, then we will fall back on loading from the network. + * However, if the ServiceWorker is already installed, an error will be + * generated and the ServiceWorker will fail to load, per spec. + * + * CacheLoadHandler does not persist some pieces of information, such as the + * sourceMapUrl. Also, the DOM Cache API storage does not yet support alternate + * data streams for JS Bytecode or WASM caching; this is tracked by Bug 1336199. + * + * [1]: https://developer.mozilla.org/en-US/docs/Web/API/caches + * + */ + +class CacheLoadHandler final : public PromiseNativeHandler, + public nsIStreamLoaderObserver { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSISTREAMLOADEROBSERVER + + CacheLoadHandler(ThreadSafeWorkerRef* aWorkerRef, + ThreadSafeRequestHandle* aRequestHandle, + bool aIsWorkerScript, WorkerScriptLoader* aLoader); + + void Fail(nsresult aRv); + + void Load(Cache* aCache); + + virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue, + ErrorResult& aRv) override; + + virtual void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue, + ErrorResult& aRv) override; + + private: + ~CacheLoadHandler() { AssertIsOnMainThread(); } + + nsresult DataReceivedFromCache(const uint8_t* aString, uint32_t aStringLen, + const mozilla::dom::ChannelInfo& aChannelInfo, + UniquePtr<PrincipalInfo> aPrincipalInfo, + const nsACString& aCSPHeaderValue, + const nsACString& aCSPReportOnlyHeaderValue, + const nsACString& aReferrerPolicyHeaderValue); + void DataReceived(); + + RefPtr<ThreadSafeRequestHandle> mRequestHandle; + const RefPtr<WorkerScriptLoader> mLoader; + RefPtr<ThreadSafeWorkerRef> mWorkerRef; + const bool mIsWorkerScript; + bool mFailed; + const ServiceWorkerState mState; + nsCOMPtr<nsIInputStreamPump> mPump; + nsCOMPtr<nsIURI> mBaseURI; + mozilla::dom::ChannelInfo mChannelInfo; + UniquePtr<PrincipalInfo> mPrincipalInfo; + UniquePtr<ScriptDecoder> mDecoder; + nsCString mCSPHeaderValue; + nsCString mCSPReportOnlyHeaderValue; + nsCString mReferrerPolicyHeaderValue; + nsCOMPtr<nsIEventTarget> mMainThreadEventTarget; +}; + +/* + * CacheCreator + * + * The CacheCreator is responsible for maintaining a CacheStorage for the + * purposes of caching ServiceWorkers (see comment on CacheLoadHandler). In + * addition, it tracks all CacheLoadHandlers and is used for cleanup once + * loading has finished. + * + */ + +class CacheCreator final : public PromiseNativeHandler { + public: + NS_DECL_ISUPPORTS + + explicit CacheCreator(WorkerPrivate* aWorkerPrivate); + + void AddLoader(MovingNotNull<RefPtr<CacheLoadHandler>> aLoader) { + AssertIsOnMainThread(); + MOZ_ASSERT(!mCacheStorage); + mLoaders.AppendElement(std::move(aLoader)); + } + + virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue, + ErrorResult& aRv) override; + + virtual void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue, + ErrorResult& aRv) override; + + // Try to load from cache with aPrincipal used for cache access. + nsresult Load(nsIPrincipal* aPrincipal); + + Cache* Cache_() const { + AssertIsOnMainThread(); + MOZ_ASSERT(mCache); + return mCache; + } + + nsIGlobalObject* Global() const { + AssertIsOnMainThread(); + MOZ_ASSERT(mSandboxGlobalObject); + return mSandboxGlobalObject; + } + + void DeleteCache(nsresult aReason); + + private: + ~CacheCreator() = default; + + nsresult CreateCacheStorage(nsIPrincipal* aPrincipal); + + void FailLoaders(nsresult aRv); + + RefPtr<Cache> mCache; + RefPtr<CacheStorage> mCacheStorage; + nsCOMPtr<nsIGlobalObject> mSandboxGlobalObject; + nsTArray<NotNull<RefPtr<CacheLoadHandler>>> mLoaders; + + nsString mCacheName; + OriginAttributes mOriginAttributes; +}; + +/* + * CachePromiseHandler + * + * This promise handler is used to track if a ServiceWorker has been written to + * Cache. It is responsible for tracking the state of the ServiceWorker being + * cached. It also handles cancelling caching of a ServiceWorker if loading is + * interrupted. It is initialized by the NetworkLoadHandler as part of the first + * load of a ServiceWorker. + * + */ +class CachePromiseHandler final : public PromiseNativeHandler { + public: + NS_DECL_ISUPPORTS + + CachePromiseHandler(WorkerScriptLoader* aLoader, + ThreadSafeRequestHandle* aRequestHandle); + + virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue, + ErrorResult& aRv) override; + + virtual void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue, + ErrorResult& aRv) override; + + private: + ~CachePromiseHandler() { AssertIsOnMainThread(); } + + RefPtr<WorkerScriptLoader> mLoader; + RefPtr<ThreadSafeRequestHandle> mRequestHandle; +}; + +} // namespace workerinternals::loader +} // namespace mozilla::dom + +#endif /* mozilla_dom_workers_CacheLoadHandler_h__ */ |