diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/base/PlacesObservers.cpp | |
parent | Initial commit. (diff) | |
download | firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/base/PlacesObservers.cpp')
-rw-r--r-- | dom/base/PlacesObservers.cpp | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/dom/base/PlacesObservers.cpp b/dom/base/PlacesObservers.cpp new file mode 100644 index 0000000000..818e815ebc --- /dev/null +++ b/dom/base/PlacesObservers.cpp @@ -0,0 +1,340 @@ +/* -*- 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 "PlacesObservers.h" + +#include "PlacesWeakCallbackWrapper.h" +#include "nsIWeakReferenceUtils.h" +#include "mozilla/ClearOnShutdown.h" + +namespace mozilla::dom { + +template <class T> +struct Flagged { + Flagged(uint32_t aFlags, T&& aValue) + : flags(aFlags), value(std::forward<T>(aValue)) {} + Flagged(Flagged&& aOther) + : Flagged(std::move(aOther.flags), std::move(aOther.value)) {} + Flagged(const Flagged& aOther) = default; + ~Flagged() = default; + + uint32_t flags; + T value; +}; + +template <class T> +using FlaggedArray = nsTArray<Flagged<T>>; + +template <class T> +struct ListenerCollection { + static StaticAutoPtr<FlaggedArray<T>> gListeners; + static StaticAutoPtr<FlaggedArray<T>> gListenersToRemove; + + static FlaggedArray<T>* GetListeners(bool aDoNotInit = false) { + MOZ_ASSERT(NS_IsMainThread()); + if (!gListeners && !aDoNotInit) { + gListeners = new FlaggedArray<T>(); + ClearOnShutdown(&gListeners); + } + return gListeners; + } + + static FlaggedArray<T>* GetListenersToRemove(bool aDoNotInit = false) { + MOZ_ASSERT(NS_IsMainThread()); + if (!gListenersToRemove && !aDoNotInit) { + gListenersToRemove = new FlaggedArray<T>(); + ClearOnShutdown(&gListenersToRemove); + } + return gListenersToRemove; + } +}; + +template <class T> +StaticAutoPtr<FlaggedArray<T>> ListenerCollection<T>::gListeners; +template <class T> +StaticAutoPtr<FlaggedArray<T>> ListenerCollection<T>::gListenersToRemove; + +typedef ListenerCollection<RefPtr<PlacesEventCallback>> JSListeners; +typedef ListenerCollection<WeakPtr<PlacesWeakCallbackWrapper>> WeakJSListeners; +typedef ListenerCollection<WeakPtr<places::INativePlacesEventCallback>> + WeakNativeListeners; + +static bool gCallingListeners = false; + +uint32_t GetEventTypeFlag(PlacesEventType aEventType) { + if (aEventType == PlacesEventType::None) { + return 0; + } + return 1 << ((uint32_t)aEventType - 1); +} + +uint32_t GetFlagsForEventTypes(const nsTArray<PlacesEventType>& aEventTypes) { + uint32_t flags = 0; + for (PlacesEventType eventType : aEventTypes) { + flags |= GetEventTypeFlag(eventType); + } + return flags; +} + +uint32_t GetFlagsForEvents( + const nsTArray<OwningNonNull<PlacesEvent>>& aEvents) { + uint32_t flags = 0; + for (const PlacesEvent& event : aEvents) { + flags |= GetEventTypeFlag(event.Type()); + } + return flags; +} + +template <class TWrapped, class TUnwrapped> +MOZ_CAN_RUN_SCRIPT void CallListeners( + uint32_t aEventFlags, FlaggedArray<TWrapped>& aListeners, + const Sequence<OwningNonNull<PlacesEvent>>& aEvents, + const std::function<TUnwrapped(TWrapped&)>& aUnwrapListener, + const std::function<void(TUnwrapped&, + const Sequence<OwningNonNull<PlacesEvent>>&)>& + aCallListener) { + for (uint32_t i = 0; i < aListeners.Length(); i++) { + Flagged<TWrapped>& l = aListeners[i]; + TUnwrapped unwrapped = aUnwrapListener(l.value); + if (!unwrapped) { + aListeners.RemoveElementAt(i); + i--; + continue; + } + + if ((l.flags & aEventFlags) == aEventFlags) { + aCallListener(unwrapped, aEvents); + } else if (l.flags & aEventFlags) { + Sequence<OwningNonNull<PlacesEvent>> filtered; + for (const OwningNonNull<PlacesEvent>& event : aEvents) { + if (l.flags & GetEventTypeFlag(event->Type())) { + bool success = !!filtered.AppendElement(event, fallible); + MOZ_RELEASE_ASSERT(success); + } + } + aCallListener(unwrapped, filtered); + } + } +} + +void PlacesObservers::AddListener(GlobalObject& aGlobal, + const nsTArray<PlacesEventType>& aEventTypes, + PlacesEventCallback& aCallback, + ErrorResult& rv) { + uint32_t flags = GetFlagsForEventTypes(aEventTypes); + + FlaggedArray<RefPtr<PlacesEventCallback>>* listeners = + JSListeners::GetListeners(); + Flagged<RefPtr<PlacesEventCallback>> pair(flags, &aCallback); + listeners->AppendElement(pair); +} + +void PlacesObservers::AddListener(GlobalObject& aGlobal, + const nsTArray<PlacesEventType>& aEventTypes, + PlacesWeakCallbackWrapper& aCallback, + ErrorResult& rv) { + uint32_t flags = GetFlagsForEventTypes(aEventTypes); + + FlaggedArray<WeakPtr<PlacesWeakCallbackWrapper>>* listeners = + WeakJSListeners::GetListeners(); + WeakPtr<PlacesWeakCallbackWrapper> weakCb(&aCallback); + MOZ_ASSERT(weakCb.get()); + Flagged<WeakPtr<PlacesWeakCallbackWrapper>> flagged(flags, std::move(weakCb)); + listeners->AppendElement(flagged); +} + +void PlacesObservers::AddListener( + const nsTArray<PlacesEventType>& aEventTypes, + places::INativePlacesEventCallback* aCallback) { + uint32_t flags = GetFlagsForEventTypes(aEventTypes); + + FlaggedArray<WeakPtr<places::INativePlacesEventCallback>>* listeners = + WeakNativeListeners::GetListeners(); + Flagged<WeakPtr<places::INativePlacesEventCallback>> pair(flags, aCallback); + listeners->AppendElement(pair); +} + +void PlacesObservers::RemoveListener( + GlobalObject& aGlobal, const nsTArray<PlacesEventType>& aEventTypes, + PlacesEventCallback& aCallback, ErrorResult& rv) { + uint32_t flags = GetFlagsForEventTypes(aEventTypes); + if (gCallingListeners) { + FlaggedArray<RefPtr<PlacesEventCallback>>* listeners = + JSListeners::GetListenersToRemove(); + Flagged<RefPtr<PlacesEventCallback>> pair(flags, &aCallback); + listeners->AppendElement(pair); + } else { + RemoveListener(flags, aCallback); + } +} + +void PlacesObservers::RemoveListener( + GlobalObject& aGlobal, const nsTArray<PlacesEventType>& aEventTypes, + PlacesWeakCallbackWrapper& aCallback, ErrorResult& rv) { + uint32_t flags = GetFlagsForEventTypes(aEventTypes); + if (gCallingListeners) { + FlaggedArray<WeakPtr<PlacesWeakCallbackWrapper>>* listeners = + WeakJSListeners::GetListenersToRemove(); + WeakPtr<PlacesWeakCallbackWrapper> weakCb(&aCallback); + MOZ_ASSERT(weakCb.get()); + Flagged<WeakPtr<PlacesWeakCallbackWrapper>> flagged(flags, + std::move(weakCb)); + listeners->AppendElement(flagged); + } else { + RemoveListener(flags, aCallback); + } +} + +void PlacesObservers::RemoveListener( + const nsTArray<PlacesEventType>& aEventTypes, + places::INativePlacesEventCallback* aCallback) { + uint32_t flags = GetFlagsForEventTypes(aEventTypes); + if (gCallingListeners) { + FlaggedArray<WeakPtr<places::INativePlacesEventCallback>>* listeners = + WeakNativeListeners::GetListenersToRemove(); + Flagged<WeakPtr<places::INativePlacesEventCallback>> pair(flags, aCallback); + listeners->AppendElement(pair); + } else { + RemoveListener(flags, aCallback); + } +} + +void PlacesObservers::RemoveListener(uint32_t aFlags, + PlacesEventCallback& aCallback) { + FlaggedArray<RefPtr<PlacesEventCallback>>* listeners = + JSListeners::GetListeners(/* aDoNotInit: */ true); + if (!listeners) { + return; + } + for (uint32_t i = 0; i < listeners->Length(); i++) { + Flagged<RefPtr<PlacesEventCallback>>& l = listeners->ElementAt(i); + if (!(*l.value == aCallback)) { + continue; + } + if (l.flags == (aFlags & l.flags)) { + listeners->RemoveElementAt(i); + i--; + } else { + l.flags &= ~aFlags; + } + } +} + +void PlacesObservers::RemoveListener(uint32_t aFlags, + PlacesWeakCallbackWrapper& aCallback) { + FlaggedArray<WeakPtr<PlacesWeakCallbackWrapper>>* listeners = + WeakJSListeners::GetListeners(/* aDoNotInit: */ true); + if (!listeners) { + return; + } + for (uint32_t i = 0; i < listeners->Length(); i++) { + Flagged<WeakPtr<PlacesWeakCallbackWrapper>>& l = listeners->ElementAt(i); + RefPtr<PlacesWeakCallbackWrapper> unwrapped = l.value.get(); + if (unwrapped != &aCallback) { + continue; + } + if (l.flags == (aFlags & l.flags)) { + listeners->RemoveElementAt(i); + i--; + } else { + l.flags &= ~aFlags; + } + } +} + +void PlacesObservers::RemoveListener( + uint32_t aFlags, places::INativePlacesEventCallback* aCallback) { + FlaggedArray<WeakPtr<places::INativePlacesEventCallback>>* listeners = + WeakNativeListeners::GetListeners(/* aDoNotInit: */ true); + if (!listeners) { + return; + } + for (uint32_t i = 0; i < listeners->Length(); i++) { + Flagged<WeakPtr<places::INativePlacesEventCallback>>& l = + listeners->ElementAt(i); + RefPtr<places::INativePlacesEventCallback> unwrapped = l.value.get(); + if (unwrapped != aCallback) { + continue; + } + if (l.flags == (aFlags & l.flags)) { + listeners->RemoveElementAt(i); + i--; + } else { + l.flags &= ~aFlags; + } + } +} + +void PlacesObservers::NotifyListeners( + GlobalObject& aGlobal, const Sequence<OwningNonNull<PlacesEvent>>& aEvents, + ErrorResult& rv) { + NotifyListeners(aEvents); +} + +void PlacesObservers::NotifyListeners( + const Sequence<OwningNonNull<PlacesEvent>>& aEvents) { + MOZ_RELEASE_ASSERT(!gCallingListeners); + gCallingListeners = true; + uint32_t flags = GetFlagsForEvents(aEvents); + + CallListeners<RefPtr<PlacesEventCallback>, RefPtr<PlacesEventCallback>>( + flags, *JSListeners::GetListeners(), aEvents, [](auto& cb) { return cb; }, + // MOZ_CAN_RUN_SCRIPT_BOUNDARY because on Windows this gets called from + // some internals of the std::function implementation that we can't + // annotate. We handle this by annotating CallListeners and making sure + // it holds a strong ref to the callback. + [&](auto& cb, const auto& events) + MOZ_CAN_RUN_SCRIPT_BOUNDARY { MOZ_KnownLive(cb)->Call(events); }); + + CallListeners<WeakPtr<places::INativePlacesEventCallback>, + RefPtr<places::INativePlacesEventCallback>>( + flags, *WeakNativeListeners::GetListeners(), aEvents, + [](auto& cb) { return cb.get(); }, + [&](auto& cb, const Sequence<OwningNonNull<PlacesEvent>>& events) { + cb->HandlePlacesEvent(events); + }); + + CallListeners<WeakPtr<PlacesWeakCallbackWrapper>, + RefPtr<PlacesWeakCallbackWrapper>>( + flags, *WeakJSListeners::GetListeners(), aEvents, + [](auto& cb) { return cb.get(); }, + // MOZ_CAN_RUN_SCRIPT_BOUNDARY because on Windows this gets called from + // some internals of the std::function implementation that we can't + // annotate. We handle this by annotating CallListeners and making sure + // it holds a strong ref to the callback. + [&](auto& cb, const auto& events) MOZ_CAN_RUN_SCRIPT_BOUNDARY { + RefPtr<PlacesEventCallback> callback(cb->mCallback); + callback->Call(events); + }); + + auto& listenersToRemove = *JSListeners::GetListenersToRemove(); + if (listenersToRemove.Length() > 0) { + for (auto& listener : listenersToRemove) { + RemoveListener(listener.flags, *listener.value); + } + } + listenersToRemove.Clear(); + + auto& weakListenersToRemove = *WeakJSListeners::GetListenersToRemove(); + if (weakListenersToRemove.Length() > 0) { + for (auto& listener : weakListenersToRemove) { + RemoveListener(listener.flags, *listener.value.get()); + } + } + weakListenersToRemove.Clear(); + + auto& nativeListenersToRemove = *WeakNativeListeners::GetListenersToRemove(); + if (nativeListenersToRemove.Length() > 0) { + for (auto& listener : nativeListenersToRemove) { + RemoveListener(listener.flags, listener.value.get()); + } + } + nativeListenersToRemove.Clear(); + + gCallingListeners = false; +} + +} // namespace mozilla::dom |