Factor matched declarations cache into a class
authorantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 Nov 2019 23:01:20 +0000 (23:01 +0000)
committerantti@apple.com <antti@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 Nov 2019 23:01:20 +0000 (23:01 +0000)
https://bugs.webkit.org/show_bug.cgi?id=203972

Reviewed by Zalan Bujtas.

Move the code out from StyleResolver and rename "matched properties cache" -> "matched declarations cache"
to better describe it.

* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* css/ElementRuleCollector.cpp:
(WebCore::ElementRuleCollector::declarationsForOrigin):

Add a helper.

(WebCore::ElementRuleCollector::transferMatchedRules):

Reserve vector capacity before adding rules.

(WebCore::ElementRuleCollector::addMatchedProperties):
* css/ElementRuleCollector.h:
(WebCore::MatchResult::operator== const):
(WebCore::MatchResult::operator!= const):

Remove the unneeded inline capacity so we can just use MatchResult itself in the cache.

* css/RuleSet.cpp:
(WebCore::RuleSet::addChildRules):
* css/StyleResolver.cpp:
(WebCore::StyleResolver::StyleResolver):
(WebCore::StyleResolver::invalidateMatchedDeclarationsCache):
(WebCore::StyleResolver::clearCachedDeclarationsAffectedByViewportUnits):
(WebCore::StyleResolver::applyMatchedProperties):
(WebCore::StyleResolver::sweepMatchedPropertiesCache): Deleted.
(WebCore::StyleResolver::computeMatchedPropertiesHash): Deleted.
(WebCore::StyleResolver::findFromMatchedPropertiesCache): Deleted.
(WebCore::StyleResolver::addToMatchedPropertiesCache): Deleted.
(WebCore::StyleResolver::invalidateMatchedPropertiesCache): Deleted.
(WebCore::StyleResolver::clearCachedPropertiesAffectedByViewportUnits): Deleted.
(WebCore::isCacheableInMatchedPropertiesCache): Deleted.
* css/StyleResolver.h:
(WebCore::StyleResolver::MatchedPropertiesCacheItem::MatchedPropertiesCacheItem): Deleted.
* dom/Document.cpp:
(WebCore::Document::invalidateMatchedPropertiesCacheAndForceStyleRecalc):
(WebCore::Document::updateViewportUnitsOnResize):
* page/Page.cpp:
(WebCore::Page::updateStyleAfterChangeInEnvironment):
* style/MatchedDeclarationsCache.cpp: Added.
(WebCore::Style::MatchedDeclarationsCache::MatchedDeclarationsCache):
(WebCore::Style::MatchedDeclarationsCache::isCacheable):
(WebCore::Style::MatchedDeclarationsCache::computeHash):
(WebCore::Style::MatchedDeclarationsCache::find):
(WebCore::Style::MatchedDeclarationsCache::add):
(WebCore::Style::MatchedDeclarationsCache::invalidate):
(WebCore::Style::MatchedDeclarationsCache::clearEntriesAffectedByViewportUnits):
(WebCore::Style::MatchedDeclarationsCache::sweep):
* style/MatchedDeclarationsCache.h: Added.
* style/StyleTreeResolver.cpp:
(WebCore::Style::TreeResolver::resolveElement):

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

13 files changed:
Source/WebCore/ChangeLog
Source/WebCore/Sources.txt
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/css/ElementRuleCollector.cpp
Source/WebCore/css/ElementRuleCollector.h
Source/WebCore/css/RuleSet.cpp
Source/WebCore/css/StyleResolver.cpp
Source/WebCore/css/StyleResolver.h
Source/WebCore/dom/Document.cpp
Source/WebCore/page/Page.cpp
Source/WebCore/style/MatchedDeclarationsCache.cpp [new file with mode: 0644]
Source/WebCore/style/MatchedDeclarationsCache.h [new file with mode: 0644]
Source/WebCore/style/StyleTreeResolver.cpp

