Skip to content

[css-text] What does the white-space-collapse apply to when white-space trimming/positioning #9724

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
andreubotella opened this issue Dec 19, 2023 · 11 comments
Labels

Comments

@andreubotella
Copy link
Member

andreubotella commented Dec 19, 2023

CSS-TEXT section 4.1.2 talks about the rendering and hanging of spaces in each line, and step 4 talks about whether a sequence of trailing spaces at the end of the line hangs, which depends on the values of white-space-collapse (and text-wrap-mode).

However, when you look at it in detail, it's not clear what value should be used here. This whole section applies to the line as a whole, and a sequence of trailing spaces can span inlines. Should then each preserved space in the sequence of the line's trailing spaces hang or not depending on its white-space-collapse value? This doesn't seem to make sense, since this would allow for some spaces to hang when not at the end of the line, which doesn't seem to make sense, and is not allowed by the definition of hanging.

<div style="white-space-collapse: continue">
foo&x#3000;&x#3000;&x#3000;<span style="white-space-collapse: break-spaces">        </span>
</div>

In this example, if the entirety of the text fits within the line, the ideographic spaces would hang (since they're not collapsed in phase I, so they count as preserved, but they're still white-space-collapse: continue), but the spaces inside the span wouldn't.

Because of this, when fixing hanging in Blink to align with the spec, I understood the white-space-collapse (and text-wrap-mode) values as applying to the line, rather than the spaces. Blink has a concept of a line box's style, which apparently is not in the spec (although I was not aware of that at the time of implementing this), which seems to be the same as the containing block box's computed style (except maybe including ::first-line styles) – and that is what I used in my implementation. This led to differences with other browsers in cases like this:

<div style="white-space-collapse: collapse; text-align: center">
Something.<span style="white-space-collapse: preserve; text-wrap-mode: wrap;">          </span>
</div>

If the relevant white-space-collapse (and text-wrap-mode) values should be those corresponding to each preserved space character, then the trailing spaces inside the span conditionally hang (since this is the last line), and therefore the "Something." text will be off-center (assuming that the text fully fits within the line, whether the spaces do or not). However, if the relevant values should be the containing block box's, then the spaces should hang (unconditionally) and the "Something." text will appear centered.

cc @kojiishi @jfkthame @fantasai @frivoal

@frivoal
Copy link
Collaborator

frivoal commented Dec 20, 2023

It does apply to the spaces, not the the line or the whole block. white-space-collapse and text-wrap-mode are explicitly defined as applied to text, not to blocks, and that's deliberate.

If towards the end of a line you've got some spaces that cannot hang followed by some spaces that can, then only the ones that can hang get to hang, and the others do not. Similarly, if you've got some non collapsible spaces followed by some collapsible spaces at the end of the line, then step 3 gets rids of the collapsible spaces, and those other non collapsible spaces remain.

If it is the other way around, and you've got a sequence of collapsible spaces followed by a sequence of non collapsible spaces, then the sequence of collapsible spaces aren't at the end of the line (the non-collapsible ones are), and so step 3 doesn't discard them.

Similarly, if you've got some spaces that can hang, followed by some that cannot, then none of them hang, because the spaces at the end of the line aren't hangable, and those that are hangable aren't at the end of the line.

I feel like the case with collapsible and non collapsible isn't even that hard to construct:

<style>
code {
  white-space: pre;
  font-family: monospace; /* not important, just to make it realistic */
  background: lightgray; /* not important, just to make it realistic */
}
</style>
<p>
  In Makefiles, do no confuse sequences of spaces like <code>        </code>
  with tabs like <code>&#9;</code> as they have a different behavior
  even if they look the same.

If the line ends so that there's room for the code element and a little more, but not enough for the next word, then you've got a collapsible space followed by the non collapsible sequence of spaces of the code element, followed by the collapsible space after the code element, at the end of the line. In this case, you remove the collapsible space after the code element, and keep both the non collapsible spaces in the code element (they're not collapsible) and the collapsible space before it (it's not at the end of the line).

I feel like realistic examples with hangable / non hangable spaces are a tad more difficult to construct (at least in English), but the logic is the same.

@andreubotella
Copy link
Member Author

If that is the intended reading, the spec should probably clarify that, I think. I'll work on a PR to do that.

@andreubotella
Copy link
Member Author

What would happen if a sequence of whitespace that hangs unconditionally is followed by a sequence that hangs conditionally?

@frivoal
Copy link
Collaborator

frivoal commented Dec 21, 2023

What would happen if a sequence of whitespace that hangs unconditionally is followed by a sequence that hangs conditionally?

Same logic? In cases where the condition is met and (all) the conditionally hangable spaces do hang because they didn't fit the line prior to justification, then condition part is fullfilled. The (conditionally) hanging spaces are just hanging spaces, as are the unconditionally hanging spaces at the end of the line, so it's just one big sequence of hanging spaces at the end of the line, and it can hang.

However, if there remains at least one conditionally hangable space at the end of the line which is not hanging because it did fit the line prior to justification, then the sequence of unconditionally hangable spaces aren't actually at the end of the line since there's something in between (a non-hanging space), and thus they don't hang.

@andreubotella
Copy link
Member Author

andreubotella commented Dec 22, 2023

<style>
  div {
    border: 1px solid black;
    text-align: center;
    animation: 5s linear infinite alternate width-animation;
  }
  .unconditional {
    white-space: normal;
    background-color: purple;
    opacity: 0.6;
  }
  .conditional {
    white-space: pre-wrap;
    background-color: orange;
    opacity: 0.6;
  }
</style>

<div><!--
  -->qwertyuiop<!--
  --><span class="unconditional">&#x3000;&#x3000;&#x3000;&#x3000;&#x3000;</span><!--
  --><span class="conditional">             </span><!--
--></div>

I understand that a correct implementation should render this animation, then.

chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Mar 14, 2024
The CL https://crrev.com/c/4881393 changed the hanging space behavior
to not depend on `text-align`, and it updated the implementation to
match the spec text. However, that patch had a misunderstanding of the
spec that made each line's hanging space behavior depend on its block
container's styles, rather than on each space's inline item styles.
That caused incompatibilities with other browsers.

This patch makes this behavior dependent on the inline item styles,
thus fixing the main source of incompatibilities.

The spec text is not yet fully clear on the behavior of some edge
cases, which is why the WPT tests this CL adds are marked as
tentative. See w3c/csswg-drafts#9724 for the
CSSWG issue discussing this.

Bug: 40944859
Change-Id: Ib93d0ffe41a8c538c8a4c9a13838bc304c26b30e
aarongable pushed a commit to chromium/chromium that referenced this issue Mar 16, 2024
The CL https://crrev.com/c/4881393 changed the hanging space behavior
to not depend on `text-align`, and it updated the implementation to
match the spec text. However, that patch had a misunderstanding of the
spec that made each line's hanging space behavior depend on its block
container's styles, rather than on each space's inline item styles.
That caused incompatibilities with other browsers.

This patch makes this behavior dependent on the inline item styles,
thus fixing the main source of incompatibilities.

The spec text is not yet fully clear on the behavior of some edge
cases, which is why the WPT tests this CL adds are marked as
tentative. See w3c/csswg-drafts#9724 for the
CSSWG issue discussing this.

