06 Make it yours
Embedding and Origin Allowlisting
In this chapter
For the hosted BrowserBox SaaS service, the current REST API and embed reference is published at https://hyper-frame.art/api/. That reference covers both session-lifecycle REST endpoints and the canonical <hyper-frame> custom element using the current method and event names.
6.1 Server-side embedding allowlist #
For self-hosted BrowserBox embeds, the main server-side allowlist is:
export ALLOWED_EMBEDDING_ORIGINS="https://app.example.com https://*.customer.example:*"This controls which parent pages may embed BrowserBox.
Supported patterns include:
exact origins, for example
https://app.example.comsubdomain wildcards, for example
https://*.customer.exampletrailing port wildcards, for example
https://app.example.com:*both together, for example
https://*.customer.example:*
Important boundary:
ALLOWED_EMBEDDING_ORIGINScontrols which parent pages may embed BrowserBox.It does not control where the remote browser may browse.
Browsing targets are controlled by BrowserBox policy.
6.2 Canonical embedder controls #
For new embeds, prefer explicit webview controls over encoding everything into the login URL:
embedder-origin="https://app.example.com"ui-visible="false"allow-user-toggle-ui="false"first-load-cleanse="https://example.com"when the embedder should remove inherited tabs and open a known first page
Minimal example:
<hyper-frame
login-link="https://localhost:8888/login?token=..."
embedder-origin="https://app.example.com"
ui-visible="false"
allow-user-toggle-ui="false"
></hyper-frame>6.3 First-load tab cleansing #
Some hosted or demo embedders need the first visible session state to be deterministic even when the remote browser already has tabs. The first-load-cleanse attribute runs once per login-link: it closes the current tab set when the webview API becomes ready, then optionally opens one replacement tab.
<hyper-frame
login-link="https://localhost:8888/login?token=..."
first-load-cleanse="https://duckduckgo.com"
></hyper-frame>The value is intentional:
a non-empty URL closes existing tabs, then opens that URL
an empty value, such as
first-load-cleanse="", closes existing tabs and opens no replacement tabomitting the attribute leaves the existing session tabs alone
The public firstLoadCleanse(url) method has the same semantics for explicit embedder control. Passing a non-empty url opens that URL after the cleanse; passing an empty string performs the delete-only cleanse.
6.4 Embedder media permissions #
Embedders that do not want BrowserBox to request microphone, camera, or display-capture access can set:
<hyper-frame
login-link="https://localhost:8888/login?token=..."
embedder-origin="https://app.example.com"
media-permissions="none"
></hyper-frame>The current values are:
| Value | Meaning |
|---|---|
| default | BrowserBox uses its normal media behavior. This is also the fallback for missing, empty, or unknown values. |
| none | The embedder does not want BrowserBox to use microphone, camera, or display capture for this embedded session. The webview removes those features from the iframe allow attribute and syncs the setting into the BrowserBox client. Safari-family clients use this setting to skip the BrowserBox media explainer and the related preflight media request. |
This is a coarse embedder media control. It is not a substitute for BrowserBox browsing policy, and it does not currently expose separate per-device settings such as audio-only or camera-only controls.
6.5 Embedder session unload warning #
BrowserBox shows a beforeunload warning (“you are about to leave your remote browser”) on the BrowserBox wrapper page itself when the end-user has interacted with the session. For embedders that own the outer page and do not want BrowserBox to interrupt the user’s navigation of their own application, this can be suppressed:
<hyper-frame
login-link="https://localhost:8888/login?token=..."
embedder-origin="https://app.example.com"
session-unload-warning="none"
></hyper-frame>| Value | Meaning |
|---|---|
| default | BrowserBox installs its wrapper-page beforeunload warning. This is also the fallback for missing, empty, or unknown values. |
| none | BrowserBox’s wrapper-page beforeunload handler still runs, but is lazily evaluated at fire time: when the embedder’s session-unload-warning is none, the handler refuses to block the unload. This design is race-free; the handler checks the current setting each time the user tries to navigate, so the embedder value does not need to win a setup-time race against the BrowserBox script load. |
This setting does not affect the BrowserBox modal for beforeunload prompts originating from the remote page being browsed. Those are governed by the beforeunload-behavior attribute below.
6.6 Remote page beforeunload behavior #
When a remote page being browsed inside BrowserBox attempts to block navigation via a beforeunload dialog, BrowserBox normally intercepts this and shows a modal to the user. For automated or programmatic use cases where navigation should never be blocked, this can be configured to automatically allow or block the navigation without showing a UI:
<hyper-frame
login-link="..."
beforeunload-behavior="leave"
></hyper-frame>| Value | Meaning |
|---|---|
| default | BrowserBox shows its standard modal, allowing the user (or the embedder via the Modal API) to decide. |
| leave | BrowserBox automatically clicks "Leave" (proceed with navigation) and does not show the modal UI. |
| remain | BrowserBox automatically clicks "Remain" (stay on page) and does not show the modal UI. |
This design uses the same JiT-evaluation pattern as the session unload warning, making it robust against initialization races.
6.7 Modal lifecycle and dismissal API #
BrowserBox modals are exposed to embedders using the same event convention as the rest of the webview surface: the BrowserBox frame sends a dashed message type with a data payload, and <hyper-frame> dispatches a CustomEvent whose detail is that payload. Where dotted aliases exist, they carry the same detail object.
The modal API should not require customers to learn separate methods for alert, confirm, prompt, file chooser, auth, or notice modals. Each modal is represented as data with an advertised list of actions. Customers either choose one of those actions or call the convenience dismissal method.
webview.addEventListener('modal-opened', event => {
const modal = event.detail;
});
webview.addEventListener('modal-updated', event => {
const modal = event.detail;
});
webview.addEventListener('modal-closed', event => {
const result = event.detail;
});For compatibility with the webview’s existing alias style, the implementation may also emit dotted aliases:
modal.openedformodal-openedmodal.updatedformodal-updatedmodal.closedformodal-closed
A modal detail object should be safe to expose to the embedding page:
{
id: "modal-42",
type: "notice",
title: "Safari Permissions",
message: "...",
url: null,
link: {
title: "View Bug",
href: "https://bugs.webkit.org/show_bug.cgi?id=189503",
target: "_blank"
},
actions: [
{ id: "close", label: "OK", role: "close" }
],
defaultAction: "close",
dismissAction: "close",
requiresInput: false
}The public modal methods are:
const modal = await webview.getCurrentModal();
await webview.respondToModal({
id: modal.id,
action: modal.dismissAction
});
await webview.dismissModal();The low-level API equivalents use the same method names:
await webview.callApi('getCurrentModal');
await webview.callApi('respondToModal', { id, action, value });
await webview.callApi('dismissModal', { id });dismissModal() activates the modal’s safest advertised dismissal action. The selection order is:
use the action whose
idisdismissAction, when present;otherwise prefer an action with role
cancel;otherwise prefer role
close;otherwise prefer role
deny;otherwise, if there is only one action, use that action;
otherwise reject with an ambiguous-action error.
Prompt-like modals can accept a value through respondToModal:
await webview.respondToModal({
id: "modal-43",
action: "ok",
value: "customer supplied text"
});Unsupported or sensitive operations are not required to be advertised as actions. dismissModal() still works whenever the modal has a cancel, close, deny, or single remaining action.
The webview policy capability names are modals.read for modal lifecycle events and modals.respond for respondToModal() and dismissModal(). Both are enabled by default for the canonical webview policy.