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 }