Explorer API
Embed, navigate, message, and transfer isolated browser surfaces.
Experimental source release. Production hardening and published SDK packages are still in progress.
Element and permissions#
Explorer is a native DOM element, HTMLExplorerElement, rather than a Rust method or a tuffite.explorer ShellAPI namespace. The Framework must enable TuffiteExplorer. Grant explorer.create to the owning document origin in app.security.capabilities; isolated script execution additionally requires explorer.executeJavaScript. Guest documents need their own grants for native calls or nested Explorers.
<explorer id="browser" src="https://example.com/" navigation="emit" dialogs="emit"></explorer>Properties and creation policy#
- src requests navigation; load(url) explicitly starts a page. contextMenuEnabled toggles the native menu and defaults to false.
- navigation="emit" intercepts renderer current-tab navigation; "allow" navigates inside the surface. dialogs="emit" forwards dialogs; "suppress" rejects them.
- partition, navigation, and dialogs are frozen when a page is created. Recreate the element to change that policy. deferred delays initial creation; load() can start a deferred element.
Events and dialogs#
- error: creation or attachment failed. loadstart, loadstop, loadcommit: navigation state. loaderror also provides errorCode and errorDescription.
- navigation, open, popup, and newwindow carry {url, disposition, userGesture, from} in event.detail. They describe intercepted navigation, ordinary window.open(), popups, and other new-window requests respectively.
- Request events and dialog bubble across Explorer boundaries to window.tuffite. stopPropagation() stops both local bubbling and cross-surface forwarding.
- dialog detail is an ExplorerDialog with type, message, defaultValue, isReload, from, accept(promptText), and dismiss(). A controller answers once; stale or repeated responses return false. Answering does not stop propagation.
- detached and attached report ownership changes; ExplorerTransfer emits statechange.
Explorer-tree messaging#
element.postMessage(data) targets its direct child. window.tuffite.postMessage(data, {to}) accepts parent, top (the default), or a browser-issued ref from event.detail.from. Messages are JSON; browser ownership and tree membership checks apply. Promise<boolean> indicates acceptance and delivery/queueing, not an application reply. ShellAPI invoke and channel events use a separate binary transport.
await explorer.postMessage({ type: 'refresh' });
await window.tuffite.postMessage({ type: 'ready' }, { to: 'parent' });
window.tuffite.addEventListener('message', ({ detail }) => {
console.log(detail.data, detail.from);
});Storage partitions#
- Omitting partition shares the immediate parent’s exact storage backend, including an in-memory backend; "shared" is not a valid attribute value.
- persist:name creates a named disk-backed partition; memory:name creates a named in-memory partition.
- inherit+persist:name and inherit+memory:name seed parent cookies and localStorage only when both destination stores are empty. This is a snapshot, not live synchronization; other storage types are not copied.
- Every Explorer retains its own renderer isolation domain, including when storage is shared. Ordinary origin rules continue to apply.
Transfer a live page#
Prepare a connected receiver without src or an existing page. detach() returns an ExplorerTransfer with a one-use token and pending, attaching, transferred, or closed state. Successful attach() consumes the token. Call close() for abandoned transfers; closing while attaching rejects. Source document navigation or teardown releases pending pages. An ordinary same-document moveBefore() preserves the existing page without a transfer.
const target = document.createElement('explorer');
target.deferred = true;
target.navigation = source.navigation;
target.dialogs = source.dialogs;
if (source.partition) target.partition = source.partition;
container.append(target);
const transfer = await source.detach();
try {
await target.attach(transfer.token);
source.remove();
} catch (error) {
await source.attach(transfer.token);
throw error;
}Canonical WebIDL interfaces#
These declarations are generated directly from the Framework’s Explorer WebIDL, including helper option dictionaries. Use the Playground Capability Lab to exercise navigation, messaging, storage, dialogs, and transfers.
ExplorerDialog
[Exposed=Window, RuntimeEnabled=TuffiteExplorer] interface ExplorerDialog {
readonly attribute DOMString type;
readonly attribute DOMString message;
readonly attribute DOMString defaultValue;
readonly attribute boolean isReload;
readonly attribute DOMString from;
[CallWith=ScriptState] Promise<boolean> accept(optional DOMString promptText = "");
[CallWith=ScriptState] Promise<boolean> dismiss();
};ExplorerExecuteJavaScriptOptions
dictionary ExplorerExecuteJavaScriptOptions {
DOMString world = "isolated";
DOMString expectedDocumentId = "";
};ExplorerReloadOptions
dictionary ExplorerReloadOptions {
boolean bypassCache = false;
};ExplorerTransfer
[Exposed=Window, RuntimeEnabled=TuffiteExplorer] interface ExplorerTransfer : EventTarget {
readonly attribute DOMString token;
readonly attribute DOMString state;
[CallWith=ScriptState] Promise<undefined> close();
attribute EventHandler onstatechange;
};HTMLExplorerElement
[Exposed=Window, RuntimeEnabled=TuffiteExplorer] interface HTMLExplorerElement : HTMLElement {
[CEReactions, Reflect, URL] attribute USVString src;
[CEReactions] attribute DOMString partition;
[CEReactions] attribute DOMString navigation;
[CEReactions] attribute DOMString dialogs;
attribute boolean contextMenuEnabled;
[CEReactions] attribute boolean deferred;
[CallWith=ScriptState] Promise<ExplorerTransfer> detach();
[CallWith=ScriptState] Promise<undefined> attach(DOMString token);
attribute EventHandler ondetached;
attribute EventHandler onattached;
void load(USVString url);
[CallWith=ScriptState] Promise<boolean> cancelDownload(unsigned long id);
[CallWith=ScriptState] Promise<boolean> download(USVString url);
[CallWith=ScriptState] Promise<any> getDownloads();
[CallWith=ScriptState] Promise<DOMString> readDownload(unsigned long id);
[CallWith=ScriptState] Promise<DOMString> capturePreview();
[CallWith=ScriptState] Promise<boolean> back();
[CallWith=ScriptState] Promise<boolean> forward();
[CallWith=ScriptState] Promise<boolean> go(long offset);
[CallWith=ScriptState] Promise<boolean> reload(optional ExplorerReloadOptions options = {});
[CallWith=ScriptState] Promise<boolean> stop();
[CallWith=ScriptState] Promise<any> getNavigationState();
[CallWith=ScriptState] Promise<any> executeJavaScript(
DOMString source,
optional ExplorerExecuteJavaScriptOptions options = {});
[CallWith=ScriptState] Promise<boolean> postMessage(any data);
};