/* -*- 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 "StorageNotifierService.h" #include "StorageUtils.h" #include "mozilla/dom/StorageEvent.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/StaticPtr.h" #include "nsThreadUtils.h" namespace mozilla::dom { namespace { // This boolean is used to avoid the creation of the service after been // distroyed on shutdown. bool gStorageShuttingDown = false; StaticRefPtr gStorageNotifierService; } // namespace /* static */ StorageNotifierService* StorageNotifierService::GetOrCreate() { MOZ_ASSERT(NS_IsMainThread()); if (!gStorageNotifierService && !gStorageShuttingDown) { gStorageNotifierService = new StorageNotifierService(); ClearOnShutdown(&gStorageNotifierService); } return gStorageNotifierService; } StorageNotifierService::StorageNotifierService() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!gStorageNotifierService); } StorageNotifierService::~StorageNotifierService() { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!gStorageNotifierService); gStorageShuttingDown = true; } /* static */ void StorageNotifierService::Broadcast(StorageEvent* aEvent, const char16_t* aStorageType, bool aPrivateBrowsing, bool aImmediateDispatch) { MOZ_ASSERT(NS_IsMainThread()); RefPtr service = gStorageNotifierService; if (!service) { return; } RefPtr event = aEvent; for (const auto& observer : service->mObservers.ForwardRange()) { // Enforce that the source storage area's private browsing state matches // this window's state. These flag checks and their maintenance independent // from the principal's OriginAttributes matter because chrome docshells // that are part of private browsing windows can be private browsing without // having their OriginAttributes set (because they have the system // principal). if (aPrivateBrowsing != observer->IsPrivateBrowsing()) { continue; } // No reasons to continue if the principal of the event doesn't match with // the window's one. if (!StorageUtils::PrincipalsEqual( aEvent->GetPrincipal(), observer->GetEffectiveStoragePrincipal())) { continue; } const auto pinnedObserver = observer; RefPtr r = NS_NewRunnableFunction( "StorageNotifierService::Broadcast", [pinnedObserver, event, aStorageType, aPrivateBrowsing, aImmediateDispatch]() { // Check principals again. EffectiveStoragePrincipal may be changed // when relaxed. if (!aImmediateDispatch && !StorageUtils::PrincipalsEqual( event->GetPrincipal(), pinnedObserver->GetEffectiveStoragePrincipal())) { return; } pinnedObserver->ObserveStorageNotification(event, aStorageType, aPrivateBrowsing); }); if (aImmediateDispatch) { r->Run(); } else { nsCOMPtr et = pinnedObserver->GetEventTarget(); if (et) { et->Dispatch(r.forget()); } } } } void StorageNotifierService::Register(StorageNotificationObserver* aObserver) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aObserver); MOZ_ASSERT(!mObservers.Contains(aObserver)); mObservers.AppendElement(aObserver); } void StorageNotifierService::Unregister( StorageNotificationObserver* aObserver) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aObserver); // No assertion about mObservers containing aObserver because window calls // this method multiple times. mObservers.RemoveElement(aObserver); } } // namespace mozilla::dom