index f95b538..d0a1810 100644 (file)
@@ -1,3 +1,65 @@
+2019-11-07  Antti Koivisto  <antti@apple.com>
+
+        Factor matched declarations cache into a class
+        https://bugs.webkit.org/show_bug.cgi?id=203972
+
+        Reviewed by Zalan Bujtas.
+
+        Move the code out from StyleResolver and rename "matched properties cache" -> "matched declarations cache"
+        to better describe it.
+
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * css/ElementRuleCollector.cpp:
+        (WebCore::ElementRuleCollector::declarationsForOrigin):
+
+        Add a helper.
+
+        (WebCore::ElementRuleCollector::transferMatchedRules):
+
+        Reserve vector capacity before adding rules.
+
+        (WebCore::ElementRuleCollector::addMatchedProperties):
+        * css/ElementRuleCollector.h:
+        (WebCore::MatchResult::operator== const):
+        (WebCore::MatchResult::operator!= const):
+
+        Remove the unneeded inline capacity so we can just use MatchResult itself in the cache.
+
+        * css/RuleSet.cpp:
+        (WebCore::RuleSet::addChildRules):
+        * css/StyleResolver.cpp:
+        (WebCore::StyleResolver::StyleResolver):
+        (WebCore::StyleResolver::invalidateMatchedDeclarationsCache):
+        (WebCore::StyleResolver::clearCachedDeclarationsAffectedByViewportUnits):
+        (WebCore::StyleResolver::applyMatchedProperties):
+        (WebCore::StyleResolver::sweepMatchedPropertiesCache): Deleted.
+        (WebCore::StyleResolver::computeMatchedPropertiesHash): Deleted.
+        (WebCore::StyleResolver::findFromMatchedPropertiesCache): Deleted.
+        (WebCore::StyleResolver::addToMatchedPropertiesCache): Deleted.
+        (WebCore::StyleResolver::invalidateMatchedPropertiesCache): Deleted.
+        (WebCore::StyleResolver::clearCachedPropertiesAffectedByViewportUnits): Deleted.
+        (WebCore::isCacheableInMatchedPropertiesCache): Deleted.
+        * css/StyleResolver.h:
+        (WebCore::StyleResolver::MatchedPropertiesCacheItem::MatchedPropertiesCacheItem): Deleted.
+        * dom/Document.cpp:
+        (WebCore::Document::invalidateMatchedPropertiesCacheAndForceStyleRecalc):
+        (WebCore::Document::updateViewportUnitsOnResize):
+        * page/Page.cpp:
+        (WebCore::Page::updateStyleAfterChangeInEnvironment):
+        * style/MatchedDeclarationsCache.cpp: Added.
+        (WebCore::Style::MatchedDeclarationsCache::MatchedDeclarationsCache):
+        (WebCore::Style::MatchedDeclarationsCache::isCacheable):
+        (WebCore::Style::MatchedDeclarationsCache::computeHash):
+        (WebCore::Style::MatchedDeclarationsCache::find):
+        (WebCore::Style::MatchedDeclarationsCache::add):
+        (WebCore::Style::MatchedDeclarationsCache::invalidate):
+        (WebCore::Style::MatchedDeclarationsCache::clearEntriesAffectedByViewportUnits):
+        (WebCore::Style::MatchedDeclarationsCache::sweep):
+        * style/MatchedDeclarationsCache.h: Added.
+        * style/StyleTreeResolver.cpp:
+        (WebCore::Style::TreeResolver::resolveElement):
+
 2019-11-07  Eric Carlson  <eric.carlson@apple.com>
 
         UserMediaController::from(document) can return nullptr
index c69fb57..35fd422 100644 (file)
@@ -2344,6 +2344,7 @@ style/AttributeChangeInvalidation.cpp
 style/ClassChangeInvalidation.cpp
 style/IdChangeInvalidation.cpp
 style/InlineTextBoxStyle.cpp
+style/MatchedDeclarationsCache.cpp
 style/PropertyCascade.cpp
 style/StyleBuilder.cpp
 style/StyleBuilderState.cpp
index d9061b2..b7ec33c 100644 (file)
                E44B4BB4141650D7002B1D8B /* SelectorChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = E44B4BB2141650D7002B1D8B /* SelectorChecker.h */; };
                E44FA1851BCA6B5A0091B6EF /* ComposedTreeIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = E44FA1841BCA6B5A0091B6EF /* ComposedTreeIterator.h */; };
                E45322AC140CE267005A0F92 /* SelectorQuery.h in Headers */ = {isa = PBXBuildFile; fileRef = E45322AA140CE267005A0F92 /* SelectorQuery.h */; };
+               E45BA6AA2374926C004DFC07 /* MatchedDeclarationsCache.h in Headers */ = {isa = PBXBuildFile; fileRef = E45BA6A82374926B004DFC07 /* MatchedDeclarationsCache.h */; };
                E4605FEC2166480900E53046 /* PrewarmInformation.h in Headers */ = {isa = PBXBuildFile; fileRef = E4605FEA2166480800E53046 /* PrewarmInformation.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E461802D1C8DD2900026C02C /* StyleRelations.h in Headers */ = {isa = PBXBuildFile; fileRef = E461802C1C8DD2900026C02C /* StyleRelations.h */; };
                E461D65F1BB0C80D00CB5645 /* StyleScope.h in Headers */ = {isa = PBXBuildFile; fileRef = E461D65E1BB0C80D00CB5645 /* StyleScope.h */; settings = {ATTRIBUTES = (Private, ); }; };
                E45390340EAFD637003695C8 /* ScrollViewIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ScrollViewIOS.mm; sourceTree = "<group>"; };
                E453903C0EAFD637003695C8 /* WidgetIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WidgetIOS.mm; sourceTree = "<group>"; };
                E45390AD0EAFF4B5003695C8 /* SystemMemoryIOS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SystemMemoryIOS.cpp; sourceTree = "<group>"; };
+               E45BA6A82374926B004DFC07 /* MatchedDeclarationsCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MatchedDeclarationsCache.h; sourceTree = "<group>"; };
+               E45BA6AB2374927B004DFC07 /* MatchedDeclarationsCache.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MatchedDeclarationsCache.cpp; sourceTree = "<group>"; };
                E4605FEA2166480800E53046 /* PrewarmInformation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrewarmInformation.h; sourceTree = "<group>"; };
                E461802C1C8DD2900026C02C /* StyleRelations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StyleRelations.h; sourceTree = "<group>"; };
                E461802E1C8DD4D20026C02C /* StyleRelations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StyleRelations.cpp; sourceTree = "<group>"; };
                                E4A814DF1C7338EB00BF85AC /* IdChangeInvalidation.h */,
                                1C0106FE192594DF008A4201 /* InlineTextBoxStyle.cpp */,
                                1C0106FF192594DF008A4201 /* InlineTextBoxStyle.h */,
+                               E45BA6AB2374927B004DFC07 /* MatchedDeclarationsCache.cpp */,
+                               E45BA6A82374926B004DFC07 /* MatchedDeclarationsCache.h */,
                                E4ABABE52361A34200FA4345 /* PropertyCascade.cpp */,
                                E4ABABE22361A32900FA4345 /* PropertyCascade.h */,
                                E4ABAC07236B018100FA4345 /* StyleBuilder.cpp */,
                                77A17AA712F28B2A004E02F6 /* JSOESVertexArrayObject.h in Headers */,
                                FDF6BAF9134A4C9800822920 /* JSOfflineAudioCompletionEvent.h in Headers */,
                                FDA9326716703BA9008982DC /* JSOfflineAudioContext.h in Headers */,
+                               E45BA6AA2374926C004DFC07 /* MatchedDeclarationsCache.h in Headers */,
                                314877E61FAAB02500C05759 /* JSOffscreenCanvas.h in Headers */,
                                3140C5271FDF558200D2A873 /* JSOffscreenCanvasRenderingContext2D.h in Headers */,
                                57E233651DC7DB1F00F28D01 /* JsonWebKey.h in Headers */,
index 9848442..531914f 100644 (file)
@@ -159,6 +159,18 @@ void ElementRuleCollector::collectMatchingRules(const MatchRequest& matchRequest
     collectMatchingRulesForList(matchRequest.ruleSet->universalRules(), matchRequest);
 }
 
+
+Vector<MatchedProperties>& ElementRuleCollector::declarationsForOrigin(MatchResult& matchResult, DeclarationOrigin declarationOrigin)
+{
+    switch (declarationOrigin) {
+    case DeclarationOrigin::UserAgent: return matchResult.userAgentDeclarations;
+    case DeclarationOrigin::User: return matchResult.userDeclarations;
+    case DeclarationOrigin::Author: return matchResult.authorDeclarations;
+    }
+    ASSERT_NOT_REACHED();
+    return matchResult.authorDeclarations;
+}
+
 void ElementRuleCollector::sortAndTransferMatchedRules(DeclarationOrigin declarationOrigin)
 {
     if (m_matchedRules.isEmpty())
@@ -171,8 +183,8 @@ void ElementRuleCollector::sortAndTransferMatchedRules(DeclarationOrigin declara
 
 void ElementRuleCollector::transferMatchedRules(DeclarationOrigin declarationOrigin, Optional<Style::ScopeOrdinal> fromScope)
 {
-    if (m_matchedRules.size() <= m_matchedRuleTransferIndex)
-        return;
+    if (m_mode != SelectorChecker::Mode::CollectingRules)
+        declarationsForOrigin(m_result, declarationOrigin).reserveCapacity(m_matchedRules.size());
 
     for (; m_matchedRuleTransferIndex < m_matchedRules.size(); ++m_matchedRuleTransferIndex) {
         auto& matchedRule = m_matchedRules[m_matchedRuleTransferIndex];
@@ -658,17 +670,7 @@ void ElementRuleCollector::addMatchedProperties(MatchedProperties&& matchedPrope
 
     m_result.isCacheable = computeIsCacheable();
 
-    switch (declarationOrigin) {
-    case DeclarationOrigin::UserAgent:
-        m_result.userAgentDeclarations.append(WTFMove(matchedProperties));
-        break;
-    case DeclarationOrigin::User:
-        m_result.userDeclarations.append(WTFMove(matchedProperties));
-        break;
-    case DeclarationOrigin::Author:
-        m_result.authorDeclarations.append(WTFMove(matchedProperties));
-        break;
-    }
+    declarationsForOrigin(m_result, declarationOrigin).append(WTFMove(matchedProperties));
 }
 
 } // namespace WebCore
index dba8e95..5b68483 100644 (file)
@@ -62,9 +62,18 @@ struct MatchedProperties {
 
 struct MatchResult {
     bool isCacheable { true };
-    Vector<MatchedProperties, 32> userAgentDeclarations;
-    Vector<MatchedProperties, 32> userDeclarations;
-    Vector<MatchedProperties, 32> authorDeclarations;
+    Vector<MatchedProperties> userAgentDeclarations;
+    Vector<MatchedProperties> userDeclarations;
+    Vector<MatchedProperties> authorDeclarations;
+
+    bool operator==(const MatchResult& other) const
+    {
+        return isCacheable == other.isCacheable
+            && userAgentDeclarations == other.userAgentDeclarations
+            && userDeclarations == other.userDeclarations
+            && authorDeclarations == other.authorDeclarations;
+    }
+    bool operator!=(const MatchResult& other) const { return !(*this == other); }
 
     bool isEmpty() const { return userAgentDeclarations.isEmpty() && userDeclarations.isEmpty() && authorDeclarations.isEmpty(); }
 };
@@ -122,6 +131,7 @@ private:
     void sortMatchedRules();
 
     enum class DeclarationOrigin { UserAgent, User, Author };
+    static Vector<MatchedProperties>& declarationsForOrigin(MatchResult&, DeclarationOrigin);
     void sortAndTransferMatchedRules(DeclarationOrigin);
     void transferMatchedRules(DeclarationOrigin, Optional<Style::ScopeOrdinal> forScope = { });
 
index 1ff9eff..6acac33 100644 (file)
@@ -398,7 +398,7 @@ void RuleSet::addChildRules(const Vector<RefPtr<StyleRuleBase>>& rules, const Me
         } else if (is<StyleRuleFontFace>(*rule) && resolver) {
             // Add this font face to our set.
             resolver->document().fontSelector().addFontFaceRule(downcast<StyleRuleFontFace>(*rule.get()), isInitiatingElementInUserAgentShadowTree);
-            resolver->invalidateMatchedPropertiesCache();
+            resolver->invalidateMatchedDeclarationsCache();
         } else if (is<StyleRuleKeyframes>(*rule) && resolver)
             resolver->addKeyframeStyle(downcast<StyleRuleKeyframes>(*rule));
         else if (is<StyleRuleSupports>(*rule) && downcast<StyleRuleSupports>(*rule).conditionIsSupported())
index d0ffd07..6de23a5 100644 (file)
@@ -116,7 +116,6 @@ inline void StyleResolver::State::clear()
 
 StyleResolver::StyleResolver(Document& document)
     : m_ruleSets(*this)
-    , m_matchedPropertiesCacheSweepTimer(*this, &StyleResolver::sweepMatchedPropertiesCache)
     , m_document(document)
 #if ENABLE(CSS_DEVICE_ADAPTATION)
     , m_viewportStyleResolver(ViewportStyleResolver::create(&document))
@@ -196,27 +195,6 @@ StyleResolver::~StyleResolver()
 #endif
 }
 
-void StyleResolver::sweepMatchedPropertiesCache()
-{
-    // Look for cache entries containing a style declaration with a single ref and remove them.
-    // This may happen when an element attribute mutation causes it to generate a new inlineStyle()
-    // or presentationAttributeStyle(), potentially leaving this cache with the last ref on the old one.
-    auto hasOneRef = [](auto& declarations) {
-        for (auto& matchedProperties : declarations) {
-            if (matchedProperties.properties->hasOneRef())
-                return true;
-        }
-        return false;
-    };
-
-    m_matchedPropertiesCache.removeIf([&](auto& keyValue) {
-        auto& entry = keyValue.value;
-        return hasOneRef(entry.userAgentDeclarations) || hasOneRef(entry.userDeclarations) || hasOneRef(entry.authorDeclarations);
-    });
-
-    m_matchedPropertiesCacheAdditionsSinceLastSweep = 0;
-}
-
 StyleResolver::State::State(const Element& element, const RenderStyle* parentStyle, const RenderStyle* documentElementStyle, const SelectorFilter* selectorFilter)
     : m_element(&element)
     , m_parentStyle(parentStyle)
@@ -1121,108 +1099,45 @@ static bool elementTypeHasAppearanceFromUAStyle(const Element& element)
         || localName == HTMLNames::meterTag;
 }
 
-unsigned StyleResolver::computeMatchedPropertiesHash(const MatchResult& matchResult)
-{
-    return StringHasher::hashMemory(matchResult.userAgentDeclarations.data(), sizeof(MatchedProperties) * matchResult.userAgentDeclarations.size())
-        ^ StringHasher::hashMemory(matchResult.userDeclarations.data(), sizeof(MatchedProperties) * matchResult.userDeclarations.size())
-        ^ StringHasher::hashMemory(matchResult.authorDeclarations.data(), sizeof(MatchedProperties) * matchResult.authorDeclarations.size());
-}
-
-const StyleResolver::MatchedPropertiesCacheItem* StyleResolver::findFromMatchedPropertiesCache(unsigned hash, const MatchResult& matchResult)
+void StyleResolver::invalidateMatchedDeclarationsCache()
 {
-    ASSERT(hash);
-
-    MatchedPropertiesCache::iterator it = m_matchedPropertiesCache.find(hash);
-    if (it == m_matchedPropertiesCache.end())
-        return nullptr;
-
-    auto& cacheItem = it->value;
-    if (matchResult.userAgentDeclarations != cacheItem.userAgentDeclarations || matchResult.userDeclarations != cacheItem.userDeclarations || matchResult.authorDeclarations != cacheItem.authorDeclarations)
-        return nullptr;
-
-    return &cacheItem;
+    m_matchedDeclarationsCache.invalidate();
 }
 
-void StyleResolver::addToMatchedPropertiesCache(const RenderStyle* style, const RenderStyle* parentStyle, unsigned hash, const MatchResult& matchResult)
+void StyleResolver::clearCachedDeclarationsAffectedByViewportUnits()
 {
-    static const unsigned matchedDeclarationCacheAdditionsBetweenSweeps = 100;
-    if (++m_matchedPropertiesCacheAdditionsSinceLastSweep >= matchedDeclarationCacheAdditionsBetweenSweeps
-        && !m_matchedPropertiesCacheSweepTimer.isActive()) {
-        static const Seconds matchedDeclarationCacheSweepTime { 1_min };
-        m_matchedPropertiesCacheSweepTimer.startOneShot(matchedDeclarationCacheSweepTime);
-    }
-
-    ASSERT(hash);
-    // Note that we don't cache the original RenderStyle instance. It may be further modified.
-    // The RenderStyle in the cache is really just a holder for the substructures and never used as-is.
-    MatchedPropertiesCacheItem cacheItem(matchResult, style, parentStyle);
-    m_matchedPropertiesCache.add(hash, WTFMove(cacheItem));
+    m_matchedDeclarationsCache.clearEntriesAffectedByViewportUnits();
 }
 
-void StyleResolver::invalidateMatchedPropertiesCache()
-{
-    m_matchedPropertiesCache.clear();
-}
-
-void StyleResolver::clearCachedPropertiesAffectedByViewportUnits()
-{
-    Vector<unsigned, 16> toRemove;
-    for (auto& cacheKeyValue : m_matchedPropertiesCache) {
-        if (cacheKeyValue.value.renderStyle->hasViewportUnits())
-            toRemove.append(cacheKeyValue.key);
-    }
-    for (auto key : toRemove)
-        m_matchedPropertiesCache.remove(key);
-}
-
-static bool isCacheableInMatchedPropertiesCache(const Element& element, const RenderStyle* style, const RenderStyle* parentStyle)
-{
-    // FIXME: Writing mode and direction properties modify state when applying to document element by calling
-    // Document::setWritingMode/DirectionSetOnDocumentElement. We can't skip the applying by caching.
-    if (&element == element.document().documentElement())
-        return false;
-    // content:attr() value depends on the element it is being applied to.
-    if (style->hasAttrContent() || (style->styleType() != PseudoId::None && parentStyle->hasAttrContent()))
-        return false;
-    if (style->hasAppearance())
-        return false;
-    if (style->zoom() != RenderStyle::initialZoom())
-        return false;
-    if (style->writingMode() != RenderStyle::initialWritingMode() || style->direction() != RenderStyle::initialDirection())
-        return false;
-    // The cache assumes static knowledge about which properties are inherited.
-    if (style->hasExplicitlyInheritedProperties())
-        return false;
-    return true;
-}
-
-void StyleResolver::applyMatchedProperties(const MatchResult& matchResult, const Element& element, ShouldUseMatchedPropertiesCache shouldUseMatchedPropertiesCache)
+void StyleResolver::applyMatchedProperties(const MatchResult& matchResult, const Element& element, UseMatchedDeclarationsCache useMatchedDeclarationsCache)
 {
     State& state = m_state;
-    unsigned cacheHash = shouldUseMatchedPropertiesCache && matchResult.isCacheable ? computeMatchedPropertiesHash(matchResult) : 0;
+    unsigned cacheHash = useMatchedDeclarationsCache == UseMatchedDeclarationsCache::Yes ? Style::MatchedDeclarationsCache::computeHash(matchResult) : 0;
     auto includedProperties = Style::PropertyCascade::IncludedProperties::All;
 
-    const MatchedPropertiesCacheItem* cacheItem = nullptr;
-    if (cacheHash && (cacheItem = findFromMatchedPropertiesCache(cacheHash, matchResult))
-        && isCacheableInMatchedPropertiesCache(element, state.style(), state.parentStyle())) {
+    auto& style = *state.style();
+    auto& parentStyle = *state.parentStyle();
+
+    auto* cacheEntry = m_matchedDeclarationsCache.find(cacheHash, matchResult);
+    if (cacheEntry && Style::MatchedDeclarationsCache::isCacheable(element, style, parentStyle)) {
         // We can build up the style by copying non-inherited properties from an earlier style object built using the same exact
         // style declarations. We then only need to apply the inherited properties, if any, as their values can depend on the 
         // element context. This is fast and saves memory by reusing the style data structures.
-        state.style()->copyNonInheritedFrom(*cacheItem->renderStyle);
-        if (state.parentStyle()->inheritedDataShared(cacheItem->parentRenderStyle.get()) && !isAtShadowBoundary(element)) {
+        style.copyNonInheritedFrom(*cacheEntry->renderStyle);
+        if (parentStyle.inheritedDataShared(cacheEntry->parentRenderStyle.get()) && !isAtShadowBoundary(element)) {
             InsideLink linkStatus = state.style()->insideLink();
             // If the cache item parent style has identical inherited properties to the current parent style then the
             // resulting style will be identical too. We copy the inherited properties over from the cache and are done.
-            state.style()->inheritFrom(*cacheItem->renderStyle);
+            style.inheritFrom(*cacheEntry->renderStyle);
 
             // Unfortunately the link status is treated like an inherited property. We need to explicitly restore it.
-            state.style()->setInsideLink(linkStatus);
+            style.setInsideLink(linkStatus);
             return;
         }
         includedProperties = Style::PropertyCascade::IncludedProperties::InheritedOnly;
     }
 
-    if (elementTypeHasAppearanceFromUAStyle(*state.element())) {
+    if (elementTypeHasAppearanceFromUAStyle(element)) {
         // FIXME: This is such a hack.
         // Find out if there's a -webkit-appearance property in effect from the UA sheet.
         // If so, we cache the border and background styles so that RenderTheme::adjustStyle()
@@ -1239,23 +1154,23 @@ void StyleResolver::applyMatchedProperties(const MatchResult& matchResult, const
     builder.applyHighPriorityProperties();
 
     // If the effective zoom value changes, we can't use the matched properties cache. Start over.
-    if (cacheItem && cacheItem->renderStyle->effectiveZoom() != state.style()->effectiveZoom())
-        return applyMatchedProperties(matchResult, element, DoNotUseMatchedPropertiesCache);
+    if (cacheEntry && cacheEntry->renderStyle->effectiveZoom() != style.effectiveZoom())
+        return applyMatchedProperties(matchResult, element, UseMatchedDeclarationsCache::No);
 
     // If the font changed, we can't use the matched properties cache. Start over.
-    if (cacheItem && cacheItem->renderStyle->fontDescription() != state.style()->fontDescription())
-        return applyMatchedProperties(matchResult, element, DoNotUseMatchedPropertiesCache);
+    if (cacheEntry && cacheEntry->renderStyle->fontDescription() != style.fontDescription())
+        return applyMatchedProperties(matchResult, element, UseMatchedDeclarationsCache::No);
 
     builder.applyLowPriorityProperties();
 
     for (auto& contentAttribute : builder.state().registeredContentAttributes())
         ruleSets().mutableFeatures().registerContentAttribute(contentAttribute);
 
-    if (cacheItem || !cacheHash)
+    if (cacheEntry || !cacheHash)
         return;
-    if (!isCacheableInMatchedPropertiesCache(*state.element(), state.style(), state.parentStyle()))
-        return;
-    addToMatchedPropertiesCache(state.style(), state.parentStyle(), cacheHash, matchResult);
+
+    if (Style::MatchedDeclarationsCache::isCacheable(element, style, parentStyle))
+        m_matchedDeclarationsCache.add(style, parentStyle, cacheHash, matchResult);
 }
 
 void StyleResolver::applyPropertyToStyle(CSSPropertyID id, CSSValue* value, std::unique_ptr<RenderStyle> style)
index ddc5127..9c8205a 100644 (file)
 #include "DocumentRuleSets.h"
 #include "ElementRuleCollector.h"
 #include "InspectorCSSOMWrappers.h"
+#include "MatchedDeclarationsCache.h"
 #include "MediaQueryEvaluator.h"
 #include "RenderStyle.h"
 #include "RuleSet.h"
-#include "SelectorChecker.h"
-#include <bitset>
 #include <memory>
-#include <wtf/Bitmap.h>
 #include <wtf/HashMap.h>
 #include <wtf/RefPtr.h>
 #include <wtf/Vector.h>
@@ -181,16 +179,15 @@ public:
     bool usesFirstLineRules() const { return m_ruleSets.features().usesFirstLineRules; }
     bool usesFirstLetterRules() const { return m_ruleSets.features().usesFirstLetterRules; }
     
-    void invalidateMatchedPropertiesCache();
-
-    void clearCachedPropertiesAffectedByViewportUnits();
+    void invalidateMatchedDeclarationsCache();
+    void clearCachedDeclarationsAffectedByViewportUnits();
 
 private:
     void adjustRenderStyle(RenderStyle&, const RenderStyle& parentStyle, const RenderStyle* parentBoxStyle, const Element*);
     void adjustRenderStyleForSiteSpecificQuirks(RenderStyle&, const Element&);
-        
-    enum ShouldUseMatchedPropertiesCache { DoNotUseMatchedPropertiesCache = 0, UseMatchedPropertiesCache };
-    void applyMatchedProperties(const MatchResult&, const Element&, ShouldUseMatchedPropertiesCache = UseMatchedPropertiesCache);
+
+    enum class UseMatchedDeclarationsCache { Yes, No };
+    void applyMatchedProperties(const MatchResult&, const Element&, UseMatchedDeclarationsCache = UseMatchedDeclarationsCache::Yes);
 
     DocumentRuleSets m_ruleSets;
 
@@ -252,37 +249,6 @@ public:
 private:
     void cacheBorderAndBackground();
 
-    static unsigned computeMatchedPropertiesHash(const MatchResult&);
-    struct MatchedPropertiesCacheItem {
-        Vector<MatchedProperties> userAgentDeclarations;
-        Vector<MatchedProperties> userDeclarations;
-        Vector<MatchedProperties> authorDeclarations;
-        std::unique_ptr<RenderStyle> renderStyle;
-        std::unique_ptr<RenderStyle> parentRenderStyle;
-        
-        MatchedPropertiesCacheItem(const MatchResult& matchResult, const RenderStyle* style, const RenderStyle* parentStyle)
-            : renderStyle(RenderStyle::clonePtr(*style))
-            , parentRenderStyle(RenderStyle::clonePtr(*parentStyle))
-        {
-            // Assign rather than copy-construct so we only allocate as much vector capacity as needed.
-            userAgentDeclarations = matchResult.userAgentDeclarations;
-            userDeclarations = matchResult.userDeclarations;
-            authorDeclarations = matchResult.authorDeclarations;
-        }
-        MatchedPropertiesCacheItem() = default;
-    };
-    const MatchedPropertiesCacheItem* findFromMatchedPropertiesCache(unsigned hash, const MatchResult&);
-    void addToMatchedPropertiesCache(const RenderStyle*, const RenderStyle* parentStyle, unsigned hash, const MatchResult&);
-
-    // Every N additions to the matched declaration cache trigger a sweep where entries holding
-    // the last reference to a style declaration are garbage collected.
-    void sweepMatchedPropertiesCache();
-
-    typedef HashMap<unsigned, MatchedPropertiesCacheItem> MatchedPropertiesCache;
-    MatchedPropertiesCache m_matchedPropertiesCache;
-
-    Timer m_matchedPropertiesCacheSweepTimer;
-
     MediaQueryEvaluator m_mediaQueryEvaluator;
     std::unique_ptr<RenderStyle> m_rootDefaultStyle;
 
@@ -302,7 +268,7 @@ private:
 
     State m_state;
 
-    unsigned m_matchedPropertiesCacheAdditionsSinceLastSweep { 0 };
+    Style::MatchedDeclarationsCache m_matchedDeclarationsCache;
 
     bool m_matchAuthorAndUserStyles { true };
     // See if we still have crashes where StyleResolver gets deleted early.
index cf54c44..a4032ac 100644 (file)
@@ -2290,7 +2290,7 @@ void Document::fontsNeedUpdate(FontSelector&)
 void Document::invalidateMatchedPropertiesCacheAndForceStyleRecalc()
 {
     if (auto* resolver = styleScope().resolverIfExists())
-        resolver->invalidateMatchedPropertiesCache();
+        resolver->invalidateMatchedDeclarationsCache();
     if (backForwardCacheState() != NotInBackForwardCache || !renderView())
         return;
     scheduleFullStyleRebuild();
@@ -3962,7 +3962,7 @@ void Document::updateViewportUnitsOnResize()
     if (!hasStyleWithViewportUnits())
         return;
 
-    styleScope().resolver().clearCachedPropertiesAffectedByViewportUnits();
+    styleScope().resolver().clearCachedDeclarationsAffectedByViewportUnits();
 
     // FIXME: Ideally, we should save the list of elements that have viewport units and only iterate over those.
     for (Element* element = ElementTraversal::firstWithin(rootNode()); element; element = ElementTraversal::nextIncludingPseudo(*element)) {
index bdb54eb..38701dd 100644 (file)
@@ -567,7 +567,7 @@ void Page::updateStyleAfterChangeInEnvironment()
 {
     forEachDocument([](Document& document) {
         if (StyleResolver* styleResolver = document.styleScope().resolverIfExists())
-            styleResolver->invalidateMatchedPropertiesCache();
+            styleResolver->invalidateMatchedDeclarationsCache();
         document.scheduleFullStyleRebuild();
         document.styleScope().didChangeStyleSheetEnvironment();
         document.scheduleTimedRenderingUpdate();
diff --git a/Source/WebCore/style/MatchedDeclarationsCache.cpp b/Source/WebCore/style/MatchedDeclarationsCache.cpp
new file mode 100644 (file)
index 0000000..639d04c
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
+ * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.com)
+ * Copyright (C) 2005-2019 Apple Inc. All rights reserved.
+ * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org>
+ * Copyright (C) 2007, 2008 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (C) Research In Motion Limited 2011. All rights reserved.
+ * Copyright (C) 2012, 2013 Google Inc. All rights reserved.
+ * Copyright (C) 2014 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "MatchedDeclarationsCache.h"
+
+#include <wtf/text/StringHash.h>
+
+namespace WebCore {
+namespace Style {
+
+MatchedDeclarationsCache::MatchedDeclarationsCache()
+    : m_sweepTimer(*this, &MatchedDeclarationsCache::sweep)
+{
+}
+
+MatchedDeclarationsCache::~MatchedDeclarationsCache() = default;
+
+bool MatchedDeclarationsCache::isCacheable(const Element& element, const RenderStyle& style, const RenderStyle& parentStyle)
+{
+    // FIXME: Writing mode and direction properties modify state when applying to document element by calling
+    // Document::setWritingMode/DirectionSetOnDocumentElement. We can't skip the applying by caching.
+    if (&element == element.document().documentElement())
+        return false;
+    // content:attr() value depends on the element it is being applied to.
+    if (style.hasAttrContent() || (style.styleType() != PseudoId::None && parentStyle.hasAttrContent()))
+        return false;
+    if (style.hasAppearance())
+        return false;
+    if (style.zoom() != RenderStyle::initialZoom())
+        return false;
+    if (style.writingMode() != RenderStyle::initialWritingMode() || style.direction() != RenderStyle::initialDirection())
+        return false;
+    // The cache assumes static knowledge about which properties are inherited.
+    if (style.hasExplicitlyInheritedProperties())
+        return false;
+
+    return true;
+}
+
+unsigned MatchedDeclarationsCache::computeHash(const MatchResult& matchResult)
+{
+    if (!matchResult.isCacheable)
+        return 0;
+
+    return StringHasher::hashMemory(matchResult.userAgentDeclarations.data(), sizeof(MatchedProperties) * matchResult.userAgentDeclarations.size())
+        ^ StringHasher::hashMemory(matchResult.userDeclarations.data(), sizeof(MatchedProperties) * matchResult.userDeclarations.size())
+        ^ StringHasher::hashMemory(matchResult.authorDeclarations.data(), sizeof(MatchedProperties) * matchResult.authorDeclarations.size());
+}
+
+const MatchedDeclarationsCache::Entry* MatchedDeclarationsCache::find(unsigned hash, const MatchResult& matchResult)
+{
+    if (!hash)
+        return nullptr;
+
+    auto it = m_entries.find(hash);
+    if (it == m_entries.end())
+        return nullptr;
+
+    auto& entry = it->value;
+    if (matchResult != entry.matchResult)
+        return nullptr;
+
+    return &entry;
+}
+
+void MatchedDeclarationsCache::add(const RenderStyle& style, const RenderStyle& parentStyle, unsigned hash, const MatchResult& matchResult)
+{
+    constexpr unsigned additionsBetweenSweeps = 100;
+    if (++m_additionsSinceLastSweep >= additionsBetweenSweeps && !m_sweepTimer.isActive()) {
+        constexpr auto sweepDelay = 1_min;
+        m_sweepTimer.startOneShot(sweepDelay);
+    }
+
+    ASSERT(hash);
+    // Note that we don't cache the original RenderStyle instance. It may be further modified.
+    // The RenderStyle in the cache is really just a holder for the substructures and never used as-is.
+    m_entries.add(hash, Entry { matchResult, RenderStyle::clonePtr(style), RenderStyle::clonePtr(parentStyle) });
+}
+
+void MatchedDeclarationsCache::invalidate()
+{
+    m_entries.clear();
+}
+
+void MatchedDeclarationsCache::clearEntriesAffectedByViewportUnits()
+{
+    m_entries.removeIf([](auto& keyValue) {
+        return keyValue.value.renderStyle->hasViewportUnits();
+    });
+}
+
+void MatchedDeclarationsCache::sweep()
+{
+    // Look for cache entries containing a style declaration with a single ref and remove them.
+    // This may happen when an element attribute mutation causes it to generate a new inlineStyle()
+    // or presentationAttributeStyle(), potentially leaving this cache with the last ref on the old one.
+    auto hasOneRef = [](auto& declarations) {
+        for (auto& matchedProperties : declarations) {
+            if (matchedProperties.properties->hasOneRef())
+                return true;
+        }
+        return false;
+    };
+
+    m_entries.removeIf([&](auto& keyValue) {
+        auto& matchResult = keyValue.value.matchResult;
+        return hasOneRef(matchResult.userAgentDeclarations) || hasOneRef(matchResult.userDeclarations) || hasOneRef(matchResult.authorDeclarations);
+    });
+
+    m_additionsSinceLastSweep = 0;
+}
+
+}
+}
diff --git a/Source/WebCore/style/MatchedDeclarationsCache.h b/Source/WebCore/style/MatchedDeclarationsCache.h
new file mode 100644 (file)
index 0000000..365fa01
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    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 INC. AND ITS CONTRIBUTORS ``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 INC. OR ITS 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 PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "ElementRuleCollector.h"
+#include "RenderStyle.h"
+#include "Timer.h"
+
+namespace WebCore {
+
+namespace Style {
+
+class MatchedDeclarationsCache {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    MatchedDeclarationsCache();
+    ~MatchedDeclarationsCache();
+
+    static bool isCacheable(const Element&, const RenderStyle&, const RenderStyle& parentStyle);
+    static unsigned computeHash(const MatchResult&);
+
+    struct Entry {
+        MatchResult matchResult;
+        std::unique_ptr<const RenderStyle> renderStyle;
+        std::unique_ptr<const RenderStyle> parentRenderStyle;
+    };
+
+    const Entry* find(unsigned hash, const MatchResult&);
+    void add(const RenderStyle&, const RenderStyle& parentStyle, unsigned hash, const MatchResult&);
+
+    // Every N additions to the matched declaration cache trigger a sweep where entries holding
+    // the last reference to a style declaration are garbage collected.
+    void invalidate();
+    void clearEntriesAffectedByViewportUnits();
+
+private:
+    void sweep();
+
+    HashMap<unsigned, Entry> m_entries;
+    Timer m_sweepTimer;
+    unsigned m_additionsSinceLastSweep { 0 };
+};
+
+}
+}
index bbeff87..44ef943 100644 (file)
@@ -217,7 +217,7 @@ ElementUpdates TreeResolver::resolveElement(Element& element)
         if (update.change != NoChange && existingStyle && existingStyle->computedFontPixelSize() != update.style->computedFontPixelSize()) {
             // "rem" units are relative to the document element's font size so we need to recompute everything.
             // In practice this is rare.
-            scope().styleResolver.invalidateMatchedPropertiesCache();
+            scope().styleResolver.invalidateMatchedDeclarationsCache();
             descendantsToResolve = DescendantsToResolve::All;
         }
     }