Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: 5e0c6a7d040bef4f723047f9838624cfa5f6d7b5
https://github.com/WebKit/WebKit/commit/5e0c6a7d040bef4f723047f9838624cfa5f6d7b5
Author: Cole Carley <[email protected]>
Date: 2026-04-12 (Sun, 12 Apr 2026)
Changed paths:
A
LayoutTests/editing/text-iterator/find-from-shadow-to-ancestor-scope-expected.txt
A LayoutTests/editing/text-iterator/find-from-shadow-to-ancestor-scope.html
A
LayoutTests/editing/text-iterator/find-string-cross-shadow-boundary-expected.txt
A LayoutTests/editing/text-iterator/find-string-cross-shadow-boundary.html
A
LayoutTests/editing/text-iterator/find-string-in-nested-shadow-roots-expected.txt
A LayoutTests/editing/text-iterator/find-string-in-nested-shadow-roots.html
M LayoutTests/editing/text-iterator/find-string-on-flat-tree-expected.txt
M LayoutTests/editing/text-iterator/find-string-on-flat-tree.html
M Source/WebCore/Sources.txt
M Source/WebCore/WebCore.xcodeproj/project.pbxproj
M Source/WebCore/dom/Document.h
A Source/WebCore/editing/CachedMatchFinder.cpp
A Source/WebCore/editing/CachedMatchFinder.h
M Source/WebCore/editing/CompositeEditCommand.cpp
M Source/WebCore/editing/Editor.cpp
M Source/WebCore/editing/Editor.h
M Source/WebCore/editing/ICUSearcher.cpp
M Source/WebCore/editing/ICUSearcher.h
M Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp
M Source/WebCore/editing/TextIterator.cpp
M Source/WebCore/page/MemoryRelease.cpp
M Tools/TestWebKitAPI/Tests/WebKit/WKWebView/FindInPage.mm
Log Message:
-----------
Improve find in page performance by using a cached flat buffer
https://bugs.webkit.org/show_bug.cgi?id=309323
rdar://171860939
Reviewed by Ryosuke Niwa.
Find in page can be laggy on large pages. This is partially because
every time you press 'next' during a find session, the entire DOM
tree is searched for text nodes. The largest bottle neck is during
counting and marking, when all matches are found.
To fix this, I have implemented a CachedMatchFinder, which is a wrapper
on top of the TextIterator. This CachedMatchFinder exposes three common
functions use in find in page: findMatchFrom, findMatches, and
countMatches.
The CachedMatchFinder keeps a flattened buffer of all the visible text. It
uses the domTreeVersion and styleRecalcCount to invalidate the buffer. It
rebuilds and traverses the DOM only when absolutely necessary. The results
of a find operation are also cached, so the bottle neck is largely avoided.
This has resulted in a visual speedup while clicking through during a
find session.
CachedMatchFinder uses the ICUSearcher in a different way than the
TextIterator/SearchBuffer does. TextIterator/SearchBuffer uses a windowed
approach, which limits the size of the buffer, where CachedMatchFinder uses
one large buffer.
This difference has exposed an issue in WebCore::findNextWordFromIndex when
finding word boundaries for context-sensitive words. The macOS version of
findNextWordFromIndex uses a dictionary based approach, and for some reason,
many unrelated characters followed by a context-sensitive word results in
inconsistent word boundaries.
To mitigate this issue, I introduced a function:
extractSubspanIncludingContextNeededForDictionaryBasedWordBreak, which will
limit the context surrounding context-sensitive words, to ensure consistent
word boundaries.
*
LayoutTests/editing/text-iterator/find-string-in-nested-shadow-roots-expected.txt:
Added.
*
LayoutTests/editing/text-iterator/find-string-cross-shadow-boundary-expected.txt:
Added.
* LayoutTests/editing/text-iterator/find-string-cross-shadow-boundary.html:
Added.
*
LayoutTests/editing/text-iterator/find-string-in-nested-shadow-roots-expected.txt:
Added.
* LayoutTests/editing/text-iterator/find-string-in-nested-shadow-roots.html:
Added.
* LayoutTests/editing/text-iterator/find-string-on-flat-tree-expected.txt:
* LayoutTests/editing/text-iterator/find-string-on-flat-tree.html:
* Source/WebCore/Sources.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/dom/Document.h:
(WebCore::Document::styleRecalcCount const):
* Source/WebCore/editing/CachedMatchFinder.cpp: Added.
(WebCore::CachedMatchFinder::CachedMatchFinder):
(WebCore::CachedMatchFinder::performSearch):
(WebCore::matchIsWithinSingleScope):
(WebCore::CachedMatchFinder::findNextMatch):
(WebCore::CachedMatchFinder::findNextMatchInShadowIncludingAncestorTree):
(WebCore::CachedMatchFinder::findMatchFrom):
(WebCore::CachedMatchFinder::findMatches):
(WebCore::CachedMatchFinder::countMatches):
(WebCore::CachedMatchFinder::bufferOffsetForBoundaryPoint):
(WebCore::CachedMatchFinder::startingOffsetForSelection):
(WebCore::CachedMatchFinder::isCacheValid const):
(WebCore::CachedMatchFinder::updateCachedText):
(WebCore::CachedMatchFinder::bufferForOptions):
(WebCore::CachedMatchFinder::buildForScope):
(WebCore::CachedMatchFinder::boundaryForOffset):
(WebCore::CachedMatchFinder::bufferRangeToSimpleRange):
(WebCore::CachedMatchFinder::isCachedSearchText const):
* Source/WebCore/editing/CachedMatchFinder.h: Added.
* Source/WebCore/editing/CompositeEditCommand.cpp:
* Source/WebCore/editing/DictationCommand.cpp:
* Source/WebCore/editing/Editor.cpp:
(WebCore::Editor::findString):
(WebCore::Editor::countMatchesForText):
(WebCore::Editor::releaseMemory):
* Source/WebCore/editing/Editor.h:
* Source/WebCore/editing/ICUSearcher.cpp:
(WebCore::ICUSearcher::ICUSearcher):
(WebCore::ICUSearcher::setCollationStrength):
(WebCore::ICUSearcher::setAttribute):
(WebCore::ICUSearcher::setPattern):
(WebCore::ICUSearcher::setText):
(WebCore::ICUSearcher::setOffset):
(WebCore::ICUSearcher::next):
(WebCore::ICUSearcher::matchedLength):
(WebCore::extractSubspanIncludingContextNeededForDictionaryBasedWordBreak):
(WebCore::isWordStartMatch):
(WebCore::isWordEndMatch):
* Source/WebCore/editing/ICUSearcher.h:
* Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp:
* Source/WebCore/editing/TextIterator.cpp:
(WebCore::SearchBuffer::SearchBuffer):
(WebCore::SearchBuffer::search):
* Source/WebCore/page/MemoryRelease.cpp:
(WebCore::releaseCriticalMemory):
* Tools/TestWebKitAPI/Tests/WebKit/WKWebView/FindInPage.mm:
(TEST(WebKit, FindMatchesWithDifferentOptionsInSuccession)):
(TEST(WebKit, FindMatchesInShadowRoots)):
(TEST(WebKit, FindMatchesCrossShadowBoundary)):
(TEST(WebKit, FindMatchesCrossNestedShadowBoundary)):
(TEST(WebKit, FindMatchesNotFoundAcrossNonAdjacentFlatTreeContent)):
(TEST(WebKit, FindMatchesUnslottedContentNotFound)):
(TEST(WebKit, FindMatchesAfterDOMMutation)):
Canonical link: https://commits.webkit.org/311049@main
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications