Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/workers/ScriptLoader.cpp
Line
Count
Source (jump to first uncovered line)
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
#include "ScriptLoader.h"
8
9
#include "nsIChannel.h"
10
#include "nsIContentPolicy.h"
11
#include "nsIContentSecurityPolicy.h"
12
#include "nsIDocShell.h"
13
#include "nsIHttpChannel.h"
14
#include "nsIHttpChannelInternal.h"
15
#include "nsIInputStreamPump.h"
16
#include "nsIIOService.h"
17
#include "nsIOService.h"
18
#include "nsIProtocolHandler.h"
19
#include "nsIScriptError.h"
20
#include "nsIScriptSecurityManager.h"
21
#include "nsIStreamLoader.h"
22
#include "nsIStreamListenerTee.h"
23
#include "nsIThreadRetargetableRequest.h"
24
#include "nsIURI.h"
25
26
#include "jsapi.h"
27
#include "jsfriendapi.h"
28
#include "js/CompilationAndEvaluation.h"
29
#include "js/SourceBufferHolder.h"
30
#include "nsError.h"
31
#include "nsContentPolicyUtils.h"
32
#include "nsContentUtils.h"
33
#include "nsDocShellCID.h"
34
#include "nsISupportsPrimitives.h"
35
#include "nsNetUtil.h"
36
#include "nsIPipe.h"
37
#include "nsIOutputStream.h"
38
#include "nsPrintfCString.h"
39
#include "nsString.h"
40
#include "nsStreamUtils.h"
41
#include "nsTArray.h"
42
#include "nsThreadUtils.h"
43
#include "nsXPCOM.h"
44
#include "xpcpublic.h"
45
46
#include "mozilla/Assertions.h"
47
#include "mozilla/LoadContext.h"
48
#include "mozilla/Maybe.h"
49
#include "mozilla/ipc/BackgroundUtils.h"
50
#include "mozilla/dom/BlobURLProtocolHandler.h"
51
#include "mozilla/dom/CacheBinding.h"
52
#include "mozilla/dom/cache/CacheTypes.h"
53
#include "mozilla/dom/cache/Cache.h"
54
#include "mozilla/dom/cache/CacheStorage.h"
55
#include "mozilla/dom/ChannelInfo.h"
56
#include "mozilla/dom/ClientChannelHelper.h"
57
#include "mozilla/dom/ClientInfo.h"
58
#include "mozilla/dom/Exceptions.h"
59
#include "mozilla/dom/InternalResponse.h"
60
#include "mozilla/dom/nsCSPService.h"
61
#include "mozilla/dom/nsCSPUtils.h"
62
#include "mozilla/dom/PerformanceStorage.h"
63
#include "mozilla/dom/Promise.h"
64
#include "mozilla/dom/PromiseNativeHandler.h"
65
#include "mozilla/dom/Response.h"
66
#include "mozilla/dom/ScriptLoader.h"
67
#include "mozilla/dom/ScriptSettings.h"
68
#include "mozilla/dom/SRILogHelper.h"
69
#include "mozilla/dom/ServiceWorkerBinding.h"
70
#include "mozilla/UniquePtr.h"
71
#include "Principal.h"
72
#include "WorkerHolder.h"
73
#include "WorkerPrivate.h"
74
#include "WorkerRunnable.h"
75
#include "WorkerScope.h"
76
77
0
#define MAX_CONCURRENT_SCRIPTS 1000
78
79
using mozilla::dom::cache::Cache;
80
using mozilla::dom::cache::CacheStorage;
81
using mozilla::ipc::PrincipalInfo;
82
83
namespace mozilla {
84
namespace dom {
85
86
namespace {
87
88
nsIURI*
89
GetBaseURI(bool aIsMainScript, WorkerPrivate* aWorkerPrivate)
90
0
{
91
0
  MOZ_ASSERT(aWorkerPrivate);
92
0
  nsIURI* baseURI;
93
0
  WorkerPrivate* parentWorker = aWorkerPrivate->GetParent();
94
0
  if (aIsMainScript) {
95
0
    if (parentWorker) {
96
0
      baseURI = parentWorker->GetBaseURI();
97
0
      NS_ASSERTION(baseURI, "Should have been set already!");
98
0
    }
99
0
    else {
100
0
      // May be null.
101
0
      baseURI = aWorkerPrivate->GetBaseURI();
102
0
    }
103
0
  }
104
0
  else {
105
0
    baseURI = aWorkerPrivate->GetBaseURI();
106
0
    NS_ASSERTION(baseURI, "Should have been set already!");
107
0
  }
108
0
109
0
  return baseURI;
110
0
}
111
112
nsresult
113
ChannelFromScriptURL(nsIPrincipal* principal,
114
                     nsIURI* baseURI,
115
                     nsIDocument* parentDoc,
116
                     WorkerPrivate* aWorkerPrivate,
117
                     nsILoadGroup* loadGroup,
118
                     nsIIOService* ios,
119
                     nsIScriptSecurityManager* secMan,
120
                     const nsAString& aScriptURL,
121
                     const Maybe<ClientInfo>& aClientInfo,
122
                     const Maybe<ServiceWorkerDescriptor>& aController,
123
                     bool aIsMainScript,
124
                     WorkerScriptType aWorkerScriptType,
125
                     nsContentPolicyType aMainScriptContentPolicyType,
126
                     nsLoadFlags aLoadFlags,
127
                     bool aDefaultURIEncoding,
128
                     nsIChannel** aChannel)
129
0
{
130
0
  AssertIsOnMainThread();
131
0
132
0
  nsresult rv;
133
0
  nsCOMPtr<nsIURI> uri;
134
0
135
0
  if (aDefaultURIEncoding) {
136
0
    rv = NS_NewURI(getter_AddRefs(uri), aScriptURL, nullptr, baseURI);
137
0
  } else {
138
0
    rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
139
0
                                                   aScriptURL, parentDoc,
140
0
                                                   baseURI);
141
0
  }
142
0
143
0
  if (NS_FAILED(rv)) {
144
0
    return NS_ERROR_DOM_SYNTAX_ERR;
145
0
  }
146
0
147
0
  // If we have the document, use it. Unfortunately, for dedicated workers
148
0
  // 'parentDoc' ends up being the parent document, which is not the document
149
0
  // that we want to use. So make sure to avoid using 'parentDoc' in that
150
0
  // situation.
151
0
  if (parentDoc && parentDoc->NodePrincipal() != principal) {
152
0
    parentDoc = nullptr;
153
0
  }
154
0
155
0
  aLoadFlags |= nsIChannel::LOAD_CLASSIFY_URI;
156
0
  uint32_t secFlags = aIsMainScript ? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED
157
0
                                    : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
158
0
159
0
  bool inheritAttrs = nsContentUtils::ChannelShouldInheritPrincipal(
160
0
    principal, uri, true /* aInheritForAboutBlank */, false /* aForceInherit */);
161
0
162
0
  bool isData = false;
163
0
  rv = uri->SchemeIs("data", &isData);
164
0
  NS_ENSURE_SUCCESS(rv, rv);
165
0
166
0
  bool isURIUniqueOrigin = net::nsIOService::IsDataURIUniqueOpaqueOrigin() && isData;
167
0
  if (inheritAttrs && !isURIUniqueOrigin) {
168
0
    secFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
169
0
  }
170
0
171
0
  if (aWorkerScriptType == DebuggerScript) {
172
0
    // A DebuggerScript needs to be a local resource like chrome: or resource:
173
0
    bool isUIResource = false;
174
0
    rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_UI_RESOURCE,
175
0
                             &isUIResource);
176
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
177
0
      return rv;
178
0
    }
179
0
180
0
    if (!isUIResource) {
181
0
      return NS_ERROR_DOM_SECURITY_ERR;
182
0
    }
183
0
184
0
    secFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
185
0
  }
186
0
187
0
  // Note: this is for backwards compatibility and goes against spec.
188
0
  // We should find a better solution.
189
0
  if (aIsMainScript && isData) {
190
0
    secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL;
191
0
  }
192
0
193
0
  nsContentPolicyType contentPolicyType =
194
0
    aIsMainScript ? aMainScriptContentPolicyType
195
0
                  : nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS;
196
0
197
0
  // The main service worker script should never be loaded over the network
198
0
  // in this path.  It should always be offlined by ServiceWorkerScriptCache.
199
0
  // We assert here since this error should also be caught by the runtime
200
0
  // check in CacheScriptLoader.
201
0
  //
202
0
  // Note, if we ever allow service worker scripts to be loaded from network
203
0
  // here we need to configure the channel properly.  For example, it must
204
0
  // not allow redirects.
205
0
  MOZ_DIAGNOSTIC_ASSERT(contentPolicyType !=
206
0
                        nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER);
207
0
208
0
  nsCOMPtr<nsIChannel> channel;
209
0
  // If we have the document, use it. Unfortunately, for dedicated workers
210
0
  // 'parentDoc' ends up being the parent document, which is not the document
211
0
  // that we want to use. So make sure to avoid using 'parentDoc' in that
212
0
  // situation.
213
0
  if (parentDoc && parentDoc->NodePrincipal() == principal) {
214
0
    rv = NS_NewChannel(getter_AddRefs(channel),
215
0
                       uri,
216
0
                       parentDoc,
217
0
                       secFlags,
218
0
                       contentPolicyType,
219
0
                       nullptr, // aPerformanceStorage
220
0
                       loadGroup,
221
0
                       nullptr, // aCallbacks
222
0
                       aLoadFlags,
223
0
                       ios);
224
0
  } else {
225
0
    // We must have a loadGroup with a load context for the principal to
226
0
    // traverse the channel correctly.
227
0
    MOZ_ASSERT(loadGroup);
228
0
    MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadGroup, principal));
229
0
230
0
    RefPtr<PerformanceStorage> performanceStorage;
231
0
    if (aWorkerPrivate && !aIsMainScript) {
232
0
      performanceStorage = aWorkerPrivate->GetPerformanceStorage();
233
0
    }
234
0
235
0
    if (aClientInfo.isSome()) {
236
0
      rv = NS_NewChannel(getter_AddRefs(channel),
237
0
                         uri,
238
0
                         principal,
239
0
                         aClientInfo.ref(),
240
0
                         aController,
241
0
                         secFlags,
242
0
                         contentPolicyType,
243
0
                         performanceStorage,
244
0
                         loadGroup,
245
0
                         nullptr, // aCallbacks
246
0
                         aLoadFlags,
247
0
                         ios);
248
0
    } else {
249
0
      rv = NS_NewChannel(getter_AddRefs(channel),
250
0
                         uri,
251
0
                         principal,
252
0
                         secFlags,
253
0
                         contentPolicyType,
254
0
                         performanceStorage,
255
0
                         loadGroup,
256
0
                         nullptr, // aCallbacks
257
0
                         aLoadFlags,
258
0
                         ios);
259
0
    }
260
0
  }
261
0
262
0
  NS_ENSURE_SUCCESS(rv, rv);
263
0
264
0
  if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel)) {
265
0
    mozilla::net::ReferrerPolicy referrerPolicy = parentDoc ?
266
0
      parentDoc->GetReferrerPolicy() : mozilla::net::RP_Unset;
267
0
    rv = nsContentUtils::SetFetchReferrerURIWithPolicy(principal, parentDoc,
268
0
                                                       httpChannel, referrerPolicy);
269
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
270
0
      return rv;
271
0
    }
272
0
  }
273
0
274
0
  channel.forget(aChannel);
275
0
  return rv;
276
0
}
277
278
struct ScriptLoadInfo
279
{
280
  ScriptLoadInfo()
281
  : mScriptTextBuf(nullptr)
282
  , mScriptTextLength(0)
283
  , mLoadResult(NS_ERROR_NOT_INITIALIZED)
284
  , mLoadingFinished(false)
285
  , mExecutionScheduled(false)
286
  , mExecutionResult(false)
287
  , mCacheStatus(Uncached)
288
  , mLoadFlags(nsIRequest::LOAD_NORMAL)
289
0
  { }
290
291
  ~ScriptLoadInfo()
292
0
  {
293
0
    if (mScriptTextBuf) {
294
0
      js_free(mScriptTextBuf);
295
0
    }
296
0
  }
297
298
  nsString mURL;
299
300
  // This full URL string is populated only if this object is used in a
301
  // ServiceWorker.
302
  nsString mFullURL;
303
304
  // This promise is set only when the script is for a ServiceWorker but
305
  // it's not in the cache yet. The promise is resolved when the full body is
306
  // stored into the cache.  mCachePromise will be set to nullptr after
307
  // resolution.
308
  RefPtr<Promise> mCachePromise;
309
310
  // The reader stream the cache entry should be filled from, for those cases
311
  // when we're going to have an mCachePromise.
312
  nsCOMPtr<nsIInputStream> mCacheReadStream;
313
314
  nsCOMPtr<nsIChannel> mChannel;
315
  Maybe<ClientInfo> mReservedClientInfo;
316
  char16_t* mScriptTextBuf;
317
  size_t mScriptTextLength;
318
319
  nsresult mLoadResult;
320
  bool mLoadingFinished;
321
  bool mExecutionScheduled;
322
  bool mExecutionResult;
323
324
  enum CacheStatus {
325
    // By default a normal script is just loaded from the network. But for
326
    // ServiceWorkers, we have to check if the cache contains the script and
327
    // load it from the cache.
328
    Uncached,
329
330
    WritingToCache,
331
332
    ReadingFromCache,
333
334
    // This script has been loaded from the ServiceWorker cache.
335
    Cached,
336
337
    // This script must be stored in the ServiceWorker cache.
338
    ToBeCached,
339
340
    // Something went wrong or the worker went away.
341
    Cancel
342
  };
343
344
  CacheStatus mCacheStatus;
345
346
  nsLoadFlags mLoadFlags;
347
348
  Maybe<bool> mMutedErrorFlag;
349
350
  bool Finished() const
351
0
  {
352
0
    return mLoadingFinished && !mCachePromise && !mChannel;
353
0
  }
354
};
355
356
class ScriptLoaderRunnable;
357
358
class ScriptExecutorRunnable final : public MainThreadWorkerSyncRunnable
359
{
360
  ScriptLoaderRunnable& mScriptLoader;
361
  bool mIsWorkerScript;
362
  uint32_t mFirstIndex;
363
  uint32_t mLastIndex;
364
365
public:
366
  ScriptExecutorRunnable(ScriptLoaderRunnable& aScriptLoader,
367
                         nsIEventTarget* aSyncLoopTarget,
368
                         bool aIsWorkerScript,
369
                         uint32_t aFirstIndex,
370
                         uint32_t aLastIndex);
371
372
private:
373
  ~ScriptExecutorRunnable()
374
0
  { }
375
376
  virtual bool
377
  IsDebuggerRunnable() const override;
378
379
  virtual bool
380
  PreRun(WorkerPrivate* aWorkerPrivate) override;
381
382
  virtual bool
383
  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
384
385
  virtual void
386
  PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
387
          override;
388
389
  nsresult
390
  Cancel() override;
391
392
  void
393
  ShutdownScriptLoader(JSContext* aCx,
394
                       WorkerPrivate* aWorkerPrivate,
395
                       bool aResult,
396
                       bool aMutedError);
397
398
  void LogExceptionToConsole(JSContext* aCx,
399
                             WorkerPrivate* WorkerPrivate);
400
};
401
402
class CacheScriptLoader;
403
404
class CacheCreator final : public PromiseNativeHandler
405
{
406
public:
407
  NS_DECL_ISUPPORTS
408
409
  explicit CacheCreator(WorkerPrivate* aWorkerPrivate)
410
    : mCacheName(aWorkerPrivate->ServiceWorkerCacheName())
411
    , mOriginAttributes(aWorkerPrivate->GetOriginAttributes())
412
0
  {
413
0
    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
414
0
    AssertIsOnMainThread();
415
0
  }
416
417
  void
418
  AddLoader(CacheScriptLoader* aLoader)
419
0
  {
420
0
    AssertIsOnMainThread();
421
0
    MOZ_ASSERT(!mCacheStorage);
422
0
    mLoaders.AppendElement(aLoader);
423
0
  }
424
425
  virtual void
426
  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
427
428
  virtual void
429
  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
430
431
  // Try to load from cache with aPrincipal used for cache access.
432
  nsresult
433
  Load(nsIPrincipal* aPrincipal);
434
435
  Cache*
436
  Cache_() const
437
0
  {
438
0
    AssertIsOnMainThread();
439
0
    MOZ_ASSERT(mCache);
440
0
    return mCache;
441
0
  }
442
443
  nsIGlobalObject*
444
  Global() const
445
0
  {
446
0
    AssertIsOnMainThread();
447
0
    MOZ_ASSERT(mSandboxGlobalObject);
448
0
    return mSandboxGlobalObject;
449
0
  }
450
451
  void
452
  DeleteCache();
453
454
private:
455
  ~CacheCreator()
456
0
  {
457
0
  }
458
459
  nsresult
460
  CreateCacheStorage(nsIPrincipal* aPrincipal);
461
462
  void
463
  FailLoaders(nsresult aRv);
464
465
  RefPtr<Cache> mCache;
466
  RefPtr<CacheStorage> mCacheStorage;
467
  nsCOMPtr<nsIGlobalObject> mSandboxGlobalObject;
468
  nsTArray<RefPtr<CacheScriptLoader>> mLoaders;
469
470
  nsString mCacheName;
471
  OriginAttributes mOriginAttributes;
472
};
473
474
NS_IMPL_ISUPPORTS0(CacheCreator)
475
476
class CacheScriptLoader final : public PromiseNativeHandler
477
                              , public nsIStreamLoaderObserver
478
{
479
public:
480
  NS_DECL_ISUPPORTS
481
  NS_DECL_NSISTREAMLOADEROBSERVER
482
483
  CacheScriptLoader(WorkerPrivate* aWorkerPrivate, ScriptLoadInfo& aLoadInfo,
484
                    uint32_t aIndex, bool aIsWorkerScript,
485
                    ScriptLoaderRunnable* aRunnable)
486
    : mLoadInfo(aLoadInfo)
487
    , mIndex(aIndex)
488
    , mRunnable(aRunnable)
489
    , mIsWorkerScript(aIsWorkerScript)
490
    , mFailed(false)
491
    , mState(aWorkerPrivate->GetServiceWorkerDescriptor().State())
492
0
  {
493
0
    MOZ_ASSERT(aWorkerPrivate);
494
0
    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
495
0
    mMainThreadEventTarget = aWorkerPrivate->MainThreadEventTarget();
496
0
    MOZ_ASSERT(mMainThreadEventTarget);
497
0
    mBaseURI = GetBaseURI(mIsWorkerScript, aWorkerPrivate);
498
0
    AssertIsOnMainThread();
499
0
  }
500
501
  void
502
  Fail(nsresult aRv);
503
504
  void
505
  Load(Cache* aCache);
506
507
  virtual void
508
  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
509
510
  virtual void
511
  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
512
513
private:
514
  ~CacheScriptLoader()
515
0
  {
516
0
    AssertIsOnMainThread();
517
0
  }
518
519
  ScriptLoadInfo& mLoadInfo;
520
  uint32_t mIndex;
521
  RefPtr<ScriptLoaderRunnable> mRunnable;
522
  bool mIsWorkerScript;
523
  bool mFailed;
524
  const ServiceWorkerState mState;
525
  nsCOMPtr<nsIInputStreamPump> mPump;
526
  nsCOMPtr<nsIURI> mBaseURI;
527
  mozilla::dom::ChannelInfo mChannelInfo;
528
  UniquePtr<PrincipalInfo> mPrincipalInfo;
529
  nsCString mCSPHeaderValue;
530
  nsCString mCSPReportOnlyHeaderValue;
531
  nsCString mReferrerPolicyHeaderValue;
532
  nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
533
};
534
535
NS_IMPL_ISUPPORTS(CacheScriptLoader, nsIStreamLoaderObserver)
536
537
class CachePromiseHandler final : public PromiseNativeHandler
538
{
539
public:
540
  NS_DECL_ISUPPORTS
541
542
  CachePromiseHandler(ScriptLoaderRunnable* aRunnable,
543
                      ScriptLoadInfo& aLoadInfo,
544
                      uint32_t aIndex)
545
    : mRunnable(aRunnable)
546
    , mLoadInfo(aLoadInfo)
547
    , mIndex(aIndex)
548
0
  {
549
0
    AssertIsOnMainThread();
550
0
    MOZ_ASSERT(mRunnable);
551
0
  }
552
553
  virtual void
554
  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
555
556
  virtual void
557
  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
558
559
private:
560
  ~CachePromiseHandler()
561
0
  {
562
0
    AssertIsOnMainThread();
563
0
  }
564
565
  RefPtr<ScriptLoaderRunnable> mRunnable;
566
  ScriptLoadInfo& mLoadInfo;
567
  uint32_t mIndex;
568
};
569
570
NS_IMPL_ISUPPORTS0(CachePromiseHandler)
571
572
class LoaderListener final : public nsIStreamLoaderObserver
573
                           , public nsIRequestObserver
574
{
575
public:
576
  NS_DECL_ISUPPORTS
577
578
  LoaderListener(ScriptLoaderRunnable* aRunnable, uint32_t aIndex)
579
    : mRunnable(aRunnable)
580
    , mIndex(aIndex)
581
0
  {
582
0
    MOZ_ASSERT(mRunnable);
583
0
  }
584
585
  NS_IMETHOD
586
  OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
587
                   nsresult aStatus, uint32_t aStringLen,
588
                   const uint8_t* aString) override;
589
590
  NS_IMETHOD
591
  OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) override;
592
593
  NS_IMETHOD
594
  OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
595
                nsresult aStatusCode) override
596
0
  {
597
0
    // Nothing to do here!
598
0
    return NS_OK;
599
0
  }
600
601
private:
602
0
  ~LoaderListener() {}
603
604
  RefPtr<ScriptLoaderRunnable> mRunnable;
605
  uint32_t mIndex;
606
};
607
608
NS_IMPL_ISUPPORTS(LoaderListener, nsIStreamLoaderObserver, nsIRequestObserver)
609
610
class ScriptLoaderRunnable final : public nsIRunnable,
611
                                   public nsINamed
612
{
613
  friend class ScriptExecutorRunnable;
614
  friend class CachePromiseHandler;
615
  friend class CacheScriptLoader;
616
  friend class LoaderListener;
617
618
  WorkerPrivate* mWorkerPrivate;
619
  nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
620
  nsTArray<ScriptLoadInfo> mLoadInfos;
621
  RefPtr<CacheCreator> mCacheCreator;
622
  Maybe<ClientInfo> mClientInfo;
623
  Maybe<ServiceWorkerDescriptor> mController;
624
  bool mIsMainScript;
625
  WorkerScriptType mWorkerScriptType;
626
  bool mCanceledMainThread;
627
  ErrorResult& mRv;
628
629
public:
630
  NS_DECL_THREADSAFE_ISUPPORTS
631
632
  ScriptLoaderRunnable(WorkerPrivate* aWorkerPrivate,
633
                       nsIEventTarget* aSyncLoopTarget,
634
                       nsTArray<ScriptLoadInfo>& aLoadInfos,
635
                       const Maybe<ClientInfo>& aClientInfo,
636
                       const Maybe<ServiceWorkerDescriptor>& aController,
637
                       bool aIsMainScript,
638
                       WorkerScriptType aWorkerScriptType,
639
                       ErrorResult& aRv)
640
  : mWorkerPrivate(aWorkerPrivate), mSyncLoopTarget(aSyncLoopTarget),
641
    mClientInfo(aClientInfo), mController(aController),
642
    mIsMainScript(aIsMainScript), mWorkerScriptType(aWorkerScriptType),
643
    mCanceledMainThread(false), mRv(aRv)
644
0
  {
645
0
    aWorkerPrivate->AssertIsOnWorkerThread();
646
0
    MOZ_ASSERT(aSyncLoopTarget);
647
0
    MOZ_ASSERT_IF(aIsMainScript, aLoadInfos.Length() == 1);
648
0
649
0
    mLoadInfos.SwapElements(aLoadInfos);
650
0
  }
651
652
  void
653
  CancelMainThreadWithBindingAborted()
654
0
  {
655
0
    CancelMainThread(NS_BINDING_ABORTED);
656
0
  }
657
658
private:
659
  ~ScriptLoaderRunnable()
660
0
  { }
661
662
  NS_IMETHOD
663
  Run() override
664
0
  {
665
0
    AssertIsOnMainThread();
666
0
667
0
    nsresult rv = RunInternal();
668
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
669
0
      CancelMainThread(rv);
670
0
    }
671
0
672
0
    return NS_OK;
673
0
  }
674
675
  NS_IMETHOD
676
  GetName(nsACString& aName) override
677
0
  {
678
0
    aName.AssignLiteral("ScriptLoaderRunnable");
679
0
    return NS_OK;
680
0
  }
681
682
  void
683
  LoadingFinished(uint32_t aIndex, nsresult aRv)
684
0
  {
685
0
    AssertIsOnMainThread();
686
0
    MOZ_ASSERT(aIndex < mLoadInfos.Length());
687
0
    ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
688
0
689
0
    loadInfo.mLoadResult = aRv;
690
0
691
0
    MOZ_ASSERT(!loadInfo.mLoadingFinished);
692
0
    loadInfo.mLoadingFinished = true;
693
0
694
0
    if (IsMainWorkerScript() && NS_SUCCEEDED(aRv)) {
695
0
      MOZ_DIAGNOSTIC_ASSERT(mWorkerPrivate->PrincipalURIMatchesScriptURL());
696
0
    }
697
0
698
0
    MaybeExecuteFinishedScripts(aIndex);
699
0
  }
700
701
  void
702
  MaybeExecuteFinishedScripts(uint32_t aIndex)
703
0
  {
704
0
    AssertIsOnMainThread();
705
0
    MOZ_ASSERT(aIndex < mLoadInfos.Length());
706
0
    ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
707
0
708
0
    // We execute the last step if we don't have a pending operation with the
709
0
    // cache and the loading is completed.
710
0
    if (loadInfo.Finished()) {
711
0
      ExecuteFinishedScripts();
712
0
    }
713
0
  }
714
715
  nsresult
716
  OnStreamComplete(nsIStreamLoader* aLoader, uint32_t aIndex,
717
                   nsresult aStatus, uint32_t aStringLen,
718
                   const uint8_t* aString)
719
0
  {
720
0
    AssertIsOnMainThread();
721
0
    MOZ_ASSERT(aIndex < mLoadInfos.Length());
722
0
723
0
    nsresult rv = OnStreamCompleteInternal(aLoader, aStatus, aStringLen,
724
0
                                           aString, mLoadInfos[aIndex]);
725
0
    LoadingFinished(aIndex, rv);
726
0
    return NS_OK;
727
0
  }
728
729
  nsresult
730
  OnStartRequest(nsIRequest* aRequest, uint32_t aIndex)
731
0
  {
732
0
    AssertIsOnMainThread();
733
0
    MOZ_ASSERT(aIndex < mLoadInfos.Length());
734
0
735
0
    // If one load info cancels or hits an error, it can race with the start
736
0
    // callback coming from another load info.
737
0
    if (mCanceledMainThread || !mCacheCreator) {
738
0
      aRequest->Cancel(NS_ERROR_FAILURE);
739
0
      return NS_ERROR_FAILURE;
740
0
    }
741
0
742
0
    ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
743
0
744
0
    nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
745
0
746
0
    // Note that importScripts() can redirect.  In theory the main
747
0
    // script could also encounter an internal redirect, but currently
748
0
    // the assert does not allow that.
749
0
    MOZ_ASSERT_IF(mIsMainScript, channel == loadInfo.mChannel);
750
0
    loadInfo.mChannel = channel;
751
0
752
0
    // We synthesize the result code, but its never exposed to content.
753
0
    RefPtr<mozilla::dom::InternalResponse> ir =
754
0
      new mozilla::dom::InternalResponse(200, NS_LITERAL_CSTRING("OK"));
755
0
    ir->SetBody(loadInfo.mCacheReadStream, InternalResponse::UNKNOWN_BODY_SIZE);
756
0
757
0
    // Drop our reference to the stream now that we've passed it along, so it
758
0
    // doesn't hang around once the cache is done with it and keep data alive.
759
0
    loadInfo.mCacheReadStream = nullptr;
760
0
761
0
    // Set the channel info of the channel on the response so that it's
762
0
    // saved in the cache.
763
0
    ir->InitChannelInfo(channel);
764
0
765
0
    // Save the principal of the channel since its URI encodes the script URI
766
0
    // rather than the ServiceWorkerRegistrationInfo URI.
767
0
    nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
768
0
    NS_ASSERTION(ssm, "Should never be null!");
769
0
770
0
    nsCOMPtr<nsIPrincipal> channelPrincipal;
771
0
    nsresult rv = ssm->GetChannelResultPrincipal(channel, getter_AddRefs(channelPrincipal));
772
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
773
0
      channel->Cancel(rv);
774
0
      return rv;
775
0
    }
776
0
777
0
    UniquePtr<PrincipalInfo> principalInfo(new PrincipalInfo());
778
0
    rv = PrincipalToPrincipalInfo(channelPrincipal, principalInfo.get());
779
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
780
0
      channel->Cancel(rv);
781
0
      return rv;
782
0
    }
783
0
784
0
    ir->SetPrincipalInfo(std::move(principalInfo));
785
0
    ir->Headers()->FillResponseHeaders(loadInfo.mChannel);
786
0
787
0
    RefPtr<mozilla::dom::Response> response =
788
0
      new mozilla::dom::Response(mCacheCreator->Global(), ir, nullptr);
789
0
790
0
    mozilla::dom::RequestOrUSVString request;
791
0
792
0
    MOZ_ASSERT(!loadInfo.mFullURL.IsEmpty());
793
0
    request.SetAsUSVString().Rebind(loadInfo.mFullURL.Data(),
794
0
                                    loadInfo.mFullURL.Length());
795
0
796
0
    // This JSContext will not end up executing JS code because here there are
797
0
    // no ReadableStreams involved.
798
0
    AutoJSAPI jsapi;
799
0
    jsapi.Init();
800
0
801
0
    ErrorResult error;
802
0
    RefPtr<Promise> cachePromise =
803
0
      mCacheCreator->Cache_()->Put(jsapi.cx(), request, *response, error);
804
0
    if (NS_WARN_IF(error.Failed())) {
805
0
      nsresult rv = error.StealNSResult();
806
0
      channel->Cancel(rv);
807
0
      return rv;
808
0
    }
809
0
810
0
    RefPtr<CachePromiseHandler> promiseHandler =
811
0
      new CachePromiseHandler(this, loadInfo, aIndex);
812
0
    cachePromise->AppendNativeHandler(promiseHandler);
813
0
814
0
    loadInfo.mCachePromise.swap(cachePromise);
815
0
    loadInfo.mCacheStatus = ScriptLoadInfo::WritingToCache;
816
0
817
0
    return NS_OK;
818
0
  }
819
820
  bool
821
  IsMainWorkerScript() const
822
0
  {
823
0
    return mIsMainScript && mWorkerScriptType == WorkerScript;
824
0
  }
825
826
  bool
827
  IsDebuggerScript() const
828
0
  {
829
0
    return mWorkerScriptType == DebuggerScript;
830
0
  }
831
832
  void
833
  CancelMainThread(nsresult aCancelResult)
834
0
  {
835
0
    AssertIsOnMainThread();
836
0
837
0
    if (mCanceledMainThread) {
838
0
      return;
839
0
    }
840
0
841
0
    mCanceledMainThread = true;
842
0
843
0
    if (mCacheCreator) {
844
0
      MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
845
0
      DeleteCache();
846
0
    }
847
0
848
0
    // Cancel all the channels that were already opened.
849
0
    for (uint32_t index = 0; index < mLoadInfos.Length(); index++) {
850
0
      ScriptLoadInfo& loadInfo = mLoadInfos[index];
851
0
852
0
      // If promise or channel is non-null, their failures will lead to
853
0
      // LoadingFinished being called.
854
0
      bool callLoadingFinished = true;
855
0
856
0
      if (loadInfo.mCachePromise) {
857
0
        MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
858
0
        loadInfo.mCachePromise->MaybeReject(aCancelResult);
859
0
        loadInfo.mCachePromise = nullptr;
860
0
        callLoadingFinished = false;
861
0
      }
862
0
863
0
      if (loadInfo.mChannel) {
864
0
        if (NS_SUCCEEDED(loadInfo.mChannel->Cancel(aCancelResult))) {
865
0
          callLoadingFinished = false;
866
0
        } else {
867
0
          NS_WARNING("Failed to cancel channel!");
868
0
        }
869
0
      }
870
0
871
0
      if (callLoadingFinished && !loadInfo.Finished()) {
872
0
        LoadingFinished(index, aCancelResult);
873
0
      }
874
0
    }
875
0
876
0
    ExecuteFinishedScripts();
877
0
  }
878
879
  void
880
  DeleteCache()
881
0
  {
882
0
    AssertIsOnMainThread();
883
0
884
0
    if (!mCacheCreator) {
885
0
      return;
886
0
    }
887
0
888
0
    mCacheCreator->DeleteCache();
889
0
    mCacheCreator = nullptr;
890
0
  }
891
892
  nsresult
893
  RunInternal()
894
0
  {
895
0
    AssertIsOnMainThread();
896
0
897
0
    if (IsMainWorkerScript()) {
898
0
      mWorkerPrivate->SetLoadingWorkerScript(true);
899
0
    }
900
0
901
0
    if (!mWorkerPrivate->IsServiceWorker() || IsDebuggerScript()) {
902
0
      for (uint32_t index = 0, len = mLoadInfos.Length(); index < len;
903
0
           ++index) {
904
0
        nsresult rv = LoadScript(index);
905
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
906
0
          LoadingFinished(index, rv);
907
0
          return rv;
908
0
        }
909
0
      }
910
0
911
0
      return NS_OK;
912
0
    }
913
0
914
0
    MOZ_ASSERT(!mCacheCreator);
915
0
    mCacheCreator = new CacheCreator(mWorkerPrivate);
916
0
917
0
    for (uint32_t index = 0, len = mLoadInfos.Length(); index < len; ++index) {
918
0
      RefPtr<CacheScriptLoader> loader =
919
0
        new CacheScriptLoader(mWorkerPrivate, mLoadInfos[index], index,
920
0
                              IsMainWorkerScript(), this);
921
0
      mCacheCreator->AddLoader(loader);
922
0
    }
923
0
924
0
    // The worker may have a null principal on first load, but in that case its
925
0
    // parent definitely will have one.
926
0
    nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
927
0
    if (!principal) {
928
0
      WorkerPrivate* parentWorker = mWorkerPrivate->GetParent();
929
0
      MOZ_ASSERT(parentWorker, "Must have a parent!");
930
0
      principal = parentWorker->GetPrincipal();
931
0
    }
932
0
933
0
    nsresult rv = mCacheCreator->Load(principal);
934
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
935
0
      return rv;
936
0
    }
937
0
938
0
    return NS_OK;
939
0
  }
940
941
  nsresult
942
  LoadScript(uint32_t aIndex)
943
0
  {
944
0
    AssertIsOnMainThread();
945
0
    MOZ_ASSERT(aIndex < mLoadInfos.Length());
946
0
    MOZ_ASSERT_IF(IsMainWorkerScript(), mWorkerScriptType != DebuggerScript);
947
0
948
0
    WorkerPrivate* parentWorker = mWorkerPrivate->GetParent();
949
0
950
0
    // For JavaScript debugging, the devtools server must run on the same
951
0
    // thread as the debuggee, indicating the worker uses content principal.
952
0
    // However, in Bug 863246, web content will no longer be able to load
953
0
    // resource:// URIs by default, so we need system principal to load
954
0
    // debugger scripts.
955
0
    nsIPrincipal* principal = (mWorkerScriptType == DebuggerScript) ?
956
0
                              nsContentUtils::GetSystemPrincipal() :
957
0
                              mWorkerPrivate->GetPrincipal();
958
0
959
0
    nsCOMPtr<nsILoadGroup> loadGroup = mWorkerPrivate->GetLoadGroup();
960
0
    MOZ_DIAGNOSTIC_ASSERT(principal);
961
0
962
0
    NS_ENSURE_TRUE(NS_LoadGroupMatchesPrincipal(loadGroup, principal),
963
0
                   NS_ERROR_FAILURE);
964
0
965
0
    // Figure out our base URI.
966
0
    nsCOMPtr<nsIURI> baseURI = GetBaseURI(mIsMainScript, mWorkerPrivate);
967
0
968
0
    // May be null.
969
0
    nsCOMPtr<nsIDocument> parentDoc = mWorkerPrivate->GetDocument();
970
0
971
0
    nsCOMPtr<nsIChannel> channel;
972
0
    if (IsMainWorkerScript()) {
973
0
      // May be null.
974
0
      channel = mWorkerPrivate->ForgetWorkerChannel();
975
0
    }
976
0
977
0
    nsCOMPtr<nsIIOService> ios(do_GetIOService());
978
0
979
0
    nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
980
0
    NS_ASSERTION(secMan, "This should never be null!");
981
0
982
0
    ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
983
0
    nsresult& rv = loadInfo.mLoadResult;
984
0
985
0
    nsLoadFlags loadFlags = loadInfo.mLoadFlags;
986
0
987
0
    // Get the top-level worker.
988
0
    WorkerPrivate* topWorkerPrivate = mWorkerPrivate;
989
0
    WorkerPrivate* parent = topWorkerPrivate->GetParent();
990
0
    while (parent) {
991
0
      topWorkerPrivate = parent;
992
0
      parent = topWorkerPrivate->GetParent();
993
0
    }
994
0
995
0
    // If the top-level worker is a dedicated worker and has a window, and the
996
0
    // window has a docshell, the caching behavior of this worker should match
997
0
    // that of that docshell.
998
0
    if (topWorkerPrivate->IsDedicatedWorker()) {
999
0
      nsCOMPtr<nsPIDOMWindowInner> window = topWorkerPrivate->GetWindow();
1000
0
      if (window) {
1001
0
        nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
1002
0
        if (docShell) {
1003
0
          nsresult rv = docShell->GetDefaultLoadFlags(&loadFlags);
1004
0
          NS_ENSURE_SUCCESS(rv, rv);
1005
0
        }
1006
0
      }
1007
0
    }
1008
0
1009
0
    if (!channel) {
1010
0
      // Only top level workers' main script use the document charset for the
1011
0
      // script uri encoding. Otherwise, default encoding (UTF-8) is applied.
1012
0
      bool useDefaultEncoding = !(!parentWorker && IsMainWorkerScript());
1013
0
      rv = ChannelFromScriptURL(principal, baseURI, parentDoc, mWorkerPrivate,
1014
0
                                loadGroup, ios,
1015
0
                                secMan, loadInfo.mURL,
1016
0
                                mClientInfo, mController,
1017
0
                                IsMainWorkerScript(),
1018
0
                                mWorkerScriptType,
1019
0
                                mWorkerPrivate->ContentPolicyType(), loadFlags,
1020
0
                                useDefaultEncoding,
1021
0
                                getter_AddRefs(channel));
1022
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1023
0
        return rv;
1024
0
      }
1025
0
    }
1026
0
1027
0
    // We need to know which index we're on in OnStreamComplete so we know
1028
0
    // where to put the result.
1029
0
    RefPtr<LoaderListener> listener = new LoaderListener(this, aIndex);
1030
0
1031
0
    // We don't care about progress so just use the simple stream loader for
1032
0
    // OnStreamComplete notification only.
1033
0
    nsCOMPtr<nsIStreamLoader> loader;
1034
0
    rv = NS_NewStreamLoader(getter_AddRefs(loader), listener);
1035
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1036
0
      return rv;
1037
0
    }
1038
0
1039
0
    if (IsMainWorkerScript()) {
1040
0
      MOZ_DIAGNOSTIC_ASSERT(loadInfo.mReservedClientInfo.isSome());
1041
0
      rv = AddClientChannelHelper(channel,
1042
0
                                  std::move(loadInfo.mReservedClientInfo),
1043
0
                                  Maybe<ClientInfo>(),
1044
0
                                  mWorkerPrivate->HybridEventTarget());
1045
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1046
0
        return rv;
1047
0
      }
1048
0
    }
1049
0
1050
0
    if (loadInfo.mCacheStatus != ScriptLoadInfo::ToBeCached) {
1051
0
      rv = channel->AsyncOpen2(loader);
1052
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1053
0
        return rv;
1054
0
      }
1055
0
    } else {
1056
0
      nsCOMPtr<nsIOutputStream> writer;
1057
0
1058
0
      // In case we return early.
1059
0
      loadInfo.mCacheStatus = ScriptLoadInfo::Cancel;
1060
0
1061
0
      rv = NS_NewPipe(getter_AddRefs(loadInfo.mCacheReadStream),
1062
0
                      getter_AddRefs(writer), 0,
1063
0
                      UINT32_MAX, // unlimited size to avoid writer WOULD_BLOCK case
1064
0
                      true, false); // non-blocking reader, blocking writer
1065
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1066
0
        return rv;
1067
0
      }
1068
0
1069
0
      nsCOMPtr<nsIStreamListenerTee> tee =
1070
0
        do_CreateInstance(NS_STREAMLISTENERTEE_CONTRACTID);
1071
0
      rv = tee->Init(loader, writer, listener);
1072
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1073
0
        return rv;
1074
0
      }
1075
0
1076
0
      nsresult rv = channel->AsyncOpen2(tee);
1077
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
1078
0
        return rv;
1079
0
      }
1080
0
    }
1081
0
1082
0
    loadInfo.mChannel.swap(channel);
1083
0
1084
0
    return NS_OK;
1085
0
  }
1086
1087
  nsresult
1088
  OnStreamCompleteInternal(nsIStreamLoader* aLoader, nsresult aStatus,
1089
                           uint32_t aStringLen, const uint8_t* aString,
1090
                           ScriptLoadInfo& aLoadInfo)
1091
0
  {
1092
0
    AssertIsOnMainThread();
1093
0
1094
0
    if (!aLoadInfo.mChannel) {
1095
0
      return NS_BINDING_ABORTED;
1096
0
    }
1097
0
1098
0
    aLoadInfo.mChannel = nullptr;
1099
0
1100
0
    if (NS_FAILED(aStatus)) {
1101
0
      return aStatus;
1102
0
    }
1103
0
1104
0
    NS_ASSERTION(aString, "This should never be null!");
1105
0
1106
0
    nsCOMPtr<nsIRequest> request;
1107
0
    nsresult rv = aLoader->GetRequest(getter_AddRefs(request));
1108
0
    NS_ENSURE_SUCCESS(rv, rv);
1109
0
1110
0
    nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
1111
0
    MOZ_ASSERT(channel);
1112
0
1113
0
    nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
1114
0
    NS_ASSERTION(ssm, "Should never be null!");
1115
0
1116
0
    nsCOMPtr<nsIPrincipal> channelPrincipal;
1117
0
    rv = ssm->GetChannelResultPrincipal(channel, getter_AddRefs(channelPrincipal));
1118
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1119
0
      return rv;
1120
0
    }
1121
0
1122
0
    nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
1123
0
    if (!principal) {
1124
0
      WorkerPrivate* parentWorker = mWorkerPrivate->GetParent();
1125
0
      MOZ_ASSERT(parentWorker, "Must have a parent!");
1126
0
      principal = parentWorker->GetPrincipal();
1127
0
    }
1128
0
1129
#ifdef DEBUG
1130
    if (IsMainWorkerScript()) {
1131
      nsCOMPtr<nsIPrincipal> loadingPrincipal =
1132
        mWorkerPrivate->GetLoadingPrincipal();
1133
      // if we are not in a ServiceWorker, and the principal is not null, then the
1134
      // loading principal must subsume the worker principal if it is not a
1135
      // nullPrincipal (sandbox).
1136
      MOZ_ASSERT(!loadingPrincipal ||
1137
                 loadingPrincipal->GetIsNullPrincipal() ||
1138
                 principal->GetIsNullPrincipal() ||
1139
                 loadingPrincipal->Subsumes(principal));
1140
    }
1141
#endif
1142
1143
0
    // We don't mute the main worker script becase we've already done
1144
0
    // same-origin checks on them so we should be able to see their errors.
1145
0
    // Note that for data: url, where we allow it through the same-origin check
1146
0
    // but then give it a different origin.
1147
0
    aLoadInfo.mMutedErrorFlag.emplace(IsMainWorkerScript()
1148
0
                                        ? false
1149
0
                                        : !principal->Subsumes(channelPrincipal));
1150
0
1151
0
    // Make sure we're not seeing the result of a 404 or something by checking
1152
0
    // the 'requestSucceeded' attribute on the http channel.
1153
0
    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request);
1154
0
    nsAutoCString tCspHeaderValue, tCspROHeaderValue, tRPHeaderCValue;
1155
0
1156
0
    if (httpChannel) {
1157
0
      bool requestSucceeded;
1158
0
      rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
1159
0
      NS_ENSURE_SUCCESS(rv, rv);
1160
0
1161
0
      if (!requestSucceeded) {
1162
0
        return NS_ERROR_NOT_AVAILABLE;
1163
0
      }
1164
0
1165
0
      Unused << httpChannel->GetResponseHeader(
1166
0
        NS_LITERAL_CSTRING("content-security-policy"),
1167
0
        tCspHeaderValue);
1168
0
1169
0
      Unused << httpChannel->GetResponseHeader(
1170
0
        NS_LITERAL_CSTRING("content-security-policy-report-only"),
1171
0
        tCspROHeaderValue);
1172
0
1173
0
      Unused << httpChannel->GetResponseHeader(
1174
0
        NS_LITERAL_CSTRING("referrer-policy"),
1175
0
        tRPHeaderCValue);
1176
0
    }
1177
0
1178
0
    // May be null.
1179
0
    nsIDocument* parentDoc = mWorkerPrivate->GetDocument();
1180
0
1181
0
    // Use the regular ScriptLoader for this grunt work! Should be just fine
1182
0
    // because we're running on the main thread.
1183
0
    // Unlike <script> tags, Worker scripts are always decoded as UTF-8,
1184
0
    // per spec. So we explicitly pass in the charset hint.
1185
0
    rv = ScriptLoader::ConvertToUTF16(aLoadInfo.mChannel, aString, aStringLen,
1186
0
                                      NS_LITERAL_STRING("UTF-8"), parentDoc,
1187
0
                                      aLoadInfo.mScriptTextBuf,
1188
0
                                      aLoadInfo.mScriptTextLength);
1189
0
    if (NS_FAILED(rv)) {
1190
0
      return rv;
1191
0
    }
1192
0
1193
0
    if (!aLoadInfo.mScriptTextLength && !aLoadInfo.mScriptTextBuf) {
1194
0
      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
1195
0
                                      NS_LITERAL_CSTRING("DOM"), parentDoc,
1196
0
                                      nsContentUtils::eDOM_PROPERTIES,
1197
0
                                      "EmptyWorkerSourceWarning");
1198
0
    } else if (!aLoadInfo.mScriptTextBuf) {
1199
0
      return NS_ERROR_FAILURE;
1200
0
    }
1201
0
1202
0
    // Figure out what we actually loaded.
1203
0
    nsCOMPtr<nsIURI> finalURI;
1204
0
    rv = NS_GetFinalChannelURI(channel, getter_AddRefs(finalURI));
1205
0
    NS_ENSURE_SUCCESS(rv, rv);
1206
0
1207
0
    nsCString filename;
1208
0
    rv = finalURI->GetSpec(filename);
1209
0
    NS_ENSURE_SUCCESS(rv, rv);
1210
0
1211
0
    if (!filename.IsEmpty()) {
1212
0
      // This will help callers figure out what their script url resolved to in
1213
0
      // case of errors.
1214
0
      aLoadInfo.mURL.Assign(NS_ConvertUTF8toUTF16(filename));
1215
0
    }
1216
0
1217
0
    nsCOMPtr<nsILoadInfo> chanLoadInfo = channel->GetLoadInfo();
1218
0
    if (chanLoadInfo && chanLoadInfo->GetEnforceSRI()) {
1219
0
      // importScripts() and the Worker constructor do not support integrity metadata
1220
0
      //  (or any fetch options). Until then, we can just block.
1221
0
      //  If we ever have those data in the future, we'll have to the check to
1222
0
      //  by using the SRICheck module
1223
0
      MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
1224
0
            ("Scriptloader::Load, SRI required but not supported in workers"));
1225
0
      nsCOMPtr<nsIContentSecurityPolicy> wcsp;
1226
0
      chanLoadInfo->LoadingPrincipal()->GetCsp(getter_AddRefs(wcsp));
1227
0
      MOZ_ASSERT(wcsp, "We should have a CSP for the worker here");
1228
0
      if (wcsp) {
1229
0
        wcsp->LogViolationDetails(
1230
0
            nsIContentSecurityPolicy::VIOLATION_TYPE_REQUIRE_SRI_FOR_SCRIPT,
1231
0
            nullptr, // triggering element
1232
0
            aLoadInfo.mURL, EmptyString(), 0, 0, EmptyString(), EmptyString());
1233
0
      }
1234
0
      return NS_ERROR_SRI_CORRUPT;
1235
0
    }
1236
0
1237
0
    // Update the principal of the worker and its base URI if we just loaded the
1238
0
    // worker's primary script.
1239
0
    if (IsMainWorkerScript()) {
1240
0
      // Take care of the base URI first.
1241
0
      mWorkerPrivate->SetBaseURI(finalURI);
1242
0
1243
0
      // Store the channel info if needed.
1244
0
      mWorkerPrivate->InitChannelInfo(channel);
1245
0
1246
0
      // Our final channel principal should match the loading principal
1247
0
      // in terms of the origin.  This used to be an assert, but it seems
1248
0
      // there are some rare cases where this check can fail in practice.
1249
0
      // Perhaps some browser script setting nsIChannel.owner, etc.
1250
0
      NS_ENSURE_TRUE(mWorkerPrivate->FinalChannelPrincipalIsValid(channel),
1251
0
                     NS_ERROR_FAILURE);
1252
0
1253
0
      // However, we must still override the principal since the nsIPrincipal
1254
0
      // URL may be different due to same-origin redirects.  Unfortunately this
1255
0
      // URL must exactly match the final worker script URL in order to
1256
0
      // properly set the referrer header on fetch/xhr requests.  If bug 1340694
1257
0
      // is ever fixed this can be removed.
1258
0
      rv = mWorkerPrivate->SetPrincipalFromChannel(channel);
1259
0
      NS_ENSURE_SUCCESS(rv, rv);
1260
0
1261
0
      nsCOMPtr<nsIContentSecurityPolicy> csp = mWorkerPrivate->GetCSP();
1262
0
      // We did inherit CSP in bug 1223647. If we do not already have a CSP, we
1263
0
      // should get it from the HTTP headers on the worker script.
1264
0
      if (StaticPrefs::security_csp_enable()) {
1265
0
        if (!csp) {
1266
0
          rv = mWorkerPrivate->SetCSPFromHeaderValues(tCspHeaderValue,
1267
0
                                                      tCspROHeaderValue);
1268
0
          NS_ENSURE_SUCCESS(rv, rv);
1269
0
        } else {
1270
0
          csp->EnsureEventTarget(mWorkerPrivate->MainThreadEventTarget());
1271
0
        }
1272
0
      }
1273
0
1274
0
      mWorkerPrivate->SetReferrerPolicyFromHeaderValue(tRPHeaderCValue);
1275
0
1276
0
      WorkerPrivate* parent = mWorkerPrivate->GetParent();
1277
0
      if (parent) {
1278
0
        // XHR Params Allowed
1279
0
        mWorkerPrivate->SetXHRParamsAllowed(parent->XHRParamsAllowed());
1280
0
      }
1281
0
1282
0
      if (chanLoadInfo) {
1283
0
        mController = chanLoadInfo->GetController();
1284
0
      }
1285
0
1286
0
      // If we are loading a blob URL we must inherit the controller
1287
0
      // from the parent.  This is a bit odd as the blob URL may have
1288
0
      // been created in a different context with a different controller.
1289
0
      // For now, though, this is what the spec says.  See:
1290
0
      //
1291
0
      // https://github.com/w3c/ServiceWorker/issues/1261
1292
0
      //
1293
0
      if (IsBlobURI(mWorkerPrivate->GetBaseURI())) {
1294
0
        MOZ_DIAGNOSTIC_ASSERT(mController.isNothing());
1295
0
        mController = mWorkerPrivate->GetParentController();
1296
0
      }
1297
0
    }
1298
0
1299
0
    return NS_OK;
1300
0
  }
1301
1302
  void
1303
  DataReceivedFromCache(uint32_t aIndex, const uint8_t* aString,
1304
                        uint32_t aStringLen,
1305
                        const mozilla::dom::ChannelInfo& aChannelInfo,
1306
                        UniquePtr<PrincipalInfo> aPrincipalInfo,
1307
                        const nsACString& aCSPHeaderValue,
1308
                        const nsACString& aCSPReportOnlyHeaderValue,
1309
                        const nsACString& aReferrerPolicyHeaderValue)
1310
0
  {
1311
0
    AssertIsOnMainThread();
1312
0
    MOZ_ASSERT(aIndex < mLoadInfos.Length());
1313
0
    ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
1314
0
    MOZ_ASSERT(loadInfo.mCacheStatus == ScriptLoadInfo::Cached);
1315
0
1316
0
    nsCOMPtr<nsIPrincipal> responsePrincipal =
1317
0
      PrincipalInfoToPrincipal(*aPrincipalInfo);
1318
0
    MOZ_DIAGNOSTIC_ASSERT(responsePrincipal);
1319
0
1320
0
    nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
1321
0
    if (!principal) {
1322
0
      WorkerPrivate* parentWorker = mWorkerPrivate->GetParent();
1323
0
      MOZ_ASSERT(parentWorker, "Must have a parent!");
1324
0
      principal = parentWorker->GetPrincipal();
1325
0
    }
1326
0
1327
0
    loadInfo.mMutedErrorFlag.emplace(!principal->Subsumes(responsePrincipal));
1328
0
1329
0
    // May be null.
1330
0
    nsIDocument* parentDoc = mWorkerPrivate->GetDocument();
1331
0
1332
0
    MOZ_ASSERT(!loadInfo.mScriptTextBuf);
1333
0
1334
0
    nsresult rv =
1335
0
      ScriptLoader::ConvertToUTF16(nullptr, aString, aStringLen,
1336
0
                                   NS_LITERAL_STRING("UTF-8"), parentDoc,
1337
0
                                   loadInfo.mScriptTextBuf,
1338
0
                                   loadInfo.mScriptTextLength);
1339
0
    if (NS_SUCCEEDED(rv) && IsMainWorkerScript()) {
1340
0
      nsCOMPtr<nsIURI> finalURI;
1341
0
      rv = NS_NewURI(getter_AddRefs(finalURI), loadInfo.mFullURL, nullptr, nullptr);
1342
0
      if (NS_SUCCEEDED(rv)) {
1343
0
        mWorkerPrivate->SetBaseURI(finalURI);
1344
0
      }
1345
0
1346
0
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
1347
0
      nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
1348
0
      MOZ_DIAGNOSTIC_ASSERT(principal);
1349
0
1350
0
      bool equal = false;
1351
0
      MOZ_ALWAYS_SUCCEEDS(responsePrincipal->Equals(principal, &equal));
1352
0
      MOZ_DIAGNOSTIC_ASSERT(equal);
1353
0
1354
0
      nsCOMPtr<nsIContentSecurityPolicy> csp;
1355
0
      MOZ_ALWAYS_SUCCEEDS(responsePrincipal->GetCsp(getter_AddRefs(csp)));
1356
0
      MOZ_DIAGNOSTIC_ASSERT(!csp);
1357
0
#endif
1358
0
1359
0
      mWorkerPrivate->InitChannelInfo(aChannelInfo);
1360
0
1361
0
      nsILoadGroup* loadGroup = mWorkerPrivate->GetLoadGroup();
1362
0
      MOZ_DIAGNOSTIC_ASSERT(loadGroup);
1363
0
1364
0
      // Override the principal on the WorkerPrivate.  This is only necessary
1365
0
      // in order to get a principal with exactly the correct URL.  The fetch
1366
0
      // referrer logic depends on the WorkerPrivate principal having a URL
1367
0
      // that matches the worker script URL.  If bug 1340694 is ever fixed
1368
0
      // this can be removed.
1369
0
      rv = mWorkerPrivate->SetPrincipalOnMainThread(responsePrincipal, loadGroup);
1370
0
      MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
1371
0
1372
0
      rv = mWorkerPrivate->SetCSPFromHeaderValues(aCSPHeaderValue,
1373
0
                                                  aCSPReportOnlyHeaderValue);
1374
0
      MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
1375
0
1376
0
      mWorkerPrivate->SetReferrerPolicyFromHeaderValue(aReferrerPolicyHeaderValue);
1377
0
    }
1378
0
1379
0
    if (NS_SUCCEEDED(rv)) {
1380
0
      DataReceived();
1381
0
    }
1382
0
1383
0
    LoadingFinished(aIndex, rv);
1384
0
  }
1385
1386
  void
1387
  DataReceived()
1388
0
  {
1389
0
    if (IsMainWorkerScript()) {
1390
0
      WorkerPrivate* parent = mWorkerPrivate->GetParent();
1391
0
1392
0
      if (parent) {
1393
0
        // XHR Params Allowed
1394
0
        mWorkerPrivate->SetXHRParamsAllowed(parent->XHRParamsAllowed());
1395
0
1396
0
        // Set Eval and ContentSecurityPolicy
1397
0
        mWorkerPrivate->SetCSP(parent->GetCSP());
1398
0
        mWorkerPrivate->SetEvalAllowed(parent->IsEvalAllowed());
1399
0
      }
1400
0
    }
1401
0
  }
1402
1403
  void
1404
  ExecuteFinishedScripts()
1405
0
  {
1406
0
    AssertIsOnMainThread();
1407
0
1408
0
    if (IsMainWorkerScript()) {
1409
0
      mWorkerPrivate->WorkerScriptLoaded();
1410
0
    }
1411
0
1412
0
    uint32_t firstIndex = UINT32_MAX;
1413
0
    uint32_t lastIndex = UINT32_MAX;
1414
0
1415
0
    // Find firstIndex based on whether mExecutionScheduled is unset.
1416
0
    for (uint32_t index = 0; index < mLoadInfos.Length(); index++) {
1417
0
      if (!mLoadInfos[index].mExecutionScheduled) {
1418
0
        firstIndex = index;
1419
0
        break;
1420
0
      }
1421
0
    }
1422
0
1423
0
    // Find lastIndex based on whether mChannel is set, and update
1424
0
    // mExecutionScheduled on the ones we're about to schedule.
1425
0
    if (firstIndex != UINT32_MAX) {
1426
0
      for (uint32_t index = firstIndex; index < mLoadInfos.Length(); index++) {
1427
0
        ScriptLoadInfo& loadInfo = mLoadInfos[index];
1428
0
1429
0
        if (!loadInfo.Finished()) {
1430
0
          break;
1431
0
        }
1432
0
1433
0
        // We can execute this one.
1434
0
        loadInfo.mExecutionScheduled = true;
1435
0
1436
0
        lastIndex = index;
1437
0
      }
1438
0
    }
1439
0
1440
0
    // This is the last index, we can unused things before the exection of the
1441
0
    // script and the stopping of the sync loop.
1442
0
    if (lastIndex == mLoadInfos.Length() - 1) {
1443
0
      mCacheCreator = nullptr;
1444
0
    }
1445
0
1446
0
    if (firstIndex != UINT32_MAX && lastIndex != UINT32_MAX) {
1447
0
      RefPtr<ScriptExecutorRunnable> runnable =
1448
0
        new ScriptExecutorRunnable(*this, mSyncLoopTarget, IsMainWorkerScript(),
1449
0
                                   firstIndex, lastIndex);
1450
0
      if (!runnable->Dispatch()) {
1451
0
        MOZ_ASSERT(false, "This should never fail!");
1452
0
      }
1453
0
    }
1454
0
  }
1455
};
1456
1457
NS_IMPL_ISUPPORTS(ScriptLoaderRunnable, nsIRunnable, nsINamed)
1458
1459
NS_IMETHODIMP
1460
LoaderListener::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
1461
                                 nsresult aStatus, uint32_t aStringLen,
1462
                                 const uint8_t* aString)
1463
0
{
1464
0
  return mRunnable->OnStreamComplete(aLoader, mIndex, aStatus, aStringLen, aString);
1465
0
}
1466
1467
NS_IMETHODIMP
1468
LoaderListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
1469
0
{
1470
0
  return mRunnable->OnStartRequest(aRequest, mIndex);
1471
0
}
1472
1473
void
1474
CachePromiseHandler::ResolvedCallback(JSContext* aCx,
1475
                                      JS::Handle<JS::Value> aValue)
1476
0
{
1477
0
  AssertIsOnMainThread();
1478
0
  // May already have been canceled by CacheScriptLoader::Fail from
1479
0
  // CancelMainThread.
1480
0
  MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::WritingToCache ||
1481
0
             mLoadInfo.mCacheStatus == ScriptLoadInfo::Cancel);
1482
0
  MOZ_ASSERT_IF(mLoadInfo.mCacheStatus == ScriptLoadInfo::Cancel, !mLoadInfo.mCachePromise);
1483
0
1484
0
  if (mLoadInfo.mCachePromise) {
1485
0
    mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
1486
0
    mLoadInfo.mCachePromise = nullptr;
1487
0
    mRunnable->MaybeExecuteFinishedScripts(mIndex);
1488
0
  }
1489
0
}
1490
1491
void
1492
CachePromiseHandler::RejectedCallback(JSContext* aCx,
1493
                                      JS::Handle<JS::Value> aValue)
1494
0
{
1495
0
  AssertIsOnMainThread();
1496
0
  // May already have been canceled by CacheScriptLoader::Fail from
1497
0
  // CancelMainThread.
1498
0
  MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::WritingToCache ||
1499
0
             mLoadInfo.mCacheStatus == ScriptLoadInfo::Cancel);
1500
0
  mLoadInfo.mCacheStatus = ScriptLoadInfo::Cancel;
1501
0
1502
0
  mLoadInfo.mCachePromise = nullptr;
1503
0
1504
0
  // This will delete the cache object and will call LoadingFinished() with an
1505
0
  // error for each ongoing operation.
1506
0
  mRunnable->DeleteCache();
1507
0
}
1508
1509
nsresult
1510
CacheCreator::CreateCacheStorage(nsIPrincipal* aPrincipal)
1511
0
{
1512
0
  AssertIsOnMainThread();
1513
0
  MOZ_ASSERT(!mCacheStorage);
1514
0
  MOZ_ASSERT(aPrincipal);
1515
0
1516
0
  nsIXPConnect* xpc = nsContentUtils::XPConnect();
1517
0
  MOZ_ASSERT(xpc, "This should never be null!");
1518
0
1519
0
  AutoJSAPI jsapi;
1520
0
  jsapi.Init();
1521
0
  JSContext* cx = jsapi.cx();
1522
0
  JS::Rooted<JSObject*> sandbox(cx);
1523
0
  nsresult rv = xpc->CreateSandbox(cx, aPrincipal, sandbox.address());
1524
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1525
0
    return rv;
1526
0
  }
1527
0
1528
0
  // The JSContext is not in a realm, so CreateSandbox returned an unwrapped
1529
0
  // global.
1530
0
  MOZ_ASSERT(JS_IsGlobalObject(sandbox));
1531
0
1532
0
  mSandboxGlobalObject = xpc::NativeGlobal(sandbox);
1533
0
  if (NS_WARN_IF(!mSandboxGlobalObject)) {
1534
0
    return NS_ERROR_FAILURE;
1535
0
  }
1536
0
1537
0
  // If we're in private browsing mode, don't even try to create the
1538
0
  // CacheStorage.  Instead, just fail immediately to terminate the
1539
0
  // ServiceWorker load.
1540
0
  if (NS_WARN_IF(mOriginAttributes.mPrivateBrowsingId > 0)) {
1541
0
    return NS_ERROR_DOM_SECURITY_ERR;
1542
0
  }
1543
0
1544
0
  // Create a CacheStorage bypassing its trusted origin checks.  The
1545
0
  // ServiceWorker has already performed its own checks before getting
1546
0
  // to this point.
1547
0
  ErrorResult error;
1548
0
  mCacheStorage =
1549
0
    CacheStorage::CreateOnMainThread(mozilla::dom::cache::CHROME_ONLY_NAMESPACE,
1550
0
                                     mSandboxGlobalObject,
1551
0
                                     aPrincipal,
1552
0
                                     true /* force trusted origin */,
1553
0
                                     error);
1554
0
  if (NS_WARN_IF(error.Failed())) {
1555
0
    return error.StealNSResult();
1556
0
  }
1557
0
1558
0
  return NS_OK;
1559
0
}
1560
1561
nsresult
1562
CacheCreator::Load(nsIPrincipal* aPrincipal)
1563
0
{
1564
0
  AssertIsOnMainThread();
1565
0
  MOZ_ASSERT(!mLoaders.IsEmpty());
1566
0
1567
0
  nsresult rv = CreateCacheStorage(aPrincipal);
1568
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1569
0
    return rv;
1570
0
  }
