Skip to content

Commit

Permalink
Factor inline stylesheet cache into a class
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=267996
rdar://problem/121514266

Reviewed by Ryosuke Niwa.

Add StyleSheetContentsCache and use it.

* Source/WebCore/Sources.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/css/CSSStyleSheet.cpp:
(WebCore::CSSStyleSheet::CSSStyleSheet):
(WebCore::CSSStyleSheet::replaceSync):
* Source/WebCore/css/StyleSheetContents.h:
* Source/WebCore/dom/InlineStyleSheetOwner.cpp:
(WebCore::makeStyleSheetContentsCacheKey):
(WebCore::InlineStyleSheetOwner::createSheet):
(WebCore::inlineStyleSheetCache): Deleted.
(WebCore::makeInlineStyleSheetCacheKey): Deleted.
(WebCore::InlineStyleSheetOwner::clearCache): Deleted.
* Source/WebCore/dom/InlineStyleSheetOwner.h:
* Source/WebCore/page/MemoryRelease.cpp:
(WebCore::releaseNoncriticalMemory):
* Source/WebCore/style/StyleSheetContentsCache.cpp: Added.
(WebCore::Style::StyleSheetContentsCache::singleton):
(WebCore::Style::StyleSheetContentsCache::get):
(WebCore::Style::StyleSheetContentsCache::add):
(WebCore::Style::StyleSheetContentsCache::clear):
* Source/WebCore/style/StyleSheetContentsCache.h: Added.

Canonical link: https://commits.webkit.org/273437@main
  • Loading branch information
anttijk committed Jan 24, 2024
1 parent 65fb3ec commit e5df402
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 33 deletions.
1 change: 1 addition & 0 deletions Source/WebCore/Sources.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2888,6 +2888,7 @@ style/StyleResolver.cpp
style/StyleScope.cpp
style/StyleScopeRuleSets.cpp
style/StyleSharingResolver.cpp
style/StyleSheetContentsCache.cpp
style/StyleTreeResolver.cpp
style/StyleUpdate.cpp
style/Styleable.cpp
Expand Down
6 changes: 6 additions & 0 deletions Source/WebCore/WebCore.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -6020,6 +6020,7 @@
E46A2B1C17CA65B9000DBCD8 /* TypedElementDescendantIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = E46A2B1B17CA65B9000DBCD8 /* TypedElementDescendantIterator.h */; settings = {ATTRIBUTES = (Private, ); }; };
E46A2B1E17CA76B1000DBCD8 /* ElementChildIterator.h in Headers */ = {isa = PBXBuildFile; fileRef = E46A2B1D17CA76B1000DBCD8 /* ElementChildIterator.h */; settings = {ATTRIBUTES = (Private, ); }; };
E46B41F91CB24E70008F11DE /* ScriptDisallowedScope.h in Headers */ = {isa = PBXBuildFile; fileRef = E46B41F81CB24E70008F11DE /* ScriptDisallowedScope.h */; settings = {ATTRIBUTES = (Private, ); }; };
E46B72512B61141D00A1F9B5 /* StyleSheetContentsCache.h in Headers */ = {isa = PBXBuildFile; fileRef = E46B724F2B61141D00A1F9B5 /* StyleSheetContentsCache.h */; };
E46C794B1F13E82B00F371E1 /* StyleInvalidationFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = E4A1822F1F13BE5800FEF698 /* StyleInvalidationFunctions.h */; };
E47127CB163438AE00ED6F5A /* StyleInvalidator.h in Headers */ = {isa = PBXBuildFile; fileRef = E47A97CF163059FC005DCD99 /* StyleInvalidator.h */; };
E476EF4A27E07A410056AC6E /* CSSContainerRule.h in Headers */ = {isa = PBXBuildFile; fileRef = E476EF4827E07A1A0056AC6E /* CSSContainerRule.h */; };
Expand Down Expand Up @@ -19583,6 +19584,8 @@
E46A2B1B17CA65B9000DBCD8 /* TypedElementDescendantIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TypedElementDescendantIterator.h; sourceTree = "<group>"; };
E46A2B1D17CA76B1000DBCD8 /* ElementChildIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ElementChildIterator.h; sourceTree = "<group>"; };
E46B41F81CB24E70008F11DE /* ScriptDisallowedScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScriptDisallowedScope.h; sourceTree = "<group>"; };
E46B724E2B61141D00A1F9B5 /* StyleSheetContentsCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StyleSheetContentsCache.cpp; sourceTree = "<group>"; };
E46B724F2B61141D00A1F9B5 /* StyleSheetContentsCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StyleSheetContentsCache.h; sourceTree = "<group>"; };
E476EF4727E07A020056AC6E /* CSSContainerRule.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = CSSContainerRule.idl; sourceTree = "<group>"; };
E476EF4827E07A1A0056AC6E /* CSSContainerRule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CSSContainerRule.h; sourceTree = "<group>"; };
E476EF4927E07A280056AC6E /* CSSContainerRule.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CSSContainerRule.cpp; sourceTree = "<group>"; };
Expand Down Expand Up @@ -35211,6 +35214,8 @@
4A4F48A816B0DFC000EDBB29 /* StyleScopeRuleSets.h */,
E47A3AC21C5EABBE00CCBFA7 /* StyleSharingResolver.cpp */,
E47A3AC41C5EAC7900CCBFA7 /* StyleSharingResolver.h */,
E46B724E2B61141D00A1F9B5 /* StyleSheetContentsCache.cpp */,
E46B724F2B61141D00A1F9B5 /* StyleSheetContentsCache.h */,
E4DEAA1517A93DC3000E0430 /* StyleTreeResolver.cpp */,
E4DEAA1617A93DC3000E0430 /* StyleTreeResolver.h */,
E42E76D91C7AF76C00E3614D /* StyleUpdate.cpp */,
Expand Down Expand Up @@ -41904,6 +41909,7 @@
E47A3AC61C5EAC9D00CCBFA7 /* StyleSharingResolver.h in Headers */,
A8EA800C0A19516E00A8EF5F /* StyleSheet.h in Headers */,
E4F9EEF3156DA00700D23E7E /* StyleSheetContents.h in Headers */,
E46B72512B61141D00A1F9B5 /* StyleSheetContentsCache.h in Headers */,
A8EA800A0A19516E00A8EF5F /* StyleSheetList.h in Headers */,
BC5EB5E50E81BF6D00B25965 /* StyleSurroundData.h in Headers */,
DD1C779029354C77003BD79B /* StyleTextBoxEdge.h in Headers */,
Expand Down
25 changes: 25 additions & 0 deletions Source/WebCore/css/CSSStyleSheet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "StyleRule.h"
#include "StyleScope.h"
#include "StyleSheetContents.h"
#include "StyleSheetContentsCache.h"

#include <wtf/HexNumber.h>
#include <wtf/text/StringBuilder.h>
Expand Down Expand Up @@ -151,6 +152,7 @@ CSSStyleSheet::CSSStyleSheet(Ref<StyleSheetContents>&& contents, Document& docum
, m_constructorDocument(document)
{
m_contents->registerClient(this);
m_contents->checkLoaded();

WTF::switchOn(WTFMove(options.media), [this](RefPtr<MediaList>&& mediaList) {
if (auto queries = mediaList->mediaQueries(); !queries.isEmpty())
Expand Down Expand Up @@ -523,6 +525,22 @@ ExceptionOr<void> CSSStyleSheet::replaceSync(String&& text)
if (!m_wasConstructedByJS)
return Exception { ExceptionCode::NotAllowedError, "This CSSStyleSheet object was not constructed by JavaScript"_s };

// Try to use the cache in the case where contents is replaced before the stylesheet is attached to the document.
if (isDetached() && m_childRuleCSSOMWrappers.isEmpty()) {
auto key = Style::StyleSheetContentsCache::Key { text, m_contents->parserContext() };
auto cachedContents = Style::StyleSheetContentsCache::singleton().get(key);
if (cachedContents) {
m_contents->unregisterClient(this);
m_contents = *cachedContents;
m_contents->registerClient(this);
} else {
m_contents->parseString(WTFMove(text));
if (m_contents->isCacheable())
Style::StyleSheetContentsCache::singleton().add(WTFMove(key), m_contents);
}
return { };
}

RuleMutationScope mutationScope(this, RuleReplace);
m_contents->clearRules();
for (auto& childRuleWrapper : m_childRuleCSSOMWrappers)
Expand All @@ -534,6 +552,13 @@ ExceptionOr<void> CSSStyleSheet::replaceSync(String&& text)
return { };
}

bool CSSStyleSheet::isDetached() const
{
return !m_ownerNode
&& !m_ownerRule
&& m_adoptingTreeScopes.isEmptyIgnoringNullReferences();
}

Document* CSSStyleSheet::constructorDocument() const
{
return m_constructorDocument.get();
Expand Down
2 changes: 2 additions & 0 deletions Source/WebCore/css/CSSStyleSheet.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ class CSSStyleSheet final : public StyleSheet, public CanMakeSingleThreadWeakPtr
String cssTextWithReplacementURLs(const HashMap<String, String>&, const HashMap<RefPtr<CSSStyleSheet>, String>&);
void getChildStyleSheets(HashSet<RefPtr<CSSStyleSheet>>&);

bool isDetached() const;

private:
CSSStyleSheet(Ref<StyleSheetContents>&&, CSSImportRule* ownerRule);
CSSStyleSheet(Ref<StyleSheetContents>&&, Node* ownerNode, const TextPosition& startPosition, bool isInlineStylesheet);
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/css/StyleSheetContents.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ class StyleSheetContents final : public RefCounted<StyleSheetContents>, public C
String originalURL() const { return m_originalURL; }
const URL& baseURL() const { return m_parserContext.baseURL; }

bool isEmpty() const { return !ruleCount(); }
unsigned ruleCount() const;
StyleRuleBase* ruleAt(unsigned index) const;

Expand Down
37 changes: 7 additions & 30 deletions Source/WebCore/dom/InlineStyleSheetOwner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,13 @@
#include "ShadowRoot.h"
#include "StyleScope.h"
#include "StyleSheetContents.h"
#include "StyleSheetContentsCache.h"
#include "TextNodeTraversal.h"
#include <wtf/HashMap.h>
#include <wtf/NeverDestroyed.h>

namespace WebCore {

using InlineStyleSheetCacheKey = std::pair<String, CSSParserContext>;
using InlineStyleSheetCache = HashMap<InlineStyleSheetCacheKey, RefPtr<StyleSheetContents>>;

static InlineStyleSheetCache& inlineStyleSheetCache()
{
static NeverDestroyed<InlineStyleSheetCache> cache;
return cache;
}

static CSSParserContext parserContextForElement(const Element& element)
{
auto* shadowRoot = element.containingShadowRoot();
Expand All @@ -59,14 +51,14 @@ static CSSParserContext parserContextForElement(const Element& element)
return result;
}

static std::optional<InlineStyleSheetCacheKey> makeInlineStyleSheetCacheKey(const String& text, const Element& element)
static std::optional<Style::StyleSheetContentsCache::Key> makeStyleSheetContentsCacheKey(const String& text, const Element& element)
{
// Only cache for shadow trees. Main document inline stylesheets are generally unique and can't be shared between documents.
// FIXME: This could be relaxed when a stylesheet does not contain document-relative URLs (or #urls).
if (!element.isInShadowTree())
return { };

return std::make_pair(text, parserContextForElement(element));
return Style::StyleSheetContentsCache::Key { text, parserContextForElement(element) };
}

InlineStyleSheetOwner::InlineStyleSheetOwner(Document& document, bool createdByParser)
Expand Down Expand Up @@ -174,9 +166,9 @@ void InlineStyleSheetOwner::createSheet(Element& element, const String& text)
if (CheckedPtr scope = m_styleScope.get())
scope->addPendingSheet(element);

auto cacheKey = makeInlineStyleSheetCacheKey(text, element);
auto cacheKey = makeStyleSheetContentsCacheKey(text, element);
if (cacheKey) {
if (RefPtr cachedSheet = inlineStyleSheetCache().get(*cacheKey)) {
if (RefPtr cachedSheet = Style::StyleSheetContentsCache::singleton().get(*cacheKey)) {
ASSERT(cachedSheet->isCacheable());
Ref sheet = CSSStyleSheet::createInline(*cachedSheet, element, m_startTextPosition);
m_sheet = sheet.copyRef();
Expand Down Expand Up @@ -206,18 +198,8 @@ void InlineStyleSheetOwner::createSheet(Element& element, const String& text)

contents->checkLoaded();

if (cacheKey && contents->isCacheable()) {
sheet->contents().addedToMemoryCache();
inlineStyleSheetCache().add(*cacheKey, &sheet->contents());

// Prevent pathological growth.
static constexpr auto maximumInlineStyleSheetCacheSize = 256;
if (inlineStyleSheetCache().size() > maximumInlineStyleSheetCacheSize) {
auto toRemove = inlineStyleSheetCache().random();
toRemove->value->removedFromMemoryCache();
inlineStyleSheetCache().remove(toRemove);
}
}
if (cacheKey && contents->isCacheable())
Style::StyleSheetContentsCache::singleton().add(WTFMove(*cacheKey), contents);
}

bool InlineStyleSheetOwner::isLoading() const
Expand All @@ -244,9 +226,4 @@ void InlineStyleSheetOwner::startLoadingDynamicSheet(Element& element)
scope->addPendingSheet(element);
}

void InlineStyleSheetOwner::clearCache()
{
inlineStyleSheetCache().clear();
}

}
2 changes: 0 additions & 2 deletions Source/WebCore/dom/InlineStyleSheetOwner.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ class InlineStyleSheetOwner {

Style::Scope* styleScope() { return m_styleScope.get(); }

static void clearCache();

private:
void createSheet(Element&, const String& text);
void createSheetFromTextContents(Element&);
Expand Down
3 changes: 2 additions & 1 deletion Source/WebCore/page/MemoryRelease.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
#include "ScrollingThread.h"
#include "SelectorQuery.h"
#include "StyleScope.h"
#include "StyleSheetContentsCache.h"
#include "StyledElement.h"
#include "TextPainter.h"
#include "WorkerGlobalScope.h"
Expand Down Expand Up @@ -89,7 +90,7 @@ static void releaseNoncriticalMemory(MaintainMemoryCache maintainMemoryCache)
if (maintainMemoryCache == MaintainMemoryCache::No)
MemoryCache::singleton().pruneDeadResourcesToSize(0);

InlineStyleSheetOwner::clearCache();
Style::StyleSheetContentsCache::singleton().clear();
HTMLNameCache::clear();
ImmutableStyleProperties::clearDeduplicationMap();
SVGPathElement::clearCache();
Expand Down
70 changes: 70 additions & 0 deletions Source/WebCore/style/StyleSheetContentsCache.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (C) 2024 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 "config.h"
#include "StyleSheetContentsCache.h"

#include "StyleSheetContents.h"

namespace WebCore {
namespace Style {

StyleSheetContentsCache::StyleSheetContentsCache() = default;

StyleSheetContentsCache& StyleSheetContentsCache::singleton()
{
static NeverDestroyed<StyleSheetContentsCache> cache;
return cache.get();
}

RefPtr<StyleSheetContents> StyleSheetContentsCache::get(const Key& key)
{
return m_cache.get(key);
}

void StyleSheetContentsCache::add(Key&& key, Ref<StyleSheetContents> contents)
{
ASSERT(contents->isCacheable());

m_cache.add(WTFMove(key), contents);
contents->addedToMemoryCache();

static constexpr auto maximumCacheSize = 256;
if (m_cache.size() > maximumCacheSize) {
auto toRemove = m_cache.random();
toRemove->value->removedFromMemoryCache();
m_cache.remove(toRemove);
}
}

void StyleSheetContentsCache::clear()
{
m_cache.clear();
}

}
}
58 changes: 58 additions & 0 deletions Source/WebCore/style/StyleSheetContentsCache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2024 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 "CSSParserContext.h"
#include <wtf/HashMap.h>
#include <wtf/text/WTFString.h>

namespace WebCore {

class StyleSheetContents;

namespace Style {

class StyleSheetContentsCache {
public:
static StyleSheetContentsCache& singleton();

using Key = std::pair<String, CSSParserContext>;

RefPtr<StyleSheetContents> get(const Key&);
void add(Key&&, Ref<StyleSheetContents>);

void clear();

private:
friend class NeverDestroyed<StyleSheetContentsCache>;

StyleSheetContentsCache();

HashMap<Key, Ref<StyleSheetContents>> m_cache;
};

}
}

0 comments on commit e5df402

Please sign in to comment.