stylo: support ::first-letter

Assigned to



CSS Parsing and Computation
6 months ago
2 days ago


(Reporter: heycam, Assigned: bz)


(Depends on: 1 bug, Blocks: 7 bugs)

Firefox Tracking Flags

(Not tracked)


Comment hidden (empty)
Blocks: 1324636
Blocks: 1324646
Blocks: 1324657
Blocks: 1324658
Blocks: 1324619
bz and I discussed this a bit, in particular how to handle the stylo restyle case where we're crawling the content tree (and not the frame tree), which means that it's harder to find the ::first-letter frame for a given originating element.

[1] Shows how there can be an arbitrarily-large separation in the content tree between the the originating element for the pseudo and the element whose inline frame actually inherits from the ::first-line style. However, there are a few interesting things to note.

First, ::first-letter and ::first-line only apply to block frames per spec. So we just ignore them if they show up on anything that's display:inline.

Second, Gecko's existing behavior (which differs from Chrome's, and both of which differ from the spec) is that ::first-letter and ::first-line do not apply into nested block elements (see [2]). This makes our job easier.

So the proposed implementation strategy is as follows:
* When servo cascades ::first-line and ::first-letter and stashes them on HashMap hanging off the originating element, it computes the styles with no style parent, since the precise parent to inherit from is heavily frame-constructor-dependent (and in the ::first-line case, can be different styles depending on which frame we're resolving for).
* When we resolve those styles from gecko, we pass in the parent for a lazy cascade, similar to what we do currently for anonymous boxes.
* To find the appropriate frame to restyle during the content tree traversal in RecreateStyleContexts, we first check whether a given block-level element has the appropriate pseudo. If it does, then we do a left-handed depth-first search of the frame tree from the primary frame, stopping when we either (a) hit a block frame or (b) find a frame for that pseudo.

One tricky thing we'll need to handle is the possibility that there might be a RECONSTRUCT_FRAME change hint lurking further down the content tree between the originating element and the affected frame. We don't want to reach through these and do extra work, and they also could affect the block-vs-inline nature of the descendants, which affects how we'd propagate the pseudo style. It might make sense to do this processing postorder, and return some bitmask indicating which pseudos we've already handled further down the tree. Or something.

[1] data:text/html,<style>div::first-letter { color: red; } </style><div><span><span><span>text</span></span></span></div>
[2] data:text/html,<style>div::first-letter { color: red; }</style><div><p>text</p></div>
> (a) hit a block frame

More precisely, when you hit anything that's not a non-replaced inline.  You might hit an image frame, or inline-table, or whatever; if you do, you stop.  

In Gecko the search basically stops when it finds either a textframe (which is where the first-letter frame then goes) or a brFrame or a frame that is not eLineParticipant.
Assignee: nobody → bzbarsky
Priority: -- → P1
Blocks: 1360424
Blocks: 1363549
Depends on: 1352743
Blocks: 1324348
Depends on: 1369187
Depends on: 1375315
Blocks: 1375338
You need to log in before you can comment on or make changes to this bug.