summaryrefslogtreecommitdiffstats
path: root/dom/workers/loader/CacheLoadHandler.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/workers/loader/CacheLoadHandler.h')
-rw-r--r--dom/workers/loader/CacheLoadHandler.h221
1 files changed, 221 insertions, 0 deletions
diff --git a/dom/workers/loader/CacheLoadHandler.h b/dom/workers/loader/CacheLoadHandler.h
new file mode 100644
index 0000000000..b1e164b79e
--- /dev/null
+++ b/dom/workers/loader/CacheLoadHandler.h
@@ -0,0 +1,221 @@
+/* -*- 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,
+ bool aOnlyExistingCachedResourcesAllowed,
+ 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;
+ bool mOnlyExistingCachedResourcesAllowed;
+ nsCOMPtr<nsIInputStreamPump> mPump;
+ nsCOMPtr<nsIURI> mBaseURI;
+ mozilla::dom::ChannelInfo mChannelInfo;
+ UniquePtr<PrincipalInfo> mPrincipalInfo;
+ UniquePtr<ScriptDecoder> mDecoder;
+ nsCString mCSPHeaderValue;
+ nsCString mCSPReportOnlyHeaderValue;
+ nsCString mReferrerPolicyHeaderValue;
+ nsCOMPtr<nsISerialEventTarget> 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__ */