<!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>[242290] 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/242290">242290</a></dd>
<dt>Author</dt> <dd>zalan@apple.com</dd>
<dt>Date</dt> <dd>2019-03-01 14:48:54 -0800 (Fri, 01 Mar 2019)</dd>
</dl>

<h3>Log Message</h3>
<pre>[ContentChangeObserver] Check for pending style recalcs at the end of each timer run.
https://bugs.webkit.org/show_bug.cgi?id=195220
<rdar://problem/48518979>

Reviewed by Simon Fraser.

Source/WebCore:

didScheduleStyleRecalc callback was introduced to see if a style recalc is scheduled while firing the DOM timer. However it does not handle the case
when in addition to this style recalc scheduling, something later (though during the same timer firing) triggers a sync style recalc.
Let's just check if we've got a pending style recalc when the DOM timer comes back.

Test: fast/events/touch/ios/style-recalc-schedule-and-force-relalc.html

* dom/Document.cpp:
(WebCore::Document::scheduleStyleRecalc):
* page/ios/ContentChangeObserver.cpp:
(WebCore::hasPendingStyleRecalc):
(WebCore::ContentChangeObserver::startObservingDOMTimerExecute):
(WebCore::ContentChangeObserver::stopObservingDOMTimerExecute):
(WebCore::ContentChangeObserver::startObservingContentChanges):
(WebCore::ContentChangeObserver::didScheduleStyleRecalc): Deleted.
* page/ios/ContentChangeObserver.h:
(WebCore::ContentChangeObserver::startObservingStyleRecalcScheduling): Deleted.
(WebCore::ContentChangeObserver::stopObservingStyleRecalcScheduling): Deleted.
(WebCore::ContentChangeObserver::isObservingStyleRecalcScheduling const): Deleted.

LayoutTests:

* fast/events/touch/ios/style-recalc-schedule-and-force-relalc-expected.txt: Added.
* fast/events/touch/ios/style-recalc-schedule-and-force-relalc.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="#trunkSourceWebCoredomDocumentcpp">trunk/Source/WebCore/dom/Document.cpp</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="#trunkLayoutTestsfasteventstouchiosstylerecalcscheduleandforcerelalcexpectedtxt">trunk/LayoutTests/fast/events/touch/ios/style-recalc-schedule-and-force-relalc-expected.txt</a></li>
<li><a href="#trunkLayoutTestsfasteventstouchiosstylerecalcscheduleandforcerelalchtml">trunk/LayoutTests/fast/events/touch/ios/style-recalc-schedule-and-force-relalc.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (242289 => 242290)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog      2019-03-01 22:18:22 UTC (rev 242289)
+++ trunk/LayoutTests/ChangeLog 2019-03-01 22:48:54 UTC (rev 242290)
</span><span class="lines">@@ -1,3 +1,14 @@
</span><ins>+2019-03-01  Zalan Bujtas  <zalan@apple.com>
+
+        [ContentChangeObserver] Check for pending style recalcs at the end of each timer run.
+        https://bugs.webkit.org/show_bug.cgi?id=195220
+        <rdar://problem/48518979>
+
+        Reviewed by Simon Fraser.
+
+        * fast/events/touch/ios/style-recalc-schedule-and-force-relalc-expected.txt: Added.
+        * fast/events/touch/ios/style-recalc-schedule-and-force-relalc.html: Added.
+
</ins><span class="cx"> 2019-03-01  John Wilander  <wilander@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Resource Load Statistics: Further restrict client-side cookie persistence after cross-site navigations with link decoration
</span></span></pre></div>
<a id="trunkLayoutTestsfasteventstouchiosstylerecalcscheduleandforcerelalcexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/events/touch/ios/style-recalc-schedule-and-force-relalc-expected.txt (0 => 242290)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/events/touch/ios/style-recalc-schedule-and-force-relalc-expected.txt                              (rev 0)
+++ trunk/LayoutTests/fast/events/touch/ios/style-recalc-schedule-and-force-relalc-expected.txt 2019-03-01 22:48:54 UTC (rev 242290)
</span><span class="lines">@@ -0,0 +1,2 @@
</span><ins>+PASS if 'clicked' text is shown below.
+clicked
</ins></span></pre></div>
<a id="trunkLayoutTestsfasteventstouchiosstylerecalcscheduleandforcerelalchtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/fast/events/touch/ios/style-recalc-schedule-and-force-relalc.html (0 => 242290)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/fast/events/touch/ios/style-recalc-schedule-and-force-relalc.html                              (rev 0)
+++ trunk/LayoutTests/fast/events/touch/ios/style-recalc-schedule-and-force-relalc.html 2019-03-01 22:48:54 UTC (rev 242290)
</span><span class="lines">@@ -0,0 +1,66 @@
</span><ins>+<html>
+<head>
+<title>This test that we trigger hover when the content change starts as async but it turns into a sync style recalc.</title>
+<script src="../../../../resources/basic-gestures.js"></script>
+<style>
+#tapthis {
+    width: 400px;
+    height: 400px;
+    border: 1px solid green;
+}
+
+#staysHidden {
+    visibility: hidden;
+    width: 100px;
+    height: 100px;
+    background-color: green;
+}
+</style>
+<script>
+async function test() {
+    if (!window.testRunner || !testRunner.runUIScript)
+        return;
+
+    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=staysHidden></div>
+<pre id=result></pre>
+<script>
+tapthis.addEventListener("mouseover", function( event ) {
+    // 1. Install a timer on hover
+    setTimeout(function() {
+        // 2. Trigger some non-forcing (non-visibility) style change with forcing offsetHeight.
+        staysHidden.style.marginLeft = "5px";
+
+        document.body.offsetHeight;
+    }, 0);
+    // 3. Install a longer, empty timer.
+    setTimeout(function() {
+    }, 5);
+}, false);
+
+staysHidden.addEventListener("click", function( event ) {   
+    result.innerHTML = "clicked hidden";
+}, false);
+
+tapthis.addEventListener("click", function( event ) {   
+    result.innerHTML = "clicked";
+    setTimeout(function() {
+        if (window.testRunner)
+            testRunner.notifyDone();
+    }, 0);
+}, false);
+</script>
+</body>
+</html>
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (242289 => 242290)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog   2019-03-01 22:18:22 UTC (rev 242289)
+++ trunk/Source/WebCore/ChangeLog      2019-03-01 22:48:54 UTC (rev 242290)
</span><span class="lines">@@ -1,3 +1,30 @@
</span><ins>+2019-03-01  Zalan Bujtas  <zalan@apple.com>
+
+        [ContentChangeObserver] Check for pending style recalcs at the end of each timer run.
+        https://bugs.webkit.org/show_bug.cgi?id=195220
+        <rdar://problem/48518979>
+
+        Reviewed by Simon Fraser.
+
+        didScheduleStyleRecalc callback was introduced to see if a style recalc is scheduled while firing the DOM timer. However it does not handle the case
+        when in addition to this style recalc scheduling, something later (though during the same timer firing) triggers a sync style recalc.
+        Let's just check if we've got a pending style recalc when the DOM timer comes back.
+
+        Test: fast/events/touch/ios/style-recalc-schedule-and-force-relalc.html
+
+        * dom/Document.cpp:
+        (WebCore::Document::scheduleStyleRecalc):
+        * page/ios/ContentChangeObserver.cpp:
+        (WebCore::hasPendingStyleRecalc):
+        (WebCore::ContentChangeObserver::startObservingDOMTimerExecute):
+        (WebCore::ContentChangeObserver::stopObservingDOMTimerExecute):
+        (WebCore::ContentChangeObserver::startObservingContentChanges):
+        (WebCore::ContentChangeObserver::didScheduleStyleRecalc): Deleted.
+        * page/ios/ContentChangeObserver.h:
+        (WebCore::ContentChangeObserver::startObservingStyleRecalcScheduling): Deleted.
+        (WebCore::ContentChangeObserver::stopObservingStyleRecalcScheduling): Deleted.
+        (WebCore::ContentChangeObserver::isObservingStyleRecalcScheduling const): Deleted.
+
</ins><span class="cx"> 2019-03-01  John Wilander  <wilander@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Resource Load Statistics: Further restrict client-side cookie persistence after cross-site navigations with link decoration
</span></span></pre></div>
<a id="trunkSourceWebCoredomDocumentcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/dom/Document.cpp (242289 => 242290)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/dom/Document.cpp    2019-03-01 22:18:22 UTC (rev 242289)
+++ trunk/Source/WebCore/dom/Document.cpp       2019-03-01 22:48:54 UTC (rev 242290)
</span><span class="lines">@@ -1833,10 +1833,6 @@
</span><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     m_styleRecalcTimer.startOneShot(0_s);
</span><del>-#if PLATFORM(IOS_FAMILY)
-    if (auto* page = this->page())
-        page->contentChangeObserver().didScheduleStyleRecalc();
-#endif
</del><span class="cx"> 
</span><span class="cx">     InspectorInstrumentation::didScheduleStyleRecalculation(*this);
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCorepageiosContentChangeObservercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/ios/ContentChangeObserver.cpp (242289 => 242290)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/ios/ContentChangeObserver.cpp  2019-03-01 22:18:22 UTC (rev 242289)
+++ trunk/Source/WebCore/page/ios/ContentChangeObserver.cpp     2019-03-01 22:48:54 UTC (rev 242290)
</span><span class="lines">@@ -35,6 +35,17 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><ins>+static bool hasPendingStyleRecalc(const Page& page)
+{
+    for (auto* frame = &page.mainFrame(); frame; frame = frame->tree().traverseNext()) {
+        if (auto* document = frame->document()) {
+            if (document->hasPendingStyleRecalc())
+                return true;
+        }
+    }
+    return false;
+}
+
</ins><span class="cx"> ContentChangeObserver::ContentChangeObserver(Page& page)
</span><span class="cx">     : m_page(page)
</span><span class="cx"> {
</span><span class="lines">@@ -71,7 +82,6 @@
</span><span class="cx">     if (!containsObservedDOMTimer(timer))
</span><span class="cx">         return;
</span><span class="cx">     LOG_WITH_STREAM(ContentObservation, stream << "startObservingDOMTimerExecute: start observing (" << &timer << ") timer callback.");
</span><del>-    startObservingStyleRecalcScheduling();
</del><span class="cx">     m_isObservingContentChanges = true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -79,18 +89,20 @@
</span><span class="cx"> {
</span><span class="cx">     if (!containsObservedDOMTimer(timer))
</span><span class="cx">         return;
</span><ins>+    LOG_WITH_STREAM(ContentObservation, stream << "stopObservingDOMTimerExecute: stop observing (" << &timer << ") timer callback.");
+
</ins><span class="cx">     removeObservedDOMTimer(timer);
</span><del>-    stopObservingStyleRecalcScheduling();
</del><span class="cx">     stopObservingContentChanges();
</span><span class="cx">     auto observedContentChange = this->observedContentChange();
</span><ins>+    auto hasPendingStyleRecalc = WebCore::hasPendingStyleRecalc(m_page);
</ins><span class="cx">     // Check if the timer callback triggered either a sync or async style update.
</span><del>-    auto inDeterminedState = observedContentChange == WKContentVisibilityChange || (!countOfObservedDOMTimers() && observedContentChange == WKContentNoChange);  
-
-    LOG_WITH_STREAM(ContentObservation, stream << "stopObservingDOMTimerExecute: stop observing (" << &timer << ") timer callback.");
-    if (inDeterminedState) {
</del><ins>+    auto hasDeterminedState = observedContentChange == WKContentVisibilityChange || (!countOfObservedDOMTimers() && observedContentChange == WKContentNoChange && !hasPendingStyleRecalc);  
+    if (hasDeterminedState) {
</ins><span class="cx">         LOG_WITH_STREAM(ContentObservation, stream << "stopObservingDOMTimerExecute: (" << &timer << ") in determined state.");
</span><span class="cx">         m_page.chrome().client().observedContentChange(m_page.mainFrame());
</span><del>-    } else if (observedContentChange == WKContentIndeterminateChange) {
</del><ins>+        return;
+    }
+    if (hasPendingStyleRecalc) {
</ins><span class="cx">         // An async style recalc has been scheduled. Let's observe it.
</span><span class="cx">         LOG_WITH_STREAM(ContentObservation, stream << "stopObservingDOMTimerExecute: (" << &timer << ") wait until next style recalc fires.");
</span><span class="cx">         setShouldObserveStyleRecalc(true);
</span><span class="lines">@@ -97,14 +109,6 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ContentChangeObserver::didScheduleStyleRecalc()
-{
-    if (!isObservingStyleRecalcScheduling())
-        return;
-    LOG(ContentObservation, "didScheduleStyleRecalc: register this style recalc schedule and observe when it fires.");
-    setObservedContentChange(WKContentIndeterminateChange);
-}
-
</del><span class="cx"> void ContentChangeObserver::startObservingStyleRecalc()
</span><span class="cx"> {
</span><span class="cx">     if (!shouldObserveStyleRecalc())
</span><span class="lines">@@ -119,8 +123,8 @@
</span><span class="cx">         return;
</span><span class="cx">     LOG(ContentObservation, "stopObservingStyleRecalc: stop observing style recalc");
</span><span class="cx">     setShouldObserveStyleRecalc(false);
</span><del>-    auto inDeterminedState = observedContentChange() == WKContentVisibilityChange || !countOfObservedDOMTimers();
-    if (!inDeterminedState) {
</del><ins>+    auto hasDeterminedState = observedContentChange() == WKContentVisibilityChange || !countOfObservedDOMTimers();
+    if (!hasDeterminedState) {
</ins><span class="cx">         LOG(ContentObservation, "stopObservingStyleRecalc: can't decided it yet.");
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="lines">@@ -154,6 +158,7 @@
</span><span class="cx"> 
</span><span class="cx"> void ContentChangeObserver::startObservingContentChanges()
</span><span class="cx"> {
</span><ins>+    ASSERT(!hasPendingStyleRecalc(m_page));
</ins><span class="cx">     startObservingDOMTimerScheduling();
</span><span class="cx">     resetObservedContentChange();
</span><span class="cx">     clearObservedDOMTimers();
</span></span></pre></div>
<a id="trunkSourceWebCorepageiosContentChangeObserverh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/ios/ContentChangeObserver.h (242289 => 242290)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/ios/ContentChangeObserver.h    2019-03-01 22:18:22 UTC (rev 242289)
+++ trunk/Source/WebCore/page/ios/ContentChangeObserver.h       2019-03-01 22:48:54 UTC (rev 242290)
</span><span class="lines">@@ -45,7 +45,6 @@
</span><span class="cx">     void didInstallDOMTimer(const DOMTimer&, Seconds timeout, bool singleShot);
</span><span class="cx">     void didRemoveDOMTimer(const DOMTimer&);
</span><span class="cx">     void didContentVisibilityChange();
</span><del>-    void didScheduleStyleRecalc();
</del><span class="cx">     void didSuspendActiveDOMObjects();
</span><span class="cx">     void willDetachPage();
</span><span class="cx"> 
</span><span class="lines">@@ -95,10 +94,6 @@
</span><span class="cx">     void removeObservedDOMTimer(const DOMTimer&);
</span><span class="cx">     bool containsObservedDOMTimer(const DOMTimer& timer) const { return m_DOMTimerList.contains(&timer); }
</span><span class="cx"> 
</span><del>-    void startObservingStyleRecalcScheduling() { m_isObservingStyleRecalcScheduling = true; }
-    void stopObservingStyleRecalcScheduling() { m_isObservingStyleRecalcScheduling = false; }
-    bool isObservingStyleRecalcScheduling() const { return m_isObservingStyleRecalcScheduling; }
-
</del><span class="cx">     void setShouldObserveStyleRecalc(bool shouldObserve) { m_shouldObserveStyleRecalc = shouldObserve; }
</span><span class="cx">     bool shouldObserveStyleRecalc() const { return m_shouldObserveStyleRecalc; }
</span><span class="cx"> 
</span><span class="lines">@@ -114,7 +109,6 @@
</span><span class="cx">     Page& m_page;
</span><span class="cx">     HashSet<const DOMTimer*> m_DOMTimerList;
</span><span class="cx">     bool m_shouldObserveStyleRecalc { false };
</span><del>-    bool m_isObservingStyleRecalcScheduling { false };
</del><span class="cx">     bool m_isObservingDOMTimerScheduling { false };
</span><span class="cx">     bool m_isObservingContentChanges { false };
</span><span class="cx"> };
</span></span></pre>
</div>
</div>

</body>
</html>