Design issues with the <portal> element
ojhunt opened this issue · comments
Ok, so it has been requested that I file a bug detailing the issues with and I don't currently have TAG access.
I recall having a few more issues, but can't recall what they were at this point.
So here goes:
Usability:
- The primary design goal is to allow "expanding an active frame to a standalone window". It is unclear how this should interact with the very common user desire to open what are functionally links in a separate tab or window. The specification does not appear to address this behavior. What happens in the context of the host window when a portal is lifted?
- There's a reference in the spec identifying the obvious problem of how back/forward should work. What happens when the portal window is closed?
- What happens when I refresh an expanded portal now that I can keep the original window open
- Users are increasingly expecting smooth (eg high frame rate) animation, yet is unclear how that is expected in the context of "animated" expansion of complex layout. Varying behaviour is generally bad UX.
API:
- By creating a separate element the API ends up duplicating a wide array of already specified behaviour. The spec already notes that it needs to specify how errors in postMessage (duplicated API) should be handled - this is already specified and well understood on iframe.
Security:
- There is a major problem in the security properties of - specifically <iframe>s have historically needed many changes to address security+privacy bugs that are discovered over time. By making a distinct element rather than an attribute/API on <iframe> we introduce a new spec that must be continuously updated to track the <iframe> specification. This is to my mind a fundamental flaw - many web facing specifications have been discovered to be attackable by existing/known flaws that have been "fixed", but are subsequently found to be duplicated in a separate spec.
- Now that an iframe can be arbitrarily opened we are introducing another clickjacking mechanism - specifically
Privacy:
- The need for referrer headings is generally considered a privacy flaw, and if we are willing to create an entirely new element we should not reproduce those errors. That means no referrer should be specified or provided in any way.
- How does this interact with various tracker blocking mechanisms? The design goal of any completely new element/API that allows network loads should be to actively prevent their abuse for tracking. Any tracking mechanisms is fundamentally user hostile.
- Historically opening a link doesn't maintain state from the original host environment, this API allows page state to be maintained - do we want to allow this? Now for a user to break the relationship between a "new" window and the origin window.
Thanks for the feedback. I'll try to address your points below.
Ok, so it has been requested that I file a bug detailing the issues with and I don't currently have TAG access.
I recall having a few more issues, but can't recall what they were at this point.
So here goes:
Usability:
- The primary design goal is to allow "expanding an active frame to a standalone window". It is unclear how this should interact with the very common user desire to open what are functionally links in a separate tab or window. The specification does not appear to address this behavior. What happens in the context of the host window when a portal is lifted?
Indeed, that's something we have yet to define. But I do agree, it does seem reasonable that a user may wish to open/activate a portal into a separate tab/window. If we do support that use case, I'd expect that we won't allow the adoption of the predecessor page as a portal. Does that sound reasonable?
- There's a reference in the spec identifying the obvious problem of how back/forward should work. What happens when the portal window is closed?
This is still being designed (see #19), but I think there are 2 options: either the browser itself activates the predecessor page, if available, when the user presses the back button, or the browser forces a reload, and thus discard the predecessor page.
- What happens when I refresh an expanded portal now that I can keep the original window open
In that case, the predecessor page would be discarded.
- Users are increasingly expecting smooth (eg high frame rate) animation, yet is unclear how that is expected in the context of "animated" expansion of complex layout. Varying behaviour is generally bad UX.
That's right -- layout is expensive, and in order to implement high quality transitions developers will need to avoid layout. This should be possible, and our hope is that frameworks will provide simple ways to automate these transitions.
API:
- By creating a separate element the API ends up duplicating a wide array of already specified behaviour. The spec already notes that it needs to specify how errors in postMessage (duplicated API) should be handled - this is already specified and well understood on iframe.
While what you said is true, and we will certainly need to duplicate some of the aspects that are already specified for iframes, we also want to simplify many of the aspects that makes iframes complicated. In particular, we want to avoid synchronous DOM access, which is a large source of complexity, in favor of a simple postMessage-based communication channel.
Security:
- There is a major problem in the security properties of - specifically <iframe>s have historically needed many changes to address security+privacy bugs that are discovered over time. By making a distinct element rather than an attribute/API on <iframe> we introduce a new spec that must be continuously updated to track the <iframe> specification. This is to my mind a fundamental flaw - many web facing specifications have been discovered to be attackable by existing/known flaws that have been "fixed", but are subsequently found to be duplicated in a separate spec.
That's right, this was one of the main motivations to propose a new element, instead of modifying iframe. We want to simplify the exposed API, and we want to do this by limiting what portals are allowed to do. We have been taking an approach that disables things we consider a security or privacy concern, in particular, synchronous DOM access, input event handling, opener/referrer, access to the frame tree. Specifying all of these as an iframe attribute would pose serious challenges, and would require a lot of monkey patching of the spec. That's why we want to start with a clean slate, and why we are proposing a new element.
- Now that an iframe can be arbitrarily opened we are introducing another clickjacking mechanism - specifically
Once a portal is activated, its origin should be in control of the entire tab, so at that point there shouldn't be any concern for clickjacking. On the other side, when a portal is embedded in another page, we should respect the frame-ancestors, frame-src CSPs, as well as X-Frame-Options headers. We are also not handling input on portals (at least initially), which means that only activated portals can receive input. That should mitigate the concern for clickjacking.
Privacy:
- The need for referrer headings is generally considered a privacy flaw, and if we are willing to create an entirely new element we should not reproduce those errors. That means no referrer should be specified or provided in any way.
This was one of our first considerations, and our proposal specifies that portals should not send the referrer. (see https://wicg.github.io/portals/#htmlportalelement-set-the-source-url-of-a-portal-element)
- How does this interact with various tracker blocking mechanisms? The design goal of any completely new element/API that allows network loads should be to actively prevent their abuse for tracking. Any tracking mechanisms is fundamentally user hostile.
We expect that portals should integrate well with the requestStorageAccess API, which would grant or deny storage access in order to prevent tracking.
- Historically opening a link doesn't maintain state from the original host environment, this API allows page state to be maintained - do we want to allow this? Now for a user to break the relationship between a "new" window and the origin window.
I'm not sure I agree that state isn't maintained while opening a link. It really depends on what you consider state. Today, opening a link in a new tab maintains the previous page's state, which can be accessed through the window.opener. pushState/popState maintains state that can be accessed through the back button or history APIs. Cookies and/or local storage can also contain state. BFCache maintains state upon navigation. I don't think keeping the previous page as a portal is fundamentally different than many of these aspects that already exist on the web today.
Thanks for the feedback. I'll try to address your points below.
- The primary design goal is to allow "expanding an active frame to a standalone window". ...
Indeed, that's something we have yet to define. But I do agree, it does seem reasonable that a user may wish to open/activate a portal into a separate tab/window. If we do support that use case, I'd expect that we won't allow the adoption of the predecessor page as a portal. Does that sound reasonable?
What I'm saying is that you must support that use case - sites that break this behaviour are user-hostile and it's a semi-frequent bug report to browsers when sites are able to break it. Not "supporting" this is breakage by design, which makes the spec user-hostile.
- There's a reference in the spec identifying the obvious problem of how back/forward should work. What happens when the portal window is closed?
This is still being designed (see #19), but I think there are 2 options: either the browser itself activates the predecessor page, if available, when the user presses the back button, or the browser forces a reload, and thus discard the predecessor page.
What do you mean by predecessor page? The original host page?
- What happens when I refresh an expanded portal now that I can keep the original window open
In that case, the predecessor page would be discarded.
Ditto.
- Users are increasingly expecting smooth (eg high frame rate) animation, yet is unclear how that is expected in the context of "animated" expansion of complex layout. Varying behaviour is generally bad UX.
That's right -- layout is expensive, and in order to implement high quality transitions developers will need to avoid layout. This should be possible, and our hope is that frameworks will provide simple ways to automate these transitions.
Ok, so I'm tired of "rely on designers to not ruin the user experience". Those inevitably result in bugs filed on browsers saying that the site is slow and blaming the browser. For anything iframe-like you have to consider what happens when the user - perfectly reasonably - triggers in page navigation.
API:
- By creating a separate element the API ends up duplicating a wide array of already specified behaviour. The spec already notes that it needs to specify how errors in postMessage (duplicated API) should be handled - this is already specified and well understood on iframe.
While what you said is true, and we will certainly need to duplicate some of the aspects that are already specified for iframes, we also want to simplify many of the aspects that makes iframes complicated. In particular, we want to avoid synchronous DOM access, which is a large source of complexity, in favor of a simple postMessage-based communication channel.
This is not a good argument, because this new element does not exist in a vacuum. It is adding a new API, and a differently behaving iframe-like object, both of which are count against the argument of "simplify".
Security:
- There is a major problem in the security properties of - specifically <iframe>s have historically needed many changes to address security+privacy bugs .
That's right, this was one of the main motivations to propose a new element, instead of modifying iframe.
I am unsure what to make of this, you're proposing a new element that has very similar semantics, and therefore will have overlapping classes of bugs.
We want to simplify the exposed API, and we want to do this by limiting what portals are allowed to do. We have been taking an approach that disables things we consider a security or privacy concern, in particular, synchronous DOM access, input event handling, opener/referrer, access to the frame tree. Specifying all of these as an iframe attribute would pose serious challenges, and would require a lot of monkey patching of the spec. That's why we want to start with a clean slate, and why we are proposing a new element.
An alternative would be to classify the frame as being separate origin, which would resolve (all of?) these issues. That would not require any monkey patching, nor the addition of much in the way of semantic modification. This would also solve the synchronous DOM "problem".
- Now that an iframe can be arbitrarily opened we are introducing another clickjacking mechanism - specifically
Once a portal is activated, its origin should be in control of the entire tab, so at that point there shouldn't be any concern for clickjacking. On the other side, when a portal is embedded in another page, we should respect the frame-ancestors, frame-src CSPs, as well as X-Frame-Options headers. We are also not handling input on portals (at least initially), which means that only activated portals can receive input. That should mitigate the concern for clickjacking.
Which puts us back in the duplicating API semantics problem.
Privacy:
- The need for referrer headings is generally considered a privacy flaw, and if we are willing to create an entirely new element we should not reproduce those errors. That means no referrer should be specified or provided in any way.
This was one of our first considerations, and our proposal specifies that portals should not send the referrer. (see https://wicg.github.io/portals/#htmlportalelement-set-the-source-url-of-a-portal-element)
There's a comment questioning whether it should be allowed through options, what I'm saying is that if you are intending to make a brand new element, it should not provide the referrer or anything else ever.
- How does this interact with various tracker blocking mechanisms? The design goal of any completely new element/API that allows network loads should be to actively prevent their abuse for tracking. Any tracking mechanisms is fundamentally user hostile.
We expect that portals should integrate well with the requestStorageAccess API, which would grant or deny storage access in order to prevent tracking.
Invisible iframes are used to track in a variety of ways (ignoring the general malsites that have "social" iframes which presumably site operators are willing to postMessage to).
- Historically opening a link doesn't maintain state from the original host environment, this API allows page state to be maintained - do we want to allow this? Now for a user to break the relationship between a "new" window and the origin window.
I'm not sure I agree that state isn't maintained while opening a link. It really depends on what you consider state. Today, opening a link in a new tab maintains the previous page's state, which can be accessed through the window.opener. pushState/popState maintains state that can be accessed through the back button or history APIs.
window.opener does not work cross origin. push/popState do not work across origin.
Cookies and/or local storage can also contain state.
Yes, but if you were - for instance - to isolate state from within linked pages from primary domain pages (essentially cookie partitioning applied to subframes), then you have a problem when the portal is opened to the main page.
BFCache maintains state upon navigation.
BFCache isn't exposed to a page.
I don't think keeping the previous page as a portal is fundamentally different than many of these aspects that already exist on the web today.
I am saying that a new tag should not have any of the existing flaws, because the only reason those flaws remain is because of legacy concerns.
What I'm saying is that you must support that use case - sites that break this behaviour are user-hostile and it's a semi-frequent bug report to browsers when sites are able to break it. Not "supporting" this is breakage by design, which makes the spec user-hostile.
Agreed. It's reasonable to support this use case. Do you have any suggestions on how this should be exposed to developers? Do you mind filing an issue for this feature?
This is still being designed (see #19), but I think there are 2 options: either the browser itself activates the predecessor page, if available, when the user presses the back button, or the browser forces a reload, and thus discard the predecessor page.
What do you mean by predecessor page? The original host page?
That's correct.
Ok, so I'm tired of "rely on designers to not ruin the user experience". Those inevitably result in bugs filed on browsers saying that the site is slow and blaming the browser. For anything iframe-like you have to consider what happens when the user - perfectly reasonably - triggers in page navigation.
As platform developers, we shouldn't stop ourselves from implementing powerful features just because bad developers may misuse them and create poor experiences. There are plenty of great developers that will be able to use these features to their limit, creating fantastic and innovative user experiences.
This is not a good argument, because this new element does not exist in a vacuum. It is adding a new API, and a differently behaving iframe-like object, both of which are count against the argument of "simplify".
If you look at the HTML spec, in particular the concepts of a browser context group and a browser context set, we want portals to be in their own browser context group. In this case, the portal wouldn't be associated with the hosts' group. This is one of the major simplifications that would be very hard to modify iframes to support.
An alternative would be to classify the frame as being separate origin, which would resolve (all of?) these issues. That would not require any monkey patching, nor the addition of much in the way of semantic modification. This would also solve the synchronous DOM "problem".
Classifying the frame as being separate origin would resolve synchronous DOM access, but it would not resolve any of the other points. Access to the frame tree (i.e. grandchildren and grandparents) would still be possible. Access to the opener would remain. Named access would remain.
Which puts us back in the duplicating API semantics problem.
Yes, like I said, there will be some level of duplication. But I think the simplifications coming out of the differentiation outweighs the complexity caused by the duplication.
There's a comment questioning whether it should be allowed through options, what I'm saying is that if you are intending to make a brand new element, it should not provide the referrer or anything else ever.
While I agree with you that disabling it by default is a good way forward, I'm not sure that not providing the referrer ever is the right way to go (I'm not convinced otherwise either). There are legitimate uses of the referrer header. What about first party use cases? What about search queries? The whole point of having this open discussion here is to shape the API in a way that can both solve the use cases we need to solve and that works in a way that's good for users and web developers. I'd suggest you comment on the other question with your point of view and to engage in the discussion.
Invisible iframes are used to track in a variety of ways (ignoring the general malsites that have "social" iframes which presumably site operators are willing to postMessage to).
Integrating with requestStorageAccess API, it means that these invisible frames won't have access to persistent storage. Wouldn't that be enough to prevent tracking?
window.opener does not work cross origin. push/popState do not work across origin.
window.opener does works cross-origin. That's why we have the noopener attribute, and the concept of disowning the opener.
pushState/popState doens't work cross origin, but first-party use cases are equally important in the portals proposal. We are seeing a lot of interest from developers in the first party use case, in order to implement multi-page apps with seamless transitions.
Yes, but if you were - for instance - to isolate state from within linked pages from primary domain pages (essentially cookie partitioning applied to subframes), then you have a problem when the portal is opened to the main page.
I don't think any browsers implement storage partitioning anymore.
BFCache isn't exposed to a page.
Yes, it is, through pageshow/pagehide events.
I am saying that a new tag should not have any of the existing flaws, because the only reason those flaws remain is because of legacy concerns.
That's exactly what we are trying to address, by engaging with the community, looking for constructive feedback and iterating on the proposal. At this point, nothing here is final, everything is subject to change.
I think the back and forth here has been constructive, and I'm unsure what remains to address. I'll note that we added more detail on portals vs. iframes in https://github.com/WICG/portals#summary-of-differences-between-portals-and-iframes.
I'll close this for now, but if there are specific issues remaining, we should open new issues to track them.