void ServiceWorkerRegistrar::RegisterServiceWorker( const ServiceWorkerRegistrationData& aData) { AssertIsOnBackgroundThread(); if (mShuttingDown) { NS_WARNING("Failed to register a serviceWorker during shutting down."); return; } { MonitorAutoLock lock(mMonitor); MOZ_ASSERT(mDataLoaded); bool found = false; for (uint32_t i = 0, len = mData.Length(); i < len; ++i) { if (mData[i].scope() == aData.scope()) { mData[i] = aData; found = true; break; } } if (!found) { mData.AppendElement(aData); } } ScheduleSaveData(); }
bool ServiceWorkerManagerParent::RecvRegister( const ServiceWorkerRegistrationData& aData) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); // Basic validation. if (aData.scope().IsEmpty() || aData.scriptSpec().IsEmpty() || aData.principal().type() == PrincipalInfo::TNullPrincipalInfo || aData.principal().type() == PrincipalInfo::TSystemPrincipalInfo) { return false; } RefPtr<RegisterServiceWorkerCallback> callback = new RegisterServiceWorkerCallback(aData, mID); RefPtr<ContentParent> parent = BackgroundParent::GetContentParent(Manager()); // If the ContentParent is null we are dealing with a same-process actor. if (!parent) { callback->Run(); return true; } RefPtr<CheckPrincipalWithCallbackRunnable> runnable = new CheckPrincipalWithCallbackRunnable(parent.forget(), aData.principal(), callback); nsresult rv = NS_DispatchToMainThread(runnable); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv)); return true; }
void ServiceWorkerRegistrar::UnregisterServiceWorker( const PrincipalInfo& aPrincipalInfo, const nsACString& aScope) { AssertIsOnBackgroundThread(); if (mShuttingDown) { NS_WARNING("Failed to unregister a serviceWorker during shutting down."); return; } bool deleted = false; { MonitorAutoLock lock(mMonitor); MOZ_ASSERT(mDataLoaded); ServiceWorkerRegistrationData tmp; tmp.principal() = aPrincipalInfo; tmp.scope() = aScope; for (uint32_t i = 0; i < mData.Length(); ++i) { if (Equivalent(tmp, mData[i])) { mData.RemoveElementAt(i); deleted = true; break; } } } if (deleted) { ScheduleSaveData(); } }
TEST(ServiceWorkerRegistrar, TestWriteData) { { RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest; nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData(); for (int i = 0; i < 10; ++i) { ServiceWorkerRegistrationData* d = data.AppendElement(); nsAutoCString spec; spec.AppendPrintf("spec write %d", i); d->principal() = mozilla::ipc::ContentPrincipalInfo(mozilla::PrincipalOriginAttributes(i, i % 2), spec); d->scope().AppendPrintf("scope write %d", i); d->currentWorkerURL().AppendPrintf("currentWorkerURL write %d", i); d->cacheName().AppendPrintf("cacheName write %d", i); } nsresult rv = swr->TestWriteData(); ASSERT_EQ(NS_OK, rv) << "WriteData() should not fail"; } RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest; nsresult rv = swr->TestReadData(); ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail"; const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData(); ASSERT_EQ((uint32_t)10, data.Length()) << "10 entries should be found"; for (int i = 0; i < 10; ++i) { nsAutoCString test; ASSERT_EQ(data[i].principal().type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo); const mozilla::ipc::ContentPrincipalInfo& cInfo = data[i].principal(); mozilla::PrincipalOriginAttributes attrs(i, i % 2); nsAutoCString suffix, expectSuffix; attrs.CreateSuffix(expectSuffix); cInfo.attrs().CreateSuffix(suffix); ASSERT_STREQ(expectSuffix.get(), suffix.get()); test.AppendPrintf("scope write %d", i); ASSERT_STREQ(test.get(), cInfo.spec().get()); test.Truncate(); test.AppendPrintf("scope write %d", i); ASSERT_STREQ(test.get(), data[i].scope().get()); test.Truncate(); test.AppendPrintf("currentWorkerURL write %d", i); ASSERT_STREQ(test.get(), data[i].currentWorkerURL().get()); test.Truncate(); test.AppendPrintf("cacheName write %d", i); ASSERT_STREQ(test.get(), NS_ConvertUTF16toUTF8(data[i].cacheName()).get()); } }
TEST(ServiceWorkerRegistrar, TestDedupeWrite) { { RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest; for (int i = 0; i < 10; ++i) { ServiceWorkerRegistrationData reg; reg.scope() = NS_LITERAL_CSTRING("scope write dedupe"); reg.currentWorkerURL() = nsPrintfCString("currentWorkerURL write %d", i); reg.cacheName() = NS_ConvertUTF8toUTF16(nsPrintfCString("cacheName write %d", i)); nsAutoCString spec; spec.AppendPrintf("spec write dedupe/%d", i); reg.principal() = mozilla::ipc::ContentPrincipalInfo(mozilla::PrincipalOriginAttributes(0, false), spec); swr->TestRegisterServiceWorker(reg); } nsresult rv = swr->TestWriteData(); ASSERT_EQ(NS_OK, rv) << "WriteData() should not fail"; } RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest; nsresult rv = swr->TestReadData(); ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail"; // Duplicate entries should be removed. const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData(); ASSERT_EQ((uint32_t)1, data.Length()) << "1 entry should be found"; ASSERT_EQ(data[0].principal().type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo); const mozilla::ipc::ContentPrincipalInfo& cInfo = data[0].principal(); mozilla::PrincipalOriginAttributes attrs(0, false); nsAutoCString suffix, expectSuffix; attrs.CreateSuffix(expectSuffix); cInfo.attrs().CreateSuffix(suffix); // Last entry passed to RegisterServiceWorkerInternal() should overwrite // previous values. So expect "9" in values here. ASSERT_STREQ(expectSuffix.get(), suffix.get()); ASSERT_STREQ("scope write dedupe", cInfo.spec().get()); ASSERT_STREQ("scope write dedupe", data[0].scope().get()); ASSERT_STREQ("currentWorkerURL write 9", data[0].currentWorkerURL().get()); ASSERT_STREQ("cacheName write 9", NS_ConvertUTF16toUTF8(data[0].cacheName()).get()); }
nsresult ServiceWorkerRegistrar::ReadData() { // We cannot assert about the correct thread because normally this method // runs on a IO thread, but in gTests we call it from the main-thread. MOZ_ASSERT(mProfileDir); nsCOMPtr<nsIFile> file; nsresult rv = mProfileDir->Clone(getter_AddRefs(file)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } bool exists; rv = file->Exists(&exists); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } if (!exists) { return NS_OK; } nsCOMPtr<nsIInputStream> stream; rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(stream); MOZ_ASSERT(lineInputStream); nsAutoCString line; bool hasMoreLines; rv = lineInputStream->ReadLine(line, &hasMoreLines); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } // The file is corrupted ? // FIXME: in case we implement a version 2, we should inform the user using // the console about this issue. if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_VERSION)) { return NS_ERROR_FAILURE; } while (hasMoreLines) { ServiceWorkerRegistrationData* entry = mData.AppendElement(); #define GET_LINE(x) \ rv = lineInputStream->ReadLine(x, &hasMoreLines); \ if (NS_WARN_IF(NS_FAILED(rv))) { \ return rv; \ } \ if (NS_WARN_IF(!hasMoreLines)) { \ return NS_ERROR_FAILURE; \ } GET_LINE(line); if (line.EqualsLiteral(SERVICEWORKERREGISTRAR_SYSTEM_PRINCIPAL)) { entry->principal() = mozilla::ipc::SystemPrincipalInfo(); } else { if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_CONTENT_PRINCIPAL)) { return NS_ERROR_FAILURE; } GET_LINE(line); uint32_t appId = line.ToInteger(&rv); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } GET_LINE(line); if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) && !line.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) { return NS_ERROR_FAILURE; } bool isInBrowserElement = line.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE); GET_LINE(line); entry->principal() = mozilla::ipc::ContentPrincipalInfo(appId, isInBrowserElement, line); } GET_LINE(entry->scope()); GET_LINE(entry->scriptSpec()); GET_LINE(entry->currentWorkerURL()); #undef GET_LINE rv = lineInputStream->ReadLine(line, &hasMoreLines); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_TERMINATOR)) { return NS_ERROR_FAILURE; } } return NS_OK; }
nsresult ServiceWorkerRegistrar::ReadData() { // We cannot assert about the correct thread because normally this method // runs on a IO thread, but in gTests we call it from the main-thread. MOZ_ASSERT(mProfileDir); nsCOMPtr<nsIFile> file; nsresult rv = mProfileDir->Clone(getter_AddRefs(file)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } bool exists; rv = file->Exists(&exists); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } if (!exists) { return NS_OK; } nsCOMPtr<nsIInputStream> stream; rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(stream); MOZ_ASSERT(lineInputStream); nsAutoCString version; bool hasMoreLines; rv = lineInputStream->ReadLine(version, &hasMoreLines); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } if (!IsSupportedVersion(version)) { nsContentUtils::LogMessageToConsole(nsPrintfCString( "Unsupported service worker registrar version: %s", version.get()).get()); return NS_ERROR_FAILURE; } nsTArray<ServiceWorkerRegistrationData> tmpData; bool overwrite = false; bool dedupe = false; while (hasMoreLines) { ServiceWorkerRegistrationData* entry = tmpData.AppendElement(); #define GET_LINE(x) \ rv = lineInputStream->ReadLine(x, &hasMoreLines); \ if (NS_WARN_IF(NS_FAILED(rv))) { \ return rv; \ } \ if (NS_WARN_IF(!hasMoreLines)) { \ return NS_ERROR_FAILURE; \ } nsAutoCString line; nsAutoCString unused; if (version.EqualsLiteral(SERVICEWORKERREGISTRAR_VERSION)) { nsAutoCString suffix; GET_LINE(suffix); PrincipalOriginAttributes attrs; if (!attrs.PopulateFromSuffix(suffix)) { return NS_ERROR_INVALID_ARG; } GET_LINE(entry->scope()); entry->principal() = mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope()); GET_LINE(entry->currentWorkerURL()); nsAutoCString cacheName; GET_LINE(cacheName); CopyUTF8toUTF16(cacheName, entry->cacheName()); } else if (version.EqualsLiteral("3")) { overwrite = true; dedupe = true; nsAutoCString suffix; GET_LINE(suffix); PrincipalOriginAttributes attrs; if (!attrs.PopulateFromSuffix(suffix)) { return NS_ERROR_INVALID_ARG; } // principal spec is no longer used; we use scope directly instead GET_LINE(unused); GET_LINE(entry->scope()); entry->principal() = mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope()); GET_LINE(entry->currentWorkerURL()); nsAutoCString cacheName; GET_LINE(cacheName); CopyUTF8toUTF16(cacheName, entry->cacheName()); } else if (version.EqualsLiteral("2")) { overwrite = true; dedupe = true; nsAutoCString suffix; GET_LINE(suffix); PrincipalOriginAttributes attrs; if (!attrs.PopulateFromSuffix(suffix)) { return NS_ERROR_INVALID_ARG; } // principal spec is no longer used; we use scope directly instead GET_LINE(unused); GET_LINE(entry->scope()); entry->principal() = mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope()); // scriptSpec is no more used in latest version. GET_LINE(unused); GET_LINE(entry->currentWorkerURL()); nsAutoCString cacheName; GET_LINE(cacheName); CopyUTF8toUTF16(cacheName, entry->cacheName()); // waitingCacheName is no more used in latest version. GET_LINE(unused); } else { MOZ_ASSERT_UNREACHABLE("Should never get here!"); } #undef GET_LINE rv = lineInputStream->ReadLine(line, &hasMoreLines); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_TERMINATOR)) { return NS_ERROR_FAILURE; } } stream->Close(); // Copy data over to mData. for (uint32_t i = 0; i < tmpData.Length(); ++i) { bool match = false; if (dedupe) { MOZ_ASSERT(overwrite); // If this is an old profile, then we might need to deduplicate. In // theory this can be removed in the future (Bug 1248449) for (uint32_t j = 0; j < mData.Length(); ++j) { // Use same comparison as RegisterServiceWorker. Scope contains // basic origin information. Combine with any principal attributes. if (Equivalent(tmpData[i], mData[j])) { // Last match wins, just like legacy loading used to do in // the ServiceWorkerManager. mData[j] = tmpData[i]; // Dupe found, so overwrite file with reduced list. match = true; break; } } } else { #ifdef DEBUG // Otherwise assert no duplications in debug builds. for (uint32_t j = 0; j < mData.Length(); ++j) { MOZ_ASSERT(!Equivalent(tmpData[i], mData[j])); } #endif } if (!match) { mData.AppendElement(tmpData[i]); } } // Overwrite previous version. // Cannot call SaveData directly because gtest uses main-thread. if (overwrite && NS_FAILED(WriteData())) { NS_WARNING("Failed to write data for the ServiceWorker Registations."); DeleteData(); } return NS_OK; }