WICG / portals

A proposal for enabling seamless navigations between sites or pages
https://wicg.github.io/portals/
Other
947 stars 66 forks source link

Accepting multipart/form-data in addition to src URL #199

Open kjacobson opened 4 years ago

kjacobson commented 4 years ago

Premises

Thesis To further advance this work, I think it's crucial to be able to provide similar transition behavior when the loading and rendering of a new HTML document comes as the result of a form submission, rather than a link click.

A functioning server-rendered web application will have to be able to handle HTTP requests with multipart form-data encoding and respond with new HTML documents, e.g.:

These experiences won't get the transition advantage portals provide without writing a bunch of JavaScript to do things the browser does natively. Again:

So it's not that there is no way whatsoever in the current spec to animate the appearance of a portal which displays the HTML document provided by the server as the response body of a form submission HTTP request. It's that to do so given the current spec would require writing a lot of JS to do things the browser is already pretty good at doing.

It is true that there's no way to get the preload behavior, as form submissions require user input and are not therefore primed for a static request.

Possible solutions (for discussion at least) You currently can set portal src programmatically: portal.src = 'https://en.wikipedia.org/wiki/World_Wide_Web'

Portals could support programmatically setting action (instead of src) to a URL and setting formData to a FormData object and handling submission from there. This still means we have to write some JS to serialize the form, but new FormData(document.getElementById('myForm')) is not bad.

Alternately, you could be able to reference a form element directly from the portal with a for attribute (like the label HTML element has). Basically, portals for forms and portals for links. <form-portal for="myForm">

It seems this should probably be limited to same-site form actions.

jeremyroman commented 4 years ago

Firstly, thank you for your well-argued issue. :)

I think there are a few ways this could be achieved.

  1. Allowing explicitly navigating a portal through requests other than a plain GET of a URL, e.g. with FormData as you suggest, or with a fetch Request which can include a POST body.
  2. The advanced version of #192, where a Request could be supplied to create a portal that must immediately navigate.
  3. @jakearchibald's idea for a navigate event that would let you initiate the form submission normally, and send the in-flight navigation request into an (ephemeral as in #192) <portal> element.

As for whether it should be limited to same-site form actions, I'd have to look at how form submission is controlled and whether we want same-origin (or same-site) restrictions in addition to the usual XSRF protections that are the classic if awkward solution.

Another point in favour of this, of course, is that forms can currently target the main frame and iframe (<form target=...>) and this capability would be missing from portals.

I think this is a reasonable enhancement that could eventually be explored, but I'm less certain that it's necessary to include in an initial/minimal version of this feature, especially since workarounds are available if inconvenient (e.g. using fetch to do a post in advance of loading the document via a GET, or first loading a stub document that receives a message and does a form submission within the portal). But I do agree that it is unnecessarily duplicative of logic that the UA already has well in hand.

jeremyroman commented 4 years ago

(The request approach runs into the fact that it's currently illegal to construct a Request whose mode is "navigate", though it's conceptually the best existing class to map to the concept of "an arbitrary navigation request, be it GET, POST, etc".)

jakearchibald commented 4 years ago

We'd either end up accepting Request objects and thoroughly vetting them, or introduce some new kind of API.

We don't want to create a type of navigation that can't already happen, so we'd be restricting the content type to text/plain, application/x-www-form-urlencoded, or form/multipart, and restricting the method to GET or POST.

Given the restrictions, I'm leaning towards a new API.

portal.navigate(url, {
  method, // GET or POST, defaults to GET.
  body, // Optional. Either a FormData object, or a URLSearchParams object.
});

As with the Request constructor, FormData would set the content type to form/multipart, and URLSearchParams would set the content type to application/x-www-form-urlencoded. We'd lose text/plain, but is that really a problem?

createPortalForNavigation could have the same signature.

domenic commented 4 years ago

One of the major complicating factors in the iframes spec and implementation architecture is how it allows navigating to various non-HTTP(S) URLs, such as data:, blob:, javascript:, about:blank, as well as the srcdoc="" feature. I am hopeful we can find a way to achieve the use cases mentioned in this issue without introducing a way for portals to navigate things that are not backed by a HTTP(S) URL.

kjacobson commented 4 years ago

Thanks for the responses. Right, I suppose that, so long as it's possible to create a JS-abetted workaround, a webapp author can maintain continuity of UX, and that's enough for a first go. What I'm thinking one doesn't want is an MPA where link clicks have slick portal transitions and form submissions give you a flash-of-blank experience.

Jake's suggestion seems like pretty good ergonomics to me:

document.getElementById('myForm').addEventListener('submit', (e) => {
  e.preventDefault()

  const portal = document.createElement('portal')
  portal.navigate(e.target.action, {
    method: e.target.method,
    body: new FormData(e.target)
  })
})

I had forgotten about form targets though:

Another point in favour of this, of course, is that forms can currently target the main frame and iframe (<form target=...>) and this capability would be missing from portals.

That seems like a maximal solution in the sense of letting the developer rely on as much native user agent behavior as possible.