/* -*- 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/JSEventHandler.h" #include "mozilla/Maybe.h" #include "nsArrayUtils.h" #include "nsCOMArray.h" #include "nsDOMClassInfoID.h" #include "nsIXPConnect.h" #include "nsJSUtils.h" #include "nsMemory.h" #include "nsServiceManagerUtils.h" #include "nsArray.h" #include "nsThreadUtils.h" namespace mozilla { using namespace dom; /****************************************************************************** * mozilla::EventListenerChange ******************************************************************************/ NS_IMPL_ISUPPORTS(EventListenerChange, nsIEventListenerChange) EventListenerChange::~EventListenerChange() {} EventListenerChange::EventListenerChange(dom::EventTarget* aTarget) : mTarget(aTarget) {} void EventListenerChange::AddChangedListenerName(nsAtom* aEventName) { mChangedListenerNames.AppendElement(aEventName); } NS_IMETHODIMP EventListenerChange::GetTarget(nsIDOMEventTarget** 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 ******************************************************************************/ NS_IMPL_CYCLE_COLLECTION(EventListenerInfo, mListener) 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::GetListenerObject(JSContext* aCx, JS::MutableHandle aObject) { Maybe ac; GetJSVal(aCx, ac, aObject); return NS_OK; } /****************************************************************************** * mozilla::EventListenerService ******************************************************************************/ NS_IMPL_ISUPPORTS(EventListenerService, nsIEventListenerService) bool EventListenerInfo::GetJSVal(JSContext* aCx, Maybe& aAc, JS::MutableHandle aJSVal) { aJSVal.setNull(); nsCOMPtr wrappedJS = do_QueryInterface(mListener); if (wrappedJS) { JS::Rooted object(aCx, wrappedJS->GetJSObject()); if (!object) { return false; } aAc.emplace(aCx, object); aJSVal.setObject(*object); return true; } nsCOMPtr jsHandler = do_QueryInterface(mListener); if (jsHandler && jsHandler->GetTypedEventHandler().HasEventHandler()) { JS::Handle handler = jsHandler->GetTypedEventHandler().Ptr()->CallableOrNull(); if (handler) { aAc.emplace(aCx, handler); aJSVal.setObject(*handler); return true; } } return false; } NS_IMETHODIMP EventListenerInfo::ToSource(nsAString& aResult) { aResult.SetIsVoid(true); AutoSafeJSContext cx; Maybe ac; JS::Rooted v(cx); if (GetJSVal(cx, ac, &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(nsIDOMEventTarget* aEventTarget, uint32_t* aCount, nsIEventListenerInfo*** aOutArray) { NS_ENSURE_ARG_POINTER(aEventTarget); *aCount = 0; *aOutArray = nullptr; nsCOMArray listenerInfos; nsCOMPtr eventTarget = do_QueryInterface(aEventTarget); NS_ENSURE_TRUE(eventTarget, NS_ERROR_NO_INTERFACE); EventListenerManager* elm = eventTarget->GetExistingListenerManager(); if (elm) { elm->GetListenerInfo(&listenerInfos); } int32_t count = listenerInfos.Count(); if (count == 0) { return NS_OK; } listenerInfos.Forget(aOutArray); *aCount = count; return NS_OK; } NS_IMETHODIMP EventListenerService::GetEventTargetChainFor(nsIDOMEventTarget* aEventTarget, bool aComposed, uint32_t* aCount, nsIDOMEventTarget*** aOutArray) { *aCount = 0; *aOutArray = nullptr; 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); int32_t count = targets.Length(); if (count == 0) { return NS_OK; } *aOutArray = static_cast( moz_xmalloc(sizeof(nsIDOMEventTarget*) * count)); NS_ENSURE_TRUE(*aOutArray, NS_ERROR_OUT_OF_MEMORY); for (int32_t i = 0; i < count; ++i) { NS_ADDREF((*aOutArray)[i] = targets[i]); } *aCount = count; return NS_OK; } NS_IMETHODIMP EventListenerService::HasListenersFor(nsIDOMEventTarget* aEventTarget, const nsAString& aType, bool* aRetVal) { nsCOMPtr eventTarget = do_QueryInterface(aEventTarget); NS_ENSURE_TRUE(eventTarget, NS_ERROR_NO_INTERFACE); EventListenerManager* elm = eventTarget->GetExistingListenerManager(); *aRetVal = elm && elm->HasListenersFor(aType); return NS_OK; } NS_IMETHODIMP EventListenerService::AddSystemEventListener(nsIDOMEventTarget* aTarget, const nsAString& aType, nsIDOMEventListener* aListener, bool aUseCapture) { NS_PRECONDITION(aTarget, "Missing target"); NS_PRECONDITION(aListener, "Missing listener"); nsCOMPtr eventTarget = do_QueryInterface(aTarget); NS_ENSURE_TRUE(eventTarget, NS_ERROR_NO_INTERFACE); EventListenerManager* manager = eventTarget->GetOrCreateListenerManager(); NS_ENSURE_STATE(manager); EventListenerFlags flags = aUseCapture ? TrustedEventsAtSystemGroupCapture() : TrustedEventsAtSystemGroupBubble(); manager->AddEventListenerByType(aListener, aType, flags); return NS_OK; } NS_IMETHODIMP EventListenerService::RemoveSystemEventListener(nsIDOMEventTarget* aTarget, const nsAString& aType, nsIDOMEventListener* aListener, bool aUseCapture) { NS_PRECONDITION(aTarget, "Missing target"); NS_PRECONDITION(aListener, "Missing listener"); nsCOMPtr eventTarget = do_QueryInterface(aTarget); NS_ENSURE_TRUE(eventTarget, NS_ERROR_NO_INTERFACE); EventListenerManager* manager = eventTarget->GetExistingListenerManager(); if (manager) { EventListenerFlags flags = aUseCapture ? TrustedEventsAtSystemGroupCapture() : TrustedEventsAtSystemGroupBubble(); manager->RemoveEventListenerByType(aListener, aType, flags); } return NS_OK; } NS_IMETHODIMP EventListenerService::AddListenerForAllEvents(nsIDOMEventTarget* aTarget, nsIDOMEventListener* aListener, bool aUseCapture, bool aWantsUntrusted, bool aSystemEventGroup) { NS_ENSURE_STATE(aTarget && aListener); nsCOMPtr eventTarget = do_QueryInterface(aTarget); NS_ENSURE_TRUE(eventTarget, NS_ERROR_NO_INTERFACE); EventListenerManager* manager = eventTarget->GetOrCreateListenerManager(); NS_ENSURE_STATE(manager); manager->AddListenerForAllEvents(aListener, aUseCapture, aWantsUntrusted, aSystemEventGroup); return NS_OK; } NS_IMETHODIMP EventListenerService::RemoveListenerForAllEvents(nsIDOMEventTarget* aTarget, nsIDOMEventListener* aListener, bool aUseCapture, bool aSystemEventGroup) { NS_ENSURE_STATE(aTarget && aListener); nsCOMPtr eventTarget = do_QueryInterface(aTarget); NS_ENSURE_TRUE(eventTarget, NS_ERROR_NO_INTERFACE); EventListenerManager* manager = eventTarget->GetExistingListenerManager(); if (manager) { manager->RemoveListenerForAllEvents(aListener, 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 (nsCOMPtr node = do_QueryInterface(aTarget)) { node->OwnerDoc()->Dispatch(TaskCategory::Other, runnable.forget()); } else { NS_DispatchToCurrentThread(runnable); } } RefPtr changes = mPendingListenerChangesSet.LookupForAdd(aTarget).OrInsert( [this, aTarget]() { EventListenerChange* c = new EventListenerChange(aTarget); mPendingListenerChanges->AppendElement(c); return c; }); changes->AddChangedListenerName(aName); } void EventListenerService::NotifyPendingChanges() { nsCOMPtr changes; mPendingListenerChanges.swap(changes); mPendingListenerChangesSet.Clear(); nsTObserverArray>::EndLimitedIterator iter(mChangeListeners); while (iter.HasMore()) { nsCOMPtr listener = iter.GetNext(); listener->ListenersChanged(changes); } } } // namespace mozilla nsresult NS_NewEventListenerService(nsIEventListenerService** aResult) { *aResult = new mozilla::EventListenerService(); NS_ADDREF(*aResult); return NS_OK; }