Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | // Microsoft's API Name hackery sucks |
michael@0 | 7 | #undef CreateEvent |
michael@0 | 8 | |
michael@0 | 9 | #include "mozilla/BasicEvents.h" |
michael@0 | 10 | #include "mozilla/EventDispatcher.h" |
michael@0 | 11 | #include "mozilla/EventListenerManager.h" |
michael@0 | 12 | #ifdef MOZ_B2G |
michael@0 | 13 | #include "mozilla/Hal.h" |
michael@0 | 14 | #endif // #ifdef MOZ_B2G |
michael@0 | 15 | #include "mozilla/HalSensor.h" |
michael@0 | 16 | #include "mozilla/InternalMutationEvent.h" |
michael@0 | 17 | #include "mozilla/JSEventHandler.h" |
michael@0 | 18 | #include "mozilla/MemoryReporting.h" |
michael@0 | 19 | #include "mozilla/dom/BindingUtils.h" |
michael@0 | 20 | #include "mozilla/dom/Element.h" |
michael@0 | 21 | #include "mozilla/dom/Event.h" |
michael@0 | 22 | |
michael@0 | 23 | #include "EventListenerService.h" |
michael@0 | 24 | #include "nsCOMArray.h" |
michael@0 | 25 | #include "nsCOMPtr.h" |
michael@0 | 26 | #include "nsContentUtils.h" |
michael@0 | 27 | #include "nsDOMCID.h" |
michael@0 | 28 | #include "nsError.h" |
michael@0 | 29 | #include "nsGkAtoms.h" |
michael@0 | 30 | #include "nsIContent.h" |
michael@0 | 31 | #include "nsIContentSecurityPolicy.h" |
michael@0 | 32 | #include "nsIDocument.h" |
michael@0 | 33 | #include "nsIDOMEventListener.h" |
michael@0 | 34 | #include "nsIScriptGlobalObject.h" |
michael@0 | 35 | #include "nsISupports.h" |
michael@0 | 36 | #include "nsIXPConnect.h" |
michael@0 | 37 | #include "nsJSUtils.h" |
michael@0 | 38 | #include "nsNameSpaceManager.h" |
michael@0 | 39 | #include "nsPIDOMWindow.h" |
michael@0 | 40 | #include "nsSandboxFlags.h" |
michael@0 | 41 | #include "xpcpublic.h" |
michael@0 | 42 | |
michael@0 | 43 | namespace mozilla { |
michael@0 | 44 | |
michael@0 | 45 | using namespace dom; |
michael@0 | 46 | using namespace hal; |
michael@0 | 47 | |
michael@0 | 48 | #define EVENT_TYPE_EQUALS(ls, type, userType, typeString, allEvents) \ |
michael@0 | 49 | ((ls->mEventType == type && \ |
michael@0 | 50 | (ls->mEventType != NS_USER_DEFINED_EVENT || \ |
michael@0 | 51 | (mIsMainThreadELM && ls->mTypeAtom == userType) || \ |
michael@0 | 52 | (!mIsMainThreadELM && ls->mTypeString.Equals(typeString)))) || \ |
michael@0 | 53 | (allEvents && ls->mAllEvents)) |
michael@0 | 54 | |
michael@0 | 55 | static const uint32_t kAllMutationBits = |
michael@0 | 56 | NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED | |
michael@0 | 57 | NS_EVENT_BITS_MUTATION_NODEINSERTED | |
michael@0 | 58 | NS_EVENT_BITS_MUTATION_NODEREMOVED | |
michael@0 | 59 | NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT | |
michael@0 | 60 | NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT | |
michael@0 | 61 | NS_EVENT_BITS_MUTATION_ATTRMODIFIED | |
michael@0 | 62 | NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED; |
michael@0 | 63 | |
michael@0 | 64 | static uint32_t |
michael@0 | 65 | MutationBitForEventType(uint32_t aEventType) |
michael@0 | 66 | { |
michael@0 | 67 | switch (aEventType) { |
michael@0 | 68 | case NS_MUTATION_SUBTREEMODIFIED: |
michael@0 | 69 | return NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED; |
michael@0 | 70 | case NS_MUTATION_NODEINSERTED: |
michael@0 | 71 | return NS_EVENT_BITS_MUTATION_NODEINSERTED; |
michael@0 | 72 | case NS_MUTATION_NODEREMOVED: |
michael@0 | 73 | return NS_EVENT_BITS_MUTATION_NODEREMOVED; |
michael@0 | 74 | case NS_MUTATION_NODEREMOVEDFROMDOCUMENT: |
michael@0 | 75 | return NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT; |
michael@0 | 76 | case NS_MUTATION_NODEINSERTEDINTODOCUMENT: |
michael@0 | 77 | return NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT; |
michael@0 | 78 | case NS_MUTATION_ATTRMODIFIED: |
michael@0 | 79 | return NS_EVENT_BITS_MUTATION_ATTRMODIFIED; |
michael@0 | 80 | case NS_MUTATION_CHARACTERDATAMODIFIED: |
michael@0 | 81 | return NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED; |
michael@0 | 82 | default: |
michael@0 | 83 | break; |
michael@0 | 84 | } |
michael@0 | 85 | return 0; |
michael@0 | 86 | } |
michael@0 | 87 | |
michael@0 | 88 | uint32_t EventListenerManager::sMainThreadCreatedCount = 0; |
michael@0 | 89 | |
michael@0 | 90 | EventListenerManager::EventListenerManager(EventTarget* aTarget) |
michael@0 | 91 | : mMayHavePaintEventListener(false) |
michael@0 | 92 | , mMayHaveMutationListeners(false) |
michael@0 | 93 | , mMayHaveCapturingListeners(false) |
michael@0 | 94 | , mMayHaveSystemGroupListeners(false) |
michael@0 | 95 | , mMayHaveTouchEventListener(false) |
michael@0 | 96 | , mMayHaveMouseEnterLeaveEventListener(false) |
michael@0 | 97 | , mMayHavePointerEnterLeaveEventListener(false) |
michael@0 | 98 | , mClearingListeners(false) |
michael@0 | 99 | , mIsMainThreadELM(NS_IsMainThread()) |
michael@0 | 100 | , mNoListenerForEvent(0) |
michael@0 | 101 | , mTarget(aTarget) |
michael@0 | 102 | { |
michael@0 | 103 | NS_ASSERTION(aTarget, "unexpected null pointer"); |
michael@0 | 104 | |
michael@0 | 105 | if (mIsMainThreadELM) { |
michael@0 | 106 | ++sMainThreadCreatedCount; |
michael@0 | 107 | } |
michael@0 | 108 | } |
michael@0 | 109 | |
michael@0 | 110 | EventListenerManager::~EventListenerManager() |
michael@0 | 111 | { |
michael@0 | 112 | // If your code fails this assertion, a possible reason is that |
michael@0 | 113 | // a class did not call our Disconnect() manually. Note that |
michael@0 | 114 | // this class can have Disconnect called in one of two ways: |
michael@0 | 115 | // if it is part of a cycle, then in Unlink() (such a cycle |
michael@0 | 116 | // would be with one of the listeners, not mTarget which is weak). |
michael@0 | 117 | // If not part of a cycle, then Disconnect must be called manually, |
michael@0 | 118 | // typically from the destructor of the owner class (mTarget). |
michael@0 | 119 | // XXX azakai: Is there any reason to not just call Disconnect |
michael@0 | 120 | // from right here, if not previously called? |
michael@0 | 121 | NS_ASSERTION(!mTarget, "didn't call Disconnect"); |
michael@0 | 122 | RemoveAllListeners(); |
michael@0 | 123 | } |
michael@0 | 124 | |
michael@0 | 125 | void |
michael@0 | 126 | EventListenerManager::RemoveAllListeners() |
michael@0 | 127 | { |
michael@0 | 128 | if (mClearingListeners) { |
michael@0 | 129 | return; |
michael@0 | 130 | } |
michael@0 | 131 | mClearingListeners = true; |
michael@0 | 132 | mListeners.Clear(); |
michael@0 | 133 | mClearingListeners = false; |
michael@0 | 134 | } |
michael@0 | 135 | |
michael@0 | 136 | void |
michael@0 | 137 | EventListenerManager::Shutdown() |
michael@0 | 138 | { |
michael@0 | 139 | Event::Shutdown(); |
michael@0 | 140 | } |
michael@0 | 141 | |
michael@0 | 142 | NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(EventListenerManager, AddRef) |
michael@0 | 143 | NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(EventListenerManager, Release) |
michael@0 | 144 | |
michael@0 | 145 | inline void |
michael@0 | 146 | ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, |
michael@0 | 147 | EventListenerManager::Listener& aField, |
michael@0 | 148 | const char* aName, |
michael@0 | 149 | unsigned aFlags) |
michael@0 | 150 | { |
michael@0 | 151 | if (MOZ_UNLIKELY(aCallback.WantDebugInfo())) { |
michael@0 | 152 | nsAutoCString name; |
michael@0 | 153 | name.AppendASCII(aName); |
michael@0 | 154 | if (aField.mTypeAtom) { |
michael@0 | 155 | name.AppendASCII(" event="); |
michael@0 | 156 | name.Append(nsAtomCString(aField.mTypeAtom)); |
michael@0 | 157 | name.AppendASCII(" listenerType="); |
michael@0 | 158 | name.AppendInt(aField.mListenerType); |
michael@0 | 159 | name.AppendASCII(" "); |
michael@0 | 160 | } |
michael@0 | 161 | CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), name.get(), |
michael@0 | 162 | aFlags); |
michael@0 | 163 | } else { |
michael@0 | 164 | CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), aName, |
michael@0 | 165 | aFlags); |
michael@0 | 166 | } |
michael@0 | 167 | } |
michael@0 | 168 | |
michael@0 | 169 | NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager) |
michael@0 | 170 | |
michael@0 | 171 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EventListenerManager) |
michael@0 | 172 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners) |
michael@0 | 173 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
michael@0 | 174 | |
michael@0 | 175 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EventListenerManager) |
michael@0 | 176 | tmp->Disconnect(); |
michael@0 | 177 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
michael@0 | 178 | |
michael@0 | 179 | |
michael@0 | 180 | nsPIDOMWindow* |
michael@0 | 181 | EventListenerManager::GetInnerWindowForTarget() |
michael@0 | 182 | { |
michael@0 | 183 | nsCOMPtr<nsINode> node = do_QueryInterface(mTarget); |
michael@0 | 184 | if (node) { |
michael@0 | 185 | // XXX sXBL/XBL2 issue -- do we really want the owner here? What |
michael@0 | 186 | // if that's the XBL document? |
michael@0 | 187 | return node->OwnerDoc()->GetInnerWindow(); |
michael@0 | 188 | } |
michael@0 | 189 | |
michael@0 | 190 | nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); |
michael@0 | 191 | return window; |
michael@0 | 192 | } |
michael@0 | 193 | |
michael@0 | 194 | already_AddRefed<nsPIDOMWindow> |
michael@0 | 195 | EventListenerManager::GetTargetAsInnerWindow() const |
michael@0 | 196 | { |
michael@0 | 197 | nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget); |
michael@0 | 198 | if (!window) { |
michael@0 | 199 | return nullptr; |
michael@0 | 200 | } |
michael@0 | 201 | |
michael@0 | 202 | NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window"); |
michael@0 | 203 | return window.forget(); |
michael@0 | 204 | } |
michael@0 | 205 | |
michael@0 | 206 | void |
michael@0 | 207 | EventListenerManager::AddEventListenerInternal( |
michael@0 | 208 | const EventListenerHolder& aListenerHolder, |
michael@0 | 209 | uint32_t aType, |
michael@0 | 210 | nsIAtom* aTypeAtom, |
michael@0 | 211 | const nsAString& aTypeString, |
michael@0 | 212 | const EventListenerFlags& aFlags, |
michael@0 | 213 | bool aHandler, |
michael@0 | 214 | bool aAllEvents) |
michael@0 | 215 | { |
michael@0 | 216 | MOZ_ASSERT((NS_IsMainThread() && aType && aTypeAtom) || // Main thread |
michael@0 | 217 | (!NS_IsMainThread() && aType && !aTypeString.IsEmpty()) || // non-main-thread |
michael@0 | 218 | aAllEvents, "Missing type"); // all-events listener |
michael@0 | 219 | |
michael@0 | 220 | if (!aListenerHolder || mClearingListeners) { |
michael@0 | 221 | return; |
michael@0 | 222 | } |
michael@0 | 223 | |
michael@0 | 224 | // Since there is no public API to call us with an EventListenerHolder, we |
michael@0 | 225 | // know that there's an EventListenerHolder on the stack holding a strong ref |
michael@0 | 226 | // to the listener. |
michael@0 | 227 | |
michael@0 | 228 | Listener* listener; |
michael@0 | 229 | uint32_t count = mListeners.Length(); |
michael@0 | 230 | for (uint32_t i = 0; i < count; i++) { |
michael@0 | 231 | listener = &mListeners.ElementAt(i); |
michael@0 | 232 | // mListener == aListenerHolder is the last one, since it can be a bit slow. |
michael@0 | 233 | if (listener->mListenerIsHandler == aHandler && |
michael@0 | 234 | listener->mFlags == aFlags && |
michael@0 | 235 | EVENT_TYPE_EQUALS(listener, aType, aTypeAtom, aTypeString, |
michael@0 | 236 | aAllEvents) && |
michael@0 | 237 | listener->mListener == aListenerHolder) { |
michael@0 | 238 | return; |
michael@0 | 239 | } |
michael@0 | 240 | } |
michael@0 | 241 | |
michael@0 | 242 | mNoListenerForEvent = NS_EVENT_NULL; |
michael@0 | 243 | mNoListenerForEventAtom = nullptr; |
michael@0 | 244 | |
michael@0 | 245 | listener = aAllEvents ? mListeners.InsertElementAt(0) : |
michael@0 | 246 | mListeners.AppendElement(); |
michael@0 | 247 | listener->mListener = aListenerHolder; |
michael@0 | 248 | MOZ_ASSERT(aType < PR_UINT16_MAX); |
michael@0 | 249 | listener->mEventType = aType; |
michael@0 | 250 | listener->mTypeString = aTypeString; |
michael@0 | 251 | listener->mTypeAtom = aTypeAtom; |
michael@0 | 252 | listener->mFlags = aFlags; |
michael@0 | 253 | listener->mListenerIsHandler = aHandler; |
michael@0 | 254 | listener->mHandlerIsString = false; |
michael@0 | 255 | listener->mAllEvents = aAllEvents; |
michael@0 | 256 | |
michael@0 | 257 | // Detect the type of event listener. |
michael@0 | 258 | nsCOMPtr<nsIXPConnectWrappedJS> wjs; |
michael@0 | 259 | if (aFlags.mListenerIsJSListener) { |
michael@0 | 260 | MOZ_ASSERT(!aListenerHolder.HasWebIDLCallback()); |
michael@0 | 261 | listener->mListenerType = Listener::eJSEventListener; |
michael@0 | 262 | } else if (aListenerHolder.HasWebIDLCallback()) { |
michael@0 | 263 | listener->mListenerType = Listener::eWebIDLListener; |
michael@0 | 264 | } else if ((wjs = do_QueryInterface(aListenerHolder.GetXPCOMCallback()))) { |
michael@0 | 265 | listener->mListenerType = Listener::eWrappedJSListener; |
michael@0 | 266 | } else { |
michael@0 | 267 | listener->mListenerType = Listener::eNativeListener; |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | |
michael@0 | 271 | if (aFlags.mInSystemGroup) { |
michael@0 | 272 | mMayHaveSystemGroupListeners = true; |
michael@0 | 273 | } |
michael@0 | 274 | if (aFlags.mCapture) { |
michael@0 | 275 | mMayHaveCapturingListeners = true; |
michael@0 | 276 | } |
michael@0 | 277 | |
michael@0 | 278 | if (aType == NS_AFTERPAINT) { |
michael@0 | 279 | mMayHavePaintEventListener = true; |
michael@0 | 280 | nsPIDOMWindow* window = GetInnerWindowForTarget(); |
michael@0 | 281 | if (window) { |
michael@0 | 282 | window->SetHasPaintEventListeners(); |
michael@0 | 283 | } |
michael@0 | 284 | } else if (aType >= NS_MUTATION_START && aType <= NS_MUTATION_END) { |
michael@0 | 285 | // For mutation listeners, we need to update the global bit on the DOM window. |
michael@0 | 286 | // Otherwise we won't actually fire the mutation event. |
michael@0 | 287 | mMayHaveMutationListeners = true; |
michael@0 | 288 | // Go from our target to the nearest enclosing DOM window. |
michael@0 | 289 | nsPIDOMWindow* window = GetInnerWindowForTarget(); |
michael@0 | 290 | if (window) { |
michael@0 | 291 | nsCOMPtr<nsIDocument> doc = window->GetExtantDoc(); |
michael@0 | 292 | if (doc) { |
michael@0 | 293 | doc->WarnOnceAbout(nsIDocument::eMutationEvent); |
michael@0 | 294 | } |
michael@0 | 295 | // If aType is NS_MUTATION_SUBTREEMODIFIED, we need to listen all |
michael@0 | 296 | // mutations. nsContentUtils::HasMutationListeners relies on this. |
michael@0 | 297 | window->SetMutationListeners((aType == NS_MUTATION_SUBTREEMODIFIED) ? |
michael@0 | 298 | kAllMutationBits : |
michael@0 | 299 | MutationBitForEventType(aType)); |
michael@0 | 300 | } |
michael@0 | 301 | } else if (aTypeAtom == nsGkAtoms::ondeviceorientation) { |
michael@0 | 302 | EnableDevice(NS_DEVICE_ORIENTATION); |
michael@0 | 303 | } else if (aTypeAtom == nsGkAtoms::ondeviceproximity || aTypeAtom == nsGkAtoms::onuserproximity) { |
michael@0 | 304 | EnableDevice(NS_DEVICE_PROXIMITY); |
michael@0 | 305 | } else if (aTypeAtom == nsGkAtoms::ondevicelight) { |
michael@0 | 306 | EnableDevice(NS_DEVICE_LIGHT); |
michael@0 | 307 | } else if (aTypeAtom == nsGkAtoms::ondevicemotion) { |
michael@0 | 308 | EnableDevice(NS_DEVICE_MOTION); |
michael@0 | 309 | #ifdef MOZ_B2G |
michael@0 | 310 | } else if (aTypeAtom == nsGkAtoms::onmoztimechange) { |
michael@0 | 311 | nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); |
michael@0 | 312 | if (window) { |
michael@0 | 313 | window->EnableTimeChangeNotifications(); |
michael@0 | 314 | } |
michael@0 | 315 | } else if (aTypeAtom == nsGkAtoms::onmoznetworkupload) { |
michael@0 | 316 | nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); |
michael@0 | 317 | if (window) { |
michael@0 | 318 | window->EnableNetworkEvent(NS_NETWORK_UPLOAD_EVENT); |
michael@0 | 319 | } |
michael@0 | 320 | } else if (aTypeAtom == nsGkAtoms::onmoznetworkdownload) { |
michael@0 | 321 | nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); |
michael@0 | 322 | if (window) { |
michael@0 | 323 | window->EnableNetworkEvent(NS_NETWORK_DOWNLOAD_EVENT); |
michael@0 | 324 | } |
michael@0 | 325 | #endif // MOZ_B2G |
michael@0 | 326 | } else if (aTypeAtom == nsGkAtoms::ontouchstart || |
michael@0 | 327 | aTypeAtom == nsGkAtoms::ontouchend || |
michael@0 | 328 | aTypeAtom == nsGkAtoms::ontouchmove || |
michael@0 | 329 | aTypeAtom == nsGkAtoms::ontouchenter || |
michael@0 | 330 | aTypeAtom == nsGkAtoms::ontouchleave || |
michael@0 | 331 | aTypeAtom == nsGkAtoms::ontouchcancel) { |
michael@0 | 332 | mMayHaveTouchEventListener = true; |
michael@0 | 333 | nsPIDOMWindow* window = GetInnerWindowForTarget(); |
michael@0 | 334 | // we don't want touchevent listeners added by scrollbars to flip this flag |
michael@0 | 335 | // so we ignore listeners created with system event flag |
michael@0 | 336 | if (window && !aFlags.mInSystemGroup) { |
michael@0 | 337 | window->SetHasTouchEventListeners(); |
michael@0 | 338 | } |
michael@0 | 339 | } else if (aType >= NS_POINTER_EVENT_START && aType <= NS_POINTER_LOST_CAPTURE) { |
michael@0 | 340 | nsPIDOMWindow* window = GetInnerWindowForTarget(); |
michael@0 | 341 | if (aTypeAtom == nsGkAtoms::onpointerenter || |
michael@0 | 342 | aTypeAtom == nsGkAtoms::onpointerleave) { |
michael@0 | 343 | mMayHavePointerEnterLeaveEventListener = true; |
michael@0 | 344 | if (window) { |
michael@0 | 345 | #ifdef DEBUG |
michael@0 | 346 | nsCOMPtr<nsIDocument> d = window->GetExtantDoc(); |
michael@0 | 347 | NS_WARN_IF_FALSE(!nsContentUtils::IsChromeDoc(d), |
michael@0 | 348 | "Please do not use pointerenter/leave events in chrome. " |
michael@0 | 349 | "They are slower than pointerover/out!"); |
michael@0 | 350 | #endif |
michael@0 | 351 | window->SetHasPointerEnterLeaveEventListeners(); |
michael@0 | 352 | } |
michael@0 | 353 | } |
michael@0 | 354 | } else if (aTypeAtom == nsGkAtoms::onmouseenter || |
michael@0 | 355 | aTypeAtom == nsGkAtoms::onmouseleave) { |
michael@0 | 356 | mMayHaveMouseEnterLeaveEventListener = true; |
michael@0 | 357 | nsPIDOMWindow* window = GetInnerWindowForTarget(); |
michael@0 | 358 | if (window) { |
michael@0 | 359 | #ifdef DEBUG |
michael@0 | 360 | nsCOMPtr<nsIDocument> d = window->GetExtantDoc(); |
michael@0 | 361 | NS_WARN_IF_FALSE(!nsContentUtils::IsChromeDoc(d), |
michael@0 | 362 | "Please do not use mouseenter/leave events in chrome. " |
michael@0 | 363 | "They are slower than mouseover/out!"); |
michael@0 | 364 | #endif |
michael@0 | 365 | window->SetHasMouseEnterLeaveEventListeners(); |
michael@0 | 366 | } |
michael@0 | 367 | #ifdef MOZ_GAMEPAD |
michael@0 | 368 | } else if (aType >= NS_GAMEPAD_START && |
michael@0 | 369 | aType <= NS_GAMEPAD_END) { |
michael@0 | 370 | nsPIDOMWindow* window = GetInnerWindowForTarget(); |
michael@0 | 371 | if (window) { |
michael@0 | 372 | window->SetHasGamepadEventListener(); |
michael@0 | 373 | } |
michael@0 | 374 | #endif |
michael@0 | 375 | } |
michael@0 | 376 | if (aTypeAtom && mTarget) { |
michael@0 | 377 | mTarget->EventListenerAdded(aTypeAtom); |
michael@0 | 378 | } |
michael@0 | 379 | } |
michael@0 | 380 | |
michael@0 | 381 | bool |
michael@0 | 382 | EventListenerManager::IsDeviceType(uint32_t aType) |
michael@0 | 383 | { |
michael@0 | 384 | switch (aType) { |
michael@0 | 385 | case NS_DEVICE_ORIENTATION: |
michael@0 | 386 | case NS_DEVICE_MOTION: |
michael@0 | 387 | case NS_DEVICE_LIGHT: |
michael@0 | 388 | case NS_DEVICE_PROXIMITY: |
michael@0 | 389 | case NS_USER_PROXIMITY: |
michael@0 | 390 | return true; |
michael@0 | 391 | default: |
michael@0 | 392 | break; |
michael@0 | 393 | } |
michael@0 | 394 | return false; |
michael@0 | 395 | } |
michael@0 | 396 | |
michael@0 | 397 | void |
michael@0 | 398 | EventListenerManager::EnableDevice(uint32_t aType) |
michael@0 | 399 | { |
michael@0 | 400 | nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); |
michael@0 | 401 | if (!window) { |
michael@0 | 402 | return; |
michael@0 | 403 | } |
michael@0 | 404 | |
michael@0 | 405 | switch (aType) { |
michael@0 | 406 | case NS_DEVICE_ORIENTATION: |
michael@0 | 407 | window->EnableDeviceSensor(SENSOR_ORIENTATION); |
michael@0 | 408 | break; |
michael@0 | 409 | case NS_DEVICE_PROXIMITY: |
michael@0 | 410 | case NS_USER_PROXIMITY: |
michael@0 | 411 | window->EnableDeviceSensor(SENSOR_PROXIMITY); |
michael@0 | 412 | break; |
michael@0 | 413 | case NS_DEVICE_LIGHT: |
michael@0 | 414 | window->EnableDeviceSensor(SENSOR_LIGHT); |
michael@0 | 415 | break; |
michael@0 | 416 | case NS_DEVICE_MOTION: |
michael@0 | 417 | window->EnableDeviceSensor(SENSOR_ACCELERATION); |
michael@0 | 418 | window->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION); |
michael@0 | 419 | window->EnableDeviceSensor(SENSOR_GYROSCOPE); |
michael@0 | 420 | break; |
michael@0 | 421 | default: |
michael@0 | 422 | NS_WARNING("Enabling an unknown device sensor."); |
michael@0 | 423 | break; |
michael@0 | 424 | } |
michael@0 | 425 | } |
michael@0 | 426 | |
michael@0 | 427 | void |
michael@0 | 428 | EventListenerManager::DisableDevice(uint32_t aType) |
michael@0 | 429 | { |
michael@0 | 430 | nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); |
michael@0 | 431 | if (!window) { |
michael@0 | 432 | return; |
michael@0 | 433 | } |
michael@0 | 434 | |
michael@0 | 435 | switch (aType) { |
michael@0 | 436 | case NS_DEVICE_ORIENTATION: |
michael@0 | 437 | window->DisableDeviceSensor(SENSOR_ORIENTATION); |
michael@0 | 438 | break; |
michael@0 | 439 | case NS_DEVICE_MOTION: |
michael@0 | 440 | window->DisableDeviceSensor(SENSOR_ACCELERATION); |
michael@0 | 441 | window->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION); |
michael@0 | 442 | window->DisableDeviceSensor(SENSOR_GYROSCOPE); |
michael@0 | 443 | break; |
michael@0 | 444 | case NS_DEVICE_PROXIMITY: |
michael@0 | 445 | case NS_USER_PROXIMITY: |
michael@0 | 446 | window->DisableDeviceSensor(SENSOR_PROXIMITY); |
michael@0 | 447 | break; |
michael@0 | 448 | case NS_DEVICE_LIGHT: |
michael@0 | 449 | window->DisableDeviceSensor(SENSOR_LIGHT); |
michael@0 | 450 | break; |
michael@0 | 451 | default: |
michael@0 | 452 | NS_WARNING("Disabling an unknown device sensor."); |
michael@0 | 453 | break; |
michael@0 | 454 | } |
michael@0 | 455 | } |
michael@0 | 456 | |
michael@0 | 457 | void |
michael@0 | 458 | EventListenerManager::RemoveEventListenerInternal( |
michael@0 | 459 | const EventListenerHolder& aListenerHolder, |
michael@0 | 460 | uint32_t aType, |
michael@0 | 461 | nsIAtom* aUserType, |
michael@0 | 462 | const nsAString& aTypeString, |
michael@0 | 463 | const EventListenerFlags& aFlags, |
michael@0 | 464 | bool aAllEvents) |
michael@0 | 465 | { |
michael@0 | 466 | if (!aListenerHolder || !aType || mClearingListeners) { |
michael@0 | 467 | return; |
michael@0 | 468 | } |
michael@0 | 469 | |
michael@0 | 470 | Listener* listener; |
michael@0 | 471 | |
michael@0 | 472 | uint32_t count = mListeners.Length(); |
michael@0 | 473 | uint32_t typeCount = 0; |
michael@0 | 474 | bool deviceType = IsDeviceType(aType); |
michael@0 | 475 | #ifdef MOZ_B2G |
michael@0 | 476 | bool timeChangeEvent = (aType == NS_MOZ_TIME_CHANGE_EVENT); |
michael@0 | 477 | bool networkEvent = (aType == NS_NETWORK_UPLOAD_EVENT || |
michael@0 | 478 | aType == NS_NETWORK_DOWNLOAD_EVENT); |
michael@0 | 479 | #endif // MOZ_B2G |
michael@0 | 480 | |
michael@0 | 481 | for (uint32_t i = 0; i < count; ++i) { |
michael@0 | 482 | listener = &mListeners.ElementAt(i); |
michael@0 | 483 | if (EVENT_TYPE_EQUALS(listener, aType, aUserType, aTypeString, |
michael@0 | 484 | aAllEvents)) { |
michael@0 | 485 | ++typeCount; |
michael@0 | 486 | if (listener->mListener == aListenerHolder && |
michael@0 | 487 | listener->mFlags.EqualsIgnoringTrustness(aFlags)) { |
michael@0 | 488 | nsRefPtr<EventListenerManager> kungFuDeathGrip(this); |
michael@0 | 489 | mListeners.RemoveElementAt(i); |
michael@0 | 490 | --count; |
michael@0 | 491 | mNoListenerForEvent = NS_EVENT_NULL; |
michael@0 | 492 | mNoListenerForEventAtom = nullptr; |
michael@0 | 493 | if (mTarget && aUserType) { |
michael@0 | 494 | mTarget->EventListenerRemoved(aUserType); |
michael@0 | 495 | } |
michael@0 | 496 | |
michael@0 | 497 | if (!deviceType |
michael@0 | 498 | #ifdef MOZ_B2G |
michael@0 | 499 | && !timeChangeEvent && !networkEvent |
michael@0 | 500 | #endif // MOZ_B2G |
michael@0 | 501 | ) { |
michael@0 | 502 | return; |
michael@0 | 503 | } |
michael@0 | 504 | --typeCount; |
michael@0 | 505 | } |
michael@0 | 506 | } |
michael@0 | 507 | } |
michael@0 | 508 | |
michael@0 | 509 | if (!aAllEvents && deviceType && typeCount == 0) { |
michael@0 | 510 | DisableDevice(aType); |
michael@0 | 511 | #ifdef MOZ_B2G |
michael@0 | 512 | } else if (timeChangeEvent && typeCount == 0) { |
michael@0 | 513 | nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); |
michael@0 | 514 | if (window) { |
michael@0 | 515 | window->DisableTimeChangeNotifications(); |
michael@0 | 516 | } |
michael@0 | 517 | } else if (!aAllEvents && networkEvent && typeCount == 0) { |
michael@0 | 518 | nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow(); |
michael@0 | 519 | if (window) { |
michael@0 | 520 | window->DisableNetworkEvent(aType); |
michael@0 | 521 | } |
michael@0 | 522 | #endif // MOZ_B2G |
michael@0 | 523 | } |
michael@0 | 524 | } |
michael@0 | 525 | |
michael@0 | 526 | bool |
michael@0 | 527 | EventListenerManager::ListenerCanHandle(Listener* aListener, |
michael@0 | 528 | WidgetEvent* aEvent) |
michael@0 | 529 | { |
michael@0 | 530 | // This is slightly different from EVENT_TYPE_EQUALS in that it returns |
michael@0 | 531 | // true even when aEvent->message == NS_USER_DEFINED_EVENT and |
michael@0 | 532 | // aListener=>mEventType != NS_USER_DEFINED_EVENT as long as the atoms are |
michael@0 | 533 | // the same |
michael@0 | 534 | if (aListener->mAllEvents) { |
michael@0 | 535 | return true; |
michael@0 | 536 | } |
michael@0 | 537 | if (aEvent->message == NS_USER_DEFINED_EVENT) { |
michael@0 | 538 | if (mIsMainThreadELM) { |
michael@0 | 539 | return aListener->mTypeAtom == aEvent->userType; |
michael@0 | 540 | } |
michael@0 | 541 | return aListener->mTypeString.Equals(aEvent->typeString); |
michael@0 | 542 | } |
michael@0 | 543 | MOZ_ASSERT(mIsMainThreadELM); |
michael@0 | 544 | return aListener->mEventType == aEvent->message; |
michael@0 | 545 | } |
michael@0 | 546 | |
michael@0 | 547 | void |
michael@0 | 548 | EventListenerManager::AddEventListenerByType( |
michael@0 | 549 | const EventListenerHolder& aListenerHolder, |
michael@0 | 550 | const nsAString& aType, |
michael@0 | 551 | const EventListenerFlags& aFlags) |
michael@0 | 552 | { |
michael@0 | 553 | nsCOMPtr<nsIAtom> atom = |
michael@0 | 554 | mIsMainThreadELM ? do_GetAtom(NS_LITERAL_STRING("on") + aType) : nullptr; |
michael@0 | 555 | uint32_t type = nsContentUtils::GetEventId(atom); |
michael@0 | 556 | AddEventListenerInternal(aListenerHolder, type, atom, aType, aFlags); |
michael@0 | 557 | } |
michael@0 | 558 | |
michael@0 | 559 | void |
michael@0 | 560 | EventListenerManager::RemoveEventListenerByType( |
michael@0 | 561 | const EventListenerHolder& aListenerHolder, |
michael@0 | 562 | const nsAString& aType, |
michael@0 | 563 | const EventListenerFlags& aFlags) |
michael@0 | 564 | { |
michael@0 | 565 | nsCOMPtr<nsIAtom> atom = |
michael@0 | 566 | mIsMainThreadELM ? do_GetAtom(NS_LITERAL_STRING("on") + aType) : nullptr; |
michael@0 | 567 | uint32_t type = nsContentUtils::GetEventId(atom); |
michael@0 | 568 | RemoveEventListenerInternal(aListenerHolder, type, atom, aType, aFlags); |
michael@0 | 569 | } |
michael@0 | 570 | |
michael@0 | 571 | EventListenerManager::Listener* |
michael@0 | 572 | EventListenerManager::FindEventHandler(uint32_t aEventType, |
michael@0 | 573 | nsIAtom* aTypeAtom, |
michael@0 | 574 | const nsAString& aTypeString) |
michael@0 | 575 | { |
michael@0 | 576 | // Run through the listeners for this type and see if a script |
michael@0 | 577 | // listener is registered |
michael@0 | 578 | Listener* listener; |
michael@0 | 579 | uint32_t count = mListeners.Length(); |
michael@0 | 580 | for (uint32_t i = 0; i < count; ++i) { |
michael@0 | 581 | listener = &mListeners.ElementAt(i); |
michael@0 | 582 | if (listener->mListenerIsHandler && |
michael@0 | 583 | EVENT_TYPE_EQUALS(listener, aEventType, aTypeAtom, aTypeString, |
michael@0 | 584 | false)) { |
michael@0 | 585 | return listener; |
michael@0 | 586 | } |
michael@0 | 587 | } |
michael@0 | 588 | return nullptr; |
michael@0 | 589 | } |
michael@0 | 590 | |
michael@0 | 591 | EventListenerManager::Listener* |
michael@0 | 592 | EventListenerManager::SetEventHandlerInternal( |
michael@0 | 593 | nsIAtom* aName, |
michael@0 | 594 | const nsAString& aTypeString, |
michael@0 | 595 | const TypedEventHandler& aTypedHandler, |
michael@0 | 596 | bool aPermitUntrustedEvents) |
michael@0 | 597 | { |
michael@0 | 598 | MOZ_ASSERT(aName || !aTypeString.IsEmpty()); |
michael@0 | 599 | |
michael@0 | 600 | uint32_t eventType = nsContentUtils::GetEventId(aName); |
michael@0 | 601 | Listener* listener = FindEventHandler(eventType, aName, aTypeString); |
michael@0 | 602 | |
michael@0 | 603 | if (!listener) { |
michael@0 | 604 | // If we didn't find a script listener or no listeners existed |
michael@0 | 605 | // create and add a new one. |
michael@0 | 606 | EventListenerFlags flags; |
michael@0 | 607 | flags.mListenerIsJSListener = true; |
michael@0 | 608 | |
michael@0 | 609 | nsCOMPtr<JSEventHandler> jsEventHandler; |
michael@0 | 610 | NS_NewJSEventHandler(mTarget, aName, |
michael@0 | 611 | aTypedHandler, getter_AddRefs(jsEventHandler)); |
michael@0 | 612 | EventListenerHolder listenerHolder(jsEventHandler); |
michael@0 | 613 | AddEventListenerInternal(listenerHolder, eventType, aName, aTypeString, |
michael@0 | 614 | flags, true); |
michael@0 | 615 | |
michael@0 | 616 | listener = FindEventHandler(eventType, aName, aTypeString); |
michael@0 | 617 | } else { |
michael@0 | 618 | JSEventHandler* jsEventHandler = listener->GetJSEventHandler(); |
michael@0 | 619 | MOZ_ASSERT(jsEventHandler, |
michael@0 | 620 | "How can we have an event handler with no JSEventHandler?"); |
michael@0 | 621 | |
michael@0 | 622 | bool same = jsEventHandler->GetTypedEventHandler() == aTypedHandler; |
michael@0 | 623 | // Possibly the same listener, but update still the context and scope. |
michael@0 | 624 | jsEventHandler->SetHandler(aTypedHandler); |
michael@0 | 625 | if (mTarget && !same && aName) { |
michael@0 | 626 | mTarget->EventListenerRemoved(aName); |
michael@0 | 627 | mTarget->EventListenerAdded(aName); |
michael@0 | 628 | } |
michael@0 | 629 | } |
michael@0 | 630 | |
michael@0 | 631 | // Set flag to indicate possible need for compilation later |
michael@0 | 632 | listener->mHandlerIsString = !aTypedHandler.HasEventHandler(); |
michael@0 | 633 | if (aPermitUntrustedEvents) { |
michael@0 | 634 | listener->mFlags.mAllowUntrustedEvents = true; |
michael@0 | 635 | } |
michael@0 | 636 | |
michael@0 | 637 | return listener; |
michael@0 | 638 | } |
michael@0 | 639 | |
michael@0 | 640 | nsresult |
michael@0 | 641 | EventListenerManager::SetEventHandler(nsIAtom* aName, |
michael@0 | 642 | const nsAString& aBody, |
michael@0 | 643 | uint32_t aLanguage, |
michael@0 | 644 | bool aDeferCompilation, |
michael@0 | 645 | bool aPermitUntrustedEvents, |
michael@0 | 646 | Element* aElement) |
michael@0 | 647 | { |
michael@0 | 648 | NS_PRECONDITION(aLanguage != nsIProgrammingLanguage::UNKNOWN, |
michael@0 | 649 | "Must know the language for the script event listener"); |
michael@0 | 650 | |
michael@0 | 651 | // |aPermitUntrustedEvents| is set to False for chrome - events |
michael@0 | 652 | // *generated* from an unknown source are not allowed. |
michael@0 | 653 | // However, for script languages with no 'sandbox', we want to reject |
michael@0 | 654 | // such scripts based on the source of their code, not just the source |
michael@0 | 655 | // of the event. |
michael@0 | 656 | if (aPermitUntrustedEvents && |
michael@0 | 657 | aLanguage != nsIProgrammingLanguage::JAVASCRIPT) { |
michael@0 | 658 | NS_WARNING("Discarding non-JS event listener from untrusted source"); |
michael@0 | 659 | return NS_ERROR_FAILURE; |
michael@0 | 660 | } |
michael@0 | 661 | |
michael@0 | 662 | nsCOMPtr<nsIDocument> doc; |
michael@0 | 663 | nsCOMPtr<nsIScriptGlobalObject> global = |
michael@0 | 664 | GetScriptGlobalAndDocument(getter_AddRefs(doc)); |
michael@0 | 665 | |
michael@0 | 666 | if (!global) { |
michael@0 | 667 | // This can happen; for example this document might have been |
michael@0 | 668 | // loaded as data. |
michael@0 | 669 | return NS_OK; |
michael@0 | 670 | } |
michael@0 | 671 | |
michael@0 | 672 | #ifdef DEBUG |
michael@0 | 673 | nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(global); |
michael@0 | 674 | if (win) { |
michael@0 | 675 | MOZ_ASSERT(win->IsInnerWindow(), "We should not have an outer window here!"); |
michael@0 | 676 | } |
michael@0 | 677 | #endif |
michael@0 | 678 | |
michael@0 | 679 | nsresult rv = NS_OK; |
michael@0 | 680 | // return early preventing the event listener from being added |
michael@0 | 681 | // 'doc' is fetched above |
michael@0 | 682 | if (doc) { |
michael@0 | 683 | // Don't allow adding an event listener if the document is sandboxed |
michael@0 | 684 | // without 'allow-scripts'. |
michael@0 | 685 | if (doc->GetSandboxFlags() & SANDBOXED_SCRIPTS) { |
michael@0 | 686 | return NS_ERROR_DOM_SECURITY_ERR; |
michael@0 | 687 | } |
michael@0 | 688 | |
michael@0 | 689 | nsCOMPtr<nsIContentSecurityPolicy> csp; |
michael@0 | 690 | rv = doc->NodePrincipal()->GetCsp(getter_AddRefs(csp)); |
michael@0 | 691 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 692 | |
michael@0 | 693 | if (csp) { |
michael@0 | 694 | bool inlineOK = true; |
michael@0 | 695 | bool reportViolations = false; |
michael@0 | 696 | rv = csp->GetAllowsInlineScript(&reportViolations, &inlineOK); |
michael@0 | 697 | NS_ENSURE_SUCCESS(rv, rv); |
michael@0 | 698 | |
michael@0 | 699 | if (reportViolations) { |
michael@0 | 700 | // gather information to log with violation report |
michael@0 | 701 | nsIURI* uri = doc->GetDocumentURI(); |
michael@0 | 702 | nsAutoCString asciiSpec; |
michael@0 | 703 | if (uri) |
michael@0 | 704 | uri->GetAsciiSpec(asciiSpec); |
michael@0 | 705 | nsAutoString scriptSample, attr, tagName(NS_LITERAL_STRING("UNKNOWN")); |
michael@0 | 706 | aName->ToString(attr); |
michael@0 | 707 | nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mTarget)); |
michael@0 | 708 | if (domNode) |
michael@0 | 709 | domNode->GetNodeName(tagName); |
michael@0 | 710 | // build a "script sample" based on what we know about this element |
michael@0 | 711 | scriptSample.Assign(attr); |
michael@0 | 712 | scriptSample.AppendLiteral(" attribute on "); |
michael@0 | 713 | scriptSample.Append(tagName); |
michael@0 | 714 | scriptSample.AppendLiteral(" element"); |
michael@0 | 715 | csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT, |
michael@0 | 716 | NS_ConvertUTF8toUTF16(asciiSpec), |
michael@0 | 717 | scriptSample, |
michael@0 | 718 | 0, |
michael@0 | 719 | EmptyString(), |
michael@0 | 720 | EmptyString()); |
michael@0 | 721 | } |
michael@0 | 722 | |
michael@0 | 723 | // return early if CSP wants us to block inline scripts |
michael@0 | 724 | if (!inlineOK) { |
michael@0 | 725 | return NS_OK; |
michael@0 | 726 | } |
michael@0 | 727 | } |
michael@0 | 728 | } |
michael@0 | 729 | |
michael@0 | 730 | // This might be the first reference to this language in the global |
michael@0 | 731 | // We must init the language before we attempt to fetch its context. |
michael@0 | 732 | if (NS_FAILED(global->EnsureScriptEnvironment())) { |
michael@0 | 733 | NS_WARNING("Failed to setup script environment for this language"); |
michael@0 | 734 | // but fall through and let the inevitable failure below handle it. |
michael@0 | 735 | } |
michael@0 | 736 | |
michael@0 | 737 | nsIScriptContext* context = global->GetScriptContext(); |
michael@0 | 738 | NS_ENSURE_TRUE(context, NS_ERROR_FAILURE); |
michael@0 | 739 | NS_ENSURE_STATE(global->GetGlobalJSObject()); |
michael@0 | 740 | |
michael@0 | 741 | Listener* listener = SetEventHandlerInternal(aName, |
michael@0 | 742 | EmptyString(), |
michael@0 | 743 | TypedEventHandler(), |
michael@0 | 744 | aPermitUntrustedEvents); |
michael@0 | 745 | |
michael@0 | 746 | if (!aDeferCompilation) { |
michael@0 | 747 | return CompileEventHandlerInternal(listener, &aBody, aElement); |
michael@0 | 748 | } |
michael@0 | 749 | |
michael@0 | 750 | return NS_OK; |
michael@0 | 751 | } |
michael@0 | 752 | |
michael@0 | 753 | void |
michael@0 | 754 | EventListenerManager::RemoveEventHandler(nsIAtom* aName, |
michael@0 | 755 | const nsAString& aTypeString) |
michael@0 | 756 | { |
michael@0 | 757 | if (mClearingListeners) { |
michael@0 | 758 | return; |
michael@0 | 759 | } |
michael@0 | 760 | |
michael@0 | 761 | uint32_t eventType = nsContentUtils::GetEventId(aName); |
michael@0 | 762 | Listener* listener = FindEventHandler(eventType, aName, aTypeString); |
michael@0 | 763 | |
michael@0 | 764 | if (listener) { |
michael@0 | 765 | mListeners.RemoveElementAt(uint32_t(listener - &mListeners.ElementAt(0))); |
michael@0 | 766 | mNoListenerForEvent = NS_EVENT_NULL; |
michael@0 | 767 | mNoListenerForEventAtom = nullptr; |
michael@0 | 768 | if (mTarget && aName) { |
michael@0 | 769 | mTarget->EventListenerRemoved(aName); |
michael@0 | 770 | } |
michael@0 | 771 | } |
michael@0 | 772 | } |
michael@0 | 773 | |
michael@0 | 774 | nsresult |
michael@0 | 775 | EventListenerManager::CompileEventHandlerInternal(Listener* aListener, |
michael@0 | 776 | const nsAString* aBody, |
michael@0 | 777 | Element* aElement) |
michael@0 | 778 | { |
michael@0 | 779 | MOZ_ASSERT(aListener->GetJSEventHandler()); |
michael@0 | 780 | MOZ_ASSERT(aListener->mHandlerIsString, "Why are we compiling a non-string JS listener?"); |
michael@0 | 781 | JSEventHandler* jsEventHandler = aListener->GetJSEventHandler(); |
michael@0 | 782 | MOZ_ASSERT(!jsEventHandler->GetTypedEventHandler().HasEventHandler(), |
michael@0 | 783 | "What is there to compile?"); |
michael@0 | 784 | |
michael@0 | 785 | nsresult result = NS_OK; |
michael@0 | 786 | nsCOMPtr<nsIDocument> doc; |
michael@0 | 787 | nsCOMPtr<nsIScriptGlobalObject> global = |
michael@0 | 788 | GetScriptGlobalAndDocument(getter_AddRefs(doc)); |
michael@0 | 789 | NS_ENSURE_STATE(global); |
michael@0 | 790 | |
michael@0 | 791 | nsIScriptContext* context = global->GetScriptContext(); |
michael@0 | 792 | NS_ENSURE_STATE(context); |
michael@0 | 793 | |
michael@0 | 794 | // Activate JSAPI, and make sure that exceptions are reported on the right |
michael@0 | 795 | // Window. |
michael@0 | 796 | AutoJSAPIWithErrorsReportedToWindow jsapi(context); |
michael@0 | 797 | JSContext* cx = jsapi.cx(); |
michael@0 | 798 | |
michael@0 | 799 | nsCOMPtr<nsIAtom> typeAtom = aListener->mTypeAtom; |
michael@0 | 800 | nsIAtom* attrName = typeAtom; |
michael@0 | 801 | |
michael@0 | 802 | // Flag us as not a string so we don't keep trying to compile strings which |
michael@0 | 803 | // can't be compiled. |
michael@0 | 804 | aListener->mHandlerIsString = false; |
michael@0 | 805 | |
michael@0 | 806 | // mTarget may not be an Element if it's a window and we're |
michael@0 | 807 | // getting an inline event listener forwarded from <html:body> or |
michael@0 | 808 | // <html:frameset> or <xul:window> or the like. |
michael@0 | 809 | // XXX I don't like that we have to reference content from |
michael@0 | 810 | // here. The alternative is to store the event handler string on |
michael@0 | 811 | // the JSEventHandler itself, and that still doesn't address |
michael@0 | 812 | // the arg names issue. |
michael@0 | 813 | nsCOMPtr<Element> element = do_QueryInterface(mTarget); |
michael@0 | 814 | MOZ_ASSERT(element || aBody, "Where will we get our body?"); |
michael@0 | 815 | nsAutoString handlerBody; |
michael@0 | 816 | const nsAString* body = aBody; |
michael@0 | 817 | if (!aBody) { |
michael@0 | 818 | if (aListener->mTypeAtom == nsGkAtoms::onSVGLoad) { |
michael@0 | 819 | attrName = nsGkAtoms::onload; |
michael@0 | 820 | } else if (aListener->mTypeAtom == nsGkAtoms::onSVGUnload) { |
michael@0 | 821 | attrName = nsGkAtoms::onunload; |
michael@0 | 822 | } else if (aListener->mTypeAtom == nsGkAtoms::onSVGResize) { |
michael@0 | 823 | attrName = nsGkAtoms::onresize; |
michael@0 | 824 | } else if (aListener->mTypeAtom == nsGkAtoms::onSVGScroll) { |
michael@0 | 825 | attrName = nsGkAtoms::onscroll; |
michael@0 | 826 | } else if (aListener->mTypeAtom == nsGkAtoms::onSVGZoom) { |
michael@0 | 827 | attrName = nsGkAtoms::onzoom; |
michael@0 | 828 | } else if (aListener->mTypeAtom == nsGkAtoms::onbeginEvent) { |
michael@0 | 829 | attrName = nsGkAtoms::onbegin; |
michael@0 | 830 | } else if (aListener->mTypeAtom == nsGkAtoms::onrepeatEvent) { |
michael@0 | 831 | attrName = nsGkAtoms::onrepeat; |
michael@0 | 832 | } else if (aListener->mTypeAtom == nsGkAtoms::onendEvent) { |
michael@0 | 833 | attrName = nsGkAtoms::onend; |
michael@0 | 834 | } |
michael@0 | 835 | |
michael@0 | 836 | element->GetAttr(kNameSpaceID_None, attrName, handlerBody); |
michael@0 | 837 | body = &handlerBody; |
michael@0 | 838 | aElement = element; |
michael@0 | 839 | } |
michael@0 | 840 | aListener = nullptr; |
michael@0 | 841 | |
michael@0 | 842 | uint32_t lineNo = 0; |
michael@0 | 843 | nsAutoCString url (NS_LITERAL_CSTRING("-moz-evil:lying-event-listener")); |
michael@0 | 844 | MOZ_ASSERT(body); |
michael@0 | 845 | MOZ_ASSERT(aElement); |
michael@0 | 846 | nsIURI *uri = aElement->OwnerDoc()->GetDocumentURI(); |
michael@0 | 847 | if (uri) { |
michael@0 | 848 | uri->GetSpec(url); |
michael@0 | 849 | lineNo = 1; |
michael@0 | 850 | } |
michael@0 | 851 | |
michael@0 | 852 | uint32_t argCount; |
michael@0 | 853 | const char **argNames; |
michael@0 | 854 | nsContentUtils::GetEventArgNames(aElement->GetNameSpaceID(), |
michael@0 | 855 | typeAtom, |
michael@0 | 856 | &argCount, &argNames); |
michael@0 | 857 | |
michael@0 | 858 | // Wrap the event target, so that we can use it as the scope for the event |
michael@0 | 859 | // handler. Note that mTarget is different from aElement in the <body> case, |
michael@0 | 860 | // where mTarget is a Window. |
michael@0 | 861 | // |
michael@0 | 862 | // The wrapScope doesn't really matter here, because the target will create |
michael@0 | 863 | // its reflector in the proper scope, and then we'll enter that compartment. |
michael@0 | 864 | JS::Rooted<JSObject*> wrapScope(cx, context->GetWindowProxy()); |
michael@0 | 865 | JS::Rooted<JS::Value> v(cx); |
michael@0 | 866 | { |
michael@0 | 867 | JSAutoCompartment ac(cx, wrapScope); |
michael@0 | 868 | nsresult rv = nsContentUtils::WrapNative(cx, mTarget, &v, |
michael@0 | 869 | /* aAllowWrapping = */ false); |
michael@0 | 870 | if (NS_WARN_IF(NS_FAILED(rv))) { |
michael@0 | 871 | return rv; |
michael@0 | 872 | } |
michael@0 | 873 | } |
michael@0 | 874 | JS::Rooted<JSObject*> target(cx, &v.toObject()); |
michael@0 | 875 | JSAutoCompartment ac(cx, target); |
michael@0 | 876 | |
michael@0 | 877 | nsDependentAtomString str(attrName); |
michael@0 | 878 | // Most of our names are short enough that we don't even have to malloc |
michael@0 | 879 | // the JS string stuff, so don't worry about playing games with |
michael@0 | 880 | // refcounting XPCOM stringbuffers. |
michael@0 | 881 | JS::Rooted<JSString*> jsStr(cx, JS_NewUCStringCopyN(cx, |
michael@0 | 882 | str.BeginReading(), |
michael@0 | 883 | str.Length())); |
michael@0 | 884 | NS_ENSURE_TRUE(jsStr, NS_ERROR_OUT_OF_MEMORY); |
michael@0 | 885 | |
michael@0 | 886 | // Get the reflector for |aElement|, so that we can pass to setElement. |
michael@0 | 887 | if (NS_WARN_IF(!WrapNewBindingObject(cx, target, aElement, &v))) { |
michael@0 | 888 | return NS_ERROR_FAILURE; |
michael@0 | 889 | } |
michael@0 | 890 | JS::CompileOptions options(cx); |
michael@0 | 891 | options.setIntroductionType("eventHandler") |
michael@0 | 892 | .setFileAndLine(url.get(), lineNo) |
michael@0 | 893 | .setVersion(SCRIPTVERSION_DEFAULT) |
michael@0 | 894 | .setElement(&v.toObject()) |
michael@0 | 895 | .setElementAttributeName(jsStr) |
michael@0 | 896 | .setDefineOnScope(false); |
michael@0 | 897 | |
michael@0 | 898 | JS::Rooted<JSObject*> handler(cx); |
michael@0 | 899 | result = nsJSUtils::CompileFunction(cx, target, options, |
michael@0 | 900 | nsAtomCString(typeAtom), |
michael@0 | 901 | argCount, argNames, *body, handler.address()); |
michael@0 | 902 | NS_ENSURE_SUCCESS(result, result); |
michael@0 | 903 | NS_ENSURE_TRUE(handler, NS_ERROR_FAILURE); |
michael@0 | 904 | |
michael@0 | 905 | nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(mTarget); |
michael@0 | 906 | if (jsEventHandler->EventName() == nsGkAtoms::onerror && win) { |
michael@0 | 907 | nsRefPtr<OnErrorEventHandlerNonNull> handlerCallback = |
michael@0 | 908 | new OnErrorEventHandlerNonNull(handler, /* aIncumbentGlobal = */ nullptr); |
michael@0 | 909 | jsEventHandler->SetHandler(handlerCallback); |
michael@0 | 910 | } else if (jsEventHandler->EventName() == nsGkAtoms::onbeforeunload && win) { |
michael@0 | 911 | nsRefPtr<OnBeforeUnloadEventHandlerNonNull> handlerCallback = |
michael@0 | 912 | new OnBeforeUnloadEventHandlerNonNull(handler, /* aIncumbentGlobal = */ nullptr); |
michael@0 | 913 | jsEventHandler->SetHandler(handlerCallback); |
michael@0 | 914 | } else { |
michael@0 | 915 | nsRefPtr<EventHandlerNonNull> handlerCallback = |
michael@0 | 916 | new EventHandlerNonNull(handler, /* aIncumbentGlobal = */ nullptr); |
michael@0 | 917 | jsEventHandler->SetHandler(handlerCallback); |
michael@0 | 918 | } |
michael@0 | 919 | |
michael@0 | 920 | return result; |
michael@0 | 921 | } |
michael@0 | 922 | |
michael@0 | 923 | nsresult |
michael@0 | 924 | EventListenerManager::HandleEventSubType(Listener* aListener, |
michael@0 | 925 | nsIDOMEvent* aDOMEvent, |
michael@0 | 926 | EventTarget* aCurrentTarget) |
michael@0 | 927 | { |
michael@0 | 928 | nsresult result = NS_OK; |
michael@0 | 929 | EventListenerHolder listenerHolder(aListener->mListener); // strong ref |
michael@0 | 930 | |
michael@0 | 931 | // If this is a script handler and we haven't yet |
michael@0 | 932 | // compiled the event handler itself |
michael@0 | 933 | if ((aListener->mListenerType == Listener::eJSEventListener) && |
michael@0 | 934 | aListener->mHandlerIsString) { |
michael@0 | 935 | result = CompileEventHandlerInternal(aListener, nullptr, nullptr); |
michael@0 | 936 | aListener = nullptr; |
michael@0 | 937 | } |
michael@0 | 938 | |
michael@0 | 939 | if (NS_SUCCEEDED(result)) { |
michael@0 | 940 | if (mIsMainThreadELM) { |
michael@0 | 941 | nsContentUtils::EnterMicroTask(); |
michael@0 | 942 | } |
michael@0 | 943 | // nsIDOMEvent::currentTarget is set in EventDispatcher. |
michael@0 | 944 | if (listenerHolder.HasWebIDLCallback()) { |
michael@0 | 945 | ErrorResult rv; |
michael@0 | 946 | listenerHolder.GetWebIDLCallback()-> |
michael@0 | 947 | HandleEvent(aCurrentTarget, *(aDOMEvent->InternalDOMEvent()), rv); |
michael@0 | 948 | result = rv.ErrorCode(); |
michael@0 | 949 | } else { |
michael@0 | 950 | result = listenerHolder.GetXPCOMCallback()->HandleEvent(aDOMEvent); |
michael@0 | 951 | } |
michael@0 | 952 | if (mIsMainThreadELM) { |
michael@0 | 953 | nsContentUtils::LeaveMicroTask(); |
michael@0 | 954 | } |
michael@0 | 955 | } |
michael@0 | 956 | |
michael@0 | 957 | return result; |
michael@0 | 958 | } |
michael@0 | 959 | |
michael@0 | 960 | /** |
michael@0 | 961 | * Causes a check for event listeners and processing by them if they exist. |
michael@0 | 962 | * @param an event listener |
michael@0 | 963 | */ |
michael@0 | 964 | |
michael@0 | 965 | void |
michael@0 | 966 | EventListenerManager::HandleEventInternal(nsPresContext* aPresContext, |
michael@0 | 967 | WidgetEvent* aEvent, |
michael@0 | 968 | nsIDOMEvent** aDOMEvent, |
michael@0 | 969 | EventTarget* aCurrentTarget, |
michael@0 | 970 | nsEventStatus* aEventStatus) |
michael@0 | 971 | { |
michael@0 | 972 | //Set the value of the internal PreventDefault flag properly based on aEventStatus |
michael@0 | 973 | if (*aEventStatus == nsEventStatus_eConsumeNoDefault) { |
michael@0 | 974 | aEvent->mFlags.mDefaultPrevented = true; |
michael@0 | 975 | } |
michael@0 | 976 | |
michael@0 | 977 | nsAutoTObserverArray<Listener, 2>::EndLimitedIterator iter(mListeners); |
michael@0 | 978 | Maybe<nsAutoPopupStatePusher> popupStatePusher; |
michael@0 | 979 | if (mIsMainThreadELM) { |
michael@0 | 980 | popupStatePusher.construct(Event::GetEventPopupControlState(aEvent)); |
michael@0 | 981 | } |
michael@0 | 982 | |
michael@0 | 983 | bool hasListener = false; |
michael@0 | 984 | while (iter.HasMore()) { |
michael@0 | 985 | if (aEvent->mFlags.mImmediatePropagationStopped) { |
michael@0 | 986 | break; |
michael@0 | 987 | } |
michael@0 | 988 | Listener* listener = &iter.GetNext(); |
michael@0 | 989 | // Check that the phase is same in event and event listener. |
michael@0 | 990 | // Handle only trusted events, except when listener permits untrusted events. |
michael@0 | 991 | if (ListenerCanHandle(listener, aEvent)) { |
michael@0 | 992 | hasListener = true; |
michael@0 | 993 | if (listener->IsListening(aEvent) && |
michael@0 | 994 | (aEvent->mFlags.mIsTrusted || |
michael@0 | 995 | listener->mFlags.mAllowUntrustedEvents)) { |
michael@0 | 996 | if (!*aDOMEvent) { |
michael@0 | 997 | // This is tiny bit slow, but happens only once per event. |
michael@0 | 998 | nsCOMPtr<EventTarget> et = |
michael@0 | 999 | do_QueryInterface(aEvent->originalTarget); |
michael@0 | 1000 | EventDispatcher::CreateEvent(et, aPresContext, |
michael@0 | 1001 | aEvent, EmptyString(), aDOMEvent); |
michael@0 | 1002 | } |
michael@0 | 1003 | if (*aDOMEvent) { |
michael@0 | 1004 | if (!aEvent->currentTarget) { |
michael@0 | 1005 | aEvent->currentTarget = aCurrentTarget->GetTargetForDOMEvent(); |
michael@0 | 1006 | if (!aEvent->currentTarget) { |
michael@0 | 1007 | break; |
michael@0 | 1008 | } |
michael@0 | 1009 | } |
michael@0 | 1010 | |
michael@0 | 1011 | if (NS_FAILED(HandleEventSubType(listener, *aDOMEvent, |
michael@0 | 1012 | aCurrentTarget))) { |
michael@0 | 1013 | aEvent->mFlags.mExceptionHasBeenRisen = true; |
michael@0 | 1014 | } |
michael@0 | 1015 | } |
michael@0 | 1016 | } |
michael@0 | 1017 | } |
michael@0 | 1018 | } |
michael@0 | 1019 | |
michael@0 | 1020 | aEvent->currentTarget = nullptr; |
michael@0 | 1021 | |
michael@0 | 1022 | if (mIsMainThreadELM && !hasListener) { |
michael@0 | 1023 | mNoListenerForEvent = aEvent->message; |
michael@0 | 1024 | mNoListenerForEventAtom = aEvent->userType; |
michael@0 | 1025 | } |
michael@0 | 1026 | |
michael@0 | 1027 | if (aEvent->mFlags.mDefaultPrevented) { |
michael@0 | 1028 | *aEventStatus = nsEventStatus_eConsumeNoDefault; |
michael@0 | 1029 | } |
michael@0 | 1030 | } |
michael@0 | 1031 | |
michael@0 | 1032 | void |
michael@0 | 1033 | EventListenerManager::Disconnect() |
michael@0 | 1034 | { |
michael@0 | 1035 | mTarget = nullptr; |
michael@0 | 1036 | RemoveAllListeners(); |
michael@0 | 1037 | } |
michael@0 | 1038 | |
michael@0 | 1039 | void |
michael@0 | 1040 | EventListenerManager::AddEventListener( |
michael@0 | 1041 | const nsAString& aType, |
michael@0 | 1042 | const EventListenerHolder& aListenerHolder, |
michael@0 | 1043 | bool aUseCapture, |
michael@0 | 1044 | bool aWantsUntrusted) |
michael@0 | 1045 | { |
michael@0 | 1046 | EventListenerFlags flags; |
michael@0 | 1047 | flags.mCapture = aUseCapture; |
michael@0 | 1048 | flags.mAllowUntrustedEvents = aWantsUntrusted; |
michael@0 | 1049 | return AddEventListenerByType(aListenerHolder, aType, flags); |
michael@0 | 1050 | } |
michael@0 | 1051 | |
michael@0 | 1052 | void |
michael@0 | 1053 | EventListenerManager::RemoveEventListener( |
michael@0 | 1054 | const nsAString& aType, |
michael@0 | 1055 | const EventListenerHolder& aListenerHolder, |
michael@0 | 1056 | bool aUseCapture) |
michael@0 | 1057 | { |
michael@0 | 1058 | EventListenerFlags flags; |
michael@0 | 1059 | flags.mCapture = aUseCapture; |
michael@0 | 1060 | RemoveEventListenerByType(aListenerHolder, aType, flags); |
michael@0 | 1061 | } |
michael@0 | 1062 | |
michael@0 | 1063 | void |
michael@0 | 1064 | EventListenerManager::AddListenerForAllEvents(nsIDOMEventListener* aDOMListener, |
michael@0 | 1065 | bool aUseCapture, |
michael@0 | 1066 | bool aWantsUntrusted, |
michael@0 | 1067 | bool aSystemEventGroup) |
michael@0 | 1068 | { |
michael@0 | 1069 | EventListenerFlags flags; |
michael@0 | 1070 | flags.mCapture = aUseCapture; |
michael@0 | 1071 | flags.mAllowUntrustedEvents = aWantsUntrusted; |
michael@0 | 1072 | flags.mInSystemGroup = aSystemEventGroup; |
michael@0 | 1073 | EventListenerHolder listenerHolder(aDOMListener); |
michael@0 | 1074 | AddEventListenerInternal(listenerHolder, NS_EVENT_ALL, nullptr, EmptyString(), |
michael@0 | 1075 | flags, false, true); |
michael@0 | 1076 | } |
michael@0 | 1077 | |
michael@0 | 1078 | void |
michael@0 | 1079 | EventListenerManager::RemoveListenerForAllEvents( |
michael@0 | 1080 | nsIDOMEventListener* aDOMListener, |
michael@0 | 1081 | bool aUseCapture, |
michael@0 | 1082 | bool aSystemEventGroup) |
michael@0 | 1083 | { |
michael@0 | 1084 | EventListenerFlags flags; |
michael@0 | 1085 | flags.mCapture = aUseCapture; |
michael@0 | 1086 | flags.mInSystemGroup = aSystemEventGroup; |
michael@0 | 1087 | EventListenerHolder listenerHolder(aDOMListener); |
michael@0 | 1088 | RemoveEventListenerInternal(listenerHolder, NS_EVENT_ALL, nullptr, |
michael@0 | 1089 | EmptyString(), flags, true); |
michael@0 | 1090 | } |
michael@0 | 1091 | |
michael@0 | 1092 | bool |
michael@0 | 1093 | EventListenerManager::HasMutationListeners() |
michael@0 | 1094 | { |
michael@0 | 1095 | if (mMayHaveMutationListeners) { |
michael@0 | 1096 | uint32_t count = mListeners.Length(); |
michael@0 | 1097 | for (uint32_t i = 0; i < count; ++i) { |
michael@0 | 1098 | Listener* listener = &mListeners.ElementAt(i); |
michael@0 | 1099 | if (listener->mEventType >= NS_MUTATION_START && |
michael@0 | 1100 | listener->mEventType <= NS_MUTATION_END) { |
michael@0 | 1101 | return true; |
michael@0 | 1102 | } |
michael@0 | 1103 | } |
michael@0 | 1104 | } |
michael@0 | 1105 | |
michael@0 | 1106 | return false; |
michael@0 | 1107 | } |
michael@0 | 1108 | |
michael@0 | 1109 | uint32_t |
michael@0 | 1110 | EventListenerManager::MutationListenerBits() |
michael@0 | 1111 | { |
michael@0 | 1112 | uint32_t bits = 0; |
michael@0 | 1113 | if (mMayHaveMutationListeners) { |
michael@0 | 1114 | uint32_t count = mListeners.Length(); |
michael@0 | 1115 | for (uint32_t i = 0; i < count; ++i) { |
michael@0 | 1116 | Listener* listener = &mListeners.ElementAt(i); |
michael@0 | 1117 | if (listener->mEventType >= NS_MUTATION_START && |
michael@0 | 1118 | listener->mEventType <= NS_MUTATION_END) { |
michael@0 | 1119 | if (listener->mEventType == NS_MUTATION_SUBTREEMODIFIED) { |
michael@0 | 1120 | return kAllMutationBits; |
michael@0 | 1121 | } |
michael@0 | 1122 | bits |= MutationBitForEventType(listener->mEventType); |
michael@0 | 1123 | } |
michael@0 | 1124 | } |
michael@0 | 1125 | } |
michael@0 | 1126 | return bits; |
michael@0 | 1127 | } |
michael@0 | 1128 | |
michael@0 | 1129 | bool |
michael@0 | 1130 | EventListenerManager::HasListenersFor(const nsAString& aEventName) |
michael@0 | 1131 | { |
michael@0 | 1132 | nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aEventName); |
michael@0 | 1133 | return HasListenersFor(atom); |
michael@0 | 1134 | } |
michael@0 | 1135 | |
michael@0 | 1136 | bool |
michael@0 | 1137 | EventListenerManager::HasListenersFor(nsIAtom* aEventNameWithOn) |
michael@0 | 1138 | { |
michael@0 | 1139 | #ifdef DEBUG |
michael@0 | 1140 | nsAutoString name; |
michael@0 | 1141 | aEventNameWithOn->ToString(name); |
michael@0 | 1142 | #endif |
michael@0 | 1143 | NS_ASSERTION(StringBeginsWith(name, NS_LITERAL_STRING("on")), |
michael@0 | 1144 | "Event name does not start with 'on'"); |
michael@0 | 1145 | uint32_t count = mListeners.Length(); |
michael@0 | 1146 | for (uint32_t i = 0; i < count; ++i) { |
michael@0 | 1147 | Listener* listener = &mListeners.ElementAt(i); |
michael@0 | 1148 | if (listener->mTypeAtom == aEventNameWithOn) { |
michael@0 | 1149 | return true; |
michael@0 | 1150 | } |
michael@0 | 1151 | } |
michael@0 | 1152 | return false; |
michael@0 | 1153 | } |
michael@0 | 1154 | |
michael@0 | 1155 | bool |
michael@0 | 1156 | EventListenerManager::HasListeners() |
michael@0 | 1157 | { |
michael@0 | 1158 | return !mListeners.IsEmpty(); |
michael@0 | 1159 | } |
michael@0 | 1160 | |
michael@0 | 1161 | nsresult |
michael@0 | 1162 | EventListenerManager::GetListenerInfo(nsCOMArray<nsIEventListenerInfo>* aList) |
michael@0 | 1163 | { |
michael@0 | 1164 | nsCOMPtr<EventTarget> target = do_QueryInterface(mTarget); |
michael@0 | 1165 | NS_ENSURE_STATE(target); |
michael@0 | 1166 | aList->Clear(); |
michael@0 | 1167 | uint32_t count = mListeners.Length(); |
michael@0 | 1168 | for (uint32_t i = 0; i < count; ++i) { |
michael@0 | 1169 | const Listener& listener = mListeners.ElementAt(i); |
michael@0 | 1170 | // If this is a script handler and we haven't yet |
michael@0 | 1171 | // compiled the event handler itself go ahead and compile it |
michael@0 | 1172 | if (listener.mListenerType == Listener::eJSEventListener && |
michael@0 | 1173 | listener.mHandlerIsString) { |
michael@0 | 1174 | CompileEventHandlerInternal(const_cast<Listener*>(&listener), nullptr, |
michael@0 | 1175 | nullptr); |
michael@0 | 1176 | } |
michael@0 | 1177 | nsAutoString eventType; |
michael@0 | 1178 | if (listener.mAllEvents) { |
michael@0 | 1179 | eventType.SetIsVoid(true); |
michael@0 | 1180 | } else { |
michael@0 | 1181 | eventType.Assign(Substring(nsDependentAtomString(listener.mTypeAtom), 2)); |
michael@0 | 1182 | } |
michael@0 | 1183 | // EventListenerInfo is defined in XPCOM, so we have to go ahead |
michael@0 | 1184 | // and convert to an XPCOM callback here... |
michael@0 | 1185 | nsRefPtr<EventListenerInfo> info = |
michael@0 | 1186 | new EventListenerInfo(eventType, listener.mListener.ToXPCOMCallback(), |
michael@0 | 1187 | listener.mFlags.mCapture, |
michael@0 | 1188 | listener.mFlags.mAllowUntrustedEvents, |
michael@0 | 1189 | listener.mFlags.mInSystemGroup); |
michael@0 | 1190 | aList->AppendObject(info); |
michael@0 | 1191 | } |
michael@0 | 1192 | return NS_OK; |
michael@0 | 1193 | } |
michael@0 | 1194 | |
michael@0 | 1195 | bool |
michael@0 | 1196 | EventListenerManager::HasUnloadListeners() |
michael@0 | 1197 | { |
michael@0 | 1198 | uint32_t count = mListeners.Length(); |
michael@0 | 1199 | for (uint32_t i = 0; i < count; ++i) { |
michael@0 | 1200 | Listener* listener = &mListeners.ElementAt(i); |
michael@0 | 1201 | if (listener->mEventType == NS_PAGE_UNLOAD || |
michael@0 | 1202 | listener->mEventType == NS_BEFORE_PAGE_UNLOAD) { |
michael@0 | 1203 | return true; |
michael@0 | 1204 | } |
michael@0 | 1205 | } |
michael@0 | 1206 | return false; |
michael@0 | 1207 | } |
michael@0 | 1208 | |
michael@0 | 1209 | void |
michael@0 | 1210 | EventListenerManager::SetEventHandler(nsIAtom* aEventName, |
michael@0 | 1211 | const nsAString& aTypeString, |
michael@0 | 1212 | EventHandlerNonNull* aHandler) |
michael@0 | 1213 | { |
michael@0 | 1214 | if (!aHandler) { |
michael@0 | 1215 | RemoveEventHandler(aEventName, aTypeString); |
michael@0 | 1216 | return; |
michael@0 | 1217 | } |
michael@0 | 1218 | |
michael@0 | 1219 | // Untrusted events are always permitted for non-chrome script |
michael@0 | 1220 | // handlers. |
michael@0 | 1221 | SetEventHandlerInternal(aEventName, aTypeString, TypedEventHandler(aHandler), |
michael@0 | 1222 | !mIsMainThreadELM || |
michael@0 | 1223 | !nsContentUtils::IsCallerChrome()); |
michael@0 | 1224 | } |
michael@0 | 1225 | |
michael@0 | 1226 | void |
michael@0 | 1227 | EventListenerManager::SetEventHandler(OnErrorEventHandlerNonNull* aHandler) |
michael@0 | 1228 | { |
michael@0 | 1229 | if (mIsMainThreadELM) { |
michael@0 | 1230 | if (!aHandler) { |
michael@0 | 1231 | RemoveEventHandler(nsGkAtoms::onerror, EmptyString()); |
michael@0 | 1232 | return; |
michael@0 | 1233 | } |
michael@0 | 1234 | |
michael@0 | 1235 | // Untrusted events are always permitted for non-chrome script |
michael@0 | 1236 | // handlers. |
michael@0 | 1237 | SetEventHandlerInternal(nsGkAtoms::onerror, EmptyString(), |
michael@0 | 1238 | TypedEventHandler(aHandler), |
michael@0 | 1239 | !nsContentUtils::IsCallerChrome()); |
michael@0 | 1240 | } else { |
michael@0 | 1241 | if (!aHandler) { |
michael@0 | 1242 | RemoveEventHandler(nullptr, NS_LITERAL_STRING("error")); |
michael@0 | 1243 | return; |
michael@0 | 1244 | } |
michael@0 | 1245 | |
michael@0 | 1246 | // Untrusted events are always permitted. |
michael@0 | 1247 | SetEventHandlerInternal(nullptr, NS_LITERAL_STRING("error"), |
michael@0 | 1248 | TypedEventHandler(aHandler), true); |
michael@0 | 1249 | } |
michael@0 | 1250 | } |
michael@0 | 1251 | |
michael@0 | 1252 | void |
michael@0 | 1253 | EventListenerManager::SetEventHandler( |
michael@0 | 1254 | OnBeforeUnloadEventHandlerNonNull* aHandler) |
michael@0 | 1255 | { |
michael@0 | 1256 | if (!aHandler) { |
michael@0 | 1257 | RemoveEventHandler(nsGkAtoms::onbeforeunload, EmptyString()); |
michael@0 | 1258 | return; |
michael@0 | 1259 | } |
michael@0 | 1260 | |
michael@0 | 1261 | // Untrusted events are always permitted for non-chrome script |
michael@0 | 1262 | // handlers. |
michael@0 | 1263 | SetEventHandlerInternal(nsGkAtoms::onbeforeunload, EmptyString(), |
michael@0 | 1264 | TypedEventHandler(aHandler), |
michael@0 | 1265 | !mIsMainThreadELM || |
michael@0 | 1266 | !nsContentUtils::IsCallerChrome()); |
michael@0 | 1267 | } |
michael@0 | 1268 | |
michael@0 | 1269 | const TypedEventHandler* |
michael@0 | 1270 | EventListenerManager::GetTypedEventHandler(nsIAtom* aEventName, |
michael@0 | 1271 | const nsAString& aTypeString) |
michael@0 | 1272 | { |
michael@0 | 1273 | uint32_t eventType = nsContentUtils::GetEventId(aEventName); |
michael@0 | 1274 | Listener* listener = FindEventHandler(eventType, aEventName, aTypeString); |
michael@0 | 1275 | |
michael@0 | 1276 | if (!listener) { |
michael@0 | 1277 | return nullptr; |
michael@0 | 1278 | } |
michael@0 | 1279 | |
michael@0 | 1280 | JSEventHandler* jsEventHandler = listener->GetJSEventHandler(); |
michael@0 | 1281 | |
michael@0 | 1282 | if (listener->mHandlerIsString) { |
michael@0 | 1283 | CompileEventHandlerInternal(listener, nullptr, nullptr); |
michael@0 | 1284 | } |
michael@0 | 1285 | |
michael@0 | 1286 | const TypedEventHandler& typedHandler = |
michael@0 | 1287 | jsEventHandler->GetTypedEventHandler(); |
michael@0 | 1288 | return typedHandler.HasEventHandler() ? &typedHandler : nullptr; |
michael@0 | 1289 | } |
michael@0 | 1290 | |
michael@0 | 1291 | size_t |
michael@0 | 1292 | EventListenerManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const |
michael@0 | 1293 | { |
michael@0 | 1294 | size_t n = aMallocSizeOf(this); |
michael@0 | 1295 | n += mListeners.SizeOfExcludingThis(aMallocSizeOf); |
michael@0 | 1296 | uint32_t count = mListeners.Length(); |
michael@0 | 1297 | for (uint32_t i = 0; i < count; ++i) { |
michael@0 | 1298 | JSEventHandler* jsEventHandler = |
michael@0 | 1299 | mListeners.ElementAt(i).GetJSEventHandler(); |
michael@0 | 1300 | if (jsEventHandler) { |
michael@0 | 1301 | n += jsEventHandler->SizeOfIncludingThis(aMallocSizeOf); |
michael@0 | 1302 | } |
michael@0 | 1303 | } |
michael@0 | 1304 | return n; |
michael@0 | 1305 | } |
michael@0 | 1306 | |
michael@0 | 1307 | void |
michael@0 | 1308 | EventListenerManager::MarkForCC() |
michael@0 | 1309 | { |
michael@0 | 1310 | uint32_t count = mListeners.Length(); |
michael@0 | 1311 | for (uint32_t i = 0; i < count; ++i) { |
michael@0 | 1312 | const Listener& listener = mListeners.ElementAt(i); |
michael@0 | 1313 | JSEventHandler* jsEventHandler = listener.GetJSEventHandler(); |
michael@0 | 1314 | if (jsEventHandler) { |
michael@0 | 1315 | const TypedEventHandler& typedHandler = |
michael@0 | 1316 | jsEventHandler->GetTypedEventHandler(); |
michael@0 | 1317 | if (typedHandler.HasEventHandler()) { |
michael@0 | 1318 | JS::ExposeObjectToActiveJS(typedHandler.Ptr()->Callable()); |
michael@0 | 1319 | } |
michael@0 | 1320 | } else if (listener.mListenerType == Listener::eWrappedJSListener) { |
michael@0 | 1321 | xpc_TryUnmarkWrappedGrayObject(listener.mListener.GetXPCOMCallback()); |
michael@0 | 1322 | } else if (listener.mListenerType == Listener::eWebIDLListener) { |
michael@0 | 1323 | // Callback() unmarks gray |
michael@0 | 1324 | listener.mListener.GetWebIDLCallback()->Callback(); |
michael@0 | 1325 | } |
michael@0 | 1326 | } |
michael@0 | 1327 | if (mRefCnt.IsPurple()) { |
michael@0 | 1328 | mRefCnt.RemovePurple(); |
michael@0 | 1329 | } |
michael@0 | 1330 | } |
michael@0 | 1331 | |
michael@0 | 1332 | already_AddRefed<nsIScriptGlobalObject> |
michael@0 | 1333 | EventListenerManager::GetScriptGlobalAndDocument(nsIDocument** aDoc) |
michael@0 | 1334 | { |
michael@0 | 1335 | nsCOMPtr<nsINode> node(do_QueryInterface(mTarget)); |
michael@0 | 1336 | nsCOMPtr<nsIDocument> doc; |
michael@0 | 1337 | nsCOMPtr<nsIScriptGlobalObject> global; |
michael@0 | 1338 | if (node) { |
michael@0 | 1339 | // Try to get context from doc |
michael@0 | 1340 | // XXX sXBL/XBL2 issue -- do we really want the owner here? What |
michael@0 | 1341 | // if that's the XBL document? |
michael@0 | 1342 | doc = node->OwnerDoc(); |
michael@0 | 1343 | if (doc->IsLoadedAsData()) { |
michael@0 | 1344 | return nullptr; |
michael@0 | 1345 | } |
michael@0 | 1346 | |
michael@0 | 1347 | // We want to allow compiling an event handler even in an unloaded |
michael@0 | 1348 | // document, so use GetScopeObject here, not GetScriptHandlingObject. |
michael@0 | 1349 | global = do_QueryInterface(doc->GetScopeObject()); |
michael@0 | 1350 | } else { |
michael@0 | 1351 | nsCOMPtr<nsPIDOMWindow> win = GetTargetAsInnerWindow(); |
michael@0 | 1352 | if (win) { |
michael@0 | 1353 | doc = win->GetExtantDoc(); |
michael@0 | 1354 | global = do_QueryInterface(win); |
michael@0 | 1355 | } else { |
michael@0 | 1356 | global = do_QueryInterface(mTarget); |
michael@0 | 1357 | } |
michael@0 | 1358 | } |
michael@0 | 1359 | |
michael@0 | 1360 | doc.forget(aDoc); |
michael@0 | 1361 | return global.forget(); |
michael@0 | 1362 | } |
michael@0 | 1363 | |
michael@0 | 1364 | } // namespace mozilla |