1571
0
1572
0
  ErrorResult error;
1573
0
  MOZ_ASSERT(!mCacheName.IsEmpty());
1574
0
  RefPtr<Promise> promise = mCacheStorage->Open(mCacheName, error);
1575
0
  if (NS_WARN_IF(error.Failed())) {
1576
0
    return error.StealNSResult();
1577
0
  }
1578
0
1579
0
  promise->AppendNativeHandler(this);
1580
0
  return NS_OK;
1581
0
}
1582
1583
void
1584
CacheCreator::FailLoaders(nsresult aRv)
1585
0
{
1586
0
  AssertIsOnMainThread();
1587
0
1588
0
  // Fail() can call LoadingFinished() which may call ExecuteFinishedScripts()
1589
0
  // which sets mCacheCreator to null, so hold a ref.
1590
0
  RefPtr<CacheCreator> kungfuDeathGrip = this;
1591
0
1592
0
  for (uint32_t i = 0, len = mLoaders.Length(); i < len; ++i) {
1593
0
    mLoaders[i]->Fail(aRv);
1594
0
  }
1595
0
1596
0
  mLoaders.Clear();
1597
0
}
1598
1599
void
1600
CacheCreator::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
1601
0
{
1602
0
  AssertIsOnMainThread();
1603
0
  FailLoaders(NS_ERROR_FAILURE);
1604
0
}
1605
1606
void
1607
CacheCreator::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
1608
0
{
1609
0
  AssertIsOnMainThread();
1610
0
1611
0
  if (!aValue.isObject()) {
1612
0
    FailLoaders(NS_ERROR_FAILURE);
1613
0
    return;
1614
0
  }
1615
0
1616
0
  JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
1617
0
  Cache* cache = nullptr;
1618
0
  nsresult rv = UNWRAP_OBJECT(Cache, &obj, cache);
1619
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1620
0
    FailLoaders(NS_ERROR_FAILURE);
1621
0
    return;
1622
0
  }
1623
0
1624
0
  mCache = cache;
1625
0
  MOZ_DIAGNOSTIC_ASSERT(mCache);
1626
0
1627
0
  // If the worker is canceled, CancelMainThread() will have cleared the
1628
0
  // loaders via DeleteCache().
1629
0
  for (uint32_t i = 0, len = mLoaders.Length(); i < len; ++i) {
1630
0
    MOZ_DIAGNOSTIC_ASSERT(mLoaders[i]);
1631
0
    mLoaders[i]->Load(cache);
1632
0
  }
1633
0
}
1634
1635
void
1636
CacheCreator::DeleteCache()
1637
0
{
1638
0
  AssertIsOnMainThread();
1639
0
1640
0
  // This is called when the load is canceled which can occur before
1641
0
  // mCacheStorage is initialized.
1642
0
  if (mCacheStorage) {
1643
0
    // It's safe to do this while Cache::Match() and Cache::Put() calls are
1644
0
    // running.
1645
0
    RefPtr<Promise> promise = mCacheStorage->Delete(mCacheName, IgnoreErrors());
1646
0
1647
0
    // We don't care to know the result of the promise object.
1648
0
  }
1649
0
1650
0
  // Always call this here to ensure the loaders array is cleared.
1651
0
  FailLoaders(NS_ERROR_FAILURE);
1652
0
}
1653
1654
void
1655
CacheScriptLoader::Fail(nsresult aRv)
1656
0
{
1657
0
  AssertIsOnMainThread();
1658
0
  MOZ_ASSERT(NS_FAILED(aRv));
1659
0
1660
0
  if (mFailed) {
1661
0
    return;
1662
0
  }
1663
0
1664
0
  mFailed = true;
1665
0
1666
0
  if (mPump) {
1667
0
    MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::ReadingFromCache);
1668
0
    mPump->Cancel(aRv);
1669
0
    mPump = nullptr;
1670
0
  }
1671
0
1672
0
  mLoadInfo.mCacheStatus = ScriptLoadInfo::Cancel;
1673
0
1674
0
  // Stop if the load was aborted on the main thread.
1675
0
  // Can't use Finished() because mCachePromise may still be true.
1676
0
  if (mLoadInfo.mLoadingFinished) {
1677
0
    MOZ_ASSERT(!mLoadInfo.mChannel);
1678
0
    MOZ_ASSERT_IF(mLoadInfo.mCachePromise,
1679
0
                  mLoadInfo.mCacheStatus == ScriptLoadInfo::WritingToCache ||
1680
0
                  mLoadInfo.mCacheStatus == ScriptLoadInfo::Cancel);
1681
0
    return;
1682
0
  }
1683
0
1684
0
  mRunnable->LoadingFinished(mIndex, aRv);
1685
0
}
1686
1687
void
1688
CacheScriptLoader::Load(Cache* aCache)
1689
0
{
1690
0
  AssertIsOnMainThread();
1691
0
  MOZ_ASSERT(aCache);
1692
0
1693
0
  nsCOMPtr<nsIURI> uri;
1694
0
  nsresult rv = NS_NewURI(getter_AddRefs(uri), mLoadInfo.mURL, nullptr,
1695
0
                          mBaseURI);
1696
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1697
0
    Fail(rv);
1698
0
    return;
1699
0
  }
1700
0
1701
0
  nsAutoCString spec;
1702
0
  rv = uri->GetSpec(spec);
1703
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1704
0
    Fail(rv);
1705
0
    return;
1706
0
  }
1707
0
1708
0
  MOZ_ASSERT(mLoadInfo.mFullURL.IsEmpty());
1709
0
  CopyUTF8toUTF16(spec, mLoadInfo.mFullURL);
1710
0
1711
0
  mozilla::dom::RequestOrUSVString request;
1712
0
  request.SetAsUSVString().Rebind(mLoadInfo.mFullURL.Data(),
1713
0
                                  mLoadInfo.mFullURL.Length());
1714
0
1715
0
  mozilla::dom::CacheQueryOptions params;
1716
0
1717
0
  // This JSContext will not end up executing JS code because here there are
1718
0
  // no ReadableStreams involved.
1719
0
  AutoJSAPI jsapi;
1720
0
  jsapi.Init();
1721
0
1722
0
  ErrorResult error;
1723
0
  RefPtr<Promise> promise = aCache->Match(jsapi.cx(), request, params, error);
1724
0
  if (NS_WARN_IF(error.Failed())) {
1725
0
    Fail(error.StealNSResult());
1726
0
    return;
1727
0
  }
1728
0
1729
0
  promise->AppendNativeHandler(this);
1730
0
}
1731
1732
void
1733
CacheScriptLoader::RejectedCallback(JSContext* aCx,
1734
                                    JS::Handle<JS::Value> aValue)
1735
0
{
1736
0
  AssertIsOnMainThread();
1737
0
  MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::Uncached);
1738
0
  Fail(NS_ERROR_FAILURE);
1739
0
}
1740
1741
void
1742
CacheScriptLoader::ResolvedCallback(JSContext* aCx,
1743
                                    JS::Handle<JS::Value> aValue)
1744
0
{
1745
0
  AssertIsOnMainThread();
1746
0
  // If we have already called 'Fail', we should not proceed.
1747
0
  if (mFailed) {
1748
0
    return;
1749
0
  }
1750
0
1751
0
  MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::Uncached);
1752
0
1753
0
  nsresult rv;
1754
0
1755
0
  // The ServiceWorkerScriptCache will store data for any scripts it
1756
0
  // it knows about.  This is always at least the top level script.
1757
0
  // Depending on if a previous version of the service worker has
1758
0
  // been installed or not it may also know about importScripts().  We
1759
0
  // must handle loading and offlining new importScripts() here, however.
1760
0
  if (aValue.isUndefined()) {
1761
0
    // If this is the main script or we're not loading a new service worker
1762
0
    // then this is an error.  This can happen for internal reasons, like
1763
0
    // storage was probably wiped without removing the service worker
1764
0
    // registration.  It can also happen for exposed reasons like the
1765
0
    // service worker script calling importScripts() after install.
1766
0
    if (NS_WARN_IF(mIsWorkerScript || (mState != ServiceWorkerState::Parsed &&
1767
0
                                       mState != ServiceWorkerState::Installing))) {
1768
0
      Fail(NS_ERROR_DOM_INVALID_STATE_ERR);
1769
0
      return;
1770
0
    }
1771
0
1772
0
    mLoadInfo.mCacheStatus = ScriptLoadInfo::ToBeCached;
1773
0
    rv = mRunnable->LoadScript(mIndex);
1774
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1775
0
      Fail(rv);
1776
0
    }
1777
0
    return;
1778
0
  }
1779
0
1780
0
  MOZ_ASSERT(aValue.isObject());
1781
0
1782
0
  JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
1783
0
  mozilla::dom::Response* response = nullptr;
1784
0
  rv = UNWRAP_OBJECT(Response, &obj, response);
1785
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1786
0
    Fail(rv);
1787
0
    return;
1788
0
  }
1789
0
1790
0
  InternalHeaders* headers = response->GetInternalHeaders();
1791
0
1792
0
  headers->Get(NS_LITERAL_CSTRING("content-security-policy"),
1793
0
               mCSPHeaderValue, IgnoreErrors());
1794
0
  headers->Get(NS_LITERAL_CSTRING("content-security-policy-report-only"),
1795
0
               mCSPReportOnlyHeaderValue, IgnoreErrors());
1796
0
  headers->Get(NS_LITERAL_CSTRING("referrer-policy"),
1797
0
               mReferrerPolicyHeaderValue, IgnoreErrors());
1798
0
1799
0
  nsCOMPtr<nsIInputStream> inputStream;
1800
0
  response->GetBody(getter_AddRefs(inputStream));
1801
0
  mChannelInfo = response->GetChannelInfo();
1802
0
  const UniquePtr<PrincipalInfo>& pInfo = response->GetPrincipalInfo();
1803
0
  if (pInfo) {
1804
0
    mPrincipalInfo = mozilla::MakeUnique<PrincipalInfo>(*pInfo);
1805
0
  }
1806
0
1807
0
  if (!inputStream) {
1808
0
    mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
1809
0
    mRunnable->DataReceivedFromCache(mIndex, (uint8_t*)"", 0, mChannelInfo,
1810
0
                                     std::move(mPrincipalInfo), mCSPHeaderValue,
1811
0
                                     mCSPReportOnlyHeaderValue,
1812
0
                                     mReferrerPolicyHeaderValue);
1813
0
    return;
1814
0
  }
1815
0
1816
0
  MOZ_ASSERT(!mPump);
1817
0
  rv = NS_NewInputStreamPump(getter_AddRefs(mPump),
1818
0
                             inputStream.forget(),
1819
0
                             0, /* default segsize */
1820
0
                             0, /* default segcount */
1821
0
                             false, /* default closeWhenDone */
1822
0
                             mMainThreadEventTarget);
1823
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1824
0
    Fail(rv);
1825
0
    return;
1826
0
  }
1827
0
1828
0
  nsCOMPtr<nsIStreamLoader> loader;
1829
0
  rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
1830
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1831
0
    Fail(rv);
1832
0
    return;
1833
0
  }
1834
0
1835
0
  rv = mPump->AsyncRead(loader, nullptr);
1836
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1837
0
    mPump = nullptr;
1838
0
    Fail(rv);
1839
0
    return;
1840
0
  }
1841
0
1842
0
1843
0
  nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(mPump);
1844
0
  if (rr) {
1845
0
    nsCOMPtr<nsIEventTarget> sts =
1846
0
      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
1847
0
    rv = rr->RetargetDeliveryTo(sts);
1848
0
    if (NS_FAILED(rv)) {
1849
0
      NS_WARNING("Failed to dispatch the nsIInputStreamPump to a IO thread.");
1850
0
    }
1851
0
  }
1852
0
1853
0
  mLoadInfo.mCacheStatus = ScriptLoadInfo::ReadingFromCache;
1854
0
}
1855
1856
NS_IMETHODIMP
1857
CacheScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
1858
                                    nsresult aStatus, uint32_t aStringLen,
1859
                                    const uint8_t* aString)
1860
0
{
1861
0
  AssertIsOnMainThread();
1862
0
1863
0
  mPump = nullptr;
1864
0
1865
0
  if (NS_FAILED(aStatus)) {
1866
0
    MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::ReadingFromCache ||
1867
0
               mLoadInfo.mCacheStatus == ScriptLoadInfo::Cancel);
1868
0
    Fail(aStatus);
1869
0
    return NS_OK;
1870
0
  }
1871
0
1872
0
  MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::ReadingFromCache);
1873
0
  mLoadInfo.mCacheStatus = ScriptLoadInfo::Cached;
1874
0
1875
0
  MOZ_ASSERT(mPrincipalInfo);
1876
0
  mRunnable->DataReceivedFromCache(mIndex, aString, aStringLen, mChannelInfo,
1877
0
                                   std::move(mPrincipalInfo), mCSPHeaderValue,
1878
0
                                   mCSPReportOnlyHeaderValue,
1879
0
                                   mReferrerPolicyHeaderValue);
