From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- dom/events/EventListenerService.cpp | 409 ++++++++++++++++++++++++++++++++++++ 1 file changed, 409 insertions(+) create mode 100644 dom/events/EventListenerService.cpp (limited to 'dom/events/EventListenerService.cpp') diff --git a/dom/events/EventListenerService.cpp b/dom/events/EventListenerService.cpp new file mode 100644 index 0000000000..4336476aa9 --- /dev/null +++ b/dom/events/EventListenerService.cpp @@ -0,0 +1,409 @@ +/* -*- 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 "EventListenerService.h" +#include "mozilla/BasicEvents.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/EventListenerManager.h" +#include "mozilla/HoldDropJSObjects.h" +#include "mozilla/JSEventHandler.h" +#include "mozilla/Maybe.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/EventListenerBinding.h" +#include "mozilla/dom/ScriptSettings.h" +#include "nsArrayUtils.h" +#include "nsCOMArray.h" +#include "nsINode.h" +#include "nsJSUtils.h" +#include "nsServiceManagerUtils.h" +#include "nsArray.h" +#include "nsThreadUtils.h" + +namespace mozilla { + +using namespace dom; + +/****************************************************************************** + * mozilla::EventListenerChange + ******************************************************************************/ + +NS_IMPL_ISUPPORTS(EventListenerChange, nsIEventListenerChange) + +EventListenerChange::~EventListenerChange() = default; + +EventListenerChange::EventListenerChange(EventTarget* aTarget) + : mTarget(aTarget) {} + +void EventListenerChange::AddChangedListenerName(nsAtom* aEventName) { + mChangedListenerNames.AppendElement(aEventName); +} + +NS_IMETHODIMP +EventListenerChange::GetTarget(EventTarget** aTarget) { + NS_ENSURE_ARG_POINTER(aTarget); + NS_ADDREF(*aTarget = mTarget); + return NS_OK; +} + +NS_IMETHODIMP +EventListenerChange::GetCountOfEventListenerChangesAffectingAccessibility( + uint32_t* aCount) { + *aCount = 0; + + size_t length = mChangedListenerNames.Length(); + for (size_t i = 0; i < length; i++) { + RefPtr listenerName = mChangedListenerNames[i]; + + // These are the event listener changes which may make an element + // accessible or inaccessible. + if (listenerName == nsGkAtoms::onclick || + listenerName == nsGkAtoms::onmousedown || + listenerName == nsGkAtoms::onmouseup) { + *aCount += 1; + } + } + + return NS_OK; +} + +/****************************************************************************** + * mozilla::EventListenerInfo + ******************************************************************************/ + +EventListenerInfo::EventListenerInfo( + EventListenerManager* aListenerManager, const nsAString& aType, + JS::Handle aScriptedListener, + JS::Handle aScriptedListenerGlobal, bool aCapturing, + bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler) + : mListenerManager(aListenerManager), + mType(aType), + mScriptedListener(aScriptedListener), + mScriptedListenerGlobal(aScriptedListenerGlobal), + mCapturing(aCapturing), + mAllowsUntrusted(aAllowsUntrusted), + mInSystemEventGroup(aInSystemEventGroup), + mIsHandler(aIsHandler) { + if (aScriptedListener) { + MOZ_ASSERT(JS_IsGlobalObject(aScriptedListenerGlobal)); + js::AssertSameCompartment(aScriptedListener, aScriptedListenerGlobal); + } + + HoldJSObjects(this); +} + +EventListenerInfo::~EventListenerInfo() { DropJSObjects(this); } + +NS_IMPL_CYCLE_COLLECTION_WITH_JS_MEMBERS(EventListenerInfo, (mListenerManager), + (mScriptedListener, + mScriptedListenerGlobal)) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventListenerInfo) + NS_INTERFACE_MAP_ENTRY(nsIEventListenerInfo) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(EventListenerInfo) +NS_IMPL_CYCLE_COLLECTING_RELEASE(EventListenerInfo) + +NS_IMETHODIMP +EventListenerInfo::GetType(nsAString& aType) { + aType = mType; + return NS_OK; +} + +NS_IMETHODIMP +EventListenerInfo::GetCapturing(bool* aCapturing) { + *aCapturing = mCapturing; + return NS_OK; +} + +NS_IMETHODIMP +EventListenerInfo::GetAllowsUntrusted(bool* aAllowsUntrusted) { + *aAllowsUntrusted = mAllowsUntrusted; + return NS_OK; +} + +NS_IMETHODIMP +EventListenerInfo::GetInSystemEventGroup(bool* aInSystemEventGroup) { + *aInSystemEventGroup = mInSystemEventGroup; + return NS_OK; +} + +NS_IMETHODIMP +EventListenerInfo::GetEnabled(bool* aEnabled) { + NS_ENSURE_STATE(mListenerManager); + return mListenerManager->IsListenerEnabled( + mType, mScriptedListener, mCapturing, mAllowsUntrusted, + mInSystemEventGroup, mIsHandler, aEnabled); +} + +NS_IMETHODIMP +EventListenerInfo::SetEnabled(bool aEnabled) { + NS_ENSURE_STATE(mListenerManager); + return mListenerManager->SetListenerEnabled( + mType, mScriptedListener, mCapturing, mAllowsUntrusted, + mInSystemEventGroup, mIsHandler, aEnabled); +} + +NS_IMETHODIMP +EventListenerInfo::GetListenerObject(JSContext* aCx, + JS::MutableHandle aObject) { + Maybe ar; + GetJSVal(aCx, ar, aObject); + return NS_OK; +} + +/****************************************************************************** + * mozilla::EventListenerService + ******************************************************************************/ + +NS_IMPL_ISUPPORTS(EventListenerService, nsIEventListenerService) + +bool EventListenerInfo::GetJSVal(JSContext* aCx, Maybe& aAr, + JS::MutableHandle aJSVal) { + if (mScriptedListener) { + aJSVal.setObject(*mScriptedListener); + aAr.emplace(aCx, mScriptedListenerGlobal); + return true; + } + + aJSVal.setNull(); + return false; +} + +NS_IMETHODIMP +EventListenerInfo::ToSource(nsAString& aResult) { + aResult.SetIsVoid(true); + + AutoSafeJSContext cx; + Maybe ar; + JS::Rooted v(cx); + if (GetJSVal(cx, ar, &v)) { + JSString* str = JS_ValueToSource(cx, v); + if (str) { + nsAutoJSString autoStr; + if (autoStr.init(cx, str)) { + aResult.Assign(autoStr); + } + } + } + return NS_OK; +} + +EventListenerService* EventListenerService::sInstance = nullptr; + +EventListenerService::EventListenerService() { + MOZ_ASSERT(!sInstance); + sInstance = this; +} + +EventListenerService::~EventListenerService() { + MOZ_ASSERT(sInstance == this); + sInstance = nullptr; +} + +NS_IMETHODIMP +EventListenerService::GetListenerInfoFor( + EventTarget* aEventTarget, + nsTArray>& aOutArray) { + NS_ENSURE_ARG_POINTER(aEventTarget); + + EventListenerManager* elm = aEventTarget->GetExistingListenerManager(); + if (elm) { + elm->GetListenerInfo(aOutArray); + } + + return NS_OK; +} + +NS_IMETHODIMP +EventListenerService::GetEventTargetChainFor( + EventTarget* aEventTarget, bool aComposed, + nsTArray>& aOutArray) { + NS_ENSURE_ARG(aEventTarget); + WidgetEvent event(true, eVoidEvent); + event.SetComposed(aComposed); + nsTArray targets; + nsresult rv = EventDispatcher::Dispatch(aEventTarget, nullptr, &event, + nullptr, nullptr, nullptr, &targets); + NS_ENSURE_SUCCESS(rv, rv); + aOutArray.AppendElements(targets); + return NS_OK; +} + +NS_IMETHODIMP +EventListenerService::HasListenersFor(EventTarget* aEventTarget, + const nsAString& aType, bool* aRetVal) { + NS_ENSURE_TRUE(aEventTarget, NS_ERROR_UNEXPECTED); + + EventListenerManager* elm = aEventTarget->GetExistingListenerManager(); + *aRetVal = elm && elm->HasListenersFor(aType); + return NS_OK; +} + +static already_AddRefed ToEventListener( + JSContext* aCx, JS::Handle aValue) { + if (NS_WARN_IF(!aValue.isObject())) { + return nullptr; + } + + JS::Rooted obj(aCx, &aValue.toObject()); + JS::Rooted global(aCx, JS::CurrentGlobalOrNull(aCx)); + RefPtr listener = + new EventListener(aCx, obj, global, GetIncumbentGlobal()); + return listener.forget(); +} + +NS_IMETHODIMP +EventListenerService::AddSystemEventListener(EventTarget* aTarget, + const nsAString& aType, + JS::Handle aListener, + bool aUseCapture, JSContext* aCx) { + MOZ_ASSERT(aTarget, "Missing target"); + + NS_ENSURE_TRUE(aTarget, NS_ERROR_UNEXPECTED); + + RefPtr listener = ToEventListener(aCx, aListener); + if (!listener) { + return NS_ERROR_UNEXPECTED; + } + + EventListenerManager* manager = aTarget->GetOrCreateListenerManager(); + NS_ENSURE_STATE(manager); + + EventListenerFlags flags = aUseCapture ? TrustedEventsAtSystemGroupCapture() + : TrustedEventsAtSystemGroupBubble(); + manager->AddEventListenerByType(listener, aType, flags); + return NS_OK; +} + +NS_IMETHODIMP +EventListenerService::RemoveSystemEventListener(EventTarget* aTarget, + const nsAString& aType, + JS::Handle aListener, + bool aUseCapture, + JSContext* aCx) { + MOZ_ASSERT(aTarget, "Missing target"); + + NS_ENSURE_TRUE(aTarget, NS_ERROR_UNEXPECTED); + + RefPtr listener = ToEventListener(aCx, aListener); + if (!listener) { + return NS_ERROR_UNEXPECTED; + } + + EventListenerManager* manager = aTarget->GetExistingListenerManager(); + if (manager) { + EventListenerFlags flags = aUseCapture ? TrustedEventsAtSystemGroupCapture() + : TrustedEventsAtSystemGroupBubble(); + manager->RemoveEventListenerByType(listener, aType, flags); + } + + return NS_OK; +} + +NS_IMETHODIMP +EventListenerService::AddListenerForAllEvents( + EventTarget* aTarget, JS::Handle aListener, bool aUseCapture, + bool aWantsUntrusted, bool aSystemEventGroup, JSContext* aCx) { + NS_ENSURE_STATE(aTarget); + + RefPtr listener = ToEventListener(aCx, aListener); + if (!listener) { + return NS_ERROR_UNEXPECTED; + } + + EventListenerManager* manager = aTarget->GetOrCreateListenerManager(); + NS_ENSURE_STATE(manager); + manager->AddListenerForAllEvents(listener, aUseCapture, aWantsUntrusted, + aSystemEventGroup); + return NS_OK; +} + +NS_IMETHODIMP +EventListenerService::RemoveListenerForAllEvents( + EventTarget* aTarget, JS::Handle aListener, bool aUseCapture, + bool aSystemEventGroup, JSContext* aCx) { + NS_ENSURE_STATE(aTarget); + + RefPtr listener = ToEventListener(aCx, aListener); + if (!listener) { + return NS_ERROR_UNEXPECTED; + } + + EventListenerManager* manager = aTarget->GetExistingListenerManager(); + if (manager) { + manager->RemoveListenerForAllEvents(listener, aUseCapture, + aSystemEventGroup); + } + return NS_OK; +} + +NS_IMETHODIMP +EventListenerService::AddListenerChangeListener( + nsIListenerChangeListener* aListener) { + if (!mChangeListeners.Contains(aListener)) { + mChangeListeners.AppendElement(aListener); + } + return NS_OK; +}; + +NS_IMETHODIMP +EventListenerService::RemoveListenerChangeListener( + nsIListenerChangeListener* aListener) { + mChangeListeners.RemoveElement(aListener); + return NS_OK; +}; + +void EventListenerService::NotifyAboutMainThreadListenerChangeInternal( + dom::EventTarget* aTarget, nsAtom* aName) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aTarget); + if (mChangeListeners.IsEmpty()) { + return; + } + + if (!mPendingListenerChanges) { + mPendingListenerChanges = nsArrayBase::Create(); + nsCOMPtr runnable = + NewRunnableMethod("EventListenerService::NotifyPendingChanges", this, + &EventListenerService::NotifyPendingChanges); + if (nsCOMPtr global = aTarget->GetOwnerGlobal()) { + global->Dispatch(TaskCategory::Other, runnable.forget()); + } else if (nsINode* node = nsINode::FromEventTarget(aTarget)) { + node->OwnerDoc()->Dispatch(TaskCategory::Other, runnable.forget()); + } else { + NS_DispatchToCurrentThread(runnable); + } + } + + RefPtr changes = + mPendingListenerChangesSet.LookupOrInsertWith(aTarget, [&] { + auto c = MakeRefPtr(aTarget); + mPendingListenerChanges->AppendElement(c); + return c; + }); + changes->AddChangedListenerName(aName); +} + +void EventListenerService::NotifyPendingChanges() { + nsCOMPtr changes; + mPendingListenerChanges.swap(changes); + mPendingListenerChangesSet.Clear(); + + for (nsCOMPtr listener : + mChangeListeners.EndLimitedRange()) { + listener->ListenersChanged(changes); + } +} + +} // namespace mozilla + +nsresult NS_NewEventListenerService(nsIEventListenerService** aResult) { + *aResult = new mozilla::EventListenerService(); + NS_ADDREF(*aResult); + return NS_OK; +} -- cgit v1.2.3