Source/WebCore:
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 10 Mar 2017 16:01:50 +0000 (16:01 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 10 Mar 2017 16:01:50 +0000 (16:01 +0000)
Allow the page to render before <link> stylesheet tags in body
https://bugs.webkit.org/show_bug.cgi?id=149157
<rdar://problem/24658830>

Reviewed by Simon Fraser.

Currently we block style and renderer building completely if document has any loading
stylesheets. In case a script queries something layout dependent we construct the render
tree with whatever style we have but block painting in it.

This patch changes behavior so that a loading stylesheet in body only blocks rendering for elements
that are after it. The expectation is that such stylesheets rarely affect elements before them
and the elements can be rendered without causing ugly visible styling changes.

The patch replaces the old flash-of-unstyled-content (FOUC) preventation mechanism with a more
fine-grained one. Paint blocking is now done on per-renderer basis with based on isNonFinal flag in
RenderStyle.

For stylesheets in head the behavior should be largely unchanged.

Test: http/tests/incremental/stylesheet-body-incremental-rendering.html

* css/StyleResolver.cpp:
(WebCore::StyleResolver::pseudoStyleRulesForElement):
* dom/Document.cpp:
(WebCore::Document::Document):
(WebCore::Document::resolveStyle):
(WebCore::Document::updateLayoutIgnorePendingStylesheets):

    Remove the old FOUC preventation state tracking.

(WebCore::Document::shouldScheduleLayout):
(WebCore::Document::didRemoveAllPendingStylesheet):

    Repaints will now get triggered by the normal style mechanism.

* dom/Document.h:
(WebCore::Document::hasNodesWithNonFinalStyle):
(WebCore::Document::setHasNodesWithNonFinalStyle):

    Track if we need to recompute the style later because non-final or unstyled elements.

(WebCore::Document::didLayoutWithPendingStylesheets): Deleted.
(WebCore::Document::hasNodesWithPlaceholderStyle): Deleted.
(WebCore::Document::setHasNodesWithPlaceholderStyle): Deleted.
* html/HTMLFrameSetElement.cpp:
(WebCore::HTMLFrameSetElement::rendererIsNeeded):
* page/FrameView.cpp:
(WebCore::FrameView::qualifiesAsVisuallyNonEmpty):

    Don't qualify as visually non-empty if we have loading stylesheets in head (even if there is
    a fouc-prevented render tree).

(WebCore::FrameView::fireLayoutRelatedMilestonesIfNeeded):
* rendering/RenderBlock.cpp:
(WebCore::RenderBlock::paintContents):

    Instead of a global test, block painting if isNonFinal is set in the renderer's style.

* rendering/RenderLayer.cpp:
(WebCore::shouldSuppressPaintingLayer):
* rendering/style/RenderStyle.cpp:
(WebCore::RenderStyle::changeRequiresRepaint):

    The isNonFinal flag prevents painting so we need to trigger repaint when it gets cleared.

* rendering/style/RenderStyle.h:
(WebCore::RenderStyle::isNotFinal):
(WebCore::RenderStyle::setIsNotFinal):
(WebCore::RenderStyle::isPlaceholderStyle): Deleted.
(WebCore::RenderStyle::setIsPlaceholderStyle): Deleted.

    There is no need for placeholder styles anymore. Reuse the bit for isNotFinal.

* rendering/style/StyleRareNonInheritedData.cpp:
(WebCore::StyleRareNonInheritedData::StyleRareNonInheritedData):
(WebCore::StyleRareNonInheritedData::operator==):
* rendering/style/StyleRareNonInheritedData.h:
* style/StyleScope.cpp:
(WebCore::Style::Scope::analyzeStyleSheetChange):
(WebCore::Style::Scope::updateActiveStyleSheets):
* style/StyleTreeResolver.cpp:
(WebCore::Style::TreeResolver::styleForElement):
(WebCore::Style::TreeResolver::resolveElement):

    If we have seens a loading stylesheet and don't have a renderer yet don't style the element.
    In case there is a renderer or we are ignoring pending sheets, resolve the style normally
    but mark it as non-final.

