Changeset 213712 in webkit
- Timestamp:
- Mar 10, 2017 8:01:50 AM (7 years ago)
- Location:
- trunk
- Files:
-
- 3 added
- 15 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r213706 r213712 1 2017-03-10 Antti Koivisto <antti@apple.com> 2 3 Loading in-body stylesheets should not block rendering of elements before them 4 https://bugs.webkit.org/show_bug.cgi?id=169345 5 6 Reviewed by Simon Fraser. 7 8 * http/tests/incremental/resources/delayed-css.php: Added. 9 * http/tests/incremental/stylesheet-body-incremental-rendering-expected.html: Added. 10 * http/tests/incremental/stylesheet-body-incremental-rendering.html: Added. 11 1 12 2017-03-10 Antoine Quint <graouts@apple.com> 2 13 -
trunk/Source/WebCore/ChangeLog
r213711 r213712 1 2017-03-10 Antti Koivisto <antti@apple.com> 2 3 Allow the page to render before <link> stylesheet tags in body 4 https://bugs.webkit.org/show_bug.cgi?id=149157 5 <rdar://problem/24658830> 6 7 Reviewed by Simon Fraser. 8 9 Currently we block style and renderer building completely if document has any loading 10 stylesheets. In case a script queries something layout dependent we construct the render 11 tree with whatever style we have but block painting in it. 12 13 This patch changes behavior so that a loading stylesheet in body only blocks rendering for elements 14 that are after it. The expectation is that such stylesheets rarely affect elements before them 15 and the elements can be rendered without causing ugly visible styling changes. 16 17 The patch replaces the old flash-of-unstyled-content (FOUC) preventation mechanism with a more 18 fine-grained one. Paint blocking is now done on per-renderer basis with based on isNonFinal flag in 19 RenderStyle. 20 21 For stylesheets in head the behavior should be largely unchanged. 22 23 Test: http/tests/incremental/stylesheet-body-incremental-rendering.html 24 25 * css/StyleResolver.cpp: 26 (WebCore::StyleResolver::pseudoStyleRulesForElement): 27 * dom/Document.cpp: 28 (WebCore::Document::Document): 29 (WebCore::Document::resolveStyle): 30 (WebCore::Document::updateLayoutIgnorePendingStylesheets): 31 32 Remove the old FOUC preventation state tracking. 33 34 (WebCore::Document::shouldScheduleLayout): 35 (WebCore::Document::didRemoveAllPendingStylesheet): 36 37 Repaints will now get triggered by the normal style mechanism. 38 39 * dom/Document.h: 40 (WebCore::Document::hasNodesWithNonFinalStyle): 41 (WebCore::Document::setHasNodesWithNonFinalStyle): 42 43 Track if we need to recompute the style later because non-final or unstyled elements. 44 45 (WebCore::Document::didLayoutWithPendingStylesheets): Deleted. 46 (WebCore::Document::hasNodesWithPlaceholderStyle): Deleted. 47 (WebCore::Document::setHasNodesWithPlaceholderStyle): Deleted. 48 * html/HTMLFrameSetElement.cpp: 49 (WebCore::HTMLFrameSetElement::rendererIsNeeded): 50 * page/FrameView.cpp: 51 (WebCore::FrameView::qualifiesAsVisuallyNonEmpty): 52 53 Don't qualify as visually non-empty if we have loading stylesheets in head (even if there is 54 a fouc-prevented render tree). 55 56 (WebCore::FrameView::fireLayoutRelatedMilestonesIfNeeded): 57 * rendering/RenderBlock.cpp: 58 (WebCore::RenderBlock::paintContents): 59 60 Instead of a global test, block painting if isNonFinal is set in the renderer's style. 61 62 * rendering/RenderLayer.cpp: 63 (WebCore::shouldSuppressPaintingLayer): 64 * rendering/style/RenderStyle.cpp: 65 (WebCore::RenderStyle::changeRequiresRepaint): 66 67 The isNonFinal flag prevents painting so we need to trigger repaint when it gets cleared. 68 69 * rendering/style/RenderStyle.h: 70 (WebCore::RenderStyle::isNotFinal): 71 (WebCore::RenderStyle::setIsNotFinal): 72 (WebCore::RenderStyle::isPlaceholderStyle): Deleted. 73 (WebCore::RenderStyle::setIsPlaceholderStyle): Deleted. 74 75 There is no need for placeholder styles anymore. Reuse the bit for isNotFinal. 76 77 * rendering/style/StyleRareNonInheritedData.cpp: 78 (WebCore::StyleRareNonInheritedData::StyleRareNonInheritedData): 79 (WebCore::StyleRareNonInheritedData::operator==): 80 * rendering/style/StyleRareNonInheritedData.h: 81 * style/StyleScope.cpp: 82 (WebCore::Style::Scope::analyzeStyleSheetChange): 83 (WebCore::Style::Scope::updateActiveStyleSheets): 84 * style/StyleTreeResolver.cpp: 85 (WebCore::Style::TreeResolver::styleForElement): 86 (WebCore::Style::TreeResolver::resolveElement): 87 88 If we have seens a loading stylesheet and don't have a renderer yet don't style the element. 89 In case there is a renderer or we are ignoring pending sheets, resolve the style normally 90 but mark it as non-final. 91 92 (WebCore::Style::makePlaceholderStyle): Deleted. 93 1 94 2017-03-10 Antti Koivisto <antti@apple.com> 2 95 -
trunk/Source/WebCore/css/StyleResolver.cpp
r213701 r213712 1124 1124 Vector<RefPtr<StyleRule>> StyleResolver::pseudoStyleRulesForElement(const Element* element, PseudoId pseudoId, unsigned rulesToInclude) 1125 1125 { 1126 if (!element || !element->document().haveStylesheetsLoaded())1127 return Vector<RefPtr<StyleRule>>();1126 if (!element) 1127 return { }; 1128 1128 1129 1129 m_state = State(*element, nullptr); -
trunk/Source/WebCore/dom/Document.cpp
r213701 r213712 443 443 , m_referencingNodeCount(0) 444 444 , m_settings(frame ? Ref<Settings>(frame->settings()) : Settings::create(nullptr)) 445 , m_hasNodesWith PlaceholderStyle(false)445 , m_hasNodesWithNonFinalStyle(false) 446 446 , m_ignorePendingStylesheets(false) 447 , m_pendingSheetLayout(NoLayoutWithPendingSheets)448 447 , m_cachedResourceLoader(m_frame ? Ref<CachedResourceLoader>(m_frame->loader().activeDocumentLoader()->cachedResourceLoader()) : CachedResourceLoader::create(nullptr)) 449 448 , m_activeParserCount(0) … … 1783 1782 if (type == ResolveStyleType::Rebuild) { 1784 1783 // This may get set again during style resolve. 1785 m_hasNodesWith PlaceholderStyle = false;1784 m_hasNodesWithNonFinalStyle = false; 1786 1785 1787 1786 auto documentStyle = Style::resolveForDocument(*this); … … 1819 1818 1820 1819 updatedCompositingLayers = frameView.updateCompositingLayersAfterStyleChange(); 1820 1821 if (m_renderView->needsLayout()) 1822 frameView.scheduleRelayout(); 1821 1823 } 1822 1824 … … 1912 1914 } 1913 1915 1914 // FIXME: This is a bad idea and needs to be removed eventually.1915 // Other browsers load stylesheets before they continue parsing the web page.1916 // Since we don't, we can run JavaScript code that needs answers before the1917 // stylesheets are loaded. Doing a layout ignoring the pending stylesheets1918 // lets us get reasonable answers. The long term solution to this problem is1919 // to instead suspend JavaScript execution.1920 1916 void Document::updateLayoutIgnorePendingStylesheets(Document::RunPostLayoutTasks runPostLayoutTasks) 1921 1917 { … … 1924 1920 if (!haveStylesheetsLoaded()) { 1925 1921 m_ignorePendingStylesheets = true; 1926 // FIXME: We are willing to attempt to suppress painting with outdated style info only once. Our assumption is that it would be 1927 // dangerous to try to stop it a second time, after page content has already been loaded and displayed 1928 // with accurate style information. (Our suppression involves blanking the whole page at the 1929 // moment. If it were more refined, we might be able to do something better.) 1930 // It's worth noting though that this entire method is a hack, since what we really want to do is 1931 // suspend JS instead of doing a layout with inaccurate information. 1932 HTMLElement* bodyElement = bodyOrFrameset(); 1933 if (bodyElement && !bodyElement->renderer() && m_pendingSheetLayout == NoLayoutWithPendingSheets) { 1934 m_pendingSheetLayout = DidLayoutWithPendingSheets; 1935 styleScope().didChangeActiveStyleSheetCandidates(); 1936 resolveStyle(ResolveStyleType::Rebuild); 1937 } else if (m_hasNodesWithPlaceholderStyle) 1938 // If new nodes have been added or style recalc has been done with style sheets still pending, some nodes 1939 // may not have had their real style calculated yet. Normally this gets cleaned when style sheets arrive 1940 // but here we need up-to-date style immediately. 1922 // FIXME: This should just invalidate elements with non-final styles. 1923 if (m_hasNodesWithNonFinalStyle) 1941 1924 resolveStyle(ResolveStyleType::Rebuild); 1942 1925 } … … 2772 2755 bool Document::shouldScheduleLayout() 2773 2756 { 2774 // This function will only be called when FrameView thinks a layout is needed. 2775 // This enforces a couple extra rules. 2776 // 2777 // (a) Only schedule a layout once the stylesheets are loaded. 2778 // (b) Only schedule layout once we have a body element. 2779 2780 return (haveStylesheetsLoaded() && bodyOrFrameset()) 2781 || (documentElement() && !is<HTMLHtmlElement>(*documentElement())); 2757 if (!documentElement()) 2758 return false; 2759 if (!is<HTMLHtmlElement>(*documentElement())) 2760 return true; 2761 if (!bodyOrFrameset()) 2762 return false; 2763 if (styleScope().hasPendingSheetsBeforeBody()) 2764 return false; 2765 2766 return true; 2782 2767 } 2783 2768 … … 3083 3068 void Document::didRemoveAllPendingStylesheet() 3084 3069 { 3085 if (m_pendingSheetLayout == DidLayoutWithPendingSheets) {3086 // Painting is disabled when doing layouts with pending sheets to avoid FOUC.3087 // We need to force paint when coming out from this state.3088 // FIXME: This is not very elegant.3089 m_pendingSheetLayout = IgnoreLayoutWithPendingSheets;3090 if (renderView())3091 renderView()->repaintViewAndCompositedLayers();3092 }3093 3094 3070 if (auto* parser = scriptableDocumentParser()) 3095 3071 parser->executeScriptsWaitingForStylesheetsSoon(); -
trunk/Source/WebCore/dom/Document.h
r213701 r213712 937 937 WEBCORE_EXPORT ExceptionOr<Ref<XPathResult>> evaluate(const String& expression, Node* contextNode, RefPtr<XPathNSResolver>&&, unsigned short type, XPathResult*); 938 938 939 enum PendingSheetLayout { NoLayoutWithPendingSheets, DidLayoutWithPendingSheets, IgnoreLayoutWithPendingSheets }; 940 941 bool didLayoutWithPendingStylesheets() const { return m_pendingSheetLayout == DidLayoutWithPendingSheets; } 942 943 bool hasNodesWithPlaceholderStyle() const { return m_hasNodesWithPlaceholderStyle; } 944 void setHasNodesWithPlaceholderStyle() { m_hasNodesWithPlaceholderStyle = true; } 939 bool hasNodesWithNonFinalStyle() const { return m_hasNodesWithNonFinalStyle; } 940 void setHasNodesWithNonFinalStyle() { m_hasNodesWithNonFinalStyle = true; } 945 941 946 942 void updateFocusAppearanceSoon(SelectionRestorationMode); … … 1386 1382 1387 1383 std::unique_ptr<StyleResolver> m_userAgentShadowTreeStyleResolver; 1388 bool m_hasNodesWith PlaceholderStyle;1384 bool m_hasNodesWithNonFinalStyle; 1389 1385 // But sometimes you need to ignore pending stylesheet count to 1390 1386 // force an immediate layout when requested by JS. 1391 1387 bool m_ignorePendingStylesheets; 1392 1393 // If we do ignore the pending stylesheet count, then we need to add a boolean1394 // to track that this happened so that we can do a full repaint when the stylesheets1395 // do eventually load.1396 PendingSheetLayout m_pendingSheetLayout;1397 1388 1398 1389 RefPtr<DOMWindow> m_domWindow; -
trunk/Source/WebCore/html/HTMLFrameSetElement.cpp
r213701 r213712 154 154 // For compatibility, frames render even when display: none is set. 155 155 // However, we delay creating a renderer until stylesheets have loaded. 156 return !style.is PlaceholderStyle();156 return !style.isNotFinal(); 157 157 } 158 158 -
trunk/Source/WebCore/page/FrameView.cpp
r213701 r213712 4630 4630 return true; 4631 4631 4632 // FIXME: We should also ignore renderers with non-final style. 4633 if (frame().document()->styleScope().hasPendingSheetsBeforeBody()) 4634 return false; 4635 4632 4636 // Require the document to grow a bit. 4633 4637 // Using a value of 48 allows the header on Google's search page to render immediately before search results populate later. … … 5172 5176 5173 5177 // If the layout was done with pending sheets, we are not in fact visually non-empty yet. 5174 if (m_isVisuallyNonEmpty && !frame().document()->didLayoutWithPendingStylesheets() &&m_firstVisuallyNonEmptyLayoutCallbackPending) {5178 if (m_isVisuallyNonEmpty &&m_firstVisuallyNonEmptyLayoutCallbackPending) { 5175 5179 m_firstVisuallyNonEmptyLayoutCallbackPending = false; 5176 5180 if (requestedMilestones & DidFirstVisuallyNonEmptyLayout) -
trunk/Source/WebCore/rendering/RenderBlock.cpp
r213701 r213712 1570 1570 void RenderBlock::paintContents(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1571 1571 { 1572 // Avoid painting descendants of the root element when stylesheets haven't loaded. This eliminates FOUC.1573 // It's ok not to draw, because later on, when all the stylesheets do load, styleResolverChanged() on the Document1574 // will do a fullrepaint.1575 if ( document().didLayoutWithPendingStylesheets() && !isRenderView())1572 // Style is non-final if the element has a pending stylesheet before it. We end up with renderers with such styles if a script 1573 // forces renderer construction by querying something layout dependent. 1574 // Avoid FOUC by not painting. Switching to final style triggers repaint. 1575 if (style().isNotFinal()) 1576 1576 return; 1577 1577 -
trunk/Source/WebCore/rendering/RenderLayer.cpp
r213701 r213712 3940 3940 static inline bool shouldSuppressPaintingLayer(RenderLayer* layer) 3941 3941 { 3942 // Avoid painting descendants of the root layer when stylesheets haven't loaded. This eliminates FOUC. 3943 // It's ok not to draw, because later on, when all the stylesheets do load, updateStyleSelector on the Document 3944 // will do a full repaint(). 3945 if (layer->renderer().document().didLayoutWithPendingStylesheets() && !layer->isRootLayer() && !layer->renderer().isDocumentElementRenderer()) 3942 if (layer->renderer().style().isNotFinal() && !layer->isRootLayer() && !layer->renderer().isDocumentElementRenderer()) 3946 3943 return true; 3947 3944 -
trunk/Source/WebCore/rendering/style/RenderStyle.cpp
r213701 r213712 854 854 return true; 855 855 856 if (m_rareNonInheritedData->isNotFinal != other.m_rareNonInheritedData->isNotFinal) 857 return true; 858 856 859 if (m_rareNonInheritedData->shapeOutside != other.m_rareNonInheritedData->shapeOutside) 857 860 return true; -
trunk/Source/WebCore/rendering/style/RenderStyle.h
r213701 r213712 1654 1654 #endif 1655 1655 1656 bool isPlaceholderStyle() const { return m_rareNonInheritedData->isPlaceholderStyle; } 1657 void setIsPlaceholderStyle() { SET_VAR(m_rareNonInheritedData, isPlaceholderStyle, true); } 1656 // Indicates the style is likely to change due to a pending stylesheet load. 1657 bool isNotFinal() const { return m_rareNonInheritedData->isNotFinal; } 1658 void setIsNotFinal() { SET_VAR(m_rareNonInheritedData, isNotFinal, true); } 1658 1659 1659 1660 void setVisitedLinkColor(const Color&); -
trunk/Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp
r213701 r213712 108 108 , resize(RenderStyle::initialResize()) 109 109 , hasAttrContent(false) 110 , is PlaceholderStyle(false)110 , isNotFinal(false) 111 111 { 112 112 maskBoxImage.setMaskDefaults(); … … 201 201 , resize(o.resize) 202 202 , hasAttrContent(o.hasAttrContent) 203 , is PlaceholderStyle(o.isPlaceholderStyle)203 , isNotFinal(o.isNotFinal) 204 204 { 205 205 } … … 305 305 && resize == o.resize 306 306 && hasAttrContent == o.hasAttrContent 307 && is PlaceholderStyle == o.isPlaceholderStyle;307 && isNotFinal == o.isNotFinal; 308 308 } 309 309 -
trunk/Source/WebCore/rendering/style/StyleRareNonInheritedData.h
r213701 r213712 221 221 unsigned hasAttrContent : 1; 222 222 223 unsigned is PlaceholderStyle: 1;223 unsigned isNotFinal : 1; 224 224 225 225 private: -
trunk/Source/WebCore/style/StyleScope.cpp
r213701 r213712 390 390 391 391 // If we are already parsing the body and so may have significant amount of elements, put some effort into trying to avoid style recalcs. 392 if (!m_document.bodyOrFrameset() || m_document.hasNodesWith PlaceholderStyle())392 if (!m_document.bodyOrFrameset() || m_document.hasNodesWithNonFinalStyle()) 393 393 return styleResolverUpdateType; 394 394 … … 441 441 // Don't bother updating, since we haven't loaded all our style info yet 442 442 // and haven't calculated the style resolver for the first time. 443 if (!m_shadowRoot && !m_didUpdateActiveStyleSheets && hasPendingSheets ()) {443 if (!m_shadowRoot && !m_didUpdateActiveStyleSheets && hasPendingSheetsBeforeBody()) { 444 444 clearResolver(); 445 445 return; -
trunk/Source/WebCore/style/StyleTreeResolver.cpp
r213701 r213712 52 52 namespace Style { 53 53 54 static std::unique_ptr<RenderStyle> makePlaceholderStyle(Document& document)55 {56 auto placeholderStyle = RenderStyle::createPtr();57 placeholderStyle->setDisplay(NONE);58 placeholderStyle->setIsPlaceholderStyle();59 60 FontCascadeDescription fontDescription;61 fontDescription.setOneFamily(standardFamily);62 fontDescription.setKeywordSizeFromIdentifier(CSSValueMedium);63 float size = Style::fontSizeForKeyword(CSSValueMedium, false, document);64 fontDescription.setSpecifiedSize(size);65 fontDescription.setComputedSize(size);66 placeholderStyle->setFontDescription(fontDescription);67 68 placeholderStyle->fontCascade().update(&document.fontSelector());69 return placeholderStyle;70 }71 72 54 TreeResolver::TreeResolver(Document& document) 73 55 : m_document(document) … … 127 109 std::unique_ptr<RenderStyle> TreeResolver::styleForElement(Element& element, const RenderStyle& inheritedStyle) 128 110 { 129 if (m_didSeePendingStylesheet && !element.renderer() && !m_document.isIgnoringPendingStylesheets()) {130 m_document.setHasNodesWithPlaceholderStyle();131 return makePlaceholderStyle(m_document);132 }133 134 111 if (element.hasCustomStyleResolveCallbacks()) { 135 112 RenderStyle* shadowHostStyle = scope().shadowRoot ? m_update->elementStyle(*scope().shadowRoot->host()) : nullptr; … … 191 168 ElementUpdate TreeResolver::resolveElement(Element& element) 192 169 { 170 if (m_didSeePendingStylesheet && !element.renderer() && !m_document.isIgnoringPendingStylesheets()) { 171 m_document.setHasNodesWithNonFinalStyle(); 172 return { }; 173 } 174 193 175 auto newStyle = styleForElement(element, parent().style); 194 176 … … 196 178 return { }; 197 179 180 auto* existingStyle = element.renderStyle(); 181 182 if (m_didSeePendingStylesheet && (!existingStyle || existingStyle->isNotFinal())) { 183 newStyle->setIsNotFinal(); 184 m_document.setHasNodesWithNonFinalStyle(); 185 } 186 198 187 auto update = createAnimatedElementUpdate(WTFMove(newStyle), element, parent().change); 199 200 auto* existingStyle = element.renderStyle();201 188 202 189 if (&element == m_document.documentElement()) {
Note: See TracChangeset
for help on using the changeset viewer.