Implement text-decoration-inset painting in Blink [chromium/src : main]

0 views
Skip to first unread message

Fredrik Söderquist (Gerrit)

unread,
Jul 27, 2026, 5:36:05 AM (yesterday) Jul 27
to Helmut Januschka, Stephen Chenney, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, Chromium Metrics Reviews, chromium...@chromium.org, mac-r...@chromium.org, apavlo...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, blink-re...@chromium.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, jmedle...@chromium.org, kinuko...@chromium.org, zol...@webkit.org
Attention needed from Helmut Januschka

Fredrik Söderquist added 1 comment

File third_party/blink/renderer/core/paint/text_decoration_info.cc
Line 412, Patchset 29 (Latest): const gfx::PointF start_point = gfx::PointF(local_origin_) +
gfx::Vector2dF(line_left_adjust, line_offset);
DecorationGeometry geometry = DecorationGeometry::Make(
style,
gfx::RectF(start_point,
gfx::SizeF(inset_width, decoration.resolved_thickness)),
Fredrik Söderquist . unresolved

I think it would make more sense if this did:
```
gfx::RectF decoration_rect(...);
ApplyInsets(decoration.applied_text_decoration, ..., decoration_rect);
```
Then it could trivially return and do nothing if the `*-inset` property is a no-op (which should be the common case). (It also avoids reducing the readability of this function.)

Open in Gerrit

Related details

Attention is currently required from:
  • Helmut Januschka
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: Ieea054f3d65a11eee05b9461728d1b30c18989e0
Gerrit-Change-Number: 7748204
Gerrit-PatchSet: 29
Gerrit-Owner: Helmut Januschka <hel...@januschka.com>
Gerrit-Reviewer: Helmut Januschka <hel...@januschka.com>
Gerrit-Reviewer: Stephen Chenney <sche...@chromium.org>
Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
Gerrit-CC: Fredrik Söderquist <f...@opera.com>
Gerrit-CC: Menard, Alexis <alexis...@intel.com>
Gerrit-Attention: Helmut Januschka <hel...@januschka.com>
Gerrit-Comment-Date: Mon, 27 Jul 2026 09:35:48 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
satisfied_requirement
unsatisfied_requirement
open
diffy

Helmut Januschka (Gerrit)

unread,
Jul 27, 2026, 5:03:43 PM (yesterday) Jul 27
to Fredrik Söderquist, Stephen Chenney, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, Chromium Metrics Reviews, chromium...@chromium.org, mac-r...@chromium.org, apavlo...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, blink-re...@chromium.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, jmedle...@chromium.org, kinuko...@chromium.org, zol...@webkit.org
Attention needed from Fredrik Söderquist and Stephen Chenney

Helmut Januschka added 3 comments

Patchset-level comments
File-level comment, Patchset 14:
Stephen Chenney . unresolved

I think the overall approach is good. I have a few requests:

  • Please split into two CLs. One to add the style code and the flag (to the point of creating AppliedTextDecoration data on the style), and then another for the painting portions. Then a style expert could review the style changes.
  • Please add someone specializing in inline layout to the painting CL to review the code related to fragment matching/searching. I do not trust myself to see issues there. Maybe tkent@, maybe ikilpatrick@, maybe koji@.
  • Please file a bug for ink overflow and implement it in a separate CL. When the insets are negative and extend beyond the text width the overflow will need to account for that. You'll also need tests that add/remove decorations to verify that the repaint is correct. There are existing tests for custom highlights that do this, IIRC.
Helmut Januschka

done, split into two CLs: crrev.com/c/7993418 is the style/flag part, this CL is now painting only. for ink overflow this CL only does conservative bounds (negative insets expand, positive ones never shrink), filed crbug.com/539095434 for the exact overflow + repaint test work, crrev.com/c/8148940 chained on top of this one (also makes text-decoration-inset-028/030 pass). ptal

File-level comment, Patchset 17:
Helmut Januschka . resolved

@sche...@chromium.org anything i can do one my end?
do you have anyone in mind who could otherwise do the review?

Stephen Chenney

Take a look at the first comment from Patchset 14. I think this should be split in 2 CLs, and I listed a couple of people who could review the text fragment logic.

Someone on the style team could review the first CL and I can do the second one.

Helmut Januschka

Done

File third_party/blink/renderer/core/paint/text_decoration_info.cc
Line 412, Patchset 29: const gfx::PointF start_point = gfx::PointF(local_origin_) +

gfx::Vector2dF(line_left_adjust, line_offset);
DecorationGeometry geometry = DecorationGeometry::Make(
style,
gfx::RectF(start_point,
gfx::SizeF(inset_width, decoration.resolved_thickness)),
Fredrik Söderquist . resolved

I think it would make more sense if this did:
```
gfx::RectF decoration_rect(...);
ApplyInsets(decoration.applied_text_decoration, ..., decoration_rect);
```
Then it could trivially return and do nothing if the `*-inset` property is a no-op (which should be the common case). (It also avoids reducing the readability of this function.)

Helmut Januschka

Done. thank you!

Open in Gerrit

Related details

Attention is currently required from:
  • Fredrik Söderquist
  • Stephen Chenney
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: Ieea054f3d65a11eee05b9461728d1b30c18989e0
Gerrit-Change-Number: 7748204
Gerrit-PatchSet: 30
Gerrit-Owner: Helmut Januschka <hel...@januschka.com>
Gerrit-Reviewer: Helmut Januschka <hel...@januschka.com>
Gerrit-Reviewer: Stephen Chenney <sche...@chromium.org>
Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
Gerrit-CC: Fredrik Söderquist <f...@opera.com>
Gerrit-CC: Menard, Alexis <alexis...@intel.com>
Gerrit-Attention: Stephen Chenney <sche...@chromium.org>
Gerrit-Attention: Fredrik Söderquist <f...@opera.com>
Gerrit-Comment-Date: Mon, 27 Jul 2026 21:03:27 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Helmut Januschka <hel...@januschka.com>
Comment-In-Reply-To: Stephen Chenney <sche...@chromium.org>
Comment-In-Reply-To: Fredrik Söderquist <f...@opera.com>
satisfied_requirement
unsatisfied_requirement
open
diffy

Fredrik Söderquist (Gerrit)

unread,
9:45 AM (11 hours ago) 9:45 AM
to Helmut Januschka, Stephen Chenney, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, Menard, Alexis, Chromium Metrics Reviews, chromium...@chromium.org, mac-r...@chromium.org, apavlo...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, blink-re...@chromium.org, blink-revi...@chromium.org, blink-rev...@chromium.org, blink-rev...@chromium.org, blink-revie...@chromium.org, blink-...@chromium.org, jmedle...@chromium.org, kinuko...@chromium.org, zol...@webkit.org
Attention needed from Helmut Januschka and Stephen Chenney

Fredrik Söderquist added 12 comments

File third_party/blink/renderer/core/paint/text_decoration_info.cc
Line 358, Patchset 30 (Latest):void TextDecorationInfo::ApplyDecorationInsets(
const ResolvedDecoration& decoration,
gfx::RectF& decoration_rect) const {
Fredrik Söderquist . unresolved

So now it looks quite obvious that this is invariant "per-index", so this could be resolved during `ResolveDecorationAt()` and then just (trivially) applied when computing a specific line.

Line 363, Patchset 30 (Latest): if (!applied_text_decoration) {
return;
}
Fredrik Söderquist . unresolved

Should always be non-null.

Line 378, Patchset 30 (Latest): if (inset.GetStart().IsZero() && inset.GetEnd().IsZero()) {
Fredrik Söderquist . unresolved

It'd probably be pertinent to check this case first, since it's expect to be true most of the time.

File third_party/blink/renderer/core/paint/text_decoration_painter.cc
Line 130, Patchset 30 (Latest): InlineCursor cursor = inline_context_->CursorForDescendantsOfLine();
bool found_item = cursor && cursor.TryMoveTo(text_item);

if (!found_item && cursor) {
found_item = TryMoveToMatchingTextFragment(cursor, text_item,
IsStrictTextFragmentMatch);
}
if (!found_item && cursor) {
// Fallback for cases where the strict fragment identity diverges across
// cursor roots. Prefer node+offset matching over giving up entirely.
found_item = TryMoveToMatchingTextFragment(cursor, text_item,
IsLooseTextFragmentMatch);
}
Fredrik Söderquist . unresolved

The `TextFragmentPainter` should have an `InlineCursor` already - could we just use that one instead of trying to re-create it? It might make sense to resolve all of this at that level so that we don't have to recompute it several times.

Line 128, Patchset 30 (Latest): if (inline_context_ &&
NeedsNeighborFragmentsForDecorationInset(style, decoration_override)) {
InlineCursor cursor = inline_context_->CursorForDescendantsOfLine();
bool found_item = cursor && cursor.TryMoveTo(text_item);

if (!found_item && cursor) {
found_item = TryMoveToMatchingTextFragment(cursor, text_item,
IsStrictTextFragmentMatch);
}
if (!found_item && cursor) {
// Fallback for cases where the strict fragment identity diverges across
// cursor roots. Prefer node+offset matching over giving up entirely.
found_item = TryMoveToMatchingTextFragment(cursor, text_item,
IsLooseTextFragmentMatch);
}

if (found_item) {
// Use a cursor rooted at the full containing block so that
// MoveTo{Previous,Next}InlineLeafOnLine() is line-scoped.
InlineCursor line_cursor = cursor;
line_cursor.ExpandRootToContainingBlock();
if (line_cursor.TryMoveTo(*cursor.CurrentItem())) {
InlineCursor previous_cursor = line_cursor;
previous_cursor.MoveToPreviousInlineLeafOnLine();
if (previous_cursor.CurrentItem() &&
previous_cursor.CurrentItem()->IsText() &&
!previous_cursor.CurrentItem()->IsLineBreak()) {
previous_fragment_on_line = previous_cursor.CurrentItem();
}
InlineCursor next_cursor = line_cursor;
next_cursor.MoveToNextInlineLeafOnLine();
if (next_cursor.CurrentItem() && next_cursor.CurrentItem()->IsText() &&
!next_cursor.CurrentItem()->IsLineBreak()) {
next_fragment_on_line = next_cursor.CurrentItem();
}
}
}
}
Fredrik Söderquist . unresolved

This looks like it could be broken out into a separate function and maybe return a struct with all the relevant data bits (that then an be forwarded to the `TextDecorationInfo` constructor). Passing a struct would also make the constructor a bit less unwieldy (it's already quite parameter-heavy).

File third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-inset-svg-fragment-001.html
Line 9, Patchset 30 (Latest): body { margin: 0; background: white; }
Fredrik Söderquist . unresolved

This is unnecessary.

Line 10, Patchset 30 (Latest): svg { display: block; }
Fredrik Söderquist . unresolved

...and so is this.

Line 13, Patchset 30 (Latest): fill: black;
Fredrik Söderquist . unresolved

And this.

Line 20, Patchset 30 (Latest):<svg width="420" height="100" viewBox="0 0 420 100">
Fredrik Söderquist . unresolved

And this. (If it differed from width/height it might be relevant.)

File third_party/blink/web_tests/external/wpt/css/css-text-decor/text-decoration-inset-svg-fragment-002.html
Line 8, Patchset 30 (Latest):<style>
body { margin: 0; background: white; }
svg { display: block; }
text {
font: 42px/1 monospace;
fill: black;
text-decoration-line: underline;
text-decoration-color: black;
text-decoration-thickness: 3px;
text-decoration-inset: -12px;
}
</style>
<svg width="420" height="100" viewBox="0 0 420 100">
Fredrik Söderquist . unresolved

Ditto here.

File third_party/blink/web_tests/svg/text/text-decoration-thickness-html-vs-svg-no-inset.html
File-level comment, Patchset 30 (Latest):
Fredrik Söderquist . unresolved

Ditto here.

File third_party/blink/web_tests/svg/text/text-decoration-thickness-html-vs-svg.html
File-level comment, Patchset 30 (Latest):
Fredrik Söderquist . unresolved

This test seems unrelated to this feature. It's also not particularly interesting (there should be a bug for this somewhere).

Open in Gerrit

Related details

Attention is currently required from:
  • Helmut Januschka
  • Stephen Chenney
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedNo-Unresolved-Comments
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: Ieea054f3d65a11eee05b9461728d1b30c18989e0
Gerrit-Change-Number: 7748204
Gerrit-PatchSet: 30
Gerrit-Owner: Helmut Januschka <hel...@januschka.com>
Gerrit-Reviewer: Helmut Januschka <hel...@januschka.com>
Gerrit-Reviewer: Stephen Chenney <sche...@chromium.org>
Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
Gerrit-CC: Fredrik Söderquist <f...@opera.com>
Gerrit-CC: Menard, Alexis <alexis...@intel.com>
Gerrit-Attention: Helmut Januschka <hel...@januschka.com>
Gerrit-Attention: Stephen Chenney <sche...@chromium.org>
Gerrit-Comment-Date: Tue, 28 Jul 2026 13:45:31 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
satisfied_requirement
unsatisfied_requirement
open
diffy
Reply all
Reply to author
Forward
0 new messages