Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/serviceworkers/ServiceWorkerRegistrar.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 "ServiceWorkerRegistrar.h"
8
#include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
9
#include "mozilla/dom/DOMException.h"
10
#include "mozilla/net/MozURL.h"
11
12
#include "nsIEventTarget.h"
13
#include "nsIInputStream.h"
14
#include "nsILineInputStream.h"
15
#include "nsIObserverService.h"
16
#include "nsIOutputStream.h"
17
#include "nsISafeOutputStream.h"
18
#include "nsIServiceWorkerManager.h"
19
20
#include "MainThreadUtils.h"
21
#include "mozilla/ClearOnShutdown.h"
22
#include "mozilla/CycleCollectedJSContext.h"
23
#include "mozilla/dom/StorageActivityService.h"
24
#include "mozilla/ErrorNames.h"
25
#include "mozilla/ipc/BackgroundChild.h"
26
#include "mozilla/ipc/BackgroundParent.h"
27
#include "mozilla/ipc/PBackgroundChild.h"
28
#include "mozilla/ModuleUtils.h"
29
#include "mozilla/Services.h"
30
#include "mozilla/StaticPtr.h"
31
#include "mozJSComponentLoader.h"
32
#include "nsAppDirectoryServiceDefs.h"
33
#include "nsContentUtils.h"
34
#include "nsDirectoryServiceUtils.h"
35
#include "nsNetCID.h"
36
#include "nsNetUtil.h"
37
#include "nsServiceManagerUtils.h"
38
#include "nsThreadUtils.h"
39
#include "nsXULAppAPI.h"
40
#include "ServiceWorkerUtils.h"
41
42
using namespace mozilla::ipc;
43
44
namespace mozilla {
45
namespace dom {
46
47
namespace {
48
49
static const char* gSupportedRegistrarVersions[] = {
50
  SERVICEWORKERREGISTRAR_VERSION,
51
  "7",
52
  "6",
53
  "5",
54
  "4",
55
  "3",
56
  "2"
57
};
58
59
static const uint32_t kInvalidGeneration = static_cast<uint32_t>(-1);
60
61
StaticRefPtr<ServiceWorkerRegistrar> gServiceWorkerRegistrar;
62
63
nsresult
64
GetOrigin(const nsACString& aURL, nsACString& aOrigin)
65
0
{
66
0
  RefPtr<net::MozURL> url;
67
0
  nsresult rv = net::MozURL::Init(getter_AddRefs(url), aURL);
68
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
69
0
    return rv;
70
0
  }
71
0
72
0
  url->Origin(aOrigin);
73
0
  return NS_OK;
74
0
}
75
76
nsresult
77
ReadLine(nsILineInputStream* aStream, nsACString& aValue)
78
0
{
79
0
  bool hasMoreLines;
80
0
  nsresult rv = aStream->ReadLine(aValue, &hasMoreLines);
81
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
82
0
    return rv;
83
0
  }
84
0
85
0
  if (NS_WARN_IF(!hasMoreLines)) {
86
0
    return NS_ERROR_FAILURE;
87
0
  }
88
0
89
0
  return NS_OK;
90
0
}
91
92
nsresult
93
CreatePrincipalInfo(nsILineInputStream* aStream,
94
                    ServiceWorkerRegistrationData* aEntry,
95
                    bool aSkipSpec = false)
96
0
{
97
0
  nsAutoCString suffix;
98
0
  nsresult rv = ReadLine(aStream, suffix);
99
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
100
0
    return rv;
101
0
  }
102
0
103
0
  OriginAttributes attrs;
104
0
  if (!attrs.PopulateFromSuffix(suffix)) {
105
0
    return NS_ERROR_INVALID_ARG;
106
0
  }
107
0
108
0
  if (aSkipSpec) {
109
0
    nsAutoCString unused;
110
0
    nsresult rv = ReadLine(aStream, unused);
111
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
112
0
      return rv;
113
0
    }
114
0
  }
115
0
116
0
  rv = ReadLine(aStream, aEntry->scope());
117
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
118
0
    return rv;
119
0
  }
120
0
121
0
  nsCString origin;
122
0
  rv = GetOrigin(aEntry->scope(), origin);
123
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
124
0
    return rv;
125
0
  }
126
0
127
0
  aEntry->principal() =
128
0
    mozilla::ipc::ContentPrincipalInfo(attrs, origin, aEntry->scope());
129
0
130
0
  return NS_OK;
131
0
}
132
133
} // namespace
134
135
NS_IMPL_ISUPPORTS(ServiceWorkerRegistrar,
136
                  nsIObserver,
137
                  nsIAsyncShutdownBlocker)
138
139
void
140
ServiceWorkerRegistrar::Initialize()
141
3
{
142
3
  MOZ_ASSERT(!gServiceWorkerRegistrar);
143
3
144
3
  if (!XRE_IsParentProcess()) {
145
0
    return;
146
0
  }
147
3
148
3
  gServiceWorkerRegistrar = new ServiceWorkerRegistrar();
149
3
  ClearOnShutdown(&gServiceWorkerRegistrar);
150
3
151
3
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
152
3
  if (obs) {
153
3
    DebugOnly<nsresult> rv = obs->AddObserver(gServiceWorkerRegistrar,
154
3
                                              "profile-after-change", false);
155
3
    MOZ_ASSERT(NS_SUCCEEDED(rv));
156
3
  }
157
3
}
158
159
/* static */ already_AddRefed<ServiceWorkerRegistrar>
160
ServiceWorkerRegistrar::Get()
161
0
{
162
0
  MOZ_ASSERT(XRE_IsParentProcess());
163
0
164
0
  MOZ_ASSERT(gServiceWorkerRegistrar);
165
0
  RefPtr<ServiceWorkerRegistrar> service = gServiceWorkerRegistrar.get();
166
0
  return service.forget();
167
0
}
168
169
ServiceWorkerRegistrar::ServiceWorkerRegistrar()
170
  : mMonitor("ServiceWorkerRegistrar.mMonitor")
171
  , mDataLoaded(false)
172
  , mDataGeneration(kInvalidGeneration)
173
  , mFileGeneration(kInvalidGeneration)
174
  , mRetryCount(0)
175
  , mShuttingDown(false)
176
  , mRunnableDispatched(false)
177
3
{
178
3
  MOZ_ASSERT(NS_IsMainThread());
179
3
}
180
181
ServiceWorkerRegistrar::~ServiceWorkerRegistrar()
182
0
{
183
0
  MOZ_ASSERT(!mRunnableDispatched);
184
0
}
185
186
void
187
ServiceWorkerRegistrar::GetRegistrations(
188
                               nsTArray<ServiceWorkerRegistrationData>& aValues)
189
0
{
190
0
  MOZ_ASSERT(NS_IsMainThread());
191
0
  MOZ_ASSERT(aValues.IsEmpty());
192
0
193
0
  MonitorAutoLock lock(mMonitor);
194
0
195
0
  // If we don't have the profile directory, profile is not started yet (and
196
0
  // probably we are in a utest).
197
0
  if (!mProfileDir) {
198
0
    return;
199
0
  }
200
0
201
0
  // We care just about the first execution because this can be blocked by
202
0
  // loading data from disk.
203
0
  static bool firstTime = true;
204
0
  TimeStamp startTime;
205
0
206
0
  if (firstTime) {
207
0
    startTime = TimeStamp::NowLoRes();
208
0
  }
209
0
210
0
  // Waiting for data loaded.
211
0
  mMonitor.AssertCurrentThreadOwns();
212
0
  while (!mDataLoaded) {
213
0
    mMonitor.Wait();
214
0
  }
215
0
216
0
  aValues.AppendElements(mData);
217
0
218
0
  MaybeResetGeneration();
219
0
  MOZ_DIAGNOSTIC_ASSERT(mDataGeneration != kInvalidGeneration);
220
0
  MOZ_DIAGNOSTIC_ASSERT(mFileGeneration != kInvalidGeneration);
221
0
222
0
  if (firstTime) {
223
0
    firstTime = false;
224
0
    Telemetry::AccumulateTimeDelta(
225
0
      Telemetry::SERVICE_WORKER_REGISTRATION_LOADING,
226
0
      startTime);
227
0
  }
228
0
}
229
230
namespace {
231
232
bool Equivalent(const ServiceWorkerRegistrationData& aLeft,
233
                const ServiceWorkerRegistrationData& aRight)
234
0
{
235
0
  MOZ_ASSERT(aLeft.principal().type() ==
236
0
             mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
237
0
  MOZ_ASSERT(aRight.principal().type() ==
238
0
             mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
239
0
240
0
  const auto& leftPrincipal = aLeft.principal().get_ContentPrincipalInfo();
241
0
  const auto& rightPrincipal = aRight.principal().get_ContentPrincipalInfo();
242
0
243
0
  // Only compare the attributes, not the spec part of the principal.
244
0
  // The scope comparison above already covers the origin and codebase
245
0
  // principals include the full path in their spec which is not what
246
0
  // we want here.
247
0
  return aLeft.scope() == aRight.scope() &&
248
0
         leftPrincipal.attrs() == rightPrincipal.attrs();
249
0
}
250
251
} // anonymous namespace
252
253
void
254
ServiceWorkerRegistrar::RegisterServiceWorker(
255
                                     const ServiceWorkerRegistrationData& aData)
256
0
{
257
0
  AssertIsOnBackgroundThread();
258
0
259
0
  if (mShuttingDown) {
260
0
    NS_WARNING("Failed to register a serviceWorker during shutting down.");
261
0
    return;
262
0
  }
263
0
264
0
  {
265
0
    MonitorAutoLock lock(mMonitor);
266
0
    MOZ_ASSERT(mDataLoaded);
267
0
    RegisterServiceWorkerInternal(aData);
268
0
  }
269
0
270
0
  MaybeScheduleSaveData();
271
0
  StorageActivityService::SendActivity(aData.principal());
272
0
}
273
274
void
275
ServiceWorkerRegistrar::UnregisterServiceWorker(
276
                                            const PrincipalInfo& aPrincipalInfo,
277
                                            const nsACString& aScope)
278
0
{
279
0
  AssertIsOnBackgroundThread();
280
0
281
0
  if (mShuttingDown) {
282
0
    NS_WARNING("Failed to unregister a serviceWorker during shutting down.");
283
0
    return;
284
0
  }
285
0
286
0
  bool deleted = false;
287
0
288
0
  {
289
0
    MonitorAutoLock lock(mMonitor);
290
0
    MOZ_ASSERT(mDataLoaded);
291
0
292
0
    ServiceWorkerRegistrationData tmp;
293
0
    tmp.principal() = aPrincipalInfo;
294
0
    tmp.scope() = aScope;
295
0
296
0
    for (uint32_t i = 0; i < mData.Length(); ++i) {
297
0
      if (Equivalent(tmp, mData[i])) {
298
0
        mData.RemoveElementAt(i);
299
0
        mDataGeneration = GetNextGeneration();
300
0
        deleted = true;
301
0
        break;
302
0
      }
303
0
    }
304
0
  }
305
0
306
0
  if (deleted) {
307
0
    MaybeScheduleSaveData();
308
0
    StorageActivityService::SendActivity(aPrincipalInfo);
309
0
  }
310
0
}
311
312
void
313
ServiceWorkerRegistrar::RemoveAll()
314
0
{
315
0
  AssertIsOnBackgroundThread();
316
0
317
0
  if (mShuttingDown) {
318
0
    NS_WARNING("Failed to remove all the serviceWorkers during shutting down.");
319
0
    return;
320
0
  }
321
0
322
0
  bool deleted = false;
323
0
324
0
  nsTArray<ServiceWorkerRegistrationData> data;
325
0
  {
326
0
    MonitorAutoLock lock(mMonitor);
327
0
    MOZ_ASSERT(mDataLoaded);
328
0
329
0
    // Let's take a copy in order to inform StorageActivityService.
330
0
    data = mData;
331
0
332
0
    deleted = !mData.IsEmpty();
333
0
    mData.Clear();
334
0
335
0
    mDataGeneration = GetNextGeneration();
336
0
  }
337
0
338
0
  if (!deleted) {
339
0
    return;
340
0
  }
341
0
342
0
  MaybeScheduleSaveData();
343
0
344
0
  for (uint32_t i = 0, len = data.Length(); i < len; ++i) {
345
0
    StorageActivityService::SendActivity(data[i].principal());
346
0
  }
347
0
}
348
349
void
350
ServiceWorkerRegistrar::LoadData()
351
0
{
352
0
  MOZ_ASSERT(!NS_IsMainThread());
353
0
  MOZ_ASSERT(!mDataLoaded);
354
0
355
0
  nsresult rv = ReadData();
356
0
357
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
358
0
    DeleteData();
359
0
    // Also if the reading failed we have to notify what is waiting for data.
360
0
  }
361
0
362
0
  MonitorAutoLock lock(mMonitor);
363
0
  MOZ_ASSERT(!mDataLoaded);
364
0
  mDataLoaded = true;
365
0
  mMonitor.Notify();
366
0
}
367
368
nsresult
369
ServiceWorkerRegistrar::ReadData()
370
0
{
371
0
  // We cannot assert about the correct thread because normally this method
372
0
  // runs on a IO thread, but in gTests we call it from the main-thread.
373
0
374
0
  nsCOMPtr<nsIFile> file;
375
0
376
0
  {
377
0
    MonitorAutoLock lock(mMonitor);
378
0
379
0
    if (!mProfileDir) {
380
0
      return NS_ERROR_FAILURE;
381
0
    }
382
0
383
0
    nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
384
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
385
0
      return rv;
386
0
    }
387
0
  }
388
0
389
0
  nsresult rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
390
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
391
0
    return rv;
392
0
  }
393
0
394
0
  bool exists;
395
0
  rv = file->Exists(&exists);
396
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
397
0
    return rv;
398
0
  }
399
0
400
0
  if (!exists) {
401
0
    return NS_OK;
402
0
  }
403
0
404
0
  nsCOMPtr<nsIInputStream> stream;
405
0
  rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
406
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
407
0
    return rv;
408
0
  }
409
0
410
0
  nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(stream);
411
0
  MOZ_ASSERT(lineInputStream);
412
0
413
0
  nsAutoCString version;
414
0
  bool hasMoreLines;
415
0
  rv = lineInputStream->ReadLine(version, &hasMoreLines);
416
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
417
0
    return rv;
418
0
  }
419
0
420
0
  if (!IsSupportedVersion(version)) {
421
0
    nsContentUtils::LogMessageToConsole(nsPrintfCString(
422
0
      "Unsupported service worker registrar version: %s", version.get()).get());
423
0
    return NS_ERROR_FAILURE;
424
0
  }
425
0
426
0
  nsTArray<ServiceWorkerRegistrationData> tmpData;
427
0
428
0
  bool overwrite = false;
429
0
  bool dedupe = false;
430
0
  while (hasMoreLines) {
431
0
    ServiceWorkerRegistrationData* entry = tmpData.AppendElement();
432
0
433
0
#define GET_LINE(x)                                   \
434
0
    rv = lineInputStream->ReadLine(x, &hasMoreLines); \
435
0
    if (NS_WARN_IF(NS_FAILED(rv))) {                  \
436
0
      return rv;                                      \
437
0
    }                                                 \
438
0
    if (NS_WARN_IF(!hasMoreLines)) {                  \
439
0
      return NS_ERROR_FAILURE;                        \
440
0
    }
441
0
442
0
    nsAutoCString line;
443
0
    if (version.EqualsLiteral(SERVICEWORKERREGISTRAR_VERSION)) {
444
0
      rv = CreatePrincipalInfo(lineInputStream, entry);
445
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
446
0
        return rv;
447
0
      }
448
0
449
0
      GET_LINE(entry->currentWorkerURL());
450
0
451
0
      nsAutoCString fetchFlag;
452
0
      GET_LINE(fetchFlag);
453
0
      if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
454
0
          !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
455
0
        return NS_ERROR_INVALID_ARG;
456
0
      }
457
0
      entry->currentWorkerHandlesFetch() =
458
0
        fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
459
0
460
0
      nsAutoCString cacheName;
461
0
      GET_LINE(cacheName);
462
0
      CopyUTF8toUTF16(cacheName, entry->cacheName());
463
0
464
0
      nsAutoCString updateViaCache;
465
0
      GET_LINE(updateViaCache);
466
0
      entry->updateViaCache() = updateViaCache.ToInteger(&rv, 16);
467
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
468
0
        return rv;
469
0
      } else if (entry->updateViaCache() > nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_NONE) {
470
0
        return NS_ERROR_INVALID_ARG;
471
0
      }
472
0
473
0
      nsAutoCString installedTimeStr;
474
0
      GET_LINE(installedTimeStr);
475
0
      int64_t installedTime = installedTimeStr.ToInteger64(&rv);
476
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
477
0
        return rv;
478
0
      }
479
0
      entry->currentWorkerInstalledTime() = installedTime;
480
0
481
0
      nsAutoCString activatedTimeStr;
482
0
      GET_LINE(activatedTimeStr);
483
0
      int64_t activatedTime = activatedTimeStr.ToInteger64(&rv);
484
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
485
0
        return rv;
486
0
      }
487
0
      entry->currentWorkerActivatedTime() = activatedTime;
488
0
489
0
      nsAutoCString lastUpdateTimeStr;
490
0
      GET_LINE(lastUpdateTimeStr);
491
0
      int64_t lastUpdateTime = lastUpdateTimeStr.ToInteger64(&rv);
492
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
493
0
        return rv;
494
0
      }
495
0
      entry->lastUpdateTime() = lastUpdateTime;
496
0
    } else if (version.EqualsLiteral("7")) {
497
0
      rv = CreatePrincipalInfo(lineInputStream, entry);
498
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
499
0
        return rv;
500
0
      }
501
0
502
0
      GET_LINE(entry->currentWorkerURL());
503
0
504
0
      nsAutoCString fetchFlag;
505
0
      GET_LINE(fetchFlag);
506
0
      if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
507
0
          !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
508
0
        return NS_ERROR_INVALID_ARG;
509
0
      }
510
0
      entry->currentWorkerHandlesFetch() =
511
0
        fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
512
0
513
0
      nsAutoCString cacheName;
514
0
      GET_LINE(cacheName);
515
0
      CopyUTF8toUTF16(cacheName, entry->cacheName());
516
0
517
0
      nsAutoCString loadFlags;
518
0
      GET_LINE(loadFlags);
519
0
      entry->updateViaCache() =
520
0
        loadFlags.ToInteger(&rv, 16) == nsIRequest::LOAD_NORMAL
521
0
          ? nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_ALL
522
0
          : nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
523
0
524
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
525
0
        return rv;
526
0
      }
527
0
528
0
      nsAutoCString installedTimeStr;
529
0
      GET_LINE(installedTimeStr);
530
0
      int64_t installedTime = installedTimeStr.ToInteger64(&rv);
531
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
532
0
        return rv;
533
0
      }
534
0
      entry->currentWorkerInstalledTime() = installedTime;
535
0
536
0
      nsAutoCString activatedTimeStr;
537
0
      GET_LINE(activatedTimeStr);
538
0
      int64_t activatedTime = activatedTimeStr.ToInteger64(&rv);
539
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
540
0
        return rv;
541
0
      }
542
0
      entry->currentWorkerActivatedTime() = activatedTime;
543
0
544
0
      nsAutoCString lastUpdateTimeStr;
545
0
      GET_LINE(lastUpdateTimeStr);
546
0
      int64_t lastUpdateTime = lastUpdateTimeStr.ToInteger64(&rv);
547
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
548
0
        return rv;
549
0
      }
550
0
      entry->lastUpdateTime() = lastUpdateTime;
551
0
    } else if (version.EqualsLiteral("6")) {
552
0
      rv = CreatePrincipalInfo(lineInputStream, entry);
553
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
554
0
        return rv;
555
0
      }
556
0
557
0
      GET_LINE(entry->currentWorkerURL());
558
0
559
0
      nsAutoCString fetchFlag;
560
0
      GET_LINE(fetchFlag);
561
0
      if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
562
0
          !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
563
0
        return NS_ERROR_INVALID_ARG;
564
0
      }
565
0
      entry->currentWorkerHandlesFetch() =
566
0
        fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
567
0
568
0
      nsAutoCString cacheName;
569
0
      GET_LINE(cacheName);
570
0
      CopyUTF8toUTF16(cacheName, entry->cacheName());
571
0
572
0
      nsAutoCString loadFlags;
573
0
      GET_LINE(loadFlags);
574
0
      entry->updateViaCache() =
575
0
        loadFlags.ToInteger(&rv, 16) == nsIRequest::LOAD_NORMAL
576
0
          ? nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_ALL
577
0
          : nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
578
0
579
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
580
0
        return rv;
581
0
      }
582
0
583
0
      entry->currentWorkerInstalledTime() = 0;
584
0
      entry->currentWorkerActivatedTime() = 0;
585
0
      entry->lastUpdateTime() = 0;
586
0
    } else if (version.EqualsLiteral("5")) {
587
0
      overwrite = true;
588
0
      dedupe = true;
589
0
590
0
      rv = CreatePrincipalInfo(lineInputStream, entry);
591
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
592
0
        return rv;
593
0
      }
594
0
595
0
      GET_LINE(entry->currentWorkerURL());
596
0
597
0
      nsAutoCString fetchFlag;
598
0
      GET_LINE(fetchFlag);
599
0
      if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
600
0
          !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
601
0
        return NS_ERROR_INVALID_ARG;
602
0
      }
603
0
      entry->currentWorkerHandlesFetch() =
604
0
        fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
605
0
606
0
      nsAutoCString cacheName;
607
0
      GET_LINE(cacheName);
608
0
      CopyUTF8toUTF16(cacheName, entry->cacheName());
609
0
610
0
      entry->updateViaCache() =
611
0
        nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
612
0
613
0
      entry->currentWorkerInstalledTime() = 0;
614
0
      entry->currentWorkerActivatedTime() = 0;
615
0
      entry->lastUpdateTime() = 0;
616
0
    } else if (version.EqualsLiteral("4")) {
617
0
      overwrite = true;
618
0
      dedupe = true;
619
0
620
0
      rv = CreatePrincipalInfo(lineInputStream, entry);
621
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
622
0
        return rv;
623
0
      }
624
0
625
0
      GET_LINE(entry->currentWorkerURL());
626
0
627
0
      // default handlesFetch flag to Enabled
628
0
      entry->currentWorkerHandlesFetch() = true;
629
0
630
0
      nsAutoCString cacheName;
631
0
      GET_LINE(cacheName);
632
0
      CopyUTF8toUTF16(cacheName, entry->cacheName());
633
0
634
0
      entry->updateViaCache() =
635
0
        nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
636
0
637
0
      entry->currentWorkerInstalledTime() = 0;
638
0
      entry->currentWorkerActivatedTime() = 0;
639
0
      entry->lastUpdateTime() = 0;
640
0
    } else if (version.EqualsLiteral("3")) {
641
0
      overwrite = true;
642
0
      dedupe = true;
643
0
644
0
      rv = CreatePrincipalInfo(lineInputStream, entry, true);
645
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
646
0
        return rv;
647
0
      }
648
0
649
0
      GET_LINE(entry->currentWorkerURL());
650
0
651
0
      // default handlesFetch flag to Enabled
652
0
      entry->currentWorkerHandlesFetch() = true;
653
0
654
0
      nsAutoCString cacheName;
655
0
      GET_LINE(cacheName);
656
0
      CopyUTF8toUTF16(cacheName, entry->cacheName());
657
0
658
0
      entry->updateViaCache() =
659
0
        nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
660
0
661
0
      entry->currentWorkerInstalledTime() = 0;
662
0
      entry->currentWorkerActivatedTime() = 0;
663
0
      entry->lastUpdateTime() = 0;
664
0
    } else if (version.EqualsLiteral("2")) {
665
0
      overwrite = true;
666
0
      dedupe = true;
667
0
668
0
      rv = CreatePrincipalInfo(lineInputStream, entry, true);
669
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
670
0
        return rv;
671
0
      }
672
0
673
0
      // scriptSpec is no more used in latest version.
674
0
      nsAutoCString unused;
675
0
      GET_LINE(unused);
676
0
677
0
      GET_LINE(entry->currentWorkerURL());
678
0
679
0
      // default handlesFetch flag to Enabled
680
0
      entry->currentWorkerHandlesFetch() = true;
681
0
682
0
      nsAutoCString cacheName;
683
0
      GET_LINE(cacheName);
684
0
      CopyUTF8toUTF16(cacheName, entry->cacheName());
685
0
686
0
      // waitingCacheName is no more used in latest version.
687
0
      GET_LINE(unused);
688
0
689
0
      entry->updateViaCache() =
690
0
        nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
691
0
692
0
      entry->currentWorkerInstalledTime() = 0;
693
0
      entry->currentWorkerActivatedTime() = 0;
694
0
      entry->lastUpdateTime() = 0;
695
0
    } else {
696
0
      MOZ_ASSERT_UNREACHABLE("Should never get here!");
697
0
    }
