Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/dom/reporting/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 23 kB image not shown  

Quelle  ReportingHeader.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "mozilla/dom/ReportingHeader.h"
#include <limits>

#include "js/Array.h"  // JS::GetArrayLength, JS::IsArrayObject
#include "js/JSON.h"
#include "js/PropertyAndElement.h"  // JS_GetElement
#include "mozilla/dom/ReportingBinding.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/SimpleGlobalObject.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/net/SFVService.h"
#include "mozilla/OriginAttributes.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPtr.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
#include "nsIEffectiveTLDService.h"
#include "nsIHttpChannel.h"
#include "nsIHttpProtocolHandler.h"
#include "nsIObserverService.h"
#include "nsIPrincipal.h"
#include "nsIRandomGenerator.h"
#include "nsIScriptError.h"
#include "nsNetUtil.h"
#include "nsXULAppAPI.h"

#define REPORTING_PURGE_ALL "reporting:purge-all"
#define REPORTING_PURGE_HOST "reporting:purge-host"

namespace mozilla::dom {

namespace {

StaticRefPtr<ReportingHeader> gReporting;

}  // namespace

/* static */
void ReportingHeader::Initialize() {
  MOZ_ASSERT(!gReporting);
  MOZ_ASSERT(NS_IsMainThread());

  if (!XRE_IsParentProcess()) {
    return;
  }

  RefPtr<ReportingHeader> service = new ReportingHeader();

  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
  if (NS_WARN_IF(!obs)) {
    return;
  }

  obs->AddObserver(service, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC, false);
  obs->AddObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
  obs->AddObserver(service, "clear-origin-attributes-data"false);
  obs->AddObserver(service, REPORTING_PURGE_HOST, false);
  obs->AddObserver(service, REPORTING_PURGE_ALL, false);

  gReporting = service;
}

/* static */
void ReportingHeader::Shutdown() {
  MOZ_ASSERT(NS_IsMainThread());

  if (!gReporting) {
    return;
  }

  RefPtr<ReportingHeader> service = gReporting;
  gReporting = nullptr;

  if (service->mCleanupTimer) {
    service->mCleanupTimer->Cancel();
    service->mCleanupTimer = nullptr;
  }

  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
  if (NS_WARN_IF(!obs)) {
    return;
  }

  obs->RemoveObserver(service, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC);
  obs->RemoveObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
  obs->RemoveObserver(service, "clear-origin-attributes-data");
  obs->RemoveObserver(service, REPORTING_PURGE_HOST);
  obs->RemoveObserver(service, REPORTING_PURGE_ALL);
}

ReportingHeader::ReportingHeader() = default;
ReportingHeader::~ReportingHeader() = default;

NS_IMETHODIMP
ReportingHeader::Observe(nsISupports* aSubject, const char* aTopic,
                         const char16_t* aData) {
  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
    Shutdown();
    return NS_OK;
  }

  // Pref disabled.
  if (!StaticPrefs::dom_reporting_header_enabled()) {
    return NS_OK;
  }

  if (!strcmp(aTopic, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC)) {
    nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aSubject);
    if (NS_WARN_IF(!channel)) {
      return NS_OK;
    }

    ReportingFromChannel(channel);
    return NS_OK;
  }

  if (!strcmp(aTopic, REPORTING_PURGE_HOST)) {
    RemoveOriginsFromHost(nsDependentString(aData));
    return NS_OK;
  }

  if (!strcmp(aTopic, "clear-origin-attributes-data")) {
    OriginAttributesPattern pattern;
    if (!pattern.Init(nsDependentString(aData))) {
      NS_ERROR("Cannot parse origin attributes pattern");
      return NS_ERROR_FAILURE;
    }

    RemoveOriginsFromOriginAttributesPattern(pattern);
    return NS_OK;
  }

  if (!strcmp(aTopic, REPORTING_PURGE_ALL)) {
    RemoveOrigins();
    return NS_OK;
  }

  return NS_ERROR_FAILURE;
}

void ReportingHeader::ReportingFromChannel(nsIHttpChannel* aChannel) {
  MOZ_ASSERT(aChannel);

  if (!StaticPrefs::dom_reporting_header_enabled()) {
    return;
  }

  // We want to use the final URI to check if Report-To should be allowed or
  // not.
  nsCOMPtr<nsIURI> uri;
  nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return;
  }

  if (!IsSecureURI(uri)) {
    return;
  }

  if (NS_UsePrivateBrowsing(aChannel)) {
    return;
  }

  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
  if (NS_WARN_IF(!ssm)) {
    return;
  }

  nsCOMPtr<nsIPrincipal> principal;
  rv = ssm->GetChannelURIPrincipal(aChannel, getter_AddRefs(principal));
  if (NS_WARN_IF(NS_FAILED(rv)) || !principal) {
    return;
  }

  nsAutoCString origin;
  rv = principal->GetOrigin(origin);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return;
  }

  // Parse Report-To and Reporting-Endpoints headers
  UniquePtr<Client> client;
  nsAutoCString header;

  if (NS_SUCCEEDED(
          aChannel->GetResponseHeader("Reporting-Endpoints"_ns, header))) {
    client = ParseReportingEndpointsHeader(header, uri);
  } else if (NS_SUCCEEDED(
                 aChannel->GetResponseHeader("Report-To"_ns, header))) {
    client = ParseReportToHeader(aChannel, uri, header);
  }

  if (!client) {
    return;
  }

  // Here we override the previous data.
  mOrigins.InsertOrUpdate(origin, std::move(client));

  MaybeCreateCleanupTimer();
}

/* static */
UniquePtr<ReportingHeader::Client>
ReportingHeader::ParseReportingEndpointsHeader(const nsACString& aHeaderValue,
                                               nsIURI* aURI) {
  nsCOMPtr<nsISFVService> sfv = mozilla::net::GetSFVService();

  nsAutoCString uriSpec;
  aURI->GetSpec(uriSpec);

  nsCOMPtr<nsIURI> baseURL;
  if (NS_FAILED(NS_NewURI(getter_AddRefs(baseURL), uriSpec))) {
    return nullptr;
  }

  nsCOMPtr<nsISFVDictionary> parsedHeader;
  if (NS_FAILED(
          sfv->ParseDictionary(aHeaderValue, getter_AddRefs(parsedHeader)))) {
    return nullptr;
  }

  nsTArray<nsCString> keys;
  if (NS_FAILED(parsedHeader->Keys(keys))) {
    return nullptr;
  }

  UniquePtr<Client> client = MakeUnique<Client>();

  for (const auto& key : keys) {
    // Extract an SFV data object from each dictionary entry
    nsCOMPtr<nsISFVItemOrInnerList> iil;
    if (NS_FAILED(parsedHeader->Get(key, getter_AddRefs(iil)))) {
      continue;
    }

    // An item needs to be extracted from the ItemOrInnerList member-value
    nsCOMPtr<nsISFVBareItem> value;
    if (nsCOMPtr<nsISFVInnerList> innerList = do_QueryInterface(iil)) {
      // Extract the first entry of each inner list, which should contain the
      // endpoint's URL string
      nsTArray<RefPtr<nsISFVItem>> items;

      if (NS_FAILED(innerList->GetItems(items))) {
        continue;
      }

      if (items.IsEmpty()) {
        continue;
      }

      nsCOMPtr<nsISFVItem> firstItem(items[0]);

      if (NS_FAILED(firstItem->GetValue(getter_AddRefs(value)))) {
        continue;
      }
    } else if (nsCOMPtr<nsISFVItem> listItem = do_QueryInterface(iil)) {
      if (NS_FAILED(listItem->GetValue(getter_AddRefs(value)))) {
        continue;
      }
    }

    // Ensure that the item's data type is a string, so the URL can be properly
    // parsed
    nsCOMPtr<nsISFVString> sfvString(do_QueryInterface(value));
    if (!sfvString) {
      continue;
    }

    nsAutoCString endpointURLString;
    if (NS_FAILED(sfvString->GetValue(endpointURLString))) {
      continue;
    }

    // Convert the URL string into a URI
    nsCOMPtr<nsIURI> endpointURL;
    nsresult rv = NS_NewURI(getter_AddRefs(endpointURL),
                            endpointURLString.get(), baseURL);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      continue;
    }

    if (!IsSecureURI(endpointURL)) {
      continue;
    }

    Group* group = client->mGroups.AppendElement();
    group->mCreationTime = TimeStamp::Now();
    group->mTTL = std::numeric_limits<int32_t>::max();
    group->mName = NS_ConvertUTF8toUTF16(key);

    // Use data extracted from dictionary entry to create an endpoint
    Endpoint* ep = group->mEndpoints.AppendElement();
    ep->mUrl = endpointURL;
    ep->mEndpointName = key;
    ep->mFailures = 0;
    ep->mPriority = 1;
    ep->mWeight = 1;
  }

  if (client->mGroups.IsEmpty()) {
    return nullptr;
  }

  return client;
}

