Esempio n. 1
0
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;
}