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

Commit

Permalink
Default passive touch event listeners on the root
Browse files Browse the repository at this point in the history
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
  • Loading branch information
dino@apple.com committed Aug 23, 2017
1 parent adc7028 commit 9c8039a
Show file tree
Hide file tree
Showing 12 changed files with 299 additions and 6 deletions.
15 changes: 15 additions & 0 deletions LayoutTests/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
2017-08-22 Dean Jackson <dino@apple.com>

Default passive touch event listeners on the root
https://bugs.webkit.org/show_bug.cgi?id=175346
<rdar://problem/33164597>

Reviewed by Sam Weinig.

* 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.

2017-08-23 Matt Lewis <jlewis3@apple.com>

Marked imported/w3c/web-platform-tests/html/webappapis/timers/type-long-settimeout.html as flaky.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
touchstart on body - cancelable: false defaultPrevented: false
touchstart on documentElement - cancelable: false defaultPrevented: false
touchstart on document - cancelable: false defaultPrevented: false
touchstart on window - cancelable: false defaultPrevented: false
touchmove on body - cancelable: false defaultPrevented: false
touchmove on documentElement - cancelable: false defaultPrevented: false
touchmove on document - cancelable: false defaultPrevented: false
touchmove on window - cancelable: false defaultPrevented: false
touchend on body - cancelable: false defaultPrevented: false
touchend on documentElement - cancelable: false defaultPrevented: false
touchend on document - cancelable: false defaultPrevented: false
touchend on window - cancelable: false defaultPrevented: false
Done
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<script>
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
}

function getUIScript()
{
return `
(function() {
uiController.dragFromPointToPoint(50, 250, 50, 30, 0.1, function() {
uiController.uiScriptComplete("Done");
});
})();`
}

function runTest()
{
let output = "";
window.addEventListener("touchstart", function(event) {
event.preventDefault();
output += `touchstart on window - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, false);

window.addEventListener("touchmove", function(event) {
event.preventDefault();
output += `touchmove on window - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, false);

window.addEventListener("touchend", function(event) {
event.preventDefault();
output += `touchend on window - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, false);

document.addEventListener("touchstart", function(event) {
event.preventDefault();
output += `touchstart on document - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, false);

document.addEventListener("touchmove", function(event) {
event.preventDefault();
output += `touchmove on document - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, false);

document.addEventListener("touchend", function(event) {
event.preventDefault();
output += `touchend on document - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, false);

document.documentElement.addEventListener("touchstart", function(event) {
event.preventDefault();
output += `touchstart on documentElement - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, false);

document.documentElement.addEventListener("touchmove", function(event) {
event.preventDefault();
output += `touchmove on documentElement - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, false);

document.documentElement.addEventListener("touchend", function(event) {
event.preventDefault();
output += `touchend on documentElement - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, false);

let body = document.querySelector("body");

body.addEventListener("touchstart", function(event) {
event.preventDefault();
output += `touchstart on body - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, false);

body.addEventListener("touchmove", function(event) {
event.preventDefault();
output += `touchmove on body - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, false);

body.addEventListener("touchend", function(event) {
event.preventDefault();
output += `touchend on body - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, false);

if (testRunner.runUIScript) {
testRunner.runUIScript(getUIScript(), function(result) {
output += result;
document.getElementById("output").innerHTML = output;
testRunner.notifyDone();
});
}
}

window.addEventListener('load', runTest, false);
</script>
<body style="height: 500vh">
<div id=output>
This test requires UIScriptController to run.
</div>
</body>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
touchstart on body - cancelable: true defaultPrevented: true
touchstart on documentElement - cancelable: true defaultPrevented: true
touchstart on document - cancelable: true defaultPrevented: true
touchstart on window - cancelable: true defaultPrevented: true
touchmove on body - cancelable: true defaultPrevented: true
touchmove on documentElement - cancelable: true defaultPrevented: true
touchmove on document - cancelable: true defaultPrevented: true
touchmove on window - cancelable: true defaultPrevented: true
touchend on body - cancelable: true defaultPrevented: true
touchend on documentElement - cancelable: true defaultPrevented: true
touchend on document - cancelable: true defaultPrevented: true
touchend on window - cancelable: true defaultPrevented: true
Done
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<script>
if (window.testRunner) {
testRunner.dumpAsText();
testRunner.waitUntilDone();
}

function getUIScript()
{
return `
(function() {
uiController.dragFromPointToPoint(50, 250, 50, 30, 0.1, function() {
uiController.uiScriptComplete("Done");
});
})();`
}

function runTest()
{
let output = "";
window.addEventListener("touchstart", function(event) {
event.preventDefault();
output += `touchstart on window - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, { "passive": false });

window.addEventListener("touchmove", function(event) {
event.preventDefault();
output += `touchmove on window - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, { "passive": false });

window.addEventListener("touchend", function(event) {
event.preventDefault();
output += `touchend on window - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, { "passive": false });

document.addEventListener("touchstart", function(event) {
event.preventDefault();
output += `touchstart on document - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, { "passive": false });

document.addEventListener("touchmove", function(event) {
event.preventDefault();
output += `touchmove on document - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, { "passive": false });

document.addEventListener("touchend", function(event) {
event.preventDefault();
output += `touchend on document - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, { "passive": false });

document.documentElement.addEventListener("touchstart", function(event) {
event.preventDefault();
output += `touchstart on documentElement - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, { "passive": false });

document.documentElement.addEventListener("touchmove", function(event) {
event.preventDefault();
output += `touchmove on documentElement - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, { "passive": false });

document.documentElement.addEventListener("touchend", function(event) {
event.preventDefault();
output += `touchend on documentElement - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, { "passive": false });

let body = document.querySelector("body");

body.addEventListener("touchstart", function(event) {
event.preventDefault();
output += `touchstart on body - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, { "passive": false });

body.addEventListener("touchmove", function(event) {
event.preventDefault();
output += `touchmove on body - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, { "passive": false });

body.addEventListener("touchend", function(event) {
event.preventDefault();
output += `touchend on body - cancelable: ${event.cancelable} defaultPrevented: ${event.defaultPrevented} <br>`;
}, { "passive": false });

if (testRunner.runUIScript) {
testRunner.runUIScript(getUIScript(), function(result) {
output += result;
document.getElementById("output").innerHTML = output;
testRunner.notifyDone();
});
}
}

window.addEventListener('load', runTest, false);
</script>
<body style="height: 500vh">
<div id=output>
This test requires UIScriptController to run.
</div>
</body>
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
window.addEventListener('touchstart', function(event) {
output += 'Received' + (event.cancelable ? ' cancelable' : '') + ' event ' + event.type + ' at ' + event.touches[0].clientX + ', ' + event.touches[0].clientY + '<br>';
event.preventDefault();
});
}, { passive: false });

window.addEventListener('touchend', function(event) {
output += 'Received' + (event.cancelable ? ' cancelable' : '') + ' event ' + event.type + '<br>';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</head>
<body>
<script>
document.addEventListener('touchstart', function() { });
document.addEventListener('touchstart', function() { }, { passive: false });
</script>
</body>
</html>
39 changes: 39 additions & 0 deletions Source/WebCore/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,42 @@
2017-08-22 Dean Jackson <dino@apple.com>

Default passive touch event listeners on the root
https://bugs.webkit.org/show_bug.cgi?id=175346
<rdar://problem/33164597>

Reviewed by Sam Weinig.

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:

https://github.com/WICG/interventions/issues/35
https://docs.google.com/document/d/1II7oSIpd8pK91V5kEM3tDLKcIj398jOJn8Niqy6_loI/edit
https://github.com/whatwg/dom/issues/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.

2017-08-23 Tim Horton <timothy_horton@apple.com>

Try to fix the WinCairo build after r221068
Expand Down
7 changes: 7 additions & 0 deletions Source/WebCore/dom/EventNames.h
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ struct EventNames {
bool isWheelEventType(const AtomicString& eventType) const;
bool isGestureEventType(const AtomicString& eventType) const;
bool isTouchEventType(const AtomicString& eventType) const;
bool isTouchScrollBlockingEventType(const AtomicString& eventType) const;
#if ENABLE(GAMEPAD)
bool isGamepadEventType(const AtomicString& eventType) const;
#endif
Expand All @@ -350,6 +351,12 @@ inline bool EventNames::isGestureEventType(const AtomicString& eventType) const
return eventType == gesturestartEvent || eventType == gesturechangeEvent || eventType == gestureendEvent;
}

inline bool EventNames::isTouchScrollBlockingEventType(const AtomicString& eventType) const
{
return eventType == touchstartEvent
|| eventType == touchmoveEvent;
}

inline bool EventNames::isTouchEventType(const AtomicString& eventType) const
{
return eventType == touchstartEvent
Expand Down
14 changes: 13 additions & 1 deletion Source/WebCore/dom/EventTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

#include "DOMWrapperWorld.h"
#include "EventNames.h"
#include "HTMLBodyElement.h"
#include "InspectorInstrumentation.h"
#include "JSEventListener.h"
#include "NoEventDispatchAssertion.h"
Expand Down Expand Up @@ -68,9 +69,20 @@ bool EventTarget::isMessagePort() const

bool EventTarget::addEventListener(const AtomicString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
{
auto passive = options.passive;

if (!passive.has_value() && eventNames().isTouchScrollBlockingEventType(eventType)) {
if (toDOMWindow())
passive = true;
else if (auto* node = toNode()) {
if (node->isDocumentNode() || node->document().documentElement() == node || node->document().body() == node)
passive = true;
}
}

bool listenerCreatedFromScript = listener->type() == EventListener::JSEventListenerType && !listener->wasCreatedFromMarkup();

if (!ensureEventTargetData().eventListenerMap.add(eventType, WTFMove(listener), { options.capture, options.passive, options.once }))
if (!ensureEventTargetData().eventListenerMap.add(eventType, WTFMove(listener), { options.capture, passive.value_or(false), options.once }))
return false;

if (listenerCreatedFromScript)
Expand Down
4 changes: 2 additions & 2 deletions Source/WebCore/dom/EventTarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,13 @@ class EventTarget : public ScriptWrappable {
};

struct AddEventListenerOptions : ListenerOptions {
AddEventListenerOptions(bool capture = false, bool passive = false, bool once = false)
AddEventListenerOptions(bool capture = false, std::optional<bool> passive = std::nullopt, bool once = false)
: ListenerOptions(capture)
, passive(passive)
, once(once)
{ }

bool passive;
std::optional<bool> passive;
bool once;
};

Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/dom/EventTarget.idl
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,6 @@ dictionary EventListenerOptions {
};

dictionary AddEventListenerOptions : EventListenerOptions {
boolean passive = false;
boolean passive;
boolean once = false;
};

0 comments on commit 9c8039a

Please sign in to comment.