Title: [201730] trunk
Revision
201730
Author
[email protected]
Date
2016-06-06 15:28:25 -0700 (Mon, 06 Jun 2016)

Log Message

Implement EventListenerOptions argument to addEventListener
https://bugs.webkit.org/show_bug.cgi?id=149466
<rdar://problem/22802031>

Reviewed by Dean Jackson.

LayoutTests/imported/w3c:

Import new test from W3C that covers EventListenerOptions.

* web-platform-tests/dom/events/EventListenerOptions-capture-expected.txt: Added.
* web-platform-tests/dom/events/EventListenerOptions-capture.html: Added.

Source/WebCore:

Implement AddEventListenerOptions dictionary argument to addEventListener()
and EventListenerOptions dictionary argument to removeEventListener(), as
per the latest DOM specification:
- https://dom.spec.whatwg.org/#interface-eventtarget

Firefox and Chrome already support this.

Support for AddEventListenerOptions in this patch is as follows:
- 'capture': fully supported.
- 'once': fully supported.
- 'passive': supported in the sense that preventDefault() will be ignored
             for passive event listeners. There are however currently no
             performance benefits from passing this flag. Those optimizations
             will be implemented in follow-up patches (in particular for
             Touch and Scroll events).

Tests: fast/events/AddEventListenerOptions-once-recursive.html
       fast/events/AddEventListenerOptions-once.html
       fast/events/AddEventListenerOptions-passive.html
       fast/events/removeEventListener-EventListenerOptions-capture.html
       imported/w3c/web-platform-tests/dom/events/EventListenerOptions-capture.html

* Modules/webaudio/AudioScheduledSourceNode.cpp:
(WebCore::AudioScheduledSourceNode::addEventListener):
(WebCore::AudioScheduledSourceNode::removeEventListener):
* Modules/webaudio/AudioScheduledSourceNode.h:
* Modules/webaudio/ScriptProcessorNode.cpp:
(WebCore::ScriptProcessorNode::addEventListener):
(WebCore::ScriptProcessorNode::removeEventListener):
* Modules/webaudio/ScriptProcessorNode.h:
* bindings/scripts/CodeGeneratorJS.pm:
(GenerateParametersCheckExpression):
* dom/Event.h:
(WebCore::Event::preventDefault):
(WebCore::Event::setInPassiveListener):
* dom/EventListenerMap.cpp:
(WebCore::addListenerToVector):
(WebCore::EventListenerMap::add):
* dom/EventListenerMap.h:
* dom/EventTarget.cpp:
(WebCore::EventTarget::addEventListener):
(WebCore::EventTarget::addEventListenerForBindings):
(WebCore::EventTarget::removeEventListenerForBindings):
(WebCore::EventTarget::removeEventListener):
(WebCore::EventTarget::setAttributeEventListener):
(WebCore::EventTarget::fireEventListeners):
* dom/EventTarget.h:
(WebCore::EventTarget::ListenerOptions::ListenerOptions):
(WebCore::EventTarget::AddEventListenerOptions::AddEventListenerOptions):
(WebCore::EventTarget::addEventListener):
(WebCore::EventTarget::addEventListenerForBindings):
(WebCore::EventTarget::removeEventListenerForBindings):
* dom/EventTarget.idl:
* dom/MessagePort.cpp:
(WebCore::MessagePort::addEventListener):
* dom/MessagePort.h:
* dom/Node.cpp:
(WebCore::tryAddEventListener):
(WebCore::Node::addEventListener):
(WebCore::tryRemoveEventListener):
(WebCore::Node::removeEventListener):
* dom/Node.h:
* dom/RegisteredEventListener.h:
(WebCore::RegisteredEventListener::Options::Options):
(WebCore::RegisteredEventListener::RegisteredEventListener):
(WebCore::operator==):
* html/HTMLMediaElement.cpp:
(WebCore::HTMLMediaElement::addEventListener):
(WebCore::HTMLMediaElement::removeEventListener):
* html/HTMLMediaElement.h:
* page/DOMWindow.cpp:
(WebCore::DOMWindow::addEventListener):
(WebCore::DOMWindow::removeEventListener):
* page/DOMWindow.h:
* svg/SVGElement.cpp:
(WebCore::SVGElement::addEventListener):
(WebCore::SVGElement::removeEventListener):
* svg/SVGElement.h:

LayoutTests:

* fast/events/AddEventListenerOptions-once-expected.txt: Added.
* fast/events/AddEventListenerOptions-once-recursive-expected.txt: Added.
* fast/events/AddEventListenerOptions-once-recursive.html: Added.
* fast/events/AddEventListenerOptions-once.html: Added.
* fast/events/AddEventListenerOptions-passive-expected.txt: Added.
* fast/events/AddEventListenerOptions-passive.html: Added.
* fast/events/removeEventListener-EventListenerOptions-capture-expected.txt: Added.
* fast/events/removeEventListener-EventListenerOptions-capture.html: Added.

Add layout testing coverage for various aspects of the functionality.

* imported/blink/fast/events/eventlisteneroptions/capture_default-expected.txt: Added.
* imported/blink/fast/events/eventlisteneroptions/capture_default.html: Added.
* imported/blink/fast/events/eventlisteneroptions/capture_equality-expected.txt: Added.
* imported/blink/fast/events/eventlisteneroptions/capture_equality.html: Added.
* imported/blink/fast/events/eventlisteneroptions/capture_query-expected.txt: Added.
* imported/blink/fast/events/eventlisteneroptions/capture_query.html: Added.
* imported/blink/fast/events/eventlisteneroptions/passive_dispatch-expected.txt: Added.
* imported/blink/fast/events/eventlisteneroptions/passive_dispatch.html: Added.
* imported/blink/fast/events/eventlisteneroptions/passive_inequality-expected.txt: Added.
* imported/blink/fast/events/eventlisteneroptions/passive_inequality.html: Added.
* imported/blink/fast/events/eventlisteneroptions/passive_query-expected.txt: Added.
* imported/blink/fast/events/eventlisteneroptions/passive_query.html: Added.

Import blink tests for this functionality.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (201729 => 201730)


--- trunk/LayoutTests/ChangeLog	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/LayoutTests/ChangeLog	2016-06-06 22:28:25 UTC (rev 201730)
@@ -1,3 +1,37 @@
+2016-06-06  Chris Dumez  <[email protected]>
+
+        Implement EventListenerOptions argument to addEventListener
+        https://bugs.webkit.org/show_bug.cgi?id=149466
+        <rdar://problem/22802031>
+
+        Reviewed by Dean Jackson.
+
+        * fast/events/AddEventListenerOptions-once-expected.txt: Added.
+        * fast/events/AddEventListenerOptions-once-recursive-expected.txt: Added.
+        * fast/events/AddEventListenerOptions-once-recursive.html: Added.
+        * fast/events/AddEventListenerOptions-once.html: Added.
+        * fast/events/AddEventListenerOptions-passive-expected.txt: Added.
+        * fast/events/AddEventListenerOptions-passive.html: Added.
+        * fast/events/removeEventListener-EventListenerOptions-capture-expected.txt: Added.
+        * fast/events/removeEventListener-EventListenerOptions-capture.html: Added.
+
+        Add layout testing coverage for various aspects of the functionality.
+
+        * imported/blink/fast/events/eventlisteneroptions/capture_default-expected.txt: Added.
+        * imported/blink/fast/events/eventlisteneroptions/capture_default.html: Added.
+        * imported/blink/fast/events/eventlisteneroptions/capture_equality-expected.txt: Added.
+        * imported/blink/fast/events/eventlisteneroptions/capture_equality.html: Added.
+        * imported/blink/fast/events/eventlisteneroptions/capture_query-expected.txt: Added.
+        * imported/blink/fast/events/eventlisteneroptions/capture_query.html: Added.
+        * imported/blink/fast/events/eventlisteneroptions/passive_dispatch-expected.txt: Added.
+        * imported/blink/fast/events/eventlisteneroptions/passive_dispatch.html: Added.
+        * imported/blink/fast/events/eventlisteneroptions/passive_inequality-expected.txt: Added.
+        * imported/blink/fast/events/eventlisteneroptions/passive_inequality.html: Added.
+        * imported/blink/fast/events/eventlisteneroptions/passive_query-expected.txt: Added.
+        * imported/blink/fast/events/eventlisteneroptions/passive_query.html: Added.
+
+        Import blink tests for this functionality.
+
 2016-06-06  Adam Bergkvist  <[email protected]>
 
         WebRTC: Update MediaEndpointPeerConnection::createOffer() to use the transceiver set

Added: trunk/LayoutTests/fast/events/AddEventListenerOptions-once-expected.txt (0 => 201730)


--- trunk/LayoutTests/fast/events/AddEventListenerOptions-once-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/events/AddEventListenerOptions-once-expected.txt	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,15 @@
+Tests support for 'once' member in AddEventListenerOptions.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+document.body.addEventListener('test', listenerFunction, { 'once': true })
+PASS listenerCallCount is 0
+document.body.dispatchEvent(new Event('test'))
+PASS listenerCallCount is 1
+document.body.dispatchEvent(new Event('test'))
+PASS listenerCallCount is 1
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/events/AddEventListenerOptions-once-recursive-expected.txt (0 => 201730)