1880
0
  return NS_OK;
1881
0
}
1882
1883
class ChannelGetterRunnable final : public WorkerMainThreadRunnable
1884
{
1885
  const nsAString& mScriptURL;
1886
  const ClientInfo mClientInfo;
1887
  WorkerLoadInfo& mLoadInfo;
1888
  nsresult mResult;
1889
1890
public:
1891
  ChannelGetterRunnable(WorkerPrivate* aParentWorker,
1892
                        const nsAString& aScriptURL,
1893
                        WorkerLoadInfo& aLoadInfo)
1894
    : WorkerMainThreadRunnable(aParentWorker,
1895
                               NS_LITERAL_CSTRING("ScriptLoader :: ChannelGetter"))
1896
    , mScriptURL(aScriptURL)
1897
    // ClientInfo should always be present since this should not be called
1898
    // if parent's status is greater than Running.
1899
    , mClientInfo(aParentWorker->GetClientInfo().ref())
1900
    , mLoadInfo(aLoadInfo)
1901
    , mResult(NS_ERROR_FAILURE)
1902
0
  {
1903
0
    MOZ_ASSERT(aParentWorker);
1904
0
    aParentWorker->AssertIsOnWorkerThread();
1905
0
  }
1906
1907
  virtual bool
1908
  MainThreadRun() override
1909
0
  {
1910
0
    AssertIsOnMainThread();
1911
0
1912
0
    // Initialize the WorkerLoadInfo principal to our triggering principal
1913
0
    // before doing anything else.  Normally we do this in the WorkerPrivate
1914
0
    // Constructor, but we can't do so off the main thread when creating
1915
0
    // a nested worker.  So do it here instead.
1916
0
    mLoadInfo.mLoadingPrincipal = mWorkerPrivate->GetPrincipal();
1917
0
    MOZ_DIAGNOSTIC_ASSERT(mLoadInfo.mLoadingPrincipal);
1918
0
1919
0
    mLoadInfo.mPrincipal = mLoadInfo.mLoadingPrincipal;
1920
0
1921
0
    // Figure out our base URI.
1922
0
    nsCOMPtr<nsIURI> baseURI = mWorkerPrivate->GetBaseURI();
1923
0
    MOZ_ASSERT(baseURI);
1924
0
1925
0
    // May be null.
1926
0
    nsCOMPtr<nsIDocument> parentDoc = mWorkerPrivate->GetDocument();
1927
0
1928
0
    mLoadInfo.mLoadGroup = mWorkerPrivate->GetLoadGroup();
1929
0
1930
0
    Maybe<ClientInfo> clientInfo;
1931
0
    clientInfo.emplace(mClientInfo);
1932
0
1933
0
    nsCOMPtr<nsIChannel> channel;
1934
0
    mResult = workerinternals::
1935
0
      ChannelFromScriptURLMainThread(mLoadInfo.mLoadingPrincipal,
1936
0
                                     baseURI, parentDoc,
1937
0
                                     mLoadInfo.mLoadGroup,
1938
0
                                     mScriptURL,
1939
0
                                     clientInfo,
1940
0
                                     // Nested workers are always dedicated.
1941
0
                                     nsIContentPolicy::TYPE_INTERNAL_WORKER,
1942
0
                                     // Nested workers use default uri encoding.
1943
0
                                     true,
1944
0
                                     getter_AddRefs(channel));
1945
0
    NS_ENSURE_SUCCESS(mResult, true);
1946
0
1947
0
    mResult = mLoadInfo.SetPrincipalFromChannel(channel);
1948
0
    NS_ENSURE_SUCCESS(mResult, true);
1949
0
1950
0
    mLoadInfo.mChannel = channel.forget();
1951
0
    return true;
1952
0
  }
1953
1954
  nsresult
1955
  GetResult() const
1956
0
  {
1957
0
    return mResult;
1958
0
  }
1959
1960
private:
1961
  virtual ~ChannelGetterRunnable()
1962
0
  { }
1963
};
1964
1965
ScriptExecutorRunnable::ScriptExecutorRunnable(
1966
                                            ScriptLoaderRunnable& aScriptLoader,
1967
                                            nsIEventTarget* aSyncLoopTarget,
1968
                                            bool aIsWorkerScript,
1969
                                            uint32_t aFirstIndex,
1970
                                            uint32_t aLastIndex)
1971
: MainThreadWorkerSyncRunnable(aScriptLoader.mWorkerPrivate, aSyncLoopTarget),
1972
  mScriptLoader(aScriptLoader), mIsWorkerScript(aIsWorkerScript),
1973
  mFirstIndex(aFirstIndex), mLastIndex(aLastIndex)
1974
0
{
1975
0
  MOZ_ASSERT(aFirstIndex <= aLastIndex);
1976
0
  MOZ_ASSERT(aLastIndex < aScriptLoader.mLoadInfos.Length());
1977
0
}
1978
1979
bool
1980
ScriptExecutorRunnable::IsDebuggerRunnable() const
1981
0
{
1982
0
  // ScriptExecutorRunnable is used to execute both worker and debugger scripts.
1983
0
  // In the latter case, the runnable needs to be dispatched to the debugger
1984
0
  // queue.
1985
0
  return mScriptLoader.mWorkerScriptType == DebuggerScript;
1986
0
}
1987
1988
bool
1989
ScriptExecutorRunnable::PreRun(WorkerPrivate* aWorkerPrivate)
1990
0
{
1991
0
  aWorkerPrivate->AssertIsOnWorkerThread();
1992
0
1993
0
  if (!mIsWorkerScript) {
1994
0
    return true;
1995
0
  }
1996
0
1997
0
  if (!aWorkerPrivate->GetJSContext()) {
1998
0
    return false;
1999
0
  }
2000
0
2001
0
  MOZ_ASSERT(mFirstIndex == 0);
2002
0
  MOZ_ASSERT(!mScriptLoader.mRv.Failed());
2003
0
2004
0
  AutoJSAPI jsapi;
2005
0
  jsapi.Init();
2006
0
2007
0
  WorkerGlobalScope* globalScope =
2008
0
    aWorkerPrivate->GetOrCreateGlobalScope(jsapi.cx());
2009
0
  if (NS_WARN_IF(!globalScope)) {
2010
0
    NS_WARNING("Failed to make global!");
2011
0
    // There's no way to report the exception on jsapi right now, because there
2012
0
    // is no way to even enter a compartment on this thread anymore.  Just clear
2013
0
    // the exception.  We'll report some sort of error to our caller in
2014
0
    // ShutdownScriptLoader, but it will get squelched for the same reason we're
2015
0
    // squelching here: all the error reporting machinery relies on being able
2016
0
    // to enter a compartment to report the error.
2017
0
    jsapi.ClearException();
2018
0
    return false;
2019
0
  }
2020
0
2021
0
  return true;
2022
0
}
2023
2024
bool
2025
ScriptExecutorRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
2026
0
{
2027
0
  aWorkerPrivate->AssertIsOnWorkerThread();
2028
0
2029
0
  nsTArray<ScriptLoadInfo>& loadInfos = mScriptLoader.mLoadInfos;
2030
0
2031
0
  // Don't run if something else has already failed.
2032
0
  for (uint32_t index = 0; index < mFirstIndex; index++) {
2033
0
    ScriptLoadInfo& loadInfo = loadInfos.ElementAt(index);
2034
0
2035
0
    NS_ASSERTION(!loadInfo.mChannel, "Should no longer have a channel!");
2036
0
    NS_ASSERTION(loadInfo.mExecutionScheduled, "Should be scheduled!");
2037
0
2038
0
    if (!loadInfo.mExecutionResult) {
2039
0
      return true;
2040
0
    }
2041
0
  }
2042
0
2043
0
  // If nothing else has failed, our ErrorResult better not be a failure either.
2044
0
  MOZ_ASSERT(!mScriptLoader.mRv.Failed(), "Who failed it and why?");
2045
0
2046
0
  // Slightly icky action at a distance, but there's no better place to stash
2047
0
  // this value, really.
2048
0
  JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
2049
0
  MOZ_ASSERT(global);
2050
0
2051
0
  for (uint32_t index = mFirstIndex; index <= mLastIndex; index++) {
2052
0
    ScriptLoadInfo& loadInfo = loadInfos.ElementAt(index);
2053
0
2054
0
    NS_ASSERTION(!loadInfo.mChannel, "Should no longer have a channel!");
2055
0
    NS_ASSERTION(loadInfo.mExecutionScheduled, "Should be scheduled!");
2056
0
    NS_ASSERTION(!loadInfo.mExecutionResult, "Should not have executed yet!");
2057
0
2058
0
    MOZ_ASSERT(!mScriptLoader.mRv.Failed(), "Who failed it and why?");
2059
0
    mScriptLoader.mRv.MightThrowJSException();
2060
0
    if (NS_FAILED(loadInfo.mLoadResult)) {
2061
0
      workerinternals::ReportLoadError(mScriptLoader.mRv,
2062
0
                                       loadInfo.mLoadResult, loadInfo.mURL);
2063
0
      return true;
2064
0
    }
2065
0
2066
0
    // If this is a top level script that succeeded, then mark the
2067
0
    // Client execution ready and possible controlled by a service worker.
2068
0
    if (mIsWorkerScript) {
2069
0
      if (mScriptLoader.mController.isSome()) {
2070
0
        aWorkerPrivate->Control(mScriptLoader.mController.ref());
2071
0
      }
2072
0
      aWorkerPrivate->ExecutionReady();
2073
0
    }
2074
0
2075
0
    NS_ConvertUTF16toUTF8 filename(loadInfo.mURL);
2076
0
2077
0
    JS::CompileOptions options(aCx);
2078
0
    options.setFileAndLine(filename.get(), 1)
2079
0
           .setNoScriptRval(true);
2080
0
2081
0
    MOZ_ASSERT(loadInfo.mMutedErrorFlag.isSome());
2082
0
    options.setMutedErrors(loadInfo.mMutedErrorFlag.valueOr(true));
2083
0
2084
0
    JS::SourceBufferHolder srcBuf(loadInfo.mScriptTextBuf,
2085
0
                                  loadInfo.mScriptTextLength,
2086
0
                                  JS::SourceBufferHolder::GiveOwnership);
2087
0
    loadInfo.mScriptTextBuf = nullptr;
2088
0
    loadInfo.mScriptTextLength = 0;
2089
0
2090
0
    // Our ErrorResult still shouldn't be a failure.
2091
0
    MOZ_ASSERT(!mScriptLoader.mRv.Failed(), "Who failed it and why?");
2092
0
    JS::Rooted<JS::Value> unused(aCx);
2093
0
    if (!JS::Evaluate(aCx, options, srcBuf, &unused)) {
2094
0
      mScriptLoader.mRv.StealExceptionFromJSContext(aCx);
2095
0
      return true;
2096
0
    }
2097
0
2098
0
    loadInfo.mExecutionResult = true;
2099
0
  }
2100
0
2101
0
  return true;
2102
0
}
2103
2104
void
2105
ScriptExecutorRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
2106
                                bool aRunResult)
2107
0
{
2108
0
  aWorkerPrivate->AssertIsOnWorkerThread();
2109
0
  MOZ_ASSERT(!JS_IsExceptionPending(aCx), "Who left an exception on there?");
2110
0
2111
0
  nsTArray<ScriptLoadInfo>& loadInfos = mScriptLoader.mLoadInfos;
2112
0
2113
0
  if (mLastIndex == loadInfos.Length() - 1) {
2114
0
    // All done. If anything failed then return false.
2115
0
    bool result = true;
2116
0
    bool mutedError = false;
2117
0
    for (uint32_t index = 0; index < loadInfos.Length(); index++) {
2118
0
      if (!loadInfos[index].mExecutionResult) {
2119
0
        mutedError = loadInfos[index].mMutedErrorFlag.valueOr(true);
2120
0
        result = false;
2121
0
        break;
2122
0
      }
2123
0
    }
2124
0
2125
0
    // The only way we can get here with "result" false but without
2126
0
    // mScriptLoader.mRv being a failure is if we're loading the main worker
2127
0
    // script and GetOrCreateGlobalScope() fails.  In that case we would have
2128
0
    // returned false from WorkerRun, so assert that.
2129
0
    MOZ_ASSERT_IF(!result && !mScriptLoader.mRv.Failed(),
2130
0
                  !aRunResult);
2131
0
    ShutdownScriptLoader(aCx, aWorkerPrivate, result, mutedError);
2132
0
  }
2133
0
}
2134
2135
nsresult
2136
ScriptExecutorRunnable::Cancel()
2137
0
{
2138
0
  if (mLastIndex == mScriptLoader.mLoadInfos.Length() - 1) {
2139
0
    ShutdownScriptLoader(mWorkerPrivate->GetJSContext(), mWorkerPrivate,
2140
0
                         false, false);
2141
0
  }
2142
0
  return MainThreadWorkerSyncRunnable::Cancel();
2143
0
}
2144
2145
void
2146
ScriptExecutorRunnable::ShutdownScriptLoader(JSContext* aCx,
2147
                                             WorkerPrivate* aWorkerPrivate,
2148
                                             bool aResult,
2149
                                             bool aMutedError)
