The Surrogate opens a tab, so the launch runs in a fresh __about_~_blank__ document with no user activation. Expect the modal, and with the fix expect it to stay until Allow/Deny.
Expect a direct launch, no modal, before and after the fix.
This is the case Ji asked about. Two possible paths depending on what the Surrogate reports as the new tab's URL:
(a) reports about:blank → client loads __about_~_blank__, then an update_navbar carries the mailto → assignOrReplace → gated, hold acquired, modal shown. Covered by the fix.
(b) reports the mailto directly → tab.js navigates the popup with Util.locationReplace → no gate, no hold, no modal. Not covered.
Both buttons are the same JS shape, only the scheme differs, so any difference between them comes purely from the scheme check on the Surrogate side. Compare what each one does and whether the popup logs the deferred-close line below.
These stay in this tab, so no tab close is pending and the modal survives with or without the fix. Here to confirm no regression.
These also reproduce in prepend mode, unlike mailto:.
These are not application links, but the Surrogate matches the tenant applicationLinks list as a plain URL prefix. An admin who put "data:", "blob:" or "javascript:" in that list would have these popups classified as app launches and their carrier tabs torn down. The client hard-blocks all three before it consults the same list, so it will not hold the close.
Prerequisite: set applicationLinks to ["mailto:", "wbx://", "zoommtg://", "data:", "blob:", "javascript:"]. Without those entries the Surrogate will not match and nothing reproduces — that misconfiguration is the scenario under test. Sections 1 and 5 stay as the controls.
data: anchor javascript: anchor blob: anchor
Current code, if the concern is real: each of the six opens a popup tab that then closes by itself. Once guarded: the popup stays open for these three, while wbx:// and zoommtg:// still close as before.
Report these separately rather than as a negative result. A javascript: popup may be refused by CSP or trusted types regardless of this logic. MonitorJS drops blob: attributes on some elements, so the blob: anchor may not survive mirroring — the window.open variant is the more reliable trigger. data: popups already open as about:blank on the client by design, so judge by whether the tab closes, not by whether content renders.
Click it, then do not touch anything for 8 seconds. The delay must exceed the browser's transient activation lifetime, which is about 5 seconds — below that the click is still active, the launch goes straight through and no modal is shown at all. Both navigations stay in this tab, so only one modal can be shown for the two of them.
Read the address in the modal. second@example.com means both navigations reached the gate, so two holds were taken against a single modal and the first address was silently overwritten. first@example.com means the Surrogate coalesced the two into one update_navbar, only one hold was taken, and the case is not reachable this way.
Nothing visible happens to this tab either way, because no tab close is pending here. An unbalanced count is latent: it stays above zero, so a later tab close for this tab would be deferred indefinitely.
Two attempts at getting both launches into the same new tab, which is the case that matters — there a tab close is pending, so an unbalanced count leaves the tab open forever.
The first reuses one tab by giving both window.open calls the same target name. The second keeps the handle from window.open and navigates it again. Either way, what confirms the case is one popup tab showing one modal for second@example.com, whose deferred close then never runs.
Two separate popups each with their own modal, or a second call that does nothing, means the case is not reachable this way. That is the expected outcome: the Surrogate flags the carrier tab at creation and closes it right after the first handoff, so a second application link has no window in which to arrive. The client also renames popup windows for pairing, which may defeat the named-target reuse.
Enable "Auto-open DevTools for popups" and "Preserve log" first, otherwise the popup takes its console with it when it closes.
Gated path (covered): the popup logs
Deferring tab close; 1 modal hold(s) outstanding
and the modal stays.
Ungated path (not covered): no such log line, no modal, and the popup either launches the app straight away or closes doing nothing.
Also check the popup's URL. __about_~_blank__ means path
(a); anything else means the Surrogate reported the applink directly.
Surrogate side: its logs go to stderr, there is no log file. Capture
with 2> /tmp/surrogate.log, or docker logs /
journalctl, then grep surrogate-tab-request. An
applink= field on the popup's line means the Surrogate
classified it as an application launch. Seeing that field for a data:,
blob: or javascript: popup is the confirmation section 6 is after;
once guarded it should appear for wbx:// and zoommtg:// only.