--- trunk/LayoutTests/fast/events/AddEventListenerOptions-once-recursive-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/events/AddEventListenerOptions-once-recursive-expected.txt	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,15 @@
+Tests support for 'once' member in AddEventListenerOptions.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+document.body.addEventListener('test', listenerFunction, { 'once': true })
+PASS listenerCallCount is 0
+document.body.dispatchEvent(new Event('test'))
+PASS listenerCallCount is 1
+document.body.dispatchEvent(new Event('test'))
+PASS listenerCallCount is 1
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/events/AddEventListenerOptions-once-recursive.html (0 => 201730)


--- trunk/LayoutTests/fast/events/AddEventListenerOptions-once-recursive.html	                        (rev 0)
+++ trunk/LayoutTests/fast/events/AddEventListenerOptions-once-recursive.html	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<script>
+description("Tests support for 'once' member in AddEventListenerOptions.");
+
+var listenerCallCount = 0;
+function listenerFunction()
+{
+    ++listenerCallCount;
+
+    if (listenerCallCount == 1)
+        document.body.dispatchEvent(new Event('test'));
+}
+
+evalAndLog("document.body.addEventListener('test', listenerFunction, { 'once': true })");
+shouldBe("listenerCallCount", "0");
+evalAndLog("document.body.dispatchEvent(new Event('test'))");
+shouldBe("listenerCallCount", "1");
+evalAndLog("document.body.dispatchEvent(new Event('test'))");
+shouldBe("listenerCallCount", "1");
+
+</script>
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/events/AddEventListenerOptions-once.html (0 => 201730)


--- trunk/LayoutTests/fast/events/AddEventListenerOptions-once.html	                        (rev 0)
+++ trunk/LayoutTests/fast/events/AddEventListenerOptions-once.html	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<script>
+description("Tests support for 'once' member in AddEventListenerOptions.");
+
+var listenerCallCount = 0;
+function listenerFunction()
+{
+    ++listenerCallCount;
+}
+
+evalAndLog("document.body.addEventListener('test', listenerFunction, { 'once': true })");
+shouldBe("listenerCallCount", "0");
+evalAndLog("document.body.dispatchEvent(new Event('test'))");
+shouldBe("listenerCallCount", "1");
+evalAndLog("document.body.dispatchEvent(new Event('test'))");
+shouldBe("listenerCallCount", "1");
+
+</script>
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/events/AddEventListenerOptions-passive-expected.txt (0 => 201730)


--- trunk/LayoutTests/fast/events/AddEventListenerOptions-passive-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/events/AddEventListenerOptions-passive-expected.txt	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,23 @@
+Tests support for 'passive' member in AddEventListenerOptions.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+document.body.addEventListener('test', passiveListenerFunction, { 'passive': true })
+document.body.dispatchEvent(testEvent1)
+PASS listenerCallCount is 1
+PASS testEvent1.defaultPrevented is false
+
+document.body.addEventListener('test', activeListenerFunction, { })
+document.body.dispatchEvent(testEvent2)
+PASS listenerCallCount is 2
+PASS testEvent2.defaultPrevented is true
+
+document.body.addEventListener('test', activeListenerFunction, { 'passive': false })
+document.body.dispatchEvent(testEvent2)
+PASS listenerCallCount is 2
+PASS testEvent2.defaultPrevented is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/events/AddEventListenerOptions-passive.html (0 => 201730)


--- trunk/LayoutTests/fast/events/AddEventListenerOptions-passive.html	                        (rev 0)
+++ trunk/LayoutTests/fast/events/AddEventListenerOptions-passive.html	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<script>
+description("Tests support for 'passive' member in AddEventListenerOptions.");
+
+var listenerCallCount = 0;
+function passiveListenerFunction(ev)
+{
+    ++listenerCallCount;
+    ev.preventDefault();
+}
+
+function activeListenerFunction(ev)
+{
+    ++listenerCallCount;
+    ev.preventDefault();
+}
+
+evalAndLog("document.body.addEventListener('test', passiveListenerFunction, { 'passive': true })");
+var testEvent1 = new Event('test', { 'cancelable': true });
+evalAndLog("document.body.dispatchEvent(testEvent1)");
+shouldBe("listenerCallCount", "1");
+shouldBeFalse("testEvent1.defaultPrevented");
+
+debug("");
+listenerCallCount = 0;
+// 'passive should be false by default.
+evalAndLog("document.body.addEventListener('test', activeListenerFunction, { })");
+var testEvent2 = new Event('test', { 'cancelable': true });
+evalAndLog("document.body.dispatchEvent(testEvent2)");
+shouldBe("listenerCallCount", "2");
+shouldBeTrue("testEvent2.defaultPrevented");
+document.body.removeEventListener('test', activeListenerFunction);
+
+debug("");
+listenerCallCount = 0;
+evalAndLog("document.body.addEventListener('test', activeListenerFunction, { 'passive': false })");
+var testEvent2 = new Event('test', { 'cancelable': true });
+evalAndLog("document.body.dispatchEvent(testEvent2)");
+shouldBe("listenerCallCount", "2");
+shouldBeTrue("testEvent2.defaultPrevented");
+
+</script>
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/events/removeEventListener-EventListenerOptions-capture-expected.txt (0 => 201730)


--- trunk/LayoutTests/fast/events/removeEventListener-EventListenerOptions-capture-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/events/removeEventListener-EventListenerOptions-capture-expected.txt	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,50 @@
+Tests support for calling removeEventListener() with an EventListenerOptions dictionary
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS testAddThenRemove(undefined, false) is true
+PASS testAddThenRemove(undefined, { 'capture': false }) is true
+PASS testAddThenRemove(undefined, { }) is true
+PASS testAddThenRemove(undefined, undefined) is true
+PASS testAddThenRemove(undefined, true) is false
+PASS testAddThenRemove(undefined, { 'capture': true }) is false
+
+PASS testAddThenRemove(false, false) is true
+PASS testAddThenRemove(false, { 'capture': false }) is true
+PASS testAddThenRemove(false, { }) is true
+PASS testAddThenRemove(false, undefined) is true
+PASS testAddThenRemove(false, true) is false
+PASS testAddThenRemove(false, { 'capture': true }) is false
+
+PASS testAddThenRemove({ 'capture': false }, false) is true
+PASS testAddThenRemove({ 'capture': false }, { 'capture': false }) is true
+PASS testAddThenRemove({ 'capture': false }, { }) is true
+PASS testAddThenRemove({ 'capture': false }, undefined) is true
+PASS testAddThenRemove({ 'capture': false }, true) is false
+PASS testAddThenRemove({ 'capture': false }, { 'capture': true }) is false
+
+PASS testAddThenRemove({ }, false) is true
+PASS testAddThenRemove({ }, { 'capture': false }) is true
+PASS testAddThenRemove({ }, { }) is true
+PASS testAddThenRemove({ }, undefined) is true
+PASS testAddThenRemove({ }, true) is false
+PASS testAddThenRemove({ }, { 'capture': true }) is false
+
+PASS testAddThenRemove(true, true) is true
+PASS testAddThenRemove(true, { 'capture': true }) is true
+PASS testAddThenRemove(true, { }) is false
+PASS testAddThenRemove(true, undefined) is false
+PASS testAddThenRemove(true, false) is false
+PASS testAddThenRemove(true, { 'capture': false }) is false
+
+PASS testAddThenRemove({ 'capture': true } , true) is true
+PASS testAddThenRemove({ 'capture': true }, { 'capture': true }) is true
+PASS testAddThenRemove({ 'capture': true }, { }) is false
+PASS testAddThenRemove({ 'capture': true }, undefined) is false
+PASS testAddThenRemove({ 'capture': true }, false) is false
+PASS testAddThenRemove({ 'capture': true }, { 'capture': false }) is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/events/removeEventListener-EventListenerOptions-capture.html (0 => 201730)


