WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
NEW
323171
CSS Custom Highlight API: registering a few hundred small highlights makes every viewport repaint several times slower, and can lock the tab for minutes
https://bugs.webkit.org/show_bug.cgi?id=323171
Summary
CSS Custom Highlight API: registering a few hundred small highlights makes ev...
David Lynch
Reported
2026-09-01 18:08:21 PDT
Created
attachment 481259
[details]
Web Inspector Timelines recording of the freeze Registering 421 CSS custom highlight ranges, which cover a total of 1,502 characters with a median range length of 2 characters, significantly slows down the repaint of the viewport in Safari by 2.5 times. In contrast, Chrome (and Firefox) shows no measurable effect on the same file. In a real application, with a more complicated surrounding CSS environment, this same registration can make the tab unresponsive for over a minute. Interestingly, the cost of registering these ranges is not proportional to the amount of highlighted text, the size of the document, or the number of CSS rules. Instead, it scales with the number of registered ranges. ## Reduced Test Case Attached: webkit-highlight-repro-exact.html This reduced test case involves a 2,158-paragraph document (one paragraph per line, approximately 790,000 characters, and around 11,700 line boxes) placed within a contenteditable div. The document is styled to be 784 pixels wide with a monospace font size of 13 pixels and a white-space setting of `break-spaces`. In this test case, 421 ranges are registered across 36 Highlight groups, each corresponding to a matching `::highlight()` rule. The document, the ranges, and the styles used in this test case are identical to those used in the live case provided below. The test case simulates the behavior of the page by dirtying the viewport once per animation frame for a total of 60 frames. This is achieved by toggling an inline `margin-right` property on the root element. The mean frame time for each frame is then reported. Safari Technology Preview Release 251 (WebKit 21626.1.4.19.1) * without highlights: 16.9 ms/frame * with 421 ranges: 42.3 ms/frame (i.e. +25.4 ms / 2.5x) Chrome 151.0.7922.175 * without highlights: 16.9 ms/frame * with 421 ranges: 16.9 ms/frame (i.e. no change) This test case demonstrates that registering 421 CSS custom highlight ranges can lead to a significant slowdown in the repaint process in Safari, with a cost of approximately 60 microseconds per registered range per repaint, assuming that the ranges average 2 characters in length. ## Live case Wikipedia’s 2017 source editor with syntax highlighting rendered using the Custom Highlight API. (This is an unreleased feature, so it's not yet a huge problem for us.) 1. Visit:
https://en.wikipedia.org/w/index.php?title=Japanese_conjugation_(mizenkei_base)&veaction=editsource&cmhighlight=1
2. Enable syntax highlighting (found in the hamburger menu). 3. Open a modal window (“Options” in the hamburger menu, or make a change and hit “Publish” to trigger the save dialog). 4. Observe the page freezing for several minutes. The same measurement from above on that page: * Without highlights: 32.8 ms/frame * With 421 ranges: 245.7 ms/frame (i.e., +212.9 ms / 7.5x) ...so there's clearly something about the Wikipedia case that makes things significantly worse than my contrived example. `CSS.highlights.clear()` before triggering a modal removes the freeze entirely. Attached: en.wikipedia.org-recording.json, a Web Inspector Timelines recording of the first few hundred frames of one such freeze. In it: * The main thread is at 99-100% from t=9.4 to t=100.5. * There’s a 76.4-second window (t=11.92 to t=88.37) with no rendering frame records, no script records, and no layout, paint, or composite records at all. Rendering frames run continuously up to 11.92 and resume at 88.36. * Total JavaScript across the entire 145-second recording is 0.118 seconds. * The CPU detail view attributes almost all the time to painting. I’ll be adding some mitigation to suppress the highlights when the modal window is opened, so it hopefully won't remain live for too long. I'll try to do so in such a way that I can leave a comment here with how to override it so the bug can be tested.
Attachments
Web Inspector Timelines recording of the freeze
(12.61 MB, application/json)
2026-09-01 18:08 PDT
,
David Lynch
no flags
Details
Demonstration page for the issue
(905.83 KB, text/html)
2026-09-01 18:09 PDT
,
David Lynch
no flags
Details
View All
Add attachment
proposed patch, testcase, etc.
David Lynch
Comment 1
2026-09-01 18:09:19 PDT
Created
attachment 481260
[details]
Demonstration page for the issue
David Lynch
Comment 2
2026-09-01 20:18:32 PDT
Okay,
https://gerrit.wikimedia.org/r/c/mediawiki/extensions/CodeMirror/+/1333377
merged with the mitigation, so (with normal deployment cadence) after Thursday September 10th you'll need to use this URL with a cmsuspend=0 parameter instead for the live demo:
https://en.wikipedia.org/w/index.php?title=Japanese_conjugation_(mizenkei_base)&veaction=editsource&cmhighlight=1&cmsuspend=0
Radar WebKit Bug Importer
Comment 3
2026-09-02 10:58:14 PDT
<
rdar://problem/186478678
>
Ryan Reno
Comment 4
2026-09-04 13:02:57 PDT
Thanks for your report and the test case. I confirmed the slowdown due to highlights. There seem to be two distinct issues. One of them is relatively straightforward and the other deals with Glyph display list caches which I'm very unfamiliar with. For the former case, we will iterate through an array of all highlights in a document when painting every text box. This obviously grows O(n) in the number of custom highlights, even if the highlights are outside the viewport. I have a draft patch for that I'll put up soon. This takes care of about half of the time in the test case. For the latter issue, I'll need to spend more time understanding the issue and if we can use the caches to skip extra work when painting text boxes.
Ryan Reno
Comment 5
2026-09-04 13:06:38 PDT
It's really O(n*m) where n is the number of highlights and m the number of text boxes. I meant the iteration grows O(n) in the number of highlights.
Ryan Reno
Comment 6
2026-09-08 16:21:33 PDT
Pull request:
https://github.com/WebKit/WebKit/pull/73511
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug