Diff
Modified: trunk/Source/WebCore/ChangeLog (104059 => 104060)
--- trunk/Source/WebCore/ChangeLog 2012-01-04 20:02:54 UTC (rev 104059)
+++ trunk/Source/WebCore/ChangeLog 2012-01-04 20:03:35 UTC (rev 104060)
@@ -1,3 +1,99 @@
+2012-01-03 Antti Koivisto <[email protected]>
+
+ Reviewed by Dave Hyatt.
+
+ Analyze stylesheet scope to minimize style recalcs
+ https://bugs.webkit.org/show_bug.cgi?id=75508
+
+ It is a relatively common pattern to use inline stylesheets in document body where all rules are scoped using descendant selector
+
+ <style>
+ #foo {...}
+ #foo div {...}
+ #foo .bar {...}
+ </style>
+
+ When this pattern is used it is also common that the rules only apply to elements that come after the style element.
+
+ When the set of active stylesheets changes we invalidate and recompute the entire document style. This is very expensive. We can
+ detect the case above and avoid the style recalc.
+
+ On engadget.com, this patch cuts the time spent in style recalcs to roughly half. There are further savings from reduced
+ relayouts. In total the engine CPU time used over the page load is reduced by ~10%.
+
+ * css/CSSStyleSelector.cpp:
+ (WebCore::CSSStyleSelector::CSSStyleSelector):
+ (WebCore::CSSStyleSelector::collectFeatures):
+
+ Refactor feature collection from constructor to a separate function.
+
+ (WebCore::CSSStyleSelector::appendAuthorStylesheets):
+
+ New function for non-destructively updating the style selector.
+
+ (WebCore::CSSStyleSelector::Features::clear):
+
+ Clear the features for another collection.
+
+ (WebCore::CSSStyleSelector::determineStylesheetSelectorScopes):
+
+ Find if all rules on a stylesheetare scoped to some set of ids and classes.
+
+ (WebCore::CSSStyleSelector::styleForDocument):
+
+ Add optional font selector argument. We updated the correct base style font on style selector construction but that is no longer sufficient
+ as font selector may be updated without reconstructing the style selector.
+
+ (WebCore::RuleSet::addRulesFromSheet):
+
+ Invalidate the matched declaration cache in case of new font-face rules.
+
+ * css/CSSStyleSelector.h:
+ * css/SelectorChecker.cpp:
+ (WebCore::SelectorChecker::determineSelectorScopes):
+
+ Find if all rules on a selector list are scoped to some set of ids and classes.
+
+ * css/SelectorChecker.h:
+ * dom/Document.cpp:
+ (WebCore::Document::Document):
+ (WebCore::Document::recalcStyle):
+
+ Pass the font selector, if exists, to styleForDocument so we always have the base font up to date.
+
+ (WebCore::Document::combineCSSFeatureFlags):
+ (WebCore::Document::resetCSSFeatureFlags):
+ (WebCore::Document::createStyleSelector):
+
+ Refactor css feature flag resetting to functions.
+
+ (WebCore::Document::removePendingSheet):
+
+ Use new PendingStylesheetCompleted flag when new stylesheets arrive.
+
+ (WebCore::Document::styleSelectorChanged):
+
+ Skip style recalc if it is not needed.
+
+ (WebCore::Document::collectActiveStylesheets):
+
+ Refactor collecting stylesheets to a separate function.
+
+ (WebCore::Document::testAddedStylesheetRequiresStyleRecalc):
+
+ Determine the scopes and use hasElementWithId/getElementsByClassName to figure out if any scoped elements currently exist in the tree.
+
+ (WebCore::Document::analyzeStylesheetChange):
+
+ Figure out if we can update the style selector incrementally and if we can skip the style recalc.
+
+ (WebCore::Document::updateActiveStylesheets):
+
+ Renamed from recalcStyleSelector.
+ Invoke the new analysis functions.
+
+ * dom/Document.h:
+
2012-01-04 Igor Oliveira <[email protected]>
Share fractional time code between AnimationBase and KeyframeAnimation
Modified: trunk/Source/WebCore/css/CSSStyleSelector.cpp (104059 => 104060)
--- trunk/Source/WebCore/css/CSSStyleSelector.cpp 2012-01-04 20:02:54 UTC (rev 104059)
+++ trunk/Source/WebCore/css/CSSStyleSelector.cpp 2012-01-04 20:03:35 UTC (rev 104060)
@@ -418,6 +418,16 @@
if (sheet->isCSSStyleSheet() && !sheet->disabled())
m_authorStyle->addRulesFromSheet(static_cast<CSSStyleSheet*>(sheet), *m_medium, this);
}
+ m_authorStyle->shrinkToFit();
+
+ collectFeatures();
+
+ if (document->renderer() && document->renderer()->style())
+ document->renderer()->style()->font().update(fontSelector());
+}
+
+void CSSStyleSelector::collectFeatures()
+{
// Collect all ids and rules using sibling selectors (:first-child and similar)
// in the current set of stylesheets. Style sharing code uses this information to reject
// sharing candidates.
@@ -429,15 +439,29 @@
m_authorStyle->collectFeatures(m_features);
if (m_userStyle)
m_userStyle->collectFeatures(m_features);
-
- m_authorStyle->shrinkToFit();
if (m_features.siblingRules)
m_features.siblingRules->shrinkToFit();
if (m_features.uncommonAttributeRules)
m_features.uncommonAttributeRules->shrinkToFit();
+}
- if (document->renderer() && document->renderer()->style())
- document->renderer()->style()->font().update(fontSelector());
+void CSSStyleSelector::appendAuthorStylesheets(unsigned firstNew, const Vector<RefPtr<StyleSheet> >& stylesheets)
+{
+ // This handles sheets added to the end of the stylesheet list only. In other cases the style resolver
+ // needs to be reconstructed. To handle insertions too the rule order numbers would need to be updated.
+ unsigned size = stylesheets.size();
+ for (unsigned i = firstNew; i < size; ++i) {
+ if (!stylesheets[i]->isCSSStyleSheet() || stylesheets[i]->disabled())
+ continue;
+ m_authorStyle->addRulesFromSheet(static_cast<CSSStyleSheet*>(stylesheets[i].get()), *m_medium, this);
+ }
+ m_authorStyle->shrinkToFit();
+ // FIXME: This really only needs to collect the features from the newly added sheets.
+ m_features.clear();
+ collectFeatures();
+
+ if (document()->renderer() && document()->renderer()->style())
+ document()->renderer()->style()->font().update(fontSelector());
}
void CSSStyleSelector::addRegionRule(PassRefPtr<WebKitCSSRegionRule> regionStyleRule)
@@ -469,6 +493,17 @@
{
}
+void CSSStyleSelector::Features::clear()
+{
+ idsInRules.clear();
+ attrsInRules.clear();
+ siblingRules.clear();
+ uncommonAttributeRules.clear();
+ usesFirstLineRules = false;
+ usesBeforeAfterRules = false;
+ usesLinkRules = false;
+}
+
static CSSStyleSheet* parseUASheet(const String& str)
{
CSSStyleSheet* sheet = CSSStyleSheet::create().leakRef(); // leak the sheet on purpose
@@ -1148,7 +1183,7 @@
}
}
-PassRefPtr<RenderStyle> CSSStyleSelector::styleForDocument(Document* document)
+PassRefPtr<RenderStyle> CSSStyleSelector::styleForDocument(Document* document, CSSFontSelector* fontSelector)
{
Frame* frame = document->frame();
@@ -1204,7 +1239,7 @@
}
documentStyle->setFontDescription(fontDescription);
- documentStyle->font().update(0);
+ documentStyle->font().update(fontSelector);
return documentStyle.release();
}
@@ -1799,6 +1834,33 @@
return false;
return true;
}
+
+bool CSSStyleSelector::determineStylesheetSelectorScopes(CSSStyleSheet* stylesheet, HashSet<AtomicStringImpl*>& idScopes, HashSet<AtomicStringImpl*>& classScopes)
+{
+ ASSERT(!stylesheet->isLoading());
+
+ size_t size = stylesheet->length();
+ for (size_t i = 0; i < size; i++) {
+ CSSRule* rule = stylesheet->item(i);
+ if (rule->isStyleRule()) {
+ CSSStyleRule* styleRule = static_cast<CSSStyleRule*>(rule);
+ if (!SelectorChecker::determineSelectorScopes(styleRule->selectorList(), idScopes, classScopes))
+ return false;
+ continue;
+ }
+ if (rule->isImportRule()) {
+ CSSImportRule* importRule = static_cast<CSSImportRule*>(rule);
+ if (importRule->styleSheet()) {
+ if (!determineStylesheetSelectorScopes(importRule->styleSheet(), idScopes, classScopes))
+ return false;
+ }
+ continue;
+ }
+ // FIXME: Media rules and maybe some others could be allowed.
+ return false;
+ }
+ return true;
+}
// -----------------------------------------------------------------
@@ -1968,6 +2030,7 @@
// Add this font face to our set.
const CSSFontFaceRule* fontFaceRule = static_cast<CSSFontFaceRule*>(childItem);
styleSelector->fontSelector()->addFontFaceRule(fontFaceRule);
+ styleSelector->invalidateMatchedDeclarationCache();
} else if (childItem->isKeyframesRule() && styleSelector) {
// Add this keyframe rule to our set.
styleSelector->addKeyframeStyle(static_cast<WebKitCSSKeyframesRule*>(childItem));
@@ -1978,6 +2041,7 @@
// Add this font face to our set.
const CSSFontFaceRule* fontFaceRule = static_cast<CSSFontFaceRule*>(rule);
styleSelector->fontSelector()->addFontFaceRule(fontFaceRule);
+ styleSelector->invalidateMatchedDeclarationCache();
} else if (rule->isKeyframesRule())
styleSelector->addKeyframeStyle(static_cast<WebKitCSSKeyframesRule*>(rule));
else if (rule->isRegionRule() && styleSelector)
Modified: trunk/Source/WebCore/css/CSSStyleSelector.h (104059 => 104060)
--- trunk/Source/WebCore/css/CSSStyleSelector.h 2012-01-04 20:02:54 UTC (rev 104059)
+++ trunk/Source/WebCore/css/CSSStyleSelector.h 2012-01-04 20:03:35 UTC (rev 104060)
@@ -112,7 +112,7 @@
PassRefPtr<RenderStyle> styleForPage(int pageIndex);
- static PassRefPtr<RenderStyle> styleForDocument(Document*);
+ static PassRefPtr<RenderStyle> styleForDocument(Document*, CSSFontSelector* = 0);
RenderStyle* style() const { return m_style.get(); }
RenderStyle* parentStyle() const { return m_parentStyle; }
@@ -126,10 +126,16 @@
void setEffectiveZoom(float f) { m_fontDirty |= style()->setEffectiveZoom(f); }
void setTextSizeAdjust(bool b) { m_fontDirty |= style()->setTextSizeAdjust(b); }
bool hasParentNode() const { return m_parentNode; }
+
+ void appendAuthorStylesheets(unsigned firstNew, const Vector<RefPtr<StyleSheet> >&);
+
+ // Find the ids or classes the selectors on a stylesheet are scoped to. The selectors only apply to elements in subtrees where the root element matches the scope.
+ static bool determineStylesheetSelectorScopes(CSSStyleSheet*, HashSet<AtomicStringImpl*>& idScopes, HashSet<AtomicStringImpl*>& classScopes);
private:
void initForStyleResolve(Element*, RenderStyle* parentStyle = 0, PseudoId = NOPSEUDO);
void initElement(Element*);
+ void collectFeatures();
RenderStyle* locateSharedStyle();
bool matchesRuleSet(RuleSet*);
Node* locateCousinList(Element* parent, unsigned& visitedNodeCount) const;
@@ -218,6 +224,7 @@
struct Features {
Features();
~Features();
+ void clear();
HashSet<AtomicStringImpl*> idsInRules;
HashSet<AtomicStringImpl*> attrsInRules;
OwnPtr<RuleSet> siblingRules;
Modified: trunk/Source/WebCore/css/SelectorChecker.cpp (104059 => 104060)
--- trunk/Source/WebCore/css/SelectorChecker.cpp 2012-01-04 20:02:54 UTC (rev 104059)
+++ trunk/Source/WebCore/css/SelectorChecker.cpp 2012-01-04 20:03:35 UTC (rev 104060)
@@ -1398,4 +1398,28 @@
return element->document()->frame() && element->document()->frame()->selection()->isFocusedAndActive();
}
+bool SelectorChecker::determineSelectorScopes(const CSSSelectorList& selectorList, HashSet<AtomicStringImpl*>& idScopes, HashSet<AtomicStringImpl*>& classScopes)
+{
+ for (CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) {
+ CSSSelector* rightmostSelector = selector;
+ CSSSelector::Relation relation = CSSSelector::Descendant;
+ for (;rightmostSelector->tagHistory(); rightmostSelector = rightmostSelector->tagHistory()) {
+ if (rightmostSelector->relation() != CSSSelector::SubSelector)
+ relation = rightmostSelector->relation();
+ }
+ if (relation != CSSSelector::Descendant && relation != CSSSelector::Child)
+ return false;
+ if (rightmostSelector->m_match == CSSSelector::Id) {
+ idScopes.add(rightmostSelector->value().impl());
+ continue;
+ }
+ if (rightmostSelector->m_match == CSSSelector::Class) {
+ classScopes.add(rightmostSelector->value().impl());
+ continue;
+ }
+ return false;
+ }
+ return true;
}
+
+}
Modified: trunk/Source/WebCore/css/SelectorChecker.h (104059 => 104060)
--- trunk/Source/WebCore/css/SelectorChecker.h 2012-01-04 20:02:54 UTC (rev 104059)
+++ trunk/Source/WebCore/css/SelectorChecker.h 2012-01-04 20:03:35 UTC (rev 104060)
@@ -90,6 +90,9 @@
enum LinkMatchMask { MatchLink = 1, MatchVisited = 2, MatchAll = MatchLink | MatchVisited };
static unsigned determineLinkMatchType(const CSSSelector*);
+ // Find the ids or classes selectors are scoped to. The selectors only apply to elements in subtrees where the root element matches the scope.
+ static bool determineSelectorScopes(const CSSSelectorList&, HashSet<AtomicStringImpl*>& idScopes, HashSet<AtomicStringImpl*>& classScopes);
+
private:
bool checkOneSelector(CSSSelector*, Element*, PseudoId& dynamicPseudo, bool isSubSelector, VisitedMatchType, RenderStyle*, RenderStyle* elementParentStyle) const;
bool checkScrollbarPseudoClass(CSSSelector*, PseudoId& dynamicPseudo) const;
Modified: trunk/Source/WebCore/dom/Document.cpp (104059 => 104060)
--- trunk/Source/WebCore/dom/Document.cpp 2012-01-04 20:02:54 UTC (rev 104059)
+++ trunk/Source/WebCore/dom/Document.cpp 2012-01-04 20:03:35 UTC (rev 104060)
@@ -382,6 +382,7 @@
, m_mutationObserverTypes(0)
#endif
, m_styleSheets(StyleSheetList::create(this))
+ , m_hadActiveLoadingStylesheet(false)
, m_readyState(Complete)
, m_styleRecalcTimer(this, &Document::styleRecalcTimerFired)
, m_pendingStyleRecalcShouldForce(false)
@@ -1528,7 +1529,7 @@
return; // Guard against re-entrancy. -dwh
if (m_hasDirtyStyleSelector)
- recalcStyleSelector();
+ updateActiveStylesheets(RecalcStyleImmediately);
InspectorInstrumentationCookie cookie = InspectorInstrumentation::willRecalculateStyle(this);
@@ -1553,7 +1554,7 @@
// style selector may set this again during recalc
m_hasNodesWithPlaceholderStyle = false;
- RefPtr<RenderStyle> documentStyle = CSSStyleSelector::styleForDocument(this);
+ RefPtr<RenderStyle> documentStyle = CSSStyleSelector::styleForDocument(this, m_styleSelector ? m_styleSelector->fontSelector() : 0);
StyleChange ch = diff(documentStyle.get(), renderer()->style());
if (ch != NoChange)
renderer()->setStyle(documentStyle.release());
@@ -1584,12 +1585,8 @@
m_inStyleRecalc = false;
// Pseudo element removal and similar may only work with these flags still set. Reset them after the style recalc.
- if (m_styleSelector) {
- m_usesSiblingRules = m_styleSelector->usesSiblingRules();
- m_usesFirstLineRules = m_styleSelector->usesFirstLineRules();
- m_usesBeforeAfterRules = m_styleSelector->usesBeforeAfterRules();
- m_usesLinkRules = m_styleSelector->usesLinkRules();
- }
+ if (m_styleSelector)
+ resetCSSFeatureFlags();
if (frameView) {
frameView->resumeScheduledEvents();
@@ -1778,6 +1775,23 @@
setSecurityOrigin(SecurityOrigin::createUnique());
}
+void Document::combineCSSFeatureFlags()
+{
+ // Delay resetting the flags until after next style recalc since unapplying the style may not work without these set (this is true at least with before/after).
+ m_usesSiblingRules = m_usesSiblingRules || m_styleSelector->usesSiblingRules();
+ m_usesFirstLineRules = m_usesFirstLineRules || m_styleSelector->usesFirstLineRules();
+ m_usesBeforeAfterRules = m_usesBeforeAfterRules || m_styleSelector->usesBeforeAfterRules();
+ m_usesLinkRules = m_usesLinkRules || m_styleSelector->usesLinkRules();
+}
+
+void Document::resetCSSFeatureFlags()
+{
+ m_usesSiblingRules = m_styleSelector->usesSiblingRules();
+ m_usesFirstLineRules = m_styleSelector->usesFirstLineRules();
+ m_usesBeforeAfterRules = m_styleSelector->usesBeforeAfterRules();
+ m_usesLinkRules = m_styleSelector->usesLinkRules();
+}
+
void Document::createStyleSelector()
{
bool matchAuthorAndUserStyles = true;
@@ -1785,11 +1799,7 @@
matchAuthorAndUserStyles = docSettings->authorAndUserStylesEnabled();
m_styleSelector = adoptPtr(new CSSStyleSelector(this, m_styleSheets.get(), m_mappedElementSheet.get(), pageUserSheet(), pageGroupUserSheets(), m_userSheets.get(),
!inQuirksMode(), matchAuthorAndUserStyles));
- // Delay resetting the flags until after next style recalc since unapplying the style may not work without these set (this is true at least with before/after).
- m_usesSiblingRules = m_usesSiblingRules || m_styleSelector->usesSiblingRules();
- m_usesFirstLineRules = m_usesFirstLineRules || m_styleSelector->usesFirstLineRules();
- m_usesBeforeAfterRules = m_usesBeforeAfterRules || m_styleSelector->usesBeforeAfterRules();
- m_usesLinkRules = m_usesLinkRules || m_styleSelector->usesLinkRules();
+ combineCSSFeatureFlags();
}
void Document::attach()
@@ -2950,7 +2960,7 @@
if (m_pendingStylesheets)
return;
- styleSelectorChanged(RecalcStyleImmediately);
+ styleSelectorChanged(RecalcStyleIfNeeded);
if (ScriptableDocumentParser* parser = scriptableDocumentParser())
parser->executeScriptsWaitingForStylesheets();
@@ -2971,8 +2981,10 @@
printf("Beginning update of style selector at time %d.\n", elapsedTime());
#endif
- recalcStyleSelector();
-
+ bool stylesheetChangeRequiresStyleRecalc = updateActiveStylesheets(updateFlag);
+ if (!stylesheetChangeRequiresStyleRecalc)
+ return;
+
if (updateFlag == DeferRecalcStyle) {
scheduleForcedStyleRecalc();
return;
@@ -3044,22 +3056,9 @@
{
m_styleSheetCandidateNodes.remove(node);
}
-
-void Document::recalcStyleSelector()
+
+void Document::collectActiveStylesheets(Vector<RefPtr<StyleSheet> >& sheets)
{
- if (m_inStyleRecalc) {
- // SVG <use> element may manage to invalidate style selector in the middle of a style recalc.
- // https://bugs.webkit.org/show_bug.cgi?id=54344
- // FIXME: This should be fixed in SVG and this code replaced with ASSERT(!m_inStyleRecalc).
- m_hasDirtyStyleSelector = true;
- scheduleForcedStyleRecalc();
- return;
- }
- if (!renderer() || !attached())
- return;
-
- StyleSheetVector sheets;
-
bool matchAuthorAndUserStyles = true;
if (Settings* settings = this->settings())
matchAuthorAndUserStyles = settings->authorAndUserStylesEnabled();
@@ -3070,9 +3069,7 @@
end = begin;
for (StyleSheetCandidateListHashSet::iterator it = begin; it != end; ++it) {
Node* n = *it;
-
StyleSheet* sheet = 0;
-
if (n->nodeType() == PROCESSING_INSTRUCTION_NODE) {
// Processing instruction (XML documents only).
// We don't support linking to embedded CSS stylesheets, see <https://bugs.webkit.org/show_bug.cgi?id=49281> for discussion.
@@ -3089,9 +3086,9 @@
#endif
} else if ((n->isHTMLElement() && (n->hasTagName(linkTag) || n->hasTagName(styleTag)))
#if ENABLE(SVG)
- || (n->isSVGElement() && n->hasTagName(SVGNames::styleTag))
+ || (n->isSVGElement() && n->hasTagName(SVGNames::styleTag))
#endif
- ) {
+ ) {
Element* e = static_cast<Element*>(n);
AtomicString title = e->getAttribute(titleAttr);
bool enabledViaScript = false;
@@ -3115,7 +3112,6 @@
if (!linkElement->sheet())
title = nullAtom;
}
-
// Get the current preferred styleset. This is the
// set of sheets that will be enabled.
#if ENABLE(SVG)
@@ -3128,7 +3124,6 @@
else
// <STYLE> element
sheet = static_cast<HTMLStyleElement*>(n)->sheet();
-
// Check to see if this sheet belongs to a styleset
// (thus making it PREFERRED or ALTERNATE rather than
// PERSISTENT).
@@ -3143,21 +3138,121 @@
if (e->hasLocalName(styleTag) || !rel.contains("alternate"))
m_preferredStylesheetSet = m_selectedStylesheetSet = title;
}
-
if (title != m_preferredStylesheetSet)
sheet = 0;
}
}
-
if (sheet)
sheets.append(sheet);
}
+}
- m_styleSheets->swap(sheets);
+bool Document::testAddedStylesheetRequiresStyleRecalc(CSSStyleSheet* stylesheet)
+{
+ if (stylesheet->disabled())
+ return false;
+ // See if all rules on the sheet are scoped to some specific ids or classes.
+ // Then test if we actually have any of those in the tree at the moment.
+ // FIXME: Even we if we find some, we could just invalidate those subtrees instead of invalidating the entire style.
+ HashSet<AtomicStringImpl*> idScopes;
+ HashSet<AtomicStringImpl*> classScopes;
+ if (!CSSStyleSelector::determineStylesheetSelectorScopes(stylesheet, idScopes, classScopes))
+ return true;
+ // Testing for classes is not particularly fast so bail out if there are more than a few.
+ static const int maximumClassScopesToTest = 4;
+ if (classScopes.size() > maximumClassScopesToTest)
+ return true;
+ HashSet<AtomicStringImpl*>::iterator end = idScopes.end();
+ for (HashSet<AtomicStringImpl*>::iterator it = idScopes.begin(); it != end; ++it) {
+ if (hasElementWithId(*it))
+ return true;
+ }
+ end = classScopes.end();
+ for (HashSet<AtomicStringImpl*>::iterator it = classScopes.begin(); it != end; ++it) {
+ // FIXME: getElementsByClassName is not optimal for this. We should handle all classes in a single pass.
+ if (getElementsByClassName(*it)->length())
+ return true;
+ }
+ return false;
+}
+
+void Document::analyzeStylesheetChange(StyleSelectorUpdateFlag updateFlag, const Vector<RefPtr<StyleSheet> >& newStylesheets, bool& requiresStyleSelectorReset, bool& requiresStyleRecalc)
+{
+ requiresStyleSelectorReset = true;
+ requiresStyleRecalc = true;
+
+ // Stylesheets of <style> elements that @import stylesheets are active but loading. We need to trigger a full recalc when such loads are done.
+ bool hasActiveLoadingStylesheet = false;
+ unsigned newStylesheetCount = newStylesheets.size();
+ for (unsigned i = 0; i < newStylesheetCount; ++i) {
+ if (newStylesheets[i]->isLoading())
+ hasActiveLoadingStylesheet = true;
+ }
+ if (m_hadActiveLoadingStylesheet && !hasActiveLoadingStylesheet) {
+ m_hadActiveLoadingStylesheet = false;
+ return;
+ }
+ m_hadActiveLoadingStylesheet = hasActiveLoadingStylesheet;
- m_styleSelector.clear();
+ if (updateFlag != RecalcStyleIfNeeded)
+ return;
+ if (!m_styleSelector)
+ return;
+
+ // See if we are just adding stylesheets.
+ unsigned oldStylesheetCount = m_styleSheets->length();
+ if (newStylesheetCount < oldStylesheetCount)
+ return;
+ for (unsigned i = 0; i < oldStylesheetCount; ++i) {
+ if (m_styleSheets->item(i) != newStylesheets[i])
+ return;
+ }
+ requiresStyleSelectorReset = false;
+
+ // 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 (!body() || m_hasNodesWithPlaceholderStyle)
+ return;
+ for (unsigned i = oldStylesheetCount; i < newStylesheetCount; ++i) {
+ if (!newStylesheets[i]->isCSSStyleSheet())
+ return;
+ if (testAddedStylesheetRequiresStyleRecalc(static_cast<CSSStyleSheet*>(newStylesheets[i].get())))
+ return;
+ }
+ requiresStyleRecalc = false;
+}
+
+bool Document::updateActiveStylesheets(StyleSelectorUpdateFlag updateFlag)
+{
+ if (m_inStyleRecalc) {
+ // SVG <use> element may manage to invalidate style selector in the middle of a style recalc.
+ // https://bugs.webkit.org/show_bug.cgi?id=54344
+ // FIXME: This should be fixed in SVG and this code replaced with ASSERT(!m_inStyleRecalc).
+ m_hasDirtyStyleSelector = true;
+ scheduleForcedStyleRecalc();
+ return false;
+ }
+ if (!renderer() || !attached())
+ return false;
+
+ StyleSheetVector newStylesheets;
+ collectActiveStylesheets(newStylesheets);
+
+ bool requiresStyleSelectorReset;
+ bool requiresStyleRecalc;
+ analyzeStylesheetChange(updateFlag, newStylesheets, requiresStyleSelectorReset, requiresStyleRecalc);
+
+ if (requiresStyleSelectorReset)
+ m_styleSelector.clear();
+ else {
+ m_styleSelector->appendAuthorStylesheets(m_styleSheets->length(), newStylesheets);
+ resetCSSFeatureFlags();
+ }
+ m_styleSheets->swap(newStylesheets);
+
m_didCalculateStyleSelector = true;
m_hasDirtyStyleSelector = false;
+
+ return requiresStyleRecalc;
}
void Document::setHoverNode(PassRefPtr<Node> newHoverNode)
Modified: trunk/Source/WebCore/dom/Document.h (104059 => 104060)
--- trunk/Source/WebCore/dom/Document.h 2012-01-04 20:02:54 UTC (rev 104059)
+++ trunk/Source/WebCore/dom/Document.h 2012-01-04 20:03:35 UTC (rev 104060)
@@ -211,7 +211,7 @@
PageshowEventPersisted = 1
};
-enum StyleSelectorUpdateFlag { RecalcStyleImmediately, DeferRecalcStyle };
+enum StyleSelectorUpdateFlag { RecalcStyleImmediately, DeferRecalcStyle, RecalcStyleIfNeeded };
class Document : public TreeScope, public ScriptExecutionContext {
public:
@@ -456,7 +456,7 @@
}
/**
- * Updates the pending sheet count and then calls updateStyleSelector.
+ * Updates the pending sheet count and then calls updateActiveStylesheets.
*/
void removePendingSheet();
@@ -491,7 +491,6 @@
* found and is used to calculate the derived styles for all rendering objects.
*/
void styleSelectorChanged(StyleSelectorUpdateFlag);
- void recalcStyleSelector();
bool usesSiblingRules() const { return m_usesSiblingRules || m_usesSiblingRulesOverride; }
void setUsesSiblingRules(bool b) { m_usesSiblingRulesOverride = b; }
@@ -1161,6 +1160,13 @@
void buildAccessKeyMap(TreeScope* root);
void createStyleSelector();
+ void combineCSSFeatureFlags();
+ void resetCSSFeatureFlags();
+
+ bool updateActiveStylesheets(StyleSelectorUpdateFlag);
+ void collectActiveStylesheets(Vector<RefPtr<StyleSheet> >&);
+ bool testAddedStylesheetRequiresStyleRecalc(CSSStyleSheet*);
+ void analyzeStylesheetChange(StyleSelectorUpdateFlag, const Vector<RefPtr<StyleSheet> >& newStylesheets, bool& requiresStyleSelectorReset, bool& requiresStyleRecalc);
void deleteCustomFonts();
@@ -1264,6 +1270,7 @@
#endif
RefPtr<StyleSheetList> m_styleSheets; // All of the stylesheets that are currently in effect for our media type and stylesheet set.
+ bool m_hadActiveLoadingStylesheet;
typedef ListHashSet<Node*, 32> StyleSheetCandidateListHashSet;
StyleSheetCandidateListHashSet m_styleSheetCandidateNodes; // All of the nodes that could potentially provide stylesheets to the document (<link>, <style>, <?xml-stylesheet>)