Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  ReportingHeader.cpp

  Sprache: C
 

/* 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/GCAPI.h"
#include "js/JSON.h"
#include "js/PropertyAndElement.h"  // JS_GetElement
#include "mozilla/OriginAttributes.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/dom/ReportingBinding.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/SimpleGlobalObject.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/net/SFV.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 = MakeUnique<Client>();
    size_t parsedItems = ParseReportingEndpointsHeader(
        header, uri, [&](const nsAString& aKey, nsCOMPtr<nsIURI> aEndpointUrl) {
          Group* group = client->mGroups.AppendElement();
          group->mCreationTime = TimeStamp::Now();
          group->mTTL = std::numeric_limits<int32_t>::max();
          group->mName = aKey;

          // Use data extracted from dictionary entry to create an endpoint
          group->mEndpoints.AppendElement(
              Endpoint::Create(aEndpointUrl.forget(), aKey));
        });
    if (parsedItems == 0) {
      client = nullptr;
    }
  } 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 */
EndpointsList ReportingHeader::ProcessReportingEndpointsListFromResponse(
    nsIHttpChannel* aChannel) {
  if (!StaticPrefs::dom_reporting_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 {};
  }

  nsAutoCString header;
  EndpointsList result;

  // Note: Legacy Report-To header supported by very few browsers
  // https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Report-To
  if (NS_SUCCEEDED(
          aChannel->GetResponseHeader("Reporting-Endpoints"_ns, header))) {
    (void)ParseReportingEndpointsHeader(
        header, uri, [&](const nsAString& aKey, nsCOMPtr<nsIURI> aEndpointURL) {
          result.mData.EmplaceBack(
              Endpoint::Create(aEndpointURL.forget(), aKey));
        });
  }
  return result;
}

/* static */
size_t ReportingHeader::ParseReportingEndpointsHeader(
    const nsACString& aHeaderValue, nsIURI* aURI,
    std::function<void(const nsAString&, nsCOMPtr<nsIURI>)>&&
        aOnParsedItemCallback) {
  nsAutoCString uriSpec;
  aURI->GetSpec(uriSpec);

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

  auto dict = mozilla::net::SFV::ParseDict(aHeaderValue);
  if (!dict.IsValid()) {
    return 0;
  }

  nsTArray<nsCString> keys;
  if (NS_FAILED(dict.GetKeys(keys))) {
    return 0;
  }

  size_t itemsParsed = 0;

  if (!IsSecureURI(aURI)) {
    return 0;
  }

  for (const auto& key : keys) {
    nsAutoCString endpointURLString;

    // Try to get value as a direct string item
    if (NS_SUCCEEDED(dict.GetItem<mozilla::net::SFV::SFVString>(
            key, endpointURLString))) {
      // Got a direct string item
    } else {
      // Try to get value as an inner list and extract first item
      auto innerList = dict.GetInnerList(key);
      if (!innerList.IsValid() || innerList.Length() == 0) {
        continue;
      }

      auto firstItem = innerList.GetItemAt(0);
      if (!firstItem.IsValid()) {
        continue;
      }

      if (NS_FAILED(firstItem.GetValue<mozilla::net::SFV::SFVString>(
              endpointURLString))) {
        continue;
      }
    }

    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;
    }

    ++itemsParsed;
    aOnParsedItemCallback(NS_ConvertUTF8toUTF16(key), std::move(endpointURL));
  }

  return itemsParsed;
}

/* 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) {
    // Drop error in favor of logging a generic message, because the error
    // message's column numbers are confusing due to the prepended characters
    // above, and a user cannot do much about it anyway (bug 2020662).
    JS_ClearPendingException(cx);
    LogToConsoleInvalidJSON(aChannel, aURI);
    return nullptr;
  }

  RootedDictionary<dom::ReportingHeaderValue> data(cx);
  if (!data.Init(cx, jsonValue)) {
    // Ignore error in favor of generic message to avoid logspam (bug 2020662).
    JS_ClearPendingException(cx);
    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) {
      // TODO: should this clear the endpoint instead?
      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(
      PropertiesFile::SECURITY_PROPERTIES, aMsg, aParams, localizedMsg);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return;
  }

  rv = nsContentUtils::ReportToConsoleByWindowID(
      localizedMsg, nsIScriptError::infoFlag, "Reporting"_ns, windowID,
      SourceLocation(aURI));
  (void)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) {
  return GetEndpointForReportIncludeSubdomains(
      aGroupName, aPrincipal, /* includeSubdomains */ false, aEndpointURI);
}
/* static */
void ReportingHeader::GetEndpointForReportIncludeSubdomains(
    const nsAString& aGroupName, nsIPrincipal* aPrincipal,
    bool aIncludeSubdomains, nsACString& aEndpointURI) {
  MOZ_ASSERT(aEndpointURI.IsEmpty());

  if (!gReporting) {
    return;
  }

  nsCOMPtr<nsIPrincipal> principal = aPrincipal;
  bool mustHaveIncludeSubdomains = false;

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

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

    nsCOMPtr<nsIPrincipal> oldPrincipal = std::move(principal);
    oldPrincipal->GetNextSubDomainPrincipal(getter_AddRefs(principal));
    mustHaveIncludeSubdomains = true;
  } while (principal && aIncludeSubdomains);

  // 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) {
    (void)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,
                                     nsIPrincipal* aPrincipal) {
  if (!gReporting) {
    return;
  }

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

  if (NS_WARN_IF(!aPrincipal)) {
    return;
  }

  nsAutoCString origin;
  rv = aPrincipal->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();
    }
  }
}

ReportingHeader::Endpoint* EndpointsList::GetEndpointWithName(
    const nsAString& aEndpointName) {
  for (auto& endpoint : mData) {
    if (endpoint.mEndpointName == aEndpointName) {
      return &endpoint;
    }
  }
  return nullptr;
}

void EndpointsList::RemoveEndpoint(const nsAString& aEndpointName) {
  const auto it = std::ranges::find_if(
      mData, [&aEndpointName](const ReportingHeader::Endpoint& aEndpoint) {
        return aEndpoint.mEndpointName == aEndpointName;
      });
  if (it != std::end(mData)) {
    mData.RemoveElementAt(it);
  }
}

/* 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);
  (void)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

Messung V0.5 in Prozent
C=91 H=93 G=91

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

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 und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=738142