(WebCore::Style::makePlaceholderStyle): Deleted.

LayoutTests:
Loading in-body stylesheets should not block rendering of elements before them
https://bugs.webkit.org/show_bug.cgi?id=169345

Reviewed by Simon Fraser.

* http/tests/incremental/resources/delayed-css.php: Added.
* http/tests/incremental/stylesheet-body-incremental-rendering-expected.html: Added.
* http/tests/incremental/stylesheet-body-incremental-rendering.html: Added.

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

18 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/incremental/resources/delayed-css.php [new file with mode: 0644]
LayoutTests/http/tests/incremental/stylesheet-body-incremental-rendering-expected.html [new file with mode: 0644]
LayoutTests/http/tests/incremental/stylesheet-body-incremental-rendering.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/css/StyleResolver.cpp
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Document.h
Source/WebCore/html/HTMLFrameSetElement.cpp
Source/WebCore/page/FrameView.cpp
Source/WebCore/rendering/RenderBlock.cpp
Source/WebCore/rendering/RenderLayer.cpp
Source/WebCore/rendering/style/RenderStyle.cpp
Source/WebCore/rendering/style/RenderStyle.h
Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp
Source/WebCore/rendering/style/StyleRareNonInheritedData.h
Source/WebCore/style/StyleScope.cpp
Source/WebCore/style/StyleTreeResolver.cpp

index 9b5c105..c7ceca9 100644 (file)
@@ -1,3 +1,14 @@
+2017-03-10  Antti Koivisto  <antti@apple.com>
+
+        Loading in-body stylesheets should not block rendering of elements before them
+        https://bugs.webkit.org/show_bug.cgi?id=169345
+
+        Reviewed by Simon Fraser.
+
+        * http/tests/incremental/resources/delayed-css.php: Added.
+        * http/tests/incremental/stylesheet-body-incremental-rendering-expected.html: Added.
+        * http/tests/incremental/stylesheet-body-incremental-rendering.html: Added.
+
 2017-03-10  Antoine Quint  <graouts@apple.com>
 
         [mac-wk1] LayoutTest media/modern-media-controls/airplay-button/airplay-button.html is a flaky timeout
diff --git a/LayoutTests/http/tests/incremental/resources/delayed-css.php b/LayoutTests/http/tests/incremental/resources/delayed-css.php
new file mode 100644 (file)
index 0000000..e7da449
--- /dev/null
@@ -0,0 +1,8 @@
+<?php
+header("Cache-Control: no-cache, no-store");
+header("Content-Type: text/css");
+
+$delay = $_GET['delay'];
+usleep($delay * 1000);
+?>
+.delayed { background-color: green !important }
diff --git a/LayoutTests/http/tests/incremental/stylesheet-body-incremental-rendering-expected.html b/LayoutTests/http/tests/incremental/stylesheet-body-incremental-rendering-expected.html
new file mode 100644 (file)
index 0000000..a1175e4
--- /dev/null
@@ -0,0 +1,6 @@
+<div style="width:100px; height:100px; background-color:green"></div>
+<div style="width:100px; height:100px; background-color:green"></div>
+<p>
+Before stylesheet load: (repaint rects (rect 8 8 100 100) )<br>
+After stylesheet load: (repaint rects (rect 8 108 100 100) )<br>
+</p>
diff --git a/LayoutTests/http/tests/incremental/stylesheet-body-incremental-rendering.html b/LayoutTests/http/tests/incremental/stylesheet-body-incremental-rendering.html
new file mode 100644 (file)
index 0000000..cc53587
--- /dev/null
@@ -0,0 +1,24 @@
+<body>
+<script>
+document.body.offsetWidth;
+
+internals.startTrackingRepaints();
+let initialRects;
+setTimeout(() => {
+   initialRects = internals.repaintRectsAsText();
+   internals.stopTrackingRepaints();
+   internals.startTrackingRepaints();
+}, 0);
+
+document.body.onload = () => {
+    let finalRects = internals.repaintRectsAsText();
+    internals.stopTrackingRepaints();
+    document.body.innerHTML += `<p>
+        Before stylesheet load: ${initialRects}<br>
+        After stylesheet load: ${finalRects}<br>
+    </p>`;
+};
+</script>
+<div style="width:100px; height:100px; background-color:green"></div>
+<link rel="stylesheet" href="resources/delayed-css.php?delay=300">
+<div class=delayed style="width:100px; height:100px; background-color:red"></div>
index 02b0fc4..25e60f4 100644 (file)
@@ -1,5 +1,98 @@
 2017-03-10  Antti Koivisto  <antti@apple.com>
 
