[First paint] Going from visually empty to non-empty should immediately trigger layer...
authorzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Mar 2020 14:53:01 +0000 (14:53 +0000)
committerzalan@apple.com <zalan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Mar 2020 14:53:01 +0000 (14:53 +0000)
https://bugs.webkit.org/show_bug.cgi?id=208385
<rdar://problem/59895900>

Reviewed by Antti Koivisto.
Source/WebCore:

Now that VNE check and layout are decoupled, we should also decouple the VNE layout milestone and
layer unfreezing.
In many cases a style change does not initiate synchronous layout (see Document::updateStyleIfNeeded) which
potentially delays the first paint. This patch ensure that we unfreeze the layer tree and issue paint soon after
the style change produced a VNE content.

* loader/EmptyFrameLoaderClient.h:
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::didReachVisuallyNonEmptyState):
* loader/FrameLoader.h:
* loader/FrameLoaderClient.h:
* page/FrameView.cpp:
(WebCore::FrameView::styleAndRenderTreeDidChange):
(WebCore::FrameView::checkAndDispatchDidReachVisuallyNonEmptyState):
(WebCore::FrameView::fireLayoutRelatedMilestonesIfNeeded):
(WebCore::FrameView::qualifiesAsVisuallyNonEmpty const): Deleted.
* page/FrameView.h:

Source/WebKit:

* WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
(WebKit::WebFrameLoaderClient::dispatchDidReachLayoutMilestone):
(WebKit::WebFrameLoaderClient::dispatchDidReachVisuallyNonEmptyState):
* WebProcess/WebCoreSupport/WebFrameLoaderClient.h:
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::didCompletePageTransition):
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::resetIdempotentTextAutosizingIfNeeded): We don't have this case anymore.
* WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm:
(WebKit::TiledCoreAnimationDrawingArea::scheduleRenderingUpdate):

LayoutTests:

* fast/scrolling/scroll-animator-overlay-scrollbars-clicked-expected.txt:
* fast/scrolling/scroll-animator-overlay-scrollbars-clicked.html:
* fast/scrolling/scroll-animator-overlay-scrollbars-hovered-expected.txt:
* fast/scrolling/scroll-animator-overlay-scrollbars-hovered.html:

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

19 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/scrolling/scroll-animator-overlay-scrollbars-clicked-expected.txt
LayoutTests/fast/scrolling/scroll-animator-overlay-scrollbars-clicked.html
LayoutTests/fast/scrolling/scroll-animator-overlay-scrollbars-hovered-expected.txt
LayoutTests/fast/scrolling/scroll-animator-overlay-scrollbars-hovered.html
Source/WebCore/ChangeLog
Source/WebCore/dom/Document.cpp
Source/WebCore/loader/EmptyFrameLoaderClient.h
Source/WebCore/loader/FrameLoader.cpp
Source/WebCore/loader/FrameLoader.h
Source/WebCore/loader/FrameLoaderClient.h
Source/WebCore/page/FrameView.cpp
Source/WebCore/page/FrameView.h
Source/WebKit/ChangeLog
Source/WebKit/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp
Source/WebKit/WebProcess/WebCoreSupport/WebFrameLoaderClient.h
Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm
Source/WebKit/WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm
Source/WebKit/mac/WebKit2.order

index fd48d33..aa0992a 100644 (file)
@@ -1,3 +1,16 @@
+2020-03-04  Zalan Bujtas  <zalan@apple.com>
+
+        [First paint] Going from visually empty to non-empty should immediately trigger layer unfreezing
+        https://bugs.webkit.org/show_bug.cgi?id=208385
+        <rdar://problem/59895900>
+
+        Reviewed by Antti Koivisto.
+        
+        * fast/scrolling/scroll-animator-overlay-scrollbars-clicked-expected.txt:
+        * fast/scrolling/scroll-animator-overlay-scrollbars-clicked.html:
+        * fast/scrolling/scroll-animator-overlay-scrollbars-hovered-expected.txt:
+        * fast/scrolling/scroll-animator-overlay-scrollbars-hovered.html:
+
 2020-03-04  Antti Koivisto  <antti@apple.com>
 
         Avoid full style resolution on Element::focus()
