Changeset 287091 in webkit
- Timestamp:
- Dec 15, 2021 11:45:12 AM (7 months ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 1 added
- 7 edited
- 1 copied
-
ChangeLog (modified) (1 diff)
-
Sources.txt (modified) (1 diff)
-
WebCore.xcodeproj/project.pbxproj (modified) (4 diffs)
-
css/SelectorChecker.cpp (modified) (7 diffs)
-
css/SelectorFilter.cpp (modified) (4 diffs)
-
css/SelectorFilter.h (modified) (1 diff)
-
style/HasSelectorFilter.cpp (added)
-
style/HasSelectorFilter.h (copied) (copied from trunk/Source/WebCore/style/SelectorMatchingState.h) (1 diff)
-
style/SelectorMatchingState.h (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r287089 r287091 1 2021-12-15 Antti Koivisto <antti@apple.com> 2 3 [:has() pseudo-class] Use Bloom filter to quickly reject :has() selectors 4 https://bugs.webkit.org/show_bug.cgi?id=234341 5 6 Reviewed by Dean Jackson. 7 8 We can dramatically speed up cases where there are many :has rules, most of which don't match their argument 9 by building a Bloom filter describing the features of the subtree. 10 11 * Sources.txt: 12 * WebCore.xcodeproj/project.pbxproj: 13 * css/SelectorChecker.cpp: 14 (WebCore::SelectorChecker::matchHasPseudoClass const): 15 16 Build and cache HasSelectorFilter per element/filter type. It will be constructed only if multiple :has() 17 selectors are tested for the same element (otherwise the regular match cache is more efficient). 18 Use it to quickly reject selectors. 19 Also add a basic inital optimization to bail out if there are no child/sibling elements that could match. 20 21 * css/SelectorFilter.cpp: 22 (WebCore::SelectorFilter::collectElementIdentifierHashes): 23 (WebCore::SelectorFilter::collectSimpleSelectorHash): 24 (WebCore::SelectorFilter::collectSelectorHashes): 25 (WebCore::SelectorFilter::chooseSelectorHashesForFilter): 26 (WebCore::collectElementIdentifierHashes): Deleted. 27 (WebCore::collectSimpleSelectorHash): Deleted. 28 (WebCore::collectSelectorHashes): Deleted. 29 (WebCore::chooseSelectorHashesForFilter): Deleted. 30 * css/SelectorFilter.h: 31 * style/HasSelectorFilter.cpp: Added. 32 (WebCore::Style::HasSelectorFilter::HasSelectorFilter): 33 (WebCore::Style::HasSelectorFilter::typeForMatchElement): 34 (WebCore::Style::HasSelectorFilter::makeKey): 35 36 The key consists of the most specific string in the rightmost compound of the selector along with 37 :hover pseudo class, if any. 38 39 (WebCore::Style::HasSelectorFilter::add): 40 41 Add an Element to the filter. 42 The features collected are the same as for the regular selector filter, plus permutations with 43 :hover pseudo-class if it would match. 44 45 * style/HasSelectorFilter.h: Copied from Source/WebCore/style/SelectorMatchingState.h. 46 (WebCore::Style::HasSelectorFilter::type const): 47 (WebCore::Style::HasSelectorFilter::reject const): 48 49 Add HasSelectorFilter which uses non-counting BloomFilter internally. The size of the filter is 512 bytes. 50 51 * style/SelectorMatchingState.h: 52 (WebCore::Style::makeHasPseudoClassSelectorFilterKey): 53 1 54 2021-12-15 Alexey Shvayka <ashvayka@apple.com> 2 55 -
trunk/Source/WebCore/Sources.txt
r287015 r287091 2544 2544 style/ClassChangeInvalidation.cpp 2545 2545 style/ElementRuleCollector.cpp 2546 style/HasSelectorFilter.cpp 2546 2547 style/IdChangeInvalidation.cpp 2547 2548 style/InlineTextBoxStyle.cpp -
trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj
r287079 r287091 5385 5385 E4E8B4F5216B956500B8834D /* FontCascadeDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = E4E8B4F2216B8B6000B8834D /* FontCascadeDescription.h */; settings = {ATTRIBUTES = (Private, ); }; }; 5386 5386 E4E94D6122FF158A00DD191F /* LegacyLineLayout.h in Headers */ = {isa = PBXBuildFile; fileRef = E4A1AC7822FAFD500017B75B /* LegacyLineLayout.h */; settings = {ATTRIBUTES = (Private, ); }; }; 5387 E4ED3ECC2768A51D00F17AC8 /* HasSelectorFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = E4ED3ECA2768A51C00F17AC8 /* HasSelectorFilter.h */; }; 5387 5388 E4F0BE3125712F6E009E7431 /* CaretRectComputation.h in Headers */ = {isa = PBXBuildFile; fileRef = E4F0BE2E25710A75009E7431 /* CaretRectComputation.h */; }; 5388 5389 E4F38D1B2626F13B007B1064 /* DefaultResourceLoadPriority.h in Headers */ = {isa = PBXBuildFile; fileRef = E4F38D192626F13B007B1064 /* DefaultResourceLoadPriority.h */; }; … … 17467 17468 E4E8B4F0216B8B5F00B8834D /* FontCascadeDescription.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FontCascadeDescription.cpp; sourceTree = "<group>"; }; 17468 17469 E4E8B4F2216B8B6000B8834D /* FontCascadeDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FontCascadeDescription.h; sourceTree = "<group>"; }; 17470 E4ED3ECA2768A51C00F17AC8 /* HasSelectorFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HasSelectorFilter.h; sourceTree = "<group>"; }; 17471 E4ED3ECD2768A68800F17AC8 /* HasSelectorFilter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = HasSelectorFilter.cpp; sourceTree = "<group>"; }; 17469 17472 E4F0BE2E25710A75009E7431 /* CaretRectComputation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CaretRectComputation.h; sourceTree = "<group>"; }; 17470 17473 E4F0BE3025710A76009E7431 /* CaretRectComputation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CaretRectComputation.cpp; sourceTree = "<group>"; }; … … 31081 31084 FBDB619A16D6032A00BB3394 /* ElementRuleCollector.cpp */, 31082 31085 FBDB619E16D6036500BB3394 /* ElementRuleCollector.h */, 31086 E4ED3ECD2768A68800F17AC8 /* HasSelectorFilter.cpp */, 31087 E4ED3ECA2768A51C00F17AC8 /* HasSelectorFilter.h */, 31083 31088 E4A814DD1C7338D100BF85AC /* IdChangeInvalidation.cpp */, 31084 31089 E4A814DF1C7338EB00BF85AC /* IdChangeInvalidation.h */, … … 34474 34479 8482B7461198C35400BFB005 /* HashChangeEvent.h in Headers */, 34475 34480 A8748BE012CBF2DC001FBA41 /* HashTools.h in Headers */, 34481 E4ED3ECC2768A51D00F17AC8 /* HasSelectorFilter.h in Headers */, 34476 34482 CD3EEF3D25799FB5006563BB /* HdrMetadataType.h in Headers */, 34477 34483 CDA595932146DEC300A84185 /* HEVCUtilities.h in Headers */, -
trunk/Source/WebCore/css/SelectorChecker.cpp
r286494 r287091 1250 1250 bool SelectorChecker::matchHasPseudoClass(CheckingContext& checkingContext, const Element& element, const CSSSelector& hasSelector) const 1251 1251 { 1252 auto matchElement = Style::computeHasPseudoClassMatchElement(hasSelector); 1253 1254 auto canMatch = [&] { 1255 switch (matchElement) { 1256 case Style::MatchElement::HasChild: 1257 case Style::MatchElement::HasDescendant: 1258 return !!element.firstElementChild(); 1259 case Style::MatchElement::HasSibling: 1260 case Style::MatchElement::HasSiblingDescendant: 1261 return !!element.nextElementSibling(); 1262 default: 1263 return true; 1264 }; 1265 }; 1266 1267 // See if there are any elements that this :has() selector could match. 1268 if (!canMatch()) 1269 return false; 1270 1252 1271 auto* cache = checkingContext.selectorMatchingState ? &checkingContext.selectorMatchingState->hasPseudoClassMatchCache : nullptr; 1253 1272 1254 Style::HasPseudoClassMatch* cachedMatch = nullptr;1255 if (cache) {1256 cachedMatch = &cache->add(Style::makeHasPseudoClassCacheKey(hasSelector, element), Style::HasPseudoClassMatch::None).iterator->value;1257 switch ( *cachedMatch) {1273 auto checkForCachedMatch = [&]() -> std::optional<bool> { 1274 if (!cache) 1275 return { }; 1276 switch (cache->get(Style::makeHasPseudoClassCacheKey(element, hasSelector))) { 1258 1277 case Style::HasPseudoClassMatch::Matches: 1259 1278 return true; … … 1264 1283 break; 1265 1284 } 1285 return { }; 1286 }; 1287 1288 // See if we know the result already. 1289 if (auto match = checkForCachedMatch()) 1290 return *match; 1291 1292 auto filterForElement = [&]() -> Style::HasSelectorFilter* { 1293 if (!checkingContext.selectorMatchingState) 1294 return nullptr; 1295 auto type = Style::HasSelectorFilter::typeForMatchElement(matchElement); 1296 if (!type) 1297 return nullptr; 1298 auto& filtersMap = checkingContext.selectorMatchingState->hasPseudoClassSelectorFilters; 1299 auto addResult = filtersMap.add(Style::makeHasPseudoClassFilterKey(element, *type), std::unique_ptr<Style::HasSelectorFilter>()); 1300 // Only build a filter if the same element gets checked second time with a different selector (misses the match cache). 1301 if (addResult.isNewEntry) 1302 return nullptr; 1303 1304 if (!addResult.iterator->value) 1305 addResult.iterator->value = makeUnique<Style::HasSelectorFilter>(element, *type); 1306 return addResult.iterator->value.get(); 1307 }; 1308 1309 // Check if the bloom filter rejects this selector 1310 if (auto* filter = filterForElement()) { 1311 if (filter->reject(hasSelector)) 1312 return false; 1266 1313 } 1267 1314 … … 1284 1331 for (auto it = descendantsOfType<Element>(descendantRoot).begin(); it;) { 1285 1332 auto& descendant = *it; 1286 if (cache ) {1287 auto key = Style::makeHasPseudoClassCacheKey( hasSelector, descendant);1333 if (cache && descendant.firstElementChild()) { 1334 auto key = Style::makeHasPseudoClassCacheKey(descendant, hasSelector); 1288 1335 if (cache->get(key) == Style::HasPseudoClassMatch::FailsSubtree) { 1289 1336 it.traverseNextSkippingChildren(); … … 1301 1348 1302 1349 auto match = [&] { 1303 auto matchElement = Style::computeHasPseudoClassMatchElement(hasSelector);1304 1305 1350 switch (matchElement) { 1306 1351 // :has(> .child) … … 1313 1358 // :has(.descendant) 1314 1359 case Style::MatchElement::HasDescendant: { 1315 if (!element.firstElementChild())1316 return false;1317 1360 if (cache) { 1318 1361 // See if we already know this descendant selector doesn't match in this subtree. 1319 1362 for (auto* ancestor = element.parentElement(); ancestor; ancestor = ancestor->parentElement()) { 1320 auto key = Style::makeHasPseudoClassCacheKey( hasSelector, *ancestor);1363 auto key = Style::makeHasPseudoClassCacheKey(*ancestor, hasSelector); 1321 1364 if (cache->get(key) == Style::HasPseudoClassMatch::FailsSubtree) 1322 1365 return false; … … 1325 1368 if (checkDescendants(element)) 1326 1369 return true; 1370 1327 1371 break; 1328 1372 } … … 1355 1399 auto result = match(); 1356 1400 1357 if (cachedMatch) { 1358 *cachedMatch = [&] { 1359 if (result) 1360 return Style::HasPseudoClassMatch::Matches; 1361 if (matchedInsideScope) 1362 return Style::HasPseudoClassMatch::Fails; 1363 return Style::HasPseudoClassMatch::FailsSubtree; 1364 }(); 1365 } 1401 auto matchTypeForCache = [&] { 1402 if (result) 1403 return Style::HasPseudoClassMatch::Matches; 1404 if (matchedInsideScope) 1405 return Style::HasPseudoClassMatch::Fails; 1406 return Style::HasPseudoClassMatch::FailsSubtree; 1407 }; 1408 1409 if (cache) 1410 cache->add(Style::makeHasPseudoClassCacheKey(element, hasSelector), matchTypeForCache()); 1366 1411 1367 1412 return result; -
trunk/Source/WebCore/css/SelectorFilter.cpp
r285195 r287091 46 46 } 47 47 48 static inline voidcollectElementIdentifierHashes(const Element& element, Vector<unsigned, 4>& identifierHashes)48 void SelectorFilter::collectElementIdentifierHashes(const Element& element, Vector<unsigned, 4>& identifierHashes) 49 49 { 50 50 AtomString tagLowercaseLocalName = element.localName().convertToASCIILowercase(); … … 135 135 } 136 136 137 struct CollectedSelectorHashes { 138 using HashVector = Vector<unsigned, 8>; 139 HashVector ids; 140 HashVector classes; 141 HashVector tags; 142 HashVector attributes; 143 }; 144 145 static inline void collectSimpleSelectorHash(CollectedSelectorHashes& collectedHashes, const CSSSelector& selector) 137 void SelectorFilter::collectSimpleSelectorHash(CollectedSelectorHashes& collectedHashes, const CSSSelector& selector) 146 138 { 147 139 switch (selector.match()) { … … 177 169 } 178 170 179 static CollectedSelectorHashes collectSelectorHashes(const CSSSelector& rightmostSelector) 171 auto SelectorFilter::collectSelectorHashes(const CSSSelector& rightmostSelector) -> CollectedSelectorHashes 180 172 { 181 173 CollectedSelectorHashes collectedHashes; … … 211 203 } 212 204 213 static SelectorFilter::Hashes chooseSelectorHashesForFilter(const CollectedSelectorHashes& collectedSelectorHashes) 214 { 215 SelectorFilter::Hashes resultHashes;205 auto SelectorFilter::chooseSelectorHashesForFilter(const CollectedSelectorHashes& collectedSelectorHashes) -> Hashes 206 { 207 Hashes resultHashes; 216 208 unsigned index = 0; 217 209 -
trunk/Source/WebCore/css/SelectorFilter.h
r228729 r287091 51 51 static Hashes collectHashes(const CSSSelector&); 52 52 53 static void collectElementIdentifierHashes(const Element&, Vector<unsigned, 4>&); 54 55 struct CollectedSelectorHashes { 56 using HashVector = Vector<unsigned, 8>; 57 HashVector ids; 58 HashVector classes; 59 HashVector tags; 60 HashVector attributes; 61 }; 62 static void collectSimpleSelectorHash(CollectedSelectorHashes&, const CSSSelector&); 63 53 64 private: 54 65 void initializeParentStack(Element& parent); 66 static CollectedSelectorHashes collectSelectorHashes(const CSSSelector& rightmostSelector); 67 static Hashes chooseSelectorHashesForFilter(const CollectedSelectorHashes&); 55 68 56 69 struct ParentStackFrame { -
trunk/Source/WebCore/style/HasSelectorFilter.h
r287090 r287091 25 25 #pragma once 26 26 27 #include " SelectorFilter.h"28 #include <wtf/ HashMap.h>27 #include "CSSSelector.h" 28 #include <wtf/BloomFilter.h> 29 29 30 namespace WebCore::Style { 30 namespace WebCore { 31 namespace Style { 31 32 32 using HasPseudoClassCacheKey = std::pair<const CSSSelector*, const Element*>;33 enum class MatchElement : uint8_t; 33 34 34 enum class HasPseudoClassMatch : uint8_t { None, Matches, Fails, FailsSubtree }; 35 class HasSelectorFilter { 36 WTF_MAKE_FAST_ALLOCATED; 37 public: 38 enum class Type : uint8_t { Children, Descendants }; 39 HasSelectorFilter(const Element&, Type); 35 40 36 struct SelectorMatchingState { 37 SelectorFilter selectorFilter; 38 HashMap<HasPseudoClassCacheKey, HasPseudoClassMatch> hasPseudoClassMatchCache; 41 Type type() const { return m_type; } 42 static std::optional<Type> typeForMatchElement(MatchElement); 43 44 using Key = unsigned; 45 static Key makeKey(const CSSSelector& hasSelector); 46 47 bool reject(const CSSSelector& hasSelector) const { return reject(makeKey(hasSelector)); } 48 bool reject(Key key) const { return !m_filter.mayContain(key); } 49 50 51 private: 52 void add(const Element&); 53 54 const Type m_type; 55 BloomFilter<12> m_filter; 39 56 }; 40 57 41 inline HasPseudoClassCacheKey makeHasPseudoClassCacheKey(const CSSSelector& selector, const Element& element)42 {43 return { &selector, &element };44 58 } 45 46 59 } -
trunk/Source/WebCore/style/SelectorMatchingState.h
r286494 r287091 25 25 #pragma once 26 26 27 #include "HasSelectorFilter.h" 27 28 #include "SelectorFilter.h" 28 29 #include <wtf/HashMap.h> … … 30 31 namespace WebCore::Style { 31 32 32 using HasPseudoClassCacheKey = std::pair<const CSSSelector*, const Element*>; 33 using HasPseudoClassCacheKey = std::pair<const Element*, const CSSSelector*>; 34 using HasPseudoClassFilterKey = std::pair<const Element*, uint8_t>; 33 35 34 36 enum class HasPseudoClassMatch : uint8_t { None, Matches, Fails, FailsSubtree }; … … 36 38 struct SelectorMatchingState { 37 39 SelectorFilter selectorFilter; 40 38 41 HashMap<HasPseudoClassCacheKey, HasPseudoClassMatch> hasPseudoClassMatchCache; 42 HashMap<HasPseudoClassFilterKey, std::unique_ptr<HasSelectorFilter>> hasPseudoClassSelectorFilters; 39 43 }; 40 44 41 inline HasPseudoClassCacheKey makeHasPseudoClassCacheKey(const CSSSelector& selector, const Element& element)45 inline HasPseudoClassCacheKey makeHasPseudoClassCacheKey(const Element& element, const CSSSelector& selector) 42 46 { 43 return { &selector, &element }; 47 return { &element, &selector }; 48 } 49 50 inline HasPseudoClassFilterKey makeHasPseudoClassFilterKey(const Element& element, HasSelectorFilter::Type type) 51 { 52 return { &element, static_cast<uint8_t>(type) }; 44 53 } 45 54
Note: See TracChangeset
for help on using the changeset viewer.