<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[248750] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/248750">248750</a></dd>
<dt>Author</dt> <dd>zalan@apple.com</dd>
<dt>Date</dt> <dd>2019-08-15 15:18:56 -0700 (Thu, 15 Aug 2019)</dd>
</dl>

<h3>Log Message</h3>
<pre>[ContentChangeObserver] Dispatch synthetic click when the visibility candidate element becomes hidden again.
https://bugs.webkit.org/show_bug.cgi?id=200773
<rdar://problem/54351728>

Reviewed by Simon Fraser.

Source/WebCore:

This patch fixes the case when the candidate element (going from hidden to visible) becomes hidden by the end of the observation window. It essentially means that no visible change has happened
and we should proceed with dispatching the synthetic click event.
We now keep track of the candidate element and reset the visiblity state when it loses its renderer.

Test: fast/events/touch/ios/content-observation/going-from-hidden-to-visible-and-to-hidden.html

* page/ios/ContentChangeObserver.cpp:
(WebCore::ContentChangeObserver::didAddTransition):
(WebCore::ContentChangeObserver::didFinishTransition):
(WebCore::ContentChangeObserver::didInstallDOMTimer):
(WebCore::ContentChangeObserver::reset):
(WebCore::ContentChangeObserver::rendererWillBeDestroyed):
(WebCore::ContentChangeObserver::contentVisibilityDidChange):
(WebCore::ContentChangeObserver::touchEventDidStart):
(WebCore::ContentChangeObserver::touchEventDidFinish):
(WebCore::ContentChangeObserver::mouseMovedDidStart):
(WebCore::ContentChangeObserver::mouseMovedDidFinish):
(WebCore::ContentChangeObserver::adjustObservedState):
(WebCore::ContentChangeObserver::StyleChangeScope::~StyleChangeScope):
(WebCore::ContentChangeObserver::hasDeterminateState const): Deleted.
* page/ios/ContentChangeObserver.h:
(WebCore::ContentChangeObserver::hasObservedTransition const):
(WebCore::ContentChangeObserver::setTouchEventIsBeingDispatched):
(WebCore::ContentChangeObserver::isTouchEventBeingDispatched const):
(WebCore::ContentChangeObserver::setMouseMovedEventIsBeingDispatched):
(WebCore::ContentChangeObserver::isMouseMovedEventBeingDispatched const):
(WebCore::ContentChangeObserver::isObservingContentChanges const):

LayoutTests:

* fast/events/touch/ios/content-observation/going-from-hidden-to-visible-and-to-hidden-expected.html: Added.
* fast/events/touch/ios/content-observation/going-from-hidden-to-visible-and-to-hidden.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorepageiosContentChangeObservercpp">trunk/Source/WebCore/page/ios/ContentChangeObserver.cpp</a></li>
<li><a href="#trunkSourceWebCorepageiosContentChangeObserverh">trunk/Source/WebCore/page/ios/ContentChangeObserver.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsfasteventstouchioscontentobservationgoingfromhiddentovisibleandtohiddenexpectedtxt">trunk/LayoutTests/fast/events/touch/ios/content-observation/going-from-hidden-to-visible-and-to-hidden-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfasteventstouchioscontentobservationgoingfromhiddentovisibleandtohiddenhtml">trunk/LayoutTests/fast/events/touch/ios/content-observation/going-from-hidden-to-visible-and-to-hidden.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (248749 => 248750)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog      2019-08-15 22:15:29 UTC (rev 248749)
+++ trunk/LayoutTests/ChangeLog 2019-08-15 22:18:56 UTC (rev 248750)
</span><span class="lines">@@ -1,3 +1,14 @@
</span><ins>+2019-08-15  Zalan Bujtas  <zalan@apple.com>
+
+        [ContentChangeObserver] Dispatch synthetic click when the visibility candidate element becomes hidden again.
+        https://bugs.webkit.org/show_bug.cgi?id=200773
+        <rdar://problem/54351728>
+
+        Reviewed by Simon Fraser.
+
+        * fast/events/touch/ios/content-observation/going-from-hidden-to-visible-and-to-hidden-expected.html: Added.
+        * fast/events/touch/ios/content-observation/going-from-hidden-to-visible-and-to-hidden.html: Added.
+
</ins><span class="cx"> 2019-08-15  Robin Morisset  <rmorisset@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [WHLSL] Don't accept operator&& or operator|| in the Lexer
</span></span></pre></div>
<a id="trunkLayoutTestsfasteventstouchioscontentobservationgoingfromhiddentovisibleandtohiddenexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/events/touch/ios/content-observation/going-from-hidden-to-visible-and-to-hidden-expected.txt (0 => 248750)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/events/touch/ios/content-observation/going-from-hidden-to-visible-and-to-hidden-expected.txt                              (rev 0)
+++ trunk/LayoutTests/fast/events/touch/ios/content-observation/going-from-hidden-to-visible-and-to-hidden-expected.txt 2019-08-15 22:18:56 UTC (rev 248750)
</span><span class="lines">@@ -0,0 +1,2 @@
</span><ins>+PASS if 'clicked' text is shown below.
+clicked
</ins></span></pre></div>
<a id="trunkLayoutTestsfasteventstouchioscontentobservationgoingfromhiddentovisibleandtohiddenhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/events/touch/ios/content-observation/going-from-hidden-to-visible-and-to-hidden.html (0 => 248750)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/events/touch/ios/content-observation/going-from-hidden-to-visible-and-to-hidden.html                              (rev 0)
+++ trunk/LayoutTests/fast/events/touch/ios/content-observation/going-from-hidden-to-visible-and-to-hidden.html 2019-08-15 22:18:56 UTC (rev 248750)
</span><span class="lines">@@ -0,0 +1,58 @@
</span><ins>+<!DOCTYPE html><!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<html>
+<head>
+<title>This tests the case when visible and actionable content shows up and gets destroyed right away.</title>
+<script src="../../../../../resources/ui-helper.js"></script>
+<style>
+#tapThis {
+    width: 400px;
+    height: 400px;
+    border: 1px solid green;
+}
+
+#willBecomeVisibleMomentarily {
+    display: none;
+    width: 100px;
+    height: 100px;
+    background-color: green;
+}
+</style>
+<script>
+async function test() {
+    if (!window.testRunner || !testRunner.runUIScript)
+        return;
+    if (window.internals)
+        internals.settings.setContentChangeObserverEnabled(true);
+
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+
+    await UIHelper.activateElement(tapThis);
+}
+</script>
+</head>
+<body onload="test()">
+<div id=tapThis>PASS if 'clicked' text is shown below.</div>
+<div id=willBecomeVisibleMomentarily></div>
+<pre id=result></pre>
+<script>
+tapThis.addEventListener("touchstart", function( event ) {
+       willBecomeVisibleMomentarily.style.display = "block";
+}, false);
+
+tapThis.addEventListener("mouseover", function( event ) {
+       willBecomeVisibleMomentarily.style.display = "none";
+}, false);
+
+willBecomeVisibleMomentarily.addEventListener("click", function( event ) {
+    result.innerHTML = "clicked willBecomeVisibleMomentarily";
+}, false);
+
+tapThis.addEventListener("click", function( event ) {   
+    result.innerHTML = "clicked";
+    if (window.testRunner)
+        testRunner.notifyDone();
+}, false);
+</script>
+</body>
+</html>
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (248749 => 248750)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog   2019-08-15 22:15:29 UTC (rev 248749)
+++ trunk/Source/WebCore/ChangeLog      2019-08-15 22:18:56 UTC (rev 248750)
</span><span class="lines">@@ -1,3 +1,39 @@
</span><ins>+2019-08-15  Zalan Bujtas  <zalan@apple.com>
+
+        [ContentChangeObserver] Dispatch synthetic click when the visibility candidate element becomes hidden again.
+        https://bugs.webkit.org/show_bug.cgi?id=200773
+        <rdar://problem/54351728>
+
+        Reviewed by Simon Fraser.
+
+        This patch fixes the case when the candidate element (going from hidden to visible) becomes hidden by the end of the observation window. It essentially means that no visible change has happened
+        and we should proceed with dispatching the synthetic click event.
+        We now keep track of the candidate element and reset the visiblity state when it loses its renderer.
+
+        Test: fast/events/touch/ios/content-observation/going-from-hidden-to-visible-and-to-hidden.html
+
+        * page/ios/ContentChangeObserver.cpp:
+        (WebCore::ContentChangeObserver::didAddTransition):
+        (WebCore::ContentChangeObserver::didFinishTransition):
+        (WebCore::ContentChangeObserver::didInstallDOMTimer):
+        (WebCore::ContentChangeObserver::reset):
+        (WebCore::ContentChangeObserver::rendererWillBeDestroyed):
+        (WebCore::ContentChangeObserver::contentVisibilityDidChange):
+        (WebCore::ContentChangeObserver::touchEventDidStart):
+        (WebCore::ContentChangeObserver::touchEventDidFinish):
+        (WebCore::ContentChangeObserver::mouseMovedDidStart):
+        (WebCore::ContentChangeObserver::mouseMovedDidFinish):
+        (WebCore::ContentChangeObserver::adjustObservedState):
+        (WebCore::ContentChangeObserver::StyleChangeScope::~StyleChangeScope):
+        (WebCore::ContentChangeObserver::hasDeterminateState const): Deleted.
+        * page/ios/ContentChangeObserver.h:
+        (WebCore::ContentChangeObserver::hasObservedTransition const):
+        (WebCore::ContentChangeObserver::setTouchEventIsBeingDispatched):
+        (WebCore::ContentChangeObserver::isTouchEventBeingDispatched const):
+        (WebCore::ContentChangeObserver::setMouseMovedEventIsBeingDispatched):
+        (WebCore::ContentChangeObserver::isMouseMovedEventBeingDispatched const):
+        (WebCore::ContentChangeObserver::isObservingContentChanges const):
+
</ins><span class="cx"> 2019-08-15  Justin Fan  <justin_fan@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Unreviewed suggested patch follow-up to https://bugs.webkit.org/show_bug.cgi?id=200740.
</span></span></pre></div>
<a id="trunkSourceWebCorepageiosContentChangeObservercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/ios/ContentChangeObserver.cpp (248749 => 248750)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/ios/ContentChangeObserver.cpp  2019-08-15 22:15:29 UTC (rev 248749)
+++ trunk/Source/WebCore/page/ios/ContentChangeObserver.cpp     2019-08-15 22:18:56 UTC (rev 248750)
</span><span class="lines">@@ -214,6 +214,8 @@
</span><span class="cx">         return;
</span><span class="cx">     if (hasVisibleChangeState())
</span><span class="cx">         return;
</span><ins>+    if (!isObservingContentChanges())
+        return;
</ins><span class="cx">     if (!isObservingTransitions())
</span><span class="cx">         return;
</span><span class="cx">     if (!transition.isDurationSet() || !transition.isPropertySet())
</span><span class="lines">@@ -251,7 +253,7 @@
</span><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx">         if (isConsideredClickable(*targetElement, ElementHadRenderer::Yes))
</span><del>-            weakThis->contentVisibilityDidChange();
</del><ins>+            weakThis->contentVisibilityDidChange(*targetElement);
</ins><span class="cx">         weakThis->adjustObservedState(Event::CompletedTransition);
</span><span class="cx">     });
</span><span class="cx"> }
</span><span class="lines">@@ -271,14 +273,16 @@
</span><span class="cx"> {
</span><span class="cx">     if (!m_document.settings().contentChangeObserverEnabled())
</span><span class="cx">         return;
</span><del>-    if (m_document.activeDOMObjectsAreSuspended())
</del><ins>+    if (!isObservingContentChanges())
</ins><span class="cx">         return;
</span><del>-    if (timeout > maximumDelayForTimers || !singleShot)
-        return;
</del><span class="cx">     if (!isObservingDOMTimerScheduling())
</span><span class="cx">         return;
</span><span class="cx">     if (hasVisibleChangeState())
</span><span class="cx">         return;
</span><ins>+    if (m_document.activeDOMObjectsAreSuspended())
+        return;
+    if (timeout > maximumDelayForTimers || !singleShot)
+        return;
</ins><span class="cx">     LOG_WITH_STREAM(ContentObservation, stream << "didInstallDOMTimer: register this timer: (" << &timer << ") and observe when it fires.");
</span><span class="cx"> 
</span><span class="cx">     registerDOMTimer(timer);
</span><span class="lines">@@ -359,13 +363,16 @@
</span><span class="cx"> {
</span><span class="cx">     stopObservingPendingActivities();
</span><span class="cx">     setHasNoChangeState();
</span><ins>+
+    setTouchEventIsBeingDispatched(false);
</ins><span class="cx">     setIsBetweenTouchEndAndMouseMoved(false);
</span><ins>+    setMouseMovedEventIsBeingDispatched(false);
</ins><span class="cx"> 
</span><del>-    m_touchEventIsBeingDispatched = false;
</del><span class="cx">     m_isInObservedStyleRecalc = false;
</span><span class="cx">     m_observedDomTimerIsBeingExecuted = false;
</span><del>-    m_mouseMovedEventIsBeingDispatched = false;
</del><span class="cx"> 
</span><ins>+    m_visibilityCandidateElement = { };
+
</ins><span class="cx">     m_contentObservationTimer.stop();
</span><span class="cx">     m_elementsWithDestroyedVisibleRenderer.clear();
</span><span class="cx">     resetHiddenTouchTarget();
</span><span class="lines">@@ -389,17 +396,24 @@
</span><span class="cx">         return;
</span><span class="cx">     if (!isObservingContentChanges())
</span><span class="cx">         return;
</span><del>-    if (hasVisibleChangeState())
-        return;
</del><span class="cx">     LOG_WITH_STREAM(ContentObservation, stream << "rendererWillBeDestroyed element: " << &element);
</span><span class="cx"> 
</span><span class="cx">     if (!isVisuallyHidden(element))
</span><span class="cx">         m_elementsWithDestroyedVisibleRenderer.add(&element);
</span><ins>+    // Candidate element is no longer visible.
+    if (m_visibilityCandidateElement == &element) {
+        // FIXME: We should also check for other type of visiblity changes.
+        ASSERT(hasVisibleChangeState());
+        m_visibilityCandidateElement = { };
+        setHasIndeterminateState();
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ContentChangeObserver::contentVisibilityDidChange()
</del><ins>+void ContentChangeObserver::contentVisibilityDidChange(const Element& element)
</ins><span class="cx"> {
</span><span class="cx">     LOG(ContentObservation, "contentVisibilityDidChange: visible content change did happen.");
</span><ins>+    // FIXME: This should evolve into a list of candidate elements.
+    m_visibilityCandidateElement = makeWeakPtr(element);
</ins><span class="cx">     adjustObservedState(Event::ContentVisibilityChanged);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -411,7 +425,7 @@
</span><span class="cx">     if (eventType != PlatformEvent::Type::TouchStart)
</span><span class="cx">         return;
</span><span class="cx">     LOG(ContentObservation, "touchEventDidStart: touch start event started.");
</span><del>-    m_touchEventIsBeingDispatched = true;
</del><ins>+    setTouchEventIsBeingDispatched(true);
</ins><span class="cx">     adjustObservedState(Event::StartedTouchStartEventDispatching);
</span><span class="cx"> #else
</span><span class="cx">     UNUSED_PARAM(eventType);
</span><span class="lines">@@ -421,11 +435,11 @@
</span><span class="cx"> void ContentChangeObserver::touchEventDidFinish()
</span><span class="cx"> {
</span><span class="cx"> #if ENABLE(TOUCH_EVENTS)
</span><del>-    if (!m_touchEventIsBeingDispatched)
</del><ins>+    if (!isTouchEventBeingDispatched())
</ins><span class="cx">         return;
</span><span class="cx">     ASSERT(m_document.settings().contentChangeObserverEnabled());
</span><span class="cx">     LOG(ContentObservation, "touchEventDidFinish: touch start event finished.");
</span><del>-    m_touchEventIsBeingDispatched = false;
</del><ins>+    setTouchEventIsBeingDispatched(false);
</ins><span class="cx">     adjustObservedState(Event::EndedTouchStartEventDispatching);
</span><span class="cx"> #endif
</span><span class="cx"> }
</span><span class="lines">@@ -435,18 +449,18 @@
</span><span class="cx">     if (!m_document.settings().contentChangeObserverEnabled())
</span><span class="cx">         return;
</span><span class="cx">     LOG(ContentObservation, "mouseMovedDidStart: mouseMoved started.");
</span><del>-    m_mouseMovedEventIsBeingDispatched = true;
</del><ins>+    setMouseMovedEventIsBeingDispatched(true);
</ins><span class="cx">     adjustObservedState(Event::StartedMouseMovedEventDispatching);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ContentChangeObserver::mouseMovedDidFinish()
</span><span class="cx"> {
</span><del>-    if (!m_mouseMovedEventIsBeingDispatched)
</del><ins>+    if (!isMouseMovedEventBeingDispatched())
</ins><span class="cx">         return;
</span><span class="cx">     ASSERT(m_document.settings().contentChangeObserverEnabled());
</span><span class="cx">     LOG(ContentObservation, "mouseMovedDidFinish: mouseMoved finished.");
</span><span class="cx">     adjustObservedState(Event::EndedMouseMovedEventDispatching);
</span><del>-    m_mouseMovedEventIsBeingDispatched = false;
</del><ins>+    setMouseMovedEventIsBeingDispatched(false);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ContentChangeObserver::setShouldObserveNextStyleRecalc(bool shouldObserve)
</span><span class="lines">@@ -456,13 +470,6 @@
</span><span class="cx">     m_isWaitingForStyleRecalc = shouldObserve;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool ContentChangeObserver::hasDeterminateState() const
-{
-    if (hasVisibleChangeState())
-        return true;
-    return observedContentChange() == WKContentNoChange && !hasPendingActivity();
-}
-
</del><span class="cx"> void ContentChangeObserver::adjustObservedState(Event event)
</span><span class="cx"> {
</span><span class="cx">     auto resetToStartObserving = [&] {
</span><span class="lines">@@ -477,27 +484,34 @@
</span><span class="cx">     };
</span><span class="cx"> 
</span><span class="cx">     auto notifyClientIfNeeded = [&] {
</span><del>-        // First demote to "no change" if there's no pending activity anymore.
-        if (observedContentChange() == WKContentIndeterminateChange && !hasPendingActivity())
-            setHasNoChangeState();
-
</del><ins>+        if (isTouchEventBeingDispatched()) {
+            LOG(ContentObservation, "notifyClientIfNeeded: Touch event is being dispatched. No need to notify the client.");
+            return;
+        }
</ins><span class="cx">         if (isBetweenTouchEndAndMouseMoved()) {
</span><del>-            LOG(ContentObservation, "adjustStateAndNotifyContentChangeIfNeeded: Not reached mouseMoved yet. No need to notify the client.");
</del><ins>+            LOG(ContentObservation, "notifyClientIfNeeded: Not reached mouseMoved yet. No need to notify the client.");
</ins><span class="cx">             return;
</span><span class="cx">         }
</span><del>-        if (m_mouseMovedEventIsBeingDispatched) {
-            LOG(ContentObservation, "adjustStateAndNotifyContentChangeIfNeeded: in mouseMoved call. No need to notify the client.");
</del><ins>+        if (isMouseMovedEventBeingDispatched()) {
+            LOG(ContentObservation, "notifyClientIfNeeded: in mouseMoved call. No need to notify the client.");
</ins><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx">         if (isObservationTimeWindowActive()) {
</span><del>-            LOG(ContentObservation, "adjustStateAndNotifyContentChangeIfNeeded: Inside the fixed window observation. No need to notify the client.");
</del><ins>+            LOG(ContentObservation, "notifyClientIfNeeded: Inside the fixed window observation. No need to notify the client.");
</ins><span class="cx">             return;
</span><span class="cx">         }
</span><del>-        if (!hasDeterminateState()) {
-            LOG(ContentObservation, "adjustStateAndNotifyContentChangeIfNeeded: not in a determined state yet.");
</del><ins>+
+        // The fixed observation window (which is the final step in content observation) is closed and now we check if are still waiting for timers or animations to finish.
+        if (hasPendingActivity()) {
+            LOG(ContentObservation, "notifyClientIfNeeded: We are still waiting on some events.");
</ins><span class="cx">             return;
</span><span class="cx">         }
</span><del>-        LOG_WITH_STREAM(ContentObservation, stream << "adjustStateAndNotifyContentChangeIfNeeded: sending observedContentChange ->" << observedContentChange());
</del><ins>+
+        // First demote to "no change" because we've got no pending activity anymore.
+        if (observedContentChange() == WKContentIndeterminateChange)
+            setHasNoChangeState();
+
+        LOG_WITH_STREAM(ContentObservation, stream << "notifyClientIfNeeded: sending observedContentChange ->" << observedContentChange());
</ins><span class="cx">         ASSERT(m_document.page());
</span><span class="cx">         ASSERT(m_document.frame());
</span><span class="cx">         m_document.page()->chrome().client().didFinishContentChangeObserving(*m_document.frame(), observedContentChange());
</span><span class="lines">@@ -595,7 +609,7 @@
</span><span class="cx">     }
</span><span class="cx">     // Either the page decided to call preventDefault on the touch action or the tap gesture evolved to some other gesture (long press, double tap). 
</span><span class="cx">     if (event == Event::WillNotProceedWithClick) {
</span><del>-        reset();
</del><ins>+        stopContentObservation();
</ins><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx">     // The page produced an visible change on an actionable content.
</span><span class="lines">@@ -628,7 +642,7 @@
</span><span class="cx">     };
</span><span class="cx"> 
</span><span class="cx">     if (changedFromHiddenToVisible() && isConsideredClickable(m_element, m_hadRenderer ? ElementHadRenderer::Yes : ElementHadRenderer::No))
</span><del>-        m_contentChangeObserver.contentVisibilityDidChange();
</del><ins>+        m_contentChangeObserver.contentVisibilityDidChange(m_element);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(TOUCH_EVENTS)
</span></span></pre></div>
<a id="trunkSourceWebCorepageiosContentChangeObserverh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/ios/ContentChangeObserver.h (248749 => 248750)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/ios/ContentChangeObserver.h    2019-08-15 22:15:29 UTC (rev 248749)
+++ trunk/Source/WebCore/page/ios/ContentChangeObserver.h       2019-08-15 22:18:56 UTC (rev 248750)
</span><span class="lines">@@ -128,7 +128,7 @@
</span><span class="cx"> 
</span><span class="cx">     void didRecognizeLongPress();
</span><span class="cx"> 
</span><del>-    void contentVisibilityDidChange();
</del><ins>+    void contentVisibilityDidChange(const Element&);
</ins><span class="cx"> 
</span><span class="cx">     void setShouldObserveDOMTimerSchedulingAndTransitions(bool);
</span><span class="cx">     bool isObservingDOMTimerScheduling() const { return m_isObservingDOMTimerScheduling; }
</span><span class="lines">@@ -159,11 +159,16 @@
</span><span class="cx">     bool hasVisibleChangeState() const { return observedContentChange() == WKContentVisibilityChange; }
</span><span class="cx">     bool hasObservedDOMTimer() const { return !m_DOMTimerList.isEmpty(); }
</span><span class="cx">     bool hasObservedTransition() const { return !m_elementsWithTransition.isEmpty(); }
</span><del>-    bool hasDeterminateState() const;
</del><span class="cx"> 
</span><span class="cx">     void setIsBetweenTouchEndAndMouseMoved(bool isBetween) { m_isBetweenTouchEndAndMouseMoved = isBetween; }
</span><span class="cx">     bool isBetweenTouchEndAndMouseMoved() const { return m_isBetweenTouchEndAndMouseMoved; }
</span><span class="cx"> 
</span><ins>+    void setTouchEventIsBeingDispatched(bool dispatching) { m_touchEventIsBeingDispatched = dispatching; }
+    bool isTouchEventBeingDispatched() const { return m_touchEventIsBeingDispatched; }
+
+    void setMouseMovedEventIsBeingDispatched(bool dispatching) { m_mouseMovedEventIsBeingDispatched = dispatching; }
+    bool isMouseMovedEventBeingDispatched() const { return m_mouseMovedEventIsBeingDispatched; }
+
</ins><span class="cx">     bool hasPendingActivity() const { return hasObservedDOMTimer() || hasObservedTransition() || m_isWaitingForStyleRecalc || isObservationTimeWindowActive(); }
</span><span class="cx">     bool isObservationTimeWindowActive() const { return m_contentObservationTimer.isActive(); }
</span><span class="cx"> 
</span><span class="lines">@@ -202,6 +207,7 @@
</span><span class="cx">     HashSet<const Element*> m_elementsWithDestroyedVisibleRenderer;
</span><span class="cx">     WKContentChange m_observedContentState { WKContentNoChange };
</span><span class="cx">     WeakPtr<Element> m_hiddenTouchTargetElement;
</span><ins>+    WeakPtr<Element> m_visibilityCandidateElement;
</ins><span class="cx">     bool m_touchEventIsBeingDispatched { false };
</span><span class="cx">     bool m_isWaitingForStyleRecalc { false };
</span><span class="cx">     bool m_isInObservedStyleRecalc { false };
</span><span class="lines">@@ -214,9 +220,9 @@
</span><span class="cx"> 
</span><span class="cx"> inline bool ContentChangeObserver::isObservingContentChanges() const
</span><span class="cx"> {
</span><del>-    return m_touchEventIsBeingDispatched
-        || m_isBetweenTouchEndAndMouseMoved
-        || m_mouseMovedEventIsBeingDispatched
</del><ins>+    return isTouchEventBeingDispatched()
+        || isBetweenTouchEndAndMouseMoved()
+        || isMouseMovedEventBeingDispatched()
</ins><span class="cx">         || m_observedDomTimerIsBeingExecuted
</span><span class="cx">         || m_isInObservedStyleRecalc
</span><span class="cx">         || isObservationTimeWindowActive();
</span></span></pre>
</div>
</div>

</body>
</html>