w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.46k stars 657 forks source link

[css-content-3] target-text can create a loop #4610

Open faceless2 opened 4 years ago

faceless2 commented 4 years ago

This seems like it could lead to very bad things:

<style>
#a::before {
    content: target-text("b", before);
}
#b::before {
    content: target-text("a", before);
}
</style>
<div id="a"></div>
<div id="b"></div>

It might be worth highlighting this risk and clarifying behaviour, for example this edit to https://drafts.csswg.org/css-content-3/#target-text-function

A target-text() function that forms a loop by referencing itself or that cannot otherwise be resolved, will evaluate to the empty string.

In the same edit, issue 15 appears to have been resolved and can be deleted (target-text can already take a string as the first argument)

Loirooriol commented 4 years ago

https://drafts.csswg.org/css-content-3/#target-text says

The target-text() function retrieves the text value of the element referred to by the URL.

You seem to be assuming that this "text value" includes the text in ::before, but the term doesn't seem to be defined anywhere, right? I guess it could mean something like https://dom.spec.whatwg.org/#concept-descendant-text-content, in your example it would be the empty string.

faceless2 commented 4 years ago

That's correct, yes. It is defined as that, and implemented this way in at least AH Formatter, RealObjects PDFReactor and I believe also "paged.js" - although as the definitions of these functions have been hopping about from css-content, css-gcpm and css-lists for the last few years it's a bit difficult to keep track.

The second parameter to target-text is defined in css-content as "... using the same values as the string-set property..." - which is defined in gcpm: https://drafts.csswg.org/css-gcpm/#funcdef-content

before = The string value of the ::before pseudo-element, determined as if white-space: normal had been set.

To lift an examples from that spec demonstrating string-set:

h1::before { content: 'Chapter ' counter(chapter); }
h1 { string-set: header content(before) ':' content(text); }
h1::after { content: '.'; }

The value of the named string “header” will be “Chapter 1: Loomings”.

Edit: on rereading your coment I need to clarify my response.

target-text(nnn) or target-text(nnn, content) will retrieve the text value of the element referred to by the URL - exactly as you state, the value would be the empty string in the above examples. Because the text content of an element cannot be replaced with generated content, this cannot create a loop.

The situation I'm referring to is specifically where the second argument to target-text is "before" or "after". In this case you have generated content potentially referring to other generated content, which is why a loop is possible.