Remote Tab Opener · Site docs: 7.11.5 · Install the extension, then reload to show installed version
Firefox MV3 Multi-tab via tabKey Allow-list Local-first

Remote Tab Opener

Control one or multiple named tabs from your own secure page: open, navigate, focus, read state, and run safe predefined DOM actions — all locally, inside the browser.

No server • No proxy • No data collection • Respects browser security (SOP)
Latest docs: 7.11.5
Built for safe cross-domain tab orchestration

What It Is

Remote Tab Opener is a local browser extension that lets a trusted page you control orchestrate real tabs through a safe bridge. Your page emits requests; the extension enforces an allow-list, targets a specific tab using tabKey or tabId, then executes predefined actions inside that tab.

Why it’s useful: you can build a small “control panel” (admin/QA/support/tools) that drives real, visible tabs — including SPAs — without introducing a backend, a proxy, or headless infrastructure.

How It Works (3 steps)

  1. Your page sends a request using window.postMessage() (or a small helper like ask() that adds requestId + timeout).
  2. The extension validates it (origin/host allow-list, HTTPS rules, optional LAN/localhost flags) and routes it to the targeted tab (tabKey or tabId).
  3. The action runs either in the background (tab control like openTab, navigate, focusTab, closeTab, getUrl) or inside the tab via a content script (safe DOM automation via a command envelope).

This does not bypass SOP: actions only run in tabs on domains you explicitly allow.

Key Features

  • Multi-tab orchestration using tabKey (name tabs like "auth", "monitor", "sales").
  • Tab control: open, navigate, focus, close, read URL/title.
  • Safe DOM automation on allow-listed hosts (type, click, submit, wait, extract bounded text/HTML).
  • Local-first: no server, no proxy, no cloud relay.
  • Explicit allow-list + optional LAN/localhost opt-in for intranet tooling.
  • Favorites management in the extension popup.

Remote Tab Opener extension popup preview

Capabilities at a Glance

Tab Control (background)
  • openTab – open (or reuse) a controlled tab by URL, optionally with tabKey;
  • navigate – change the URL of a targeted tab (tabKey or tabId);
  • focusTab – bring the targeted tab to front;
  • closeTab – close a targeted tab;
  • getUrl – read current url + title;
  • waitForNavigation – wait until navigation settles;
  • listTabs, adoptTab, releaseTab — manage multiple tabs;
  • screenshot — capture a screenshot of the controlled tab (where permitted).

Uses standard extension APIs: tabs.create / update / sendMessage / query / get / remove.

DOM Actions (content script via command)
  • setValue, typeText, pressKey
  • click, submit / submitForm
  • waitFor / waitForSelector (SPA timing)
  • getText, getHtml, getAttr, getValue (bounded)
  • highlight, overlay* helpers (visual guidance)
  • selectOption, selectSetValue, setChecked

Runs inside the target page, only on allow-listed hosts. No arbitrary eval.

Messaging & Events
  • Detect gate: detectdetectResult
  • Requests: window.postMessage → extension → tab
  • Events back to your page: tabStatus, tabPageChanged

Stable schemas + requestId pairing to avoid cross-talk.

Safety
  • Domain allow-list configured in the popup
  • LAN/localhost require explicit opt-in + allow-list (double barrier)
  • Local-only: no data collection, no external requests
  • Respects browser security boundaries (SOP)

Deny-by-default model: if a host isn’t allowed, actions are rejected.

Real-World Flows — without the overhead

What this means: orchestrate a real user path end-to-end — open → type → click → navigate → verify — using your own page. No Selenium grid, no remote runner.

  • ✔️ Runs locally with a real, visible tab.
  • ✔️ requestId + timeout for robust request/response pairing.
  • ✔️ Safe by default: allow-list + named actions.
  • 🚫 No SOP bypass, no cookie access, no hidden network interception.
A minimal flow (tab control + DOM commands)
<script type="module">
const RTO_PAGE_ORIGIN = "rto-page";
const RTO_EXT_ORIGIN  = "rto-extension";

function makeId(){ return "rto_" + Math.random().toString(16).slice(2) + "_" + Date.now(); }

// Tab actions handled by background:
const TAB_ACTIONS = new Set(["openTab","navigate","focusTab","closeTab","getUrl","waitForNavigation","listTabs","adoptTab","releaseTab","screenshot"]);

// DOM actions go through { type:"command", action, payload }:
const DOM_ACTIONS = new Set([
  "setValue","typeText","pressKey","click","submit","submitForm",
  "waitFor","waitForSelector","getText","getHtml","getAttr","getValue",
  "highlight","setChecked","selectOption","selectSetValue"
]);

function ask(action, args = {}, opts = {}) {
  const requestId = makeId();
  const timeoutMs = Number(opts.timeoutMs || 8000);

  const isDom = DOM_ACTIONS.has(action);
  const isTab = TAB_ACTIONS.has(action);
  const isDetect = (action === "detect");

  const msg = isDetect
    ? { origin: RTO_PAGE_ORIGIN, type: "detect", requestId }
    : isDom
      ? { origin: RTO_PAGE_ORIGIN, type: "command", action, payload: args, requestId, tabKey: args.tabKey, tabId: args.tabId }
      : { origin: RTO_PAGE_ORIGIN, type: action, ...args, requestId };

  const expectedType = isDetect ? "detectResult"
    : (isDom ? (action + "Result") : (action + "Result"));

  return new Promise((resolve) => {
    const t = setTimeout(() => done({ ok:false, error:{ code:"TIMEOUT", message:"No response within timeout" } }), timeoutMs);

    function done(out){
      clearTimeout(t);
      window.removeEventListener("message", onMsg);
      resolve(out);
    }

    function normalizeError(e){
      if (!e) return { code:"ACTION_FAILED", message:"Unknown error" };
      if (typeof e === "string") return { code:e, message:e };
      const code = e.code || e.error || e.name || "ACTION_FAILED";
      const message = e.message || e.msg || String(code);
      return { code, message };
    }

    function onMsg(ev){
      const d = ev && ev.data ? ev.data : null;
      if (!d || typeof d !== "object") return;
      if (d.origin !== RTO_EXT_ORIGIN) return;
      if (d.requestId !== requestId) return;
      if (d.type !== expectedType) return;

      if (d.ok === true) done({ ok:true, data:d.data || d });
      else done({ ok:false, error: normalizeError(d.error || d) });
    }

    window.addEventListener("message", onMsg);
    window.postMessage(msg, "*");
  });
}

(async () => {
  const det = await ask("detect", {}, { timeoutMs: 1200 });
  if (!det.ok) { console.warn("RTO not detected. Install the extension and reload."); return; }

  // 1) Open a named tab
  const opened = await ask("openTab", { url:"https://example.org/login", tabKey:"auth", focus:true });
  if (!opened.ok) { console.warn("openTab failed:", opened.error); return; }

  // 2) DOM automation (never hardcode real credentials in examples)
  await ask("setValue", { tabKey:"auth", selector:"#email", value:"user@example.org" });
  await ask("setValue", { tabKey:"auth", selector:"#password", value:"example-password" });
  await ask("click",    { tabKey:"auth", selector:"form button[type=submit]" });

  // 3) Read tab state
  const st = await ask("getUrl", { tabKey:"auth" });
  console.log("Auth tab:", st.ok ? st.data : st.error);
})();
</script>
✅ You can
  • Drive multiple tabs using tabKey / tabId.
  • Wait for SPA elements and click/type safely.
  • Read current URL/title and take screenshots (where permitted).
🚫 You cannot
  • Bypass Same-Origin Policy or read cookies.
  • Execute arbitrary code on arbitrary sites.
  • Automate file uploads (<input type="file"> limitation).

Remote Tab Opener cover

See the live E2E demonstration examples now.

Security by design

  • Runs locally in your browser — no backend required.
  • Works only on domains you authorize (allow-list).
  • LAN/localhost require explicit opt-in in the popup and must be present in the allow-list.
  • Predefined actions executed by the extension’s content script (no arbitrary code).
  • Respects browser security rules (no SOP bypass; clean messaging).

See the LAN/localhost page and full notes in the official documentation.

Controlled tab indicator (optional)

Controlled tabs can display a thin indicator banner at the very top (when enabled by the extension / page policy). It helps users understand they’re in a tab driven by RTO, and can surface the current tabKey.

  • Visible only in the controlled tab (never in other tabs).
  • Can be minimized into a compact, draggable chip.
  • Helps debugging: you instantly see “this tab is controlled”.
  • If a site blocks content script injection, the indicator won’t appear (tab is not controlled).
Quick try:
  1. Open a remote tab from your master page (e.g. tabKey:"sales").
  2. Minimize the banner.
  3. Drag the chip wherever you like.
  4. Reload the tab to confirm it’s still controlled.
DANGER : This tab is controlled by RTO (key: admin)
RTO plugin (key: admin)

Try it: Click the "" (Minimize) to collapse the banner into a draggable chip.

Drag the chip anywhere; click to restore the banner.

Browser Limits — What RTO Solves

Remote Tab Opener does not break browser security. It uses the extension layer to provide capabilities that ordinary pages don’t have, while still respecting SOP and user consent.

Native Limitation What RTO Enables (legit & local) What RTO Will Not Do
Page A cannot directly control a tab on another domain. Open / navigate / focus / close a tab via the extension bridge; run predefined DOM actions inside that tab (allow-listed host). No universal control of arbitrary sites; no cross-origin injection without permission.
Pages can’t reliably bring background tabs to front. focus brings the targeted tab to foreground (via extension APIs). No stealing focus randomly; only for known targeted tabs.
Direct DOM automation is scoped to same-origin only. command DOM actions run in the target tab via a content script on hosts you approved. No arbitrary eval(); no access to cookies or sessions.
Reading another tab’s URL/title is restricted. getUrl returns current url + title from the targeted tab. No network interception; no webRequest spying.
SPAs render late; selectors are brittle. waitForSelector helps you act only when UI is ready. Does not bypass CSRF/auth; does not fill <input type="file">.
Hard Guarantees
  • No SOP bypass. Actions run only with explicit host permission (allow-list).
  • No data collection. No remote servers required.
  • Local-only. All logic lives in the browser (background/content + postMessage).
  • User control. You choose which domains are controllable in the popup.