index 2685c1b..55eff21 100644 (file)
@@ -1,17 +1,19 @@
-CONSOLE MESSAGE: line 13: MainFrameView: didAddVerticalScrollbar
-CONSOLE MESSAGE: line 13: MainFrameView: didAddHorizontalScrollbar
-CONSOLE MESSAGE: line 13: FrameView: didAddVerticalScrollbar
-CONSOLE MESSAGE: line 13: FrameView: willRemoveVerticalScrollbar
-CONSOLE MESSAGE: line 16: MainFrameView: mouseEnteredContentArea
-CONSOLE MESSAGE: line 16: MainFrameView: mouseMovedInContentArea
-CONSOLE MESSAGE: line 18: MainFrameView: mouseEnteredVerticalScrollbar
-CONSOLE MESSAGE: line 18: FrameView: mouseEnteredContentArea
-CONSOLE MESSAGE: line 18: MainFrameView: mouseMovedInContentArea
-CONSOLE MESSAGE: line 19: MainFrameView: mouseIsDownInVerticalScrollbar
-CONSOLE MESSAGE: line 20: MainFrameView: mouseIsUpInVerticalScrollbar
-CONSOLE MESSAGE: line 21: MainFrameView: mouseExitedVerticalScrollbar
-CONSOLE MESSAGE: line 21: FrameView: mouseExitedContentArea
+CONSOLE MESSAGE: MainFrameView: didAddVerticalScrollbar
+CONSOLE MESSAGE: MainFrameView: didAddHorizontalScrollbar
+CONSOLE MESSAGE: FrameView: didAddVerticalScrollbar
+CONSOLE MESSAGE: FrameView: willRemoveVerticalScrollbar
+CONSOLE MESSAGE: FrameView: didAddVerticalScrollbar
+CONSOLE MESSAGE: FrameView: willRemoveVerticalScrollbar
+CONSOLE MESSAGE: line 19: MainFrameView: mouseEnteredContentArea
+CONSOLE MESSAGE: line 19: MainFrameView: mouseMovedInContentArea
+CONSOLE MESSAGE: line 21: MainFrameView: mouseEnteredVerticalScrollbar
+CONSOLE MESSAGE: line 21: FrameView: mouseEnteredContentArea
 CONSOLE MESSAGE: line 21: MainFrameView: mouseMovedInContentArea
+CONSOLE MESSAGE: line 22: MainFrameView: mouseIsDownInVerticalScrollbar
+CONSOLE MESSAGE: line 23: MainFrameView: mouseIsUpInVerticalScrollbar
+CONSOLE MESSAGE: line 24: MainFrameView: mouseExitedVerticalScrollbar
+CONSOLE MESSAGE: line 24: FrameView: mouseExitedContentArea
+CONSOLE MESSAGE: line 24: MainFrameView: mouseMovedInContentArea
 PASS successfullyParsed is true
 
 TEST COMPLETE
index f064d70..3ed8a54 100644 (file)
@@ -1,34 +1,37 @@
 <html>
 <script src="../../resources/js-test-pre.js"></script>
 <script>
-  if (window.internals) {
-      window.internals.setUsesMockScrollAnimator(true);
-      window.internals.setUsesOverlayScrollbars(true);
-  }
-  if (window.testRunner) {
-      testRunner.waitUntilDone();
-      testRunner.dumpAsText();
-  }
-  window.onload = function () {
-      document.body.offsetTop; // force layout.
-      setTimeout(() => {
-      if (window.eventSender) {
-          eventSender.mouseMoveTo(0, 0);
-          // Move to a position that is over the vertical scrollbar and the iframe.
-          eventSender.mouseMoveTo(window.innerWidth - 4, 100);
-          eventSender.mouseDown();
-          eventSender.mouseUp();
-          eventSender.mouseMoveTo(0, 0);
-      }
-      if (window.testRunner)
-          testRunner.notifyDone();
-     }, 0);
-  };
+if (window.internals) {
+    window.internals.setUsesMockScrollAnimator(true);
+    window.internals.setUsesOverlayScrollbars(true);
+}
+
+if (window.testRunner) {
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+}
+
+function runTest() {
+    setTimeout(() => {
+        if (window.internals)
+            internals.updateLayoutAndStyleForAllFrames();
+        if (window.eventSender) {
+            eventSender.mouseMoveTo(0, 0);
+            // Move to a position that is over the vertical scrollbar and the iframe.
+            eventSender.mouseMoveTo(window.innerWidth - 4, 100);
+            eventSender.mouseDown();
+            eventSender.mouseUp();
+            eventSender.mouseMoveTo(0, 0);
+        }
+        if (window.testRunner)
+            testRunner.notifyDone();
+    }, 0);
+};
 </script>
 <script src="../../resources/js-test-post.js"></script>
 <body>
   <p>Test for <a href="https://bugs.webkit.org/show_bug.cgi?id=165056">https://bugs.webkit.org/show_bug.cgi?id=165056</a>.</p>
-  <iframe width="1024" height="768" id="frame" src="data:text/html,<html></html>"></iframe>
+  <iframe onload="runTest()" width="1024" height="768" id="frame" src="data:text/html,<html></html>"></iframe>
   <pre id="console"></pre>
 </body>
 </html>
index 43645b3..2693a10 100644 (file)
@@ -1,15 +1,17 @@
-CONSOLE MESSAGE: line 15: MainFrameView: didAddVerticalScrollbar
-CONSOLE MESSAGE: line 15: FrameView: didAddVerticalScrollbar
-CONSOLE MESSAGE: line 15: FrameView: didAddHorizontalScrollbar
-CONSOLE MESSAGE: line 15: FrameView: willRemoveVerticalScrollbar
-CONSOLE MESSAGE: line 15: MainFrameView: mouseEnteredContentArea
-CONSOLE MESSAGE: line 15: MainFrameView: mouseMovedInContentArea
-CONSOLE MESSAGE: line 16: FrameView: mouseEnteredHorizontalScrollbar
-CONSOLE MESSAGE: line 16: FrameView: mouseEnteredContentArea
-CONSOLE MESSAGE: line 16: MainFrameView: mouseMovedInContentArea
-CONSOLE MESSAGE: line 17: FrameView: mouseExitedHorizontalScrollbar
-CONSOLE MESSAGE: line 17: FrameView: mouseExitedContentArea
+CONSOLE MESSAGE: MainFrameView: didAddVerticalScrollbar
+CONSOLE MESSAGE: FrameView: didAddVerticalScrollbar
+CONSOLE MESSAGE: FrameView: willRemoveVerticalScrollbar
+CONSOLE MESSAGE: line 14: FrameView: didAddVerticalScrollbar
+CONSOLE MESSAGE: line 14: FrameView: didAddHorizontalScrollbar
+CONSOLE MESSAGE: line 14: FrameView: willRemoveVerticalScrollbar
+CONSOLE MESSAGE: line 17: MainFrameView: mouseEnteredContentArea
 CONSOLE MESSAGE: line 17: MainFrameView: mouseMovedInContentArea
+CONSOLE MESSAGE: line 18: FrameView: mouseEnteredHorizontalScrollbar
+CONSOLE MESSAGE: line 18: FrameView: mouseEnteredContentArea
+CONSOLE MESSAGE: line 18: MainFrameView: mouseMovedInContentArea
+CONSOLE MESSAGE: line 19: FrameView: mouseExitedHorizontalScrollbar
+CONSOLE MESSAGE: line 19: FrameView: mouseExitedContentArea
+CONSOLE MESSAGE: line 19: MainFrameView: mouseMovedInContentArea
 PASS successfullyParsed is true
 
 TEST COMPLETE
index cb5e0ac..10b618b 100644 (file)
@@ -1,29 +1,31 @@
 <html>
 <script src="../../resources/js-test-pre.js"></script>
 <script>
-  if (window.internals) {
-      window.internals.setUsesMockScrollAnimator(true);
-      window.internals.setUsesOverlayScrollbars(true);
-  }
-  if (window.testRunner) {
-      testRunner.waitUntilDone();
-      testRunner.dumpAsText();
-  }
-  window.onload = function () {
-      var frame = document.getElementById("frame");
-      if (window.eventSender) {
-          eventSender.mouseMoveTo(0, 0);
-          eventSender.mouseMoveTo(frame.offsetLeft + 4, frame.offsetTop + frame.offsetHeight - 4);
-          eventSender.mouseMoveTo(0, 0);
-      }
-      if (window.testRunner)
-          testRunner.notifyDone();
-  };
+if (window.internals) {
+    window.internals.setUsesMockScrollAnimator(true);
+    window.internals.setUsesOverlayScrollbars(true);
+}
+if (window.testRunner) {
+    testRunner.waitUntilDone();
+    testRunner.dumpAsText();
+}
+function runTest() {
+    if (window.internals)
+        internals.updateLayoutAndStyleForAllFrames();
+    document.body.offsetHeight;
+    if (window.eventSender) {
+        eventSender.mouseMoveTo(0, 0);
+        eventSender.mouseMoveTo(frame.offsetLeft + 4, frame.offsetTop + frame.offsetHeight - 4);
+        eventSender.mouseMoveTo(0, 0);
+    }
+    if (window.testRunner)
+        testRunner.notifyDone();
+};
 </script>
 <script src="../../resources/js-test-post.js"></script>
 <body>
   <p>Test for <a href="https://bugs.webkit.org/show_bug.cgi?id=153304">https://bugs.webkit.org/show_bug.cgi?id=153304</a>.</p>
-  <iframe width=120 scrolling=yes id="frame" src="data:text/html,
+  <iframe onload="runTest()" width=120 scrolling=yes id="frame" src="data:text/html,
       <p style='white-space: nowrap'>Scroll animator should be notified when overlay scrollbars in main frame are hovered</p>
       ">
   </iframe>
index 8f21e7b..717c3e5 100644 (file)
@@ -1,3 +1,29 @@
+2020-03-04  Zalan Bujtas  <zalan@apple.com>
+
+        [First paint] Going from visually empty to non-empty should immediately trigger layer unfreezing
+        https://bugs.webkit.org/show_bug.cgi?id=208385
+        <rdar://problem/59895900>
+
+        Reviewed by Antti Koivisto.
+
+        Now that VNE check and layout are decoupled, we should also decouple the VNE layout milestone and
+        layer unfreezing.
+        In many cases a style change does not initiate synchronous layout (see Document::updateStyleIfNeeded) which
+        potentially delays the first paint. This patch ensure that we unfreeze the layer tree and issue paint soon after
+        the style change produced a VNE content. 
+
+        * loader/EmptyFrameLoaderClient.h:
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::didReachVisuallyNonEmptyState):
+        * loader/FrameLoader.h:
+        * loader/FrameLoaderClient.h:
+        * page/FrameView.cpp:
+        (WebCore::FrameView::styleAndRenderTreeDidChange):
+        (WebCore::FrameView::checkAndDispatchDidReachVisuallyNonEmptyState):
+        (WebCore::FrameView::fireLayoutRelatedMilestonesIfNeeded):
+        (WebCore::FrameView::qualifiesAsVisuallyNonEmpty const): Deleted.
+        * page/FrameView.h:
+
 2020-03-04  Antti Koivisto  <antti@apple.com>
 
         Avoid full style resolution on Element::focus()
index 78f6a6c..8e1b040 100644 (file)
@@ -1356,7 +1356,7 @@ void Document::setVisualUpdatesAllowed(bool visualUpdatesAllowed)
         renderView->repaintViewAndCompositedLayers();
 
     if (Frame* frame = this->frame())
-        frame->loader().forcePageTransitionIfNeeded();
+        frame->loader().completePageTransitionIfNeeded();
 }
 
 void Document::visualUpdatesSuppressionTimerFired()
index df62ba0..1d9f113 100644 (file)
@@ -95,6 +95,7 @@ class WEBCORE_EXPORT EmptyFrameLoaderClient : public FrameLoaderClient {
     void dispatchDidFinishDocumentLoad() final { }
     void dispatchDidFinishLoad() final { }
     void dispatchDidReachLayoutMilestone(OptionSet<LayoutMilestone>) final { }
+    void dispatchDidReachVisuallyNonEmptyState() final { }
 
     Frame* dispatchCreatePage(const NavigationAction&) final { return nullptr; }
     void dispatchShow() final { }
index 9c18d76..247d9ae 100644 (file)
@@ -2692,6 +2692,12 @@ void FrameLoader::didFirstLayout()
         m_stateMachine.advanceTo(FrameLoaderStateMachine::FirstLayoutDone);
 }
 
+void FrameLoader::didReachVisuallyNonEmptyState()
+{
+    ASSERT(m_frame.isMainFrame());
+    m_client.dispatchDidReachVisuallyNonEmptyState();
+}
+
 void FrameLoader::frameLoadCompleted()
 {
     // Note: Can be called multiple times.
@@ -4073,9 +4079,9 @@ void FrameLoader::loadProgressingStatusChanged()
         view->loadProgressingStatusChanged();
 }
 
-void FrameLoader::forcePageTransitionIfNeeded()
+void FrameLoader::completePageTransitionIfNeeded()
 {
-    m_client.forcePageTransitionIfNeeded();
+    m_client.completePageTransitionIfNeeded();
 }
 
 void FrameLoader::clearTestingOverrides()
index 7af42b5..6d04a31 100644 (file)
@@ -211,6 +211,7 @@ public:
 
     void didReachLayoutMilestone(OptionSet<LayoutMilestone>);
     void didFirstLayout();
+    void didReachVisuallyNonEmptyState();
 
     void loadedResourceFromMemoryCache(CachedResource&, ResourceRequest& newRequest, ResourceError&);
     void tellClientAboutPastMemoryCacheLoads();
@@ -311,7 +312,7 @@ public:
 
     const URL& previousURL() const { return m_previousURL; }
 
-    void forcePageTransitionIfNeeded();
+    void completePageTransitionIfNeeded();
 
     void setOverrideCachePolicyForTesting(ResourceRequestCachePolicy policy) { m_overrideCachePolicyForTesting = policy; }
     void setOverrideResourceLoadPriorityForTesting(ResourceLoadPriority priority) { m_overrideResourceLoadPriorityForTesting = priority; }
index a968ac8..841bb24 100644 (file)
@@ -187,6 +187,7 @@ public:
 
     virtual void dispatchDidLayout() { }
     virtual void dispatchDidReachLayoutMilestone(OptionSet<LayoutMilestone>) { }
+    virtual void dispatchDidReachVisuallyNonEmptyState() { }
 
     virtual Frame* dispatchCreatePage(const NavigationAction&) = 0;
     virtual void dispatchShow() = 0;
@@ -346,7 +347,7 @@ public:
     virtual WebGLLoadPolicy resolveWebGLPolicyForURL(const URL&) const { return WebGLAllowCreation; }
 #endif
 
-    virtual void forcePageTransitionIfNeeded() { }
+    virtual void completePageTransitionIfNeeded() { }
 
     // FIXME (bug 116233): We need to get rid of EmptyFrameLoaderClient completely, then this will no longer be needed.
     virtual bool isEmptyFrameLoaderClient() const { return false; }
index 79d4d2e..cbabea3 100644 (file)
@@ -809,10 +809,7 @@ void FrameView::styleAndRenderTreeDidChange()
     if (!renderView)
         return;
 
-    if (!m_contentQualifiesAsVisuallyNonEmpty) {
-        // Let's check whether the newly constructed content qualifies as non-empty now.
-        m_contentQualifiesAsVisuallyNonEmpty = qualifiesAsVisuallyNonEmpty();
-    }
+    checkAndDispatchDidReachVisuallyNonEmptyState();
     auto* layerTreeMutationRoot = renderView->takeStyleChangeLayerTreeMutationRoot();
     if (layerTreeMutationRoot && !needsLayout())
         layerTreeMutationRoot->updateLayerPositionsAfterStyleChange();
@@ -4377,70 +4374,82 @@ bool FrameView::qualifiesAsSignificantRenderedText() const
     return m_hasReachedSignificantRenderedTextThreshold;
 }
 
-bool FrameView::qualifiesAsVisuallyNonEmpty() const
+void FrameView::checkAndDispatchDidReachVisuallyNonEmptyState()
 {
-    // No content yet.
-    Element* documentElement = frame().document()->documentElement();
-    if (!documentElement || !documentElement->renderer())
-        return false;
+    auto qualifiesAsVisuallyNonEmpty = [&] {
+        // No content yet.
+        Element* documentElement = frame().document()->documentElement();
+        if (!documentElement || !documentElement->renderer())
+            return false;
 
-    // FIXME: We should also ignore renderers with non-final style.
-    if (frame().document()->styleScope().hasPendingSheetsBeforeBody())
-        return false;
+        // FIXME: We should also ignore renderers with non-final style.
+        if (frame().document()->styleScope().hasPendingSheetsBeforeBody())
+            return false;
 
-    auto finishedParsingMainDocument = frame().loader().stateMachine().committedFirstRealDocumentLoad() && (frame().document()->readyState() == Document::Interactive || frame().document()->readyState() == Document::Complete);
-    // Ensure that we always fire visually non-empty milestone eventually.
-    if (finishedParsingMainDocument && frame().loader().isComplete())
-        return true;
+        auto finishedParsingMainDocument = frame().loader().stateMachine().committedFirstRealDocumentLoad() && (frame().document()->readyState() == Document::Interactive || frame().document()->readyState() == Document::Complete);
+        // Ensure that we always fire visually non-empty milestone eventually.
+        if (finishedParsingMainDocument && frame().loader().isComplete())
+            return true;
+
+        auto isVisible = [](const Element* element) {
+            if (!element || !element->renderer())
+                return false;
+            if (!element->renderer()->opacity())
+                return false;
+            return element->renderer()->style().visibility() == Visibility::Visible;
+        };
 
-    auto isVisible = [](const Element* element) {
-        if (!element || !element->renderer())
+        if (!isVisible(documentElement))
             return false;
-        if (!element->renderer()->opacity())
+
+        if (!isVisible(frame().document()->body()))
             return false;
-        return element->renderer()->style().visibility() == Visibility::Visible;
-    };
 
-    if (!isVisible(documentElement))
-        return false;
+        // The first few hundred characters rarely contain the interesting content of the page.
+        if (m_visuallyNonEmptyCharacterCount > visualCharacterThreshold)
+            return true;
 
-    if (!isVisible(frame().document()->body()))
-        return false;
+        // Use a threshold value to prevent very small amounts of visible content from triggering didFirstVisuallyNonEmptyLayout
+        if (m_visuallyNonEmptyPixelCount > visualPixelThreshold)
+            return true;
 
-    // The first few hundred characters rarely contain the interesting content of the page.
-    if (m_visuallyNonEmptyCharacterCount > visualCharacterThreshold)
-        return true;
+        auto isMoreContentExpected = [&]() {
+            ASSERT(finishedParsingMainDocument);
+            // Pending css/font loading means we should wait a little longer. Classic non-async, non-defer scripts are all processed by now.
+            auto* documentLoader = frame().loader().documentLoader();
+            if (!documentLoader)
+                return false;
 
-    // Use a threshold value to prevent very small amounts of visible content from triggering didFirstVisuallyNonEmptyLayout
-    if (m_visuallyNonEmptyPixelCount > visualPixelThreshold)
-        return true;
+            auto& resourceLoader = documentLoader->cachedResourceLoader();
+            if (!resourceLoader.requestCount())
+                return false;
 
-    auto isMoreContentExpected = [&]() {
-        ASSERT(finishedParsingMainDocument);
-        // Pending css/font loading means we should wait a little longer. Classic non-async, non-defer scripts are all processed by now.
-        auto* documentLoader = frame().loader().documentLoader();
-        if (!documentLoader)
+            auto& resources = resourceLoader.allCachedResources();
+            for (auto& resource : resources) {
+                if (resource.value->isLoaded())
+                    continue;
+                if (resource.value->type() == CachedResource::Type::CSSStyleSheet || resource.value->type() == CachedResource::Type::FontResource)
+                    return true;
+            }
             return false;
+        };
 
-        auto& resourceLoader = documentLoader->cachedResourceLoader();
-        if (!resourceLoader.requestCount())
-            return false;
+        // Finished parsing the main document and we still don't yet have enough content. Check if we might be getting some more.
+        if (finishedParsingMainDocument)
+            return !isMoreContentExpected();
 
-        auto& resources = resourceLoader.allCachedResources();
-        for (auto& resource : resources) {
-            if (resource.value->isLoaded())
-                continue;
-            if (resource.value->type() == CachedResource::Type::CSSStyleSheet || resource.value->type() == CachedResource::Type::FontResource)
-                return true;
-        }
         return false;
     };
 
-    // Finished parsing the main document and we still don't yet have enough content. Check if we might be getting some more.
-    if (finishedParsingMainDocument)
-        return !isMoreContentExpected();
+    if (m_contentQualifiesAsVisuallyNonEmpty)
+        return;
 
-    return false;
+    if (!qualifiesAsVisuallyNonEmpty())
+        return;
+
+    m_contentQualifiesAsVisuallyNonEmpty = true;
+    if (frame().isMainFrame())
+        frame().loader().didReachVisuallyNonEmptyState();
 }
 
 bool FrameView::isViewForDocumentInFrame() const
@@ -5090,9 +5099,7 @@ void FrameView::fireLayoutRelatedMilestonesIfNeeded()
     }
 
     if (m_firstVisuallyNonEmptyLayoutMilestoneIsPending) {
-        if (!m_contentQualifiesAsVisuallyNonEmpty)
-            m_contentQualifiesAsVisuallyNonEmpty = qualifiesAsVisuallyNonEmpty();
-
+        checkAndDispatchDidReachVisuallyNonEmptyState();
         if (m_contentQualifiesAsVisuallyNonEmpty) {
             m_firstVisuallyNonEmptyLayoutMilestoneIsPending = false;
 
index 2c50cbf..8f2c57e 100644 (file)
@@ -397,7 +397,7 @@ public:
     void incrementVisuallyNonEmptyCharacterCount(const String&);
     void incrementVisuallyNonEmptyPixelCount(const IntSize&);
     bool isVisuallyNonEmpty() const { return m_contentQualifiesAsVisuallyNonEmpty; }
-    WEBCORE_EXPORT bool qualifiesAsVisuallyNonEmpty() const;
+    void checkAndDispatchDidReachVisuallyNonEmptyState();
 
     WEBCORE_EXPORT void enableAutoSizeMode(bool enable, const IntSize& minSize);
     WEBCORE_EXPORT void setAutoSizeFixedMinimumHeight(int);
index 7f4977f..f9a5c4f 100644 (file)
@@ -1,3 +1,22 @@
+2020-03-04  Zalan Bujtas  <zalan@apple.com>
+
+        [First paint] Going from visually empty to non-empty should immediately trigger layer unfreezing
+        https://bugs.webkit.org/show_bug.cgi?id=208385
+        <rdar://problem/59895900>
+
+        Reviewed by Antti Koivisto.
+
+        * WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
+        (WebKit::WebFrameLoaderClient::dispatchDidReachLayoutMilestone):
+        (WebKit::WebFrameLoaderClient::dispatchDidReachVisuallyNonEmptyState):
+        * WebProcess/WebCoreSupport/WebFrameLoaderClient.h:
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::didCompletePageTransition):
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::resetIdempotentTextAutosizingIfNeeded): We don't have this case anymore.
+        * WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm:
+        (WebKit::TiledCoreAnimationDrawingArea::scheduleRenderingUpdate):
+
 2020-03-03  Devin Rousso  <drousso@apple.com>
 
         Web Inspector: re-add `InspectorFrontendHost` support for moving the inspected window
index 5f76dbd..174be71 100644 (file)
@@ -680,17 +680,18 @@ void WebFrameLoaderClient::dispatchDidFinishLoad()
     webPage->didFinishLoad(*m_frame);
 }
 
-void WebFrameLoaderClient::forcePageTransitionIfNeeded()
+void WebFrameLoaderClient::completePageTransitionIfNeeded()
 {
     if (m_didCompletePageTransition)
         return;
 
-    WebPage* webPage = m_frame->page();
+    auto* webPage = m_frame->page();
     if (!webPage)
         return;
 
     webPage->didCompletePageTransition();
     m_didCompletePageTransition = true;
+    WEBFRAMELOADERCLIENT_RELEASE_LOG(Layout, "completePageTransitionIfNeeded: dispatching didCompletePageTransition");
 }
 
 void WebFrameLoaderClient::dispatchDidReachLayoutMilestone(OptionSet<WebCore::LayoutMilestone> milestones)
@@ -742,20 +743,22 @@ void WebFrameLoaderClient::dispatchDidReachLayoutMilestone(OptionSet<WebCore::La
     webPage->dispatchDidReachLayoutMilestone(milestones);
 
     if (milestones & DidFirstVisuallyNonEmptyLayout) {
-        if (m_frame->isMainFrame() && !m_didCompletePageTransition && !webPage->corePage()->settings().suppressesIncrementalRendering()) {
-            WEBFRAMELOADERCLIENT_RELEASE_LOG(Layout, "dispatchDidReachLayoutMilestone: dispatching didCompletePageTransition");
-            webPage->didCompletePageTransition();
-            m_didCompletePageTransition = true;
-        }
-
-        // FIXME: We should consider removing the old didFirstVisuallyNonEmptyLayoutForFrame API since this is doing
-        // double duty with the new didLayout API.
+        ASSERT(!m_frame->isMainFrame() || webPage->corePage()->settings().suppressesIncrementalRendering() || m_didCompletePageTransition);
+        // FIXME: We should consider removing the old didFirstVisuallyNonEmptyLayoutForFrame API since this is doing double duty with the new didLayout API.
         WEBFRAMELOADERCLIENT_RELEASE_LOG(Layout, "dispatchDidReachLayoutMilestone: dispatching DidFirstVisuallyNonEmptyLayoutForFrame");
         webPage->injectedBundleLoaderClient().didFirstVisuallyNonEmptyLayoutForFrame(*webPage, *m_frame, userData);
         webPage->send(Messages::WebPageProxy::DidFirstVisuallyNonEmptyLayoutForFrame(m_frame->frameID(), UserData(WebProcess::singleton().transformObjectsToHandles(userData.get()).get())));
     }
 }
 
+void WebFrameLoaderClient::dispatchDidReachVisuallyNonEmptyState()
+{
+    if (!m_frame->page() || m_frame->page()->corePage()->settings().suppressesIncrementalRendering())
+        return;
+    ASSERT(m_frame->isMainFrame());
+    completePageTransitionIfNeeded();
+}
+
 void WebFrameLoaderClient::dispatchDidLayout()
 {
     WebPage* webPage = m_frame->page();
@@ -1372,14 +1375,9 @@ String WebFrameLoaderClient::generatedMIMETypeForURLScheme(const String& /*URLSc
 void WebFrameLoaderClient::frameLoadCompleted()
 {
     // Note: Can be called multiple times.
-    WebPage* webPage = m_frame->page();
-    if (!webPage)
+    if (!m_frame->isMainFrame())
         return;
-
-    if (m_frame->isMainFrame() && !m_didCompletePageTransition) {
-        webPage->didCompletePageTransition();
-        m_didCompletePageTransition = true;
-    }
+    completePageTransitionIfNeeded();
 }
 
 void WebFrameLoaderClient::saveViewStateToItem(HistoryItem& historyItem)
index 50b4280..6c894f0 100644 (file)
@@ -125,6 +125,7 @@ private:
     void dispatchDidExplicitOpen(const URL&, const String& mimeType) final;
 
     void dispatchDidReachLayoutMilestone(OptionSet<WebCore::LayoutMilestone>) final;
+    void dispatchDidReachVisuallyNonEmptyState() final;
     void dispatchDidLayout() final;
 
     WebCore::Frame* dispatchCreatePage(const WebCore::NavigationAction&) final;
@@ -256,7 +257,7 @@ private:
 
     Ref<WebCore::FrameNetworkingContext> createNetworkingContext() final;
 
-    void forcePageTransitionIfNeeded() final;
+    void completePageTransitionIfNeeded() final;
 
 #if USE(QUICK_LOOK)
     RefPtr<WebCore::LegacyPreviewLoaderClient> createPreviewLoaderClient(const String& fileName, const String& uti) final;
index a9764be..b79aa0f 100644 (file)
@@ -3500,15 +3500,8 @@ void WebPage::resetIdempotentTextAutosizingIfNeeded(double previousInitialScale)
 
     auto textAutoSizingDelay = [&] {
         auto& frameView = *m_page->mainFrame().view();
-        auto isVisaullyNonEmpty = frameView.isVisuallyNonEmpty();
-        auto willBeVisuallyNonEmptySoon = !isVisaullyNonEmpty && frameView.qualifiesAsVisuallyNonEmpty();
-        if (willBeVisuallyNonEmptySoon) {
-            // Be a bit more agressive on the first display.
-            const Seconds shortTextAutoSizingDelayOnViewportChange = 20_ms;
-            return shortTextAutoSizingDelayOnViewportChange;
-        } 
-        if (!isVisaullyNonEmpty) {
-            // We don't anticipate any paining after the next upcoming layout.
+        if (!frameView.isVisuallyNonEmpty()) {
+            // We don't anticipate any painting after the next upcoming layout.
             const Seconds longTextAutoSizingDelayOnViewportChange = 100_ms;
             return longTextAutoSizingDelayOnViewportChange;
         }
index 05e477e..b2a6d3d 100644 (file)
@@ -196,10 +196,8 @@ bool TiledCoreAnimationDrawingArea::layerTreeStateIsFrozen() const
 
 void TiledCoreAnimationDrawingArea::scheduleRenderingUpdate()
 {
-    if (m_layerTreeStateIsFrozen) {
+    if (m_layerTreeStateIsFrozen)
         return;
-    }
-
     scheduleRenderingUpdateRunLoopObserver();
 }
 
index 3479995..7c305f4 100644 (file)
@@ -5329,7 +5329,7 @@ __ZN7CoreIPC10Arguments2IN7WebCore9FloatRectEbE6decodeERNS_15ArgumentDecoderERS3
 __ZN6WebKit7WebPage22viewExposedRectChangedERKN7WebCore9FloatRectEb
 __ZN6WebKit29TiledCoreAnimationDrawingArea14setExposedRectERKN7WebCore9FloatRectE
 __ZN6WebKit29TiledCoreAnimationDrawingArea21setClipsToExposedRectEb
-__ZN6WebKit20WebFrameLoaderClient27forcePageTransitionIfNeededEv
+__ZN6WebKit20WebFrameLoaderClient27completePageTransitionIfNeededEv
 __ZN7WebCore5TimerIN6WebKit29TiledCoreAnimationDrawingAreaEE5firedEv
 __ZN6WebKit29TiledCoreAnimationDrawingArea36updateIntrinsicContentSizeTimerFiredEPN7WebCore5TimerIS0_EE
 __ZN7CoreIPC13MessageSender4sendIN8Messages16DrawingAreaProxy29IntrinsicContentSizeDidChangeEEEbRKT_y