Skip to content

attachFrame()

The attachFrame() function of @fkn/lib attaches an <iframe> element your page embeds and returns a Frame handle: the root locator for finding elements and performing actions inside it, plus navigation helpers.

Page automation by execution backend

All APIs below are called from browser code. The columns show which backend performs the work.

API / featureExtensionCloudDesktop
attachFrame()SupportedSupportedNot available
Frame.goto()SupportedSupportedNot available
Locators & actionsSupportedSupportedNot available

attachFrame() has two backends behind one API. An exposed extension handles the frame: syncCookies: true seeds it from the user’s browser session, while false starts with a clean cookie partition. When the extension is absent, the call degrades to the cloud backend, which renders the target through FKN’s render proxy: the default cookie mode uses the render proxy’s persistent cloud jar, and syncCookies: false a fresh, isolated jar. Both build the same Frame, so calling code can use the same locators and navigation methods.

The attachment itself is severity 0 and is granted automatically. On the extension backend, each frame operation is checked by the consent system: severity-0 operations are automatic, while sensitive operations prompt when no grant covers them. The cloud backend has no extension consent prompts.

These are real consumers of @fkn/lib running inline on this page, with no account, login, or test credentials. The guided demo needs no install: it embeds the real en.wikipedia.org through FKN’s cloud render proxy, restyles it, then reads the heading, types a search and clicks Search. With the FKN extension present it upgrades itself: a single consent prompt covers all three actions. Those permission decisions are kept in the on-device activity log.

Guided demo: FKN acting on en.wikipedia.orgOpen in new tab

The second demo walks the extension’s capabilities one at a time: single actions (one consent each), an area grant that covers a whole region, and the on-device activity log. Its subject is the extension’s permission model, which has no cloud equivalent, so it waits for the extension. The selectors and actions it uses are the reference material of Locators and actions.

Locators, consent and the audit logOpen in new tab
const
const frame: Frame
frame
= await
function attachFrame(options: AttachFrameOptions): Promise<Frame>
attachFrame
({
iframe: HTMLIFrameElement
iframe
:
var document: Document

window.document returns a reference to the document contained in the window.

MDN Reference

document
.
ParentNode.querySelector<"iframe">(selectors: "iframe"): HTMLIFrameElement | null (+4 overloads)

Returns the first element that is a descendant of node that matches selectors.

MDN Reference

querySelector
('iframe')!,
})
  • iframe: the HTMLIFrameElement to attach. It must be connected to the document. Its src may be empty or about:blank and filled in later via goto().
  • domains (optional): extension-only host rules for the initial page and expected cross-site navigation. Include the initial host when attaching a preloaded frame. The cloud backend accepts this option but does not use it.
  • syncCookies (optional, default true): selects session handling. On the extension backend, true uses the browser’s logged-in session. On the cloud backend, true uses the render proxy’s persistent cloud cookie jar and false creates a fresh jar that is dropped when the attachment ends.
  • lockdown (optional): request a sealed frame. Supported on the extension backend only; the cloud backend rejects it, so bare attachFrame({ lockdown: true }) always resolves to the extension backend and surfaces the install flow when the extension is missing.

The iframe must be connected to the document. If it already has a sandbox attribute and cloud fallback is possible, that attribute must include allow-scripts and allow-same-origin so the render proxy can initialize.

A Promise resolving to a Frame: a FrameLocator extended with:

attachFrame() picks a backend for you:

  • Extension: selected when the extension is exposed, including when its content script lands moments after the call (the library waits briefly for exposure before deciding). It supports lockdown, browser-session cookies, and the complete locator set. lockdown: true always selects this backend and surfaces the install flow when the extension is missing.
  • Cloud: selected when the extension never exposes, in every cookie mode, with no install prompt. It mounts the target through proxy.sdbx.app inside a first-party middle page; the default cookie mode uses the persistent cloud jar and syncCookies: false an isolated per-attach jar. It does not support lockdown or the videoElement locator; those calls reject with a terminal LocatorUnsupportedError.

Both backends build the returned Frame from the same core. Locator chains, goto(), and url() therefore share one API shape. Session handling, consent, lockdown, and videoElement() remain backend-specific.

