CacheLoadHandler.h (7876B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_dom_workers_CacheLoadHandler_h__ 8 #define mozilla_dom_workers_CacheLoadHandler_h__ 9 10 #include "mozilla/StaticPrefs_browser.h" 11 #include "mozilla/dom/CacheBinding.h" 12 #include "mozilla/dom/ChannelInfo.h" 13 #include "mozilla/dom/Promise.h" 14 #include "mozilla/dom/PromiseNativeHandler.h" 15 #include "mozilla/dom/ScriptLoadHandler.h" 16 #include "mozilla/dom/WorkerCommon.h" 17 #include "mozilla/dom/WorkerRef.h" 18 #include "mozilla/dom/cache/Cache.h" 19 #include "mozilla/dom/cache/CacheStorage.h" 20 #include "mozilla/dom/workerinternals/ScriptLoader.h" 21 #include "nsIContentPolicy.h" 22 #include "nsIInputStreamPump.h" 23 #include "nsIStreamLoader.h" 24 #include "nsStreamUtils.h" 25 #include "nsStringFwd.h" 26 27 using mozilla::dom::cache::Cache; 28 using mozilla::dom::cache::CacheStorage; 29 using mozilla::ipc::PrincipalInfo; 30 31 namespace mozilla::dom { 32 33 class WorkerLoadContext; 34 35 namespace workerinternals::loader { 36 37 /* 38 * [DOMDOC] CacheLoadHandler for Workers 39 * 40 * A LoadHandler is a ScriptLoader helper class that reacts to an 41 * nsIStreamLoader's events for loading JS scripts. It is primarily responsible 42 * for decoding the stream into UTF8 or UTF16. Additionally, it takes care of 43 * any work that needs to follow the completion of a stream. Every LoadHandler 44 * also manages additional tasks for the type of load that it is doing. 45 * 46 * CacheLoadHandler is a specialized LoadHandler used by ServiceWorkers to 47 * implement the installation model used by ServiceWorkers to support running 48 * offline. When a ServiceWorker is installed, its main script is evaluated and 49 * all script resources that are loaded are saved. The spec does not specify the 50 * storage mechanism for this, but we chose to reuse the Cache API[1] mechanism 51 * that we expose to content to also store the script and its dependencies. We 52 * store the script resources in a special chrome namespace CacheStorage that is 53 * not visible to content. Each distinct ServiceWorker installation gets its own 54 * Cache keyed by a randomly-generated UUID. 55 * 56 * In terms of specification, this class implements step 4 of 57 * https://w3c.github.io/ServiceWorker/#importscripts 58 * 59 * Relationship to NetworkLoadHandler 60 * 61 * During ServiceWorker installation, the CacheLoadHandler falls back on the 62 * NetworkLoadHandler by calling `mLoader->LoadScript(...)`. If a script has not 63 * been seen before, then we will fall back on loading from the network. 64 * However, if the ServiceWorker is already installed, an error will be 65 * generated and the ServiceWorker will fail to load, per spec. 66 * 67 * CacheLoadHandler does not persist some pieces of information, such as the 68 * sourceMapUrl. Also, the DOM Cache API storage does not yet support alternate 69 * data streams for JS Bytecode or WASM caching; this is tracked by Bug 1336199. 70 * 71 * [1]: https://developer.mozilla.org/en-US/docs/Web/API/caches 72 * 73 */ 74 75 class CacheLoadHandler final : public PromiseNativeHandler, 76 public nsIStreamLoaderObserver { 77 public: 78 NS_DECL_ISUPPORTS 79 NS_DECL_NSISTREAMLOADEROBSERVER 80 81 CacheLoadHandler(ThreadSafeWorkerRef* aWorkerRef, 82 ThreadSafeRequestHandle* aRequestHandle, 83 bool aIsWorkerScript, 84 bool aOnlyExistingCachedResourcesAllowed, 85 WorkerScriptLoader* aLoader); 86 87 void Fail(nsresult aRv); 88 89 void Load(Cache* aCache); 90 91 virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue, 92 ErrorResult& aRv) override; 93 94 virtual void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue, 95 ErrorResult& aRv) override; 96 97 private: 98 ~CacheLoadHandler() { AssertIsOnMainThread(); } 99 100 nsresult DataReceivedFromCache(const uint8_t* aString, uint32_t aStringLen, 101 const mozilla::dom::ChannelInfo& aChannelInfo, 102 UniquePtr<PrincipalInfo> aPrincipalInfo, 103 const nsACString& aCSPHeaderValue, 104 const nsACString& aCSPReportOnlyHeaderValue, 105 const nsACString& aReferrerPolicyHeaderValue); 106 nsresult DataReceived(); 107 108 RefPtr<ThreadSafeRequestHandle> mRequestHandle; 109 const RefPtr<WorkerScriptLoader> mLoader; 110 RefPtr<ThreadSafeWorkerRef> mWorkerRef; 111 const bool mIsWorkerScript; 112 bool mFailed; 113 bool mOnlyExistingCachedResourcesAllowed; 114 nsCOMPtr<nsIInputStreamPump> mPump; 115 nsCOMPtr<nsIURI> mBaseURI; 116 mozilla::dom::ChannelInfo mChannelInfo; 117 UniquePtr<PrincipalInfo> mPrincipalInfo; 118 UniquePtr<ScriptDecoder> mDecoder; 119 nsCString mCSPHeaderValue; 120 nsCString mCSPReportOnlyHeaderValue; 121 nsCString mReferrerPolicyHeaderValue; 122 nsCOMPtr<nsISerialEventTarget> mMainThreadEventTarget; 123 }; 124 125 /* 126 * CacheCreator 127 * 128 * The CacheCreator is responsible for maintaining a CacheStorage for the 129 * purposes of caching ServiceWorkers (see comment on CacheLoadHandler). In 130 * addition, it tracks all CacheLoadHandlers and is used for cleanup once 131 * loading has finished. 132 * 133 */ 134 135 class CacheCreator final : public PromiseNativeHandler { 136 public: 137 NS_DECL_ISUPPORTS 138 139 explicit CacheCreator(WorkerPrivate* aWorkerPrivate); 140 141 void AddLoader(MovingNotNull<RefPtr<CacheLoadHandler>> aLoader) { 142 AssertIsOnMainThread(); 143 MOZ_ASSERT(!mCacheStorage); 144 mLoaders.AppendElement(std::move(aLoader)); 145 } 146 147 virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue, 148 ErrorResult& aRv) override; 149 150 virtual void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue, 151 ErrorResult& aRv) override; 152 153 // Try to load from cache with aPrincipal used for cache access. 154 nsresult Load(nsIPrincipal* aPrincipal); 155 156 Cache* Cache_() const { 157 AssertIsOnMainThread(); 158 MOZ_ASSERT(mCache); 159 return mCache; 160 } 161 162 nsIGlobalObject* Global() const { 163 AssertIsOnMainThread(); 164 MOZ_ASSERT(mSandboxGlobalObject); 165 return mSandboxGlobalObject; 166 } 167 168 void DeleteCache(nsresult aReason); 169 170 private: 171 ~CacheCreator() = default; 172 173 nsresult CreateCacheStorage(nsIPrincipal* aPrincipal); 174 175 void FailLoaders(nsresult aRv); 176 177 RefPtr<Cache> mCache; 178 RefPtr<CacheStorage> mCacheStorage; 179 nsCOMPtr<nsIGlobalObject> mSandboxGlobalObject; 180 nsTArray<NotNull<RefPtr<CacheLoadHandler>>> mLoaders; 181 182 nsString mCacheName; 183 OriginAttributes mOriginAttributes; 184 }; 185 186 /* 187 * CachePromiseHandler 188 * 189 * This promise handler is used to track if a ServiceWorker has been written to 190 * Cache. It is responsible for tracking the state of the ServiceWorker being 191 * cached. It also handles cancelling caching of a ServiceWorker if loading is 192 * interrupted. It is initialized by the NetworkLoadHandler as part of the first 193 * load of a ServiceWorker. 194 * 195 */ 196 class CachePromiseHandler final : public PromiseNativeHandler { 197 public: 198 NS_DECL_ISUPPORTS 199 200 CachePromiseHandler(WorkerScriptLoader* aLoader, 201 ThreadSafeRequestHandle* aRequestHandle); 202 203 virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue, 204 ErrorResult& aRv) override; 205 206 virtual void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue, 207 ErrorResult& aRv) override; 208 209 private: 210 ~CachePromiseHandler() { AssertIsOnMainThread(); } 211 212 RefPtr<WorkerScriptLoader> mLoader; 213 RefPtr<ThreadSafeRequestHandle> mRequestHandle; 214 }; 215 216 } // namespace workerinternals::loader 217 } // namespace mozilla::dom 218 219 #endif /* mozilla_dom_workers_CacheLoadHandler_h__ */