/* static */ UniquePtr<ReportingHeader::Client>
ReportingHeader::ParseReportToHeader(nsIHttpChannel* aChannel, nsIURI* aURI,
                                     const nsACString& aHeaderValue) {
  MOZ_ASSERT(aURI);
  // aChannel can be null in gtest

  AutoJSAPI jsapi;

  JSObject* cleanGlobal =
      SimpleGlobalObject::Create(SimpleGlobalObject::GlobalType::BindingDetail);
  if (NS_WARN_IF(!cleanGlobal)) {
    return nullptr;
  }

  if (NS_WARN_IF(!jsapi.Init(cleanGlobal))) {
    return nullptr;
  }

  // WebIDL dictionary parses single items. Let's create a object to parse the
  // header.
  nsAutoString json;
  json.AppendASCII("{ \"items\": [");
  json.Append(NS_ConvertUTF8toUTF16(aHeaderValue));
  json.AppendASCII("]}");

  JSContext* cx = jsapi.cx();
  JS::Rooted<JS::Value> jsonValue(cx);
  bool ok = JS_ParseJSON(cx, json.BeginReading(), json.Length(), &jsonValue);
  if (!ok) {
    LogToConsoleInvalidJSON(aChannel, aURI);
    return nullptr;
  }

  dom::ReportingHeaderValue data;
  if (!data.Init(cx, jsonValue)) {
    LogToConsoleInvalidJSON(aChannel, aURI);
    return nullptr;
  }

  if (!data.mItems.WasPassed() || data.mItems.Value().IsEmpty()) {
    return nullptr;
  }

  UniquePtr<Client> client = MakeUnique<Client>();

  for (const dom::ReportingItem& item : data.mItems.Value()) {
    nsAutoString groupName;

    if (item.mGroup.isUndefined()) {
      groupName.AssignLiteral("default");
    } else if (!item.mGroup.isString()) {
      LogToConsoleInvalidNameItem(aChannel, aURI);
      continue;
    } else {
      JS::Rooted<JSString*> groupStr(cx, item.mGroup.toString());
      MOZ_ASSERT(groupStr);

      nsAutoJSString string;
      if (NS_WARN_IF(!string.init(cx, groupStr))) {
        continue;
      }

      groupName = string;
    }

    if (!item.mMax_age.isNumber() || !item.mEndpoints.isObject()) {
      LogToConsoleIncompleteItem(aChannel, aURI, groupName);
      continue;
    }

    JS::Rooted<JSObject*> endpoints(cx, &item.mEndpoints.toObject());
    MOZ_ASSERT(endpoints);

    bool isArray = false;
    if (!JS::IsArrayObject(cx, endpoints, &isArray) || !isArray) {
      LogToConsoleIncompleteItem(aChannel, aURI, groupName);
      continue;
    }

    uint32_t endpointsLength;
    if (!JS::GetArrayLength(cx, endpoints, &endpointsLength) ||
        endpointsLength == 0) {
      LogToConsoleIncompleteItem(aChannel, aURI, groupName);
      continue;
    }

    const auto [begin, end] = client->mGroups.NonObservingRange();
    if (std::any_of(begin, end, [&groupName](const Group& group) {
          return group.mName == groupName;
        })) {
      LogToConsoleDuplicateGroup(aChannel, aURI, groupName);
      continue;
    }

    Group* group = client->mGroups.AppendElement();
    group->mName = groupName;
    group->mIncludeSubdomains = item.mInclude_subdomains;
    group->mTTL = item.mMax_age.toNumber();
    group->mCreationTime = TimeStamp::Now();

    for (uint32_t i = 0; i < endpointsLength; ++i) {
      JS::Rooted<JS::Value> element(cx);
      if (!JS_GetElement(cx, endpoints, i, &element)) {
        return nullptr;
      }

      RootedDictionary<ReportingEndpoint> endpoint(cx);
      if (!endpoint.Init(cx, element)) {
        LogToConsoleIncompleteEndpoint(aChannel, aURI, groupName);
        continue;
      }

      if (!endpoint.mUrl.isString() ||
          (!endpoint.mPriority.isUndefined() &&
           (!endpoint.mPriority.isNumber() ||
            endpoint.mPriority.toNumber() < 0)) ||
          (!endpoint.mWeight.isUndefined() &&
           (!endpoint.mWeight.isNumber() || endpoint.mWeight.toNumber() < 0))) {
        LogToConsoleIncompleteEndpoint(aChannel, aURI, groupName);
        continue;
      }

      JS::Rooted<JSString*> endpointUrl(cx, endpoint.mUrl.toString());
      MOZ_ASSERT(endpointUrl);

      nsAutoJSString endpointString;
      if (NS_WARN_IF(!endpointString.init(cx, endpointUrl))) {
        continue;
      }

      nsCOMPtr<nsIURI> uri;
      nsresult rv = NS_NewURI(getter_AddRefs(uri), endpointString);
      if (NS_FAILED(rv)) {
        LogToConsoleInvalidURLEndpoint(aChannel, aURI, groupName,
                                       endpointString);
        continue;
      }

      Endpoint* ep = group->mEndpoints.AppendElement();
      ep->mUrl = uri;
      ep->mPriority =
          endpoint.mPriority.isUndefined() ? 1 : endpoint.mPriority.toNumber();
      ep->mWeight =
          endpoint.mWeight.isUndefined() ? 1 : endpoint.mWeight.toNumber();
    }
  }

  if (client->mGroups.IsEmpty()) {
    return nullptr;
  }

  return client;
}

/* static */
bool ReportingHeader::IsSecureURI(nsIURI* aURI) {
  MOZ_ASSERT(aURI);

  bool prioriAuthenticated = false;
  if (NS_WARN_IF(NS_FAILED(NS_URIChainHasFlags(
          aURI, nsIProtocolHandler::URI_IS_POTENTIALLY_TRUSTWORTHY,
          &prioriAuthenticated)))) {
    return false;
  }

  return prioriAuthenticated;
}

/* static */
void ReportingHeader::LogToConsoleInvalidJSON(nsIHttpChannel* aChannel,
                                              nsIURI* aURI) {
  nsTArray<nsString> params;
  LogToConsoleInternal(aChannel, aURI, "ReportingHeaderInvalidJSON", params);
}

/* static */
void ReportingHeader::LogToConsoleDuplicateGroup(nsIHttpChannel* aChannel,
                                                 nsIURI* aURI,
                                                 const nsAString& aName) {
  nsTArray<nsString> params;
  params.AppendElement(aName);

  LogToConsoleInternal(aChannel, aURI, "ReportingHeaderDuplicateGroup", params);
}

/* static */
void ReportingHeader::LogToConsoleInvalidNameItem(nsIHttpChannel* aChannel,
                                                  nsIURI* aURI) {
  nsTArray<nsString> params;
  LogToConsoleInternal(aChannel, aURI, "ReportingHeaderInvalidNameItem",
                       params);
}

