Skip to content
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

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

Closed
emilio opened this issue Jun 21, 2017 · 15 comments
Assignees
Labels

Comments

@emilio
Copy link
Collaborator

emilio commented Jun 21, 2017

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.

@emilio
Copy link
Collaborator Author

emilio commented Jun 21, 2017

cc @bzbarsky

@bzbarsky
Copy link

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.

@emilio
Copy link
Collaborator Author

emilio commented Jun 21, 2017

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.

@birtles
Copy link
Contributor

birtles commented Jun 22, 2017

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.

@FremyCompany
Copy link
Contributor

FremyCompany commented Feb 6, 2018

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?

@bzbarsky
Copy link

bzbarsky commented Feb 6, 2018

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.

@emilio
Copy link
Collaborator Author

emilio commented Feb 6, 2018

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?

@FremyCompany
Copy link
Contributor

FremyCompany commented Feb 7, 2018

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.

@emilio
Copy link
Collaborator Author

emilio commented Feb 7, 2018

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.

@bzbarsky
Copy link

bzbarsky commented Feb 7, 2018

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).

@FremyCompany
Copy link
Contributor

FremyCompany commented Feb 7, 2018

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.

@bzbarsky
Copy link

bzbarsky commented Feb 7, 2018

Applying the stylesheets of one document on another document doesn't seem relevant

It's what https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle explicitly requires, note.... (In the case of Firefox there's an implementation constraint that a display:none iframe doesn't have the things that are needed to compute styles at all, so we do the next-best thing, because not computing styles at all here would fail for web compat.)

@FremyCompany
Copy link
Contributor

Applying the stylesheets of one document on another document doesn't seem relevant

It's what https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle explicitly requires, note....

🙄

@emilio
Copy link
Collaborator Author

emilio commented Mar 28, 2018

One slightly annoying thing of having to expose styles for elements not connected to the document is that you need to update styles for shadow roots whose host is outside of the document, which is slightly annoying.

https://bugzilla.mozilla.org/show_bug.cgi?id=1449243 is an assertion failure resulting from me forgetting to do that :)

moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this issue Mar 28, 2018
…y default. r=bz

Now that NODE_FORCE_XBL_BINDINGS is gone, the only reason OwnerDoc() was added
in bug 1432490 is also gone.

Let's go back to using the composed doc, at least until the CSSWG decides on
what's the computed style of something that's not in[1].

[1]: w3c/csswg-drafts#1548

MozReview-Commit-ID: EV1c46kkUva

--HG--
extra : rebase_source : 3cd036519b90b42cb920d8f854d530c8ced6ea9c
@css-meeting-bot
Copy link
Member

The Working Group just discussed Clarify behavior of window.getComputedStyle on detached subtrees, and the definition of "the root element", and agreed to the following resolutions:

  • RESOLVED: For elements not part of a tree or part of a detached tree they return no computed styles
  • RESOLVED: Elements not part of a flat tree return no style for getComputedStyle
  • RESOLVED: Regardless of what window you obtain the gCS function from the origin of the style is where ever the element is
  • RESOLVED: getComputedStyle returns none for elements part of non-rendered iframes
The full IRC log of that discussion <dael> Topic: Clarify behavior of window.getComputedStyle on detached subtrees, and the definition of "the root element"
<dael> github: https://github.com//issues/1548
<dael> emilio: While I was doing the styling stuff it became unclear and it became obvious that each browser does something different.
<dael> emilio: FF does if you go across document it's sometimes across document, sometimes the ones attached to that doc. We should decide how it should work.
<dael> Rossen: In your issue it's detached sub trees.
<dael> emilio: Spec says whenever you call window.getComputedStyle on a document it says you should only get values in that window.
<dael> TabAtkins: I'd rather say they don't have a computedStyle.
<dael> emilio: Yes, you can't do it on an iframe. If you break that the web breaks. There's 3 cases. Element outside doc, computed stylle cross document.
<dael> TabAtkins: Also shadow dom. If it's detached no style. If it's in an iframe that happens to be detached we can discuss. The others have no style.
<dael> TabAtkins: The flat tree case there's nothing to inherit from.
<dael> rune_: But for not slotted and fallback for slotted?
<dael> TabAtkins: Yeah.
<dael> rune_: I don't know what is a sensible choice.
<dael> TabAtkins: String param.
<dael> fremy: For us it's work. Anything that's not tree I think it's reasoable to say don't render a style. All the use cases I have are internal and we don't need the API. For the API elements outside the tree can return no style. Still want it to work in documents. Spec requests if you call getComputedStyle from one window to another you have to get the values from the other window.
<dael> TabAtkins: spec bug
<dael> fremy: We don't want this. If we try and run this we get the wrong docuemnt. if you call getComputedStyle you get the values for the document of the element for which you called.
<dael> TabAtkins: Agree.
<dael> Rossen: Separate issue, though. Back to this one. What do we return on elements not part of any document?
<dael> Rossen: In Edge we go threw pains to try and calc what it should have been and it's a pain. We'd rather return some stock style with initial values or not return anything.
<dael> emilio: Makes sense. I think we can switch FF for non-iframes because we have archetectur that prevents us from this for iframes.
<dael> TabAtkins: I'd like to know more why you think web compat on iframes
<dael> emilio: blink returns style of actual document. But you can't say there's no style.
<dael> dbaron: Gecko has had security bugs about getting confused about styles between doc
<dael> emilio: When we know we're in the mess we're now consistant.
<dael> TabAtkins: Boris mentions iframes. What's the next best thing.
<dael> emilio: computing style from the window.
<dael> TabAtkins Returning some styles is required for web compat.
<dael> Rossen: But a stock style with initials
<dael> emilio: Might not work.
<dael> emilio: Still talking corss document? Detached sub trees returning no style works.
<dael> TabAtkins: Detached element or one in a detached sub tree should return no computed styles
<dael> rune_: Boris mentioned...
<dael> Rossen: One at a time.
<dael> Rossen: For elements not part of a tree or part of a detached tree they return no computed styles
<dael> Rossen: Obj?
<dael> RESOLVED: For elements not part of a tree or part of a detached tree they return no computed styles
<dael> Rossen: next.
<astearns> (and by no computed styles, we mean empty strings)
<dael> TabAtkins: An element not in the flat tree
<dael> emilio: Either return no style or return from the default.
<dael> Rossen: Reason to do that?
<dael> emilio: We don't ship shadow dom.
<dael> rune_: Blink does the same.
<dael> TabAtkins: Let's do it. No style.
<dael> Rossen: Elements not part of a flat tree return no style for getComputedStyle
<dael> emilio: You sure blink does that?
<dael> rune_: Not in the flat tree you get parent value which is null.
<dael> rune_: If not we should fix it.
<dael> Rossen: Obj?
<dael> RESOLVED: Elements not part of a flat tree return no style for getComputedStyle
<dael> TabAtkins: calling gCS from one window on an element from another document should return the element's normal styles
<dael> TabAtkins: If we decide later we needed it we can change.
<dael> fremy: That's what we do.
<dael> TabAtkins: Regardless of what window you obtain the gCS function from the origin of the style is whereever the element is
<dael> Rossen: Obj?
<dael> RESOLVED: Regardless of what window you obtain the gCS function from the origin of the style is where ever the element is
<dael> TabAtkins: Next is a display:none or detached iframe or elements inside of it have a window they can hit and a styling context, but are not being rendered. In this case we have to return something, we can return all initial.
<dael> Rossen: I think last time we tried this someone...FF or Blink...returned stock style
<dael> emilio: I suspect returning stock style is not going to be web compat. jQuery used to create display:none iframe and point computed style there and it wants to know the default style for a div.
<dael> fremy: I think no change.
<dael> emilio: There's a question of how MQ are resolved.
<dael> TabAtkins: All MQ resolve.
<dael> fremy: I don't remember what we do. I don't think it matters for compat.
<TabAtkins> s/resolve/are false/
<dael> smfr: I have this memory that browsers are going up the render tree for a display:none iframe. Don't ads create them?
<dael> emilio: gecko doesn't do anything like that. I think blink doesn't create render trees.
<dael> smfr: Yeah, we created some optimizations.
<dael> ??: We've gone back and forth. We've tried to create it, but we've gone back and forth.
<emilio> s/??/eae/
<dael> smfr: getComputed Style may have a [missed]
<smfr> s/[missed]/created a render tree
<dael> eae: WE don't do that today and haven't had a reason to revert that decision.
<dael> smfr: I'm not objecting, but I thik there's compat. Long term I think we all agree.
<dael> Rossen: We've held the display:none hammer for a long time.
<dael> Rossen: If you guys are doing it then it should be web compat.
<dael> eae: Too early to tell.
<dael> Rossen: If we spec it would be that display:none iframes return nothing?
<dael> eae: Yeah.
<dael> emilio: iframes that are not in the document? I guess the have no style.
<dael> Rossen: They shouldn't.
<dael> emilio: So display:none iframes and iframes not rendered in the document.
<dael> Rossen: They're elements that are not part of anything. That they're iframes doesn't make them less of an element.
<dael> Rossen: I think what you raised is for the subtrees inside iframes what do we do.
<dael> emilio: FF has an API that we added for jQuery. We tried to remove it and failed because calling getComputedStyle on an iframe that's not rendered.
<dael> Rossen: So there is a compat issue.
<dael> emilio: I suspect it's still the case.
<dael> Rossen: Proposal?
<dael> emilio: I'd love to say we don't return style but I don't thinkt hat's reaslistic. I think all browsers return.
<dael> Rossen: We do. We go through serious pains to compute styles in all situations and we want out. If there's a compat reason for jQuery
<dael> emilio: It's an old version of jQuery.
<dael> Rossen: Plenty uses older jQuery, though.
<dael> emilio: If someone wants to try not returning style on iframes and see what it does that would be nice.
<dael> eae: I don't think we've shipped not returning styles
<dael> Rossen: From behavior PoV returning none makes most sense. We can try and spec it and based on impl feedback we might come back and say compat blocks this and we need to revisit.
<dael> emilio: And if we do we have to define MQs. If people are willing to try this works.
<dael> Rossen: Prop: getComputedStyle returns no result for elements part of display:none or detached iframe
<dael> emilio: non-rendered type iframes
<dael> Rossen: Prop: getComputedStyle returns no result for elements part of non-rendered iframes
<dael> RESOLVED: getComputedStyle returns none for elements part of non-rendered iframes
<dael> Rossen: Anything else o nthis issue?

emilio added a commit to emilio/web-platform-tests that referenced this issue Apr 11, 2018
emilio added a commit to emilio/web-platform-tests that referenced this issue Apr 11, 2018
getComputedStyle returns styles in the node's document per the resolution in
w3c/csswg-drafts#1548.
emilio added a commit to emilio/web-platform-tests that referenced this issue Apr 11, 2018
emilio added a commit to emilio/web-platform-tests that referenced this issue Apr 11, 2018
getComputedStyle returns styles in the node's document per the resolution in
w3c/csswg-drafts#1548.
emilio added a commit to emilio/web-platform-tests that referenced this issue Apr 13, 2018
emilio added a commit to emilio/web-platform-tests that referenced this issue Apr 13, 2018
getComputedStyle returns styles in the node's document per the resolution in
w3c/csswg-drafts#1548.
emilio added a commit to emilio/csswg-drafts that referenced this issue Apr 13, 2018
emilio added a commit to emilio/csswg-drafts that referenced this issue Apr 13, 2018
@emilio emilio mentioned this issue Apr 16, 2018
@fantasai fantasai removed the ready label Apr 20, 2018
@w3c w3c deleted a comment from css-meeting-bot Apr 20, 2018
fergald pushed a commit to fergald/csswg-drafts that referenced this issue May 7, 2018
* [cssom] Make elements outside of the flat tree or detached not expose styles.

Fixes w3c#1548

* [cssom] Let elements in non-rendered iframes not expose styles.

Additional resolution in w3c#1548.

* fixup! [cssom] Make elements outside of the flat tree or detached not expose styles.

* fixup! [cssom] Let elements in non-rendered iframes not expose styles.
tabatkins added a commit that referenced this issue Jan 13, 2021
…the flat tree do not have any CSS values at all. #1964 #1548
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants