summaryrefslogtreecommitdiffstats
path: root/dom/workers/loader/CacheLoadHandler.h
blob: b1e164b79e9520163422fd9b09324695fb2aadf6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
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__ */