--- trunk/LayoutTests/fast/events/removeEventListener-EventListenerOptions-capture.html	                        (rev 0)
+++ trunk/LayoutTests/fast/events/removeEventListener-EventListenerOptions-capture.html	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<script>
+description("Tests support for calling removeEventListener() with an EventListenerOptions dictionary");
+
+var wasListenerCalled = false;
+function listenerFunction(ev)
+{
+    wasListenerCalled = true;
+}
+
+function isListenerRegistered()
+{
+    document.body.dispatchEvent(new Event('test'));
+    var result = wasListenerCalled;
+    wasListenerCalled = false;
+    return result;
+}
+
+function testAddThenRemove(addOptions, removeOptions)
+{
+    document.body.addEventListener('test', listenerFunction, addOptions);
+    if (!isListenerRegistered())
+        testFailed("Failed to add event listener with given options");
+    document.body.removeEventListener('test', listenerFunction, removeOptions);
+    var result = !isListenerRegistered();
+
+    // clean up.
+    capture = false;
+    if (typeof(addOptions) === "boolean")
+        capture = addOptions;
+    else if (typeof(addOptions) === "object")
+        capture = addOptions.capture;
+    document.body.removeEventListener('test', listenerFunction, capture);
+
+    return result;
+}
+
+testAddThenRemove(undefined, undefined, true);
+
+// capture is false by default.
+shouldBeTrue("testAddThenRemove(undefined, false)");
+shouldBeTrue("testAddThenRemove(undefined, { 'capture': false })");
+shouldBeTrue("testAddThenRemove(undefined, { })");
+shouldBeTrue("testAddThenRemove(undefined, undefined)");
+shouldBeFalse("testAddThenRemove(undefined, true)");
+shouldBeFalse("testAddThenRemove(undefined, { 'capture': true })");
+
+debug("");
+shouldBeTrue("testAddThenRemove(false, false)");
+shouldBeTrue("testAddThenRemove(false, { 'capture': false })");
+shouldBeTrue("testAddThenRemove(false, { })");
+shouldBeTrue("testAddThenRemove(false, undefined)");
+shouldBeFalse("testAddThenRemove(false, true)");
+shouldBeFalse("testAddThenRemove(false, { 'capture': true })");
+
+debug("");
+shouldBeTrue("testAddThenRemove({ 'capture': false }, false)");
+shouldBeTrue("testAddThenRemove({ 'capture': false }, { 'capture': false })");
+shouldBeTrue("testAddThenRemove({ 'capture': false }, { })");
+shouldBeTrue("testAddThenRemove({ 'capture': false }, undefined)");
+shouldBeFalse("testAddThenRemove({ 'capture': false }, true)");
+shouldBeFalse("testAddThenRemove({ 'capture': false }, { 'capture': true })");
+
+debug("");
+shouldBeTrue("testAddThenRemove({ }, false)");
+shouldBeTrue("testAddThenRemove({ }, { 'capture': false })");
+shouldBeTrue("testAddThenRemove({ }, { })");
+shouldBeTrue("testAddThenRemove({ }, undefined)");
+shouldBeFalse("testAddThenRemove({ }, true)");
+shouldBeFalse("testAddThenRemove({ }, { 'capture': true })");
+
+debug("");
+shouldBeTrue("testAddThenRemove(true, true)");
+shouldBeTrue("testAddThenRemove(true, { 'capture': true })");
+shouldBeFalse("testAddThenRemove(true, { })");
+shouldBeFalse("testAddThenRemove(true, undefined)");
+shouldBeFalse("testAddThenRemove(true, false)");
+shouldBeFalse("testAddThenRemove(true, { 'capture': false })");
+
+debug("");
+shouldBeTrue("testAddThenRemove({ 'capture': true } , true)");
+shouldBeTrue("testAddThenRemove({ 'capture': true }, { 'capture': true })");
+shouldBeFalse("testAddThenRemove({ 'capture': true }, { })");
+shouldBeFalse("testAddThenRemove({ 'capture': true }, undefined)");
+shouldBeFalse("testAddThenRemove({ 'capture': true }, false)");
+shouldBeFalse("testAddThenRemove({ 'capture': true }, { 'capture': false })");
+</script>
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_default-expected.txt (0 => 201730)


--- trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_default-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_default-expected.txt	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,14 @@
+
+PASS True value 
+PASS False value 
+PASS Empty object 
+PASS Null object 
+PASS Undefined object 
+PASS Positive value 
+PASS Negative value 
+PASS NaN value 
+PASS Postive zero value 
+PASS Negative zero value 
+PASS Empty string value 
+PASS Non empty string value 
+

Added: trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_default.html (0 => 201730)


--- trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_default.html	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_default.html	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<body/>
+<script src=""
+<script src=""
+
+<script>
+
+function testCaptureValue(captureValue, expectedValue, test) {
+  var handlerPhase = undefined;
+  var handler = function handler(e) {
+      assert_equals(handlerPhase, undefined);
+      handlerPhase = e.eventPhase;
+  }
+  document.addEventListener('test', handler, captureValue);
+  document.body.dispatchEvent(new Event('test', {'bubbles': true}));
+  document.removeEventListener('test', handler, captureValue);
+  document.body.dispatchEvent(new Event('test', {'bubbles': true}));
+  assert_equals(handlerPhase, expectedValue);
+  test.done();
+}
+
+test(function(t) { testCaptureValue(true, Event.CAPTURING_PHASE, t); }, "True value");
+test(function(t) { testCaptureValue(false, Event.BUBBLING_PHASE, t); }, "False value");
+test(function(t) { testCaptureValue({}, Event.BUBBLING_PHASE, t); }, "Empty object");
+test(function(t) { testCaptureValue(null, Event.BUBBLING_PHASE, t); }, "Null object");
+test(function(t) { testCaptureValue(undefined, Event.BUBBLING_PHASE, t); }, "Undefined object");
+test(function(t) { testCaptureValue(2.3, Event.CAPTURING_PHASE, t); }, "Positive value");
+test(function(t) { testCaptureValue(-1000.3, Event.CAPTURING_PHASE, t); }, "Negative value");
+test(function(t) { testCaptureValue(NaN, Event.BUBBLING_PHASE, t); }, "NaN value");
+test(function(t) { testCaptureValue(+0.0, Event.BUBBLING_PHASE, t); }, "Postive zero value");
+test(function(t) { testCaptureValue(-0.0, Event.BUBBLING_PHASE, t); }, "Negative zero value");
+test(function(t) { testCaptureValue("", Event.BUBBLING_PHASE, t); }, "Empty string value");
+test(function(t) { testCaptureValue("AAAA", Event.CAPTURING_PHASE, t); }, "Non empty string value");
+</script>

Added: trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_equality-expected.txt (0 => 201730)


--- trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_equality-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_equality-expected.txt	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,6 @@
+
+PASS Empty object 
+PASS Capture false 
+PASS Capture true 
+PASS Non-empty object 
+

Added: trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_equality.html (0 => 201730)


--- trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_equality.html	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_equality.html	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<body/>
+<script src=""
+<script src=""
+
+<script>
+function testOptionEquality(addOptionValue, removeOptionValue, test) {
+  var handler = function handler(e) {
+      assert_unreached("dummy value getter invoked");
+  }
+  document.addEventListener('test', handler, addOptionValue);
+  document.removeEventListener('test', handler, removeOptionValue);
+  document.body.dispatchEvent(new Event('test', {'bubbles': true}));
+  test.done();
+}
+
+test(function(t) { testOptionEquality({}, false, t); }, "Empty object");
+test(function(t) { testOptionEquality({'capture': false}, false, t); }, "Capture false");
+test(function(t) { testOptionEquality({'capture': true}, true, t); }, "Capture true");
+test(function(t) { testOptionEquality({'dummy': true}, false, t); }, "Non-empty object");
+
+</script>

Added: trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_query-expected.txt (0 => 201730)


--- trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_query-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_query-expected.txt	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,3 @@
+
+PASS Supports Capture 
+

Added: trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_query.html (0 => 201730)


--- trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_query.html	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_query.html	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<body/>
+<script src=""
+<script src=""
+
+<script>
+test(function(t) {
+    var supportsCapture = false;
+    var query_function = function(e) {};
+    var query_options = {
+        get capture() {
+            supportsCapture = true;
+            return false;
+        },
+        get dummy() {
+            assert_unreached("dummy value getter invoked");
+            return false;
+        }
+    };
+
+    document.addEventListener('test_event', query_function, query_options);
+    assert_true(supportsCapture);
+    supportsCapture = false;
+    document.removeEventListener('test_event', query_function, query_options);
+    assert_true(supportsCapture);
+    t.done();
+}, "Supports Capture");
+</script>

Added: trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_dispatch-expected.txt (0 => 201730)


--- trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_dispatch-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_dispatch-expected.txt	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,9 @@
+
+PASS Two argument register 
+PASS Prevent default capture false 
+PASS Prevent default capture true 
+PASS Prevent default with empty object 
+PASS Prevent default with passive false 
+PASS Prevent default with passive true  
+PASS Passive and Blocking Registered Handlers 
+

Added: trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_dispatch.html (0 => 201730)


--- trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_dispatch.html	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_dispatch.html	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<body/>
+<script src=""
+<script src=""
+
+<!-- This test is run with normal layout tests with passiveEventListeners
+     enabled and in virtual/stable with passiveEventListeners disabled -->
+<script>
+
+function testTwoArgumentRegister() {
+  var handler = function handler(e) {
+    e.preventDefault();
+  }
+  document.addEventListener('test', handler);
+  assert_equals(false, document.body.dispatchEvent(new Event('test', {'bubbles': true, 'cancelable': true})));
+  document.removeEventListener('test', handler);
+  assert_equals(true, document.body.dispatchEvent(new Event('test', {'bubbles': true, 'cancelable': true})));
+}
+
+function testPassiveValue(registerValue, expectedValue, test) {
+  var handler = function handler(e) {
+    e.preventDefault();
+  }
+  document.addEventListener('test', handler, registerValue);
+  var event = new Event('test', {'bubbles': true, 'cancelable': true});
+  assert_equals(expectedValue, document.body.dispatchEvent(event));
+  assert_equals(!expectedValue, event.defaultPrevented);
+  document.removeEventListener('test', handler, registerValue);
+  assert_equals(true, document.body.dispatchEvent(new Event('test', {'bubbles': true, 'cancelable': true})));
+  test.done();
+}
+
+function testTwoHandlers() {
+  var passive_called = undefined;
+  var blocking_called = undefined;
+  var passive_handler = function handler(e) {
+    passive_called = true;
+  }
+  var blocking_handler = function handler(e) {
+    blocking_called = true;
+    e.preventDefault();
+  }
+  document.addEventListener('test', passive_handler, {"passive" : true});
+  document.addEventListener('test', blocking_handler, {"passive" : false});
+  var event = new Event('test', {'bubbles': true, 'cancelable': true});
+  document.body.dispatchEvent(event);
+  assert_true(passive_called);
+  assert_true(blocking_called);
+  assert_true(event.defaultPrevented);
+  document.removeEventListener('test', passive_handler, {});
+  document.removeEventListener('test', blocking_handler, {});
+}
+
+test(testTwoArgumentRegister, "Two argument register");
+test(function(t) { testPassiveValue(false, false, t); }, "Prevent default capture false");
+test(function(t) { testPassiveValue(true, false, t); }, "Prevent default capture true");
+test(function(t) { testPassiveValue({}, false, t); }, "Prevent default with empty object");
+test(function(t) { testPassiveValue({passive: false}, false, t); }, "Prevent default with passive false");
+test(function(t) { testPassiveValue({passive: true}, true, t); }, "Prevent default with passive true ");
+test(testTwoHandlers, "Passive and Blocking Registered Handlers");
+
+</script>

Added: trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_inequality-expected.txt (0 => 201730)


--- trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_inequality-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_inequality-expected.txt	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,5 @@
+
+PASS Passive Handler 
+PASS Non Passive Handler 
+PASS Passive Handler And Non Passive Capturing 
+

Added: trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_inequality.html (0 => 201730)


--- trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_inequality.html	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_inequality.html	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<body/>
+<script src=""
+<script src=""
+
+<script>
+
+function passiveHandlerAddNonPassive() {
+  var handler = function handler(e) {
+    e.preventDefault();
+    assert_false(e.defaultPrevented);
+  }
+  document.addEventListener('test', handler, {"passive": true});
+  document.addEventListener('test', handler, {"passive": false});
+  document.body.dispatchEvent(new Event('test', {'bubbles': true, 'cancelable': true}));
+}
+
+function nonPassiveHandlerAddPassive() {
+  var handler = function handler(e) {
+    e.preventDefault();
+    assert_true(e.defaultPrevented);
+  }
+  document.addEventListener('test', handler, {"passive": false});
+  document.addEventListener('test', handler, {"passive": true});
+  document.body.dispatchEvent(new Event('test', {'bubbles': true, 'cancelable': true}));
+}
+
+function passiveHandlerAddNonPassiveCapturing() {
+  var handlersCalled = 0;
+  var handler = function handler(e) {
+    handlersCalled++;
+  }
+  document.addEventListener('test', handler, {"passive": true});
+  document.addEventListener('test', handler, {"passive": false, "capture": true});
+  document.body.dispatchEvent(new Event('test', {'bubbles': true, 'cancelable': true}));
+  assert_equals(handlersCalled, 2, "Handlers called correct number of times");
+}
+
+test(passiveHandlerAddNonPassive, "Passive Handler");
+test(nonPassiveHandlerAddPassive, "Non Passive Handler");
+test(passiveHandlerAddNonPassiveCapturing, "Passive Handler And Non Passive Capturing");
+
+</script>

Added: trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_query-expected.txt (0 => 201730)


--- trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_query-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_query-expected.txt	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,3 @@
+
+PASS Supports Passive 
+

Added: trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_query.html (0 => 201730)


--- trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_query.html	                        (rev 0)
+++ trunk/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_query.html	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<body/>
+<script src=""
+<script src=""
+
+<!-- This test is run with normal layout tests with passiveEventListeners
+     enabled and in virtual/stable with passiveEventListeners disabled -->
+<script>
+test(function(t) {
+    var supportsPassive = false;
+    var query_function = function(e) {};
+    var query_options = {
+        get passive() {
+            supportsPassive = true;
+            return false;
+        },
+        get dummy() {
+            assert_unreached("dummy value getter invoked");
+            return false;
+        }
+    };
+
+    document.addEventListener('test_event', query_function, query_options);
+    assert_equals(supportsPassive, true);
+    t.done();
+}, "Supports Passive");
+</script>

Modified: trunk/LayoutTests/imported/w3c/ChangeLog (201729 => 201730)


--- trunk/LayoutTests/imported/w3c/ChangeLog	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/LayoutTests/imported/w3c/ChangeLog	2016-06-06 22:28:25 UTC (rev 201730)
@@ -1,3 +1,16 @@
+2016-06-06  Chris Dumez  <[email protected]>
+
+        Implement EventListenerOptions argument to addEventListener
+        https://bugs.webkit.org/show_bug.cgi?id=149466
+        <rdar://problem/22802031>
+
+        Reviewed by Dean Jackson.
+
+        Import new test from W3C that covers EventListenerOptions.
+
+        * web-platform-tests/dom/events/EventListenerOptions-capture-expected.txt: Added.
+        * web-platform-tests/dom/events/EventListenerOptions-capture.html: Added.
+
 2016-06-03  Chris Dumez  <[email protected]>
 
         CanvasRenderingContext2D.createPattern() / putImageData() throw wrong exception type

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/dom/events/EventListenerOptions-capture-expected.txt (0 => 201730)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/dom/events/EventListenerOptions-capture-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/dom/events/EventListenerOptions-capture-expected.txt	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,6 @@
+
+PASS Capture boolean should be honored correctly 
+PASS Capture option should be honored correctly 
+PASS Supports capture option 
+PASS Equivalence of option values 
+

Added: trunk/LayoutTests/imported/w3c/web-platform-tests/dom/events/EventListenerOptions-capture.html (0 => 201730)