2150
0
{
2151
0
  aWorkerPrivate->AssertIsOnWorkerThread();
2152
0
2153
0
  MOZ_ASSERT(mLastIndex == mScriptLoader.mLoadInfos.Length() - 1);
2154
0
2155
0
  if (mIsWorkerScript) {
2156
0
    aWorkerPrivate->SetLoadingWorkerScript(false);
2157
0
  }
2158
0
2159
0
  if (!aResult) {
2160
0
    // At this point there are two possibilities:
2161
0
    //
2162
0
    // 1) mScriptLoader.mRv.Failed().  In that case we just want to leave it
2163
0
    //    as-is, except if it has a JS exception and we need to mute JS
2164
0
    //    exceptions.  In that case, we log the exception without firing any
2165
0
    //    events and then replace it on the ErrorResult with a NetworkError,
2166
0
    //    per spec.
2167
0
    //
2168
0
    // 2) mScriptLoader.mRv succeeded.  As far as I can tell, this can only
2169
0
    //    happen when loading the main worker script and
2170
0
    //    GetOrCreateGlobalScope() fails or if ScriptExecutorRunnable::Cancel
2171
0
    //    got called.  Does it matter what we throw in this case?  I'm not
2172
0
    //    sure...
2173
0
    if (mScriptLoader.mRv.Failed()) {
2174
0
      if (aMutedError && mScriptLoader.mRv.IsJSException()) {
2175
0
        LogExceptionToConsole(aCx, aWorkerPrivate);
2176
0
        mScriptLoader.mRv.ThrowWithCustomCleanup(NS_ERROR_DOM_NETWORK_ERR);
2177
0
      }
2178
0
    } else {
2179
0
      mScriptLoader.mRv.ThrowWithCustomCleanup(NS_ERROR_DOM_INVALID_STATE_ERR);
2180
0
    }
2181
0
  }
2182
0
2183
0
  aWorkerPrivate->StopSyncLoop(mSyncLoopTarget, aResult);
2184
0
}
2185
2186
void
2187
ScriptExecutorRunnable::LogExceptionToConsole(JSContext* aCx,
2188
                                              WorkerPrivate* aWorkerPrivate)
2189
0
{
2190
0
  aWorkerPrivate->AssertIsOnWorkerThread();
2191
0
2192
0
  MOZ_ASSERT(mScriptLoader.mRv.IsJSException());
2193
0
2194
0
  JS::Rooted<JS::Value> exn(aCx);
2195
0
  if (!ToJSValue(aCx, mScriptLoader.mRv, &exn)) {
2196
0
    return;
2197
0
  }
2198
0
2199
0
  // Now the exception state should all be in exn.
2200
0
  MOZ_ASSERT(!JS_IsExceptionPending(aCx));
2201
0
  MOZ_ASSERT(!mScriptLoader.mRv.Failed());
2202
0
2203
0
  js::ErrorReport report(aCx);
2204
0
  if (!report.init(aCx, exn, js::ErrorReport::WithSideEffects)) {
2205
0
    JS_ClearPendingException(aCx);
2206
0
    return;
2207
0
  }
2208
0
2209
0
  RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport();
2210
0
  xpcReport->Init(report.report(), report.toStringResult().c_str(),
2211
0
                  aWorkerPrivate->IsChromeWorker(), aWorkerPrivate->WindowID());
2212
0
2213
0
  RefPtr<AsyncErrorReporter> r = new AsyncErrorReporter(xpcReport);
2214
0
  NS_DispatchToMainThread(r);
2215
0
}
2216
2217
void
2218
LoadAllScripts(WorkerPrivate* aWorkerPrivate,
2219
               nsTArray<ScriptLoadInfo>& aLoadInfos, bool aIsMainScript,
2220
               WorkerScriptType aWorkerScriptType, ErrorResult& aRv)
2221
0
{
2222
0
  aWorkerPrivate->AssertIsOnWorkerThread();
2223
0
  NS_ASSERTION(!aLoadInfos.IsEmpty(), "Bad arguments!");
2224
0
2225
0
  AutoSyncLoopHolder syncLoop(aWorkerPrivate, Canceling);
2226
0
  nsCOMPtr<nsIEventTarget> syncLoopTarget = syncLoop.GetEventTarget();
2227
0
  if (!syncLoopTarget) {
2228
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2229
0
    return;
2230
0
  }
2231
0
2232
0
  Maybe<ClientInfo> clientInfo;
2233
0
  Maybe<ServiceWorkerDescriptor> controller;
2234
0
  if (!aIsMainScript) {
2235
0
    clientInfo = aWorkerPrivate->GetClientInfo();
2236
0
    controller = aWorkerPrivate->GetController();
2237
0
  }
2238
0
2239
0
  RefPtr<ScriptLoaderRunnable> loader =
2240
0
    new ScriptLoaderRunnable(aWorkerPrivate, syncLoopTarget, aLoadInfos,
2241
0
                             clientInfo, controller,
2242
0
                             aIsMainScript, aWorkerScriptType, aRv);
2243
0
2244
0
  NS_ASSERTION(aLoadInfos.IsEmpty(), "Should have swapped!");
2245
0
2246
0
  RefPtr<StrongWorkerRef> workerRef =
2247
0
    StrongWorkerRef::Create(aWorkerPrivate, "ScriptLoader", [loader]() {
2248
0
      NS_DispatchToMainThread(NewRunnableMethod("ScriptLoader::CancelMainThreadWithBindingAborted",
2249
0
                                                loader,
2250
0
                                                &ScriptLoaderRunnable::CancelMainThreadWithBindingAborted));
2251
0
    });
2252
0
2253
0
  if (NS_WARN_IF(!workerRef)) {
2254
0
    aRv.Throw(NS_ERROR_FAILURE);
2255
0
    return;
2256
0
  }
2257
0
2258
0
  if (NS_FAILED(NS_DispatchToMainThread(loader))) {
2259
0
    NS_ERROR("Failed to dispatch!");
2260
0
    aRv.Throw(NS_ERROR_FAILURE);
2261
0
    return;
2262
0
  }
2263
0
2264
0
  syncLoop.Run();
2265
0
}
2266
2267
} /* anonymous namespace */
2268
2269
namespace workerinternals {
2270
2271
nsresult
2272
ChannelFromScriptURLMainThread(nsIPrincipal* aPrincipal,
2273
                               nsIURI* aBaseURI,
2274
                               nsIDocument* aParentDoc,
2275
                               nsILoadGroup* aLoadGroup,
2276
                               const nsAString& aScriptURL,
2277
                               const Maybe<ClientInfo>& aClientInfo,
2278
                               nsContentPolicyType aMainScriptContentPolicyType,
2279
                               bool aDefaultURIEncoding,
2280
                               nsIChannel** aChannel)
2281
0
{
2282
0
  AssertIsOnMainThread();
2283
0
2284
0
  nsCOMPtr<nsIIOService> ios(do_GetIOService());
2285
0
2286
0
  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
2287
0
  NS_ASSERTION(secMan, "This should never be null!");
2288
0
2289
0
  return ChannelFromScriptURL(aPrincipal, aBaseURI, aParentDoc, nullptr,
2290
0
                              aLoadGroup, ios, secMan, aScriptURL, aClientInfo,
2291
0
                              Maybe<ServiceWorkerDescriptor>(),
2292
0
                              true, WorkerScript, aMainScriptContentPolicyType,
2293
0
                              nsIRequest::LOAD_NORMAL, aDefaultURIEncoding,
2294
0
                              aChannel);
2295
0
}
2296
2297
nsresult
2298
ChannelFromScriptURLWorkerThread(JSContext* aCx,
2299
                                 WorkerPrivate* aParent,
2300
                                 const nsAString& aScriptURL,
2301
                                 WorkerLoadInfo& aLoadInfo)
2302
0
{
2303
0
  aParent->AssertIsOnWorkerThread();
2304
0
2305
0
  RefPtr<ChannelGetterRunnable> getter =
2306
0
    new ChannelGetterRunnable(aParent, aScriptURL, aLoadInfo);
2307
0
2308
0
  ErrorResult rv;
2309
0
  getter->Dispatch(Canceling, rv);
2310
0
  if (rv.Failed()) {
2311
0
    NS_ERROR("Failed to dispatch!");
2312
0
    return rv.StealNSResult();
2313
0
  }
2314
0
2315
0
  return getter->GetResult();
2316
0
}
2317
2318
void ReportLoadError(ErrorResult& aRv, nsresult aLoadResult,
2319
                     const nsAString& aScriptURL)
2320
0
{
2321
0
  MOZ_ASSERT(!aRv.Failed());
2322
0
2323
0
  switch (aLoadResult) {
2324
0
    case NS_ERROR_FILE_NOT_FOUND:
2325
0
    case NS_ERROR_NOT_AVAILABLE:
2326
0
      aLoadResult = NS_ERROR_DOM_NETWORK_ERR;
2327
0
      break;
2328
0
2329
0
    case NS_ERROR_MALFORMED_URI:
2330
0
      aLoadResult = NS_ERROR_DOM_SYNTAX_ERR;
2331
0
      break;
2332
0
2333
0
    case NS_BINDING_ABORTED:
2334
0
      // Note: we used to pretend like we didn't set an exception for
2335
0
      // NS_BINDING_ABORTED, but then ShutdownScriptLoader did it anyway.  The
2336
0
      // other callsite, in WorkerPrivate::Constructor, never passed in
2337
0
      // NS_BINDING_ABORTED.  So just throw it directly here.  Consumers will
2338
0
      // deal as needed.  But note that we do NOT want to ThrowDOMException()
2339
0
      // for this case, because that will make it impossible for consumers to
2340
0
      // realize that our error was NS_BINDING_ABORTED.
2341
0
      aRv.Throw(aLoadResult);
2342
0
      return;
2343
0
2344
0
    case NS_ERROR_DOM_SECURITY_ERR:
2345
0
    case NS_ERROR_DOM_SYNTAX_ERR:
2346
0
      break;
2347
0
2348
0
    case NS_ERROR_DOM_BAD_URI:
2349
0
      // This is actually a security error.
2350
0
      aLoadResult = NS_ERROR_DOM_SECURITY_ERR;
2351
0
      break;
2352
0
2353
0
    default:
2354
0
      // For lack of anything better, go ahead and throw a NetworkError here.
2355
0
      // We don't want to throw a JS exception, because for toplevel script
2356
0
      // loads that would get squelched.
2357
0
      aRv.ThrowDOMException(NS_ERROR_DOM_NETWORK_ERR,
2358
0
        nsPrintfCString("Failed to load worker script at %s (nsresult = 0x%" PRIx32 ")",
2359
0
                        NS_ConvertUTF16toUTF8(aScriptURL).get(),
2360
0
                        static_cast<uint32_t>(aLoadResult)));
2361
0
      return;
2362
0
  }
2363
0
2364
0
  aRv.ThrowDOMException(aLoadResult,
2365
0
                        NS_LITERAL_CSTRING("Failed to load worker script at \"") +
2366
0
                        NS_ConvertUTF16toUTF8(aScriptURL) +
2367
0
                        NS_LITERAL_CSTRING("\""));
2368
0
}
2369
2370
void
2371
LoadMainScript(WorkerPrivate* aWorkerPrivate,
2372
               const nsAString& aScriptURL,
2373
               WorkerScriptType aWorkerScriptType,
2374
               ErrorResult& aRv)
2375
0
{
2376
0
  nsTArray<ScriptLoadInfo> loadInfos;
2377
0
2378
0
  ScriptLoadInfo* info = loadInfos.AppendElement();
2379
0
  info->mURL = aScriptURL;
2380
0
  info->mLoadFlags = aWorkerPrivate->GetLoadFlags();
2381
0
2382
0
  // We are loading the main script, so the worker's Client must be
2383
0
  // reserved.
2384
0
  info->mReservedClientInfo = aWorkerPrivate->GetClientInfo();
2385
0
2386
0
  LoadAllScripts(aWorkerPrivate, loadInfos, true, aWorkerScriptType, aRv);
2387
0
}
2388
2389
void
2390
Load(WorkerPrivate* aWorkerPrivate,
2391
     const nsTArray<nsString>& aScriptURLs, WorkerScriptType aWorkerScriptType,
2392
     ErrorResult& aRv)
2393
0
{
2394
0
  const uint32_t urlCount = aScriptURLs.Length();
2395
0
2396
0
  if (!urlCount) {
2397
0
    return;
2398
0
  }
2399
0
2400
0
  if (urlCount > MAX_CONCURRENT_SCRIPTS) {
2401
0
    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
2402
0
    return;
2403
0
  }
2404
0
2405
0
  nsTArray<ScriptLoadInfo> loadInfos;
2406
0
  loadInfos.SetLength(urlCount);
2407
0
2408
0
  for (uint32_t index = 0; index < urlCount; index++) {
2409
0
    loadInfos[index].mURL = aScriptURLs[index];
2410
0
    loadInfos[index].mLoadFlags = aWorkerPrivate->GetLoadFlags();
2411
0
  }
2412
0
2413
0
  LoadAllScripts(aWorkerPrivate, loadInfos, false, aWorkerScriptType, aRv);
2414
0
}
2415
2416
} // namespace workerinternals
2417
2418
} // dom namespace
2419
} // mozilla namespace