698
0
699
0
#undef GET_LINE
700
0
701
0
    rv = lineInputStream->ReadLine(line, &hasMoreLines);
702
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
703
0
      return rv;
704
0
    }
705
0
706
0
    if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_TERMINATOR)) {
707
0
      return NS_ERROR_FAILURE;
708
0
    }
709
0
  }
710
0
711
0
  stream->Close();
712
0
713
0
  // XXX: The following code is writing to mData without holding a
714
0
  //      monitor lock.  This might be ok since this is currently
715
0
  //      only called at startup where we block the main thread
716
0
  //      preventing further operation until it completes.  We should
717
0
  //      consider better locking here in the future.
718
0
719
0
  // Copy data over to mData.
720
0
  for (uint32_t i = 0; i < tmpData.Length(); ++i) {
721
0
    // Older versions could sometimes write out empty, useless entries.
722
0
    // Prune those here.
723
0
    if (!ServiceWorkerRegistrationDataIsValid(tmpData[i])) {
724
0
      continue;
725
0
    }
726
0
727
0
    bool match = false;
728
0
    if (dedupe) {
729
0
      MOZ_ASSERT(overwrite);
730
0
      // If this is an old profile, then we might need to deduplicate.  In
731
0
      // theory this can be removed in the future (Bug 1248449)
732
0
      for (uint32_t j = 0; j < mData.Length(); ++j) {
733
0
        // Use same comparison as RegisterServiceWorker. Scope contains
734
0
        // basic origin information.  Combine with any principal attributes.
735
0
        if (Equivalent(tmpData[i], mData[j])) {
736
0
          // Last match wins, just like legacy loading used to do in
737
0
          // the ServiceWorkerManager.
738
0
          mData[j] = tmpData[i];
739
0
          // Dupe found, so overwrite file with reduced list.
740
0
          match = true;
741
0
          break;
742
0
        }
743
0
      }
744
0
    } else {
745
#ifdef DEBUG
746
      // Otherwise assert no duplications in debug builds.
747
      for (uint32_t j = 0; j < mData.Length(); ++j) {
748
        MOZ_ASSERT(!Equivalent(tmpData[i], mData[j]));
749
      }
750
#endif
751
    }
752
0
    if (!match) {
753
0
      mData.AppendElement(tmpData[i]);
754
0
    }
755
0
  }
756
0
757
0
  // Overwrite previous version.
758
0
  // Cannot call SaveData directly because gtest uses main-thread.
759
0
  if (overwrite && NS_FAILED(WriteData(mData))) {
760
0
    NS_WARNING("Failed to write data for the ServiceWorker Registations.");
761
0
    DeleteData();
762
0
  }
763
0
764
0
  return NS_OK;
765
0
}
766
767
void
768
ServiceWorkerRegistrar::DeleteData()
769
0
{
770
0
  // We cannot assert about the correct thread because normally this method
771
0
  // runs on a IO thread, but in gTests we call it from the main-thread.
772
0
773
0
  nsCOMPtr<nsIFile> file;
774
0
775
0
  {
776
0
    MonitorAutoLock lock(mMonitor);
777
0
    mData.Clear();
778
0
779
0
    if (!mProfileDir) {
780
0
      return;
781
0
    }
782
0
783
0
    nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
784
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
785
0
      return;
786
0
    }
787
0
  }
788
0
789
0
  nsresult rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
790
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
791
0
    return;
792
0
  }
793
0
794
0
  rv = file->Remove(false);
795
0
  if (rv == NS_ERROR_FILE_NOT_FOUND) {
796
0
    return;
797
0
  }
798
0
799
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
800
0
    return;
801
0
  }
