Source/WebCore/ChangeLog

 12019-03-08 Zalan Bujtas <zalan@apple.com>
 2
 3 [ContentChangeObserver] Start observing for content change between touchEnd and mouseMoved start
 4 https://bugs.webkit.org/show_bug.cgi?id=195510
 5 <rdar://problem/48735695>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 This patch covers the observation of async changes triggered by touchStart/touchEnd (animations, timers, style recalcs).
 10
 11 Test: fast/events/touch/ios/content-observation/visibility-change-after-touch-end.html
 12
 13 * page/ios/ContentChangeObserver.cpp:
 14 (WebCore::ContentChangeObserver::didCancelTouchEvent):
 15 (WebCore::ContentChangeObserver::adjustObservedState):
 16 * page/ios/ContentChangeObserver.h:
 17 (WebCore::ContentChangeObserver::setIsInBetweenTouchEndAndMouseMoved):
 18 (WebCore::ContentChangeObserver::isInBetweenTouchEndAndMouseMoved const):
 19 (WebCore::ContentChangeObserver::isObservingContentChanges const):
 20
1212019-03-08 Zalan Bujtas <zalan@apple.com>
222
323 [ContentChangeObserver] Expand "isConsideredClickable" to descendants

Source/WebKit/ChangeLog

 12019-03-08 Zalan Bujtas <zalan@apple.com>
 2
 3 [ContentChangeObserver] Start observing for content change between touchEnd and mouseMoved start
 4 https://bugs.webkit.org/show_bug.cgi?id=195510
 5 <rdar://problem/48735695>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 * WebProcess/WebPage/ios/WebPageIOS.mm:
 10 (WebKit::WebPage::cancelPotentialTapInFrame):
 11
1122019-03-08 Chris Dumez <cdumez@apple.com>
213
314 Add assertions to help debug a WebProcessCache crash

Source/WebCore/page/ios/ContentChangeObserver.cpp

@@void ContentChangeObserver::didRemoveDOMTimer(const DOMTimer& timer)
8888 adjustObservedState(Event::RemovedDOMTimer);
8989}
9090
 91void ContentChangeObserver::didCancelTouchEvent()
 92{
 93 LOG(ContentObservation, "didCancelTouchEvent: click will not happen.");
 94 setIsInBetweenTouchEndAndMouseMoved(false);
 95 // FIXME: Add support for preventDefault() and long press.
 96}
 97
9198void ContentChangeObserver::domTimerExecuteDidStart(const DOMTimer& timer)
9299{
93100 if (!containsObservedDOMTimer(timer))

@@void ContentChangeObserver::adjustObservedState(Event event)
247254 case Event::StartedTouchStartEventDispatching:
248255 setHasNoChangeState();
249256 clearObservedDOMTimers();
250  m_isMouseMovedPrecededByTouch = true;
251257 setShouldObserveDOMTimerScheduling(true);
252258 break;
253259 case Event::EndedTouchStartEventDispatching:
254260 setShouldObserveDOMTimerScheduling(false);
 261 setIsInBetweenTouchEndAndMouseMoved(true);
255262 break;
256263 case Event::StartedMouseMovedEventDispatching:
257264 ASSERT(!m_document.hasPendingStyleRecalc());
258  if (!m_isMouseMovedPrecededByTouch) {
 265 if (!isInBetweenTouchEndAndMouseMoved()) {
259266 setHasNoChangeState();
260267 clearObservedDOMTimers();
261268 }
 269 setIsInBetweenTouchEndAndMouseMoved(false);
262270 setShouldObserveDOMTimerScheduling(true);
263  m_isMouseMovedPrecededByTouch = false;
264271 break;
265272 case Event::EndedMouseMovedEventDispatching:
266273 setShouldObserveDOMTimerScheduling(false);

Source/WebCore/page/ios/ContentChangeObserver.h

@@public:
4949
5050 void didInstallDOMTimer(const DOMTimer&, Seconds timeout, bool singleShot);
5151 void didRemoveDOMTimer(const DOMTimer&);
 52 WEBCORE_EXPORT void didCancelTouchEvent();
5253 void didSuspendActiveDOMObjects();
5354 void willDetachPage();
5455

@@private:
123124 void setShouldObserveNextStyleRecalc(bool);
124125 bool isWaitingForStyleRecalc() const { return m_isWaitingForStyleRecalc; }
125126
126  bool isObservingContentChanges() const { return m_mouseMovedEventIsBeingDispatched || m_touchEventIsBeingDispatched || m_observedDomTimerIsBeingExecuted || m_isInObservedStyleRecalc || m_contentObservationTimer.isActive(); }
 127 bool isObservingContentChanges() const;
127128
128129 void cancelPendingActivities();
129130

@@private:
135136 bool hasObservedDOMTimer() const { return !m_DOMTimerList.isEmpty(); }
136137 bool hasDeterminateState() const;
137138
 139 void setIsInBetweenTouchEndAndMouseMoved(bool isInbetween) { m_isInBetweenTouchEndAndMouseMoved = isInbetween; }
 140 bool isInBetweenTouchEndAndMouseMoved() const { return m_isInBetweenTouchEndAndMouseMoved; }
 141
138142 bool hasPendingActivity() const { return hasObservedDOMTimer() || m_document.hasPendingStyleRecalc() || isObservationTimeWindowActive(); }
139143 bool isObservationTimeWindowActive() const { return m_contentObservationTimer.isActive(); }
140144#if !ASSERT_DISABLED

@@private:
168172 bool m_isInObservedStyleRecalc { false };
169173 bool m_isObservingDOMTimerScheduling { false };
170174 bool m_observedDomTimerIsBeingExecuted { false };
171  bool m_isMouseMovedPrecededByTouch { false };
172175 bool m_mouseMovedEventIsBeingDispatched { false };
 176 bool m_isInBetweenTouchEndAndMouseMoved { false };
173177};
174178
175179inline void ContentChangeObserver::setHasNoChangeState()

@@inline void ContentChangeObserver::setHasVisibleChangeState()
188192 WKSetObservedContentChange(WKContentVisibilityChange);
189193}
190194
 195inline bool ContentChangeObserver::isObservingContentChanges() const
 196{
 197 return m_touchEventIsBeingDispatched
 198 || isInBetweenTouchEndAndMouseMoved()
 199 || m_mouseMovedEventIsBeingDispatched
 200 || m_observedDomTimerIsBeingExecuted
 201 || m_isInObservedStyleRecalc
 202 || m_contentObservationTimer.isActive();
 203 }
191204}
 205
192206#endif

Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm

@@void WebPage::cancelPotentialTap()
865865void WebPage::cancelPotentialTapInFrame(WebFrame& frame)
866866{
867867 if (m_potentialTapNode) {
868  Frame* potentialTapFrame = m_potentialTapNode->document().frame();
 868 m_potentialTapNode->document().contentChangeObserver().didCancelTouchEvent();
 869 auto* potentialTapFrame = m_potentialTapNode->document().frame();
869870 if (potentialTapFrame && !potentialTapFrame->tree().isDescendantOf(frame.coreFrame()))
870871 return;
871872 }

LayoutTests/ChangeLog

 12019-03-08 Zalan Bujtas <zalan@apple.com>
 2
 3 [ContentChangeObserver] Start observing for content change between touchEnd and mouseMoved start
 4 https://bugs.webkit.org/show_bug.cgi?id=195510
 5 <rdar://problem/48735695>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 * fast/events/touch/ios/content-observation/visibility-change-after-touch-end-expected.txt: Added.
 10 * fast/events/touch/ios/content-observation/visibility-change-after-touch-end.html: Added.
 11
1122019-03-08 Zalan Bujtas <zalan@apple.com>
213
314 [ContentChangeObserver] Expand "isConsideredClickable" to descendants

LayoutTests/fast/events/touch/ios/content-observation/visibility-change-after-touch-end-expected.txt

 1PASS if 'clicked' text is not shown below.
 2

LayoutTests/fast/events/touch/ios/content-observation/visibility-change-after-touch-end.html

 1<html>
 2<head>
 3<title>This tests the case when visible content change happens after touchend but before mouseMoved.</title>
 4<script src="../../../../../resources/basic-gestures.js"></script>
 5<style>
 6#tapthis {
 7 width: 400px;
 8 height: 400px;
 9 border: 1px solid green;
 10}
 11
 12#becomesVisible {
 13 visibility: hidden;
 14 width: 100px;
 15 height: 100px;
 16 background-color: green;
 17}
 18</style>
 19<script>
 20async function test() {
 21 if (!window.testRunner || !testRunner.runUIScript)
 22 return;
 23 if (window.internals)
 24 internals.settings.setContentChangeObserverEnabled(true);
 25
 26 testRunner.waitUntilDone();
 27 testRunner.dumpAsText();
 28
 29 let rect = tapthis.getBoundingClientRect();
 30 let x = rect.left + rect.width / 2;
 31 let y = rect.top + rect.height / 2;
 32
 33 await tapAtPoint(x, y);
 34}
 35</script>
 36</head>
 37<body onload="test()">
 38<div id=tapthis>PASS if 'clicked' text is not shown below.</div>
 39<div id=becomesVisible></div>
 40<pre id=result></pre>
 41<script>
 42tapthis.addEventListener("touchend", function( event ) {
 43 setTimeout(function() {
 44 becomesVisible.style.visibility = "visible";
 45 if (window.testRunner)
 46 testRunner.notifyDone();
 47 }, 0);
 48}, false);
 49
 50becomesVisible.addEventListener("click", function( event ) {
 51 result.innerHTML = "clicked hidden";
 52}, false);
 53
 54tapthis.addEventListener("click", function( event ) {
 55 result.innerHTML = "clicked";
 56}, false);
 57</script>
 58</body>
 59</html>