--- trunk/LayoutTests/imported/w3c/web-platform-tests/dom/events/EventListenerOptions-capture.html	                        (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/dom/events/EventListenerOptions-capture.html	2016-06-06 22:28:25 UTC (rev 201730)
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<title>EventListenerOptions.capture</title>
+<link rel="author" title="Rick Byers" href=""
+<link rel="help" href=""
+<script src=""
+<script src=""
+<div id="log"></div>
+
+<script>
+
+function testCaptureValue(captureValue, expectedValue) {
+  var handlerPhase = undefined;
+  var handler = function handler(e) {
+    assert_equals(handlerPhase, undefined, "Handler invoked after remove");
+    handlerPhase = e.eventPhase;
+  }
+  document.addEventListener('test', handler, captureValue);
+  document.body.dispatchEvent(new Event('test', {'bubbles': true}));
+  document.removeEventListener('test', handler, captureValue);
+  document.body.dispatchEvent(new Event('test', {'bubbles': true}));
+  assert_equals(handlerPhase, expectedValue, "Incorrect event phase for value: " + JSON.stringify(captureValue));
+}
+
+test(function() {
+  testCaptureValue(true, Event.CAPTURING_PHASE);
+  testCaptureValue(false, Event.BUBBLING_PHASE);
+  testCaptureValue(null, Event.BUBBLING_PHASE);
+  testCaptureValue(undefined, Event.BUBBLING_PHASE);
+  testCaptureValue(2.3, Event.CAPTURING_PHASE);
+  testCaptureValue(-1000.3, Event.CAPTURING_PHASE);
+  testCaptureValue(NaN, Event.BUBBLING_PHASE);
+  testCaptureValue(+0.0, Event.BUBBLING_PHASE);
+  testCaptureValue(-0.0, Event.BUBBLING_PHASE);
+  testCaptureValue("", Event.BUBBLING_PHASE);
+  testCaptureValue("AAAA", Event.CAPTURING_PHASE);
+}, "Capture boolean should be honored correctly");
+
+test(function() {
+  testCaptureValue({}, Event.BUBBLING_PHASE);
+  testCaptureValue({capture:true}, Event.CAPTURING_PHASE);
+  testCaptureValue({capture:false}, Event.BUBBLING_PHASE);
+  testCaptureValue({capture:2}, Event.CAPTURING_PHASE);
+  testCaptureValue({capture:0}, Event.BUBBLING_PHASE);
+}, "Capture option should be honored correctly");
+
+test(function() {
+  var supportsCapture = false;
+  var query_options = {
+    get capture() {
+      supportsCapture = true;
+      return false;
+    },
+    get dummy() {
+      assert_unreached("dummy value getter invoked");
+      return false;
+    }
+  };
+
+  document.addEventListener('test_event', null, query_options);
+  assert_true(supportsCapture, "addEventListener doesn't support the capture option");
+  supportsCapture = false;
+  document.removeEventListener('test_event', null, query_options);
+  assert_true(supportsCapture, "removeEventListener doesn't support the capture option");
+}, "Supports capture option");
+
+function testOptionEquality(addOptionValue, removeOptionValue, expectedEquality) {
+  var handlerInvoked = false;
+  var handler = function handler(e) {
+    assert_equals(handlerInvoked, false, "Handler invoked multiple times");
+    handlerInvoked = true;
+  }
+  document.addEventListener('test', handler, addOptionValue);
+  document.removeEventListener('test', handler, removeOptionValue);
+  document.body.dispatchEvent(new Event('test', {'bubbles': true}));
+  assert_equals(!handlerInvoked, expectedEquality, "equivalence of options " +
+    JSON.stringify(addOptionValue) + " and " + JSON.stringify(removeOptionValue));
+  if (handlerInvoked)
+    document.removeEventListener('test', handler, addOptionValue);
+}
+
+test(function() {
+  // Option values that should be treated as equivalent
+  testOptionEquality({}, false, true);
+  testOptionEquality({capture: false}, false, true);
+  testOptionEquality(true, {capture: true}, true);
+  testOptionEquality({capture: null}, undefined, true);
+  testOptionEquality({capture: true}, {dummy: false, capture: 1}, true);
+  testOptionEquality({dummy: true}, false, true);
+
+  // Option values that should be treated as distinct
+  testOptionEquality(true, false, false);
+  testOptionEquality(true, {capture:false}, false);
+  testOptionEquality({}, true, false);
+
+}, "Equivalence of option values");
+
+</script>

Modified: trunk/Source/WebCore/ChangeLog (201729 => 201730)


--- trunk/Source/WebCore/ChangeLog	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/ChangeLog	2016-06-06 22:28:25 UTC (rev 201730)
@@ -1,3 +1,90 @@
+2016-06-06  Chris Dumez  <[email protected]>
+
+        Implement EventListenerOptions argument to addEventListener
+        https://bugs.webkit.org/show_bug.cgi?id=149466
+        <rdar://problem/22802031>
+
+        Reviewed by Dean Jackson.
+
+        Implement AddEventListenerOptions dictionary argument to addEventListener()
+        and EventListenerOptions dictionary argument to removeEventListener(), as
+        per the latest DOM specification:
+        - https://dom.spec.whatwg.org/#interface-eventtarget
+
+        Firefox and Chrome already support this.
+
+        Support for AddEventListenerOptions in this patch is as follows:
+        - 'capture': fully supported.
+        - 'once': fully supported.
+        - 'passive': supported in the sense that preventDefault() will be ignored
+                     for passive event listeners. There are however currently no
+                     performance benefits from passing this flag. Those optimizations
+                     will be implemented in follow-up patches (in particular for
+                     Touch and Scroll events).
+
+        Tests: fast/events/AddEventListenerOptions-once-recursive.html
+               fast/events/AddEventListenerOptions-once.html
+               fast/events/AddEventListenerOptions-passive.html
+               fast/events/removeEventListener-EventListenerOptions-capture.html
+               imported/w3c/web-platform-tests/dom/events/EventListenerOptions-capture.html
+
+        * Modules/webaudio/AudioScheduledSourceNode.cpp:
+        (WebCore::AudioScheduledSourceNode::addEventListener):
+        (WebCore::AudioScheduledSourceNode::removeEventListener):
+        * Modules/webaudio/AudioScheduledSourceNode.h:
+        * Modules/webaudio/ScriptProcessorNode.cpp:
+        (WebCore::ScriptProcessorNode::addEventListener):
+        (WebCore::ScriptProcessorNode::removeEventListener):
+        * Modules/webaudio/ScriptProcessorNode.h:
+        * bindings/scripts/CodeGeneratorJS.pm:
+        (GenerateParametersCheckExpression):
+        * dom/Event.h:
+        (WebCore::Event::preventDefault):
+        (WebCore::Event::setInPassiveListener):
+        * dom/EventListenerMap.cpp:
+        (WebCore::addListenerToVector):
+        (WebCore::EventListenerMap::add):
+        * dom/EventListenerMap.h:
+        * dom/EventTarget.cpp:
+        (WebCore::EventTarget::addEventListener):
+        (WebCore::EventTarget::addEventListenerForBindings):
+        (WebCore::EventTarget::removeEventListenerForBindings):
+        (WebCore::EventTarget::removeEventListener):
+        (WebCore::EventTarget::setAttributeEventListener):
+        (WebCore::EventTarget::fireEventListeners):
+        * dom/EventTarget.h:
+        (WebCore::EventTarget::ListenerOptions::ListenerOptions):
+        (WebCore::EventTarget::AddEventListenerOptions::AddEventListenerOptions):
+        (WebCore::EventTarget::addEventListener):
+        (WebCore::EventTarget::addEventListenerForBindings):
+        (WebCore::EventTarget::removeEventListenerForBindings):
+        * dom/EventTarget.idl:
+        * dom/MessagePort.cpp:
+        (WebCore::MessagePort::addEventListener):
+        * dom/MessagePort.h:
+        * dom/Node.cpp:
+        (WebCore::tryAddEventListener):
+        (WebCore::Node::addEventListener):
+        (WebCore::tryRemoveEventListener):
+        (WebCore::Node::removeEventListener):
+        * dom/Node.h:
+        * dom/RegisteredEventListener.h:
+        (WebCore::RegisteredEventListener::Options::Options):
+        (WebCore::RegisteredEventListener::RegisteredEventListener):
+        (WebCore::operator==):
+        * html/HTMLMediaElement.cpp:
+        (WebCore::HTMLMediaElement::addEventListener):
+        (WebCore::HTMLMediaElement::removeEventListener):
+        * html/HTMLMediaElement.h:
+        * page/DOMWindow.cpp:
+        (WebCore::DOMWindow::addEventListener):
+        (WebCore::DOMWindow::removeEventListener):
+        * page/DOMWindow.h:
+        * svg/SVGElement.cpp:
+        (WebCore::SVGElement::addEventListener):
+        (WebCore::SVGElement::removeEventListener):
+        * svg/SVGElement.h:
+
 2016-06-06  Jer Noble  <[email protected]>
 
         Media elements should only be allowed to control controls manager during a user gesture.

Modified: trunk/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.cpp (201729 => 201730)


--- trunk/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.cpp	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.cpp	2016-06-06 22:28:25 UTC (rev 201730)
@@ -201,17 +201,17 @@
     }
 }
 
-bool AudioScheduledSourceNode::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, bool useCapture)
+bool AudioScheduledSourceNode::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
 {
-    bool success = AudioNode::addEventListener(eventType, WTFMove(listener), useCapture);
+    bool success = AudioNode::addEventListener(eventType, WTFMove(listener), options);
     if (success && eventType == eventNames().endedEvent)
         m_hasEndedListener = hasEventListeners(eventNames().endedEvent);
     return success;
 }
 
-bool AudioScheduledSourceNode::removeEventListener(const AtomicString& eventType, EventListener& listener, bool useCapture)
+bool AudioScheduledSourceNode::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
 {
-    bool success = AudioNode::removeEventListener(eventType, listener, useCapture);
+    bool success = AudioNode::removeEventListener(eventType, listener, options);
     if (success && eventType == eventNames().endedEvent)
         m_hasEndedListener = hasEventListeners(eventNames().endedEvent);
     return success;

Modified: trunk/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.h (201729 => 201730)


--- trunk/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.h	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.h	2016-06-06 22:28:25 UTC (rev 201730)
@@ -101,8 +101,8 @@
     static const double UnknownTime;
 
 private:
-    bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, bool useCapture) override;
-    bool removeEventListener(const AtomicString& eventType, EventListener&, bool useCapture) override;
+    bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) override;
+    bool removeEventListener(const AtomicString& eventType, EventListener&, const ListenerOptions&) override;
     void removeAllEventListeners() override;
 };
 

Modified: trunk/Source/WebCore/Modules/webaudio/ScriptProcessorNode.cpp (201729 => 201730)


--- trunk/Source/WebCore/Modules/webaudio/ScriptProcessorNode.cpp	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/Modules/webaudio/ScriptProcessorNode.cpp	2016-06-06 22:28:25 UTC (rev 201730)
@@ -273,17 +273,17 @@
     return std::numeric_limits<double>::infinity();
 }
 
-bool ScriptProcessorNode::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, bool useCapture)
+bool ScriptProcessorNode::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
 {
-    bool success = AudioNode::addEventListener(eventType, WTFMove(listener), useCapture);
+    bool success = AudioNode::addEventListener(eventType, WTFMove(listener), options);
     if (success && eventType == eventNames().audioprocessEvent)
         m_hasAudioProcessListener = hasEventListeners(eventNames().audioprocessEvent);
     return success;
 }
 
-bool ScriptProcessorNode::removeEventListener(const AtomicString& eventType, EventListener& listener, bool useCapture)
+bool ScriptProcessorNode::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
 {
-    bool success = AudioNode::removeEventListener(eventType, listener, useCapture);
+    bool success = AudioNode::removeEventListener(eventType, listener, options);
     if (success && eventType == eventNames().audioprocessEvent)
         m_hasAudioProcessListener = hasEventListeners(eventNames().audioprocessEvent);
     return success;

Modified: trunk/Source/WebCore/Modules/webaudio/ScriptProcessorNode.h (201729 => 201730)


--- trunk/Source/WebCore/Modules/webaudio/ScriptProcessorNode.h	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/Modules/webaudio/ScriptProcessorNode.h	2016-06-06 22:28:25 UTC (rev 201730)
@@ -73,8 +73,8 @@
 
     void fireProcessEvent();
 
-    bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, bool useCapture) override;
-    bool removeEventListener(const AtomicString& eventType, EventListener&, bool useCapture) override;
+    bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) override;
+    bool removeEventListener(const AtomicString& eventType, EventListener&, const ListenerOptions&) override;
     void removeAllEventListeners() override;
 
     // Double buffering

Modified: trunk/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm (201729 => 201730)


--- trunk/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm	2016-06-06 22:28:25 UTC (rev 201730)
@@ -1678,7 +1678,10 @@
                 push(@andExpression, "(${value}.isNull() || ${value}.isObject())");
             }
             $usedArguments{$parameterIndex} = 1;
-        } elsif ($codeGenerator->GetArrayOrSequenceType($type) || $codeGenerator->IsTypedArrayType($type) || $codeGenerator->IsWrapperType($type)) {
+        } elsif ($codeGenerator->IsDictionaryType($parameter->type)) {
+            push(@andExpression, "(${value}.isUndefinedOrNull() || ${value}.isObject())");
+            $usedArguments{$parameterIndex} = 1;
+        } elsif (($codeGenerator->GetArrayOrSequenceType($type) || $codeGenerator->IsTypedArrayType($type) || $codeGenerator->IsWrapperType($type)) && $type ne "EventListener") {
             my $condition = "";
 
             if ($parameter->isNullable) {

Modified: trunk/Source/WebCore/dom/Event.h (201729 => 201730)


--- trunk/Source/WebCore/dom/Event.h	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/dom/Event.h	2016-06-06 22:28:25 UTC (rev 201730)
@@ -172,7 +172,7 @@
     bool defaultPrevented() const { return m_defaultPrevented; }
     void preventDefault()
     {
-        if (m_cancelable)
+        if (m_cancelable && !m_isExecutingPassiveEventListener)
             m_defaultPrevented = true;
     }
     void setDefaultPrevented(bool defaultPrevented) { m_defaultPrevented = defaultPrevented; }
@@ -180,6 +180,8 @@
     bool defaultHandled() const { return m_defaultHandled; }
     void setDefaultHandled() { m_defaultHandled = true; }
 
+    void setInPassiveListener(bool value) { m_isExecutingPassiveEventListener = value; }
+
     bool cancelBubble() const { return m_cancelBubble; }
     void setCancelBubble(bool cancel) { m_cancelBubble = cancel; }
 
@@ -217,6 +219,7 @@
     bool m_defaultHandled { false };
     bool m_cancelBubble { false };
     bool m_isTrusted { false };
+    bool m_isExecutingPassiveEventListener { false };
 
     unsigned short m_eventPhase { 0 };
     EventTarget* m_currentTarget { nullptr };

Modified: trunk/Source/WebCore/dom/EventListenerMap.cpp (201729 => 201730)


--- trunk/Source/WebCore/dom/EventListenerMap.cpp	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/dom/EventListenerMap.cpp	2016-06-06 22:28:25 UTC (rev 201730)
@@ -95,9 +95,9 @@
     return types;
 }
 
-static bool addListenerToVector(EventListenerVector* vector, Ref<EventListener>&& listener, bool useCapture)
+static bool addListenerToVector(EventListenerVector* vector, Ref<EventListener>&& listener, const RegisteredEventListener::Options& options)
 {
-    RegisteredEventListener registeredListener(WTFMove(listener), useCapture);
+    RegisteredEventListener registeredListener(WTFMove(listener), options);
 
     if (vector->find(registeredListener) != notFound)
         return false; // Duplicate listener.
@@ -106,17 +106,17 @@
     return true;
 }
 
-bool EventListenerMap::add(const AtomicString& eventType, Ref<EventListener>&& listener, bool useCapture)
+bool EventListenerMap::add(const AtomicString& eventType, Ref<EventListener>&& listener, const RegisteredEventListener::Options& options)
 {
     assertNoActiveIterators();
 
     for (auto& entry : m_entries) {
         if (entry.first == eventType)
-            return addListenerToVector(entry.second.get(), WTFMove(listener), useCapture);
+            return addListenerToVector(entry.second.get(), WTFMove(listener), options);
     }
 
     m_entries.append(std::make_pair(eventType, std::make_unique<EventListenerVector>()));
-    return addListenerToVector(m_entries.last().second.get(), WTFMove(listener), useCapture);
+    return addListenerToVector(m_entries.last().second.get(), WTFMove(listener), options);
 }
 
 static bool removeListenerFromVector(EventListenerVector* listenerVector, EventListener& listener, bool useCapture, size_t& indexOfRemovedListener)

Modified: trunk/Source/WebCore/dom/EventListenerMap.h (201729 => 201730)


--- trunk/Source/WebCore/dom/EventListenerMap.h	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/dom/EventListenerMap.h	2016-06-06 22:28:25 UTC (rev 201730)
@@ -54,7 +54,8 @@
     bool containsCapturing(const AtomicString& eventType) const;
 
     void clear();
-    bool add(const AtomicString& eventType, Ref<EventListener>&&, bool useCapture);
+
+    bool add(const AtomicString& eventType, Ref<EventListener>&&, const RegisteredEventListener::Options&);
     bool remove(const AtomicString& eventType, EventListener&, bool useCapture, size_t& indexOfRemovedListener);
     EventListenerVector* find(const AtomicString& eventType);
     Vector<AtomicString> eventTypes() const;

Modified: trunk/Source/WebCore/dom/EventTarget.cpp (201729 => 201730)


--- trunk/Source/WebCore/dom/EventTarget.cpp	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/dom/EventTarget.cpp	2016-06-06 22:28:25 UTC (rev 201730)
@@ -75,28 +75,26 @@
     return false;
 }
 
-bool EventTarget::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, bool useCapture)
+bool EventTarget::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
 {
-    return ensureEventTargetData().eventListenerMap.add(eventType, WTFMove(listener), useCapture);
+    return ensureEventTargetData().eventListenerMap.add(eventType, WTFMove(listener), { options.capture, options.passive, options.once });
 }
 
-void EventTarget::addEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, bool useCapture)
+void EventTarget::addEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, const AddEventListenerOptions& options)
 {
-    // FIXME: listener is not supposed to be nullable.
     if (!listener)
         return;
-    addEventListener(eventType, listener.releaseNonNull(), useCapture);
+    addEventListener(eventType, listener.releaseNonNull(), options);
 }
 
-void EventTarget::removeEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, bool useCapture)
+void EventTarget::removeEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, const ListenerOptions& options)
 {
-    // FIXME: listener is not supposed to be nullable.
     if (!listener)
         return;
-    removeEventListener(eventType, *listener, useCapture);
+    removeEventListener(eventType, *listener, options);
 }
 
-bool EventTarget::removeEventListener(const AtomicString& eventType, EventListener& listener, bool useCapture)
+bool EventTarget::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
 {
     EventTargetData* d = eventTargetData();
     if (!d)
@@ -104,7 +102,7 @@
 
     size_t indexOfRemovedListener;
 
-    if (!d->eventListenerMap.remove(eventType, listener, useCapture, indexOfRemovedListener))
+    if (!d->eventListenerMap.remove(eventType, listener, options.capture, indexOfRemovedListener))
         return false;
 
     // Notify firing events planning to invoke the listener at 'index' that
@@ -131,7 +129,7 @@
     clearAttributeEventListener(eventType);
     if (!listener)
         return false;
-    return addEventListener(eventType, listener.releaseNonNull(), false);
+    return addEventListener(eventType, listener.releaseNonNull());
 }
 
 EventListener* EventTarget::getAttributeEventListener(const AtomicString& eventType)
@@ -260,6 +258,10 @@
 
     for (; i < size; ++i) {
         RegisteredEventListener& registeredListener = entry[i];
+
+        if (registeredListener.isMarkedForRemoval)
+            continue;
+
         if (event.eventPhase() == Event::CAPTURING_PHASE && !registeredListener.useCapture)
             continue;
         if (event.eventPhase() == Event::BUBBLING_PHASE && registeredListener.useCapture)
@@ -270,12 +272,27 @@
         if (event.immediatePropagationStopped())
             break;
 
+        if (registeredListener.isPassive)
+            event.setInPassiveListener(true);
+
+        // Mark listener for removal before executing the listener, in case the listener tries to
+        // dispatch an event that would cause it to get executed again.
+        if (registeredListener.isOnce)
+            registeredListener.isMarkedForRemoval = true;
+
         InspectorInstrumentationCookie cookie = InspectorInstrumentation::willHandleEvent(context, event);
         // To match Mozilla, the AT_TARGET phase fires both capturing and bubbling
         // event listeners, even though that violates some versions of the DOM spec.
         registeredListener.listener->handleEvent(context, &event);
         InspectorInstrumentation::didHandleEvent(cookie);
+
+        if (registeredListener.isPassive)
+            event.setInPassiveListener(false);
+
+        if (registeredListener.isOnce)
+            removeEventListener(event.type(), *registeredListener.listener, ListenerOptions(registeredListener.useCapture));
     }
+
     d->firingEventIterators->removeLast();
 
     if (document)

Modified: trunk/Source/WebCore/dom/EventTarget.h (201729 => 201730)


--- trunk/Source/WebCore/dom/EventTarget.h	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/dom/EventTarget.h	2016-06-06 22:28:25 UTC (rev 201730)
@@ -121,10 +121,31 @@
     virtual DOMWindow* toDOMWindow();
     virtual bool isMessagePort() const;
 
+    struct ListenerOptions {
+        ListenerOptions(bool capture = false)
+            : capture(capture)
+        { }
+
+        bool capture;
+    };
+
+    struct AddEventListenerOptions : public ListenerOptions {
+        AddEventListenerOptions(bool capture = false, bool passive = false, bool _once_ = false)
+            : ListenerOptions(capture)
+            , passive(passive)
+            , once(once)
+        { }
+
+        bool passive;
+        bool once;
+    };
+
     void addEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&&, bool useCapture);
     void removeEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&&, bool useCapture);
-    virtual bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, bool useCapture);
-    virtual bool removeEventListener(const AtomicString& eventType, EventListener&, bool useCapture);
+    void addEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&&, const AddEventListenerOptions&);
+    void removeEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&&, const ListenerOptions&);
+    virtual bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, const AddEventListenerOptions& = { });
+    virtual bool removeEventListener(const AtomicString& eventType, EventListener&, const ListenerOptions&);
 
     virtual void removeAllEventListeners();
     virtual bool dispatchEvent(Event&);
@@ -209,6 +230,16 @@
     return d->eventListenerMap.containsCapturing(eventType);
 }
 
+inline void EventTarget::addEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, bool useCapture)
+{
+    addEventListenerForBindings(eventType, WTFMove(listener), AddEventListenerOptions(useCapture));
+}
+
+inline void EventTarget::removeEventListenerForBindings(const AtomicString& eventType, RefPtr<EventListener>&& listener, bool useCapture)
+{
+    removeEventListenerForBindings(eventType, WTFMove(listener), ListenerOptions(useCapture));
+}
+
 } // namespace WebCore
 
 #endif // EventTarget_h

Modified: trunk/Source/WebCore/dom/EventTarget.idl (201729 => 201730)


--- trunk/Source/WebCore/dom/EventTarget.idl	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/dom/EventTarget.idl	2016-06-06 22:28:25 UTC (rev 201730)
@@ -25,6 +25,11 @@
     JSCustomToNativeObject,
     ObjCProtocol,
 ] interface EventTarget {
+#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT
+    [ImplementedAs=addEventListenerForBindings] void addEventListener([AtomicString] DOMString type, EventListener? listener, optional AddEventListenerOptions options);
+    [ImplementedAs=removeEventListenerForBindings] void removeEventListener([AtomicString] DOMString type, EventListener? listener, optional EventListenerOptions options);
+#endif
+
     // FIXME: The 'type' and 'listener' parameters should not be optional.
     [ObjCLegacyUnnamedParameters, ImplementedAs=addEventListenerForBindings] void addEventListener([AtomicString] optional DOMString type = "undefined", optional EventListener? listener, optional boolean useCapture = false);
     [ObjCLegacyUnnamedParameters, ImplementedAs=removeEventListenerForBindings] void removeEventListener([AtomicString] optional DOMString type = "undefined", optional EventListener? listener, optional boolean useCapture = false);
@@ -32,3 +37,13 @@
     // FIXME: event should not be nullable.
     [ImplementedAs=dispatchEventForBindings, RaisesException] boolean dispatchEvent(Event? event);
 };
+
+dictionary EventListenerOptions {
+    boolean capture = false;
+};
+
+dictionary AddEventListenerOptions {
+    boolean capture = false;
+    boolean passive = false;
+    boolean _once_ = false;
+};

Modified: trunk/Source/WebCore/dom/MessagePort.cpp (201729 => 201730)


--- trunk/Source/WebCore/dom/MessagePort.cpp	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/dom/MessagePort.cpp	2016-06-06 22:28:25 UTC (rev 201730)
@@ -221,11 +221,11 @@
     return portArray;
 }
 
-bool MessagePort::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, bool useCapture)
+bool MessagePort::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
 {
     if (listener->isAttribute() && eventType == eventNames().messageEvent)
         start();
-    return EventTargetWithInlineData::addEventListener(eventType, WTFMove(listener), useCapture);
+    return EventTargetWithInlineData::addEventListener(eventType, WTFMove(listener), options);
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/dom/MessagePort.h (201729 => 201730)


--- trunk/Source/WebCore/dom/MessagePort.h	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/dom/MessagePort.h	2016-06-06 22:28:25 UTC (rev 201730)
@@ -92,7 +92,7 @@
         // A port gets neutered when it is transferred to a new owner via postMessage().
         bool isNeutered() { return !m_entangledChannel; }
 
-        bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, bool useCapture) override;
+        bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) override;
 
     private:
         explicit MessagePort(ScriptExecutionContext&);

Modified: trunk/Source/WebCore/dom/Node.cpp (201729 => 201730)


--- trunk/Source/WebCore/dom/Node.cpp	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/dom/Node.cpp	2016-06-06 22:28:25 UTC (rev 201730)
@@ -1907,9 +1907,9 @@
     }
 }
 
-static inline bool tryAddEventListener(Node* targetNode, const AtomicString& eventType, Ref<EventListener>&& listener, bool useCapture)
+static inline bool tryAddEventListener(Node* targetNode, const AtomicString& eventType, Ref<EventListener>&& listener, const EventTarget::AddEventListenerOptions& options)
 {
-    if (!targetNode->EventTarget::addEventListener(eventType, listener.copyRef(), useCapture))
+    if (!targetNode->EventTarget::addEventListener(eventType, listener.copyRef(), options))
         return false;
 
     targetNode->document().addListenerTypeIfNeeded(eventType);
@@ -1927,7 +1927,7 @@
     // This code was added to address <rdar://problem/5846492> Onorientationchange event not working for document.body.
     // Forward this call to addEventListener() to the window since these are window-only events.
     if (eventType == eventNames().orientationchangeEvent || eventType == eventNames().resizeEvent)
-        targetNode->document().domWindow()->addEventListener(eventType, WTFMove(listener), useCapture);
+        targetNode->document().domWindow()->addEventListener(eventType, WTFMove(listener), options);
 
 #if ENABLE(TOUCH_EVENTS)
     if (eventNames().isTouchEventType(eventType))
@@ -1943,14 +1943,14 @@
     return true;
 }
 
-bool Node::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, bool useCapture)
+bool Node::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
 {
-    return tryAddEventListener(this, eventType, WTFMove(listener), useCapture);
+    return tryAddEventListener(this, eventType, WTFMove(listener), options);
 }
 
-static inline bool tryRemoveEventListener(Node* targetNode, const AtomicString& eventType, EventListener& listener, bool useCapture)
+static inline bool tryRemoveEventListener(Node* targetNode, const AtomicString& eventType, EventListener& listener, const EventTarget::ListenerOptions& options)
 {
-    if (!targetNode->EventTarget::removeEventListener(eventType, listener, useCapture))
+    if (!targetNode->EventTarget::removeEventListener(eventType, listener, options))
         return false;
 
     // FIXME: Notify Document that the listener has vanished. We need to keep track of a number of
@@ -1968,7 +1968,7 @@
     // This code was added to address <rdar://problem/5846492> Onorientationchange event not working for document.body.
     // Forward this call to removeEventListener() to the window since these are window-only events.
     if (eventType == eventNames().orientationchangeEvent || eventType == eventNames().resizeEvent)
-        targetNode->document().domWindow()->removeEventListener(eventType, listener, useCapture);
+        targetNode->document().domWindow()->removeEventListener(eventType, listener, options);
 
 #if ENABLE(TOUCH_EVENTS)
     if (eventNames().isTouchEventType(eventType))
@@ -1984,9 +1984,9 @@
     return true;
 }
 
-bool Node::removeEventListener(const AtomicString& eventType, EventListener& listener, bool useCapture)
+bool Node::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
 {
-    return tryRemoveEventListener(this, eventType, listener, useCapture);
+    return tryRemoveEventListener(this, eventType, listener, options);
 }
 
 typedef HashMap<Node*, std::unique_ptr<EventTargetData>> EventTargetDataMap;

Modified: trunk/Source/WebCore/dom/Node.h (201729 => 201730)


--- trunk/Source/WebCore/dom/Node.h	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/dom/Node.h	2016-06-06 22:28:25 UTC (rev 201730)
@@ -507,8 +507,8 @@
     EventTargetInterface eventTargetInterface() const override;
     ScriptExecutionContext* scriptExecutionContext() const final; // Implemented in Document.h
 
-    bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, bool useCapture) override;
-    bool removeEventListener(const AtomicString& eventType, EventListener&, bool useCapture) override;
+    bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) override;
+    bool removeEventListener(const AtomicString& eventType, EventListener&, const ListenerOptions&) override;
 
     using EventTarget::dispatchEvent;
     bool dispatchEvent(Event&) override;

Modified: trunk/Source/WebCore/dom/RegisteredEventListener.h (201729 => 201730)


--- trunk/Source/WebCore/dom/RegisteredEventListener.h	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/dom/RegisteredEventListener.h	2016-06-06 22:28:25 UTC (rev 201730)
@@ -31,6 +31,26 @@
 
     class RegisteredEventListener {
     public:
+        struct Options {
+            Options(bool capture = false, bool passive = false, bool _once_ = false)
+                : capture(capture)
+                , passive(passive)
+                , once(once)
+            { }
+
+            bool capture;
+            bool passive;
+            bool once;
+        };
+
+        RegisteredEventListener(Ref<EventListener>&& listener, const Options& options)
+            : listener(WTFMove(listener))
+            , useCapture(options.capture)
+            , isPassive(options.passive)
+            , isOnce(options.once)
+        {
+        }
+
         RegisteredEventListener(Ref<EventListener>&& listener, bool useCapture)
             : listener(WTFMove(listener))
             , useCapture(useCapture)
@@ -38,11 +58,16 @@
         }
 
         RefPtr<EventListener> listener;
-        bool useCapture;
+        bool useCapture { false };
+        bool isPassive { false };
+        bool isOnce { false };
+        bool isMarkedForRemoval { false };
     };
     
     inline bool operator==(const RegisteredEventListener& a, const RegisteredEventListener& b)
     {
+        // Other data members are purposefully not checked. The DOM specification says that upon adding / removing
+        // EventListeners, we should only check the type and the capture flag.
         return *a.listener == *b.listener && a.useCapture == b.useCapture;
     }
 

Modified: trunk/Source/WebCore/html/HTMLMediaElement.cpp (201729 => 201730)


--- trunk/Source/WebCore/html/HTMLMediaElement.cpp	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/html/HTMLMediaElement.cpp	2016-06-06 22:28:25 UTC (rev 201730)
@@ -5228,13 +5228,13 @@
     return HTMLElement::dispatchEvent(event);
 }
 
-bool HTMLMediaElement::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, bool useCapture)
+bool HTMLMediaElement::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
 {
     if (eventType != eventNames().webkitplaybacktargetavailabilitychangedEvent)
-        return Node::addEventListener(eventType, WTFMove(listener), useCapture);
+        return Node::addEventListener(eventType, WTFMove(listener), options);
 
     bool isFirstAvailabilityChangedListener = !hasEventListeners(eventNames().webkitplaybacktargetavailabilitychangedEvent);
-    if (!Node::addEventListener(eventType, WTFMove(listener), useCapture))
+    if (!Node::addEventListener(eventType, WTFMove(listener), options))
         return false;
 
     if (isFirstAvailabilityChangedListener) {
@@ -5248,12 +5248,12 @@
     return true;
 }
 
-bool HTMLMediaElement::removeEventListener(const AtomicString& eventType, EventListener& listener, bool useCapture)
+bool HTMLMediaElement::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
 {
     if (eventType != eventNames().webkitplaybacktargetavailabilitychangedEvent)
-        return Node::removeEventListener(eventType, listener, useCapture);
+        return Node::removeEventListener(eventType, listener, options);
 
-    if (!Node::removeEventListener(eventType, listener, useCapture))
+    if (!Node::removeEventListener(eventType, listener, options))
         return false;
 
     bool didRemoveLastAvailabilityChangedListener = !hasEventListeners(eventNames().webkitplaybacktargetavailabilitychangedEvent);

Modified: trunk/Source/WebCore/html/HTMLMediaElement.h (201729 => 201730)


--- trunk/Source/WebCore/html/HTMLMediaElement.h	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/html/HTMLMediaElement.h	2016-06-06 22:28:25 UTC (rev 201730)
@@ -342,8 +342,8 @@
 
 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
     void webkitShowPlaybackTargetPicker();
-    bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, bool useCapture) override;
-    bool removeEventListener(const AtomicString& eventType, EventListener&, bool useCapture) override;
+    bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) override;
+    bool removeEventListener(const AtomicString& eventType, EventListener&, const ListenerOptions&) override;
 
     void wirelessRoutesAvailableDidChange() override;
     bool canPlayToWirelessPlaybackTarget() const override;

Modified: trunk/Source/WebCore/page/DOMWindow.cpp (201729 => 201730)


--- trunk/Source/WebCore/page/DOMWindow.cpp	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/page/DOMWindow.cpp	2016-06-06 22:28:25 UTC (rev 201730)
@@ -1722,9 +1722,9 @@
     return false;
 }
 
-bool DOMWindow::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, bool useCapture)
+bool DOMWindow::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
 {
-    if (!EventTarget::addEventListener(eventType, WTFMove(listener), useCapture))
+    if (!EventTarget::addEventListener(eventType, WTFMove(listener), options))
         return false;
 
     if (Document* document = this->document()) {
@@ -1825,9 +1825,9 @@
 #endif
 }
 
-bool DOMWindow::removeEventListener(const AtomicString& eventType, EventListener& listener, bool useCapture)
+bool DOMWindow::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
 {
-    if (!EventTarget::removeEventListener(eventType, listener, useCapture))
+    if (!EventTarget::removeEventListener(eventType, listener, options.capture))
         return false;
 
     if (Document* document = this->document()) {

Modified: trunk/Source/WebCore/page/DOMWindow.h (201729 => 201730)


--- trunk/Source/WebCore/page/DOMWindow.h	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/page/DOMWindow.h	2016-06-06 22:28:25 UTC (rev 201730)
@@ -279,8 +279,8 @@
 
         // Events
         // EventTarget API
-        bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, bool useCapture) override;
-        bool removeEventListener(const AtomicString& eventType, EventListener&, bool useCapture) override;
+        bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) override;
+        bool removeEventListener(const AtomicString& eventType, EventListener&, const ListenerOptions&) override;
         void removeAllEventListeners() override;
 
         using EventTarget::dispatchEvent;

Modified: trunk/Source/WebCore/svg/SVGElement.cpp (201729 => 201730)


--- trunk/Source/WebCore/svg/SVGElement.cpp	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/svg/SVGElement.cpp	2016-06-06 22:28:25 UTC (rev 201730)
@@ -528,10 +528,10 @@
     return true;
 }
 
-bool SVGElement::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, bool useCapture)
+bool SVGElement::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
 {   
     // Add event listener to regular DOM element
-    if (!Node::addEventListener(eventType, listener.copyRef(), useCapture))
+    if (!Node::addEventListener(eventType, listener.copyRef(), options))
         return false;
 
     if (containingShadowRoot())
@@ -541,17 +541,17 @@
     ASSERT(!instanceUpdatesBlocked());
     for (auto* instance : instances()) {
         ASSERT(instance->correspondingElement() == this);
-        bool result = instance->Node::addEventListener(eventType, listener.copyRef(), useCapture);
+        bool result = instance->Node::addEventListener(eventType, listener.copyRef(), options);
         ASSERT_UNUSED(result, result);
     }
 
     return true;
 }
 
-bool SVGElement::removeEventListener(const AtomicString& eventType, EventListener& listener, bool useCapture)
+bool SVGElement::removeEventListener(const AtomicString& eventType, EventListener& listener, const ListenerOptions& options)
 {
     if (containingShadowRoot())
-        return Node::removeEventListener(eventType, listener, useCapture);
+        return Node::removeEventListener(eventType, listener, options);
 
     // EventTarget::removeEventListener creates a PassRefPtr around the given EventListener
     // object when creating a temporary RegisteredEventListener object used to look up the
@@ -561,7 +561,7 @@
     Ref<EventListener> protector(listener);
 
     // Remove event listener from regular DOM element
-    if (!Node::removeEventListener(eventType, listener, useCapture))
+    if (!Node::removeEventListener(eventType, listener, options))
         return false;
 
     // Remove event listener from all shadow tree DOM element instances
@@ -569,7 +569,7 @@
     for (auto& instance : instances()) {
         ASSERT(instance->correspondingElement() == this);
 
-        if (instance->Node::removeEventListener(eventType, listener, useCapture))
+        if (instance->Node::removeEventListener(eventType, listener, options))
             continue;
 
         // This case can only be hit for event listeners created from markup

Modified: trunk/Source/WebCore/svg/SVGElement.h (201729 => 201730)


--- trunk/Source/WebCore/svg/SVGElement.h	2016-06-06 21:47:05 UTC (rev 201729)
+++ trunk/Source/WebCore/svg/SVGElement.h	2016-06-06 22:28:25 UTC (rev 201730)
@@ -133,8 +133,8 @@
 
     virtual bool haveLoadedRequiredResources();
 
-    bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, bool useCapture) override;
-    bool removeEventListener(const AtomicString& eventType, EventListener&, bool useCapture) override;
+    bool addEventListener(const AtomicString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) override;
+    bool removeEventListener(const AtomicString& eventType, EventListener&, const ListenerOptions&) override;
     bool hasFocusEventListeners() const;
 
 #if ENABLE(CSS_REGIONS)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to