Skip to content
This repository was archived by the owner on Jun 24, 2022. It is now read-only.

Commit 9c8039a

Browse files
Default passive touch event listeners on the root
https://bugs.webkit.org/show_bug.cgi?id=175346 <rdar://problem/33164597> Reviewed by Sam Weinig. Source/WebCore: Make any touchstart or touchmove event listeners passive by default if they are on the document, window, body or document element targets. This follows the "intervention" first implemented by Chrome/Blink: WICG/interventions#35 https://docs.google.com/document/d/1II7oSIpd8pK91V5kEM3tDLKcIj398jOJn8Niqy6_loI/edit whatwg/dom#365 If the event listener explicitly defines "passive" to false in their options dictionary, then they'll still get a non-passive listener. NOTE: Any fallout from this bug should be collected in: https://bugs.webkit.org/show_bug.cgi?id=175869 Please do not revert this change just because a site is broken. We'll gather the issues and see if we can evangelise or detect via code. Tests: fast/events/touch/ios/passive-by-default-on-document-and-window.html fast/events/touch/ios/passive-by-default-overridden-on-document-and-window.html * dom/EventNames.h: (WebCore::EventNames::isTouchScrollBlockingEventType const): Added this helper to identify the types of touches we want to check for. * dom/EventTarget.cpp: (WebCore::EventTarget::addEventListener): Check for the event being one of the touch-types that we care about, and the target being one of the Node/Window types we care about. If so, tell the event listener to be passive. * dom/EventTarget.h: Use an optional for the passive member. (WebCore::EventTarget::AddEventListenerOptions::AddEventListenerOptions): * dom/EventTarget.idl: Change "passive" to not have a default value, so we can detect if it was explicitly set to false. LayoutTests: * fast/events/touch/ios/passive-by-default-on-document-and-window-expected.txt: Added. * fast/events/touch/ios/passive-by-default-on-document-and-window.html: Added. * fast/events/touch/ios/passive-by-default-overridden-on-document-and-window-expected.txt: Added. * fast/events/touch/ios/passive-by-default-overridden-on-document-and-window.html: Added. * fast/events/touch/ios/tap-with-active-listener-on-window.html: Explicitly set passive to false. * fast/events/touch/ios/touch-event-regions/document.html: Ditto. git-svn-id: http://svn.webkit.org/repository/webkit/trunk@221092 268f45cc-cd09-0410-ab3c-d52691b4dbfc
1 parent adc7028 commit 9c8039a

12 files changed

+299
-6
lines changed

LayoutTests/ChangeLog

+15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
2017-08-22 Dean Jackson <[email protected]>
2+
3+
Default passive touch event listeners on the root
4+
https://bugs.webkit.org/show_bug.cgi?id=175346
5+
<rdar://problem/33164597>
6+
7+
Reviewed by Sam Weinig.
8+
9+
* fast/events/touch/ios/passive-by-default-on-document-and-window-expected.txt: Added.
10+
* fast/events/touch/ios/passive-by-default-on-document-and-window.html: Added.
11+
* fast/events/touch/ios/passive-by-default-overridden-on-document-and-window-expected.txt: Added.
12+
* fast/events/touch/ios/passive-by-default-overridden-on-document-and-window.html: Added.
13+
* fast/events/touch/ios/tap-with-active-listener-on-window.html: Explicitly set passive to false.
14+
* fast/events/touch/ios/touch-event-regions/document.html: Ditto.
15+
116
2017-08-23 Matt Lewis <[email protected]>
217

318
Marked imported/w3c/web-platform-tests/html/webappapis/timers/type-long-settimeout.html as flaky.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
touchstart on body - cancelable: false defaultPrevented: false
2+
touchstart on documentElement - cancelable: false defaultPrevented: false
3+
touchstart on document - cancelable: false defaultPrevented: false
4+
touchstart on window - cancelable: false defaultPrevented: false
5+
touchmove on body - cancelable: false defaultPrevented: false
6+
touchmove on documentElement - cancelable: false defaultPrevented: false
7+
touchmove on document - cancelable: false defaultPrevented: false
8+
touchmove on window - cancelable: false defaultPrevented: false
9+
touchend on body - cancelable: false defaultPrevented: false
10+
touchend on documentElement - cancelable: false defaultPrevented: false
11+
touchend on document - cancelable: false defaultPrevented: false
12+
touchend on window - cancelable: false defaultPrevented: false
13+
Done
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<script>
2+
if (window.testRunner) {
3+
testRunner.dumpAsText();
4+
testRunner.waitUntilDone();
5+
}
6+
7+
function getUIScript()
8+
{
9+
return `
10+
(function() {
11+
uiController.dragFromPointToPoint(50, 250, 50, 30, 0.1, function() {
12+
uiController.uiScriptComplete("Done");
13+
});
14+
})();`
15+
}
16+
17+
function runTest()
18+
{
19+
let output = "";
20+
window.addEventListener("touchstart", function(event) {
21+
event.preventDefault();
22+
output += `touchstart on window - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
23+
}, false);
24+
25+
window.addEventListener("touchmove", function(event) {
26+
event.preventDefault();
27+
output += `touchmove on window - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
28+
}, false);
29+
30+
window.addEventListener("touchend", function(event) {
31+
event.preventDefault();
32+
output += `touchend on window - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
33+
}, false);
34+
35+
document.addEventListener("touchstart", function(event) {
36+
event.preventDefault();
37+
output += `touchstart on document - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
38+
}, false);
39+
40+
document.addEventListener("touchmove", function(event) {
41+
event.preventDefault();
42+
output += `touchmove on document - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
43+
}, false);
44+
45+
document.addEventListener("touchend", function(event) {
46+
event.preventDefault();
47+
output += `touchend on document - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
48+
}, false);
49+
50+
document.documentElement.addEventListener("touchstart", function(event) {
51+
event.preventDefault();
52+
output += `touchstart on documentElement - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
53+
}, false);
54+
55+
document.documentElement.addEventListener("touchmove", function(event) {
56+
event.preventDefault();
57+
output += `touchmove on documentElement - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
58+
}, false);
59+
60+
document.documentElement.addEventListener("touchend", function(event) {
61+
event.preventDefault();
62+
output += `touchend on documentElement - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
63+
}, false);
64+
65+
let body = document.querySelector("body");
66+
67+
body.addEventListener("touchstart", function(event) {
68+
event.preventDefault();
69+
output += `touchstart on body - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
70+
}, false);
71+
72+
body.addEventListener("touchmove", function(event) {
73+
event.preventDefault();
74+
output += `touchmove on body - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
75+
}, false);
76+
77+
body.addEventListener("touchend", function(event) {
78+
event.preventDefault();
79+
output += `touchend on body - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
80+
}, false);
81+
82+
if (testRunner.runUIScript) {
83+
testRunner.runUIScript(getUIScript(), function(result) {
84+
output += result;
85+
document.getElementById("output").innerHTML = output;
86+
testRunner.notifyDone();
87+
});
88+
}
89+
}
90+
91+
window.addEventListener('load', runTest, false);
92+
</script>
93+
<body style="height: 500vh">
94+
<div id=output>
95+
This test requires UIScriptController to run.
96+
</div>
97+
</body>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
touchstart on body - cancelable: true defaultPrevented: true
2+
touchstart on documentElement - cancelable: true defaultPrevented: true
3+
touchstart on document - cancelable: true defaultPrevented: true
4+
touchstart on window - cancelable: true defaultPrevented: true
5+
touchmove on body - cancelable: true defaultPrevented: true
6+
touchmove on documentElement - cancelable: true defaultPrevented: true
7+
touchmove on document - cancelable: true defaultPrevented: true
8+
touchmove on window - cancelable: true defaultPrevented: true
9+
touchend on body - cancelable: true defaultPrevented: true
10+
touchend on documentElement - cancelable: true defaultPrevented: true
11+
touchend on document - cancelable: true defaultPrevented: true
12+
touchend on window - cancelable: true defaultPrevented: true
13+
Done
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<script>
2+
if (window.testRunner) {
3+
testRunner.dumpAsText();
4+
testRunner.waitUntilDone();
5+
}
6+
7+
function getUIScript()
8+
{
9+
return `
10+
(function() {
11+
uiController.dragFromPointToPoint(50, 250, 50, 30, 0.1, function() {
12+
uiController.uiScriptComplete("Done");
13+
});
14+
})();`
15+
}
16+
17+
function runTest()
18+
{
19+
let output = "";
20+
window.addEventListener("touchstart", function(event) {
21+
event.preventDefault();
22+
output += `touchstart on window - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
23+
}, { "passive": false });
24+
25+
window.addEventListener("touchmove", function(event) {
26+
event.preventDefault();
27+
output += `touchmove on window - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
28+
}, { "passive": false });
29+
30+
window.addEventListener("touchend", function(event) {
31+
event.preventDefault();
32+
output += `touchend on window - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
33+
}, { "passive": false });
34+
35+
document.addEventListener("touchstart", function(event) {
36+
event.preventDefault();
37+
output += `touchstart on document - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
38+
}, { "passive": false });
39+
40+
document.addEventListener("touchmove", function(event) {
41+
event.preventDefault();
42+
output += `touchmove on document - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
43+
}, { "passive": false });
44+
45+
document.addEventListener("touchend", function(event) {
46+
event.preventDefault();
47+
output += `touchend on document - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
48+
}, { "passive": false });
49+
50+
document.documentElement.addEventListener("touchstart", function(event) {
51+
event.preventDefault();
52+
output += `touchstart on documentElement - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
53+
}, { "passive": false });
54+
55+
document.documentElement.addEventListener("touchmove", function(event) {
56+
event.preventDefault();
57+
output += `touchmove on documentElement - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
58+
}, { "passive": false });
59+
60+
document.documentElement.addEventListener("touchend", function(event) {
61+
event.preventDefault();
62+
output += `touchend on documentElement - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
63+
}, { "passive": false });
64+
65+
let body = document.querySelector("body");
66+
67+
body.addEventListener("touchstart", function(event) {
68+
event.preventDefault();
69+
output += `touchstart on body - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
70+
}, { "passive": false });
71+
72+
body.addEventListener("touchmove", function(event) {
73+
event.preventDefault();
74+
output += `touchmove on body - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
75+
}, { "passive": false });
76+
77+
body.addEventListener("touchend", function(event) {
78+
event.preventDefault();
79+
output += `touchend on body - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
80+
}, { "passive": false });
81+
82+
if (testRunner.runUIScript) {
83+
testRunner.runUIScript(getUIScript(), function(result) {
84+
output += result;
85+
document.getElementById("output").innerHTML = output;
86+
testRunner.notifyDone();
87+
});
88+
}
89+
}
90+
91+
window.addEventListener('load', runTest, false);
92+
</script>
93+
<body style="height: 500vh">
94+
<div id=output>
95+
This test requires UIScriptController to run.
96+
</div>
97+
</body>

LayoutTests/fast/events/touch/ios/tap-with-active-listener-on-window.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
window.addEventListener('touchstart', function(event) {
2525
output += 'Received' + (event.cancelable ? ' cancelable' : '') + ' event ' + event.type + ' at ' + event.touches[0].clientX + ', ' + event.touches[0].clientY + '<br>';
2626
event.preventDefault();
27-
});
27+
}, { passive: false });
2828

2929
window.addEventListener('touchend', function(event) {
3030
output += 'Received' + (event.cancelable ? ' cancelable' : '') + ' event ' + event.type + '<br>';

LayoutTests/fast/events/touch/ios/touch-event-regions/document.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
</head>
1414
<body>
1515
<script>
16-
document.addEventListener('touchstart', function() { });
16+
document.addEventListener('touchstart', function() { }, { passive: false });
1717
</script>
1818
</body>
1919
</html>

Source/WebCore/ChangeLog

+39
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,42 @@
1+
2017-08-22 Dean Jackson <[email protected]>
2+
3+
Default passive touch event listeners on the root
4+
https://bugs.webkit.org/show_bug.cgi?id=175346
5+
<rdar://problem/33164597>
6+
7+
Reviewed by Sam Weinig.
8+
9+
Make any touchstart or touchmove event listeners passive by default
10+
if they are on the document, window, body or document element targets.
11+
This follows the "intervention" first implemented by Chrome/Blink:
12+
13+
https://github.com/WICG/interventions/issues/35
14+
https://docs.google.com/document/d/1II7oSIpd8pK91V5kEM3tDLKcIj398jOJn8Niqy6_loI/edit
15+
https://github.com/whatwg/dom/issues/365
16+
17+
If the event listener explicitly defines "passive" to false in their
18+
options dictionary, then they'll still get a non-passive listener.
19+
20+
NOTE: Any fallout from this bug should be collected in:
21+
https://bugs.webkit.org/show_bug.cgi?id=175869
22+
Please do not revert this change just because a site is broken. We'll
23+
gather the issues and see if we can evangelise or detect via code.
24+
25+
Tests: fast/events/touch/ios/passive-by-default-on-document-and-window.html
26+
fast/events/touch/ios/passive-by-default-overridden-on-document-and-window.html
27+
28+
* dom/EventNames.h:
29+
(WebCore::EventNames::isTouchScrollBlockingEventType const): Added this helper
30+
to identify the types of touches we want to check for.
31+
* dom/EventTarget.cpp:
32+
(WebCore::EventTarget::addEventListener): Check for the event being one of the touch-types
33+
that we care about, and the target being one of the Node/Window types we care about. If
34+
so, tell the event listener to be passive.
35+
* dom/EventTarget.h: Use an optional for the passive member.
36+
(WebCore::EventTarget::AddEventListenerOptions::AddEventListenerOptions):
37+
* dom/EventTarget.idl: Change "passive" to not have a default value, so we
38+
can detect if it was explicitly set to false.
39+
140
2017-08-23 Tim Horton <[email protected]>
241

342
Try to fix the WinCairo build after r221068

Source/WebCore/dom/EventNames.h

+7
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ struct EventNames {
324324
bool isWheelEventType(const AtomicString& eventType) const;
325325
bool isGestureEventType(const AtomicString& eventType) const;
326326
bool isTouchEventType(const AtomicString& eventType) const;
327+
bool isTouchScrollBlockingEventType(const AtomicString& eventType) const;
327328
#if ENABLE(GAMEPAD)
328329
bool isGamepadEventType(const AtomicString& eventType) const;
329330
#endif
@@ -350,6 +351,12 @@ inline bool EventNames::isGestureEventType(const AtomicString& eventType) const
350351
return eventType == gesturestartEvent || eventType == gesturechangeEvent || eventType == gestureendEvent;
351352
}
352353

354+
inline bool EventNames::isTouchScrollBlockingEventType(const AtomicString& eventType) const
355+
{
356+
return eventType == touchstartEvent
357+
|| eventType == touchmoveEvent;
358+
}
359+
353360
inline bool EventNames::isTouchEventType(const AtomicString& eventType) const
354361
{
355362
return eventType == touchstartEvent

Source/WebCore/dom/EventTarget.cpp

+13-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
#include "DOMWrapperWorld.h"
3636
#include "EventNames.h"
37+
#include "HTMLBodyElement.h"
3738
#include "InspectorInstrumentation.h"
3839
#include "JSEventListener.h"
3940
#include "NoEventDispatchAssertion.h"
@@ -68,9 +69,20 @@ bool EventTarget::isMessagePort() const
6869

6970
bool EventTarget::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
7071
{
72+
auto passive = options.passive;
73+
74+
if (!passive.has_value() && eventNames().isTouchScrollBlockingEventType(eventType)) {
75+
if (toDOMWindow())
76+
passive = true;
77+
else if (auto* node = toNode()) {
78+
if (node->isDocumentNode() || node->document().documentElement() == node || node->document().body() == node)
79+
passive = true;
80+
}
81+
}
82+
7183
bool listenerCreatedFromScript = listener->type() == EventListener::JSEventListenerType && !listener->wasCreatedFromMarkup();
7284

73-
if (!ensureEventTargetData().eventListenerMap.add(eventType, WTFMove(listener), { options.capture, options.passive, options.once }))
85+
if (!ensureEventTargetData().eventListenerMap.add(eventType, WTFMove(listener), { options.capture, passive.value_or(false), options.once }))
7486
return false;
7587

7688
if (listenerCreatedFromScript)

Source/WebCore/dom/EventTarget.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,13 @@ class EventTarget : public ScriptWrappable {
8181
};
8282

8383
struct AddEventListenerOptions : ListenerOptions {
84-
AddEventListenerOptions(bool capture = false, bool passive = false, bool once = false)
84+
AddEventListenerOptions(bool capture = false, std::optional<bool> passive = std::nullopt, bool once = false)
8585
: ListenerOptions(capture)
8686
, passive(passive)
8787
, once(once)
8888
{ }
8989

90-
bool passive;
90+
std::optional<bool> passive;
9191
bool once;
9292
};
9393

Source/WebCore/dom/EventTarget.idl

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,6 @@ dictionary EventListenerOptions {
3636
};
3737

3838
dictionary AddEventListenerOptions : EventListenerOptions {
39-
boolean passive = false;
39+
boolean passive;
4040
boolean once = false;
4141
};

0 commit comments

Comments
 (0)