summaryrefslogtreecommitdiffstats
path: root/dom/workers/loader/WorkerLoadContext.h
blob: 97362f2871ef75069f056192e6d8357a32e11cef (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
/* -*- 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_WorkerLoadContext_h__
#define mozilla_dom_workers_WorkerLoadContext_h__

#include "nsIChannel.h"
#include "nsIInputStream.h"
#include "nsIRequest.h"
#include "mozilla/CORSMode.h"
#include "mozilla/dom/Promise.h"
#include "js/loader/ScriptKind.h"
#include "js/loader/ScriptLoadRequest.h"
#include "js/loader/LoadContextBase.h"

class nsIReferrerInfo;
class nsIURI;

namespace mozilla::dom {

class ClientInfo;
class WorkerPrivate;

namespace workerinternals::loader {
class CacheCreator;
class ScriptLoaderRunnable;
class WorkerScriptLoader;
}  // namespace workerinternals::loader

/*
 * WorkerLoadContext (for all workers)
 *
 * LoadContexts augment the loading of a ScriptLoadRequest. They
 * describe how a ScriptLoadRequests loading and evaluation needs to be
 * augmented, based on the information provided by the loading context. The
 * WorkerLoadContext has the following generic fields applied to all worker
 * ScriptLoadRequests (and primarily used for error handling):
 *
 *    * mMutedErrorFlag
 *        Set when we finish loading a script, and used to determine whether a
 *        given error is thrown or muted.
 *    * mLoadResult
 *        In order to report errors correctly in the worker thread, we need to
 *        move them from the main thread to the worker. This field records the
 *        load error, for throwing when we return to the worker thread.
 *    * mKind
 *        See documentation of WorkerLoadContext::Kind.
 *    * mClientInfo
 *        A snapshot of a global living in the system (see documentation for
 *        ClientInfo). In worker loading, this field is important for CSP
 *        information and knowing what to intercept for Service Worker
 *        interception.
 *    * mChannel
 *        The channel used by this request for it's load. Used for cancellation,
 *        in order to cancel the stream.
 *
 * The rest of the fields on this class focus on enabling the ServiceWorker
 * usecase, in particular -- using the Cache API to store the worker so that
 * in the case of (for example) a page refresh, the service worker itself is
 * persisted so that it can do other work. For more details see the
 * CacheLoadHandler.h file.
 *
 */

class WorkerLoadContext : public JS::loader::LoadContextBase {
 public:
  /* Worker Load Context Kinds
   *
   * A script that is loaded and run as a worker can be one of several species.
   * Each may have slightly different behavior, but they fall into roughly two
   * categories: the Main Worker Script (the script that triggers the first
   * load) and scripts that are attached to this main worker script.
   *
   * In the specification, the Main Worker Script is referred to as the "top
   * level script" and is defined here:
   * https://html.spec.whatwg.org/multipage/webappapis.html#fetching-scripts-is-top-level
   */

  enum Kind {
    // Indicates that the is-top-level bit is true. This may be a Classic script
    // or a Module script.
    MainScript,
    // We are importing a script from the worker via ImportScript. This may only
    // be a Classic script.
    ImportScript,
    // We are importing a script from the worker via a Static Import. This may
    // only
    // be a Module script.
    StaticImport,
    DynamicImport,
    // We have an attached debugger, and these should be treated specially and
    // not like a main script (regardless of their type). This is not part of
    // the specification.
    DebuggerScript
  };

  WorkerLoadContext(Kind aKind, const Maybe<ClientInfo>& aClientInfo,
                    workerinternals::loader::WorkerScriptLoader* aScriptLoader,
                    bool aOnlyExistingCachedResourcesAllowed);

  // Used to detect if the `is top-level` bit is set on a given module.
  bool IsTopLevel() {
    return mRequest->IsTopLevel() && (mKind == Kind::MainScript);
  };

  static Kind GetKind(bool isMainScript, bool isDebuggerScript) {
    if (isDebuggerScript) {
      return Kind::DebuggerScript;
    }
    if (isMainScript) {
      return Kind::MainScript;
    }
    return Kind::ImportScript;
  };

  /* These fields are used by all workers */
  Maybe<bool> mMutedErrorFlag;
  nsresult mLoadResult = NS_ERROR_NOT_INITIALIZED;
  bool mLoadingFinished = false;
  bool mIsTopLevel = true;
  Kind mKind;
  Maybe<ClientInfo> mClientInfo;
  nsCOMPtr<nsIChannel> mChannel;
  RefPtr<workerinternals::loader::WorkerScriptLoader> mScriptLoader;

  /* These fields are only used by service workers */
  /* TODO: Split out a ServiceWorkerLoadContext */
  // This full URL string is populated only if this object is used in a
  // ServiceWorker.
  nsString mFullURL;

  // This promise is set only when the script is for a ServiceWorker but
  // it's not in the cache yet. The promise is resolved when the full body is
  // stored into the cache.  mCachePromise will be set to nullptr after
  // resolution.
  RefPtr<Promise> mCachePromise;

  // The reader stream the cache entry should be filled from, for those cases
  // when we're going to have an mCachePromise.
  nsCOMPtr<nsIInputStream> mCacheReadStream;

  enum CacheStatus {
    // By default a normal script is just loaded from the network. But for
    // ServiceWorkers, we have to check if the cache contains the script and
    // load it from the cache.
    Uncached,

    WritingToCache,

    ReadingFromCache,

    // This script has been loaded from the ServiceWorker cache.
    Cached,

    // This script must be stored in the ServiceWorker cache.
    ToBeCached,

    // Something went wrong or the worker went away.
    Cancel
  };

  CacheStatus mCacheStatus = Uncached;

  // If the requested script is not currently in the cache, should we initiate
  // a request to fetch and cache it?  Only ServiceWorkers that are being
  // installed are allowed to go to the network (and then cache the result).
  bool mOnlyExistingCachedResourcesAllowed = false;

  bool IsAwaitingPromise() const { return bool(mCachePromise); }
};

class ThreadSafeRequestHandle final {
 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ThreadSafeRequestHandle)

  ThreadSafeRequestHandle(JS::loader::ScriptLoadRequest* aRequest,
                          nsISerialEventTarget* aSyncTarget);

  JS::loader::ScriptLoadRequest* GetRequest() const { return mRequest; }

  WorkerLoadContext* GetContext() { return mRequest->GetWorkerLoadContext(); }

  bool IsEmpty() { return !mRequest; }

  // Runnable controls
  nsresult OnStreamComplete(nsresult aStatus);

  void LoadingFinished(nsresult aRv);

  void MaybeExecuteFinishedScripts();

  bool IsCancelled();

  bool Finished() {
    return GetContext()->mLoadingFinished && !GetContext()->IsAwaitingPromise();
  }

  nsresult GetCancelResult();

  already_AddRefed<JS::loader::ScriptLoadRequest> ReleaseRequest();

  workerinternals::loader::CacheCreator* GetCacheCreator();

  RefPtr<workerinternals::loader::ScriptLoaderRunnable> mRunnable;

  bool mExecutionScheduled = false;

 private:
  ~ThreadSafeRequestHandle();

  RefPtr<JS::loader::ScriptLoadRequest> mRequest;
  nsCOMPtr<nsISerialEventTarget> mOwningEventTarget;
};

}  // namespace mozilla::dom
#endif /* mozilla_dom_workers_WorkerLoadContext_h__ */