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

Quelle  StorageObserver.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 "StorageObserver.h"

#include "LocalStorageCache.h"
#include "StorageCommon.h"
#include "StorageDBThread.h"
#include "StorageIPC.h"
#include "StorageUtils.h"

#include "mozilla/BasePrincipal.h"
#include "nsCOMPtr.h"
#include "nsICookieNotification.h"
#include "nsIObserverService.h"
#include "nsIURI.h"
#include "nsIPermission.h"
#include "nsNetUtil.h"
#include "nsICookiePermission.h"

#include "nsPrintfCString.h"
#include "nsXULAppAPI.h"
#include "nsEscape.h"
#include "nsNetCID.h"
#include "mozilla/dom/LocalStorageCommon.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/SpinEventLoopUntil.h"
#include "nsServiceManagerUtils.h"
#include "nsIClearBySiteEntry.h"

namespace mozilla::dom {

using namespace StorageUtils;

static const char kStartupTopic[] = "sessionstore-windows-restored";
static const uint32_t kStartupDelay = 0;

const char kTestingPref[] = "dom.storage.testing";

constexpr auto kPrivateBrowsingPattern = u"{ \"privateBrowsingId\": 1 }"_ns;

NS_IMPL_ISUPPORTS(StorageObserver, nsIObserver, nsINamed,
                  nsISupportsWeakReference)

StorageObserver* StorageObserver::sSelf = nullptr;

// static
nsresult StorageObserver::Init() {
  static_assert(kPrivateBrowsingIdCount * sizeof(mBackgroundThread[0]) ==
                sizeof(mBackgroundThread));
  if (sSelf) {
    return NS_OK;
  }

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

  sSelf = new StorageObserver();
  NS_ADDREF(sSelf);

  // Chrome clear operations.
  obs->AddObserver(sSelf, kStartupTopic, true);
  obs->AddObserver(sSelf, "cookie-changed"true);
  obs->AddObserver(sSelf, "perm-changed"true);
  obs->AddObserver(sSelf, "last-pb-context-exited"true);
  obs->AddObserver(sSelf, "clear-origin-attributes-data"true);
  obs->AddObserver(sSelf, "dom-storage:clear-origin-attributes-data"true);
  obs->AddObserver(sSelf, "extension:purge-localStorage"true);
  obs->AddObserver(sSelf, "browser:purge-sessionStorage"true);

  // Shutdown
  obs->AddObserver(sSelf, "profile-after-change"true);
  if (XRE_IsParentProcess()) {
    obs->AddObserver(sSelf, "profile-before-change"true);
  }

  // Testing
#ifdef DOM_STORAGE_TESTS
  Preferences::RegisterCallbackAndCall(TestingPrefChanged, kTestingPref);
#endif

  return NS_OK;
}

// static
nsresult StorageObserver::Shutdown() {
  AssertIsOnMainThread();

  if (!sSelf) {
    return NS_ERROR_NOT_INITIALIZED;  // Is this always an error?
  }

  sSelf->mSinks.Clear();

  NS_RELEASE(sSelf);
  return NS_OK;
}

// static
void StorageObserver::TestingPrefChanged(const char* aPrefName,
                                         void* aClosure) {
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  if (!obs || !sSelf) {
    return;
  }

  if (Preferences::GetBool(kTestingPref)) {
    obs->AddObserver(sSelf, "domstorage-test-flush-force"true);
    if (XRE_IsParentProcess()) {
      // Only to forward to child process.
      obs->AddObserver(sSelf, "domstorage-test-flushed"true);
    }
    obs->AddObserver(sSelf, "domstorage-test-reload"true);
  } else {
    obs->RemoveObserver(sSelf, "domstorage-test-flush-force");
    if (XRE_IsParentProcess()) {
      // Only to forward to child process.
      obs->RemoveObserver(sSelf, "domstorage-test-flushed");
    }
    obs->RemoveObserver(sSelf, "domstorage-test-reload");
  }
}

void StorageObserver::AddSink(StorageObserverSink* aObs) {
  AssertIsOnMainThread();

  MOZ_ASSERT(sSelf);

  mSinks.AppendElement(aObs);
}

void StorageObserver::RemoveSink(StorageObserverSink* aObs) {
  AssertIsOnMainThread();

  MOZ_ASSERT(sSelf);

  mSinks.RemoveElement(aObs);
}

void StorageObserver::Notify(const char* aTopic,
                             const nsAString& aOriginAttributesPattern,
                             const nsACString& aOriginScope) {
  AssertIsOnMainThread();

  MOZ_ASSERT(sSelf);

  for (auto sink : mSinks.ForwardRange()) {
    sink->Observe(aTopic, aOriginAttributesPattern, aOriginScope);
  }
}

void StorageObserver::NoteBackgroundThread(const uint32_t aPrivateBrowsingId,
                                           nsIEventTarget* aBackgroundThread) {
  MOZ_RELEASE_ASSERT(aPrivateBrowsingId < kPrivateBrowsingIdCount);

  mBackgroundThread[aPrivateBrowsingId] = aBackgroundThread;
}

nsresult StorageObserver::GetOriginScope(const char16_t* aData,
                                         nsACString& aOriginScope) {
  nsresult rv;

  NS_ConvertUTF16toUTF8 domain(aData);

  nsAutoCString convertedDomain;
  rv = NS_DomainToASCIIAllowAnyGlyphfulASCII(domain, convertedDomain);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  nsCString originScope;
  rv = CreateReversedDomain(convertedDomain, originScope);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  aOriginScope = originScope;
  return NS_OK;
}

NS_IMETHODIMP
StorageObserver::Observe(nsISupports* aSubject, const char* aTopic,
                         const char16_t* aData) {
  if (NS_WARN_IF(!sSelf)) {  // Shutdown took place
    return NS_OK;
  }

  nsresult rv;

  // Start the thread that opens the database.
  if (!strcmp(aTopic, kStartupTopic)) {
    MOZ_ASSERT(XRE_IsParentProcess());

    if (NextGenLocalStorageEnabled()) {
      return NS_OK;
    }

    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    obs->RemoveObserver(this, kStartupTopic);

    return NS_NewTimerWithObserver(getter_AddRefs(mDBThreadStartDelayTimer),
                                   this, nsITimer::TYPE_ONE_SHOT,
                                   kStartupDelay);
  }

  // Timer callback used to start the database a short timer after startup
  if (!strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC)) {
    MOZ_ASSERT(XRE_IsParentProcess());
    MOZ_ASSERT(!NextGenLocalStorageEnabled());

    nsCOMPtr<nsITimer> timer = do_QueryInterface(aSubject);
    if (!timer) {
      return NS_ERROR_UNEXPECTED;
    }

    if (timer == mDBThreadStartDelayTimer) {
      mDBThreadStartDelayTimer = nullptr;

      for (const uint32_t id : {0, 1}) {
        StorageDBChild* storageChild = StorageDBChild::GetOrCreate(id);
        if (NS_WARN_IF(!storageChild)) {
          return NS_ERROR_FAILURE;
        }

        storageChild->SendStartup();
      }
    }

    return NS_OK;
  }