To pin a backend, call it through an explicit backend namespace. cloud.attachFrame() always uses the render proxy, even when the extension is installed. Its own syncCookies default is true, which selects the persistent cloud jar. extension.attachFrame() waits for extension exposure and may invoke the configured install flow, but it never falls back to cloud.

import {
(alias) namespace cloud
import cloud
cloud
} from '@fkn/lib'
// Pinned to the render proxy with an isolated session
const
const frame: Frame
frame
= await
(alias) namespace cloud
import cloud
cloud
.
cloud_d_exports.attachFrame({ iframe, domains, syncCookies, lockdown }: AttachFrameOptions): Promise<Frame>
export cloud_d_exports.attachFrame
attachFrame
({
iframe: HTMLIFrameElement
iframe
:
var document: Document

window.document returns a reference to the document contained in the window.

MDN Reference

document
.
ParentNode.querySelector<"iframe">(selectors: "iframe"): HTMLIFrameElement | null (+4 overloads)

Returns the first element that is a descendant of node that matches selectors.

MDN Reference

querySelector
('iframe')!,
syncCookies?: boolean | undefined
syncCookies
: false,
})
import {
const attachFrame: (options: AttachFrameOptions) => Promise<Frame>
attachFrame
,
const waitForExtensionExposure: (timeout?: number) => Promise<void>
waitForExtensionExposure
} from '@fkn/lib'
await
function waitForExtensionExposure(timeout?: number): Promise<void>
waitForExtensionExposure
()
const
const iframe: HTMLIFrameElement
iframe
=
var document: Document

window.document returns a reference to the document contained in the window.

MDN Reference

document
.
ParentNode.querySelector<HTMLIFrameElement>(selectors: string): HTMLIFrameElement | null (+4 overloads)

Returns the first element that is a descendant of node that matches selectors.

MDN Reference

querySelector
<
interface HTMLIFrameElement

The HTMLIFrameElement interface provides special properties and methods (beyond those of the HTMLElement interface it also has available to it by inheritance) for manipulating the layout and presentation of inline frame elements.

MDN Reference

HTMLIFrameElement
>('#player')!
const
const frame: Frame
frame
= await
function attachFrame(options: AttachFrameOptions): Promise<Frame>
attachFrame
({
iframe: HTMLIFrameElement
iframe
})
await
const frame: Frame
frame
.
function goto(url: string, options?: GotoOptions): Promise<void>
goto
('https://example.com/watch/123', {
waitUntil?: "documentstart" | "load" | undefined
waitUntil
: 'load' })
await
const frame: Frame
frame
.
locator: (selector: string) => Locator
locator
('.controls').
getByText: (text: string) => Locator
getByText
('Play').
click: (options?: (OperationTimeoutOptions & {
position?: {
x?: number;
y?: number;
};
}) | undefined) => Promise<void>
click
()

One prompt covering a whole region of the page, instead of one per control. See Permissions and consent for how covered use is recorded.

await
const frame: Frame
frame
.
locator: (selector: string) => Locator
locator
('.controls').
ensure: (operation: "click" | "fill" | "hover" | "textContent" | "isVisible" | "count" | "exists" | "getAttribute" | "videoElement", options?: {
reason?: string;
timeout?: number;
subtree?: boolean;
} & Record<string, unknown>) => Promise<void>
ensure
('click', {
subtree?: boolean | undefined
subtree
: true })
// These run without further prompts. Covered use is recorded once per scope per visit.
await
const frame: Frame
frame
.
locator: (selector: string) => Locator
locator
('.controls').
locator: (selector: string) => Locator
locator
('#play').
click: (options?: (OperationTimeoutOptions & {
position?: {
x?: number;
y?: number;
};
}) | undefined) => Promise<void>
click
()
await
const frame: Frame
frame
.
locator: (selector: string) => Locator
locator
('.controls').
locator: (selector: string) => Locator
locator
('#volume-up').
click: (options?: (OperationTimeoutOptions & {
position?: {
x?: number;
y?: number;
};
}) | undefined) => Promise<void>
click
()

attachFrame() and goto() refuse the extension’s own pages (chrome-extension:, moz-extension:) and FKN platform origins (fkn.app, fkn.dev, sdbx.app, and their subdomains). On the extension backend the content script applies the boundary. On the cloud backend the first-party middle page applies it to both the initial target and every goto(). The page-side API mirrors the same rule for an immediate error.