802
0
}
803
804
void
805
ServiceWorkerRegistrar::RegisterServiceWorkerInternal(const ServiceWorkerRegistrationData& aData)
806
0
{
807
0
  bool found = false;
808
0
  for (uint32_t i = 0, len = mData.Length(); i < len; ++i) {
809
0
    if (Equivalent(aData, mData[i])) {
810
0
      mData[i] = aData;
811
0
      found = true;
812
0
      break;
813
0
    }
814
0
  }
815
0
816
0
  if (!found) {
817
0
    MOZ_ASSERT(ServiceWorkerRegistrationDataIsValid(aData));
818
0
    mData.AppendElement(aData);
819
0
  }
820
0
821
0
  mDataGeneration = GetNextGeneration();
822
0
}
823
824
class ServiceWorkerRegistrarSaveDataRunnable final : public Runnable
825
{
826
  nsCOMPtr<nsIEventTarget> mEventTarget;
827
  const nsTArray<ServiceWorkerRegistrationData> mData;
828
  const uint32_t mGeneration;
829
830
public:
831
  ServiceWorkerRegistrarSaveDataRunnable(nsTArray<ServiceWorkerRegistrationData>&& aData,
832
                                         uint32_t aGeneration)
833
    : Runnable("dom::ServiceWorkerRegistrarSaveDataRunnable")
834
    , mEventTarget(GetCurrentThreadEventTarget())
835
    , mData(std::move(aData))
836
    , mGeneration(aGeneration)
837
0
  {
838
0
    AssertIsOnBackgroundThread();
839
0
    MOZ_DIAGNOSTIC_ASSERT(mGeneration != kInvalidGeneration);
840
0
  }
841
842
  NS_IMETHOD
843
  Run() override
844
0
  {
845
0
    RefPtr<ServiceWorkerRegistrar> service = ServiceWorkerRegistrar::Get();
846
0
    MOZ_ASSERT(service);
847
0
848
0
    uint32_t fileGeneration = kInvalidGeneration;
849
0
850
0
    if (NS_SUCCEEDED(service->SaveData(mData))) {
851
0
      fileGeneration = mGeneration;
852
0
    }
853
0
854
0
    RefPtr<Runnable> runnable =
855
0
      NewRunnableMethod<uint32_t>("ServiceWorkerRegistrar::DataSaved",
856
0
                                  service,
857
0
                                  &ServiceWorkerRegistrar::DataSaved,
858
0
                                  fileGeneration);
859
0
    MOZ_ALWAYS_SUCCEEDS(
860
0
      mEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
861
0
862
0
    return NS_OK;
863
0
  }
864
};
865
866
void
867
ServiceWorkerRegistrar::MaybeScheduleSaveData()
868
0
{
869
0
  AssertIsOnBackgroundThread();
870
0
  MOZ_ASSERT(!mShuttingDown);
871
0
872
0
  if (mShuttingDown || mRunnableDispatched ||
873
0
      mDataGeneration <= mFileGeneration) {
874
0
    return;
875
0
  }
876
0
877
0
  nsCOMPtr<nsIEventTarget> target =
878
0
    do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
879
0
  MOZ_ASSERT(target, "Must have stream transport service");
880
0
881
0
  uint32_t generation = kInvalidGeneration;
882
0
  nsTArray<ServiceWorkerRegistrationData> data;
883
0
884
0
  {
885
0
    MonitorAutoLock lock(mMonitor);
886
0
    generation = mDataGeneration;
887
0
    data.AppendElements(mData);
888
0
  }
889
0
890
0
  RefPtr<Runnable> runnable =
891
0
    new ServiceWorkerRegistrarSaveDataRunnable(std::move(data), generation);
892
0
  nsresult rv = target->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
893
0
  NS_ENSURE_SUCCESS_VOID(rv);
894
0
895
0
  mRunnableDispatched = true;
896
0
}
897
898
void
899
ServiceWorkerRegistrar::ShutdownCompleted()
900
0
{
901
0
  MOZ_ASSERT(NS_IsMainThread());
902
0
903
0
  DebugOnly<nsresult> rv = GetShutdownPhase()->RemoveBlocker(this);
904
0
  MOZ_ASSERT(NS_SUCCEEDED(rv));
905
0
}
906
907
nsresult
908
ServiceWorkerRegistrar::SaveData(const nsTArray<ServiceWorkerRegistrationData>& aData)
909
0
{
910
0
  MOZ_ASSERT(!NS_IsMainThread());
911
0
912
0
  nsresult rv = WriteData(aData);
913
0
  if (NS_FAILED(rv)) {
914
0
    NS_WARNING("Failed to write data for the ServiceWorker Registations.");
915
0
    // Don't touch the file or in-memory state.  Writing files can
916
0
    // sometimes fail due to virus scanning, etc.  We should just leave
917
0
    // things as is so the next save operation can pick up any changes
918
0
    // without losing data.
919
0
  }
920
0
  return rv;
921
0
}
922
923
void
924
ServiceWorkerRegistrar::DataSaved(uint32_t aFileGeneration)
925
0
{
926
0
  AssertIsOnBackgroundThread();
927
0
  MOZ_ASSERT(mRunnableDispatched);
928
0
929
0
  mRunnableDispatched = false;
930
0
931
0
  // Check for shutdown before possibly triggering any more saves
932
0
  // runnables.
933
0
  MaybeScheduleShutdownCompleted();
934
0
  if (mShuttingDown) {
935
0
    return;
936
0
  }
937
0
938
0
  // If we got a valid generation, then the save was successful.
939
0
  if (aFileGeneration != kInvalidGeneration) {
940
0
    // Update the file generation.  We also check to see if we
941
0
    // can reset the generation back to zero if the file and data
942
0
    // are now in sync.  This allows us to avoid dealing with wrap
943
0
    // around of the generation count.
944
0
    mFileGeneration = aFileGeneration;
945
0
    MaybeResetGeneration();
946
0
947
0
    // Successful write resets the retry count.
948
0
    mRetryCount = 0;
949
0
950
0
    // Possibly schedule another save operation if more data
951
0
    // has come in while processing this one.
952
0
    MaybeScheduleSaveData();
953
0
954
0
    return;
955
0
  }
956
0
957
0
  // Otherwise, the save failed since the generation is invalid.  We
958
0
  // want to retry the save, but only a limited number of times.
959
0
  static const uint32_t kMaxRetryCount = 2;
960
0
  if (mRetryCount >= kMaxRetryCount) {
961
0
    return;
962
0
  }
963
0
964
0
  mRetryCount += 1;
965
0
  MaybeScheduleSaveData();
966
0
}
967
968
void
969
ServiceWorkerRegistrar::MaybeScheduleShutdownCompleted()
970
0
{
971
0
  AssertIsOnBackgroundThread();
972
0
973
0
  if (mRunnableDispatched || !mShuttingDown) {
974
0
    return;
975
0
  }
976
0
977
0
  RefPtr<Runnable> runnable =
978
0
    NewRunnableMethod("dom::ServiceWorkerRegistrar::ShutdownCompleted",
979
0
                      this,
980
0
                      &ServiceWorkerRegistrar::ShutdownCompleted);
981
0
  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable.forget()));
982
0
}
983
984
uint32_t
985
ServiceWorkerRegistrar::GetNextGeneration()
986
0
{
987
0
  uint32_t ret = mDataGeneration + 1;
988
0
  if (ret == kInvalidGeneration) {
989
0
    ret += 1;
990
0
  }
991
0
  return ret;
992
0
}
993
994
void
995
ServiceWorkerRegistrar::MaybeResetGeneration()
996
0
{
997
0
  if (mDataGeneration != mFileGeneration) {
998
0
    return;
999
0
  }
1000
0
  mDataGeneration = mFileGeneration = 0;
1001
0
}
1002
1003
bool
1004
ServiceWorkerRegistrar::IsSupportedVersion(const nsACString& aVersion) const
1005
0
{
1006
0
  uint32_t numVersions = ArrayLength(gSupportedRegistrarVersions);
1007
0
  for (uint32_t i = 0; i < numVersions; i++) {
1008
0
    if (aVersion.EqualsASCII(gSupportedRegistrarVersions[i])) {
1009
0
      return true;
1010
0
    }
1011
0
  }
1012
0
  return false;
1013
0
}
1014
1015
nsresult
1016
ServiceWorkerRegistrar::WriteData(const nsTArray<ServiceWorkerRegistrationData>& aData)
1017
0
{
1018
0
  // We cannot assert about the correct thread because normally this method
1019
0
  // runs on a IO thread, but in gTests we call it from the main-thread.
1020
0
1021
0
  nsCOMPtr<nsIFile> file;
1022
0
1023
0
  {
1024
0
    MonitorAutoLock lock(mMonitor);
1025
0
1026
0
    if (!mProfileDir) {
1027
0
      return NS_ERROR_FAILURE;
1028
0
    }
1029
0
1030
0
    nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
1031
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1032
0
      return rv;
1033
0
    }
1034
0
  }
1035
0
1036
0
  nsresult rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