  // Clear everything, caches + database
  if (!strcmp(aTopic, "cookie-changed")) {
    nsCOMPtr<nsICookieNotification> notification = do_QueryInterface(aSubject);
    NS_ENSURE_TRUE(notification, NS_ERROR_FAILURE);

    if (notification->GetAction() !=
        nsICookieNotification::ALL_COOKIES_CLEARED) {
      return NS_OK;
    }

    if (!NextGenLocalStorageEnabled()) {
      for (const uint32_t id : {0, 1}) {
        StorageDBChild* storageChild = StorageDBChild::GetOrCreate(id);
        if (NS_WARN_IF(!storageChild)) {
          return NS_ERROR_FAILURE;
        }

        storageChild->AsyncClearAll();

        if (XRE_IsParentProcess()) {
          storageChild->SendClearAll();
        }
      }
    }

    Notify("cookie-cleared");

    return NS_OK;
  }

  // Clear from caches everything that has been stored
  // while in session-only mode
  if (!strcmp(aTopic, "perm-changed")) {
    // Check for cookie permission change
    nsCOMPtr<nsIPermission> perm(do_QueryInterface(aSubject));
    if (!perm) {
      return NS_OK;
    }

    nsAutoCString type;
    perm->GetType(type);
    if (type != "cookie"_ns) {
      return NS_OK;
    }

    uint32_t cap = 0;
    perm->GetCapability(&cap);
    if (!(cap & nsICookiePermission::ACCESS_SESSION) ||
        !u"deleted"_ns.Equals(nsDependentString(aData))) {
      return NS_OK;
    }

    nsCOMPtr<nsIPrincipal> principal;
    perm->GetPrincipal(getter_AddRefs(principal));
    if (!principal) {
      return NS_OK;
    }

    nsAutoCString originSuffix;
    BasePrincipal::Cast(principal)->OriginAttributesRef().CreateSuffix(
        originSuffix);

    nsAutoCString host;
    principal->GetHost(host);
    if (host.IsEmpty()) {
      return NS_OK;
    }

    nsAutoCString originScope;
    rv = CreateReversedDomain(host, originScope);
    NS_ENSURE_SUCCESS(rv, rv);

    Notify("session-only-cleared", NS_ConvertUTF8toUTF16(originSuffix),
           originScope);

    return NS_OK;
  }

  if (!strcmp(aTopic, "extension:purge-localStorage")) {
    if (NextGenLocalStorageEnabled()) {
      return NS_OK;
    }

    const char topic[] = "extension:purge-localStorage-caches";

    if (aData) {
      nsCString originScope;

      rv = GetOriginScope(aData, originScope);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }

      if (XRE_IsParentProcess()) {
        for (const uint32_t id : {0, 1}) {
          StorageDBChild* storageChild = StorageDBChild::GetOrCreate(id);
          if (NS_WARN_IF(!storageChild)) {
            return NS_ERROR_FAILURE;
          }

          storageChild->SendClearMatchingOrigin(originScope);
        }
      }

      Notify(topic, u""_ns, originScope);
    } else {
      for (const uint32_t id : {0, 1}) {
        StorageDBChild* storageChild = StorageDBChild::GetOrCreate(id);
        if (NS_WARN_IF(!storageChild)) {
          return NS_ERROR_FAILURE;
        }

        storageChild->AsyncClearAll();

        if (XRE_IsParentProcess()) {
          storageChild->SendClearAll();
        }
      }

      Notify(topic);
    }

    return NS_OK;
  }

  if (!strcmp(aTopic, "browser:purge-sessionStorage")) {
    // The caller passed an nsIClearBySiteEntry object which consists of both
    // site and pattern.
    // If both are passed, aSubject takes precedence over aData.
    if (aSubject) {
      nsCOMPtr<nsIClearBySiteEntry> entry = do_QueryInterface(aSubject);

      NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);

      nsAutoCString schemelessSite;
      rv = entry->GetSchemelessSite(schemelessSite);
      NS_ENSURE_SUCCESS(rv, rv);

      nsAutoString patternJSON;
      rv = entry->GetPatternJSON(patternJSON);
      NS_ENSURE_SUCCESS(rv, rv);

      nsCString originScope;
      rv = GetOriginScope(NS_ConvertUTF8toUTF16(schemelessSite).get(),
                          originScope);
      NS_ENSURE_SUCCESS(rv, rv);

      Notify(aTopic, patternJSON, originScope);
    } else if (aData) {
      // The caller passed only a schemeless site, origin or host.
      nsCString originScope;
      rv = GetOriginScope(aData, originScope);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        return rv;
      }

      Notify(aTopic, u""_ns, originScope);
    } else {
      // The caller passed nothing, clear everything.
      Notify(aTopic, u""_ns, ""_ns);
    }

    return NS_OK;
  }

  // Clear all private-browsing caches
  if (!strcmp(aTopic, "last-pb-context-exited")) {
    if (NextGenLocalStorageEnabled()) {
      return NS_OK;
    }

    // We get the notification in both processes (parent and content), but the
    // clearing of the in-memory database should be triggered from the parent
    // process only to avoid creation of redundant clearing operations.
    // Also, if we create a new StorageDBChild instance late during content
    // process shutdown, then it might be leaked in debug builds because it
    // could happen that there is no chance to properly destroy it.
    if (XRE_IsParentProcess()) {
      // This doesn't use a loop with privateBrowsingId 0 and 1, since we only
      // need to clear the in-memory database which is represented by
      // privateBrowsingId 1.
      static const uint32_t id = 1;

      StorageDBChild* storageChild = StorageDBChild::GetOrCreate(id);
      if (NS_WARN_IF(!storageChild)) {
        return NS_ERROR_FAILURE;
      }

      OriginAttributesPattern pattern;
      if (!pattern.Init(kPrivateBrowsingPattern)) {
        NS_ERROR("Cannot parse origin attributes pattern");
        return NS_ERROR_FAILURE;
      }

      storageChild->SendClearMatchingOriginAttributes(pattern);
    }

    Notify("private-browsing-data-cleared", kPrivateBrowsingPattern);

    return NS_OK;
  }

  // Clear data of the origins whose prefixes will match the suffix.
  if (!strcmp(aTopic, "clear-origin-attributes-data") ||
      !strcmp(aTopic, "dom-storage:clear-origin-attributes-data")) {
    MOZ_ASSERT(XRE_IsParentProcess());

    OriginAttributesPattern pattern;
    if (!pattern.Init(nsDependentString(aData))) {
      NS_ERROR("Cannot parse origin attributes pattern");
      return NS_ERROR_FAILURE;
    }

    if (NextGenLocalStorageEnabled()) {
      Notify("session-storage:clear-origin-attributes-data",
             nsDependentString(aData));
      return NS_OK;
    }

    for (const uint32_t id : {0, 1}) {
      StorageDBChild* storageChild = StorageDBChild::GetOrCreate(id);
      if (NS_WARN_IF(!storageChild)) {
        return NS_ERROR_FAILURE;
      }

      storageChild->SendClearMatchingOriginAttributes(pattern);
    }

    Notify(aTopic, nsDependentString(aData));

    return NS_OK;
  }

  if (!strcmp(aTopic, "profile-after-change")) {
    Notify("profile-change");

    return NS_OK;
  }

  if (!strcmp(aTopic, "profile-before-change")) {
    MOZ_ASSERT(XRE_IsParentProcess());

    if (NextGenLocalStorageEnabled()) {
      return NS_OK;
    }

    for (const uint32_t id : {0, 1}) {
      if (mBackgroundThread[id]) {
        bool done = false;

        RefPtr<StorageDBThread::ShutdownRunnable> shutdownRunnable =
            new StorageDBThread::ShutdownRunnable(id, done);
        MOZ_ALWAYS_SUCCEEDS(mBackgroundThread[id]->Dispatch(
            shutdownRunnable, NS_DISPATCH_NORMAL));

        MOZ_ALWAYS_TRUE(SpinEventLoopUntil(
            "StorageObserver::Observe profile-before-change"_ns,
            [&]() { return done; }));

        mBackgroundThread[id] = nullptr;
      }
    }

    return NS_OK;
  }

#ifdef DOM_STORAGE_TESTS
  if (!strcmp(aTopic, "domstorage-test-flush-force")) {
    if (NextGenLocalStorageEnabled()) {
      return NS_OK;
    }

    for (const uint32_t id : {0, 1}) {
      StorageDBChild* storageChild = StorageDBChild::GetOrCreate(id);
      if (NS_WARN_IF(!storageChild)) {
        return NS_ERROR_FAILURE;
      }

      storageChild->SendAsyncFlush();
    }

    return NS_OK;
  }

  if (!strcmp(aTopic, "domstorage-test-flushed")) {
    if (NextGenLocalStorageEnabled()) {
      return NS_OK;
    }

    // Only used to propagate to IPC children
    Notify("test-flushed");

    return NS_OK;
  }

  if (!strcmp(aTopic, "domstorage-test-reload")) {
    if (NextGenLocalStorageEnabled()) {
      return NS_OK;
    }

    Notify("test-reload");

    return NS_OK;
  }
#endif

  NS_ERROR("Unexpected topic");
  return NS_ERROR_UNEXPECTED;
}

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

}  // namespace mozilla::dom

92%


¤ Dauer der Verarbeitung: 0.14 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.