ServiceWorkerRegistrar.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "ServiceWorkerRegistrar.h"
  6. #include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
  7. #include "nsIEventTarget.h"
  8. #include "nsIInputStream.h"
  9. #include "nsILineInputStream.h"
  10. #include "nsIObserverService.h"
  11. #include "nsIOutputStream.h"
  12. #include "nsISafeOutputStream.h"
  13. #include "MainThreadUtils.h"
  14. #include "mozilla/ClearOnShutdown.h"
  15. #include "mozilla/ipc/BackgroundChild.h"
  16. #include "mozilla/ipc/BackgroundParent.h"
  17. #include "mozilla/ipc/PBackgroundChild.h"
  18. #include "mozilla/ModuleUtils.h"
  19. #include "mozilla/Services.h"
  20. #include "mozilla/StaticPtr.h"
  21. #include "nsAppDirectoryServiceDefs.h"
  22. #include "nsContentUtils.h"
  23. #include "nsDirectoryServiceUtils.h"
  24. #include "nsNetCID.h"
  25. #include "nsNetUtil.h"
  26. #include "nsServiceManagerUtils.h"
  27. #include "nsThreadUtils.h"
  28. #include "nsXULAppAPI.h"
  29. using namespace mozilla::ipc;
  30. namespace mozilla {
  31. namespace dom {
  32. namespace {
  33. static const char* gSupportedRegistrarVersions[] = {
  34. SERVICEWORKERREGISTRAR_VERSION,
  35. "3",
  36. "2"
  37. };
  38. StaticRefPtr<ServiceWorkerRegistrar> gServiceWorkerRegistrar;
  39. } // namespace
  40. NS_IMPL_ISUPPORTS(ServiceWorkerRegistrar,
  41. nsIObserver)
  42. void
  43. ServiceWorkerRegistrar::Initialize()
  44. {
  45. MOZ_ASSERT(!gServiceWorkerRegistrar);
  46. if (!XRE_IsParentProcess()) {
  47. return;
  48. }
  49. gServiceWorkerRegistrar = new ServiceWorkerRegistrar();
  50. ClearOnShutdown(&gServiceWorkerRegistrar);
  51. nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  52. if (obs) {
  53. DebugOnly<nsresult> rv = obs->AddObserver(gServiceWorkerRegistrar,
  54. "profile-after-change", false);
  55. MOZ_ASSERT(NS_SUCCEEDED(rv));
  56. rv = obs->AddObserver(gServiceWorkerRegistrar, "profile-before-change",
  57. false);
  58. MOZ_ASSERT(NS_SUCCEEDED(rv));
  59. }
  60. }
  61. /* static */ already_AddRefed<ServiceWorkerRegistrar>
  62. ServiceWorkerRegistrar::Get()
  63. {
  64. MOZ_ASSERT(XRE_IsParentProcess());
  65. MOZ_ASSERT(gServiceWorkerRegistrar);
  66. RefPtr<ServiceWorkerRegistrar> service = gServiceWorkerRegistrar.get();
  67. return service.forget();
  68. }
  69. ServiceWorkerRegistrar::ServiceWorkerRegistrar()
  70. : mMonitor("ServiceWorkerRegistrar.mMonitor")
  71. , mDataLoaded(false)
  72. , mShuttingDown(false)
  73. , mShutdownCompleteFlag(nullptr)
  74. , mRunnableCounter(0)
  75. {
  76. MOZ_ASSERT(NS_IsMainThread());
  77. }
  78. ServiceWorkerRegistrar::~ServiceWorkerRegistrar()
  79. {
  80. MOZ_ASSERT(!mRunnableCounter);
  81. }
  82. void
  83. ServiceWorkerRegistrar::GetRegistrations(
  84. nsTArray<ServiceWorkerRegistrationData>& aValues)
  85. {
  86. MOZ_ASSERT(NS_IsMainThread());
  87. MOZ_ASSERT(aValues.IsEmpty());
  88. MonitorAutoLock lock(mMonitor);
  89. // If we don't have the profile directory, profile is not started yet (and
  90. // probably we are in a utest).
  91. if (!mProfileDir) {
  92. return;
  93. }
  94. // We care just about the first execution because this can be blocked by
  95. // loading data from disk.
  96. static bool firstTime = true;
  97. TimeStamp startTime;
  98. if (firstTime) {
  99. startTime = TimeStamp::NowLoRes();
  100. }
  101. // Waiting for data loaded.
  102. mMonitor.AssertCurrentThreadOwns();
  103. while (!mDataLoaded) {
  104. mMonitor.Wait();
  105. }
  106. aValues.AppendElements(mData);
  107. if (firstTime) {
  108. firstTime = false;
  109. }
  110. }
  111. namespace {
  112. bool Equivalent(const ServiceWorkerRegistrationData& aLeft,
  113. const ServiceWorkerRegistrationData& aRight)
  114. {
  115. MOZ_ASSERT(aLeft.principal().type() ==
  116. mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
  117. MOZ_ASSERT(aRight.principal().type() ==
  118. mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
  119. const auto& leftPrincipal = aLeft.principal().get_ContentPrincipalInfo();
  120. const auto& rightPrincipal = aRight.principal().get_ContentPrincipalInfo();
  121. // Only compare the attributes, not the spec part of the principal.
  122. // The scope comparison above already covers the origin and codebase
  123. // principals include the full path in their spec which is not what
  124. // we want here.
  125. return aLeft.scope() == aRight.scope() &&
  126. leftPrincipal.attrs() == rightPrincipal.attrs();
  127. }
  128. } // anonymous namespace
  129. void
  130. ServiceWorkerRegistrar::RegisterServiceWorker(
  131. const ServiceWorkerRegistrationData& aData)
  132. {
  133. AssertIsOnBackgroundThread();
  134. if (mShuttingDown) {
  135. NS_WARNING("Failed to register a serviceWorker during shutting down.");
  136. return;
  137. }
  138. {
  139. MonitorAutoLock lock(mMonitor);
  140. MOZ_ASSERT(mDataLoaded);
  141. RegisterServiceWorkerInternal(aData);
  142. }
  143. ScheduleSaveData();
  144. }
  145. void
  146. ServiceWorkerRegistrar::UnregisterServiceWorker(
  147. const PrincipalInfo& aPrincipalInfo,
  148. const nsACString& aScope)
  149. {
  150. AssertIsOnBackgroundThread();
  151. if (mShuttingDown) {
  152. NS_WARNING("Failed to unregister a serviceWorker during shutting down.");
  153. return;
  154. }
  155. bool deleted = false;
  156. {
  157. MonitorAutoLock lock(mMonitor);
  158. MOZ_ASSERT(mDataLoaded);
  159. ServiceWorkerRegistrationData tmp;
  160. tmp.principal() = aPrincipalInfo;
  161. tmp.scope() = aScope;
  162. for (uint32_t i = 0; i < mData.Length(); ++i) {
  163. if (Equivalent(tmp, mData[i])) {
  164. mData.RemoveElementAt(i);
  165. deleted = true;
  166. break;
  167. }
  168. }
  169. }
  170. if (deleted) {
  171. ScheduleSaveData();
  172. }
  173. }
  174. void
  175. ServiceWorkerRegistrar::RemoveAll()
  176. {
  177. AssertIsOnBackgroundThread();
  178. if (mShuttingDown) {
  179. NS_WARNING("Failed to remove all the serviceWorkers during shutting down.");
  180. return;
  181. }
  182. bool deleted = false;
  183. {
  184. MonitorAutoLock lock(mMonitor);
  185. MOZ_ASSERT(mDataLoaded);
  186. deleted = !mData.IsEmpty();
  187. mData.Clear();
  188. }
  189. if (deleted) {
  190. ScheduleSaveData();
  191. }
  192. }
  193. void
  194. ServiceWorkerRegistrar::LoadData()
  195. {
  196. MOZ_ASSERT(!NS_IsMainThread());
  197. MOZ_ASSERT(!mDataLoaded);
  198. nsresult rv = ReadData();
  199. if (NS_WARN_IF(NS_FAILED(rv))) {
  200. DeleteData();
  201. // Also if the reading failed we have to notify what is waiting for data.
  202. }
  203. MonitorAutoLock lock(mMonitor);
  204. MOZ_ASSERT(!mDataLoaded);
  205. mDataLoaded = true;
  206. mMonitor.Notify();
  207. }
  208. nsresult
  209. ServiceWorkerRegistrar::ReadData()
  210. {
  211. // We cannot assert about the correct thread because normally this method
  212. // runs on a IO thread, but in gTests we call it from the main-thread.
  213. nsCOMPtr<nsIFile> file;
  214. {
  215. MonitorAutoLock lock(mMonitor);
  216. if (!mProfileDir) {
  217. return NS_ERROR_FAILURE;
  218. }
  219. nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
  220. if (NS_WARN_IF(NS_FAILED(rv))) {
  221. return rv;
  222. }
  223. }
  224. nsresult rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
  225. if (NS_WARN_IF(NS_FAILED(rv))) {
  226. return rv;
  227. }
  228. bool exists;
  229. rv = file->Exists(&exists);
  230. if (NS_WARN_IF(NS_FAILED(rv))) {
  231. return rv;
  232. }
  233. if (!exists) {
  234. return NS_OK;
  235. }
  236. nsCOMPtr<nsIInputStream> stream;
  237. rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
  238. if (NS_WARN_IF(NS_FAILED(rv))) {
  239. return rv;
  240. }
  241. nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(stream);
  242. MOZ_ASSERT(lineInputStream);
  243. nsAutoCString version;
  244. bool hasMoreLines;
  245. rv = lineInputStream->ReadLine(version, &hasMoreLines);
  246. if (NS_WARN_IF(NS_FAILED(rv))) {
  247. return rv;
  248. }
  249. if (!IsSupportedVersion(version)) {
  250. nsContentUtils::LogMessageToConsole(nsPrintfCString(
  251. "Unsupported service worker registrar version: %s", version.get()).get());
  252. return NS_ERROR_FAILURE;
  253. }
  254. nsTArray<ServiceWorkerRegistrationData> tmpData;
  255. bool overwrite = false;
  256. bool dedupe = false;
  257. while (hasMoreLines) {
  258. ServiceWorkerRegistrationData* entry = tmpData.AppendElement();
  259. #define GET_LINE(x) \
  260. rv = lineInputStream->ReadLine(x, &hasMoreLines); \
  261. if (NS_WARN_IF(NS_FAILED(rv))) { \
  262. return rv; \
  263. } \
  264. if (NS_WARN_IF(!hasMoreLines)) { \
  265. return NS_ERROR_FAILURE; \
  266. }
  267. nsAutoCString line;
  268. nsAutoCString unused;
  269. if (version.EqualsLiteral(SERVICEWORKERREGISTRAR_VERSION)) {
  270. nsAutoCString suffix;
  271. GET_LINE(suffix);
  272. PrincipalOriginAttributes attrs;
  273. if (!attrs.PopulateFromSuffix(suffix)) {
  274. return NS_ERROR_INVALID_ARG;
  275. }
  276. GET_LINE(entry->scope());
  277. entry->principal() =
  278. mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
  279. GET_LINE(entry->currentWorkerURL());
  280. nsAutoCString cacheName;
  281. GET_LINE(cacheName);
  282. CopyUTF8toUTF16(cacheName, entry->cacheName());
  283. } else if (version.EqualsLiteral("3")) {
  284. overwrite = true;
  285. dedupe = true;
  286. nsAutoCString suffix;
  287. GET_LINE(suffix);
  288. PrincipalOriginAttributes attrs;
  289. if (!attrs.PopulateFromSuffix(suffix)) {
  290. return NS_ERROR_INVALID_ARG;
  291. }
  292. // principal spec is no longer used; we use scope directly instead
  293. GET_LINE(unused);
  294. GET_LINE(entry->scope());
  295. entry->principal() =
  296. mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
  297. GET_LINE(entry->currentWorkerURL());
  298. nsAutoCString cacheName;
  299. GET_LINE(cacheName);
  300. CopyUTF8toUTF16(cacheName, entry->cacheName());
  301. } else if (version.EqualsLiteral("2")) {
  302. overwrite = true;
  303. dedupe = true;
  304. nsAutoCString suffix;
  305. GET_LINE(suffix);
  306. PrincipalOriginAttributes attrs;
  307. if (!attrs.PopulateFromSuffix(suffix)) {
  308. return NS_ERROR_INVALID_ARG;
  309. }
  310. // principal spec is no longer used; we use scope directly instead
  311. GET_LINE(unused);
  312. GET_LINE(entry->scope());
  313. entry->principal() =
  314. mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
  315. // scriptSpec is no more used in latest version.
  316. GET_LINE(unused);
  317. GET_LINE(entry->currentWorkerURL());
  318. nsAutoCString cacheName;
  319. GET_LINE(cacheName);
  320. CopyUTF8toUTF16(cacheName, entry->cacheName());
  321. // waitingCacheName is no more used in latest version.
  322. GET_LINE(unused);
  323. } else {
  324. MOZ_ASSERT_UNREACHABLE("Should never get here!");
  325. }
  326. #undef GET_LINE
  327. rv = lineInputStream->ReadLine(line, &hasMoreLines);
  328. if (NS_WARN_IF(NS_FAILED(rv))) {
  329. return rv;
  330. }
  331. if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_TERMINATOR)) {
  332. return NS_ERROR_FAILURE;
  333. }
  334. }
  335. stream->Close();
  336. // Copy data over to mData.
  337. for (uint32_t i = 0; i < tmpData.Length(); ++i) {
  338. bool match = false;
  339. if (dedupe) {
  340. MOZ_ASSERT(overwrite);
  341. // If this is an old profile, then we might need to deduplicate. In
  342. // theory this can be removed in the future (Bug 1248449)
  343. for (uint32_t j = 0; j < mData.Length(); ++j) {
  344. // Use same comparison as RegisterServiceWorker. Scope contains
  345. // basic origin information. Combine with any principal attributes.
  346. if (Equivalent(tmpData[i], mData[j])) {
  347. // Last match wins, just like legacy loading used to do in
  348. // the ServiceWorkerManager.
  349. mData[j] = tmpData[i];
  350. // Dupe found, so overwrite file with reduced list.
  351. match = true;
  352. break;
  353. }
  354. }
  355. } else {
  356. #ifdef DEBUG
  357. // Otherwise assert no duplications in debug builds.
  358. for (uint32_t j = 0; j < mData.Length(); ++j) {
  359. MOZ_ASSERT(!Equivalent(tmpData[i], mData[j]));
  360. }
  361. #endif
  362. }
  363. if (!match) {
  364. mData.AppendElement(tmpData[i]);
  365. }
  366. }
  367. // Overwrite previous version.
  368. // Cannot call SaveData directly because gtest uses main-thread.
  369. if (overwrite && NS_FAILED(WriteData())) {
  370. NS_WARNING("Failed to write data for the ServiceWorker Registations.");
  371. DeleteData();
  372. }
  373. return NS_OK;
  374. }
  375. void
  376. ServiceWorkerRegistrar::DeleteData()
  377. {
  378. // We cannot assert about the correct thread because normally this method
  379. // runs on a IO thread, but in gTests we call it from the main-thread.
  380. nsCOMPtr<nsIFile> file;
  381. {
  382. MonitorAutoLock lock(mMonitor);
  383. mData.Clear();
  384. if (!mProfileDir) {
  385. return;
  386. }
  387. nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
  388. if (NS_WARN_IF(NS_FAILED(rv))) {
  389. return;
  390. }
  391. }
  392. nsresult rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
  393. if (NS_WARN_IF(NS_FAILED(rv))) {
  394. return;
  395. }
  396. rv = file->Remove(false);
  397. if (rv == NS_ERROR_FILE_NOT_FOUND) {
  398. return;
  399. }
  400. if (NS_WARN_IF(NS_FAILED(rv))) {
  401. return;
  402. }
  403. }
  404. void
  405. ServiceWorkerRegistrar::RegisterServiceWorkerInternal(const ServiceWorkerRegistrationData& aData)
  406. {
  407. bool found = false;
  408. for (uint32_t i = 0, len = mData.Length(); i < len; ++i) {
  409. if (Equivalent(aData, mData[i])) {
  410. mData[i] = aData;
  411. found = true;
  412. break;
  413. }
  414. }
  415. if (!found) {
  416. mData.AppendElement(aData);
  417. }
  418. }
  419. class ServiceWorkerRegistrarSaveDataRunnable final : public Runnable
  420. {
  421. public:
  422. ServiceWorkerRegistrarSaveDataRunnable()
  423. : mThread(do_GetCurrentThread())
  424. {
  425. AssertIsOnBackgroundThread();
  426. }
  427. NS_IMETHOD
  428. Run() override
  429. {
  430. RefPtr<ServiceWorkerRegistrar> service = ServiceWorkerRegistrar::Get();
  431. MOZ_ASSERT(service);
  432. service->SaveData();
  433. RefPtr<Runnable> runnable =
  434. NewRunnableMethod(service, &ServiceWorkerRegistrar::DataSaved);
  435. nsresult rv = mThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
  436. if (NS_WARN_IF(NS_FAILED(rv))) {
  437. return rv;
  438. }
  439. return NS_OK;
  440. }
  441. private:
  442. nsCOMPtr<nsIThread> mThread;
  443. };
  444. void
  445. ServiceWorkerRegistrar::ScheduleSaveData()
  446. {
  447. AssertIsOnBackgroundThread();
  448. MOZ_ASSERT(!mShuttingDown);
  449. nsCOMPtr<nsIEventTarget> target =
  450. do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
  451. MOZ_ASSERT(target, "Must have stream transport service");
  452. RefPtr<Runnable> runnable =
  453. new ServiceWorkerRegistrarSaveDataRunnable();
  454. nsresult rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
  455. if (NS_WARN_IF(NS_FAILED(rv))) {
  456. return;
  457. }
  458. ++mRunnableCounter;
  459. }
  460. void
  461. ServiceWorkerRegistrar::ShutdownCompleted()
  462. {
  463. MOZ_ASSERT(NS_IsMainThread());
  464. MOZ_ASSERT(mShutdownCompleteFlag && !*mShutdownCompleteFlag);
  465. *mShutdownCompleteFlag = true;
  466. }
  467. void
  468. ServiceWorkerRegistrar::SaveData()
  469. {
  470. MOZ_ASSERT(!NS_IsMainThread());
  471. nsresult rv = WriteData();
  472. if (NS_FAILED(rv)) {
  473. NS_WARNING("Failed to write data for the ServiceWorker Registations.");
  474. DeleteData();
  475. }
  476. }
  477. void
  478. ServiceWorkerRegistrar::DataSaved()
  479. {
  480. AssertIsOnBackgroundThread();
  481. MOZ_ASSERT(mRunnableCounter);
  482. --mRunnableCounter;
  483. MaybeScheduleShutdownCompleted();
  484. }
  485. void
  486. ServiceWorkerRegistrar::MaybeScheduleShutdownCompleted()
  487. {
  488. AssertIsOnBackgroundThread();
  489. if (mRunnableCounter || !mShuttingDown) {
  490. return;
  491. }
  492. RefPtr<Runnable> runnable =
  493. NewRunnableMethod(this, &ServiceWorkerRegistrar::ShutdownCompleted);
  494. nsresult rv = NS_DispatchToMainThread(runnable);
  495. if (NS_WARN_IF(NS_FAILED(rv))) {
  496. return;
  497. }
  498. }
  499. bool
  500. ServiceWorkerRegistrar::IsSupportedVersion(const nsACString& aVersion) const
  501. {
  502. uint32_t numVersions = ArrayLength(gSupportedRegistrarVersions);
  503. for (uint32_t i = 0; i < numVersions; i++) {
  504. if (aVersion.EqualsASCII(gSupportedRegistrarVersions[i])) {
  505. return true;
  506. }
  507. }
  508. return false;
  509. }
  510. nsresult
  511. ServiceWorkerRegistrar::WriteData()
  512. {
  513. // We cannot assert about the correct thread because normally this method
  514. // runs on a IO thread, but in gTests we call it from the main-thread.
  515. nsCOMPtr<nsIFile> file;
  516. {
  517. MonitorAutoLock lock(mMonitor);
  518. if (!mProfileDir) {
  519. return NS_ERROR_FAILURE;
  520. }
  521. nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
  522. if (NS_WARN_IF(NS_FAILED(rv))) {
  523. return rv;
  524. }
  525. }
  526. nsresult rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
  527. if (NS_WARN_IF(NS_FAILED(rv))) {
  528. return rv;
  529. }
  530. // We need a lock to take a snapshot of the data.
  531. nsTArray<ServiceWorkerRegistrationData> data;
  532. {
  533. MonitorAutoLock lock(mMonitor);
  534. data = mData;
  535. }
  536. nsCOMPtr<nsIOutputStream> stream;
  537. rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(stream), file);
  538. if (NS_WARN_IF(NS_FAILED(rv))) {
  539. return rv;
  540. }
  541. nsAutoCString buffer;
  542. buffer.AppendLiteral(SERVICEWORKERREGISTRAR_VERSION);
  543. buffer.Append('\n');
  544. uint32_t count;
  545. rv = stream->Write(buffer.Data(), buffer.Length(), &count);
  546. if (NS_WARN_IF(NS_FAILED(rv))) {
  547. return rv;
  548. }
  549. if (count != buffer.Length()) {
  550. return NS_ERROR_UNEXPECTED;
  551. }
  552. for (uint32_t i = 0, len = data.Length(); i < len; ++i) {
  553. const mozilla::ipc::PrincipalInfo& info = data[i].principal();
  554. MOZ_ASSERT(info.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
  555. const mozilla::ipc::ContentPrincipalInfo& cInfo =
  556. info.get_ContentPrincipalInfo();
  557. nsAutoCString suffix;
  558. cInfo.attrs().CreateSuffix(suffix);
  559. buffer.Truncate();
  560. buffer.Append(suffix.get());
  561. buffer.Append('\n');
  562. buffer.Append(data[i].scope());
  563. buffer.Append('\n');
  564. buffer.Append(data[i].currentWorkerURL());
  565. buffer.Append('\n');
  566. buffer.Append(NS_ConvertUTF16toUTF8(data[i].cacheName()));
  567. buffer.Append('\n');
  568. buffer.AppendLiteral(SERVICEWORKERREGISTRAR_TERMINATOR);
  569. buffer.Append('\n');
  570. rv = stream->Write(buffer.Data(), buffer.Length(), &count);
  571. if (NS_WARN_IF(NS_FAILED(rv))) {
  572. return rv;
  573. }
  574. if (count != buffer.Length()) {
  575. return NS_ERROR_UNEXPECTED;
  576. }
  577. }
  578. nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(stream);
  579. MOZ_ASSERT(safeStream);
  580. rv = safeStream->Finish();
  581. if (NS_WARN_IF(NS_FAILED(rv))) {
  582. return rv;
  583. }
  584. return NS_OK;
  585. }
  586. void
  587. ServiceWorkerRegistrar::ProfileStarted()
  588. {
  589. MOZ_ASSERT(NS_IsMainThread());
  590. MonitorAutoLock lock(mMonitor);
  591. MOZ_DIAGNOSTIC_ASSERT(!mProfileDir);
  592. nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
  593. getter_AddRefs(mProfileDir));
  594. if (NS_WARN_IF(NS_FAILED(rv))) {
  595. return;
  596. }
  597. nsCOMPtr<nsIEventTarget> target =
  598. do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
  599. MOZ_ASSERT(target, "Must have stream transport service");
  600. nsCOMPtr<nsIRunnable> runnable =
  601. NewRunnableMethod(this, &ServiceWorkerRegistrar::LoadData);
  602. rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
  603. if (NS_FAILED(rv)) {
  604. NS_WARNING("Failed to dispatch the LoadDataRunnable.");
  605. }
  606. }
  607. void
  608. ServiceWorkerRegistrar::ProfileStopped()
  609. {
  610. MOZ_ASSERT(NS_IsMainThread());
  611. MonitorAutoLock lock(mMonitor);
  612. if (!mProfileDir) {
  613. nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
  614. getter_AddRefs(mProfileDir));
  615. if (NS_WARN_IF(NS_FAILED(rv))) {
  616. return;
  617. }
  618. }
  619. // We must set the pointer before potentially entering the fast-path shutdown
  620. // below.
  621. bool completed = false;
  622. mShutdownCompleteFlag = &completed;
  623. PBackgroundChild* child = BackgroundChild::GetForCurrentThread();
  624. if (!child) {
  625. // Mutations to the ServiceWorkerRegistrar happen on the PBackground thread,
  626. // issued by the ServiceWorkerManagerService, so the appropriate place to
  627. // trigger shutdown is on that thread.
  628. //
  629. // However, it's quite possible that the PBackground thread was not brought
  630. // into existence for xpcshell tests. We don't cause it to be created
  631. // ourselves for any reason, for example.
  632. //
  633. // In this scenario, we know that:
  634. // - We will receive exactly one call to ourself from BlockShutdown() and
  635. // BlockShutdown() will be called (at most) once.
  636. // - The only way our Shutdown() method gets called is via
  637. // BackgroundParentImpl::RecvShutdownServiceWorkerRegistrar() being
  638. // invoked, which only happens if we get to that send below here that we
  639. // can't get to.
  640. // - All Shutdown() does is set mShuttingDown=true (essential for
  641. // invariants) and invoke MaybeScheduleShutdownCompleted().
  642. // - Since there is no PBackground thread, mRunnableCounter must be 0
  643. // because only ScheduleSaveData() increments it and it only runs on the
  644. // background thread, so it cannot have run. And so we would expect
  645. // MaybeScheduleShutdownCompleted() to schedule an invocation of
  646. // ShutdownCompleted on the main thread.
  647. //
  648. // So it's appropriate for us to set mShuttingDown=true (as Shutdown would
  649. // do) and directly invoke ShutdownCompleted() (as Shutdown would indirectly
  650. // do via MaybeScheduleShutdownCompleted).
  651. mShuttingDown = true;
  652. ShutdownCompleted();
  653. return;
  654. }
  655. child->SendShutdownServiceWorkerRegistrar();
  656. nsCOMPtr<nsIThread> thread(do_GetCurrentThread());
  657. while (true) {
  658. MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(thread));
  659. if (completed) {
  660. break;
  661. }
  662. }
  663. }
  664. void
  665. ServiceWorkerRegistrar::Shutdown()
  666. {
  667. AssertIsOnBackgroundThread();
  668. MOZ_ASSERT(!mShuttingDown);
  669. mShuttingDown = true;
  670. MaybeScheduleShutdownCompleted();
  671. }
  672. NS_IMETHODIMP
  673. ServiceWorkerRegistrar::Observe(nsISupports* aSubject, const char* aTopic,
  674. const char16_t* aData)
  675. {
  676. MOZ_ASSERT(NS_IsMainThread());
  677. if (!strcmp(aTopic, "profile-after-change")) {
  678. nsCOMPtr<nsIObserverService> observerService =
  679. services::GetObserverService();
  680. observerService->RemoveObserver(this, "profile-after-change");
  681. // The profile is fully loaded, now we can proceed with the loading of data
  682. // from disk.
  683. ProfileStarted();
  684. return NS_OK;
  685. }
  686. if (!strcmp(aTopic, "profile-before-change")) {
  687. // Hygiene; gServiceWorkerRegistrar should still be keeping a reference
  688. // alive well past this phase of shutdown, but it's bad form to drop your
  689. // last potentially owning reference and then make a call that requires you
  690. // to still be alive, especially when you spin a nested event loop.
  691. RefPtr<ServiceWorkerRegistrar> kungFuDeathGrip(this);
  692. nsCOMPtr<nsIObserverService> observerService =
  693. services::GetObserverService();
  694. observerService->RemoveObserver(this, "profile-before-change");
  695. // Shutting down, let's sync the data.
  696. ProfileStopped();
  697. return NS_OK;
  698. }
  699. MOZ_ASSERT(false, "ServiceWorkerRegistrar got unexpected topic!");
  700. return NS_ERROR_UNEXPECTED;
  701. }
  702. } // namespace dom
  703. } // namespace mozilla