1037
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1038
0
    return rv;
1039
0
  }
1040
0
1041
0
  nsCOMPtr<nsIOutputStream> stream;
1042
0
  rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(stream), file);
1043
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1044
0
    return rv;
1045
0
  }
1046
0
1047
0
  nsAutoCString buffer;
1048
0
  buffer.AppendLiteral(SERVICEWORKERREGISTRAR_VERSION);
1049
0
  buffer.Append('\n');
1050
0
1051
0
  uint32_t count;
1052
0
  rv = stream->Write(buffer.Data(), buffer.Length(), &count);
1053
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1054
0
    return rv;
1055
0
  }
1056
0
1057
0
  if (count != buffer.Length()) {
1058
0
    return NS_ERROR_UNEXPECTED;
1059
0
  }
1060
0
1061
0
  for (uint32_t i = 0, len = aData.Length(); i < len; ++i) {
1062
0
    // We have an assertion further up the stack, but as a last
1063
0
    // resort avoid writing out broken entries here.
1064
0
    if (!ServiceWorkerRegistrationDataIsValid(aData[i])) {
1065
0
      continue;
1066
0
    }
1067
0
1068
0
    const mozilla::ipc::PrincipalInfo& info = aData[i].principal();
1069
0
1070
0
    MOZ_ASSERT(info.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
1071
0
1072
0
    const mozilla::ipc::ContentPrincipalInfo& cInfo =
1073
0
      info.get_ContentPrincipalInfo();
1074
0
1075
0
    nsAutoCString suffix;
1076
0
    cInfo.attrs().CreateSuffix(suffix);
1077
0
1078
0
    buffer.Truncate();
1079
0
    buffer.Append(suffix.get());
1080
0
    buffer.Append('\n');
1081
0
1082
0
    buffer.Append(aData[i].scope());
1083
0
    buffer.Append('\n');
1084
0
1085
0
    buffer.Append(aData[i].currentWorkerURL());
1086
0
    buffer.Append('\n');
1087
0
1088
0
    buffer.Append(aData[i].currentWorkerHandlesFetch() ?
1089
0
                    SERVICEWORKERREGISTRAR_TRUE : SERVICEWORKERREGISTRAR_FALSE);
1090
0
    buffer.Append('\n');
1091
0
1092
0
    buffer.Append(NS_ConvertUTF16toUTF8(aData[i].cacheName()));
1093
0
    buffer.Append('\n');
1094
0
1095
0
    buffer.AppendInt(aData[i].updateViaCache(), 16);
1096
0
    buffer.Append('\n');
1097
0
    MOZ_DIAGNOSTIC_ASSERT(
1098
0
      aData[i].updateViaCache() == nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS ||
1099
0
      aData[i].updateViaCache() == nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_ALL ||
1100
0
      aData[i].updateViaCache() == nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_NONE
1101
0
    );
1102
0
1103
0
    static_assert(nsIRequest::LOAD_NORMAL == 0,
1104
0
                  "LOAD_NORMAL matches serialized value.");
1105
0
    static_assert(nsIRequest::VALIDATE_ALWAYS == (1 << 11),
1106
0
                  "VALIDATE_ALWAYS matches serialized value");
1107
0
1108
0
    buffer.AppendInt(aData[i].currentWorkerInstalledTime());
1109
0
    buffer.Append('\n');
1110
0
1111
0
    buffer.AppendInt(aData[i].currentWorkerActivatedTime());
1112
0
    buffer.Append('\n');
1113
0
1114
0
    buffer.AppendInt(aData[i].lastUpdateTime());
1115
0
    buffer.Append('\n');
1116
0
1117
0
    buffer.AppendLiteral(SERVICEWORKERREGISTRAR_TERMINATOR);
1118
0
    buffer.Append('\n');
1119
0
1120
0
    rv = stream->Write(buffer.Data(), buffer.Length(), &count);
1121
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1122
0
      return rv;
1123
0
    }
1124
0
1125
0
    if (count != buffer.Length()) {
1126
0
      return NS_ERROR_UNEXPECTED;
1127
0
    }
1128
0
  }
1129
0
1130
0
  nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(stream);
1131
0
  MOZ_ASSERT(safeStream);
1132
0
1133
0
  rv = safeStream->Finish();
1134
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1135
0
    return rv;
1136
0
  }
1137
0
1138
0
  return NS_OK;
1139
0
}
1140
1141
void
1142
ServiceWorkerRegistrar::ProfileStarted()
1143
0
{
1144
0
  MOZ_ASSERT(NS_IsMainThread());
1145
0
1146
0
  MonitorAutoLock lock(mMonitor);
1147
0
  MOZ_DIAGNOSTIC_ASSERT(!mProfileDir);
1148
0
1149
0
  nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
1150
0
                                       getter_AddRefs(mProfileDir));
1151
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1152
0
    return;
1153
0
  }
1154
0
1155
0
  rv = GetShutdownPhase()->AddBlocker(
1156
0
    this, NS_LITERAL_STRING(__FILE__), __LINE__,
1157
0
    NS_LITERAL_STRING("ServiceWorkerRegistrar: Flushing data"));
1158
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1159
0
    return;
1160
0
  }
1161
0
1162
0
  nsCOMPtr<nsIEventTarget> target =
1163
0
    do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
1164
0
  MOZ_ASSERT(target, "Must have stream transport service");
1165
0
1166
0
  nsCOMPtr<nsIRunnable> runnable =
1167
0
    NewRunnableMethod("dom::ServiceWorkerRegistrar::LoadData",
1168
0
                      this,
1169
0
                      &ServiceWorkerRegistrar::LoadData);
1170
0
  rv = target->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
1171
0
  if (NS_FAILED(rv)) {
1172
0
    NS_WARNING("Failed to dispatch the LoadDataRunnable.");
1173
0
  }
1174
0
}
1175
1176
void
1177
ServiceWorkerRegistrar::ProfileStopped()
1178
0
{
1179
0
  MOZ_ASSERT(NS_IsMainThread());
1180
0
1181
0
  MonitorAutoLock lock(mMonitor);
1182
0
1183
0
  if (!mProfileDir) {
1184
0
    nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
1185
0
                                         getter_AddRefs(mProfileDir));
1186
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
1187
0
      return;
1188
0
    }
1189
0
  }
1190
0
1191
0
  PBackgroundChild* child = BackgroundChild::GetForCurrentThread();