+        Allow the page to render before <link> stylesheet tags in body
+        https://bugs.webkit.org/show_bug.cgi?id=149157
+        <rdar://problem/24658830>
+
+        Reviewed by Simon Fraser.
+
+        Currently we block style and renderer building completely if document has any loading
+        stylesheets. In case a script queries something layout dependent we construct the render
+        tree with whatever style we have but block painting in it.
+
+        This patch changes behavior so that a loading stylesheet in body only blocks rendering for elements
+        that are after it. The expectation is that such stylesheets rarely affect elements before them
+        and the elements can be rendered without causing ugly visible styling changes.
+
+        The patch replaces the old flash-of-unstyled-content (FOUC) preventation mechanism with a more
+        fine-grained one. Paint blocking is now done on per-renderer basis with based on isNonFinal flag in
+        RenderStyle.
+
+        For stylesheets in head the behavior should be largely unchanged.
+
+        Test: http/tests/incremental/stylesheet-body-incremental-rendering.html
+
+        * css/StyleResolver.cpp:
+        (WebCore::StyleResolver::pseudoStyleRulesForElement):
+        * dom/Document.cpp:
+        (WebCore::Document::Document):
+        (WebCore::Document::resolveStyle):
+        (WebCore::Document::updateLayoutIgnorePendingStylesheets):
+
+            Remove the old FOUC preventation state tracking.
+
+        (WebCore::Document::shouldScheduleLayout):
+        (WebCore::Document::didRemoveAllPendingStylesheet):
+
+            Repaints will now get triggered by the normal style mechanism.
+
+        * dom/Document.h:
+        (WebCore::Document::hasNodesWithNonFinalStyle):
+        (WebCore::Document::setHasNodesWithNonFinalStyle):
+
+            Track if we need to recompute the style later because non-final or unstyled elements.
+
+        (WebCore::Document::didLayoutWithPendingStylesheets): Deleted.
+        (WebCore::Document::hasNodesWithPlaceholderStyle): Deleted.
+        (WebCore::Document::setHasNodesWithPlaceholderStyle): Deleted.
+        * html/HTMLFrameSetElement.cpp:
+        (WebCore::HTMLFrameSetElement::rendererIsNeeded):
+        * page/FrameView.cpp:
+        (WebCore::FrameView::qualifiesAsVisuallyNonEmpty):
+
+            Don't qualify as visually non-empty if we have loading stylesheets in head (even if there is
+            a fouc-prevented render tree).
+
+        (WebCore::FrameView::fireLayoutRelatedMilestonesIfNeeded):
+        * rendering/RenderBlock.cpp:
+        (WebCore::RenderBlock::paintContents):
+
+            Instead of a global test, block painting if isNonFinal is set in the renderer's style.
+
+        * rendering/RenderLayer.cpp:
+        (WebCore::shouldSuppressPaintingLayer):
+        * rendering/style/RenderStyle.cpp:
+        (WebCore::RenderStyle::changeRequiresRepaint):
+
+            The isNonFinal flag prevents painting so we need to trigger repaint when it gets cleared.
+
+        * rendering/style/RenderStyle.h:
+        (WebCore::RenderStyle::isNotFinal):
+        (WebCore::RenderStyle::setIsNotFinal):
+        (WebCore::RenderStyle::isPlaceholderStyle): Deleted.
+        (WebCore::RenderStyle::setIsPlaceholderStyle): Deleted.
+
+            There is no need for placeholder styles anymore. Reuse the bit for isNotFinal.
+
+        * rendering/style/StyleRareNonInheritedData.cpp:
+        (WebCore::StyleRareNonInheritedData::StyleRareNonInheritedData):
+        (WebCore::StyleRareNonInheritedData::operator==):
+        * rendering/style/StyleRareNonInheritedData.h:
+        * style/StyleScope.cpp:
+        (WebCore::Style::Scope::analyzeStyleSheetChange):
+        (WebCore::Style::Scope::updateActiveStyleSheets):
+        * style/StyleTreeResolver.cpp:
+        (WebCore::Style::TreeResolver::styleForElement):
+        (WebCore::Style::TreeResolver::resolveElement):
+
+            If we have seens a loading stylesheet and don't have a renderer yet don't style the element.
+            In case there is a renderer or we are ignoring pending sheets, resolve the style normally
+            but mark it as non-final.
+
+        (WebCore::Style::makePlaceholderStyle): Deleted.
+
+2017-03-10  Antti Koivisto  <antti@apple.com>
+
         imported/w3c/web-platform-tests/html/semantics/embedded-content/the-img-element/sizes/parse-a-sizes-attribute.html is unreliable
         https://bugs.webkit.org/show_bug.cgi?id=169465
 
index 0ace4c2..ae7f976 100644 (file)
@@ -1123,8 +1123,8 @@ Vector<RefPtr<StyleRule>> StyleResolver::styleRulesForElement(const Element* ele
 
 Vector<RefPtr<StyleRule>> StyleResolver::pseudoStyleRulesForElement(const Element* element, PseudoId pseudoId, unsigned rulesToInclude)
 {
-    if (!element || !element->document().haveStylesheetsLoaded())
-        return Vector<RefPtr<StyleRule>>();
+    if (!element)
+        return { };
 
     m_state = State(*element, nullptr);
 
index b71aab3..0aa29cc 100644 (file)
@@ -442,9 +442,8 @@ Document::Document(Frame* frame, const URL& url, unsigned documentClasses, unsig
 #endif
     , m_referencingNodeCount(0)
     , m_settings(frame ? Ref<Settings>(frame->settings()) : Settings::create(nullptr))
-    , m_hasNodesWithPlaceholderStyle(false)
+    , m_hasNodesWithNonFinalStyle(false)
     , m_ignorePendingStylesheets(false)
-    , m_pendingSheetLayout(NoLayoutWithPendingSheets)
     , m_cachedResourceLoader(m_frame ? Ref<CachedResourceLoader>(m_frame->loader().activeDocumentLoader()->cachedResourceLoader()) : CachedResourceLoader::create(nullptr))
     , m_activeParserCount(0)
     , m_wellFormed(false)
@@ -1782,7 +1781,7 @@ void Document::resolveStyle(ResolveStyleType type)
 
         if (type == ResolveStyleType::Rebuild) {
             // This may get set again during style resolve.
-            m_hasNodesWithPlaceholderStyle = false;
+            m_hasNodesWithNonFinalStyle = false;
 
             auto documentStyle = Style::resolveForDocument(*this);
 
@@ -1818,6 +1817,9 @@ void Document::resolveStyle(ResolveStyleType type)
         }
 
         updatedCompositingLayers = frameView.updateCompositingLayersAfterStyleChange();
+
+        if (m_renderView->needsLayout())
+            frameView.scheduleRelayout();
     }
 
     // If we wanted to call implicitClose() during recalcStyle, do so now that we're finished.
@@ -1911,33 +1913,14 @@ void Document::updateLayout()
         frameView->layout();
 }
 
-// FIXME: This is a bad idea and needs to be removed eventually.
-// Other browsers load stylesheets before they continue parsing the web page.
-// Since we don't, we can run JavaScript code that needs answers before the
-// stylesheets are loaded. Doing a layout ignoring the pending stylesheets
-// lets us get reasonable answers. The long term solution to this problem is
-// to instead suspend JavaScript execution.
 void Document::updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasks runPostLayoutTasks)
 {
     bool oldIgnore = m_ignorePendingStylesheets;
 
     if (!haveStylesheetsLoaded()) {
         m_ignorePendingStylesheets = true;
-        // FIXME: We are willing to attempt to suppress painting with outdated style info only once.  Our assumption is that it would be
-        // dangerous to try to stop it a second time, after page content has already been loaded and displayed
-        // with accurate style information.  (Our suppression involves blanking the whole page at the
-        // moment.  If it were more refined, we might be able to do something better.)
-        // It's worth noting though that this entire method is a hack, since what we really want to do is
-        // suspend JS instead of doing a layout with inaccurate information.
-        HTMLElement* bodyElement = bodyOrFrameset();
-        if (bodyElement && !bodyElement->renderer() && m_pendingSheetLayout == NoLayoutWithPendingSheets) {
-            m_pendingSheetLayout = DidLayoutWithPendingSheets;
-            styleScope().didChangeActiveStyleSheetCandidates();
-            resolveStyle(ResolveStyleType::Rebuild);
-        } else if (m_hasNodesWithPlaceholderStyle)
-            // If new nodes have been added or style recalc has been done with style sheets still pending, some nodes 
-            // may not have had their real style calculated yet. Normally this gets cleaned when style sheets arrive 
-            // but here we need up-to-date style immediately.
+        // FIXME: This should just invalidate elements with non-final styles.
+        if (m_hasNodesWithNonFinalStyle)
             resolveStyle(ResolveStyleType::Rebuild);
     }
 
@@ -2771,14 +2754,16 @@ void Document::setParsing(bool b)
 
 bool Document::shouldScheduleLayout()
 {
-    // This function will only be called when FrameView thinks a layout is needed.
-    // This enforces a couple extra rules.
-    //
-    //    (a) Only schedule a layout once the stylesheets are loaded.
-    //    (b) Only schedule layout once we have a body element.
+    if (!documentElement())
+        return false;
+    if (!is<HTMLHtmlElement>(*documentElement()))
+        return true;
+    if (!bodyOrFrameset())
+        return false;
+    if (styleScope().hasPendingSheetsBeforeBody())
+        return false;
 
-    return (haveStylesheetsLoaded() && bodyOrFrameset())
-        || (documentElement() && !is<HTMLHtmlElement>(*documentElement()));
+    return true;
 }
     
 bool Document::isLayoutTimerActive()
@@ -3082,15 +3067,6 @@ Frame* Document::findUnsafeParentScrollPropagationBoundary()
 
 void Document::didRemoveAllPendingStylesheet()
 {
-    if (m_pendingSheetLayout == DidLayoutWithPendingSheets) {
-        // Painting is disabled when doing layouts with pending sheets to avoid FOUC.
-        // We need to force paint when coming out from this state.
-        // FIXME: This is not very elegant.
-        m_pendingSheetLayout = IgnoreLayoutWithPendingSheets;
-        if (renderView())
-            renderView()->repaintViewAndCompositedLayers();
-    }
-
     if (auto* parser = scriptableDocumentParser())
         parser->executeScriptsWaitingForStylesheetsSoon();
 }
index 29788e1..4a67c5f 100644 (file)
@@ -936,12 +936,8 @@ public:
     WEBCORE_EXPORT Ref<XPathNSResolver> createNSResolver(Node* nodeResolver);
     WEBCORE_EXPORT ExceptionOr<Ref<XPathResult>> evaluate(const String& expression, Node* contextNode, RefPtr<XPathNSResolver>&&, unsigned short type, XPathResult*);
 
-    enum PendingSheetLayout { NoLayoutWithPendingSheets, DidLayoutWithPendingSheets, IgnoreLayoutWithPendingSheets };
-
-    bool didLayoutWithPendingStylesheets() const { return m_pendingSheetLayout == DidLayoutWithPendingSheets; }
-
-    bool hasNodesWithPlaceholderStyle() const { return m_hasNodesWithPlaceholderStyle; }
-    void setHasNodesWithPlaceholderStyle() { m_hasNodesWithPlaceholderStyle = true; }
+    bool hasNodesWithNonFinalStyle() const { return m_hasNodesWithNonFinalStyle; }
+    void setHasNodesWithNonFinalStyle() { m_hasNodesWithNonFinalStyle = true; }
 
     void updateFocusAppearanceSoon(SelectionRestorationMode);
     void cancelFocusAppearanceUpdate();
@@ -1385,16 +1381,11 @@ private:
     const Ref<Settings> m_settings;
 
     std::unique_ptr<StyleResolver> m_userAgentShadowTreeStyleResolver;
-    bool m_hasNodesWithPlaceholderStyle;
+    bool m_hasNodesWithNonFinalStyle;
     // But sometimes you need to ignore pending stylesheet count to
     // force an immediate layout when requested by JS.
     bool m_ignorePendingStylesheets;
 
-    // If we do ignore the pending stylesheet count, then we need to add a boolean
-    // to track that this happened so that we can do a full repaint when the stylesheets
-    // do eventually load.
-    PendingSheetLayout m_pendingSheetLayout;
-
     RefPtr<DOMWindow> m_domWindow;
     WeakPtr<Document> m_contextDocument;
 
index 01c5d1f..2a6477a 100644 (file)
@@ -153,7 +153,7 @@ bool HTMLFrameSetElement::rendererIsNeeded(const RenderStyle& style)
 {
     // For compatibility, frames render even when display: none is set.
     // However, we delay creating a renderer until stylesheets have loaded. 
-    return !style.isPlaceholderStyle();
+    return !style.isNotFinal();
 }
 
 RenderPtr<RenderElement> HTMLFrameSetElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
index 776ad77..e7e4e25 100644 (file)
@@ -4629,6 +4629,10 @@ bool FrameView::qualifiesAsVisuallyNonEmpty() const
     if (!frame().document()->parsing() && frame().loader().stateMachine().committedFirstRealDocumentLoad())
         return true;
 
+    // FIXME: We should also ignore renderers with non-final style.
+    if (frame().document()->styleScope().hasPendingSheetsBeforeBody())
+        return false;
+
     // Require the document to grow a bit.
     // Using a value of 48 allows the header on Google's search page to render immediately before search results populate later.
     static const int documentHeightThreshold = 48;
@@ -5171,7 +5175,7 @@ void FrameView::fireLayoutRelatedMilestonesIfNeeded()
     updateIsVisuallyNonEmpty();
 
     // If the layout was done with pending sheets, we are not in fact visually non-empty yet.
-    if (m_isVisuallyNonEmpty && !frame().document()->didLayoutWithPendingStylesheets() && m_firstVisuallyNonEmptyLayoutCallbackPending) {
+    if (m_isVisuallyNonEmpty &&m_firstVisuallyNonEmptyLayoutCallbackPending) {
         m_firstVisuallyNonEmptyLayoutCallbackPending = false;
         if (requestedMilestones & DidFirstVisuallyNonEmptyLayout)
             milestonesAchieved |= DidFirstVisuallyNonEmptyLayout;
index 44c1d78..44ae565 100644 (file)
@@ -1569,10 +1569,10 @@ void RenderBlock::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
 
 void RenderBlock::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
 {
-    // Avoid painting descendants of the root element when stylesheets haven't loaded.  This eliminates FOUC.
-    // It's ok not to draw, because later on, when all the stylesheets do load, styleResolverChanged() on the Document
-    // will do a full repaint.
-    if (document().didLayoutWithPendingStylesheets() && !isRenderView())
+    // Style is non-final if the element has a pending stylesheet before it. We end up with renderers with such styles if a script
+    // forces renderer construction by querying something layout dependent.
+    // Avoid FOUC by not painting. Switching to final style triggers repaint.
+    if (style().isNotFinal())
         return;
 
     if (childrenInline())
index 8f3e34e..e1a1d8e 100644 (file)
@@ -3939,10 +3939,7 @@ static inline bool shouldDoSoftwarePaint(const RenderLayer* layer, bool painting
     
 static inline bool shouldSuppressPaintingLayer(RenderLayer* layer)
 {
-    // Avoid painting descendants of the root layer when stylesheets haven't loaded. This eliminates FOUC.
-    // It's ok not to draw, because later on, when all the stylesheets do load, updateStyleSelector on the Document
-    // will do a full repaint().
-    if (layer->renderer().document().didLayoutWithPendingStylesheets() && !layer->isRootLayer() && !layer->renderer().isDocumentElementRenderer())
+    if (layer->renderer().style().isNotFinal() && !layer->isRootLayer() && !layer->renderer().isDocumentElementRenderer())
         return true;
 
     // Avoid painting all layers if the document is in a state where visual updates aren't allowed.
index 0addd0a..870f28a 100644 (file)
@@ -853,6 +853,9 @@ bool RenderStyle::changeRequiresRepaint(const RenderStyle& other, unsigned& chan
         || m_rareInheritedData->imageRendering != other.m_rareInheritedData->imageRendering)
         return true;
 
+    if (m_rareNonInheritedData->isNotFinal != other.m_rareNonInheritedData->isNotFinal)
+        return true;
+
     if (m_rareNonInheritedData->shapeOutside != other.m_rareNonInheritedData->shapeOutside)
         return true;
 
index 2219d9e..278adf5 100644 (file)
@@ -1653,8 +1653,9 @@ public:
     static Isolation initialIsolation() { return IsolationAuto; }
 #endif
 
-    bool isPlaceholderStyle() const { return m_rareNonInheritedData->isPlaceholderStyle; }
-    void setIsPlaceholderStyle() { SET_VAR(m_rareNonInheritedData, isPlaceholderStyle, true); }
+    // Indicates the style is likely to change due to a pending stylesheet load.
+    bool isNotFinal() const { return m_rareNonInheritedData->isNotFinal; }
+    void setIsNotFinal() { SET_VAR(m_rareNonInheritedData, isNotFinal, true); }
 
     void setVisitedLinkColor(const Color&);
     void setVisitedLinkBackgroundColor(const Color& v) { SET_VAR(m_rareNonInheritedData, visitedLinkBackgroundColor, v); }
index 3b48888..8776b33 100644 (file)
@@ -107,7 +107,7 @@ StyleRareNonInheritedData::StyleRareNonInheritedData()
     , breakInside(RenderStyle::initialBreakInside())
     , resize(RenderStyle::initialResize())
     , hasAttrContent(false)
-    , isPlaceholderStyle(false)
+    , isNotFinal(false)
 {
     maskBoxImage.setMaskDefaults();
 }
@@ -200,7 +200,7 @@ inline StyleRareNonInheritedData::StyleRareNonInheritedData(const StyleRareNonIn
     , breakInside(o.breakInside)
     , resize(o.resize)
     , hasAttrContent(o.hasAttrContent)
-    , isPlaceholderStyle(o.isPlaceholderStyle)
+    , isNotFinal(o.isNotFinal)
 {
 }
 
@@ -304,7 +304,7 @@ bool StyleRareNonInheritedData::operator==(const StyleRareNonInheritedData& o) c
         && breakInside == o.breakInside
         && resize == o.resize
         && hasAttrContent == o.hasAttrContent
-        && isPlaceholderStyle == o.isPlaceholderStyle;
+        && isNotFinal == o.isNotFinal;
 }
 
 bool StyleRareNonInheritedData::contentDataEquivalent(const StyleRareNonInheritedData& other) const
index b58abae..6981db6 100644 (file)
@@ -220,7 +220,7 @@ public:
 
     unsigned hasAttrContent : 1;
 
-    unsigned isPlaceholderStyle : 1;
+    unsigned isNotFinal : 1;
 
 private:
     StyleRareNonInheritedData();
index 02f4d44..14e881c 100644 (file)
@@ -389,7 +389,7 @@ Scope::StyleResolverUpdateType Scope::analyzeStyleSheetChange(const Vector<RefPt
     auto styleResolverUpdateType = hasInsertions ? Reset : Additive;
 
     // If we are already parsing the body and so may have significant amount of elements, put some effort into trying to avoid style recalcs.
-    if (!m_document.bodyOrFrameset() || m_document.hasNodesWithPlaceholderStyle())
+    if (!m_document.bodyOrFrameset() || m_document.hasNodesWithNonFinalStyle())
         return styleResolverUpdateType;
 
     StyleInvalidationAnalysis invalidationAnalysis(addedSheets, styleResolver.mediaQueryEvaluator());
@@ -440,7 +440,7 @@ void Scope::updateActiveStyleSheets(UpdateType updateType)
 
     // Don't bother updating, since we haven't loaded all our style info yet
     // and haven't calculated the style resolver for the first time.
-    if (!m_shadowRoot && !m_didUpdateActiveStyleSheets && hasPendingSheets()) {
+    if (!m_shadowRoot && !m_didUpdateActiveStyleSheets && hasPendingSheetsBeforeBody()) {
         clearResolver();
         return;
     }
index 8674247..8ba7397 100644 (file)
@@ -51,24 +51,6 @@ namespace WebCore {
 
 namespace Style {
 
-static std::unique_ptr<RenderStyle> makePlaceholderStyle(Document& document)
-{
-    auto placeholderStyle = RenderStyle::createPtr();
-    placeholderStyle->setDisplay(NONE);
-    placeholderStyle->setIsPlaceholderStyle();
-
-    FontCascadeDescription fontDescription;
-    fontDescription.setOneFamily(standardFamily);
-    fontDescription.setKeywordSizeFromIdentifier(CSSValueMedium);
-    float size = Style::fontSizeForKeyword(CSSValueMedium, false, document);
-    fontDescription.setSpecifiedSize(size);
-    fontDescription.setComputedSize(size);
-    placeholderStyle->setFontDescription(fontDescription);
-
-    placeholderStyle->fontCascade().update(&document.fontSelector());
-    return placeholderStyle;
-}
-
 TreeResolver::TreeResolver(Document& document)
     : m_document(document)
 {
@@ -126,11 +108,6 @@ void TreeResolver::popScope()
 
 std::unique_ptr<RenderStyle> TreeResolver::styleForElement(Element& element, const RenderStyle& inheritedStyle)
 {
-    if (m_didSeePendingStylesheet && !element.renderer() && !m_document.isIgnoringPendingStylesheets()) {
-        m_document.setHasNodesWithPlaceholderStyle();
-        return makePlaceholderStyle(m_document);
-    }
-
     if (element.hasCustomStyleResolveCallbacks()) {
         RenderStyle* shadowHostStyle = scope().shadowRoot ? m_update->elementStyle(*scope().shadowRoot->host()) : nullptr;
         if (auto customStyle = element.resolveCustomStyle(inheritedStyle, shadowHostStyle)) {
@@ -190,15 +167,25 @@ static bool affectsRenderedSubtree(Element& element, const RenderStyle& newStyle
 
 ElementUpdate TreeResolver::resolveElement(Element& element)
 {
+    if (m_didSeePendingStylesheet && !element.renderer() && !m_document.isIgnoringPendingStylesheets()) {
+        m_document.setHasNodesWithNonFinalStyle();
+        return { };
+    }
+
     auto newStyle = styleForElement(element, parent().style);
 
     if (!affectsRenderedSubtree(element, *newStyle))
         return { };
 
-    auto update = createAnimatedElementUpdate(WTFMove(newStyle), element, parent().change);
-
     auto* existingStyle = element.renderStyle();
 
+    if (m_didSeePendingStylesheet && (!existingStyle || existingStyle->isNotFinal())) {
+        newStyle->setIsNotFinal();
+        m_document.setHasNodesWithNonFinalStyle();
+    }
+
+    auto update = createAnimatedElementUpdate(WTFMove(newStyle), element, parent().change);
+
     if (&element == m_document.documentElement()) {
         m_documentElementStyle = RenderStyle::clonePtr(*update.style);
         scope().styleResolver.setOverrideDocumentElementStyle(m_documentElementStyle.get());