/* static */
void ReportingHeader::LogToConsoleIncompleteItem(nsIHttpChannel* aChannel,
                                                 nsIURI* aURI,
                                                 const nsAString& aName) {
  nsTArray<nsString> params;
  params.AppendElement(aName);

  LogToConsoleInternal(aChannel, aURI, "ReportingHeaderInvalidItem", params);
}

/* static */
void ReportingHeader::LogToConsoleIncompleteEndpoint(nsIHttpChannel* aChannel,
                                                     nsIURI* aURI,
                                                     const nsAString& aName) {
  nsTArray<nsString> params;
  params.AppendElement(aName);

  LogToConsoleInternal(aChannel, aURI, "ReportingHeaderInvalidEndpoint",
                       params);
}

/* static */
void ReportingHeader::LogToConsoleInvalidURLEndpoint(nsIHttpChannel* aChannel,
                                                     nsIURI* aURI,
                                                     const nsAString& aName,
                                                     const nsAString& aURL) {
  nsTArray<nsString> params;
  params.AppendElement(aURL);
  params.AppendElement(aName);

  LogToConsoleInternal(aChannel, aURI, "ReportingHeaderInvalidURLEndpoint",
                       params);
}

/* static */
void ReportingHeader::LogToConsoleInternal(nsIHttpChannel* aChannel,
                                           nsIURI* aURI, const char* aMsg,
                                           const nsTArray<nsString>& aParams) {
  MOZ_ASSERT(aURI);

  if (!aChannel) {
    // We are in a gtest.
    return;
  }

  uint64_t windowID = 0;

  nsresult rv = aChannel->GetTopLevelContentWindowId(&windowID);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return;
  }

  if (!windowID) {
    nsCOMPtr<nsILoadGroup> loadGroup;
    nsresult rv = aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return;
    }

    if (loadGroup) {
      windowID = nsContentUtils::GetInnerWindowID(loadGroup);
    }
  }

  nsAutoString localizedMsg;
  rv = nsContentUtils::FormatLocalizedString(
      nsContentUtils::eSECURITY_PROPERTIES, aMsg, aParams, localizedMsg);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return;
  }

  rv = nsContentUtils::ReportToConsoleByWindowID(
      localizedMsg, nsIScriptError::infoFlag, "Reporting"_ns, windowID,
      SourceLocation(aURI));
  Unused << NS_WARN_IF(NS_FAILED(rv));
}

/* static */
void ReportingHeader::GetEndpointForReport(
    const nsAString& aGroupName,
    const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
    nsACString& aEndpointURI) {
  auto principalOrErr = PrincipalInfoToPrincipal(aPrincipalInfo);
  if (NS_WARN_IF(principalOrErr.isErr())) {
    return;
  }

  nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
  GetEndpointForReport(aGroupName, principal, aEndpointURI);
}

/* static */
void ReportingHeader::GetEndpointForReport(const nsAString& aGroupName,
                                           nsIPrincipal* aPrincipal,
                                           nsACString& aEndpointURI) {
  MOZ_ASSERT(aEndpointURI.IsEmpty());

  if (!gReporting) {
    return;
  }

  nsAutoCString origin;
  nsresult rv = aPrincipal->GetOrigin(origin);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return;
  }

  Client* client = gReporting->mOrigins.Get(origin);
  if (!client) {
    return;
  }

  const auto [begin, end] = client->mGroups.NonObservingRange();
  const auto foundIt = std::find_if(
      begin, end,
      [&aGroupName](const Group& group) { return group.mName == aGroupName; });
  if (foundIt != end) {
    GetEndpointForReportInternal(*foundIt, aEndpointURI);
  }

  // XXX More explicitly report an error if not found?
}

