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

Allow portal elements to be re-used across navigations on same-origin sites. #276

Open
michalczaplinski opened this issue Nov 4, 2021 · 2 comments

Comments

@michalczaplinski
Copy link

There is currently no way for the portals created in the embedder page to be re-used in the portaled page. This can be wasteful if both an embedder and the portaled page have multiple portals pointing to the same URLs. In this case, when one of the portals on the embedder page is activated and navigated into, the portaled page would create the same portals as have already been created on the embedder page.

To make this case a bit more clear let's consider a small app like:

image

And the code that would roughly represents the wireframe from above:

<body>
  <nav>
    <ul>
      <li><a href="/products">Products</a></li>
      <li><a href="/blog">Blog</a></li>
      <li><a href="/about-us">About Us </a></li>
    </ul>
  </nav>
  <main>
    <!-- some content... -->
  </main>
</body>
function createPortals() {
	const pageLinks = document.querySelectorAll('nav ul a');

	for ( const el of pageLinks ) {
	    // don't create a portal for link to current page.
		if ( el.href === window.location.href ) continue;

		const portal = document.createElement( 'portal' );
		portal.src = el.href;
		portal.hidden = true;

		document.body.append( portal );

		el.onclick = async ( ev ) => {
			ev.preventDefault();
			portal.hidden = false;

			try {
				await portal.activate();
			} catch {
				portal.hidden = true;
				window.location.href = el.href;
			}
		};
	}
}

if ( ! window.portalHost ) {
	window.addEventListener( 'DOMContentLoaded', () => {
		createPortals();
	} );
} else {
	// Wait for the `portalactivate` event to avoid infinite loop
	window.addEventListener( 'portalactivate', () => {
		createPortals();
	} );
}

Let's say that we're on the /products page. The page has created a portal for /blog and /about-us. The user clicks on the /blog link and the portal for /blog is be activated. Now, the portals for /product and /about-us will have to be created again on the page for /blog because the new page is isolated from the embedder.

In practice, this might limit the usefulness of portals in use cases where SPAs currently provide a superior user experience. One such example would be logged-in applications (facebook.com, gmail.com, etc.)

With this in mind, has there been any discussion about creating a mechanism for re-using the portals when navigating to a same-origin page?

Proposal

As mentioned in https://github.com/WICG/portals#same-origin-communication-channels, currently the only way of communicating between same-origin pages is via .postMessage() API which is limited to using data that can be serialized using the structured clone algorithm. This precludes the possibility of passing the portal HTML elements to the portaled page. I'm not aware of any other means that allow users to store HTML elements from one page in the browser and allow another (even same-origin page) access to them.

Instead, I could imagine an API where the portaled page could explicity opt into inheriting some portals from the embedder. It could be something like the following. (Note that this is NOT a real proposal, but merely a suggestion for the kind of API that could be introduced - I'm aware that the actual code below is not particularly elegant)

// in the embedder page
const inheritedPortals = document.querySelectorAll('portal');

// This should only work if the portal is same-origin
portal.activate({ inheritedPortals });
// in the portaled page
window.addEventListener( 'portalactivate', (e) => {
  const portals = e.inheritedPortals();
  
  for (portal of portals) {
    document.body.append( portal );
  }
});

Security

I do not have much to add here as, admittedly, this is not my area of expertise so I'm willing to be educated if this kind of API would have any gaping holes security-wise that I failed to consider.

@legowerewolf
Copy link

I feel like this is something that should be/might already be handled by the browser instead as part of normal caching operations. When the content behind the portals is loaded, is it not cached? And thus can't the portal content be loaded from the cache?

You might want an additional attribute to control this load-from-cache behavior, whether it wants to load fresh from the network or show a cached copy of the page.

@jeremyroman
Copy link
Collaborator

It's an interesting idea, but it's quite complicated. It might also be resource-intensive especially if you expect to have many portals each holding a full prerendered page.

I'll leave this issue open, but as a heads up it'll likely be some time before we're able to properly evaluate this idea in depth.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants