Line data Source code
1 :
2 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
3 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
4 : /* This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 :
8 : #include "ServiceWorkerRegistrar.h"
9 : #include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
10 :
11 : #include "nsIEventTarget.h"
12 : #include "nsIInputStream.h"
13 : #include "nsILineInputStream.h"
14 : #include "nsIObserverService.h"
15 : #include "nsIOutputStream.h"
16 : #include "nsISafeOutputStream.h"
17 :
18 : #include "MainThreadUtils.h"
19 : #include "mozilla/ClearOnShutdown.h"
20 : #include "mozilla/ipc/BackgroundChild.h"
21 : #include "mozilla/ipc/BackgroundParent.h"
22 : #include "mozilla/ipc/PBackgroundChild.h"
23 : #include "mozilla/ModuleUtils.h"
24 : #include "mozilla/Services.h"
25 : #include "mozilla/StaticPtr.h"
26 : #include "nsAppDirectoryServiceDefs.h"
27 : #include "nsContentUtils.h"
28 : #include "nsDirectoryServiceUtils.h"
29 : #include "nsNetCID.h"
30 : #include "nsNetUtil.h"
31 : #include "nsServiceManagerUtils.h"
32 : #include "nsThreadUtils.h"
33 : #include "nsXULAppAPI.h"
34 :
35 : using namespace mozilla::ipc;
36 :
37 : namespace mozilla {
38 : namespace dom {
39 :
40 : namespace {
41 :
42 : static const char* gSupportedRegistrarVersions[] = {
43 : SERVICEWORKERREGISTRAR_VERSION,
44 : "6",
45 : "5",
46 : "4",
47 : "3",
48 : "2"
49 : };
50 :
51 3 : StaticRefPtr<ServiceWorkerRegistrar> gServiceWorkerRegistrar;
52 :
53 : } // namespace
54 :
55 21 : NS_IMPL_ISUPPORTS(ServiceWorkerRegistrar,
56 : nsIObserver)
57 :
58 : void
59 3 : ServiceWorkerRegistrar::Initialize()
60 : {
61 3 : MOZ_ASSERT(!gServiceWorkerRegistrar);
62 :
63 3 : if (!XRE_IsParentProcess()) {
64 2 : return;
65 : }
66 :
67 1 : gServiceWorkerRegistrar = new ServiceWorkerRegistrar();
68 1 : ClearOnShutdown(&gServiceWorkerRegistrar);
69 :
70 2 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
71 1 : if (obs) {
72 3 : DebugOnly<nsresult> rv = obs->AddObserver(gServiceWorkerRegistrar,
73 3 : "profile-after-change", false);
74 1 : MOZ_ASSERT(NS_SUCCEEDED(rv));
75 :
76 3 : rv = obs->AddObserver(gServiceWorkerRegistrar, "profile-before-change",
77 2 : false);
78 1 : MOZ_ASSERT(NS_SUCCEEDED(rv));
79 : }
80 : }
81 :
82 : /* static */ already_AddRefed<ServiceWorkerRegistrar>
83 3 : ServiceWorkerRegistrar::Get()
84 : {
85 3 : MOZ_ASSERT(XRE_IsParentProcess());
86 :
87 3 : MOZ_ASSERT(gServiceWorkerRegistrar);
88 6 : RefPtr<ServiceWorkerRegistrar> service = gServiceWorkerRegistrar.get();
89 6 : return service.forget();
90 : }
91 :
92 1 : ServiceWorkerRegistrar::ServiceWorkerRegistrar()
93 : : mMonitor("ServiceWorkerRegistrar.mMonitor")
94 : , mDataLoaded(false)
95 : , mShuttingDown(false)
96 : , mShutdownCompleteFlag(nullptr)
97 1 : , mRunnableCounter(0)
98 : {
99 1 : MOZ_ASSERT(NS_IsMainThread());
100 1 : }
101 :
102 0 : ServiceWorkerRegistrar::~ServiceWorkerRegistrar()
103 : {
104 0 : MOZ_ASSERT(!mRunnableCounter);
105 0 : }
106 :
107 : void
108 3 : ServiceWorkerRegistrar::GetRegistrations(
109 : nsTArray<ServiceWorkerRegistrationData>& aValues)
110 : {
111 3 : MOZ_ASSERT(NS_IsMainThread());
112 3 : MOZ_ASSERT(aValues.IsEmpty());
113 :
114 6 : MonitorAutoLock lock(mMonitor);
115 :
116 : // If we don't have the profile directory, profile is not started yet (and
117 : // probably we are in a utest).
118 3 : if (!mProfileDir) {
119 0 : return;
120 : }
121 :
122 : // We care just about the first execution because this can be blocked by
123 : // loading data from disk.
124 : static bool firstTime = true;
125 3 : TimeStamp startTime;
126 :
127 3 : if (firstTime) {
128 1 : startTime = TimeStamp::NowLoRes();
129 : }
130 :
131 : // Waiting for data loaded.
132 3 : mMonitor.AssertCurrentThreadOwns();
133 3 : while (!mDataLoaded) {
134 0 : mMonitor.Wait();
135 : }
136 :
137 3 : aValues.AppendElements(mData);
138 :
139 3 : if (firstTime) {
140 1 : firstTime = false;
141 1 : Telemetry::AccumulateTimeDelta(
142 : Telemetry::SERVICE_WORKER_REGISTRATION_LOADING,
143 1 : startTime);
144 : }
145 : }
146 :
147 : namespace {
148 :
149 0 : bool Equivalent(const ServiceWorkerRegistrationData& aLeft,
150 : const ServiceWorkerRegistrationData& aRight)
151 : {
152 0 : MOZ_ASSERT(aLeft.principal().type() ==
153 : mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
154 0 : MOZ_ASSERT(aRight.principal().type() ==
155 : mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
156 :
157 0 : const auto& leftPrincipal = aLeft.principal().get_ContentPrincipalInfo();
158 0 : const auto& rightPrincipal = aRight.principal().get_ContentPrincipalInfo();
159 :
160 : // Only compare the attributes, not the spec part of the principal.
161 : // The scope comparison above already covers the origin and codebase
162 : // principals include the full path in their spec which is not what
163 : // we want here.
164 0 : return aLeft.scope() == aRight.scope() &&
165 0 : leftPrincipal.attrs() == rightPrincipal.attrs();
166 : }
167 :
168 : } // anonymous namespace
169 :
170 : void
171 0 : ServiceWorkerRegistrar::RegisterServiceWorker(
172 : const ServiceWorkerRegistrationData& aData)
173 : {
174 0 : AssertIsOnBackgroundThread();
175 :
176 0 : if (mShuttingDown) {
177 0 : NS_WARNING("Failed to register a serviceWorker during shutting down.");
178 0 : return;
179 : }
180 :
181 : {
182 0 : MonitorAutoLock lock(mMonitor);
183 0 : MOZ_ASSERT(mDataLoaded);
184 0 : RegisterServiceWorkerInternal(aData);
185 : }
186 :
187 0 : ScheduleSaveData();
188 : }
189 :
190 : void
191 0 : ServiceWorkerRegistrar::UnregisterServiceWorker(
192 : const PrincipalInfo& aPrincipalInfo,
193 : const nsACString& aScope)
194 : {
195 0 : AssertIsOnBackgroundThread();
196 :
197 0 : if (mShuttingDown) {
198 0 : NS_WARNING("Failed to unregister a serviceWorker during shutting down.");
199 0 : return;
200 : }
201 :
202 0 : bool deleted = false;
203 :
204 : {
205 0 : MonitorAutoLock lock(mMonitor);
206 0 : MOZ_ASSERT(mDataLoaded);
207 :
208 0 : ServiceWorkerRegistrationData tmp;
209 0 : tmp.principal() = aPrincipalInfo;
210 0 : tmp.scope() = aScope;
211 :
212 0 : for (uint32_t i = 0; i < mData.Length(); ++i) {
213 0 : if (Equivalent(tmp, mData[i])) {
214 0 : mData.RemoveElementAt(i);
215 0 : deleted = true;
216 0 : break;
217 : }
218 : }
219 : }
220 :
221 0 : if (deleted) {
222 0 : ScheduleSaveData();
223 : }
224 : }
225 :
226 : void
227 0 : ServiceWorkerRegistrar::RemoveAll()
228 : {
229 0 : AssertIsOnBackgroundThread();
230 :
231 0 : if (mShuttingDown) {
232 0 : NS_WARNING("Failed to remove all the serviceWorkers during shutting down.");
233 0 : return;
234 : }
235 :
236 0 : bool deleted = false;
237 :
238 : {
239 0 : MonitorAutoLock lock(mMonitor);
240 0 : MOZ_ASSERT(mDataLoaded);
241 :
242 0 : deleted = !mData.IsEmpty();
243 0 : mData.Clear();
244 : }
245 :
246 0 : if (deleted) {
247 0 : ScheduleSaveData();
248 : }
249 : }
250 :
251 : void
252 1 : ServiceWorkerRegistrar::LoadData()
253 : {
254 1 : MOZ_ASSERT(!NS_IsMainThread());
255 1 : MOZ_ASSERT(!mDataLoaded);
256 :
257 1 : nsresult rv = ReadData();
258 :
259 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
260 0 : DeleteData();
261 : // Also if the reading failed we have to notify what is waiting for data.
262 : }
263 :
264 2 : MonitorAutoLock lock(mMonitor);
265 1 : MOZ_ASSERT(!mDataLoaded);
266 1 : mDataLoaded = true;
267 1 : mMonitor.Notify();
268 1 : }
269 :
270 : nsresult
271 1 : ServiceWorkerRegistrar::ReadData()
272 : {
273 : // We cannot assert about the correct thread because normally this method
274 : // runs on a IO thread, but in gTests we call it from the main-thread.
275 :
276 2 : nsCOMPtr<nsIFile> file;
277 :
278 : {
279 2 : MonitorAutoLock lock(mMonitor);
280 :
281 1 : if (!mProfileDir) {
282 0 : return NS_ERROR_FAILURE;
283 : }
284 :
285 1 : nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
286 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
287 0 : return rv;
288 : }
289 : }
290 :
291 1 : nsresult rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
292 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
293 0 : return rv;
294 : }
295 :
296 : bool exists;
297 1 : rv = file->Exists(&exists);
298 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
299 0 : return rv;
300 : }
301 :
302 1 : if (!exists) {
303 1 : return NS_OK;
304 : }
305 :
306 0 : nsCOMPtr<nsIInputStream> stream;
307 0 : rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
308 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
309 0 : return rv;
310 : }
311 :
312 0 : nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(stream);
313 0 : MOZ_ASSERT(lineInputStream);
314 :
315 0 : nsAutoCString version;
316 : bool hasMoreLines;
317 0 : rv = lineInputStream->ReadLine(version, &hasMoreLines);
318 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
319 0 : return rv;
320 : }
321 :
322 0 : if (!IsSupportedVersion(version)) {
323 0 : nsContentUtils::LogMessageToConsole(nsPrintfCString(
324 0 : "Unsupported service worker registrar version: %s", version.get()).get());
325 0 : return NS_ERROR_FAILURE;
326 : }
327 :
328 0 : nsTArray<ServiceWorkerRegistrationData> tmpData;
329 :
330 0 : bool overwrite = false;
331 0 : bool dedupe = false;
332 0 : while (hasMoreLines) {
333 0 : ServiceWorkerRegistrationData* entry = tmpData.AppendElement();
334 :
335 : #define GET_LINE(x) \
336 : rv = lineInputStream->ReadLine(x, &hasMoreLines); \
337 : if (NS_WARN_IF(NS_FAILED(rv))) { \
338 : return rv; \
339 : } \
340 : if (NS_WARN_IF(!hasMoreLines)) { \
341 : return NS_ERROR_FAILURE; \
342 : }
343 :
344 0 : nsAutoCString line;
345 0 : nsAutoCString unused;
346 0 : if (version.EqualsLiteral(SERVICEWORKERREGISTRAR_VERSION)) {
347 0 : nsAutoCString suffix;
348 0 : GET_LINE(suffix);
349 :
350 0 : OriginAttributes attrs;
351 0 : if (!attrs.PopulateFromSuffix(suffix)) {
352 0 : return NS_ERROR_INVALID_ARG;
353 : }
354 :
355 0 : GET_LINE(entry->scope());
356 :
357 0 : entry->principal() =
358 0 : mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
359 :
360 0 : GET_LINE(entry->currentWorkerURL());
361 :
362 0 : nsAutoCString fetchFlag;
363 0 : GET_LINE(fetchFlag);
364 0 : if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
365 0 : !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
366 0 : return NS_ERROR_INVALID_ARG;
367 : }
368 0 : entry->currentWorkerHandlesFetch() =
369 0 : fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
370 :
371 0 : nsAutoCString cacheName;
372 0 : GET_LINE(cacheName);
373 0 : CopyUTF8toUTF16(cacheName, entry->cacheName());
374 :
375 0 : nsAutoCString loadFlags;
376 0 : GET_LINE(loadFlags);
377 0 : entry->loadFlags() = loadFlags.ToInteger(&rv, 16);
378 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
379 0 : return rv;
380 0 : } else if (entry->loadFlags() != nsIRequest::LOAD_NORMAL &&
381 0 : entry->loadFlags() != nsIRequest::VALIDATE_ALWAYS) {
382 0 : return NS_ERROR_INVALID_ARG;
383 : }
384 :
385 0 : nsAutoCString installedTimeStr;
386 0 : GET_LINE(installedTimeStr);
387 0 : int64_t installedTime = installedTimeStr.ToInteger64(&rv);
388 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
389 0 : return rv;
390 : }
391 0 : entry->currentWorkerInstalledTime() = installedTime;
392 :
393 0 : nsAutoCString activatedTimeStr;
394 0 : GET_LINE(activatedTimeStr);
395 0 : int64_t activatedTime = activatedTimeStr.ToInteger64(&rv);
396 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
397 0 : return rv;
398 : }
399 0 : entry->currentWorkerActivatedTime() = activatedTime;
400 :
401 0 : nsAutoCString lastUpdateTimeStr;
402 0 : GET_LINE(lastUpdateTimeStr);
403 0 : int64_t lastUpdateTime = lastUpdateTimeStr.ToInteger64(&rv);
404 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
405 0 : return rv;
406 : }
407 0 : entry->lastUpdateTime() = lastUpdateTime;
408 0 : } else if (version.EqualsLiteral("6")) {
409 0 : nsAutoCString suffix;
410 0 : GET_LINE(suffix);
411 :
412 0 : OriginAttributes attrs;
413 0 : if (!attrs.PopulateFromSuffix(suffix)) {
414 0 : return NS_ERROR_INVALID_ARG;
415 : }
416 :
417 0 : GET_LINE(entry->scope());
418 :
419 0 : entry->principal() =
420 0 : mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
421 :
422 0 : GET_LINE(entry->currentWorkerURL());
423 :
424 0 : nsAutoCString fetchFlag;
425 0 : GET_LINE(fetchFlag);
426 0 : if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
427 0 : !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
428 0 : return NS_ERROR_INVALID_ARG;
429 : }
430 0 : entry->currentWorkerHandlesFetch() =
431 0 : fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
432 :
433 0 : nsAutoCString cacheName;
434 0 : GET_LINE(cacheName);
435 0 : CopyUTF8toUTF16(cacheName, entry->cacheName());
436 :
437 0 : nsAutoCString loadFlags;
438 0 : GET_LINE(loadFlags);
439 0 : entry->loadFlags() = loadFlags.ToInteger(&rv, 16);
440 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
441 0 : return rv;
442 0 : } else if (entry->loadFlags() != nsIRequest::LOAD_NORMAL &&
443 0 : entry->loadFlags() != nsIRequest::VALIDATE_ALWAYS) {
444 0 : return NS_ERROR_INVALID_ARG;
445 : }
446 :
447 0 : entry->currentWorkerInstalledTime() = 0;
448 0 : entry->currentWorkerActivatedTime() = 0;
449 0 : entry->lastUpdateTime() = 0;
450 0 : } else if (version.EqualsLiteral("5")) {
451 0 : overwrite = true;
452 0 : dedupe = true;
453 :
454 0 : nsAutoCString suffix;
455 0 : GET_LINE(suffix);
456 :
457 0 : OriginAttributes attrs;
458 0 : if (!attrs.PopulateFromSuffix(suffix)) {
459 0 : return NS_ERROR_INVALID_ARG;
460 : }
461 :
462 0 : GET_LINE(entry->scope());
463 :
464 0 : entry->principal() =
465 0 : mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
466 :
467 0 : GET_LINE(entry->currentWorkerURL());
468 :
469 0 : nsAutoCString fetchFlag;
470 0 : GET_LINE(fetchFlag);
471 0 : if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
472 0 : !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
473 0 : return NS_ERROR_INVALID_ARG;
474 : }
475 0 : entry->currentWorkerHandlesFetch() =
476 0 : fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
477 :
478 0 : nsAutoCString cacheName;
479 0 : GET_LINE(cacheName);
480 0 : CopyUTF8toUTF16(cacheName, entry->cacheName());
481 :
482 0 : entry->loadFlags() = nsIRequest::VALIDATE_ALWAYS;
483 :
484 0 : entry->currentWorkerInstalledTime() = 0;
485 0 : entry->currentWorkerActivatedTime() = 0;
486 0 : entry->lastUpdateTime() = 0;
487 0 : } else if (version.EqualsLiteral("4")) {
488 0 : overwrite = true;
489 0 : dedupe = true;
490 :
491 0 : nsAutoCString suffix;
492 0 : GET_LINE(suffix);
493 :
494 0 : OriginAttributes attrs;
495 0 : if (!attrs.PopulateFromSuffix(suffix)) {
496 0 : return NS_ERROR_INVALID_ARG;
497 : }
498 :
499 0 : GET_LINE(entry->scope());
500 :
501 0 : entry->principal() =
502 0 : mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
503 :
504 0 : GET_LINE(entry->currentWorkerURL());
505 :
506 : // default handlesFetch flag to Enabled
507 0 : entry->currentWorkerHandlesFetch() = true;
508 :
509 0 : nsAutoCString cacheName;
510 0 : GET_LINE(cacheName);
511 0 : CopyUTF8toUTF16(cacheName, entry->cacheName());
512 :
513 0 : entry->loadFlags() = nsIRequest::VALIDATE_ALWAYS;
514 :
515 0 : entry->currentWorkerInstalledTime() = 0;
516 0 : entry->currentWorkerActivatedTime() = 0;
517 0 : entry->lastUpdateTime() = 0;
518 0 : } else if (version.EqualsLiteral("3")) {
519 0 : overwrite = true;
520 0 : dedupe = true;
521 :
522 0 : nsAutoCString suffix;
523 0 : GET_LINE(suffix);
524 :
525 0 : OriginAttributes attrs;
526 0 : if (!attrs.PopulateFromSuffix(suffix)) {
527 0 : return NS_ERROR_INVALID_ARG;
528 : }
529 :
530 : // principal spec is no longer used; we use scope directly instead
531 0 : GET_LINE(unused);
532 :
533 0 : GET_LINE(entry->scope());
534 :
535 0 : entry->principal() =
536 0 : mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
537 :
538 0 : GET_LINE(entry->currentWorkerURL());
539 :
540 : // default handlesFetch flag to Enabled
541 0 : entry->currentWorkerHandlesFetch() = true;
542 :
543 0 : nsAutoCString cacheName;
544 0 : GET_LINE(cacheName);
545 0 : CopyUTF8toUTF16(cacheName, entry->cacheName());
546 :
547 0 : entry->loadFlags() = nsIRequest::VALIDATE_ALWAYS;
548 :
549 0 : entry->currentWorkerInstalledTime() = 0;
550 0 : entry->currentWorkerActivatedTime() = 0;
551 0 : entry->lastUpdateTime() = 0;
552 0 : } else if (version.EqualsLiteral("2")) {
553 0 : overwrite = true;
554 0 : dedupe = true;
555 :
556 0 : nsAutoCString suffix;
557 0 : GET_LINE(suffix);
558 :
559 0 : OriginAttributes attrs;
560 0 : if (!attrs.PopulateFromSuffix(suffix)) {
561 0 : return NS_ERROR_INVALID_ARG;
562 : }
563 :
564 : // principal spec is no longer used; we use scope directly instead
565 0 : GET_LINE(unused);
566 :
567 0 : GET_LINE(entry->scope());
568 :
569 0 : entry->principal() =
570 0 : mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
571 :
572 : // scriptSpec is no more used in latest version.
573 0 : GET_LINE(unused);
574 :
575 0 : GET_LINE(entry->currentWorkerURL());
576 :
577 : // default handlesFetch flag to Enabled
578 0 : entry->currentWorkerHandlesFetch() = true;
579 :
580 0 : nsAutoCString cacheName;
581 0 : GET_LINE(cacheName);
582 0 : CopyUTF8toUTF16(cacheName, entry->cacheName());
583 :
584 : // waitingCacheName is no more used in latest version.
585 0 : GET_LINE(unused);
586 :
587 0 : entry->loadFlags() = nsIRequest::VALIDATE_ALWAYS;
588 :
589 0 : entry->currentWorkerInstalledTime() = 0;
590 0 : entry->currentWorkerActivatedTime() = 0;
591 0 : entry->lastUpdateTime() = 0;
592 : } else {
593 0 : MOZ_ASSERT_UNREACHABLE("Should never get here!");
594 : }
595 :
596 : #undef GET_LINE
597 :
598 0 : rv = lineInputStream->ReadLine(line, &hasMoreLines);
599 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
600 0 : return rv;
601 : }
602 :
603 0 : if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_TERMINATOR)) {
604 0 : return NS_ERROR_FAILURE;
605 : }
606 : }
607 :
608 0 : stream->Close();
609 :
610 : // Copy data over to mData.
611 0 : for (uint32_t i = 0; i < tmpData.Length(); ++i) {
612 0 : bool match = false;
613 0 : if (dedupe) {
614 0 : MOZ_ASSERT(overwrite);
615 : // If this is an old profile, then we might need to deduplicate. In
616 : // theory this can be removed in the future (Bug 1248449)
617 0 : for (uint32_t j = 0; j < mData.Length(); ++j) {
618 : // Use same comparison as RegisterServiceWorker. Scope contains
619 : // basic origin information. Combine with any principal attributes.
620 0 : if (Equivalent(tmpData[i], mData[j])) {
621 : // Last match wins, just like legacy loading used to do in
622 : // the ServiceWorkerManager.
623 0 : mData[j] = tmpData[i];
624 : // Dupe found, so overwrite file with reduced list.
625 0 : match = true;
626 0 : break;
627 : }
628 : }
629 : } else {
630 : #ifdef DEBUG
631 : // Otherwise assert no duplications in debug builds.
632 0 : for (uint32_t j = 0; j < mData.Length(); ++j) {
633 0 : MOZ_ASSERT(!Equivalent(tmpData[i], mData[j]));
634 : }
635 : #endif
636 : }
637 0 : if (!match) {
638 0 : mData.AppendElement(tmpData[i]);
639 : }
640 : }
641 :
642 : // Overwrite previous version.
643 : // Cannot call SaveData directly because gtest uses main-thread.
644 0 : if (overwrite && NS_FAILED(WriteData())) {
645 0 : NS_WARNING("Failed to write data for the ServiceWorker Registations.");
646 0 : DeleteData();
647 : }
648 :
649 0 : return NS_OK;
650 : }
651 :
652 : void
653 0 : ServiceWorkerRegistrar::DeleteData()
654 : {
655 : // We cannot assert about the correct thread because normally this method
656 : // runs on a IO thread, but in gTests we call it from the main-thread.
657 :
658 0 : nsCOMPtr<nsIFile> file;
659 :
660 : {
661 0 : MonitorAutoLock lock(mMonitor);
662 0 : mData.Clear();
663 :
664 0 : if (!mProfileDir) {
665 0 : return;
666 : }
667 :
668 0 : nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
669 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
670 0 : return;
671 : }
672 : }
673 :
674 0 : nsresult rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
675 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
676 0 : return;
677 : }
678 :
679 0 : rv = file->Remove(false);
680 0 : if (rv == NS_ERROR_FILE_NOT_FOUND) {
681 0 : return;
682 : }
683 :
684 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
685 0 : return;
686 : }
687 : }
688 :
689 : void
690 0 : ServiceWorkerRegistrar::RegisterServiceWorkerInternal(const ServiceWorkerRegistrationData& aData)
691 : {
692 0 : bool found = false;
693 0 : for (uint32_t i = 0, len = mData.Length(); i < len; ++i) {
694 0 : if (Equivalent(aData, mData[i])) {
695 0 : mData[i] = aData;
696 0 : found = true;
697 0 : break;
698 : }
699 : }
700 :
701 0 : if (!found) {
702 0 : mData.AppendElement(aData);
703 : }
704 0 : }
705 :
706 0 : class ServiceWorkerRegistrarSaveDataRunnable final : public Runnable
707 : {
708 : public:
709 0 : ServiceWorkerRegistrarSaveDataRunnable()
710 0 : : Runnable("dom::ServiceWorkerRegistrarSaveDataRunnable")
711 0 : , mEventTarget(GetCurrentThreadEventTarget())
712 : {
713 0 : AssertIsOnBackgroundThread();
714 0 : }
715 :
716 : NS_IMETHOD
717 0 : Run() override
718 : {
719 0 : RefPtr<ServiceWorkerRegistrar> service = ServiceWorkerRegistrar::Get();
720 0 : MOZ_ASSERT(service);
721 :
722 0 : service->SaveData();
723 :
724 : RefPtr<Runnable> runnable =
725 0 : NewRunnableMethod("ServiceWorkerRegistrar::DataSaved",
726 0 : service, &ServiceWorkerRegistrar::DataSaved);
727 0 : nsresult rv = mEventTarget->Dispatch(runnable, NS_DISPATCH_NORMAL);
728 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
729 0 : return rv;
730 : }
731 :
732 0 : return NS_OK;
733 : }
734 :
735 : private:
736 : nsCOMPtr<nsIEventTarget> mEventTarget;
737 : };
738 :
739 : void
740 0 : ServiceWorkerRegistrar::ScheduleSaveData()
741 : {
742 0 : AssertIsOnBackgroundThread();
743 0 : MOZ_ASSERT(!mShuttingDown);
744 :
745 : nsCOMPtr<nsIEventTarget> target =
746 0 : do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
747 0 : MOZ_ASSERT(target, "Must have stream transport service");
748 :
749 : RefPtr<Runnable> runnable =
750 0 : new ServiceWorkerRegistrarSaveDataRunnable();
751 0 : nsresult rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
752 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
753 0 : return;
754 : }
755 :
756 0 : ++mRunnableCounter;
757 : }
758 :
759 : void
760 0 : ServiceWorkerRegistrar::ShutdownCompleted()
761 : {
762 0 : MOZ_ASSERT(NS_IsMainThread());
763 :
764 0 : MOZ_ASSERT(mShutdownCompleteFlag && !*mShutdownCompleteFlag);
765 0 : *mShutdownCompleteFlag = true;
766 0 : }
767 :
768 : void
769 0 : ServiceWorkerRegistrar::SaveData()
770 : {
771 0 : MOZ_ASSERT(!NS_IsMainThread());
772 :
773 0 : nsresult rv = WriteData();
774 0 : if (NS_FAILED(rv)) {
775 0 : NS_WARNING("Failed to write data for the ServiceWorker Registations.");
776 0 : DeleteData();
777 : }
778 0 : }
779 :
780 : void
781 0 : ServiceWorkerRegistrar::DataSaved()
782 : {
783 0 : AssertIsOnBackgroundThread();
784 0 : MOZ_ASSERT(mRunnableCounter);
785 :
786 0 : --mRunnableCounter;
787 0 : MaybeScheduleShutdownCompleted();
788 0 : }
789 :
790 : void
791 0 : ServiceWorkerRegistrar::MaybeScheduleShutdownCompleted()
792 : {
793 0 : AssertIsOnBackgroundThread();
794 :
795 0 : if (mRunnableCounter || !mShuttingDown) {
796 0 : return;
797 : }
798 :
799 : RefPtr<Runnable> runnable =
800 0 : NewRunnableMethod("dom::ServiceWorkerRegistrar::ShutdownCompleted",
801 : this,
802 0 : &ServiceWorkerRegistrar::ShutdownCompleted);
803 0 : nsresult rv = NS_DispatchToMainThread(runnable);
804 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
805 0 : return;
806 : }
807 : }
808 :
809 : bool
810 0 : ServiceWorkerRegistrar::IsSupportedVersion(const nsACString& aVersion) const
811 : {
812 0 : uint32_t numVersions = ArrayLength(gSupportedRegistrarVersions);
813 0 : for (uint32_t i = 0; i < numVersions; i++) {
814 0 : if (aVersion.EqualsASCII(gSupportedRegistrarVersions[i])) {
815 0 : return true;
816 : }
817 : }
818 0 : return false;
819 : }
820 :
821 : nsresult
822 0 : ServiceWorkerRegistrar::WriteData()
823 : {
824 : // We cannot assert about the correct thread because normally this method
825 : // runs on a IO thread, but in gTests we call it from the main-thread.
826 :
827 0 : nsCOMPtr<nsIFile> file;
828 :
829 : {
830 0 : MonitorAutoLock lock(mMonitor);
831 :
832 0 : if (!mProfileDir) {
833 0 : return NS_ERROR_FAILURE;
834 : }
835 :
836 0 : nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
837 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
838 0 : return rv;
839 : }
840 : }
841 :
842 0 : nsresult rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
843 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
844 0 : return rv;
845 : }
846 :
847 : // We need a lock to take a snapshot of the data.
848 0 : nsTArray<ServiceWorkerRegistrationData> data;
849 : {
850 0 : MonitorAutoLock lock(mMonitor);
851 0 : data = mData;
852 : }
853 :
854 0 : nsCOMPtr<nsIOutputStream> stream;
855 0 : rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(stream), file);
856 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
857 0 : return rv;
858 : }
859 :
860 0 : nsAutoCString buffer;
861 0 : buffer.AppendLiteral(SERVICEWORKERREGISTRAR_VERSION);
862 0 : buffer.Append('\n');
863 :
864 : uint32_t count;
865 0 : rv = stream->Write(buffer.Data(), buffer.Length(), &count);
866 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
867 0 : return rv;
868 : }
869 :
870 0 : if (count != buffer.Length()) {
871 0 : return NS_ERROR_UNEXPECTED;
872 : }
873 :
874 0 : for (uint32_t i = 0, len = data.Length(); i < len; ++i) {
875 0 : const mozilla::ipc::PrincipalInfo& info = data[i].principal();
876 :
877 0 : MOZ_ASSERT(info.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
878 :
879 : const mozilla::ipc::ContentPrincipalInfo& cInfo =
880 0 : info.get_ContentPrincipalInfo();
881 :
882 0 : nsAutoCString suffix;
883 0 : cInfo.attrs().CreateSuffix(suffix);
884 :
885 0 : buffer.Truncate();
886 0 : buffer.Append(suffix.get());
887 0 : buffer.Append('\n');
888 :
889 0 : buffer.Append(data[i].scope());
890 0 : buffer.Append('\n');
891 :
892 0 : buffer.Append(data[i].currentWorkerURL());
893 0 : buffer.Append('\n');
894 :
895 0 : buffer.Append(data[i].currentWorkerHandlesFetch() ?
896 0 : SERVICEWORKERREGISTRAR_TRUE : SERVICEWORKERREGISTRAR_FALSE);
897 0 : buffer.Append('\n');
898 :
899 0 : buffer.Append(NS_ConvertUTF16toUTF8(data[i].cacheName()));
900 0 : buffer.Append('\n');
901 :
902 0 : buffer.AppendInt(data[i].loadFlags(), 16);
903 0 : buffer.Append('\n');
904 0 : MOZ_DIAGNOSTIC_ASSERT(data[i].loadFlags() == nsIRequest::LOAD_NORMAL ||
905 : data[i].loadFlags() == nsIRequest::VALIDATE_ALWAYS);
906 :
907 : static_assert(nsIRequest::LOAD_NORMAL == 0,
908 : "LOAD_NORMAL matches serialized value.");
909 : static_assert(nsIRequest::VALIDATE_ALWAYS == (1 << 11),
910 : "VALIDATE_ALWAYS matches serialized value");
911 :
912 0 : buffer.AppendInt(data[i].currentWorkerInstalledTime());
913 0 : buffer.Append('\n');
914 :
915 0 : buffer.AppendInt(data[i].currentWorkerActivatedTime());
916 0 : buffer.Append('\n');
917 :
918 0 : buffer.AppendInt(data[i].lastUpdateTime());
919 0 : buffer.Append('\n');
920 :
921 0 : buffer.AppendLiteral(SERVICEWORKERREGISTRAR_TERMINATOR);
922 0 : buffer.Append('\n');
923 :
924 0 : rv = stream->Write(buffer.Data(), buffer.Length(), &count);
925 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
926 0 : return rv;
927 : }
928 :
929 0 : if (count != buffer.Length()) {
930 0 : return NS_ERROR_UNEXPECTED;
931 : }
932 : }
933 :
934 0 : nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(stream);
935 0 : MOZ_ASSERT(safeStream);
936 :
937 0 : rv = safeStream->Finish();
938 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
939 0 : return rv;
940 : }
941 :
942 0 : return NS_OK;
943 : }
944 :
945 : void
946 1 : ServiceWorkerRegistrar::ProfileStarted()
947 : {
948 1 : MOZ_ASSERT(NS_IsMainThread());
949 :
950 2 : MonitorAutoLock lock(mMonitor);
951 1 : MOZ_DIAGNOSTIC_ASSERT(!mProfileDir);
952 :
953 1 : nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
954 2 : getter_AddRefs(mProfileDir));
955 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
956 0 : return;
957 : }
958 :
959 : nsCOMPtr<nsIEventTarget> target =
960 2 : do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
961 1 : MOZ_ASSERT(target, "Must have stream transport service");
962 :
963 : nsCOMPtr<nsIRunnable> runnable =
964 2 : NewRunnableMethod("dom::ServiceWorkerRegistrar::LoadData",
965 : this,
966 2 : &ServiceWorkerRegistrar::LoadData);
967 1 : rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
968 1 : if (NS_FAILED(rv)) {
969 0 : NS_WARNING("Failed to dispatch the LoadDataRunnable.");
970 : }
971 : }
972 :
973 : void
974 0 : ServiceWorkerRegistrar::ProfileStopped()
975 : {
976 0 : MOZ_ASSERT(NS_IsMainThread());
977 :
978 0 : MonitorAutoLock lock(mMonitor);
979 :
980 0 : if (!mProfileDir) {
981 0 : nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
982 0 : getter_AddRefs(mProfileDir));
983 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
984 0 : return;
985 : }
986 : }
987 :
988 0 : PBackgroundChild* child = BackgroundChild::GetForCurrentThread();
989 0 : if (!child) {
990 0 : return;
991 : }
992 :
993 0 : bool completed = false;
994 0 : mShutdownCompleteFlag = &completed;
995 :
996 0 : child->SendShutdownServiceWorkerRegistrar();
997 :
998 0 : MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return completed; }));
999 : }
1000 :
1001 : void
1002 0 : ServiceWorkerRegistrar::Shutdown()
1003 : {
1004 0 : AssertIsOnBackgroundThread();
1005 0 : MOZ_ASSERT(!mShuttingDown);
1006 :
1007 0 : mShuttingDown = true;
1008 0 : MaybeScheduleShutdownCompleted();
1009 0 : }
1010 :
1011 : NS_IMETHODIMP
1012 1 : ServiceWorkerRegistrar::Observe(nsISupports* aSubject, const char* aTopic,
1013 : const char16_t* aData)
1014 : {
1015 1 : MOZ_ASSERT(NS_IsMainThread());
1016 :
1017 1 : if (!strcmp(aTopic, "profile-after-change")) {
1018 : nsCOMPtr<nsIObserverService> observerService =
1019 2 : services::GetObserverService();
1020 1 : observerService->RemoveObserver(this, "profile-after-change");
1021 :
1022 : // The profile is fully loaded, now we can proceed with the loading of data
1023 : // from disk.
1024 1 : ProfileStarted();
1025 :
1026 1 : return NS_OK;
1027 : }
1028 :
1029 0 : if (!strcmp(aTopic, "profile-before-change")) {
1030 : nsCOMPtr<nsIObserverService> observerService =
1031 0 : services::GetObserverService();
1032 0 : observerService->RemoveObserver(this, "profile-before-change");
1033 :
1034 : // Shutting down, let's sync the data.
1035 0 : ProfileStopped();
1036 :
1037 0 : return NS_OK;
1038 : }
1039 :
1040 0 : MOZ_ASSERT(false, "ServiceWorkerRegistrar got unexpected topic!");
1041 : return NS_ERROR_UNEXPECTED;
1042 : }
1043 :
1044 : } // namespace dom
1045 : } // namespace mozilla
|