1192
0
  if (!child) {
1193
0
    // Mutations to the ServiceWorkerRegistrar happen on the PBackground thread,
1194
0
    // issued by the ServiceWorkerManagerService, so the appropriate place to
1195
0
    // trigger shutdown is on that thread.
1196
0
    //
1197
0
    // However, it's quite possible that the PBackground thread was not brought
1198
0
    // into existence for xpcshell tests.  We don't cause it to be created
1199
0
    // ourselves for any reason, for example.
1200
0
    //
1201
0
    // In this scenario, we know that:
1202
0
    // - We will receive exactly one call to ourself from BlockShutdown() and
1203
0
    //   BlockShutdown() will be called (at most) once.
1204
0
    // - The only way our Shutdown() method gets called is via
1205
0
    //   BackgroundParentImpl::RecvShutdownServiceWorkerRegistrar() being
1206
0
    //   invoked, which only happens if we get to that send below here that we
1207
0
    //   can't get to.
1208
0
    // - All Shutdown() does is set mShuttingDown=true (essential for
1209
0
    //   invariants) and invoke MaybeScheduleShutdownCompleted().
1210
0
    // - Since there is no PBackground thread, mRunnableDispatched must be false
1211
0
    //   because only MaybeScheduleSaveData() set it and it only runs on the
1212
0
    //   background thread, so it cannot have run.  And so we would expect
1213
0
    //   MaybeScheduleShutdownCompleted() to schedule an invocation of
1214
0
    //   ShutdownCompleted on the main thread.
1215
0
    //
1216
0
    // So it's appropriate for us to set mShuttingDown=true (as Shutdown would
1217
0
    // do) and directly invoke ShutdownCompleted() (as Shutdown would indirectly
1218
0
    // do via MaybeScheduleShutdownCompleted).
1219
0
    mShuttingDown = true;
1220
0
    ShutdownCompleted();
1221
0
    return;
1222
0
  }
1223
0
1224
0
  child->SendShutdownServiceWorkerRegistrar();
1225
0
}
1226
1227
// Async shutdown blocker methods
1228
1229
NS_IMETHODIMP
1230
ServiceWorkerRegistrar::BlockShutdown(nsIAsyncShutdownClient* aClient)
1231
0
{
1232
0
  ProfileStopped();
1233
0
  return NS_OK;
1234
0
}
1235
1236
NS_IMETHODIMP
1237
ServiceWorkerRegistrar::GetName(nsAString& aName)
1238
0
{
1239
0
  aName = NS_LITERAL_STRING("ServiceWorkerRegistrar: Flushing data");
1240
0
  return NS_OK;
1241
0
}
1242
1243
NS_IMETHODIMP
1244
ServiceWorkerRegistrar::GetState(nsIPropertyBag**)
1245
0
{
1246
0
  return NS_OK;
1247
0
}
1248
1249
0
#define RELEASE_ASSERT_SUCCEEDED(rv, name) do { \
1250
0
    if (NS_FAILED(rv)) {                                                       \
1251
0
      mozJSComponentLoader::Get()->AnnotateCrashReport();                      \
1252
0
      if (rv == NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS) {                  \
1253
0
        if (auto* context = CycleCollectedJSContext::Get()) {                  \
1254
0
          if (RefPtr<Exception> exn = context->GetPendingException()) {        \
1255
0
            MOZ_CRASH_UNSAFE_PRINTF("Failed to get " name ": %s",              \
1256
0
                                    exn->GetMessageMoz().get());               \
1257
0
          }                                                                    \
1258
0
        }                                                                      \
1259
0
      }                                                                        \
1260
0
                                                                               \
1261
0
      nsAutoCString errorName;                                                 \
1262
0
      GetErrorName(rv, errorName);                                             \
1263
0
      MOZ_CRASH_UNSAFE_PRINTF("Failed to get " name ": %s",                    \
1264
0
                              errorName.get());                                \
1265
0
    }                                                                          \
1266
0
  } while (0)
1267
1268
1269
nsCOMPtr<nsIAsyncShutdownClient>
1270
ServiceWorkerRegistrar::GetShutdownPhase() const
1271
0
{
1272
0
  nsresult rv;
1273
0
  nsCOMPtr<nsIAsyncShutdownService> svc = do_GetService(
1274
0
      "@mozilla.org/async-shutdown-service;1", &rv);
1275
0
  // If this fails, something is very wrong on the JS side (or we're out of
1276
0
  // memory), and there's no point in continuing startup. Include as much
1277
0
  // information as possible in the crash report.
1278
0
  RELEASE_ASSERT_SUCCEEDED(rv, "async shutdown service");
1279
0
1280
0
1281
0
  nsCOMPtr<nsIAsyncShutdownClient> client;
1282
0
  rv = svc->GetProfileBeforeChange(getter_AddRefs(client));
1283
0
  RELEASE_ASSERT_SUCCEEDED(rv, "profileBeforeChange shutdown blocker");
1284
0
  return client;
1285
0
}
1286
1287
#undef RELEASE_ASSERT_SUCCEEDED
1288
1289
void
1290
ServiceWorkerRegistrar::Shutdown()
1291
0
{
1292
0
  AssertIsOnBackgroundThread();
1293
0
  MOZ_ASSERT(!mShuttingDown);
1294
0
1295
0
  mShuttingDown = true;
1296
0
  MaybeScheduleShutdownCompleted();
1297
0
}
1298
1299
NS_IMETHODIMP
1300
ServiceWorkerRegistrar::Observe(nsISupports* aSubject, const char* aTopic,
1301
                                const char16_t* aData)
1302
0
{
1303
0
  MOZ_ASSERT(NS_IsMainThread());
1304
0
1305
0
  if (!strcmp(aTopic, "profile-after-change")) {
1306
0
    nsCOMPtr<nsIObserverService> observerService =
1307
0
      services::GetObserverService();
1308
0
    observerService->RemoveObserver(this, "profile-after-change");
1309
0
1310
0
    // The profile is fully loaded, now we can proceed with the loading of data
1311
0
    // from disk.
1312
0
    ProfileStarted();
1313
0
1314
0
    return NS_OK;
1315
0
  }
1316
0
1317
0
  MOZ_ASSERT(false, "ServiceWorkerRegistrar got unexpected topic!");
1318
0
  return NS_ERROR_UNEXPECTED;
1319
0
}
1320
1321
} // namespace dom
1322
} // namespace mozilla