kristoff-it / zig-live-webserver

A webserver that supports hot reloading and that integrates with the Zig build system
MIT License
10 stars 2 forks source link

iframe investigation #6

Open scottredig opened 3 weeks ago

scottredig commented 3 weeks ago

This is a bunch of loosely related points of consideration around my current attempt to replace the script injection with an iframe. This can be closed when either we're happy with iframes and they're merged in, or decide to stick with script injection.

Overall I think I'm still happy with the direction, though there's a lot of possible improvement to be had on the issue of urls.

Advantage: Correctness

The current strategy has some potential race conditions related to being notified of changed content, specifically if this scenario happens:

sequenceDiagram
  participant page
  participant server
  page->>server: request page
  page->>server: request resource A
  note over server: resource A is modified
  page->>server: establish refresher websocket

With an iframe, the socket can be established before any content loaded, eliminating this race condition:

sequenceDiagram
  participant page
  participant server
  page->>server: request iframe container page
  page->>server: establish refresher websocket
  note over page: set url of iframe
  page->>server: request actual page
  note over server,page: additional requests must happen after this point

I find this scenario to be unlikely in any given instance of use, but it'd be nice to not have this risk.

Advantage: Toolbar and Conceptual Simplicity

I would really like an active display of the following:

Injecting this type of feature into existing webpages has all sorts of problems. There's basically no way to reserve the top portion of a page for this toolbar without hitting all sorts of issues with how pages are trying to do layout. Doing a floating toolbar is less risky, but issues with conflicts can still happen with extra content the page isn't expecting to be there. Plus a clean divider between toolbar and content visually separates them and prevents the toolbar from being annoying.

There's a mostly lateral step in implementation complexity going from script injection to an iframe. An iframe makes loading the content much simpler (no inspecting of the content, only directly serve the file), however it makes many parts of the project, if not more complicated, more finicky.

Consideration: Compatibility with other webservers.

It's easy to picture wanting to use live-webserver's capabilities with a different server that's built by the Zig build system. (eg, a website which isn't static). I potentially have a use case for this myself. There are two possibilites for this use case:

  1. Proxy everything through live-webserver. This is compatible with both iframe and injection, though injection would be more complicated that iframe.
  2. Have iframe point to different server directly. Much simplier implementation, iframe only, but the limits on cross site interactions with iframes might cause the complexity to crawl back up again. The nested website would need to include a small bit of javascript, and surgical refreshing of assets would be much harder.

Concern: iframe limitations

There is reasonable concern that iframes might have some limitations that break some websites. There are definitely restrictions, but I've done a bunch of searching and have only found restrictions that the host page can explicitly allow. I see no mentions of "you can never do this inside of an iframe".

Concern: page url, and everything with following links

This is where iframes become a real pita. In my iframe protype, the page url looks like this: http://127.0.0.1:1990/__live_webserver/#/index.html. I've added a second navigation bar below that has the url the iframe is going to. I don't mind this much, but there's reasonable desire to clean up the stuttering navbar.

The larger concern I have in this area is links to external websites. Granted, they aren't super important for this use case, but they're highly problematic for iframes. Essentially, when an iframe is at an external url, the url can't be read and the outer frame's javascript can't access anything on the inner frame. (Though messages can be based if both agree on what those messages do.) Additionally, many websites forbid being embeded inside an iframe. So when you click a link to an external website, my prototype either has the url break, or everything break. Ugh.

All of this is elegantly solved by NavigateEvent. With this api, javascript can look for when the page is going to navigate, and, for example, update the page contents without actually doing a load. The outer page can watch if the iframe is going to navigate to an external website (because the event happens in the current page, not like a load which happens on the new website which has xss limitations), so that the outer page could direct the url to that site and keep external links working. The stupid reality: This is listed as an experimental feature, available on chromium browsers but not Firefox nor Safari. So....shit. One option would be to use this if the browser supports it, but fall back onto different methods. Not ideal, especially as my main browser is Firefox. According to https://caniuse.com/?search=NavigateEvent Chrome has had this for two years, so who knows when Firefox will get it, and Safari is never early to a party.

Consideration: Why not both?

The choice between iframes and script injection could be a configuration options. The injected version probably wouldn't get a toolbar. The complexity of both would exist inside the project. So this is something that I'd like to avoid.

kristoff-it commented 3 weeks ago

When you don't have NavigateEvent available, I think you could listen for the load event in the iframe and attach a click listener to all links inside the iframe. This would not solve complex corner cases but would solve 90% of the use cases and tide you over until NavigateEvent is implemented also on FF and other browsers.

scottredig commented 3 weeks ago

Ah, yeah, good point. Either load watching for page loads or detecting link clicks I don't feel by themselves is good enough, but together they'd probably have good coverage.