summaryrefslogtreecommitdiff
path: root/Source/WebCore/css/StyleInvalidationAnalysis.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/css/StyleInvalidationAnalysis.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/css/StyleInvalidationAnalysis.cpp')
-rw-r--r--Source/WebCore/css/StyleInvalidationAnalysis.cpp221
1 files changed, 147 insertions, 74 deletions
diff --git a/Source/WebCore/css/StyleInvalidationAnalysis.cpp b/Source/WebCore/css/StyleInvalidationAnalysis.cpp
index 48383ed77..b89eac08c 100644
--- a/Source/WebCore/css/StyleInvalidationAnalysis.cpp
+++ b/Source/WebCore/css/StyleInvalidationAnalysis.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
@@ -29,106 +29,179 @@
#include "CSSSelectorList.h"
#include "Document.h"
#include "ElementIterator.h"
+#include "ElementRuleCollector.h"
+#include "HTMLSlotElement.h"
+#include "SelectorFilter.h"
+#include "ShadowRoot.h"
#include "StyleRuleImport.h"
#include "StyleSheetContents.h"
-#include "StyledElement.h"
namespace WebCore {
-StyleInvalidationAnalysis::StyleInvalidationAnalysis(const Vector<StyleSheetContents*>& sheets)
- : m_dirtiesAllStyle(false)
+static bool shouldDirtyAllStyle(const Vector<RefPtr<StyleRuleBase>>& rules)
{
- for (unsigned i = 0; i < sheets.size() && !m_dirtiesAllStyle; ++i)
- analyzeStyleSheet(sheets[i]);
+ for (auto& rule : rules) {
+ if (is<StyleRuleMedia>(*rule)) {
+ const auto* childRules = downcast<StyleRuleMedia>(*rule).childRulesWithoutDeferredParsing();
+ if (childRules && shouldDirtyAllStyle(*childRules))
+ return true;
+ continue;
+ }
+ // FIXME: At least font faces don't need full recalc in all cases.
+ if (!is<StyleRule>(*rule))
+ return true;
+ }
+ return false;
}
-static bool determineSelectorScopes(const CSSSelectorList& selectorList, HashSet<AtomicStringImpl*>& idScopes, HashSet<AtomicStringImpl*>& classScopes)
+static bool shouldDirtyAllStyle(const StyleSheetContents& sheet)
{
- for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) {
- const CSSSelector* scopeSelector = 0;
- // This picks the widest scope, not the narrowest, to minimize the number of found scopes.
- for (const CSSSelector* current = selector; current; current = current->tagHistory()) {
- // Prefer ids over classes.
- if (current->m_match == CSSSelector::Id)
- scopeSelector = current;
- else if (current->m_match == CSSSelector::Class && (!scopeSelector || scopeSelector->m_match != CSSSelector::Id))
- scopeSelector = current;
- CSSSelector::Relation relation = current->relation();
- if (relation != CSSSelector::Descendant && relation != CSSSelector::Child && relation != CSSSelector::SubSelector)
- break;
- }
- if (!scopeSelector)
- return false;
- ASSERT(scopeSelector->m_match == CSSSelector::Class || scopeSelector->m_match == CSSSelector::Id);
- if (scopeSelector->m_match == CSSSelector::Id)
- idScopes.add(scopeSelector->value().impl());
- else
- classScopes.add(scopeSelector->value().impl());
+ for (auto& import : sheet.importRules()) {
+ if (!import->styleSheet())
+ continue;
+ if (shouldDirtyAllStyle(*import->styleSheet()))
+ return true;
+ }
+ if (shouldDirtyAllStyle(sheet.childRules()))
+ return true;
+ return false;
+}
+
+static bool shouldDirtyAllStyle(const Vector<StyleSheetContents*>& sheets)
+{
+ for (auto& sheet : sheets) {
+ if (shouldDirtyAllStyle(*sheet))
+ return true;
}
- return true;
+ return false;
}
-void StyleInvalidationAnalysis::analyzeStyleSheet(StyleSheetContents* styleSheetContents)
+StyleInvalidationAnalysis::StyleInvalidationAnalysis(const Vector<StyleSheetContents*>& sheets, const MediaQueryEvaluator& mediaQueryEvaluator)
+ : m_ownedRuleSet(std::make_unique<RuleSet>())
+ , m_ruleSet(*m_ownedRuleSet)
+ , m_dirtiesAllStyle(shouldDirtyAllStyle(sheets))
{
- ASSERT(!styleSheetContents->isLoading());
+ if (m_dirtiesAllStyle)
+ return;
- // 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.
- const Vector<RefPtr<StyleRuleImport>>& importRules = styleSheetContents->importRules();
- for (unsigned i = 0; i < importRules.size(); ++i) {
- if (!importRules[i]->styleSheet())
- continue;
- analyzeStyleSheet(importRules[i]->styleSheet());
- if (m_dirtiesAllStyle)
- return;
+ m_ownedRuleSet->disableAutoShrinkToFit();
+ for (auto& sheet : sheets)
+ m_ownedRuleSet->addRulesFromSheet(*sheet, mediaQueryEvaluator);
+
+ m_hasShadowPseudoElementRulesInAuthorSheet = m_ruleSet.hasShadowPseudoElementRules();
+}
+
+StyleInvalidationAnalysis::StyleInvalidationAnalysis(const RuleSet& ruleSet)
+ : m_ruleSet(ruleSet)
+ , m_hasShadowPseudoElementRulesInAuthorSheet(ruleSet.hasShadowPseudoElementRules())
+{
+}
+
+StyleInvalidationAnalysis::CheckDescendants StyleInvalidationAnalysis::invalidateIfNeeded(Element& element, const SelectorFilter* filter)
+{
+ if (m_hasShadowPseudoElementRulesInAuthorSheet) {
+ // FIXME: This could do actual rule matching too.
+ if (element.shadowRoot())
+ element.invalidateStyleForSubtree();
}
- const Vector<RefPtr<StyleRuleBase>>& rules = styleSheetContents->childRules();
- for (unsigned i = 0; i < rules.size(); i++) {
- StyleRuleBase* rule = rules[i].get();
- if (!rule->isStyleRule()) {
- // FIXME: Media rules and maybe some others could be allowed.
- m_dirtiesAllStyle = true;
- return;
- }
- StyleRule* styleRule = static_cast<StyleRule*>(rule);
- if (!determineSelectorScopes(styleRule->selectorList(), m_idScopes, m_classScopes)) {
- m_dirtiesAllStyle = true;
- return;
+
+ bool shouldCheckForSlots = !m_ruleSet.slottedPseudoElementRules().isEmpty() && !m_didInvalidateHostChildren;
+ if (shouldCheckForSlots && is<HTMLSlotElement>(element)) {
+ auto* containingShadowRoot = element.containingShadowRoot();
+ if (containingShadowRoot && containingShadowRoot->host()) {
+ for (auto& possiblySlotted : childrenOfType<Element>(*containingShadowRoot->host()))
+ possiblySlotted.invalidateStyle();
}
+ // No need to do this again.
+ m_didInvalidateHostChildren = true;
}
+
+ switch (element.styleValidity()) {
+ case Style::Validity::Valid: {
+ ElementRuleCollector ruleCollector(element, m_ruleSet, filter);
+ ruleCollector.setMode(SelectorChecker::Mode::CollectingRulesIgnoringVirtualPseudoElements);
+ ruleCollector.matchAuthorRules(false);
+
+ if (ruleCollector.hasMatchedRules())
+ element.invalidateStyle();
+ return CheckDescendants::Yes;
+ }
+ case Style::Validity::ElementInvalid:
+ return CheckDescendants::Yes;
+ case Style::Validity::SubtreeInvalid:
+ case Style::Validity::SubtreeAndRenderersInvalid:
+ if (shouldCheckForSlots)
+ return CheckDescendants::Yes;
+ return CheckDescendants::No;
+ }
+ ASSERT_NOT_REACHED();
+ return CheckDescendants::Yes;
}
-static bool elementMatchesSelectorScopes(const Element& element, const HashSet<AtomicStringImpl*>& idScopes, const HashSet<AtomicStringImpl*>& classScopes)
+void StyleInvalidationAnalysis::invalidateStyleForTree(Element& root, SelectorFilter* filter)
{
- if (!idScopes.isEmpty() && element.hasID() && idScopes.contains(element.idForStyleResolution().impl()))
- return true;
- if (classScopes.isEmpty() || !element.hasClass())
- return false;
- const SpaceSplitString& classNames = element.classNames();
- for (unsigned i = 0; i < classNames.size(); ++i) {
- if (classScopes.contains(classNames[i].impl()))
- return true;
+ if (invalidateIfNeeded(root, filter) == CheckDescendants::No)
+ return;
+
+ Vector<Element*, 20> parentStack;
+ Element* previousElement = &root;
+ auto descendants = descendantsOfType<Element>(root);
+ for (auto it = descendants.begin(), end = descendants.end(); it != end;) {
+ auto& descendant = *it;
+ auto* parent = descendant.parentElement();
+ if (parentStack.isEmpty() || parentStack.last() != parent) {
+ if (parent == previousElement) {
+ parentStack.append(parent);
+ if (filter)
+ filter->pushParent(parent);
+ } else {
+ while (parentStack.last() != parent) {
+ parentStack.removeLast();
+ if (filter)
+ filter->popParent();
+ }
+ }
+ }
+ previousElement = &descendant;
+
+ if (invalidateIfNeeded(descendant, filter) == CheckDescendants::Yes)
+ it.traverseNext();
+ else
+ it.traverseNextSkippingChildren();
}
- return false;
}
void StyleInvalidationAnalysis::invalidateStyle(Document& document)
{
ASSERT(!m_dirtiesAllStyle);
- if (m_idScopes.isEmpty() && m_classScopes.isEmpty())
+
+ Element* documentElement = document.documentElement();
+ if (!documentElement)
return;
- auto it = descendantsOfType<Element>(document).begin();
- auto end = descendantsOfType<Element>(document).end();
- while (it != end) {
- if (elementMatchesSelectorScopes(*it, m_idScopes, m_classScopes)) {
- it->setNeedsStyleRecalc();
- // The whole subtree is now invalidated, we can skip to the next sibling.
- it.traverseNextSkippingChildren();
- continue;
- }
- ++it;
+ SelectorFilter filter;
+ invalidateStyleForTree(*documentElement, &filter);
+}
+
+void StyleInvalidationAnalysis::invalidateStyle(ShadowRoot& shadowRoot)
+{
+ ASSERT(!m_dirtiesAllStyle);
+
+ if (!m_ruleSet.hostPseudoClassRules().isEmpty() && shadowRoot.host())
+ shadowRoot.host()->invalidateStyle();
+
+ for (auto& child : childrenOfType<Element>(shadowRoot)) {
+ SelectorFilter filter;
+ invalidateStyleForTree(child, &filter);
}
}
+void StyleInvalidationAnalysis::invalidateStyle(Element& element)
+{
+ ASSERT(!m_dirtiesAllStyle);
+
+ // Don't use SelectorFilter as the rule sets here tend to be small and the filter would have setup cost deep in the tree.
+ invalidateStyleForTree(element, nullptr);
+}
+
}