Implement EventListenerOptions argument to addEventListener
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 7 Jun 2016 17:53:15 +0000 (17:53 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 7 Jun 2016 17:53:15 +0000 (17:53 +0000)
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.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@201757 268f45cc-cd09-0410-ab3c-d52691b4dbfc

47 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/events/AddEventListenerOptions-once-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/AddEventListenerOptions-once-recursive-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/AddEventListenerOptions-once-recursive.html [new file with mode: 0644]
LayoutTests/fast/events/AddEventListenerOptions-once.html [new file with mode: 0644]
LayoutTests/fast/events/AddEventListenerOptions-passive-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/AddEventListenerOptions-passive.html [new file with mode: 0644]
LayoutTests/fast/events/removeEventListener-EventListenerOptions-capture-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/removeEventListener-EventListenerOptions-capture.html [new file with mode: 0644]
LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_default-expected.txt [new file with mode: 0644]
LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_default.html [new file with mode: 0644]
LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_equality-expected.txt [new file with mode: 0644]
LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_equality.html [new file with mode: 0644]
LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_query-expected.txt [new file with mode: 0644]
LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_query.html [new file with mode: 0644]
LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_dispatch-expected.txt [new file with mode: 0644]
LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_dispatch.html [new file with mode: 0644]
LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_inequality-expected.txt [new file with mode: 0644]
LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_inequality.html [new file with mode: 0644]
LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_query-expected.txt [new file with mode: 0644]
LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_query.html [new file with mode: 0644]
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/dom/events/EventListenerOptions-capture-expected.txt [new file with mode: 0644]
LayoutTests/imported/w3c/web-platform-tests/dom/events/EventListenerOptions-capture.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.cpp
Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.h
Source/WebCore/Modules/webaudio/ScriptProcessorNode.cpp
Source/WebCore/Modules/webaudio/ScriptProcessorNode.h
Source/WebCore/bindings/scripts/CodeGeneratorJS.pm
Source/WebCore/dom/Event.h
Source/WebCore/dom/EventListenerMap.cpp
Source/WebCore/dom/EventListenerMap.h
Source/WebCore/dom/EventTarget.cpp
Source/WebCore/dom/EventTarget.h
Source/WebCore/dom/EventTarget.idl
Source/WebCore/dom/MessagePort.cpp
Source/WebCore/dom/MessagePort.h
Source/WebCore/dom/Node.cpp
Source/WebCore/dom/Node.h
Source/WebCore/dom/RegisteredEventListener.h
Source/WebCore/html/HTMLMediaElement.cpp
Source/WebCore/html/HTMLMediaElement.h
Source/WebCore/page/DOMWindow.cpp
Source/WebCore/page/DOMWindow.h
Source/WebCore/svg/SVGElement.cpp
Source/WebCore/svg/SVGElement.h

index 0c70f10..d28a027 100644 (file)
@@ -1,3 +1,37 @@
+2016-06-07  Chris Dumez  <cdumez@apple.com>
+
+        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-07  Brent Fulgham  <bfulgham@apple.com>
 
         CSP: Content Security Policy directive, upgrade-insecure-requests (UIR)
diff --git a/LayoutTests/fast/events/AddEventListenerOptions-once-expected.txt b/LayoutTests/fast/events/AddEventListenerOptions-once-expected.txt
new file mode 100644 (file)
index 0000000..20029af
--- /dev/null
@@ -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
+
diff --git a/LayoutTests/fast/events/AddEventListenerOptions-once-recursive-expected.txt b/LayoutTests/fast/events/AddEventListenerOptions-once-recursive-expected.txt
new file mode 100644 (file)
index 0000000..20029af
--- /dev/null
@@ -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
+
diff --git a/LayoutTests/fast/events/AddEventListenerOptions-once-recursive.html b/LayoutTests/fast/events/AddEventListenerOptions-once-recursive.html
new file mode 100644 (file)
index 0000000..4c2fb4e
--- /dev/null
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../../resources/js-test-pre.js"></script>
+<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="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/events/AddEventListenerOptions-once.html b/LayoutTests/fast/events/AddEventListenerOptions-once.html
new file mode 100644 (file)
index 0000000..566427b
--- /dev/null
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../../resources/js-test-pre.js"></script>
+<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="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/events/AddEventListenerOptions-passive-expected.txt b/LayoutTests/fast/events/AddEventListenerOptions-passive-expected.txt
new file mode 100644 (file)
index 0000000..97608c3
--- /dev/null
@@ -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
+
diff --git a/LayoutTests/fast/events/AddEventListenerOptions-passive.html b/LayoutTests/fast/events/AddEventListenerOptions-passive.html
new file mode 100644 (file)
index 0000000..d0b6061
--- /dev/null
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../../resources/js-test-pre.js"></script>
+<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="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/events/removeEventListener-EventListenerOptions-capture-expected.txt b/LayoutTests/fast/events/removeEventListener-EventListenerOptions-capture-expected.txt
new file mode 100644 (file)
index 0000000..04fd076
--- /dev/null
@@ -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
+
diff --git a/LayoutTests/fast/events/removeEventListener-EventListenerOptions-capture.html b/LayoutTests/fast/events/removeEventListener-EventListenerOptions-capture.html
new file mode 100644 (file)
index 0000000..638d3b5
--- /dev/null
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../../resources/js-test-pre.js"></script>
+<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="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_default-expected.txt b/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_default-expected.txt
new file mode 100644 (file)
index 0000000..28bf1e8
--- /dev/null
@@ -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 
+
diff --git a/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_default.html b/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_default.html
new file mode 100644 (file)
index 0000000..0294f84
--- /dev/null
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<body/>
+<script src="../../../../../resources/testharness.js"></script>
+<script src="../../../../../resources/testharnessreport.js"></script>
+
+<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>
diff --git a/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_equality-expected.txt b/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_equality-expected.txt
new file mode 100644 (file)
index 0000000..22cfac2
--- /dev/null
@@ -0,0 +1,6 @@
+
+PASS Empty object 
+PASS Capture false 
+PASS Capture true 
+PASS Non-empty object 
+
diff --git a/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_equality.html b/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_equality.html
new file mode 100644 (file)
index 0000000..5c3ff11
--- /dev/null
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML>
+<body/>
+<script src="../../../../../resources/testharness.js"></script>
+<script src="../../../../../resources/testharnessreport.js"></script>
+
+<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>
diff --git a/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_query-expected.txt b/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_query-expected.txt
new file mode 100644 (file)
index 0000000..9aa3c68
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Supports Capture 
+
diff --git a/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_query.html b/LayoutTests/imported/blink/fast/events/eventlisteneroptions/capture_query.html
new file mode 100644 (file)
index 0000000..f37ac13
--- /dev/null
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<body/>
+<script src="../../../../../resources/testharness.js"></script>
+<script src="../../../../../resources/testharnessreport.js"></script>
+
+<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>
diff --git a/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_dispatch-expected.txt b/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_dispatch-expected.txt
new file mode 100644 (file)
index 0000000..7247bce
--- /dev/null
@@ -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 
+
diff --git a/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_dispatch.html b/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_dispatch.html
new file mode 100644 (file)
index 0000000..6aef9ec
--- /dev/null
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<body/>
+<script src="../../../../../resources/testharness.js"></script>
+<script src="../../../../../resources/testharnessreport.js"></script>
+
+<!-- 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>
diff --git a/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_inequality-expected.txt b/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_inequality-expected.txt
new file mode 100644 (file)
index 0000000..b045342
--- /dev/null
@@ -0,0 +1,5 @@
+
+PASS Passive Handler 
+PASS Non Passive Handler 
+PASS Passive Handler And Non Passive Capturing 
+
diff --git a/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_inequality.html b/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_inequality.html
new file mode 100644 (file)
index 0000000..a89f315
--- /dev/null
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<body/>
+<script src="../../../../../resources/testharness.js"></script>
+<script src="../../../../../resources/testharnessreport.js"></script>
+
+<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>
diff --git a/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_query-expected.txt b/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_query-expected.txt
new file mode 100644 (file)
index 0000000..3b91d8d
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS Supports Passive 
+
diff --git a/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_query.html b/LayoutTests/imported/blink/fast/events/eventlisteneroptions/passive_query.html
new file mode 100644 (file)
index 0000000..cb37907
--- /dev/null
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<body/>
+<script src="../../../../../resources/testharness.js"></script>
+<script src="../../../../../resources/testharnessreport.js"></script>
+
+<!-- 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>
index 1778ffe..9a4a378 100644 (file)
@@ -1,3 +1,16 @@
+2016-06-07  Chris Dumez  <cdumez@apple.com>
+
+        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-06  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r201735.
diff --git a/LayoutTests/imported/w3c/web-platform-tests/dom/events/EventListenerOptions-capture-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/dom/events/EventListenerOptions-capture-expected.txt
new file mode 100644 (file)
index 0000000..7bfa47c
--- /dev/null
@@ -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 
+
diff --git a/LayoutTests/imported/w3c/web-platform-tests/dom/events/EventListenerOptions-capture.html b/LayoutTests/imported/w3c/web-platform-tests/dom/events/EventListenerOptions-capture.html
new file mode 100644 (file)
index 0000000..377b6c7
--- /dev/null
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<title>EventListenerOptions.capture</title>
+<link rel="author" title="Rick Byers" href="mailto:rbyers@chromium.org">
+<link rel="help" href="https://dom.spec.whatwg.org/#dom-eventlisteneroptions-capture">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<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>
index 5c6adf5..574fb98 100644 (file)
@@ -1,3 +1,90 @@
+2016-06-07  Chris Dumez  <cdumez@apple.com>
+
+        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-07  Brent Fulgham  <bfulgham@apple.com>
 
         CSP: Content Security Policy directive, upgrade-insecure-requests (UIR)
index 4ca6a04..34d5d0f 100644 (file)
@@ -201,17 +201,17 @@ void AudioScheduledSourceNode::finish()
     }
 }
 
-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;
index cb4dd3c..d5bc696 100644 (file)
@@ -101,8 +101,8 @@ protected:
     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;
 };
 
index 0cc41b7..8bccbbc 100644 (file)
@@ -273,17 +273,17 @@ double ScriptProcessorNode::latencyTime() const
     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;
index 1306259..b4c73bb 100644 (file)
@@ -73,8 +73,8 @@ private:
 
     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
index 355e796..6f1a8c1 100644 (file)
@@ -1678,7 +1678,10 @@ sub GenerateParametersCheckExpression
                 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) {
index 15c335d..e81ffc4 100644 (file)
@@ -172,7 +172,7 @@ public:
     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 @@ public:
     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 @@ private:
     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 };
index b04e121..5a5156f 100644 (file)
@@ -95,9 +95,9 @@ Vector<AtomicString> EventListenerMap::eventTypes() const
     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 @@ static bool addListenerToVector(EventListenerVector* vector, Ref<EventListener>&
     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)
index 1efe6ab..d434bd7 100644 (file)
@@ -54,7 +54,8 @@ public:
     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;
index d248385..b792b28 100644 (file)
@@ -75,28 +75,26 @@ bool EventTarget::isMessagePort() const
     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 @@ bool EventTarget::removeEventListener(const AtomicString& eventType, EventListen
 
     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 @@ bool EventTarget::setAttributeEventListener(const AtomicString& eventType, RefPt
     clearAttributeEventListener(eventType);
     if (!listener)
         return false;
-    return addEventListener(eventType, listener.releaseNonNull(), false);
+    return addEventListener(eventType, listener.releaseNonNull());
 }
 
 EventListener* EventTarget::getAttributeEventListener(const AtomicString& eventType)
@@ -259,7 +257,8 @@ void EventTarget::fireEventListeners(Event& event, EventTargetData* d, EventList
     }
 
     for (; i < size; ++i) {
-        RegisteredEventListener& registeredListener = entry[i];
+        RegisteredEventListener registeredListener = entry[i];
+
         if (event.eventPhase() == Event::CAPTURING_PHASE && !registeredListener.useCapture)
             continue;
         if (event.eventPhase() == Event::BUBBLING_PHASE && registeredListener.useCapture)
@@ -270,12 +269,23 @@ void EventTarget::fireEventListeners(Event& event, EventTargetData* d, EventList
         if (event.immediatePropagationStopped())
             break;
 
+        // Do this before invocation to avoid reentrancy issues.
+        if (registeredListener.isOnce)
+            removeEventListener(event.type(), *registeredListener.listener, ListenerOptions(registeredListener.useCapture));
+
+        if (registeredListener.isPassive)
+            event.setInPassiveListener(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);
     }
+
     d->firingEventIterators->removeLast();
 
     if (document)
index 1b1ded1..b65a144 100644 (file)
@@ -121,10 +121,31 @@ public:
     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 @@ inline bool EventTarget::hasCapturingEventListeners(const AtomicString& eventTyp
     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
index c594589..9d9016f 100644 (file)
     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);
     // 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;
+};
index 3d7847c..fde2c92 100644 (file)
@@ -221,11 +221,11 @@ std::unique_ptr<MessagePortArray> MessagePort::entanglePorts(ScriptExecutionCont
     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
index 9ca124f..6318aa4 100644 (file)
@@ -92,7 +92,7 @@ namespace WebCore {
         // 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&);
index f6f09e8..4f8145b 100644 (file)
@@ -1907,9 +1907,9 @@ void Node::didMoveToNewDocument(Document* oldDocument)
     }
 }
 
-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 @@ static inline bool tryAddEventListener(Node* targetNode, const AtomicString& eve
     // 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 @@ static inline bool tryAddEventListener(Node* targetNode, const AtomicString& eve
     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 @@ static inline bool tryRemoveEventListener(Node* targetNode, const AtomicString&
     // 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 @@ static inline bool tryRemoveEventListener(Node* targetNode, const AtomicString&
     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;
index 3f48fc5..f8d122d 100644 (file)
@@ -507,8 +507,8 @@ public:
     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;
index 3f22264..d55d9e2 100644 (file)
@@ -31,6 +31,26 @@ namespace WebCore {
 
     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,15 @@ namespace WebCore {
         }
 
         RefPtr<EventListener> listener;
-        bool useCapture;
+        bool useCapture { false };
+        bool isPassive { false };
+        bool isOnce { 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;
     }
 
index 893ce06..a1a60fc 100644 (file)
@@ -5228,13 +5228,13 @@ bool HTMLMediaElement::dispatchEvent(Event& event)
     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 @@ bool HTMLMediaElement::addEventListener(const AtomicString& eventType, Ref<Event
     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);
index 5cd0e41..5178e7a 100644 (file)
@@ -342,8 +342,8 @@ public:
 
 #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;
index fb304b4..ff5700c 100644 (file)
@@ -1722,9 +1722,9 @@ bool DOMWindow::isSameSecurityOriginAsMainFrame() const
     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 @@ void DOMWindow::resetAllGeolocationPermission()
 #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()) {
index 0158e14..9b7b7f8 100644 (file)
@@ -279,8 +279,8 @@ namespace WebCore {
 
         // 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;
index ab245d5..5aa917e 100644 (file)
@@ -528,10 +528,10 @@ bool SVGElement::haveLoadedRequiredResources()
     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 @@ bool SVGElement::addEventListener(const AtomicString& eventType, Ref<EventListen
     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 @@ bool SVGElement::removeEventListener(const AtomicString& eventType, EventListene
     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 @@ bool SVGElement::removeEventListener(const AtomicString& eventType, EventListene
     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
index 96d6c90..1ef68b0 100644 (file)
@@ -133,8 +133,8 @@ public:
 
     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)