/* static */
void ReportingHeader::GetEndpointForReportInternal(
    const ReportingHeader::Group& aGroup, nsACString& aEndpointURI) {
  TimeDuration diff = TimeStamp::Now() - aGroup.mCreationTime;
  if (diff.ToSeconds() > aGroup.mTTL) {
    // Expired.
    return;
  }

  if (aGroup.mEndpoints.IsEmpty()) {
    return;
  }

  int64_t minPriority = -1;
  uint32_t totalWeight = 0;

  for (const Endpoint& endpoint : aGroup.mEndpoints.NonObservingRange()) {
    if (minPriority == -1 || minPriority > endpoint.mPriority) {
      minPriority = endpoint.mPriority;
      totalWeight = endpoint.mWeight;
    } else if (minPriority == endpoint.mPriority) {
      totalWeight += endpoint.mWeight;
    }
  }

  nsCOMPtr<nsIRandomGenerator> randomGenerator =
      do_GetService("@mozilla.org/security/random-generator;1");
  if (NS_WARN_IF(!randomGenerator)) {
    return;
  }

  uint32_t randomNumber = 0;

  nsresult rv = randomGenerator->GenerateRandomBytesInto(randomNumber);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return;
  }

  totalWeight = randomNumber % totalWeight;

  const auto [begin, end] = aGroup.mEndpoints.NonObservingRange();
  const auto foundIt = std::find_if(
      begin, end, [minPriority, totalWeight](const Endpoint& endpoint) {
        return minPriority == endpoint.mPriority &&
               totalWeight < endpoint.mWeight;
      });
  if (foundIt != end) {
    Unused << NS_WARN_IF(NS_FAILED(foundIt->mUrl->GetSpec(aEndpointURI)));
  }
  // XXX More explicitly report an error if not found?
}

/* static */
void ReportingHeader::RemoveEndpoint(
    const nsAString& aGroupName, const nsACString& aEndpointURL,
    const mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
  if (!gReporting) {
    return;
  }

  nsCOMPtr<nsIURI> uri;
  nsresult rv = NS_NewURI(getter_AddRefs(uri), aEndpointURL);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return;
  }

  auto principalOrErr = PrincipalInfoToPrincipal(aPrincipalInfo);
  if (NS_WARN_IF(principalOrErr.isErr())) {
    return;
  }

  nsAutoCString origin;
  rv = principalOrErr.unwrap()->GetOrigin(origin);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return;
  }

  Client* client = gReporting->mOrigins.Get(origin);
  if (!client) {
    return;
  }

  // Scope for the group iterator.
  {
    nsTObserverArray<Group>::BackwardIterator iter(client->mGroups);
    while (iter.HasMore()) {
      const Group& group = iter.GetNext();
      if (group.mName != aGroupName) {
        continue;
      }

      // Scope for the endpoint iterator.
      {
        nsTObserverArray<Endpoint>::BackwardIterator endpointIter(
            group.mEndpoints);
        while (endpointIter.HasMore()) {
          const Endpoint& endpoint = endpointIter.GetNext();

          bool equal = false;
          rv = endpoint.mUrl->Equals(uri, &equal);
          if (NS_WARN_IF(NS_FAILED(rv))) {
            continue;
          }

          if (equal) {
            endpointIter.Remove();
            break;
          }
        }
      }

      if (group.mEndpoints.IsEmpty()) {
        iter.Remove();
      }

      break;
    }
  }

  if (client->mGroups.IsEmpty()) {
    gReporting->mOrigins.Remove(origin);
    gReporting->MaybeCancelCleanupTimer();
  }
}

