Skip to content

[cssom] [css-display] [css-values] Clarify behavior of window.getComputedStyle on detached subtrees, and the definition of "the root element". #1548

Closed

Description

@emilio

Consider the following test-case:

<!doctype html>
<style>
:root { font-size: 100px; }
</style>
<script>
let detached = document.createElement('div');
detached.style.display = "inline";
detached.style.fontSize = "1rem";
alert(getComputedStyle(detached).display);
alert(getComputedStyle(detached).fontSize);
</script>

The behavior across browsers is as follows:

  • Gecko: block, 16px.
  • Edge: inline, 16px
  • Gecko (with Servo's style system): inline, 100px.
  • Blink and WebKit: Empty string in both cases.

Per https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle, there isn't anything off-hand that justifies WebKit/Blink's behavior here.

Now, for the other interop problems the question is: what is the definition of "the root element"?

There are two spec sections that potentially apply here:

https://drafts.csswg.org/css-display/#transformations (1):

The root element’s display type is always blockified. Additionally, a display of contents computes to block on the root element.

and https://drafts.csswg.org/css-values/#rem (2):

Equal to the computed value of font-size on the root element. When specified on the font-size property of the root element, the rem units refer to the property’s initial value.

So the different behaviours are as follows:

  • Gecko takes the detached element as "the root element". For Gecko "the root element" is effectively the root of the inheritance subtree. That means that in Gecko, for example, rem units don't work as expected on the ::backdrop pseudo (since it inherits from nothing, and thus 1rem will always be the initial font-size).

  • Edge seems to apply that same definition for (2), but not for (1).

  • Servo uses the document element of the document for both sections of the spec (which I think it's consistent, and doesn't have the problem with ::backdrop).

  • Blink and WebKit seem to just not compute style if the element is outside of the document, which seems wrong per https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle.

So, two questions:

  • Is the behavior of Blink/WebKit justified by any reasonable interpretation of the spec, or is it just a browser bug? It sounds like the second to me, but worth confirming.

  • What is the definition of "the root element"?

Edge's behavior seems inconsistent here, but both of the Gecko or Servo's interpretation look consistent to me.

Gecko's behavior have the inconveniency of rem units on ::backdrop though. I haven't checked other browser's behaviour in depth with regards to rem units and ::backdrop. From code inspection, I think Blink at least uses the document element's font-size1.

Activity

  1. emilio commented on Jun 21, 2017

    @emilio
    CollaboratorAuthor
  2. bzbarsky commented on Jun 21, 2017

    @bzbarsky

    Servo uses the document element of the document

    Which document? The node document of the element passed to getComputedStyle, or the document the style is being computed in (that is, the document of the window which was the this for the getComputedStyle call)?

    outside of the document

    "The document" (which one, per above), or "a document" or "a document that has a browsing context"? I'm pretty sure they compute style for an element that is in its node document's document tree if that node document has a browsing context. Dunno what they do if you getComputedStyle on something in an XHR responseXML, say.

  3. emilio commented on Jun 21, 2017

    @emilio
    CollaboratorAuthor

    Which document? The node document of the element passed to getComputedStyle, or the document the style is being computed in (that is, the document of the window which was the this for the getComputedStyle call)?

    Err, sorry. I meant Stylo (Servo's style system). Servo itself apparently returns the empty string for disconnected nodes.

    So in the following test-case, with frame.html being:

    <!doctype html>
    <style>
      div { color: yellow }
    </style>

    and test.html being:

    <!doctype html>
    <style>
    :root { font-size: 100px; }
    div { color: green; }
    </style>
    <iframe src="./frame.html"></iframe>
    <script>
    let iframe = document.querySelector('iframe');
    iframe.onload = function() {
      let detached = document.createElement('div');
      alert(iframe.contentWindow.getComputedStyle(detached).color)
      alert(getComputedStyle(detached).color)
      document.body.appendChild(detached);
      document.body.offsetTop;
      alert(iframe.contentWindow.getComputedStyle(detached).color)
      alert(getComputedStyle(detached).color)
    }
    </script>

    Both Gecko and Stylo behave the same way, resolving against the document associated with the window getComputedStyle was called on if the element is not connected, and otherwise returning the value in document the node is connected to. (This seems somewhat inconsistent, btw).

    Not sure what does Edge do, I can't test it.

  4. birtles commented on Jun 22, 2017

    @birtles
    Contributor

    From a recent test I ran for the no browsing context case:

    const xdoc = xhr.responseXML;
    const div = xdoc.getElementById('test');
    div.style.opacity = '0';
    alert(getComputedStyle(div).opacity);

    We get '0' in Firefox and Edge, but null in Chrome.

    However, if instead of using the style attribute, we set style in a <style> element using a div { } selector in either the document we're calling from (i.e. window.document above) or the XHR doc and do the same we get '1' in Firefox and Edge, but null in Chrome.

  5. FremyCompany commented on Feb 6, 2018

    @FremyCompany
    Contributor
    Click here for the full-length explaination of Edge's behavior for font-size

    FWIW, Edge resolves the REM unit against the root of the current document (like Stylo does).

    The difference however is that root of the document is not the <html> element but a <::root> pseudo-element (think of it as the "document" node) which therefore is not affected by the style applied to the <html> element using the :root rule. We therefore do not need to prevent the html element from becoming inline, though I guess we maybe should to prevent interop issues (who would do that, though?).

    https://wptest.center/#/vur7k5 (test case showing an inline <html> in Edge)

    image

    That being said, I don't believe the real question here is the details of how things work for font-size with rem units; the real question is:

    Question 1

    Is it okay for getComputedStyle() to return an object that returns an empty string for each property value when an element is not part of the tree, or should you always return some form of default style?

    Question 2

    If you return some default style, how do you compute it?

    Current results

    https://wptest.center/#/2firn0 (only default styles)
    https://wptest.center/#/o41af0 (includes author rules)

    Edge:

    • returns the style resulting from:
      • applying the default stylesheet and inline styles, but
      • ignoring any author styles.

    Firefox:

    • returns the style resulting from:
      • applying all css rules and inline styles.

    Chrome and Safari:

    • return empty strings.

    Moving forward

    My personal point of view is that returning the default style is a very nice feature but, while Webkit's solution is lazy, it is also very simple to implement.

    We would really like Chrome to support computing styles on detached subtrees but if the Chrome team isn't interested in pursuing this, the easiest path to interop is for us to stop computing this default styles and return empty strings.

    I think Edge's behavior is interesting because you can use it to determine the minimum subset of inline styles you need to add to an element to create a proper copy/paste. You compute the styles of all the elements, then you clone then, get the computedStyles of the cloned root and add inline styles for anything that has changed, and continue to do so in dom order until you have processed all elements.

    I cannot imagine another use case for this on top of my head.

    Pinging @tabatkins -- what are your thoughts here?

  6. bzbarsky commented on Feb 6, 2018

    @bzbarsky

    when an element is not part of the tree

    Which tree? With shadow DOM this is not a trivial question.

    Further, in all cases the "tree" involved is that of the element's node document, I assume, not of the computed style object's window's document. And in that case, again, what happens when that node document is an XHR responseXML or equivalent.

  7. emilio commented on Feb 6, 2018

    @emilio
    CollaboratorAuthor

    Also, an element can be in the document tree but not be on the flat tree (#1964), like if it is an unassigned node of a shadow host. Where should you inherit from in that case?

  8. FremyCompany commented on Feb 7, 2018

    @FremyCompany
    Contributor

    In all cases the "tree" involved is that of the element's node document, I assume, not of the computed style object's window's document

    That makes sense to me. I have the feeling that an API like getComputedStyle should always return the same result independently of the realm you are calling from. It just doesn't make sense otherwise.

    This is another reason why you don't want to apply the document's stylesheets on nodes that are currently in a detached tree, because it yields inconsistent results depending on your current realm. Edge's behavior guarantees stable results for identical detached markups in different documents since we only apply inline styles and the default style on these elements.

    What happens when that node document is an XHR responseXML or equivalent?

    How does where you document comes from matter? In Edge at least, it is perfectly fine to interpret any xml document as a stylable document (xhtml by default). In fact, when you open an XML file in a tab, we render the XML file directly (though we add a script to append XHTML nodes to it so there is something more interesting to render than inline text).

    https://wptest.center/#/hl9yd1 (Computed styles in xml trees)

    Also, an element can be in the document tree but not be on the flat tree

    There is indeed also the question of what to do with nodes that do not have used styles. I don't think there should be special sub-categories inside that category; nodes that do not generate a box (whether it's because one of their ancestors is display:none, because they were moved into a region flow and overflowed beyond the last region box, or because they were under a web components and didn't get pulled back into its shadow tree) should return their computed style, and their computed style should follow the same rules as usual (inherit from its parent element, or host if it's a shadow root, etc...). If that is not the case right now in some browsers, I would file bugs on the browsers that don't comply with this. Everybody seems to be interoperable regarding nodes in display:none subtrees.

  9. emilio commented on Feb 7, 2018

    @emilio
    CollaboratorAuthor

    An element assigned to a slot doesn't inherit from the shadow host, but from the assigned slot. There's no spec that defines that in case it's not assigned to any slot it should inherit from the host, afaict.

  10. bzbarsky commented on Feb 7, 2018

    @bzbarsky

    I have the feeling that an API like getComputedStyle should always return the same result independently of the realm you are calling from.

    Not that this is neither what the current spec says nor what browsers currently do, last I checked... So while it would be nice, it's not obvious how web-compatible it is. :(

    and the default style on these elements

    Of course the "default" (UA + user) style can be different in different documents. For example, nothing prevents media queries in UA/user sheets. I know for a fact people use them in user sheets.

    How does where you document comes from matter

    Because it may not have an attached browsing context or other machinery that might be needed to compute styles. What is the viewport size of an XHR responseXML document and why, for example?

    The rest of what you say about XML is not relevant to my question: my question is specifically about documents that are not "in a browsing context".

    There is indeed also the question of what to do with nodes that do not have used styles.

    Things that are not in the flat tree don't even have a concept of computed styles, because inheritance is undefined for them.

    And it's not clear that they have a clearly defined concept of specified styles either.

    Everybody seems to be interoperable regarding nodes in display:none subtrees

    "seems" is key. For example, Gecko and WebKit/Blink don't agree on http://jsbin.com/xiluvogequ/edit?html,output (I haven't tested Edge so far).

  11. FremyCompany commented on Feb 7, 2018

    @FremyCompany
    Contributor

    An element assigned to a slot doesn't inherit from the shadow host, but from the assigned slot. There's no spec that defines that in case it's not assigned to any slot it should inherit from the host, afaict.

    Ah, that should probably be added then; more than likely it's just an oversight. Using their parent's style is the closest approximation of the behavior that would happen whenever it gets slotted, so that looks reasonable to me.


    I have the feeling that an API like getComputedStyle should always return the same result independently of the realm you are calling from.

    Not that this is neither what the current spec says nor what browsers currently do, last I checked

    I'm fairly sure Edge is doing that, or is at least pretty close from that.


    The rest of what you say about XML is not relevant to my question: my question is specifically about documents that are not "in a browsing context".

    The testcase I provided shows that, at least in Edge, that does not matter.

    I agree that we should probably define what to do with media queries in that case. In Edge, we seem to evaluate everything as if the viewport was 0px by 0px.

    https://wptest.center/#/zq8ym1 (Computed styles in xml markup, with media queries)


    Everybody seems to be interoperable regarding nodes in display:none subtrees

    "seems" is key. For example, Gecko and WebKit/Blink don't agree on http://jsbin.com/xiluvogequ/edit?html,output (I haven't tested Edge so far).

    http://jsbin.com/sitivicafo/1/edit?html,output (updated to work in Edge as we don't support srcdoc).

    We match Chrome and return the style you would get from applying the styles of the iframe.

    I will admit I'm really perplexed with Firefox's behavior here. Applying the stylesheets of one document on another document doesn't seem relevant, and seems more like a bug than a feature.

  12. 6 remaining items

  13. added 2 commits that reference this issue on Apr 13, 2018
    634893f
    2516640
  14. deleted a comment from css-meeting-bot on Apr 20, 2018
  15. added a commit that references this issue on May 7, 2018
    4d38085
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions