Diff
Modified: trunk/LayoutTests/ChangeLog (253615 => 253616)
--- trunk/LayoutTests/ChangeLog 2019-12-17 09:32:03 UTC (rev 253615)
+++ trunk/LayoutTests/ChangeLog 2019-12-17 09:51:54 UTC (rev 253616)
@@ -1,3 +1,15 @@
+2019-12-17 Antti Koivisto <an...@apple.com>
+
+ Resolve dynamic media queries without reconstructing RuleSets
+ https://bugs.webkit.org/show_bug.cgi?id=205264
+
+ Reviewed by Zalan Bujtas.
+
+ Add a test verifying that @font-face inside @media works in dynamic scenarios.
+
+ * fast/media/media-query-dynamic-with-font-face-expected.html: Added.
+ * fast/media/media-query-dynamic-with-font-face.html: Added.
+
2019-12-16 Said Abou-Hallawa <sabouhall...@apple.com>
WebGLRenderingContext.texImage2D() should respect EXIF orientation
Added: trunk/LayoutTests/fast/media/media-query-dynamic-with-font-face-expected.html (0 => 253616)
--- trunk/LayoutTests/fast/media/media-query-dynamic-with-font-face-expected.html (rev 0)
+++ trunk/LayoutTests/fast/media/media-query-dynamic-with-font-face-expected.html 2019-12-17 09:51:54 UTC (rev 253616)
@@ -0,0 +1,11 @@
+<iframe width=100 srcdoc="
+<style>
+@font-face {
+ font-family: family1;
+ src: local(courier);
+}
+body { background-color: green }
+body { font-family:family1 }
+</style>
+Test frame
+"></iframe>
Added: trunk/LayoutTests/fast/media/media-query-dynamic-with-font-face.html (0 => 253616)
--- trunk/LayoutTests/fast/media/media-query-dynamic-with-font-face.html (rev 0)
+++ trunk/LayoutTests/fast/media/media-query-dynamic-with-font-face.html 2019-12-17 09:51:54 UTC (rev 253616)
@@ -0,0 +1,27 @@
+<iframe id=frame srcdoc="
+<style>
+@media (min-width:200px) {
+ @font-face {
+ font-family: family1;
+ src: local(times);
+ }
+ body { background-color: red }
+}
+
+@media (max-width:200px) {
+ @font-face {
+ font-family: family1;
+ src: local(courier);
+ }
+ body { background-color: green }
+}
+body { font-family:family1 }
+</style>
+Test frame
+"></iframe>
+<script>
+frame._onload_ = () => {
+ frame.contentDocument.offsetWidth;
+ frame.width = 100;
+}
+</script>
Modified: trunk/Source/WebCore/ChangeLog (253615 => 253616)
--- trunk/Source/WebCore/ChangeLog 2019-12-17 09:32:03 UTC (rev 253615)
+++ trunk/Source/WebCore/ChangeLog 2019-12-17 09:51:54 UTC (rev 253616)
@@ -1,3 +1,104 @@
+2019-12-17 Antti Koivisto <an...@apple.com>
+
+ Resolve dynamic media queries without reconstructing RuleSets
+ https://bugs.webkit.org/show_bug.cgi?id=205264
+
+ Reviewed by Zalan Bujtas.
+
+ We currently do a full style resolver reset whenever a media query result changes. This is very inefficient
+ as we need to reconstuct all RuleSets and optimization structures. We also lose related caches and are forced
+ to re-resolve full document style. This may happen frequently, for example when resizing window on a responsive
+ web site.
+
+ With this patch we construct RuleDatas also for non-matching dynamic media queries and simply mark them disabled.
+ We create a data structure that allows enabling and disabling them efficiently as a response to environment changes
+ (like view resize). This allows us to avoid throwing away anything during common scenarios.
+
+ Test: fast/media/media-query-dynamic-with-font-face.html
+
+ * css/MediaQueryEvaluator.cpp:
+ (WebCore::MediaQueryEvaluator::evaluate const):
+
+ Add a mode where dynamic media queries all evaluate to true and only static properties can cause the query to fail.
+
+ * css/MediaQueryEvaluator.h:
+ * style/ElementRuleCollector.cpp:
+ (WebCore::Style::ElementRuleCollector::collectMatchingRulesForList):
+
+ Skip disabled rules during rule collection.
+
+ * style/RuleData.cpp:
+ (WebCore::Style::RuleData::RuleData):
+ * style/RuleData.h:
+ (WebCore::Style::RuleData::isEnabled const):
+ (WebCore::Style::RuleData::setEnabled):
+
+ Add a bit.
+
+ * style/RuleSet.cpp:
+ (WebCore::Style::RuleSet::addRule):
+
+ Collect positions of rules affected by dynamic media queries.
+
+ (WebCore::Style::RuleSet::addPageRule):
+ (WebCore::Style::RuleSet::addChildRules):
+ (WebCore::Style::RuleSet::addRulesFromSheet):
+
+ First check for a special case where we have style resolver mutating rules (like @font-face) inside a media query.
+ In this case we fall back to static resolution.
+
+ Then collect the rules. Static media queries (print etc) are evaluated right away, dynamic ones are collected by MediaQueryCollector.
+
+ (WebCore::Style::RuleSet::addStyleRule):
+ (WebCore::Style::RuleSet::traverseRuleDatas):
+ (WebCore::Style::RuleSet::evaluteDynamicMediaQueryRules):
+
+ Evaluate media queries for changes and flip the enabled state of the rules if needed.
+
+ (WebCore::Style::RuleSet::MediaQueryCollector::pushAndEvaluate):
+ (WebCore::Style::RuleSet::MediaQueryCollector::pop):
+ (WebCore::Style::RuleSet::MediaQueryCollector::didMutateResolver):
+ (WebCore::Style::RuleSet::MediaQueryCollector::addRulePositionIfNeeded):
+ * style/RuleSet.h:
+ (WebCore::Style::RuleSet::hasViewportDependentMediaQueries const):
+ * style/StyleResolver.cpp:
+ (WebCore::Style::Resolver::hasViewportDependentMediaQueries const):
+ (WebCore::Style::Resolver::evaluateDynamicMediaQueries):
+ (WebCore::Style::Resolver::addMediaQueryDynamicResults): Deleted.
+ (WebCore::Style::Resolver::hasMediaQueriesAffectedByViewportChange const): Deleted.
+ (WebCore::Style::Resolver::hasMediaQueriesAffectedByAccessibilitySettingsChange const): Deleted.
+ (WebCore::Style::Resolver::hasMediaQueriesAffectedByAppearanceChange const): Deleted.
+
+ Profiling doesn't show any need to handle the cases separately. Replace with single evaluateDynamicMediaQueries path.
+ We can bring type specific paths back easily if needed.
+
+ * style/StyleResolver.h:
+ (WebCore::Style::Resolver::hasViewportDependentMediaQueries const): Deleted.
+ (WebCore::Style::Resolver::hasAccessibilitySettingsDependentMediaQueries const): Deleted.
+ (WebCore::Style::Resolver::hasAppearanceDependentMediaQueries const): Deleted.
+ * style/StyleScope.cpp:
+ (WebCore::Style::Scope::evaluateMediaQueriesForViewportChange):
+ (WebCore::Style::Scope::evaluateMediaQueriesForAccessibilitySettingsChange):
+ (WebCore::Style::Scope::evaluateMediaQueriesForAppearanceChange):
+
+ Call into general evaluateDynamicMediaQueries.
+
+ (WebCore::Style::Scope::evaluateMediaQueries):
+
+ In normal case we can just invalidate style, not throw everything away.
+ This can be further improved by adding optimization rule sets.
+
+ * style/StyleScopeRuleSets.cpp:
+ (WebCore::Style::ScopeRuleSets::updateUserAgentMediaQueryStyleIfNeeded const):
+ (WebCore::Style::ScopeRuleSets::initializeUserStyle):
+ (WebCore::Style::ScopeRuleSets::collectRulesFromUserStyleSheets):
+ (WebCore::Style::makeRuleSet):
+ (WebCore::Style::ScopeRuleSets::hasViewportDependentMediaQueries const):
+ (WebCore::Style::ScopeRuleSets::evaluteDynamicMediaQueryRules):
+ (WebCore::Style::ScopeRuleSets::appendAuthorStyleSheets):
+ (WebCore::Style::ensureInvalidationRuleSets):
+ * style/StyleScopeRuleSets.h:
+
2019-12-17 youenn fablet <you...@apple.com>
FileList should be exposed to workers
Modified: trunk/Source/WebCore/css/MediaQueryEvaluator.cpp (253615 => 253616)
--- trunk/Source/WebCore/css/MediaQueryEvaluator.cpp 2019-12-17 09:32:03 UTC (rev 253615)
+++ trunk/Source/WebCore/css/MediaQueryEvaluator.cpp 2019-12-17 09:51:54 UTC (rev 253616)
@@ -145,7 +145,7 @@
return r == MediaQuery::Not ? !value : value;
}
-bool MediaQueryEvaluator::evaluate(const MediaQuerySet& querySet, MediaQueryDynamicResults* dynamicResults) const
+bool MediaQueryEvaluator::evaluate(const MediaQuerySet& querySet, MediaQueryDynamicResults* dynamicResults, Mode mode) const
{
LOG_WITH_STREAM(MediaQueries, stream << "MediaQueryEvaluator::evaluate on " << (m_document ? m_document->url().string() : emptyString()));
@@ -166,22 +166,36 @@
if (mediaTypeMatch(query.mediaType())) {
auto& expressions = query.expressions();
// Iterate through expressions, stop if any of them eval to false (AND semantics).
+ bool isDynamic = false;
size_t j = 0;
for (; j < expressions.size(); ++j) {
bool expressionResult = evaluate(expressions[j]);
if (dynamicResults) {
- if (isViewportDependent(expressions[j].mediaFeature()))
+ if (isViewportDependent(expressions[j].mediaFeature())) {
+ isDynamic = true;
dynamicResults->viewport.append({ expressions[j], expressionResult });
- if (isAppearanceDependent(expressions[j].mediaFeature()))
+ }
+ if (isAppearanceDependent(expressions[j].mediaFeature())) {
+ isDynamic = true;
dynamicResults->appearance.append({ expressions[j], expressionResult });
- if (isAccessibilitySettingsDependent(expressions[j].mediaFeature()))
+ }
+ if (isAccessibilitySettingsDependent(expressions[j].mediaFeature())) {
+ isDynamic = true;
dynamicResults->accessibilitySettings.append({ expressions[j], expressionResult });
+ }
}
+ if (mode == Mode::AlwaysMatchDynamic && isDynamic)
+ continue;
if (!expressionResult)
break;
}
+ if (mode == Mode::AlwaysMatchDynamic && isDynamic) {
+ result = true;
+ continue;
+ }
+
// Assume true if we are at the end of the list, otherwise assume false.
result = applyRestrictor(query.restrictor(), expressions.size() == j);
} else
Modified: trunk/Source/WebCore/css/MediaQueryEvaluator.h (253615 => 253616)
--- trunk/Source/WebCore/css/MediaQueryEvaluator.h 2019-12-17 09:32:03 UTC (rev 253615)
+++ trunk/Source/WebCore/css/MediaQueryEvaluator.h 2019-12-17 09:51:54 UTC (rev 253616)
@@ -83,7 +83,8 @@
bool evaluate(const MediaQueryExpression&) const;
bool evaluateForChanges(const MediaQueryDynamicResults&) const;
- WEBCORE_EXPORT bool evaluate(const MediaQuerySet&, MediaQueryDynamicResults* = nullptr) const;
+ enum class Mode { Normal, AlwaysMatchDynamic };
+ WEBCORE_EXPORT bool evaluate(const MediaQuerySet&, MediaQueryDynamicResults* = nullptr, Mode = Mode::Normal) const;
static bool mediaAttributeMatches(Document&, const String& attributeValue);
Modified: trunk/Source/WebCore/style/ElementRuleCollector.cpp (253615 => 253616)
--- trunk/Source/WebCore/style/ElementRuleCollector.cpp 2019-12-17 09:32:03 UTC (rev 253615)
+++ trunk/Source/WebCore/style/ElementRuleCollector.cpp 2019-12-17 09:51:54 UTC (rev 253616)
@@ -524,6 +524,9 @@
for (unsigned i = 0, size = rules->size(); i < size; ++i) {
const auto& ruleData = rules->data()[i];
+ if (UNLIKELY(!ruleData.isEnabled()))
+ continue;
+
if (!ruleData.canMatchPseudoElement() && m_pseudoElementRequest.pseudoId != PseudoId::None)
continue;
Modified: trunk/Source/WebCore/style/RuleData.cpp (253615 => 253616)
--- trunk/Source/WebCore/style/RuleData.cpp 2019-12-17 09:32:03 UTC (rev 253615)
+++ trunk/Source/WebCore/style/RuleData.cpp 2019-12-17 09:51:54 UTC (rev 253616)
@@ -169,6 +169,7 @@
, m_containsUncommonAttributeSelector(computeContainsUncommonAttributeSelector(*selector()))
, m_linkMatchType(SelectorChecker::determineLinkMatchType(selector()))
, m_propertyWhitelistType(determinePropertyWhitelistType(selector()))
+ , m_isEnabled(true)
, m_descendantSelectorIdentifierHashes(SelectorFilter::collectHashes(*selector()))
{
ASSERT(m_position == position);
Modified: trunk/Source/WebCore/style/RuleData.h (253615 => 253616)
--- trunk/Source/WebCore/style/RuleData.h 2019-12-17 09:32:03 UTC (rev 253615)
+++ trunk/Source/WebCore/style/RuleData.h 2019-12-17 09:51:54 UTC (rev 253616)
@@ -60,6 +60,9 @@
bool containsUncommonAttributeSelector() const { return m_containsUncommonAttributeSelector; }
unsigned linkMatchType() const { return m_linkMatchType; }
PropertyWhitelistType propertyWhitelistType() const { return static_cast<PropertyWhitelistType>(m_propertyWhitelistType); }
+ bool isEnabled() const { return m_isEnabled; }
+ void setEnabled(bool value) { m_isEnabled = value; }
+
const SelectorFilter::Hashes& descendantSelectorIdentifierHashes() const { return m_descendantSelectorIdentifierHashes; }
void disableSelectorFiltering() { m_descendantSelectorIdentifierHashes[0] = 0; }
@@ -76,6 +79,7 @@
unsigned m_containsUncommonAttributeSelector : 1;
unsigned m_linkMatchType : 2; // SelectorChecker::LinkMatchMask
unsigned m_propertyWhitelistType : 2;
+ unsigned m_isEnabled : 1;
SelectorFilter::Hashes m_descendantSelectorIdentifierHashes;
};
Modified: trunk/Source/WebCore/style/RuleSet.cpp (253615 => 253616)
--- trunk/Source/WebCore/style/RuleSet.cpp 2019-12-17 09:32:03 UTC (rev 253615)
+++ trunk/Source/WebCore/style/RuleSet.cpp 2019-12-17 09:51:54 UTC (rev 253616)
@@ -88,11 +88,15 @@
return leftmostSelector->match() == CSSSelector::PseudoClass && leftmostSelector->pseudoClassType() == CSSSelector::PseudoClassHost;
}
-void RuleSet::addRule(StyleRule* rule, unsigned selectorIndex, unsigned selectorListIndex)
+void RuleSet::addRule(StyleRule& rule, unsigned selectorIndex, unsigned selectorListIndex, MediaQueryCollector* mediaQueryCollector)
{
- RuleData ruleData(rule, selectorIndex, selectorListIndex, m_ruleCount++);
+ RuleData ruleData(&rule, selectorIndex, selectorListIndex, m_ruleCount++);
+
m_features.collectFeatures(ruleData);
+ if (mediaQueryCollector)
+ mediaQueryCollector->addRulePositionIfNeeded(ruleData.position());
+
unsigned classBucketSize = 0;
const CSSSelector* idSelector = nullptr;
const CSSSelector* tagSelector = nullptr;
@@ -260,65 +264,176 @@
m_universalRules.append(ruleData);
}
-void RuleSet::addPageRule(StyleRulePage* rule)
+void RuleSet::addPageRule(StyleRulePage& rule)
{
- m_pageRules.append(rule);
+ m_pageRules.append(&rule);
}
-void RuleSet::addChildRules(const Vector<RefPtr<StyleRuleBase>>& rules, const MediaQueryEvaluator& medium, Resolver* resolver, MediaQueryDynamicResults& mediaQueryDynamicResults)
+void RuleSet::addChildRules(const Vector<RefPtr<StyleRuleBase>>& rules, MediaQueryCollector& mediaQueryCollector, Resolver* resolver, AddRulesMode mode)
{
for (auto& rule : rules) {
- if (is<StyleRule>(*rule))
- addStyleRule(downcast<StyleRule>(rule.get()));
- else if (is<StyleRulePage>(*rule))
- addPageRule(downcast<StyleRulePage>(rule.get()));
- else if (is<StyleRuleMedia>(*rule)) {
+ if (mode == AddRulesMode::ResolverMutationScan && mediaQueryCollector.didMutateResolverWithinDynamicMediaQuery)
+ break;
+
+ if (is<StyleRule>(*rule)) {
+ if (mode == AddRulesMode::Normal)
+ addStyleRule(downcast<StyleRule>(*rule), mediaQueryCollector);
+ continue;
+ }
+ if (is<StyleRulePage>(*rule)) {
+ if (mode == AddRulesMode::Normal)
+ addPageRule(downcast<StyleRulePage>(*rule));
+ continue;
+ }
+ if (is<StyleRuleMedia>(*rule)) {
auto& mediaRule = downcast<StyleRuleMedia>(*rule);
- if ((!mediaRule.mediaQueries() || medium.evaluate(*mediaRule.mediaQueries(), &mediaQueryDynamicResults)))
- addChildRules(mediaRule.childRules(), medium, resolver, mediaQueryDynamicResults);
- } else if (is<StyleRuleFontFace>(*rule) && resolver) {
+ if (mediaQueryCollector.pushAndEvaluate(mediaRule.mediaQueries())) {
+ addChildRules(mediaRule.childRules(), mediaQueryCollector, resolver, mode);
+ mediaQueryCollector.pop(mediaRule.mediaQueries());
+ }
+ continue;
+ }
+ if (is<StyleRuleFontFace>(*rule)) {
// Add this font face to our set.
- resolver->document().fontSelector().addFontFaceRule(downcast<StyleRuleFontFace>(*rule.get()), false);
- resolver->invalidateMatchedDeclarationsCache();
- } else if (is<StyleRuleKeyframes>(*rule) && resolver)
- resolver->addKeyframeStyle(downcast<StyleRuleKeyframes>(*rule));
- else if (is<StyleRuleSupports>(*rule) && downcast<StyleRuleSupports>(*rule).conditionIsSupported())
- addChildRules(downcast<StyleRuleSupports>(*rule).childRules(), medium, resolver, mediaQueryDynamicResults);
+ if (resolver) {
+ resolver->document().fontSelector().addFontFaceRule(downcast<StyleRuleFontFace>(*rule.get()), false);
+ resolver->invalidateMatchedDeclarationsCache();
+ }
+ mediaQueryCollector.didMutateResolver();
+ continue;
+ }
+ if (is<StyleRuleKeyframes>(*rule)) {
+ if (resolver)
+ resolver->addKeyframeStyle(downcast<StyleRuleKeyframes>(*rule));
+ mediaQueryCollector.didMutateResolver();
+ continue;
+ }
+ if (is<StyleRuleSupports>(*rule) && downcast<StyleRuleSupports>(*rule).conditionIsSupported()) {
+ addChildRules(downcast<StyleRuleSupports>(*rule).childRules(), mediaQueryCollector, resolver, mode);
+ continue;
+ }
#if ENABLE(CSS_DEVICE_ADAPTATION)
- else if (is<StyleRuleViewport>(*rule) && resolver)
- resolver->viewportStyleResolver()->addViewportRule(downcast<StyleRuleViewport>(rule.get()));
+ if (is<StyleRuleViewport>(*rule)) {
+ if (resolver)
+ resolver->viewportStyleResolver()->addViewportRule(downcast<StyleRuleViewport>(rule.get()));
+ mediaQueryCollector.didMutateResolver();
+ continue;
+ }
#endif
}
}
-void RuleSet::addRulesFromSheet(StyleSheetContents& sheet, const MediaQueryEvaluator& mediaQueryEvaluator, Resolver* resolver)
+void RuleSet::addRulesFromSheet(StyleSheetContents& sheet, const MediaQueryEvaluator& evaluator)
{
- MediaQueryDynamicResults mediaQueryDynamicResults;
+ auto mediaQueryCollector = MediaQueryCollector { evaluator };
+ addRulesFromSheet(sheet, mediaQueryCollector, nullptr, AddRulesMode::Normal);
+}
+void RuleSet::addRulesFromSheet(StyleSheetContents& sheet, MediaQuerySet* sheetQuery, const MediaQueryEvaluator& evaluator, Style::Resolver& resolver)
+{
+ auto canUseDynamicMediaQueryResolution = [&] {
+ auto mediaQueryCollector = MediaQueryCollector { evaluator, true };
+ if (mediaQueryCollector.pushAndEvaluate(sheetQuery))
+ addRulesFromSheet(sheet, mediaQueryCollector, nullptr, AddRulesMode::ResolverMutationScan);
+ return !mediaQueryCollector.didMutateResolverWithinDynamicMediaQuery;
+ }();
+
+ auto mediaQueryCollector = MediaQueryCollector { evaluator, canUseDynamicMediaQueryResolution };
+
+ if (mediaQueryCollector.pushAndEvaluate(sheetQuery)) {
+ addRulesFromSheet(sheet, mediaQueryCollector, &resolver, AddRulesMode::Normal);
+ mediaQueryCollector.pop(sheetQuery);
+ }
+
+ m_hasViewportDependentMediaQueries = mediaQueryCollector.hasViewportDependentMediaQueries;
+ m_dynamicMediaQueryRules.appendVector(WTFMove(mediaQueryCollector.dynamicMediaQueryRules));
+
+ evaluteDynamicMediaQueryRules(evaluator);
+}
+
+void RuleSet::addRulesFromSheet(StyleSheetContents& sheet, MediaQueryCollector& mediaQueryCollector, Resolver* resolver, AddRulesMode mode)
+{
for (auto& rule : sheet.importRules()) {
if (!rule->styleSheet())
continue;
- if (rule->mediaQueries() && !mediaQueryEvaluator.evaluate(*rule->mediaQueries(), &mediaQueryDynamicResults))
- continue;
- addRulesFromSheet(*rule->styleSheet(), mediaQueryEvaluator, resolver);
+
+ if (mediaQueryCollector.pushAndEvaluate(rule->mediaQueries())) {
+ addRulesFromSheet(*rule->styleSheet(), mediaQueryCollector, resolver, mode);
+ mediaQueryCollector.pop(rule->mediaQueries());
+ }
}
- addChildRules(sheet.childRules(), mediaQueryEvaluator, resolver, mediaQueryDynamicResults);
+ addChildRules(sheet.childRules(), mediaQueryCollector, resolver, mode);
- if (resolver)
- resolver->addMediaQueryDynamicResults(mediaQueryDynamicResults);
-
- if (m_autoShrinkToFitEnabled)
+ if (m_autoShrinkToFitEnabled && mode == AddRulesMode::Normal)
shrinkToFit();
}
-void RuleSet::addStyleRule(StyleRule* rule)
+void RuleSet::addStyleRule(StyleRule& rule, MediaQueryCollector& mediaQueryCollector)
{
unsigned selectorListIndex = 0;
- for (size_t selectorIndex = 0; selectorIndex != notFound; selectorIndex = rule->selectorList().indexOfNextSelectorAfter(selectorIndex))
- addRule(rule, selectorIndex, selectorListIndex++);
+ for (size_t selectorIndex = 0; selectorIndex != notFound; selectorIndex = rule.selectorList().indexOfNextSelectorAfter(selectorIndex))
+ addRule(rule, selectorIndex, selectorListIndex++, &mediaQueryCollector);
}
+template<typename Function>
+void RuleSet::traverseRuleDatas(Function&& function)
+{
+ auto traverseVector = [&](auto& vector) {
+ for (auto& ruleData : vector)
+ function(ruleData);
+ };
+
+ auto traverseMap = [&](auto& map) {
+ for (auto& ruleDatas : map.values())
+ traverseVector(*ruleDatas);
+ };
+
+ traverseMap(m_idRules);
+ traverseMap(m_classRules);
+ traverseMap(m_tagLocalNameRules);
+ traverseMap(m_tagLowercaseLocalNameRules);
+ traverseMap(m_shadowPseudoElementRules);
+ traverseVector(m_linkPseudoClassRules);
+#if ENABLE(VIDEO_TRACK)
+ traverseVector(m_cuePseudoRules);
+#endif
+ traverseVector(m_hostPseudoClassRules);
+ traverseVector(m_slottedPseudoElementRules);
+ traverseVector(m_partPseudoElementRules);
+ traverseVector(m_focusPseudoClassRules);
+ traverseVector(m_universalRules);
+}
+
+RuleSet::MediaQueryStyleUpdateType RuleSet::evaluteDynamicMediaQueryRules(const MediaQueryEvaluator& evaluator)
+{
+ bool changes = false;
+ for (auto& dynamicRules : m_dynamicMediaQueryRules) {
+ bool result = true;
+ for (auto& set : dynamicRules.mediaQuerySets) {
+ if (!evaluator.evaluate(set.get())) {
+ result = false;
+ break;
+ }
+ }
+
+ if (result != dynamicRules.result) {
+ dynamicRules.result = result;
+ if (dynamicRules.requiresFullReset)
+ return MediaQueryStyleUpdateType::Reset;
+
+ traverseRuleDatas([&](RuleData& ruleData) {
+ if (!dynamicRules.affectedRulePositions.contains(ruleData.position()))
+ return;
+ ruleData.setEnabled(result);
+ changes = true;
+ });
+ }
+ }
+
+ return changes ? MediaQueryStyleUpdateType::Resolve : MediaQueryStyleUpdateType::None;
+}
+
bool RuleSet::hasShadowPseudoElementRules() const
{
if (!m_shadowPseudoElementRules.isEmpty())
@@ -355,5 +470,67 @@
m_features.shrinkToFit();
}
+RuleSet::MediaQueryCollector::~MediaQueryCollector() = default;
+
+bool RuleSet::MediaQueryCollector::pushAndEvaluate(MediaQuerySet* set)
+{
+ if (!set)
+ return true;
+
+ // Only evaluate static expressions that require style rebuild.
+ MediaQueryDynamicResults dynamicResults;
+ auto mode = collectDynamic ? MediaQueryEvaluator::Mode::AlwaysMatchDynamic : MediaQueryEvaluator::Mode::Normal;
+
+ bool result = evaluator.evaluate(*set, &dynamicResults, mode);
+
+ if (!dynamicResults.viewport.isEmpty())
+ hasViewportDependentMediaQueries = true;
+
+ if (!result)
+ return false;
+
+ if (!dynamicResults.isEmpty())
+ dynamicContextStack.append({ *set });
+
+ return true;
+}
+
+void RuleSet::MediaQueryCollector::pop(MediaQuerySet* set)
+{
+ if (!set || dynamicContextStack.isEmpty() || set != &dynamicContextStack.last().set.get())
+ return;
+
+ if (!dynamicContextStack.last().affectedRulePositions.isEmpty()) {
+ DynamicMediaQueryRules rules;
+ for (auto& context : dynamicContextStack)
+ rules.mediaQuerySets.append(context.set.get());
+
+ if (collectDynamic) {
+ auto& toAdd = dynamicContextStack.last().affectedRulePositions;
+ rules.affectedRulePositions.add(toAdd.begin(), toAdd.end());
+ } else
+ rules.requiresFullReset = true;
+
+ dynamicMediaQueryRules.append(WTFMove(rules));
+ }
+
+ dynamicContextStack.removeLast();
+}
+
+void RuleSet::MediaQueryCollector::didMutateResolver()
+{
+ if (dynamicContextStack.isEmpty())
+ return;
+ didMutateResolverWithinDynamicMediaQuery = true;
+}
+
+void RuleSet::MediaQueryCollector::addRulePositionIfNeeded(size_t index)
+{
+ if (dynamicContextStack.isEmpty())
+ return;
+ dynamicContextStack.last().affectedRulePositions.append(index);
+}
+
+
} // namespace Style
} // namespace WebCore
Modified: trunk/Source/WebCore/style/RuleSet.h (253615 => 253616)
--- trunk/Source/WebCore/style/RuleSet.h 2019-12-17 09:32:03 UTC (rev 253615)
+++ trunk/Source/WebCore/style/RuleSet.h 2019-12-17 09:51:54 UTC (rev 253616)
@@ -21,6 +21,7 @@
#pragma once
+#include "MediaList.h"
#include "RuleData.h"
#include "RuleFeature.h"
#include "SelectorCompiler.h"
@@ -35,7 +36,6 @@
class CSSSelector;
class MediaQueryEvaluator;
class StyleSheetContents;
-struct MediaQueryDynamicResults;
namespace Style {
@@ -62,15 +62,50 @@
typedef Vector<RuleData, 1> RuleDataVector;
typedef HashMap<AtomString, std::unique_ptr<RuleDataVector>> AtomRuleMap;
- void addRulesFromSheet(StyleSheetContents&, const MediaQueryEvaluator&, Style::Resolver* = nullptr);
+ struct DynamicMediaQueryRules {
+ Vector<Ref<MediaQuerySet>> mediaQuerySets;
+ HashSet<size_t, DefaultHash<size_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<size_t>> affectedRulePositions;
+ bool requiresFullReset { false };
+ bool result { true };
+ };
- void addStyleRule(StyleRule*);
- void addRule(StyleRule*, unsigned selectorIndex, unsigned selectorListIndex);
- void addPageRule(StyleRulePage*);
+ struct MediaQueryCollector {
+ ~MediaQueryCollector();
+
+ const MediaQueryEvaluator& evaluator;
+ const bool collectDynamic { false };
+
+ struct DynamicContext {
+ Ref<MediaQuerySet> set;
+ Vector<size_t> affectedRulePositions { };
+ };
+ Vector<DynamicContext> dynamicContextStack { };
+
+ Vector<DynamicMediaQueryRules> dynamicMediaQueryRules { };
+ bool didMutateResolverWithinDynamicMediaQuery { false };
+ bool hasViewportDependentMediaQueries { false };
+
+ bool pushAndEvaluate(MediaQuerySet*);
+ void pop(MediaQuerySet*);
+ void didMutateResolver();
+ void addRulePositionIfNeeded(size_t);
+ };
+
+ void addRulesFromSheet(StyleSheetContents&, const MediaQueryEvaluator&);
+ void addRulesFromSheet(StyleSheetContents&, MediaQuerySet* sheetQuery, const MediaQueryEvaluator&, Style::Resolver&);
+
+ void addStyleRule(StyleRule&, MediaQueryCollector&);
+ void addRule(StyleRule&, unsigned selectorIndex, unsigned selectorListIndex, MediaQueryCollector* = nullptr);
+ void addPageRule(StyleRulePage&);
void addToRuleSet(const AtomString& key, AtomRuleMap&, const RuleData&);
void shrinkToFit();
void disableAutoShrinkToFit() { m_autoShrinkToFitEnabled = false; }
+ bool hasViewportDependentMediaQueries() const { return m_hasViewportDependentMediaQueries; }
+
+ enum class MediaQueryStyleUpdateType { None, Resolve, Reset };
+ MediaQueryStyleUpdateType evaluteDynamicMediaQueryRules(const MediaQueryEvaluator&);
+
const RuleFeatureSet& features() const { return m_features; }
const RuleDataVector* idRules(const AtomString& key) const { return m_idRules.get(key); }
@@ -95,8 +130,12 @@
bool hasHostPseudoClassRulesMatchingInShadowTree() const { return m_hasHostPseudoClassRulesMatchingInShadowTree; }
private:
- void addChildRules(const Vector<RefPtr<StyleRuleBase>>&, const MediaQueryEvaluator& medium, Style::Resolver*, MediaQueryDynamicResults&);
+ enum class AddRulesMode { Normal, ResolverMutationScan };
+ void addRulesFromSheet(StyleSheetContents&, MediaQueryCollector&, Style::Resolver*, AddRulesMode);
+ void addChildRules(const Vector<RefPtr<StyleRuleBase>>&, MediaQueryCollector&, Style::Resolver*, AddRulesMode);
+ template<typename Function> void traverseRuleDatas(Function&&);
+
AtomRuleMap m_idRules;
AtomRuleMap m_classRules;
AtomRuleMap m_tagLocalNameRules;
@@ -116,6 +155,8 @@
bool m_hasHostPseudoClassRulesMatchingInShadowTree { false };
bool m_autoShrinkToFitEnabled { true };
RuleFeatureSet m_features;
+ bool m_hasViewportDependentMediaQueries { false };
+ Vector<DynamicMediaQueryRules> m_dynamicMediaQueryRules;
};
inline const RuleSet::RuleDataVector* RuleSet::tagRules(const AtomString& key, bool isHTMLName) const
Modified: trunk/Source/WebCore/style/StyleResolver.cpp (253615 => 253616)
--- trunk/Source/WebCore/style/StyleResolver.cpp 2019-12-17 09:32:03 UTC (rev 253615)
+++ trunk/Source/WebCore/style/StyleResolver.cpp 2019-12-17 09:51:54 UTC (rev 253616)
@@ -602,40 +602,15 @@
m_matchedDeclarationsCache.add(style, parentStyle, cacheHash, matchResult);
}
-void Resolver::addMediaQueryDynamicResults(const MediaQueryDynamicResults& results)
+bool Resolver::hasViewportDependentMediaQueries() const
{
- m_mediaQueryDynamicResults.append(results);
+ return m_ruleSets.hasViewportDependentMediaQueries();
}
-bool Resolver::hasMediaQueriesAffectedByViewportChange() const
+RuleSet::MediaQueryStyleUpdateType Resolver::evaluateDynamicMediaQueries()
{
- LOG(MediaQueries, "Style::Resolver::hasMediaQueriesAffectedByViewportChange evaluating queries");
- for (auto& result : m_mediaQueryDynamicResults.viewport) {
- if (m_mediaQueryEvaluator.evaluate(result._expression_) != result.result)
- return true;
- }
- return false;
+ return m_ruleSets.evaluteDynamicMediaQueryRules(m_mediaQueryEvaluator);
}
-bool Resolver::hasMediaQueriesAffectedByAccessibilitySettingsChange() const
-{
- LOG(MediaQueries, "Style::Resolver::hasMediaQueriesAffectedByAccessibilitySettingsChange evaluating queries");
- for (auto& result : m_mediaQueryDynamicResults.accessibilitySettings) {
- if (m_mediaQueryEvaluator.evaluate(result._expression_) != result.result)
- return true;
- }
- return false;
-}
-
-bool Resolver::hasMediaQueriesAffectedByAppearanceChange() const
-{
- LOG(MediaQueries, "Style::Resolver::hasMediaQueriesAffectedByAppearanceChange evaluating queries");
- for (auto& result : m_mediaQueryDynamicResults.appearance) {
- if (m_mediaQueryEvaluator.evaluate(result._expression_) != result.result)
- return true;
- }
- return false;
-}
-
} // namespace Style
} // namespace WebCore
Modified: trunk/Source/WebCore/style/StyleResolver.h (253615 => 253616)
--- trunk/Source/WebCore/style/StyleResolver.h 2019-12-17 09:32:03 UTC (rev 253615)
+++ trunk/Source/WebCore/style/StyleResolver.h 2019-12-17 09:51:54 UTC (rev 253616)
@@ -131,16 +131,9 @@
ViewportStyleResolver* viewportStyleResolver() { return m_viewportStyleResolver.get(); }
#endif
- void addMediaQueryDynamicResults(const MediaQueryDynamicResults&);
- bool hasViewportDependentMediaQueries() const { return !m_mediaQueryDynamicResults.viewport.isEmpty(); }
- bool hasMediaQueriesAffectedByViewportChange() const;
+ bool hasViewportDependentMediaQueries() const;
+ RuleSet::MediaQueryStyleUpdateType evaluateDynamicMediaQueries();
- bool hasAccessibilitySettingsDependentMediaQueries() const { return !m_mediaQueryDynamicResults.accessibilitySettings.isEmpty(); }
- bool hasMediaQueriesAffectedByAccessibilitySettingsChange() const;
-
- bool hasAppearanceDependentMediaQueries() const { return !m_mediaQueryDynamicResults.appearance.isEmpty(); }
- bool hasMediaQueriesAffectedByAppearanceChange() const;
-
void addKeyframeStyle(Ref<StyleRuleKeyframes>&&);
bool usesFirstLineRules() const { return m_ruleSets.features().usesFirstLineRules; }
@@ -200,8 +193,6 @@
RenderStyle* m_overrideDocumentElementStyle { nullptr };
- MediaQueryDynamicResults m_mediaQueryDynamicResults;
-
#if ENABLE(CSS_DEVICE_ADAPTATION)
RefPtr<ViewportStyleResolver> m_viewportStyleResolver;
#endif
Modified: trunk/Source/WebCore/style/StyleScope.cpp (253615 => 253616)
--- trunk/Source/WebCore/style/StyleScope.cpp 2019-12-17 09:32:03 UTC (rev 253615)
+++ trunk/Source/WebCore/style/StyleScope.cpp 2019-12-17 09:51:54 UTC (rev 253616)
@@ -622,7 +622,7 @@
void Scope::evaluateMediaQueriesForViewportChange()
{
evaluateMediaQueries([] (Resolver& resolver) {
- return resolver.hasMediaQueriesAffectedByViewportChange();
+ return resolver.evaluateDynamicMediaQueries();
});
}
@@ -629,7 +629,7 @@
void Scope::evaluateMediaQueriesForAccessibilitySettingsChange()
{
evaluateMediaQueries([] (Resolver& resolver) {
- return resolver.hasMediaQueriesAffectedByAccessibilitySettingsChange();
+ return resolver.evaluateDynamicMediaQueries();
});
}
@@ -636,7 +636,7 @@
void Scope::evaluateMediaQueriesForAppearanceChange()
{
evaluateMediaQueries([] (Resolver& resolver) {
- return resolver.hasMediaQueriesAffectedByAppearanceChange();
+ return resolver.evaluateDynamicMediaQueries();
});
}
@@ -650,9 +650,22 @@
auto* resolver = resolverIfExists();
if (!resolver)
return;
- if (!testFunction(*resolver))
+
+ auto updateType = testFunction(*resolver);
+
+ switch (updateType) {
+ case RuleSet::MediaQueryStyleUpdateType::None:
return;
- scheduleUpdate(UpdateType::ContentsOrInterpretation);
+ case RuleSet::MediaQueryStyleUpdateType::Resolve:
+ // FIXME: We could have an invalidation ruleset for rules inside dynamic media queries.
+ if (auto* documentElement = m_document.documentElement())
+ documentElement->invalidateStyleForSubtree();
+ break;
+ case RuleSet::MediaQueryStyleUpdateType::Reset:
+ scheduleUpdate(UpdateType::ContentsOrInterpretation);
+ break;
+ }
+
InspectorInstrumentation::mediaQueryResultChanged(m_document);
}
Modified: trunk/Source/WebCore/style/StyleScopeRuleSets.cpp (253615 => 253616)
--- trunk/Source/WebCore/style/StyleScopeRuleSets.cpp 2019-12-17 09:32:03 UTC (rev 253615)
+++ trunk/Source/WebCore/style/StyleScopeRuleSets.cpp 2019-12-17 09:51:54 UTC (rev 253616)
@@ -70,18 +70,11 @@
return;
m_userAgentMediaQueryRuleCountOnUpdate = ruleCount;
-#if !ASSERT_DISABLED
- bool hadViewportDependentMediaQueries = m_styleResolver.hasViewportDependentMediaQueries();
-#endif
-
// Media queries on user agent sheet need to evaluated in document context. They behave like author sheets in this respect.
auto& mediaQueryEvaluator = m_styleResolver.mediaQueryEvaluator();
m_userAgentMediaQueryStyle = makeUnique<RuleSet>();
- m_userAgentMediaQueryStyle->addRulesFromSheet(*UserAgentStyle::mediaQueryStyleSheet, mediaQueryEvaluator, &m_styleResolver);
-
- // Viewport dependent queries are currently too inefficient to allow on UA sheet.
- ASSERT(!m_styleResolver.hasViewportDependentMediaQueries() || hadViewportDependentMediaQueries);
+ m_userAgentMediaQueryStyle->addRulesFromSheet(*UserAgentStyle::mediaQueryStyleSheet, nullptr, mediaQueryEvaluator, m_styleResolver);
}
RuleSet* ScopeRuleSets::userStyle() const
@@ -97,7 +90,7 @@
auto& mediaQueryEvaluator = m_styleResolver.mediaQueryEvaluator();
auto tempUserStyle = makeUnique<RuleSet>();
if (CSSStyleSheet* pageUserSheet = extensionStyleSheets.pageUserSheet())
- tempUserStyle->addRulesFromSheet(pageUserSheet->contents(), mediaQueryEvaluator, &m_styleResolver);
+ tempUserStyle->addRulesFromSheet(pageUserSheet->contents(), nullptr, mediaQueryEvaluator, m_styleResolver);
collectRulesFromUserStyleSheets(extensionStyleSheets.injectedUserStyleSheets(), *tempUserStyle, mediaQueryEvaluator);
collectRulesFromUserStyleSheets(extensionStyleSheets.documentUserStyleSheets(), *tempUserStyle, mediaQueryEvaluator);
if (tempUserStyle->ruleCount() > 0 || tempUserStyle->pageRules().size() > 0)
@@ -108,7 +101,7 @@
{
for (unsigned i = 0; i < userSheets.size(); ++i) {
ASSERT(userSheets[i]->contents().isUserStyleSheet());
- userStyle.addRulesFromSheet(userSheets[i]->contents(), medium, &m_styleResolver);
+ userStyle.addRulesFromSheet(userSheets[i]->contents(), nullptr, medium, m_styleResolver);
}
}
@@ -119,7 +112,7 @@
return nullptr;
auto ruleSet = makeUnique<RuleSet>();
for (size_t i = 0; i < size; ++i)
- ruleSet->addRule(rules[i].rule, rules[i].selectorIndex, rules[i].selectorListIndex);
+ ruleSet->addRule(*rules[i].rule, rules[i].selectorIndex, rules[i].selectorListIndex);
ruleSet->shrinkToFit();
return ruleSet;
}
@@ -136,22 +129,49 @@
m_userAgentMediaQueryStyle = nullptr;
}
+bool ScopeRuleSets::hasViewportDependentMediaQueries() const
+{
+ if (m_authorStyle->hasViewportDependentMediaQueries())
+ return true;
+ if (m_userStyle && m_userStyle->hasViewportDependentMediaQueries())
+ return true;
+ if (m_userAgentMediaQueryStyle && m_userAgentMediaQueryStyle->hasViewportDependentMediaQueries())
+ return true;
+
+ return false;
+}
+
+RuleSet::MediaQueryStyleUpdateType ScopeRuleSets::evaluteDynamicMediaQueryRules(const MediaQueryEvaluator& evaluator)
+{
+ auto updateType = RuleSet::MediaQueryStyleUpdateType::None;
+
+ auto evaluate = [&](auto& ruleSet) {
+ if (!ruleSet)
+ return false;
+ auto newUpdateType = ruleSet->evaluteDynamicMediaQueryRules(evaluator);
+ if (newUpdateType > updateType)
+ updateType = newUpdateType;
+ return updateType == RuleSet::MediaQueryStyleUpdateType::Reset;
+ };
+
+ if (evaluate(m_authorStyle))
+ return updateType;
+
+ if (evaluate(m_userStyle))
+ return updateType;
+
+ evaluate(m_userAgentMediaQueryStyle);
+ return updateType;
+}
+
void ScopeRuleSets::appendAuthorStyleSheets(const Vector<RefPtr<CSSStyleSheet>>& styleSheets, MediaQueryEvaluator* medium, InspectorCSSOMWrappers& inspectorCSSOMWrappers)
{
- // 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.
- MediaQueryDynamicResults mediaQueryDynamicResults;
-
for (auto& cssSheet : styleSheets) {
ASSERT(!cssSheet->disabled());
- if (cssSheet->mediaQueries() && !medium->evaluate(*cssSheet->mediaQueries(), &mediaQueryDynamicResults))
- continue;
- m_authorStyle->addRulesFromSheet(cssSheet->contents(), *medium, &m_styleResolver);
+ m_authorStyle->addRulesFromSheet(cssSheet->contents(), cssSheet->mediaQueries(), *medium, m_styleResolver);
inspectorCSSOMWrappers.collectFromStyleSheetIfNeeded(cssSheet.get());
}
- m_styleResolver.addMediaQueryDynamicResults(mediaQueryDynamicResults);
-
m_authorStyle->shrinkToFit();
collectFeatures();
}
@@ -200,7 +220,7 @@
auto& ruleSet = matchElementArray[arrayIndex];
if (!ruleSet)
ruleSet = makeUnique<RuleSet>();
- ruleSet->addRule(feature.rule, feature.selectorIndex, feature.selectorListIndex);
+ ruleSet->addRule(*feature.rule, feature.selectorIndex, feature.selectorListIndex);
if (feature.invalidationSelector)
invalidationSelectorArray[arrayIndex].append(feature.invalidationSelector);
}
Modified: trunk/Source/WebCore/style/StyleScopeRuleSets.h (253615 => 253616)
--- trunk/Source/WebCore/style/StyleScopeRuleSets.h 2019-12-17 09:32:03 UTC (rev 253615)
+++ trunk/Source/WebCore/style/StyleScopeRuleSets.h 2019-12-17 09:51:54 UTC (rev 253616)
@@ -78,6 +78,9 @@
void resetUserAgentMediaQueryStyle();
+ bool hasViewportDependentMediaQueries() const;
+ RuleSet::MediaQueryStyleUpdateType evaluteDynamicMediaQueryRules(const MediaQueryEvaluator&);
+
RuleFeatureSet& mutableFeatures();
bool& isInvalidatingStyleWithRuleSets() { return m_isInvalidatingStyleWithRuleSets; }