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. nsCOMPtr<nsIFile> file; { MonitorAutoLock lock(mMonitor); if (!mProfileDir) { return NS_ERROR_FAILURE; } nsresult rv = mProfileDir->Clone(getter_AddRefs(file)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } nsresult 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); OriginAttributes 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 fetchFlag; GET_LINE(fetchFlag); if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) && !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) { return NS_ERROR_INVALID_ARG; } entry->currentWorkerHandlesFetch() = fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE); nsAutoCString cacheName; GET_LINE(cacheName); CopyUTF8toUTF16(cacheName, entry->cacheName()); nsAutoCString loadFlags; GET_LINE(loadFlags); entry->loadFlags() = loadFlags.ToInteger(&rv, 16); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } else if (entry->loadFlags() != nsIRequest::LOAD_NORMAL && entry->loadFlags() != nsIRequest::VALIDATE_ALWAYS) { return NS_ERROR_INVALID_ARG; } } else if (version.EqualsLiteral("5")) { overwrite = true; dedupe = true; nsAutoCString suffix; GET_LINE(suffix); OriginAttributes 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 fetchFlag; GET_LINE(fetchFlag); if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) && !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) { return NS_ERROR_INVALID_ARG; } entry->currentWorkerHandlesFetch() = fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE); nsAutoCString cacheName; GET_LINE(cacheName); CopyUTF8toUTF16(cacheName, entry->cacheName()); entry->loadFlags() = nsIRequest::VALIDATE_ALWAYS; } else if (version.EqualsLiteral("4")) { overwrite = true; dedupe = true; nsAutoCString suffix; GET_LINE(suffix); OriginAttributes 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()); // default handlesFetch flag to Enabled entry->currentWorkerHandlesFetch() = true; nsAutoCString cacheName; GET_LINE(cacheName); CopyUTF8toUTF16(cacheName, entry->cacheName()); entry->loadFlags() = nsIRequest::VALIDATE_ALWAYS; } else if (version.EqualsLiteral("3")) { overwrite = true; dedupe = true; nsAutoCString suffix; GET_LINE(suffix); OriginAttributes 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()); // default handlesFetch flag to Enabled entry->currentWorkerHandlesFetch() = true; nsAutoCString cacheName; GET_LINE(cacheName); CopyUTF8toUTF16(cacheName, entry->cacheName()); entry->loadFlags() = nsIRequest::VALIDATE_ALWAYS; } else if (version.EqualsLiteral("2")) { overwrite = true; dedupe = true; nsAutoCString suffix; GET_LINE(suffix); OriginAttributes 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()); // default handlesFetch flag to Enabled entry->currentWorkerHandlesFetch() = true; nsAutoCString cacheName; GET_LINE(cacheName); CopyUTF8toUTF16(cacheName, entry->cacheName()); // waitingCacheName is no more used in latest version. GET_LINE(unused); entry->loadFlags() = nsIRequest::VALIDATE_ALWAYS; } 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; }