void ReportingHeader::RemoveOriginsFromHost(const nsAString& aHost) {
  nsCOMPtr<nsIEffectiveTLDService> tldService =
      do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
  if (NS_WARN_IF(!tldService)) {
    return;
  }

  NS_ConvertUTF16toUTF8 host(aHost);

  for (auto iter = mOrigins.Iter(); !iter.Done(); iter.Next()) {
    bool hasRootDomain = false;
    nsresult rv = tldService->HasRootDomain(iter.Key(), host, &hasRootDomain);
    if (NS_WARN_IF(NS_FAILED(rv)) || !hasRootDomain) {
      continue;
    }

    iter.Remove();
  }

  MaybeCancelCleanupTimer();
}

void ReportingHeader::RemoveOriginsFromOriginAttributesPattern(
    const OriginAttributesPattern& aPattern) {
  for (auto iter = mOrigins.Iter(); !iter.Done(); iter.Next()) {
    nsAutoCString suffix;
    OriginAttributes attr;
    if (NS_WARN_IF(!attr.PopulateFromOrigin(iter.Key(), suffix))) {
      continue;
    }

    if (aPattern.Matches(attr)) {
      iter.Remove();
    }
  }

  MaybeCancelCleanupTimer();
}

void ReportingHeader::RemoveOrigins() {
  mOrigins.Clear();
  MaybeCancelCleanupTimer();
}

void ReportingHeader::RemoveOriginsForTTL() {
  TimeStamp now = TimeStamp::Now();

  for (auto iter = mOrigins.Iter(); !iter.Done(); iter.Next()) {
    Client* client = iter.UserData();

    // Scope of the iterator.
    {
      nsTObserverArray<Group>::BackwardIterator groupIter(client->mGroups);
      while (groupIter.HasMore()) {
        const Group& group = groupIter.GetNext();
        TimeDuration diff = now - group.mCreationTime;
        if (diff.ToSeconds() > group.mTTL) {
          groupIter.Remove();
          return;
        }
      }
    }

    if (client->mGroups.IsEmpty()) {
      iter.Remove();
    }
  }
}

/* static */
bool ReportingHeader::HasReportingHeaderForOrigin(const nsACString& aOrigin) {
  if (!gReporting) {
    return false;
  }

  return gReporting->mOrigins.Contains(aOrigin);
}

NS_IMETHODIMP
ReportingHeader::Notify(nsITimer* aTimer) {
  mCleanupTimer = nullptr;

  RemoveOriginsForTTL();
  MaybeCreateCleanupTimer();

  return NS_OK;
}

NS_IMETHODIMP
ReportingHeader::GetName(nsACString& aName) {
  aName.AssignLiteral("ReportingHeader");
  return NS_OK;
}

void ReportingHeader::MaybeCreateCleanupTimer() {
  if (mCleanupTimer) {
    return;
  }

  if (mOrigins.Count() == 0) {
    return;
  }

  uint32_t timeout = StaticPrefs::dom_reporting_cleanup_timeout() * 1000;
  nsresult rv =
      NS_NewTimerWithCallback(getter_AddRefs(mCleanupTimer), this, timeout,
                              nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY);
  Unused << NS_WARN_IF(NS_FAILED(rv));
}

void ReportingHeader::MaybeCancelCleanupTimer() {
  if (!mCleanupTimer) {
    return;
  }

  if (mOrigins.Count() != 0) {
    return;
  }

  mCleanupTimer->Cancel();
  mCleanupTimer = nullptr;
}

NS_INTERFACE_MAP_BEGIN(ReportingHeader)
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
  NS_INTERFACE_MAP_ENTRY(nsIObserver)
  NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
  NS_INTERFACE_MAP_ENTRY(nsINamed)
NS_INTERFACE_MAP_END

NS_IMPL_ADDREF(ReportingHeader)
NS_IMPL_RELEASE(ReportingHeader)

}  // namespace mozilla::dom

94%


¤ Dauer der Verarbeitung: 0.26 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.