[ContentChangeObserver] Start tracking implicit transitions at touchStart
authorzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 21 Mar 2019 18:49:52 +0000 (18:49 +0000)
committerzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 21 Mar 2019 18:49:52 +0000 (18:49 +0000)
https://bugs.webkit.org/show_bug.cgi?id=196051
<rdar://problem/49092952>

Reviewed by Simon Fraser.

Source/WebCore:

This patch enables transition tracking on touchStart.

1. Start observing for new transitions at touchStart
2. Stop observing at touchEnd
3. Check the next style recalc when the transition is finished (at onAnimationEnd we don't yet have the final computed style).
4. Remove the tracked transition when it is canceled.

Tests: fast/events/touch/ios/content-observation/0ms-transition-on-touch-start.html
       fast/events/touch/ios/content-observation/100ms-transition-on-touch-start.html
       fast/events/touch/ios/content-observation/10ms-delay-transition-on-touch-start.html
       fast/events/touch/ios/content-observation/transition-on-touch-start-and-remove.html

* page/ios/ContentChangeObserver.cpp:
(WebCore::ContentChangeObserver::didAddTransition):
(WebCore::ContentChangeObserver::didFinishTransition):
(WebCore::ContentChangeObserver::didRemoveTransition):
(WebCore::ContentChangeObserver::adjustObservedState):
* page/ios/ContentChangeObserver.h:
(WebCore::ContentChangeObserver::setShouldObserveTransitions):
(WebCore::ContentChangeObserver::clearObservedTransitions):
(WebCore::ContentChangeObserver::hasObservedTransition const):
(WebCore::ContentChangeObserver::hasPendingActivity const):

LayoutTests:

* fast/events/touch/ios/content-observation/0ms-transition-on-touch-start-expected.txt: Added.
* fast/events/touch/ios/content-observation/0ms-transition-on-touch-start.html: Added.
* fast/events/touch/ios/content-observation/100ms-transition-on-touch-start-expected.txt: Added.
* fast/events/touch/ios/content-observation/100ms-transition-on-touch-start.html: Added.
* fast/events/touch/ios/content-observation/10ms-delay-transition-on-touch-start-expected.txt: Added.
* fast/events/touch/ios/content-observation/10ms-delay-transition-on-touch-start.html: Added.
* fast/events/touch/ios/content-observation/transition-on-touch-start-and-remove-expected.txt: Added.
* fast/events/touch/ios/content-observation/transition-on-touch-start-and-remove.html: Added.

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

12 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/events/touch/ios/content-observation/0ms-transition-on-touch-start-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/touch/ios/content-observation/0ms-transition-on-touch-start.html [new file with mode: 0644]
LayoutTests/fast/events/touch/ios/content-observation/100ms-transition-on-touch-start-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/touch/ios/content-observation/100ms-transition-on-touch-start.html [new file with mode: 0644]
LayoutTests/fast/events/touch/ios/content-observation/10ms-delay-transition-on-touch-start-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/touch/ios/content-observation/10ms-delay-transition-on-touch-start.html [new file with mode: 0644]
LayoutTests/fast/events/touch/ios/content-observation/transition-on-touch-start-and-remove-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/touch/ios/content-observation/transition-on-touch-start-and-remove.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/page/ios/ContentChangeObserver.cpp
Source/WebCore/page/ios/ContentChangeObserver.h

index 24fbfed..57fef80 100644 (file)
@@ -1,3 +1,20 @@
+2019-03-21  Zalan Bujtas  <zalan@apple.com>
+
+        [ContentChangeObserver] Start tracking implicit transitions at touchStart
+        https://bugs.webkit.org/show_bug.cgi?id=196051
+        <rdar://problem/49092952>
+
+        Reviewed by Simon Fraser.
+
+        * fast/events/touch/ios/content-observation/0ms-transition-on-touch-start-expected.txt: Added.
+        * fast/events/touch/ios/content-observation/0ms-transition-on-touch-start.html: Added.
+        * fast/events/touch/ios/content-observation/100ms-transition-on-touch-start-expected.txt: Added.
+        * fast/events/touch/ios/content-observation/100ms-transition-on-touch-start.html: Added.
+        * fast/events/touch/ios/content-observation/10ms-delay-transition-on-touch-start-expected.txt: Added.
+        * fast/events/touch/ios/content-observation/10ms-delay-transition-on-touch-start.html: Added.
+        * fast/events/touch/ios/content-observation/transition-on-touch-start-and-remove-expected.txt: Added.
+        * fast/events/touch/ios/content-observation/transition-on-touch-start-and-remove.html: Added.
+
 2019-03-21  Shawn Roberts  <sroberts@apple.com>
 
         Unreviewed, rolling out r243250.
diff --git a/LayoutTests/fast/events/touch/ios/content-observation/0ms-transition-on-touch-start-expected.txt b/LayoutTests/fast/events/touch/ios/content-observation/0ms-transition-on-touch-start-expected.txt
new file mode 100644 (file)
index 0000000..66db4c9
--- /dev/null
@@ -0,0 +1,2 @@
+PASS if 'clicked' text is not shown below.
+
diff --git a/LayoutTests/fast/events/touch/ios/content-observation/0ms-transition-on-touch-start.html b/LayoutTests/fast/events/touch/ios/content-observation/0ms-transition-on-touch-start.html
new file mode 100644 (file)
index 0000000..6101a5f
--- /dev/null
@@ -0,0 +1,60 @@
+<html>
+<head>
+<title>This tests the case when touchEnd triggers a 0ms transition.</title>
+<script src="../../../../../resources/basic-gestures.js"></script>
+<style>
+#tapthis {
+    width: 400px;
+    height: 400px;
+    border: 1px solid green;
+}
+
+#becomesVisible {
+       position: absolute;
+       top: 100px;
+       left: -1000px;
+       width: 100px;
+       height: 100px;
+       background-color: green;
+       transition: left 0ms ease-in-out 0ms;
+}
+</style>
+<script>
+async function test() {
+    if (!window.testRunner || !testRunner.runUIScript)
+        return;
+    if (window.internals)
+        internals.settings.setContentChangeObserverEnabled(true);
+
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+
+    let rect = tapthis.getBoundingClientRect();
+    let x = rect.left + rect.width / 2;
+    let y = rect.top + rect.height / 2;
+
+    await tapAtPoint(x, y);
+}
+</script>
+</head>
+<body onload="test()">
+<div id=tapthis>PASS if 'clicked' text is not shown below.</div>
+<div id=becomesVisible></div>
+<pre id=result></pre>
+<script>
+tapthis.addEventListener("touchstart", function( event ) {
+    becomesVisible.style.left = "10px";
+    if (window.testRunner)
+        testRunner.notifyDone();
+}, false);
+
+becomesVisible.addEventListener("click", function( event ) {   
+    result.innerHTML = "clicked hidden";
+}, false);
+
+tapthis.addEventListener("click", function( event ) {   
+    result.innerHTML = "clicked";
+}, false);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/fast/events/touch/ios/content-observation/100ms-transition-on-touch-start-expected.txt b/LayoutTests/fast/events/touch/ios/content-observation/100ms-transition-on-touch-start-expected.txt
new file mode 100644 (file)
index 0000000..66db4c9
--- /dev/null
@@ -0,0 +1,2 @@
+PASS if 'clicked' text is not shown below.
+
diff --git a/LayoutTests/fast/events/touch/ios/content-observation/100ms-transition-on-touch-start.html b/LayoutTests/fast/events/touch/ios/content-observation/100ms-transition-on-touch-start.html
new file mode 100644 (file)
index 0000000..f0ee86e
--- /dev/null
@@ -0,0 +1,60 @@
+<html>
+<head>
+<title>This tests the case when touchEnd triggers a non-0ms transition.</title>
+<script src="../../../../../resources/basic-gestures.js"></script>
+<style>
+#tapthis {
+    width: 400px;
+    height: 400px;
+    border: 1px solid green;
+}
+
+#becomesVisible {
+       position: absolute;
+       top: 100px;
+       left: -1000px;
+       width: 100px;
+       height: 100px;
+       background-color: green;
+       transition: left 100ms ease-in-out 0ms;
+}
+</style>
+<script>
+async function test() {
+    if (!window.testRunner || !testRunner.runUIScript)
+        return;
+    if (window.internals)
+        internals.settings.setContentChangeObserverEnabled(true);
+
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+
+    let rect = tapthis.getBoundingClientRect();
+    let x = rect.left + rect.width / 2;
+    let y = rect.top + rect.height / 2;
+
+    await tapAtPoint(x, y);
+}
+</script>
+</head>
+<body onload="test()">
+<div id=tapthis>PASS if 'clicked' text is not shown below.</div>
+<div id=becomesVisible></div>
+<pre id=result></pre>
+<script>
+tapthis.addEventListener("touchstart", function( event ) {
+    becomesVisible.style.left = "10px";
+    if (window.testRunner)
+        testRunner.notifyDone();
+}, false);
+
+becomesVisible.addEventListener("click", function( event ) {   
+    result.innerHTML = "clicked hidden";
+}, false);
+
+tapthis.addEventListener("click", function( event ) {   
+    result.innerHTML = "clicked";
+}, false);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/fast/events/touch/ios/content-observation/10ms-delay-transition-on-touch-start-expected.txt b/LayoutTests/fast/events/touch/ios/content-observation/10ms-delay-transition-on-touch-start-expected.txt
new file mode 100644 (file)
index 0000000..66db4c9
--- /dev/null
@@ -0,0 +1,2 @@
+PASS if 'clicked' text is not shown below.
+
diff --git a/LayoutTests/fast/events/touch/ios/content-observation/10ms-delay-transition-on-touch-start.html b/LayoutTests/fast/events/touch/ios/content-observation/10ms-delay-transition-on-touch-start.html
new file mode 100644 (file)
index 0000000..e2894cd
--- /dev/null
@@ -0,0 +1,60 @@
+<html>
+<head>
+<title>This tests the case when touchEnd triggers a non-0ms transition with delay.</title>
+<script src="../../../../../resources/basic-gestures.js"></script>
+<style>
+#tapthis {
+    width: 400px;
+    height: 400px;
+    border: 1px solid green;
+}
+
+#becomesVisible {
+       position: absolute;
+       top: 100px;
+       left: -1000px;
+       width: 100px;
+       height: 100px;
+       background-color: green;
+       transition: left 10ms ease-in-out 10ms;
+}
+</style>
+<script>
+async function test() {
+    if (!window.testRunner || !testRunner.runUIScript)
+        return;
+    if (window.internals)
+        internals.settings.setContentChangeObserverEnabled(true);
+
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+
+    let rect = tapthis.getBoundingClientRect();
+    let x = rect.left + rect.width / 2;
+    let y = rect.top + rect.height / 2;
+
+    await tapAtPoint(x, y);
+}
+</script>
+</head>
+<body onload="test()">
+<div id=tapthis>PASS if 'clicked' text is not shown below.</div>
+<div id=becomesVisible></div>
+<pre id=result></pre>
+<script>
+tapthis.addEventListener("touchstart", function( event ) {
+    becomesVisible.style.left = "10px";
+    if (window.testRunner)
+        testRunner.notifyDone();
+}, false);
+
+becomesVisible.addEventListener("click", function( event ) {   
+    result.innerHTML = "clicked hidden";
+}, false);
+
+tapthis.addEventListener("click", function( event ) {   
+    result.innerHTML = "clicked";
+}, false);
+</script>
+</body>
+</html>
diff --git a/LayoutTests/fast/events/touch/ios/content-observation/transition-on-touch-start-and-remove-expected.txt b/LayoutTests/fast/events/touch/ios/content-observation/transition-on-touch-start-and-remove-expected.txt
new file mode 100644 (file)
index 0000000..677e3a3
--- /dev/null
@@ -0,0 +1,2 @@
+PASS if 'clicked' text is shown below.
+clicked
diff --git a/LayoutTests/fast/events/touch/ios/content-observation/transition-on-touch-start-and-remove.html b/LayoutTests/fast/events/touch/ios/content-observation/transition-on-touch-start-and-remove.html
new file mode 100644 (file)
index 0000000..4bdaded
--- /dev/null
@@ -0,0 +1,60 @@
+<html>
+<head>
+<title>This tests the case when touchEnd triggers a transition and the element is removed.</title>
+<script src="../../../../../resources/basic-gestures.js"></script>
+<style>
+#tapthis {
+    width: 400px;
+    height: 400px;
+    border: 1px solid green;
+}
+
+#becomesVisible {
+       position: absolute;
+       top: 100px;
+       left: -1000px;
+       width: 100px;
+       height: 100px;
+       background-color: green;
+       transition: left 100ms ease-in-out 0ms;
+}
+</style>
+<script>
+async function test() {
+    if (!window.testRunner || !testRunner.runUIScript)
+        return;
+    if (window.internals)
+        internals.settings.setContentChangeObserverEnabled(true);
+
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+
+    let rect = tapthis.getBoundingClientRect();
+    let x = rect.left + rect.width / 2;
+    let y = rect.top + rect.height / 2;
+
+    await tapAtPoint(x, y);
+}
+</script>
+</head>
+<body onload="test()">
+<div id=tapthis>PASS if 'clicked' text is shown below.</div>
+<div id=becomesVisible></div>
+<pre id=result></pre>
+<script>
+tapthis.addEventListener("touchstart", function( event ) {
+    becomesVisible.remove();
+}, false);
+
+becomesVisible.addEventListener("click", function( event ) {   
+    result.innerHTML = "clicked hidden";
+}, false);
+
+tapthis.addEventListener("click", function( event ) {   
+    result.innerHTML = "clicked";
+    if (window.testRunner)
+        testRunner.notifyDone();
+}, false);
+</script>
+</body>
+</html>
index d151148..e2baead 100644 (file)
@@ -1,5 +1,36 @@
 2019-03-21  Zalan Bujtas  <zalan@apple.com>
 
+        [ContentChangeObserver] Start tracking implicit transitions at touchStart
+        https://bugs.webkit.org/show_bug.cgi?id=196051
+        <rdar://problem/49092952>
+
+        Reviewed by Simon Fraser.
+
+        This patch enables transition tracking on touchStart.
+
+        1. Start observing for new transitions at touchStart
+        2. Stop observing at touchEnd
+        3. Check the next style recalc when the transition is finished (at onAnimationEnd we don't yet have the final computed style).
+        4. Remove the tracked transition when it is canceled.
+
+        Tests: fast/events/touch/ios/content-observation/0ms-transition-on-touch-start.html
+               fast/events/touch/ios/content-observation/100ms-transition-on-touch-start.html
+               fast/events/touch/ios/content-observation/10ms-delay-transition-on-touch-start.html
+               fast/events/touch/ios/content-observation/transition-on-touch-start-and-remove.html
+
+        * page/ios/ContentChangeObserver.cpp:
+        (WebCore::ContentChangeObserver::didAddTransition):
+        (WebCore::ContentChangeObserver::didFinishTransition):
+        (WebCore::ContentChangeObserver::didRemoveTransition):
+        (WebCore::ContentChangeObserver::adjustObservedState):
+        * page/ios/ContentChangeObserver.h:
+        (WebCore::ContentChangeObserver::setShouldObserveTransitions):
+        (WebCore::ContentChangeObserver::clearObservedTransitions):
+        (WebCore::ContentChangeObserver::hasObservedTransition const):
+        (WebCore::ContentChangeObserver::hasPendingActivity const):
+
+2019-03-21  Zalan Bujtas  <zalan@apple.com>
+
         [ContentChangeObserver] Track hidden elements only while transitioning.
         https://bugs.webkit.org/show_bug.cgi?id=196050
         <rdar://problem/49092037>
index b5e1c2e..779087c 100644 (file)
@@ -141,7 +141,7 @@ void ContentChangeObserver::didAddTransition(const Element& element, const Anima
     LOG_WITH_STREAM(ContentObservation, stream << "didAddTransition: transition created on " << &element << " (" << transitionEnd.milliseconds() << "ms).");
 
     m_elementsWithTransition.add(&element);
-    // FIXME: report state change.
+    adjustObservedState(Event::AddedTransition);
 }
 
 void ContentChangeObserver::didFinishTransition(const Element& element, CSSPropertyID propertyID)
@@ -151,7 +151,8 @@ void ContentChangeObserver::didFinishTransition(const Element& element, CSSPrope
     if (!m_elementsWithTransition.take(&element))
         return;
     LOG_WITH_STREAM(ContentObservation, stream << "didFinishTransition: transition finished (" << &element << ").");
-    // FIXME: report state change.
+
+    adjustObservedState(Event::EndedTransition);
 }
 
 void ContentChangeObserver::didRemoveTransition(const Element& element, CSSPropertyID propertyID)
@@ -161,7 +162,8 @@ void ContentChangeObserver::didRemoveTransition(const Element& element, CSSPrope
     if (!m_elementsWithTransition.take(&element))
         return;
     LOG_WITH_STREAM(ContentObservation, stream << "didRemoveTransition: transition got interrupted (" << &element << ").");
-    // FIXME: report state change.
+
+    adjustObservedState(Event::CanceledTransition);
 }
 
 void ContentChangeObserver::didInstallDOMTimer(const DOMTimer& timer, Seconds timeout, bool singleShot)
@@ -334,6 +336,7 @@ void ContentChangeObserver::adjustObservedState(Event event)
     auto reset = [&] {
         setHasNoChangeState();
         clearObservedDOMTimers();
+        clearObservedTransitions();
         setIsBetweenTouchEndAndMouseMoved(false);
         ASSERT(!m_isObservingDOMTimerScheduling);
         ASSERT(!m_isWaitingForStyleRecalc);
@@ -351,6 +354,10 @@ void ContentChangeObserver::adjustObservedState(Event event)
             LOG(ContentObservation, "adjustStateAndNotifyContentChangeIfNeeded: in mouseMoved call. No need to notify the client.");
             return;
         }
+        if (isBetweenTouchEndAndMouseMoved()) {
+            LOG(ContentObservation, "adjustStateAndNotifyContentChangeIfNeeded: Not reached mouseMoved yet. No need to notify the client.");
+            return;
+        }
         if (!hasDeterminateState()) {
             LOG(ContentObservation, "adjustStateAndNotifyContentChangeIfNeeded: not in a determined state yet.");
             return;
@@ -365,9 +372,11 @@ void ContentChangeObserver::adjustObservedState(Event event)
     case Event::StartedTouchStartEventDispatching:
         reset();
         setShouldObserveDOMTimerScheduling(true);
+        setShouldObserveTransitions(true);
         break;
     case Event::EndedTouchStartEventDispatching:
         setShouldObserveDOMTimerScheduling(false);
+        setShouldObserveTransitions(false);
         setIsBetweenTouchEndAndMouseMoved(true);
         break;
     case Event::WillNotProceedWithClick:
@@ -394,14 +403,17 @@ void ContentChangeObserver::adjustObservedState(Event event)
         break;
     case Event::InstalledDOMTimer:
     case Event::StartedFixedObservationTimeWindow:
+    case Event::AddedTransition:
         ASSERT(!hasVisibleChangeState());
         setHasIndeterminateState();
         break;
     case Event::EndedDOMTimerExecution:
+    case Event::EndedTransition:
         setShouldObserveNextStyleRecalc(m_document.hasPendingStyleRecalc());
         FALLTHROUGH;
     case Event::EndedStyleRecalc:
     case Event::RemovedDOMTimer:
+    case Event::CanceledTransition:
         if (!isObservationTimeWindowActive())
             adjustStateAndNotifyContentChangeIfNeeded();
         break;
@@ -413,6 +425,7 @@ void ContentChangeObserver::adjustObservedState(Event event)
         // Remove pending activities. We don't need to observe them anymore.
         setShouldObserveNextStyleRecalc(false);
         clearObservedDOMTimers();
+        clearObservedTransitions();
         break;
     }
 }
index fce8676..efc22bd 100644 (file)
@@ -120,6 +120,7 @@ private:
 
     void setShouldObserveDOMTimerScheduling(bool observe) { m_isObservingDOMTimerScheduling = observe; }
     bool isObservingDOMTimerScheduling() const { return m_isObservingDOMTimerScheduling; }
+    void setShouldObserveTransitions(bool observe) { m_isObservingTransitions = observe; }
     bool isObservingTransitions() const { return m_isObservingTransitions; }
     bool isObservedPropertyForTransition(CSSPropertyID propertyId) const { return propertyId == CSSPropertyLeft; }
     void domTimerExecuteDidStart(const DOMTimer&);
@@ -127,6 +128,7 @@ private:
     void registerDOMTimer(const DOMTimer& timer) { m_DOMTimerList.add(&timer); }
     void unregisterDOMTimer(const DOMTimer& timer) { m_DOMTimerList.remove(&timer); }
     void clearObservedDOMTimers() { m_DOMTimerList.clear(); }
+    void clearObservedTransitions() { m_elementsWithTransition.clear(); }
     bool containsObservedDOMTimer(const DOMTimer& timer) const { return m_DOMTimerList.contains(&timer); }
 
     void styleRecalcDidStart();
@@ -144,12 +146,13 @@ private:
 
     bool hasVisibleChangeState() const { return observedContentChange() == WKContentVisibilityChange; }
     bool hasObservedDOMTimer() const { return !m_DOMTimerList.isEmpty(); }
+    bool hasObservedTransition() const { return !m_elementsWithTransition.isEmpty(); }
     bool hasDeterminateState() const;
 
     void setIsBetweenTouchEndAndMouseMoved(bool isBetween) { m_isBetweenTouchEndAndMouseMoved = isBetween; }
     bool isBetweenTouchEndAndMouseMoved() const { return m_isBetweenTouchEndAndMouseMoved; }
 
-    bool hasPendingActivity() const { return hasObservedDOMTimer() || m_isWaitingForStyleRecalc || isObservationTimeWindowActive(); }
+    bool hasPendingActivity() const { return hasObservedDOMTimer() || hasObservedTransition() || m_isWaitingForStyleRecalc || isObservationTimeWindowActive(); }
     bool isObservationTimeWindowActive() const { return m_contentObservationTimer.isActive(); }
 
     void completeDurationBasedContentObservation();
@@ -166,6 +169,9 @@ private:
         EndedDOMTimerExecution,
         StartedStyleRecalc,
         EndedStyleRecalc,
+        AddedTransition,
+        EndedTransition,
+        CanceledTransition,
         StartedFixedObservationTimeWindow,
         EndedFixedObservationTimeWindow,
         ContentVisibilityChanged