Quick Snippets (copy & run)

Open a named tab + wait + click (modern contract)
<script type="module">
  // Minimal: you can copy the ask() helper from the E2E section above.
  // Then:
  const det = await ask("detect", {}, { timeoutMs: 1200 });
  if (!det.ok) return;

  await ask("openTab", { url:"https://example.org/", tabKey:"demo", focus:true });
  await ask("waitForSelector", { tabKey:"demo", selector:"main a[href]" }, { timeoutMs: 12000 });
  await ask("click", { tabKey:"demo", selector:"main a[href]" });
</script>
Handle the allow-list error cleanly
const r = await ask("openTab", { url:"https://not-allowed.example", tabKey:"x" });
if (!r.ok && r.error && r.error.code === "DOMAIN_NOT_ALLOWED") {
  alert("Domain not allowed. Add it in the extension popup Allow-list, then retry.");
}

Tip: add your admin origin + target hosts in the allow-list. For intranet/localhost, enable the popup flags and keep hosts in the allow-list.

Use Cases

🔐
Moderate external content

Open a controlled tab to preview user content and move through submissions without leaving your dashboard.

🧪
QA navigation flows

Launch test sessions, navigate across cases, wait for SPA UI, and verify URL/title programmatically.

🧭
Guided support

Open the right help page, bring it to front when needed, and close it once the issue is resolved.

🚀
Admin workflows

Streamline repetitive navigation across domains from a single secure hub — safely, locally, visibly.

FAQ (short)

Does this bypass the Same-Origin Policy? No. The extension executes actions inside the target tab with explicit host permissions.

Do I need a server or proxy? No. Everything is local in the browser.

Is any data collected? No. The extension declares no data collection.

Where do I configure allowed domains? In the extension popup (allow-list). For LAN/localhost, enable the dedicated flags too.

How do I control 2 tabs? Use tabKey (e.g. "sales", "monitor") or a tabId from listTabs. Pass tabKey/tabId on each action.

Changelog (short)

  • v7.11.5: current Firefox MV3 release line — multi-tab orchestration via tabKey, modern command DOM actions, strong allow-list + optional LAN/localhost opt-in.
View the complete changelog

Honest comparison — RTO vs. alternatives

A visual guide to when each approach shines.

TL;DR: Downloading HTML ≠ Reading the rendered DOM. RTO queries the live, post-JavaScript DOM of a real tab and orchestrates named tabs with tabKey — great for demos, teaching, and local tooling.
What RTO does best
  • ✅ Reads the rendered (post-JS) DOM from a live tab
  • Named tab control: open / focus / close / navigate
  • Teaching-friendly UX: overlays/highlights, safe automation primitives
  • Local & visible: no cloud relay, allow-listed actions
  • Low setup: extension + small snippets
When another tool is better
  • 🏭 Headless at scale (CI farms): Playwright / Puppeteer
  • 🧪 Heavy E2E UI testing: WebDriver / RPA suites
  • 📄 Pure SSR/static pages: fetch() / view-source suffices
  • 🧩 One-off tweaks: userscripts / bookmarklets

Competitors

RTO - Remote Tab Opener

Live DOM orchestration for real, visible tabs — great for demos and education.

Rendered DOM ✓ Tab keys ✓ Local & visible ✓ Allow-list ✓
  • Use for: multi-tab demos, manual QA, teaching SPA realities
  • Avoid for: huge headless farms / heavy CI
Lightweight View-source / fetch()

Reads downloaded HTML only — great for SSR, blind to SPA hydration.

Rendered DOM ✗ Zero setup Static pages only
  • Use for: server-rendered pages, sitemap sweeps
  • Avoid for: SPAs (no hydrated anchors)
Headless Playwright / Puppeteer

Full-power automation, perfect for CI — less ideal for live teaching.

Rendered DOM ✓ Heavier setup Not “local visible” by default
  • Use for: scale, reliability, regression checks
  • Avoid for: human-visible demos / quick workshops
Enterprise WebDriver / RPA

Broad UI control across apps, but verbose and brittle for fast local tooling.

Rendered DOM ✓ Infra & maintenance Complex flows
  • Use for: enterprise E2E & regulated environments
  • Avoid for: lightweight, local demos
Hobbyist Userscripts / Bookmarklets

Quick wins inside the current page; limited cross-tab control.

Rendered DOM (same tab) No tab orchestration Low friction
  • Use for: small tweaks, shortcuts, helpers
  • Avoid for: multi-tab workflows
If you need to show the real rendered DOM and control named tabs safely, locally, and with minimal setup, RTO is the right tool.