whatwg / html

HTML Standard
https://html.spec.whatwg.org/multipage/
Other
8.01k stars 2.62k forks source link

Shadow DOM and <iframe> #763

Open annevk opened 8 years ago

annevk commented 8 years ago

<iframe> works if its "shadow-host-including inclusive ancestor" is a document that has a browsing context. I.e., including when it does not end up in the flattened tree.

<iframe> has a couple of major pain points when used in a shadow tree:

Paging @hayatoito @smaug---- @rniwa.

TakayoshiKochi commented 8 years ago

For history API, let the discussion go in https://github.com/w3c/webcomponents/issues/184.

For named properties, do we agree that anything in shadow tree should not show up? I'd hope no one would object, but let us get a rough consensus.

Related, window.frames should not include <iframe>s in shadow trees.

For link targeting, I agree that target= should not cross boundaries.

For allowfullscreen, I'm not sure - is this discussed elsewhere for shadow root?

annevk commented 8 years ago

Yeah, the named properties and indexed properties should not point to anything in a shadow tree (note that window.frames === window, it's not really a collection). https://github.com/w3c/webcomponents/issues/180 is about the Fullscreen API in general. I think it makes sense for shadow trees to have the same capabilities as the documents they are in, so no need for allowfullscreen on shadow root. They could do the same through script after all.

TakayoshiKochi commented 8 years ago

Aha, okay, I have a bad memory - I commented in https://github.com/w3c/webcomponents/issues/180.

I'd like to confirm <base>'s behavior. As <base> is declared inert in shadow tree in the spec, <base> in shadow has no effect, but for one defined in document tree (in <head>),

annevk commented 8 years ago

I'm not quite sure what you mean by target.

TakayoshiKochi commented 8 years ago

I was assuming the following HTML:

<base target="myframe">
<a href="http://foo">foo</a>
<div>
  #shadow-root
     <a href="http://bar">bar</a>
     <iframe name="myframe"></iframe>
</div>

Clicking on "foo" will navigate the whole document to foo, and clicking on "bar" will also navigate the whole document to bar, both cases should not navigate inside the <iframe name="myframe"></iframe>.

annevk commented 8 years ago

That probably makes sense, yes.

domenic commented 7 years ago

Updated OP with an additional bullet about inertness. That one is easy to fix, at least.

rniwa commented 5 years ago

TPAC F2F note: target content attribute will be discussed along side with https://github.com/w3c/webcomponents/issues/179.

annevk commented 5 years ago

Tentative model: shadow roots have their own name bucket and any popups they end up opening should probably be restricted to that shadow root (would only work for <a target>).

mfreed7 commented 3 years ago

Quick comment here, as it came up from an internal bug, on the question of named properties on the Window object. It seems that this issue was roughly resolved in WICG/webcomponents#145, to not leak frames that are located within (open or closed) shadow roots. And is how the implementations work for WebKit and Chromium, but not Gecko. In Gecko, even frames located in closed shadow trees are exposed on the Window object, thereby leaking the entire closed tree. This WPT shows this result.

It seems that #1625 attempted to codify https://github.com/WICG/webcomponents/issues/145 this way, so the spec should (?) match the current behavior of WebKit/Chromium. I couldn't find a Mozilla bug, so I filed this one.

Let me know if I'm wrong about the above.

mfreed7 commented 3 years ago

Another followup comment on this issue. The fact that even open shadow roots hide any contained frames seems to preclude some natural use cases. Consider the example where sibling (cross-origin) iframes want to communicate with each other:

<div class="container">
  <iframe src="cross-origin1.html"></iframe>
  <iframe src="foo" name="frame2"></iframe>
</div>

with cross-origin1.html containing:

window.frames['frame2'].postMessage('bar');

This works great without Shadow DOM. But this completely breaks if anywhere in the ancestor chain, someone tries to use Shadow DOM:

<div id=host>
  <template shadowroot=open>
    <div class="container">
      <iframe src="cross-origin1.html"></iframe>
      <iframe src="foo" name="frame2"></iframe>
    </div>
  </template>
</div>

In this case, the two frames can no longer communicate with each other. Is there any other mechanism by which they can get window references to each other? They are contained within the same shadow tree here, so it would seem that they should be able to somehow communicate. This is a real use case from an internal group, so this isn't hypothetical. Is there any guidance on how to do this? @annevk @rniwa @domenic

Again, I can see why we probably don't want this to work for a closed shadow root, but even in that case it's a bit weird that iframes contained within the same shadow tree can't talk to each other.

P.S. I wasn't sure whether to post this here, or on https://github.com/WICG/webcomponents/issues/145, but that one is closed so I went with this one.

domenic commented 3 years ago

In this case, the two frames can no longer communicate with each other. Is there any other mechanism by which they can get window references to each other?

For sure. They can do

document.querySelector("#host").shadowRoot.querySelector("iframe").contentWindow.postMessage('bar');

(I think I did my shadow-tree navigation right...)

mfreed7 commented 3 years ago

In this case, the two frames can no longer communicate with each other. Is there any other mechanism by which they can get window references to each other?

For sure. They can do

document.querySelector("#host").shadowRoot.querySelector("iframe").contentWindow.postMessage('bar');

(I think I did my shadow-tree navigation right...)

But the <iframe> is cross origin...

domenic commented 3 years ago

That doesn't impact things.

mfreed7 commented 3 years ago

Perhaps I don't understand. From cross-origin.html, document is just the iframe document, so querySelector("#host") returns null. In this example, frame1 is trying to talk to frame2.

domenic commented 3 years ago

Hmm, then the original example doesn't work. window.frames['x'] does not contain your parent's child frames. It contains your own child frames.

In general, window.frames['x'] is legacy syntax for document.querySelector("#x")?.contentWindow || document.querySelector("[name=x]")?.contentWindow.

mfreed7 commented 3 years ago

Sorry, that's a typo in my original post: cross-origin1.html should include .parent:

window.parent.frames['frame2'].postMessage('bar');

This example does work, and is broken by shadow dom.

domenic commented 3 years ago

I see. Yeah, I guess that is broken. I tend to think that's OK because named access in such a fashion is a legacy feature, so when designing new features like shadow DOM we try to restrict such legacy features so as to make the design space manageable. (The proper pattern would be to ask the parent to message the child by postMessage()ing the parent, or use BroadcastChannel or similar.) But I'll let others chime in.

rniwa commented 3 years ago

That is definitely intentional. We don't want iframe inside a shadow tree to be exposed on the global scope such that other scripts can stumble upon them.

mfreed7 commented 3 years ago

Thanks for the feedback here. I can see your points. The reason it (still?) feels a bit odd to me is that otherwise-working behavior is broken when the entire tree is placed into shadow dom. But I suppose the message is that this form of sibling-to-sibling direct frame communication, via the global window.frames, isn't great anyway, and there's a downside to exposing shadow-hidden frames just to fix this use case. I'm ok with that, and I'll feed that back to the requesting team.

annevk commented 3 years ago

Indeed, if we designed nested browsing contexts today they'd have far less side channels such as that one.

itayadler commented 2 years ago

In Firefox this isn't the behavior, window.frames include iframes inside ShadowDOM.

mfreed7 commented 2 years ago

In Firefox this isn't the behavior, window.frames include iframes inside ShadowDOM.

Right - that’s this bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1695969