Bug: 40944859
Change-Id: Ib93d0ffe41a8c538c8a4c9a13838bc304c26b30e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5147580
Reviewed-by: Koji Ishii <kojii@chromium.org>
Commit-Queue: Andreu Botella <abotella@igalia.com>
Cr-Commit-Position: refs/heads/main@{#1273823}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Mar 16, 2024
The CL https://crrev.com/c/4881393 changed the hanging space behavior
to not depend on `text-align`, and it updated the implementation to
match the spec text. However, that patch had a misunderstanding of the
spec that made each line's hanging space behavior depend on its block
container's styles, rather than on each space's inline item styles.
That caused incompatibilities with other browsers.

This patch makes this behavior dependent on the inline item styles,
thus fixing the main source of incompatibilities.

The spec text is not yet fully clear on the behavior of some edge
cases, which is why the WPT tests this CL adds are marked as
tentative. See w3c/csswg-drafts#9724 for the
CSSWG issue discussing this.

Bug: 40944859
Change-Id: Ib93d0ffe41a8c538c8a4c9a13838bc304c26b30e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5147580
Reviewed-by: Koji Ishii <kojii@chromium.org>
Commit-Queue: Andreu Botella <abotella@igalia.com>
Cr-Commit-Position: refs/heads/main@{#1273823}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this issue Mar 19, 2024
The CL https://crrev.com/c/4881393 changed the hanging space behavior
to not depend on `text-align`, and it updated the implementation to
match the spec text. However, that patch had a misunderstanding of the
spec that made each line's hanging space behavior depend on its block
container's styles, rather than on each space's inline item styles.
That caused incompatibilities with other browsers.

This patch makes this behavior dependent on the inline item styles,
thus fixing the main source of incompatibilities.

The spec text is not yet fully clear on the behavior of some edge
cases, which is why the WPT tests this CL adds are marked as
tentative. See w3c/csswg-drafts#9724 for the
CSSWG issue discussing this.

Bug: 40944859
Change-Id: Ib93d0ffe41a8c538c8a4c9a13838bc304c26b30e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5147580
Reviewed-by: Koji Ishii <kojii@chromium.org>
Commit-Queue: Andreu Botella <abotella@igalia.com>
Cr-Commit-Position: refs/heads/main@{#1273823}
BruceDai pushed a commit to BruceDai/wpt that referenced this issue Mar 25, 2024
The CL https://crrev.com/c/4881393 changed the hanging space behavior
to not depend on `text-align`, and it updated the implementation to
match the spec text. However, that patch had a misunderstanding of the
spec that made each line's hanging space behavior depend on its block
container's styles, rather than on each space's inline item styles.
That caused incompatibilities with other browsers.

This patch makes this behavior dependent on the inline item styles,
thus fixing the main source of incompatibilities.

The spec text is not yet fully clear on the behavior of some edge
cases, which is why the WPT tests this CL adds are marked as
tentative. See w3c/csswg-drafts#9724 for the
CSSWG issue discussing this.

Bug: 40944859
Change-Id: Ib93d0ffe41a8c538c8a4c9a13838bc304c26b30e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5147580
Reviewed-by: Koji Ishii <kojii@chromium.org>
Commit-Queue: Andreu Botella <abotella@igalia.com>
Cr-Commit-Position: refs/heads/main@{#1273823}
ChunMinChang pushed a commit to ChunMinChang/gecko-dev that referenced this issue Mar 25, 2024
…end on the item's style, a=testonly

Automatic update from web-platform-tests
Make trailing space hanging behavior depend on the item's style

The CL https://crrev.com/c/4881393 changed the hanging space behavior
to not depend on `text-align`, and it updated the implementation to
match the spec text. However, that patch had a misunderstanding of the
spec that made each line's hanging space behavior depend on its block
container's styles, rather than on each space's inline item styles.
That caused incompatibilities with other browsers.

This patch makes this behavior dependent on the inline item styles,
thus fixing the main source of incompatibilities.

The spec text is not yet fully clear on the behavior of some edge
cases, which is why the WPT tests this CL adds are marked as
tentative. See w3c/csswg-drafts#9724 for the
CSSWG issue discussing this.

Bug: 40944859
Change-Id: Ib93d0ffe41a8c538c8a4c9a13838bc304c26b30e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5147580
Reviewed-by: Koji Ishii <kojii@chromium.org>
Commit-Queue: Andreu Botella <abotella@igalia.com>
Cr-Commit-Position: refs/heads/main@{#1273823}

--

wpt-commits: 5f46e7c41541e9f2589ddf79177f113e98944427
wpt-pr: 45105
@frivoal
Copy link
Collaborator

frivoal commented Sep 2, 2024

I agree with the above logic, as well as with the tests introduced in web-platform-tests/wpt#44384.

@fantasai, can you have a look too and see if you agree?

As for the spec

  • I believe that hanging things followed by non hanging things doesn't need clarification. If they're not at the end of the line, they don't hang, that seems covered.
  • We might want to clarify the cases where we have both conditionally and unconditionally hanging things at the end of the line. The way I described it and that @andreubotella wrote tests for seems sound to me, but whether it is fully supported by the spec, I'm less sure. We could clarify by adding the following to the last paragraph of 9.2:

    When (unconditionally) hanging glyphs at the end of the line are immediately preceded by conditionally hangable glyphs, the condition for whether to hang those glyphs is evaluated as usual, as if the unconditionally hanging glyphs were not there. Conversely When conditionally hangable glyphs at the end of the line are immediately preceded by (unconditionally) hangable glyphs, the unconditionally hangable glyphs hang if and only if all subsequent conditionally hangable glyphs do indeed hang; otherwise, the (unconditionally) hangable glyphs are effectively not at the end of the line and do not get to hang.

An alternative interpretation for the last case could be that any sequence of unconditionally hangable glyphs followed conditionally hangable glyphs becomes conditionally hangable as well. Honestly, while I can construct cases where this happens, I'm having a hard time thinking realistic cases where this would matter.

@frivoal frivoal added the Agenda+ label Sep 2, 2024
@astearns astearns moved this to TPAC/FTF agenda items in CSSWG Agenda TPAC 2024 Sep 13, 2024
@astearns astearns moved this from TPAC/FTF agenda items to Regular agenda items in CSSWG Agenda TPAC 2024 Sep 13, 2024
@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-text] What does the `white-space-collapse` apply to when white-space trimming/positioning.

The full IRC log of that discussion <emilio> andreubotella: we have hanging and conditionally-hanging glyphs
<emilio> ... you can only hang at the end of a line
<emilio> ... and you can have unconditional hanging spaces before hanging spaces
<emilio> ... in which case if you grow the size of the line box there'd be a point where you reach the end of the conditionally hanging spaces and then all the spaces hang
<emilio> ... I have a demo in the issue
<emilio> andreubotella: [screenshares https://github.com//issues/9724#issuecomment-1867809865]
<emilio> ... question is are we fine with that behavior?
<emilio> florian: this can happen with different white-space-collapse at the end of the line
<emilio> ... can't think of when this would happen in real content
<emilio> ... another alternative would be that unconditionally-hanging followed by conditionally-hanging spaces all behave as conditionally-hanging
<emilio> ... I don't think this matters in practice
<emilio> ... the demo makes it the more obvious interpretation of the spec
<emilio> ... I think we could do that
<emilio> q+
<emilio> ack bramus
<astearns> I think I prefer the proposal to propagating conditionality
<emilio> andreubotella: the reason this would be rare in practice is that in order to have unconditional hanging spaces _before_ conditional you need to have combination of these values and characters such as ideographic spaces
<emilio> florian: right, just a mix of ideographic and regular spaces won't cause this, you also need different white-space-collapse values
<TabAtkins> emilio: Unless this really matters for some use-case, I think this is fine
<TabAtkins> emilio: I'd be a bit skeptical of introducing this kind of "how you hang depends on how things after you hang" that Florian mentioned
<TabAtkins> emilio: so unless taht behavior is really important for some realistic use-case, I thinkw hat you suggest is fine
<astearns> ack emilio
<astearns> ack fantasai
<emilio> fantasai: I feel the second definition florian gave seemed simpler?
<emilio> fantasai: so I lean towards that
<emilio> ... but I agree it doesn't really matter in practice, so whatever is more straight-forward
<emilio> andreubotella: yeah I guess whether you hang at all depends on what's after or how you hang seems fine
<emilio> florian: what's harder to implement
<emilio> andreubotella: don't know off the top of my head
<emilio> fantasai: can ask internally
<emilio> ... agree to whatever is simpler
<emilio> astearns: So we can resolve either with whatever is simpler or with no change
<emilio> fantasai: can we put option a and option b together and just ask for impl feedback?
<emilio> florian: last comment in the issue does contain that but can clarify
<emilio> fantasai: let's get those options together and poll at the end of the F2F

@frivoal
Copy link
Collaborator

frivoal commented Jan 22, 2025

Option a:

When conditionally hangable glyphs at the end of the line are immediately preceded by (unconditionally) hangable glyphs, the unconditionally hangable glyphs hang if and only if all subsequent conditionally hangable glyphs do indeed hang; otherwise, the (unconditionally) hangable glyphs are effectively not at the end of the line and do not get to hang.

Option b:

When conditionally hangable glyphs at the end of the line are immediately preceded by (unconditionally) hangable glyphs, the unconditionally hangable glyphs are treated as if they were conditionally hangable.

@astearns
Copy link
Member

Since we do not expect this is a case where authors will use and/or expect any particular behavior, the question is which of the options is easier to implement

@andreubotella
Copy link
Member Author

In Chromium, option B would only be very slightly harder to implement than option A, to the point where it doesn't really make any meaningful difference. But it might be possible that other browsers implement this differently.

@astearns astearns moved this to FTF agenda items in CSSWG January 2025 meeting Jan 22, 2025
@astearns astearns moved this from FTF agenda items to Regular agenda items in CSSWG January 2025 meeting Jan 22, 2025
@astearns astearns moved this from Regular agenda items to Friday afternoon in CSSWG January 2025 meeting Jan 24, 2025
@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-text] What does the `white-space-collapse` apply to when white-space trimming/positioning, and agreed to the following:

  • RESOLVED: Option A, but will revisit if impl feedback disagrees
The full IRC log of that discussion <TabAtkins> florian: we were discussing two options for what happens if, at the end of a line, you have a combo of conditionally hangeable glyphs and uncondtionally hangeable glyphs
<TabAtkins> florian: option a and b are both describ3ed in the issue
<TabAtkins> florian: [describes the options, read the issue for details]
<TabAtkins> florian: last time we decided it deosn't matter, do whatever's simpler
<TabAtkins> florian: we have feedback from andrew, they say B is slighty harder than A in chromium, but only slightly
<TabAtkins> florian: anyone else have opinions?
<TabAtkins> fantasai: i pinged our impl in Slack, we'll see if they come back with a response
<TabAtkins> fantasai: (forgot to do that last week)
<TabAtkins> emilio: no gecko feedback off the top of my head, gotta check with Jonathan
<TabAtkins> florian: in order to push should we conditionally resolve unless someone objects? or re-reschedule for two weeks and come back?
<TabAtkins> fantasai: pings are in flight
<TabAtkins> astearns: pings should have happened last time we discussed, so i'm inclined to resolve on option A and come back if people do objec t
<TabAtkins> emilio: i think they're both implementable
<TabAtkins> emilio: unlikely to matter
<TabAtkins> astearns: proposed resolution: option a
<TabAtkins> astearns: objections?
<TabAtkins> RESOLVED: Option A, but will revisit if impl feedback disagrees

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Regular agenda items
Status: Friday afternoon
Development

No branches or pull requests

4 participants