tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

EventListenerService.cpp (10248B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "EventListenerService.h"
      8 
      9 #include "mozilla/BasicEvents.h"
     10 #include "mozilla/EventDispatcher.h"
     11 #include "mozilla/EventListenerManager.h"
     12 #include "mozilla/HoldDropJSObjects.h"
     13 #include "mozilla/JSEventHandler.h"
     14 #include "mozilla/Maybe.h"
     15 #include "mozilla/dom/Document.h"
     16 #include "mozilla/dom/EventListenerBinding.h"
     17 #include "mozilla/dom/ScriptSettings.h"
     18 #include "nsArray.h"
     19 #include "nsArrayUtils.h"
     20 #include "nsCOMArray.h"
     21 #include "nsINode.h"
     22 #include "nsJSUtils.h"
     23 #include "nsServiceManagerUtils.h"
     24 #include "nsThreadUtils.h"
     25 
     26 namespace mozilla {
     27 
     28 using namespace dom;
     29 
     30 /******************************************************************************
     31 * mozilla::EventListenerChange
     32 ******************************************************************************/
     33 
     34 NS_IMPL_ISUPPORTS(EventListenerChange, nsIEventListenerChange)
     35 
     36 EventListenerChange::~EventListenerChange() = default;
     37 
     38 EventListenerChange::EventListenerChange(EventTarget* aTarget)
     39    : mTarget(aTarget) {}
     40 
     41 void EventListenerChange::AddChangedListenerName(nsAtom* aEventName) {
     42  mChangedListenerNames.AppendElement(aEventName);
     43 }
     44 
     45 NS_IMETHODIMP
     46 EventListenerChange::GetTarget(EventTarget** aTarget) {
     47  NS_ENSURE_ARG_POINTER(aTarget);
     48  NS_ADDREF(*aTarget = mTarget);
     49  return NS_OK;
     50 }
     51 
     52 NS_IMETHODIMP
     53 EventListenerChange::GetCountOfEventListenerChangesAffectingAccessibility(
     54    uint32_t* aCount) {
     55  *aCount = 0;
     56 
     57  size_t length = mChangedListenerNames.Length();
     58  for (size_t i = 0; i < length; i++) {
     59    RefPtr<nsAtom> listenerName = mChangedListenerNames[i];
     60 
     61    // These are the event listener changes which may make an element
     62    // accessible or inaccessible.
     63    if (listenerName == nsGkAtoms::onclick ||
     64        listenerName == nsGkAtoms::onmousedown ||
     65        listenerName == nsGkAtoms::onmouseup) {
     66      *aCount += 1;
     67    }
     68  }
     69 
     70  return NS_OK;
     71 }
     72 
     73 /******************************************************************************
     74 * mozilla::EventListenerInfo
     75 ******************************************************************************/
     76 
     77 EventListenerInfo::EventListenerInfo(
     78    EventListenerManager* aListenerManager, const nsAString& aType,
     79    JS::Handle<JSObject*> aScriptedListener,
     80    JS::Handle<JSObject*> aScriptedListenerGlobal, bool aCapturing,
     81    bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler)
     82    : mListenerManager(aListenerManager),
     83      mType(aType),
     84      mScriptedListener(aScriptedListener),
     85      mScriptedListenerGlobal(aScriptedListenerGlobal),
     86      mCapturing(aCapturing),
     87      mAllowsUntrusted(aAllowsUntrusted),
     88      mInSystemEventGroup(aInSystemEventGroup),
     89      mIsHandler(aIsHandler) {
     90  if (aScriptedListener) {
     91    MOZ_ASSERT(JS_IsGlobalObject(aScriptedListenerGlobal));
     92    js::AssertSameCompartment(aScriptedListener, aScriptedListenerGlobal);
     93  }
     94 
     95  HoldJSObjects(this);
     96 }
     97 
     98 EventListenerInfo::~EventListenerInfo() { DropJSObjects(this); }
     99 
    100 NS_IMPL_CYCLE_COLLECTION_WITH_JS_MEMBERS(EventListenerInfo, (mListenerManager),
    101                                         (mScriptedListener,
    102                                          mScriptedListenerGlobal))
    103 
    104 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventListenerInfo)
    105  NS_INTERFACE_MAP_ENTRY(nsIEventListenerInfo)
    106  NS_INTERFACE_MAP_ENTRY(nsISupports)
    107 NS_INTERFACE_MAP_END
    108 
    109 NS_IMPL_CYCLE_COLLECTING_ADDREF(EventListenerInfo)
    110 NS_IMPL_CYCLE_COLLECTING_RELEASE(EventListenerInfo)
    111 
    112 NS_IMETHODIMP
    113 EventListenerInfo::GetType(nsAString& aType) {
    114  aType = mType;
    115  return NS_OK;
    116 }
    117 
    118 NS_IMETHODIMP
    119 EventListenerInfo::GetCapturing(bool* aCapturing) {
    120  *aCapturing = mCapturing;
    121  return NS_OK;
    122 }
    123 
    124 NS_IMETHODIMP
    125 EventListenerInfo::GetAllowsUntrusted(bool* aAllowsUntrusted) {
    126  *aAllowsUntrusted = mAllowsUntrusted;
    127  return NS_OK;
    128 }
    129 
    130 NS_IMETHODIMP
    131 EventListenerInfo::GetInSystemEventGroup(bool* aInSystemEventGroup) {
    132  *aInSystemEventGroup = mInSystemEventGroup;
    133  return NS_OK;
    134 }
    135 
    136 NS_IMETHODIMP
    137 EventListenerInfo::GetEnabled(bool* aEnabled) {
    138  NS_ENSURE_STATE(mListenerManager);
    139  return mListenerManager->IsListenerEnabled(
    140      mType, mScriptedListener, mCapturing, mAllowsUntrusted,
    141      mInSystemEventGroup, mIsHandler, aEnabled);
    142 }
    143 
    144 NS_IMETHODIMP
    145 EventListenerInfo::SetEnabled(bool aEnabled) {
    146  NS_ENSURE_STATE(mListenerManager);
    147  return mListenerManager->SetListenerEnabled(
    148      mType, mScriptedListener, mCapturing, mAllowsUntrusted,
    149      mInSystemEventGroup, mIsHandler, aEnabled);
    150 }
    151 
    152 NS_IMETHODIMP
    153 EventListenerInfo::GetListenerObject(JSContext* aCx,
    154                                     JS::MutableHandle<JS::Value> aObject) {
    155  Maybe<JSAutoRealm> ar;
    156  GetJSVal(aCx, ar, aObject);
    157  return NS_OK;
    158 }
    159 
    160 /******************************************************************************
    161 * mozilla::EventListenerService
    162 ******************************************************************************/
    163 
    164 NS_IMPL_ISUPPORTS(EventListenerService, nsIEventListenerService)
    165 
    166 bool EventListenerInfo::GetJSVal(JSContext* aCx, Maybe<JSAutoRealm>& aAr,
    167                                 JS::MutableHandle<JS::Value> aJSVal) {
    168  if (mScriptedListener) {
    169    aJSVal.setObject(*mScriptedListener);
    170    aAr.emplace(aCx, mScriptedListenerGlobal);
    171    return true;
    172  }
    173 
    174  aJSVal.setNull();
    175  return false;
    176 }
    177 
    178 NS_IMETHODIMP
    179 EventListenerInfo::ToSource(nsAString& aResult) {
    180  aResult.SetIsVoid(true);
    181 
    182  AutoSafeJSContext cx;
    183  Maybe<JSAutoRealm> ar;
    184  JS::Rooted<JS::Value> v(cx);
    185  if (GetJSVal(cx, ar, &v)) {
    186    JSString* str = JS_ValueToSource(cx, v);
    187    if (str) {
    188      nsAutoJSString autoStr;
    189      if (autoStr.init(cx, str)) {
    190        aResult.Assign(autoStr);
    191      }
    192    }
    193  }
    194  return NS_OK;
    195 }
    196 
    197 EventListenerService* EventListenerService::sInstance = nullptr;
    198 
    199 EventListenerService::EventListenerService() {
    200  MOZ_ASSERT(!sInstance);
    201  sInstance = this;
    202 }
    203 
    204 EventListenerService::~EventListenerService() {
    205  MOZ_ASSERT(sInstance == this);
    206  sInstance = nullptr;
    207 }
    208 
    209 NS_IMETHODIMP
    210 EventListenerService::GetListenerInfoFor(
    211    EventTarget* aEventTarget,
    212    nsTArray<RefPtr<nsIEventListenerInfo>>& aOutArray) {
    213  NS_ENSURE_ARG_POINTER(aEventTarget);
    214 
    215  EventListenerManager* elm = aEventTarget->GetExistingListenerManager();
    216  if (elm) {
    217    elm->GetListenerInfo(aOutArray);
    218  }
    219 
    220  return NS_OK;
    221 }
    222 
    223 NS_IMETHODIMP
    224 EventListenerService::HasListenersFor(EventTarget* aEventTarget,
    225                                      const nsAString& aType, bool* aRetVal) {
    226  NS_ENSURE_TRUE(aEventTarget, NS_ERROR_UNEXPECTED);
    227 
    228  EventListenerManager* elm = aEventTarget->GetExistingListenerManager();
    229  *aRetVal = elm && elm->HasListenersFor(aType);
    230  return NS_OK;
    231 }
    232 
    233 static already_AddRefed<EventListener> ToEventListener(
    234    JSContext* aCx, JS::Handle<JS::Value> aValue) {
    235  if (NS_WARN_IF(!aValue.isObject())) {
    236    return nullptr;
    237  }
    238 
    239  JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
    240  JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
    241  RefPtr<EventListener> listener =
    242      new EventListener(aCx, obj, global, GetIncumbentGlobal());
    243  return listener.forget();
    244 }
    245 
    246 NS_IMETHODIMP
    247 EventListenerService::AddListenerForAllEvents(
    248    EventTarget* aTarget, JS::Handle<JS::Value> aListener, bool aUseCapture,
    249    bool aWantsUntrusted, bool aSystemEventGroup, JSContext* aCx) {
    250  NS_ENSURE_STATE(aTarget);
    251 
    252  RefPtr<EventListener> listener = ToEventListener(aCx, aListener);
    253  if (!listener) {
    254    return NS_ERROR_UNEXPECTED;
    255  }
    256 
    257  EventListenerManager* manager = aTarget->GetOrCreateListenerManager();
    258  NS_ENSURE_STATE(manager);
    259  manager->AddListenerForAllEvents(listener, aUseCapture, aWantsUntrusted,
    260                                   aSystemEventGroup);
    261  return NS_OK;
    262 }
    263 
    264 NS_IMETHODIMP
    265 EventListenerService::RemoveListenerForAllEvents(
    266    EventTarget* aTarget, JS::Handle<JS::Value> aListener, bool aUseCapture,
    267    bool aSystemEventGroup, JSContext* aCx) {
    268  NS_ENSURE_STATE(aTarget);
    269 
    270  RefPtr<EventListener> listener = ToEventListener(aCx, aListener);
    271  if (!listener) {
    272    return NS_ERROR_UNEXPECTED;
    273  }
    274 
    275  EventListenerManager* manager = aTarget->GetExistingListenerManager();
    276  if (manager) {
    277    manager->RemoveListenerForAllEvents(listener, aUseCapture,
    278                                        aSystemEventGroup);
    279  }
    280  return NS_OK;
    281 }
    282 
    283 NS_IMETHODIMP
    284 EventListenerService::AddListenerChangeListener(
    285    nsIListenerChangeListener* aListener) {
    286  if (!mChangeListeners.Contains(aListener)) {
    287    mChangeListeners.AppendElement(aListener);
    288  }
    289  return NS_OK;
    290 };
    291 
    292 NS_IMETHODIMP
    293 EventListenerService::RemoveListenerChangeListener(
    294    nsIListenerChangeListener* aListener) {
    295  mChangeListeners.RemoveElement(aListener);
    296  return NS_OK;
    297 };
    298 
    299 void EventListenerService::NotifyAboutMainThreadListenerChangeInternal(
    300    dom::EventTarget* aTarget, nsAtom* aName) {
    301  MOZ_ASSERT(NS_IsMainThread());
    302  MOZ_ASSERT(aTarget);
    303  if (mChangeListeners.IsEmpty()) {
    304    return;
    305  }
    306 
    307  if (!mPendingListenerChanges) {
    308    mPendingListenerChanges = nsArrayBase::Create();
    309    nsCOMPtr<nsIRunnable> runnable =
    310        NewRunnableMethod("EventListenerService::NotifyPendingChanges", this,
    311                          &EventListenerService::NotifyPendingChanges);
    312    NS_DispatchToCurrentThread(runnable.forget());
    313  }
    314 
    315  RefPtr<EventListenerChange> changes =
    316      mPendingListenerChangesSet.LookupOrInsertWith(aTarget, [&] {
    317        auto c = MakeRefPtr<EventListenerChange>(aTarget);
    318        mPendingListenerChanges->AppendElement(c);
    319        return c;
    320      });
    321  changes->AddChangedListenerName(aName);
    322 }
    323 
    324 void EventListenerService::NotifyPendingChanges() {
    325  nsCOMPtr<nsIMutableArray> changes;
    326  mPendingListenerChanges.swap(changes);
    327  mPendingListenerChangesSet.Clear();
    328 
    329  for (nsCOMPtr<nsIListenerChangeListener> listener :
    330       mChangeListeners.EndLimitedRange()) {
    331    listener->ListenersChanged(changes);
    332  }
    333 }
    334 
    335 }  // namespace mozilla
    336 
    337 nsresult NS_NewEventListenerService(nsIEventListenerService** aResult) {
    338  *aResult = new mozilla::EventListenerService();
    339  NS_ADDREF(*aResult);
    340  return NS_OK;
    341 }