tuffite0.1
THE CONTRACT, IN DETAIL

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.

html
<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.

javascript
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.

javascript
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

webidl
[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();
};
View source

ExplorerExecuteJavaScriptOptions

webidl
dictionary ExplorerExecuteJavaScriptOptions {
    DOMString world = "isolated";
    DOMString expectedDocumentId = "";
};
View source

ExplorerReloadOptions

webidl
dictionary ExplorerReloadOptions {
    boolean bypassCache = false;
};
View source

ExplorerTransfer

webidl
[Exposed=Window, RuntimeEnabled=TuffiteExplorer] interface ExplorerTransfer : EventTarget {
    readonly attribute DOMString token;
    readonly attribute DOMString state;
    [CallWith=ScriptState] Promise<undefined> close();
    attribute EventHandler onstatechange;
};
View source

HTMLExplorerElement

webidl
[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);
};
View source