Closed alancutter closed 2 years ago
It would be reasonable for the service worker path to override the navigate_client
behaviour when it selects a client.
I think this is a reasonable take.
I'm not sure what the point of new_client_url
is. If I set it to something other than the default, then when would start_url
ever be used? Isn't this just ... the same as start URL?
start_url
will be the launch URL for OS app list launches. new_client_url
is for sites that want a routing page to process launch event URLs that's more lightweight than their start_url
. It's not a necessary component of this proposal though.
I'm really liking this. It's good to periodically rethink a design that was rather hastily put together and hasn't really been questioned since I wrote it.
Things that I like about it:
launch
event (rather than only doing it in the *-event
modes) is consistent with how the web's event model works: events always fire, you don't need to specifically enable them, you just either listen for them or ignore them.capture_links
.existing-client-navigate
versus existing-client-event
. First, you decide whether to find an existing client or open a new one, then you decide whether to force a navigation or let an event handler act.launch
event designed. A lot of the design decisions we were debating about are already resolved by the fact that navigate-client
is a separate choice.Some other thoughts:
"new-client"
+ false
is a pretty weird thing. (When the user navigates to a specific page, open a different URL and fire an event at it.) It does have utility; it gives sites a lot of control over what URLs can be navigated to in the window context. But it's a weird thing nonetheless.new_client_url
is for (distinct from start_url
). For example, you could use it with "new-client"
+ false
which would mean when the all is launched normally, navigate to new_client_url
but send an event containing start_url
. But again, "new-client"
+ false
is a weird thing and I'm not sure we need a feature specifically designed around it. Maybe new_client_url
doesn't need to be in the initial version.launch_handler
is the equivalent of no link capturing. But is it bad that there's no way to express that default explicitly in the value of route_to
?navigate_client
should be true, which gives "mostly normal" behaviour consistent with how the web already works (without needing to write JS event handlers).For completeness, pasting a private email from @alancutter here, which explains the mapping from the previous design to the above proposal:
"capture_links": "new-client"
"launch_handler": { "route_to": "new-client", "navigate_client": true }
navigate_client
can be omitted (defaults to true)."capture_links": "existing-client-navigate"
"launch_handler": { "route_to": "existing-client", "navigate_client": true }
navigate_client
can be omitted (defaults to true)."capture_links": "existing-client-event"
"launch_handler": { "route_to": "existing-client", "navigate_client": false }
"launch_handler": { "route_to": "service-worker", "navigate_client": true | false | <service worker may override> }
I assume the default value for launch_handler is the equivalent of no link capturing
In Chrome's implementation there is no such thing as no link capturing. It's a user choice that the site has no control over (similar to users forcing web apps to open in a window/browser tab).
I would reserve this kind of link capturing site configuration to the capture_links
manifest field. launch_handler
doesn't know about link capturing, link capturing is an app launch callsite that invokes launch handling.
I like this structure.
Questions:
Do we imagine that developers want the same settings for all launch events (share, file handlers, etc)? If not, how would we want the API to look to accomplish this?
If we want to provide that granularity I envision just embedding this launch_handler
structure in whatever launch surface API wants it to override the general launch_handler
behaviour.
"capture_links": {
"exclude_paths": ["/example"],
"launch_handler: { "route_to": "existing-client" }
}
Can you put examples of things this API CAN specify that the old one can't?
The old one can't:
This change is less about adding more capabilities and more about decoupling orthogonal concepts from the original DLC concept.
Great suggestion from glenrob: Remove "route_to": "service-worker"
and just always fire the service worker event. The route_to
value is what to do if the service worker noops (same for navigate_client
).
This makes the service worker event a way of "explaining" the declarative behaviour as a more procedural browser pipeline primitive.
So IIUC there are now 4 possible behaviours (please correct me if I'm describing it wrong):
It seems to me that 3 and 4 would be better handled by a service worker event - less overhead and no declaration needed assuming we always fire the SW event, and able to customise the behaviour based on what URL is coming in. What is the advantage of firing the event inside a client instead?
So there would still be capture_links
? For path overrides, and potentially for putting link-capturing-specific launch_handler
in there as well?
In Chrome's implementation there is no such thing as no link capturing. It's a user choice that the site has no control over (similar to users forcing web apps to open in a window/browser tab).
OK, so then what is the default value of launch_handler
?
It seems that in Chrome's implementation, launch_handler: undefined
or {}
will have different behaviour to any explicit value in route_to
, right? So maybe even though it's implementation-specific whether capturing happens or not, you are still providing a hint to the browser that you want capturing, by specifying route_to
.
It seems to me that 3 and 4 would be better handled by a service worker event
This would make "navigate_client": false
behaviour only accessible from the service worker launch event handler.
I think existing-client-event
is a common enough desire to warrant a declarative way of getting it without needing SW code. It's important for apps that don't want to lose existing page state.
So there would still be
capture_links
? For path overrides, and potentially for putting link-capturing-specificlaunch_handler
in there as well?
Yes, if we have any link capturing specific configurations we want to provide e.g. excluding sub paths and link-capture-specific launch_handler
as you said.
OK, so then what is the default value of launch_handler?
It should probably be null
(or whatever the manifest's equivalent of CSS auto
is) so the user agent can have its own default appropriate for the platform its running on (e.g. Android would only support "existing-client").
I think
existing-client-event
is a common enough desire to warrant a declarative way of getting it without needing SW code. It's important for apps that don't want to lose existing page state.
Yes. This was essentially the whole insight behind DLC in the first place, as an evolution on the older sw-launch plans (see the Overview section of the DLC explainer):
sw-launch has been proposed for a number of years and has never made it past a proposal stage, largely due to the complexity involved in both spec and implementation (a complex effort spanning PWAs, service workers, and HTML navigation stack).
We found that almost all
launch
use cases could be covered by a handful of fixed rules (for example, "choose an existing window in the same app, focus it, and navigate it to the launched URL"). Thus, this proposal, "Declarative Link Capturing", allows sites to choose one of those fixed rules without having to implement customlaunch
event logic, which should satisfy most use cases, and simplify the implementation in the browser and in all the sites. We leave open the option of expanding into the fulllaunch
event later on.
It's not only what we think will be the overwhelmingly common case, but it also means we can ship it without having to do a major implementation project on the guts of the navigation flow (& that follows for all the browser engines).
alancutter@ and I chatted about this. I can see the value in the decomposition into 2x2 properties now. They make more sense than the old 3 names (new client navigate (default), existing client event, existing client navigate) in the context that we are always sending the event.
My only remaining note is that "3. route_to: new-client + navigate_client: false" is quite an odd option, and most likely to be handled better by a service worker eventually, so we should avoid adding new_client_url to support that case, just use start_url.
I think the service worker approach wouldn't be much more complex for sites to implement but I can see how it would be more complex for the browser if we have to drop in to SW code to check if the event is handled during the navigation. Possibly we'd want to make that part declarative somehow. This is just musing though. The proposal (except new_client_url) LGTM.
@phoglenix Re new_client_url
: that was my line of thinking in this comment as well:
But again,
"new-client"
+false
is a weird thing and I'm not sure we need a feature specifically designed around it. Maybenew_client_url
doesn't need to be in the initial version.
But I thought Alan explained (must've been privately): new_client_url
isn't just used for new_client
. It's also used for existing_client
when there's no existing client. So it makes sense to have "existing_client"
+ false
(which we expect to be common: it's the new equivalent of existing_client_event
), but still have the common case where no window is open, we would spawn a new window at new_client_url
. I think most sites will just want this to open start_url
, but there is a case for having a lightweight page at new_client_url
that does routing to whichever page is being requested, which may be start_url
or it could be a share target, shortcut URL, etc. So I definitely see the value of new_client_url
.
Having said that, I'm fine if it doesn't appear in the MVP. It isn't crucial.
I think start_url
was intended for use by "app launched from OS app launch surface", I don't think it's very relevent for any of the other ~6 ways of launching a web app. IMO the start_url
member is a conceptual sibiling to share_target
, new_note_url
, shortcuts
etc in that it's just configuring its own particular app launch entrypoint.
Yes, I agree, in theory / conceptually @alancutter .
However, in practice, I think the start_url
is going to be the "main page" of an app, which most apps will want to load before they show any specific content for subpages. I think for the first version, we can always load start_url
, then we can always add new_client_url
later, with a default value of start_url
.
Edit: Which I see you have done in the explainer (NCU is listed as a future work).
I think if you want a lightweight page to fire an event at, you're better off with a service worker. new_client_url
seems like an awkward midpoint: not necessary for the initial version and not desirable for the ideal version.
Well let's talk practical terms.
Say you've got a video chat client with two pages:
Let's assume both of these pages load non-trivial resources that are unique to that page and not the other. And you want the single-window mode option so if you start a new video call, you focus the existing window and show a dialog asking if the user wants to switch to the new call.
I think in this case the ideal architecture is:
start_url
is the home page.{"route_to": "existing-client", "navigate-client": false}
."navigate-client": "true"
, because then you have no control over the dialog that the browser shows asking the user if they want to navigate.start_url
first. So we need new_client_url
to be a lightweight page that can decide which to navigate to.In light of that ... maybe new_client_url
is actually needed in the MVP? Or ......
Hurr.... okay so actually, I think this has introduced a bit of a design flaw, @alancutter , when compared to my DLC proposal. In the DLC proposal, existing_client_event
would not have navigated to start_url
if no window was open; it would just directly open the intended URL. Basically, if no window is open, it behaves as if "navigate-client": true
, but if a window is open, it behaves as if "navigate-client": false
. There's no way to get that in the new design. Which leads to the above architecture, which let's face it, is pretty complex to reason about and implement (you need to build another lightweight page, which as we've seen with install URLs, is pretty difficult for complex sites, even if it's basically just blank HTML).
So maybe what we need is for navigate-client
to be three-valued:
"always"
"never"
"if-opening"
"auto"
)(As a bonus, it removes the awkward "string or Boolean" type that you had with "auto"
, true
and false
.)
Now we can map the previous DLC proposal onto the new one:
"capture_links": "new-client"
"launch_handler": { "route_to": "new-client", "navigate_client": "always" }
"capture_links": "existing-client-navigate"
"launch_handler": { "route_to": "existing-client", "navigate_client": "always" }
"capture_links": "existing-client-event"
"launch_handler": { "route_to": "existing-client", "navigate_client": "if-opening" }
Then maybe we don't need new_client_url
. It would only be used for navigate_client
: never
, and TBH I'm not sure what you'd ever want that for, though for completeness I'd be OK with including it.
In CSS spec land they don't let you use true/false, you have to use enum values like visibility: visible
and for good reason; booleans are not extensible. I think switching to "always" etc. is a good move and I'm on board with capturing the more complex "navigate if client is new" behaviour.
This was added to the explainer.
In the same vein as https://github.com/w3c/web-share-target/issues/84#issuecomment-865646597:
I propose that we always enqueue the launch event for any app launch (link capturing, OS app launch, menu shortcuts, file handlers, share target...).
What does this mean for
existing-client-navigate
vsexisting-client-event
? It removes the "event" aspect and leaves behind "navigation". If we always enqueue launch events then the behavioural options for the developer become "which app window gets used" and "whether the browser automatically navigates the app window to the launch URL".Possible restructuring of the API:
new_client_url
defaults tostart_url
if unset/null.This decomposes the "launching" of the app into two configurable pipeline steps:
route_to
Choose which app client to launch in (possibly new).new-client (default): Opens a new app client to receive the launch.
existing-client: Selects the most recently used app client to receive the launch if any are open otherwise does the same as
new-client
.service-worker: Fires an event in the app service worker, provides it an API to select a client to use to receive the launch. Does the same as
new-client
if no client selected.navigate_client
Choose whether to navigate that window to the launch URL or let JS handle that.true (default): App clients that receive a launch get navigated to the launch URL before having the launch event enqueued in them (launch URL not provided).
false: New app clients are opened at
new_client_url
. App clients that receive a launch have a launch event enqueued containing the launch URL.