diff --git a/dom/workers/Events.cpp b/dom/workers/Events.cpp index d2884a88ad3..a28a1df91aa 100644 --- a/dom/workers/Events.cpp +++ b/dom/workers/Events.cpp @@ -75,6 +75,7 @@ class Event : public PrivatizableBase protected: bool mStopPropagationCalled; + bool mStopImmediatePropagationCalled; public: static bool @@ -168,9 +169,16 @@ public: return JSVAL_TO_BOOLEAN(canceled); } + static bool + ImmediatePropagationStopped(JSContext* aCx, JSObject* aEvent) + { + Event* event = GetPrivate(aCx, aEvent); + return event ? event->mStopImmediatePropagationCalled : false; + } + protected: Event() - : mStopPropagationCalled(false) + : mStopPropagationCalled(false), mStopImmediatePropagationCalled(false) { MOZ_COUNT_CTOR(mozilla::dom::workers::Event); } @@ -230,6 +238,7 @@ protected: bool aIsTrusted) { aEvent->mStopPropagationCalled = false; + aEvent->mStopImmediatePropagationCalled = false; jsval now; if (!JS_NewNumberValue(aCx, JS_Now(), &now)) { @@ -319,6 +328,21 @@ private: return true; } + static JSBool + StopImmediatePropagation(JSContext* aCx, uintN aArgc, jsval* aVp) + { + JSObject* obj = JS_THIS_OBJECT(aCx, aVp); + + Event* event = GetInstancePrivate(aCx, obj, sFunctions[3].name); + if (!event) { + return false; + } + + event->mStopImmediatePropagationCalled = true; + + return true; + } + static JSBool PreventDefault(JSContext* aCx, uintN aArgc, jsval* aVp) { @@ -399,6 +423,7 @@ JSFunctionSpec Event::sFunctions[] = { JS_FN("stopPropagation", StopPropagation, 0, FUNCTION_FLAGS), JS_FN("preventDefault", PreventDefault, 0, FUNCTION_FLAGS), JS_FN("initEvent", InitEvent, 3, FUNCTION_FLAGS), + JS_FN("stopImmediatePropagation", StopImmediatePropagation, 0, FUNCTION_FLAGS), JS_FS_END }; @@ -1115,6 +1140,12 @@ EventWasCanceled(JSContext* aCx, JSObject* aEvent) return Event::WasCanceled(aCx, aEvent); } +bool +EventImmediatePropagationStopped(JSContext* aCx, JSObject* aEvent) +{ + return Event::ImmediatePropagationStopped(aCx, aEvent); +} + bool DispatchEventToTarget(JSContext* aCx, JSObject* aTarget, JSObject* aEvent, bool* aPreventDefaultCalled) diff --git a/dom/workers/Events.h b/dom/workers/Events.h index cb5ad2a6bb6..f71e6e50f91 100644 --- a/dom/workers/Events.h +++ b/dom/workers/Events.h @@ -80,6 +80,9 @@ SetEventTarget(JSContext* aCx, JSObject* aEvent, JSObject* aTarget); bool EventWasCanceled(JSContext* aCx, JSObject* aEvent); +bool +EventImmediatePropagationStopped(JSContext* aCx, JSObject* aEvent); + bool DispatchEventToTarget(JSContext* aCx, JSObject* aTarget, JSObject* aEvent, bool* aPreventDefaultCalled); diff --git a/dom/workers/ListenerManager.cpp b/dom/workers/ListenerManager.cpp index 3f7da2bb573..79ee053ab10 100644 --- a/dom/workers/ListenerManager.cpp +++ b/dom/workers/ListenerManager.cpp @@ -392,6 +392,10 @@ ListenerManager::DispatchEvent(JSContext* aCx, JSObject* aTarget, } for (size_t index = 0; index < listeners.length(); index++) { + if (events::EventImmediatePropagationStopped(aCx, aEvent)) { + break; + } + // If anything fails in here we want to report the exception and continue on // to the next listener rather than bailing out. If something fails and // does not set an exception then we bail out entirely as we've either run diff --git a/dom/workers/test/eventDispatch_worker.js b/dom/workers/test/eventDispatch_worker.js index 586eeb95e65..1b633f67248 100644 --- a/dom/workers/test/eventDispatch_worker.js +++ b/dom/workers/test/eventDispatch_worker.js @@ -22,9 +22,14 @@ addEventListener(fakeEventType, function(event) { if (event.isTrusted) { throw new Error("Event should be untrusted!"); } + event.stopImmediatePropagation(); postMessage(event.data); }, false, true); +addEventListener(fakeEventType, function(event) { + throw new Error("This shouldn't get called because of stopImmediatePropagation."); +}, false, true); + var count = 0; onmessage = function(event) { if (event.target !== self || event.currentTarget !== self) {