Plugin / Docs / DOM & Automation

DOM & Automation — Full Catalog

Interact safely with the remote page: type, click, select, submit, wait, highlight, style, read fragments, run small page-side snippets.

Everything runs inside the controlled tab (content script) — no SOP bypass, no remote server. v7.10.1

v7.10.1
Multi-tab: all actions accept tabKey (or tabId). LAN/localhost: see the LAN page (double barrier + allow-list).

2) Minimal Request Helper (canonical)

Place a single helper in your layout. It returns { ok, data } or { ok:false, error:{ code, message } }.

<script>
const ADMIN_ORIGIN='admin', EXT_ORIGIN='extension';
let _seq=0, _waiters=new Map();

addEventListener('message', (e) => {
  const d=e.data; if(!d||typeof d!=='object') return;
  if(d.origin!==EXT_ORIGIN) return;
  if(d.requestId && _waiters.has(d.requestId)) { _waiters.get(d.requestId)(d); _waiters.delete(d.requestId); }
});

function ask(action, args={}, timeoutMs=12000){
  const requestId='r'+(++_seq);
  postMessage({ origin:ADMIN_ORIGIN, type:'RTO_REQUEST', requestId, action, args }, '*');
  return new Promise((resolve,reject)=>{
    const t=setTimeout(()=>{ _waiters.delete(requestId); reject(Object.assign(new Error('timeout'),{ code:'TIMEOUT' })); }, timeoutMs);
    _waiters.set(requestId, (resp)=>{ clearTimeout(t); resolve(resp); });
  });
}
</script>

3) Safety Model (important)

  • No SOP bypass: the content script acts within the target page. Cross-origin iframes remain inaccessible.
  • Mandatory allow-list (deny-by-default): actions only work on hosts added by the user. On first attempt, an embedded banner proposes to add the domain, then replays the action with the same focus.
  • Refused schemes: http://, file://, javascript:. LAN/localhost targets are disabled by default and require double opt-in (flags + allow-list). See LAN.
  • Sensitive-field guard: read/write is blocked on passwords and sensitive fields → ok:false with error.code:"FORBIDDEN".
  • Faithful events: typing and value updates trigger input/change to mimic real user behavior.
  • Control banner: a thin orange banner (can minimize into a draggable chip) shows only in the controlled tab.

4) Action Catalog (v7.10.1)

Call ask('<action>', { ...args, tabKey? }) directly — no domAction envelope required.

Action Main args Effect Return
domType { selector, value, delayMs? } Types character by character (optional), then fires input/change. { ok:true }
domSetValue { selector, value, clear? } Sets value immediately (no delay), fires input/change. { ok:true } or { ok:false, error:{ code:'FORBIDDEN' }}
domClick { selector } Scrolls into view and clicks a visible element. { ok:true }
focusElement { selector } Sets focus (tries to avoid scroll jumps). { ok:true }
submitForm { selector } (a <form>) Uses requestSubmit() if available, otherwise a safe fallback. { ok:true }
selectSetValue { selector, value } Changes a <select> and triggers input/change. { ok:true }
waitFor { selector, timeoutMs } Resolves when the element appears, otherwise TIMEOUT. { ok:true } or { ok:false, error:{ code:'TIMEOUT' }}
domSetStyle { selector, style:{ ... } } Applies a safe subset of CSS properties (with !important). { ok:true }
highlight { selector, color?, ms? } Temporary halo; auto-restore after ms. { ok:true }
getHtml { selector, property } where propertyouterHTML|innerHTML|textContent|innerText|value Reads a safe fragment (with sensitive-field guard). { ok:true, data:{ value:<string> } } or an error
Compatibility: if your project still uses { type:'domAction', action:'...' }, see the “Legacy → current” table below.
Legacy (≤ v7.9.x)v7.10.1 (recommended)
{ type:'domAction', action:'type', payload:{...} }ask('domType', {...})
{ type:'domAction', action:'setValue', payload:{...} }ask('domSetValue', {...})
{ type:'domAction', action:'click', payload:{...} }ask('domClick', {...})
{ type:'domAction', action:'submit', payload:{...} }ask('submitForm', {...})
{ type:'domAction', action:'selectSetValue', payload:{...} }ask('selectSetValue', {...})
{ type:'domAction', action:'waitFor', payload:{...} }ask('waitFor', {...})
{ type:'domAction', action:'domSetStyle', payload:{...} }ask('domSetStyle', {...})
{ type:'domAction', action:'highlight', payload:{...} }ask('highlight', {...})
{ type:'domAction', action:'getHtml', payload:{...} }ask('getHtml', {...})

5) Safe Style & Highlight

5.1 Apply a safe style
await ask('domSetStyle', {
  selector:'#email',
  style:{ outline:'3px solid #1e88e5', backgroundColor:'rgba(30,136,229,0.10)' },
  tabKey:'sales'
});

Allowed families: outline*, background*, border*, boxShadow.

5.2 Temporary highlight
await ask('highlight', { selector:'#email', color:'#FFCC00', ms:1400, tabKey:'sales' });

6) Typing & Value Updates

6.1 Typing with per-character delay

Useful for fields with live validation/search.

await ask('domType', {
  selector:'#email', value:'dev@example.com', delayMs:20, tabKey:'sales'
});
6.2 Instant set

Faster; still triggers input/change.

await ask('domSetValue', {
  selector:'#search', value:'hello world', clear:true, tabKey:'sales'
});
Note: browsers do not allow programmatic filling of <input type="file">.

7) Click & Submit

7.1 Click a button

Scrolls into view, then clicks.

await ask('domClick', { selector:'button[type=submit]', tabKey:'sales' });

If the button is disabled, fill required fields first.

7.2 Submit a form

Works even without a visible button.

await ask('submitForm', { selector:'form#login', tabKey:'sales' });

8) Dropdowns

8.1 By value
await ask('selectSetValue', { selector:'select#country', value:'BE', tabKey:'sales' });
8.2 By visible label (via runJs)

If you need to select by label:

await ask('runJs', { code:`(function(){
  const s=document.querySelector('select#country'); if(!s) return false;
  const opt=[...s.options].find(o=>o.textContent.trim()==='Belgium'); if(!opt) return false;
  s.value=opt.value; s.dispatchEvent(new Event('input',{bubbles:true}));
  s.dispatchEvent(new Event('change',{bubbles:true}));
  return true;
})()`, tabKey:'sales' });

9) Wait for elements (SPA-friendly)

9.1 Wait → Then act
// Wait for the form
await ask('waitFor', { selector:'#email', timeoutMs:20000, tabKey:'sales' });
// Fill & submit
await ask('domType', { selector:'#email', value:'dev@example.com', tabKey:'sales' });
await ask('domType', { selector:'#note',  value:'hello world',     tabKey:'sales' });
await ask('domClick', { selector:'button[type=submit]',           tabKey:'sales' });
Timeouts: if missing, you get { ok:false, error:{ code:'TIMEOUT' }}.

10) Reading with getHtml

10.1 Read text
const r = await ask('getHtml', { selector:'#lorem-block', property:'textContent', tabKey:'sales' });
console.log('Text (first 120):', (r.ok ? r.data.value : '').trim().slice(0,120)+'…');
10.2 Read a value (with sensitive guard)
const r = await ask('getHtml', { selector:'#search', property:'value', tabKey:'sales' });
if (r.ok) console.log('Value:', r.data.value);
else console.warn('getHtml failed:', r.error?.code);

On sensitive fields (password/CC/IBAN…), the response is ok:false, error.code:'FORBIDDEN'.

11) runJs (small page-side snippets)

Keep your snippets small and self-contained. For sensitive operations, prefer named actions.

11.1 Read a title/counter
const { ok, data, error } = await ask('runJs', { code:'document.title', tabKey:'sales' });
console.log('Title:', ok ? data : error?.message);
11.2 Modify a fragment
await ask('runJs', { code:`(function(){
  const el = document.querySelector('#status'); if (!el) return false;
  el.textContent = 'Connected ✅'; el.classList.add('ok'); el.style.outline = '2px solid #0d6efd'; return true;
})()`, tabKey:'sales' });
11.3 Insert a floating banner
await ask('runJs', { code:`(function(){
  const id='rto-banner'; let el=document.getElementById(id);
  if(!el){ el=document.createElement('div'); el.id=id;
    el.textContent='Controlled by Remote Tab Opener';
    el.style.cssText='position:fixed;bottom:0;left:0;right:0;padding:8px;background:#0d6efd;color:#fff;z-index:2147483647;font:14px system-ui';
    document.body.appendChild(el);
  } return true;
})()`, tabKey:'sales' });

12) Error Mapping

Always check ok. On failure, use error.code (and message) to guide the user.

error.code Actions Probable cause Solution
DOMAIN_NOT_ALLOWED open/navigate/DOM Domain not on the allow-list. App-side banner + extension popup. Replay after adding.
INVALID_URL openTab/navigate Non-HTTPS/forbidden/LAN URL. Absolute https:// URL. See LAN.
NO_CONTROLLED_TAB getCurrentUrl/DOM/runJs No controlled tab. Call openTab or navigate first.
ELEMENT_NOT_FOUND domType/domSetValue/domClick/… Wrong selector or DOM not ready. waitFor, review selector, account for SPA rendering.
TIMEOUT waitFor Element never appeared. Increase timeoutMs, retries, other signal.
FORBIDDEN domType/domSetValue/getHtml(value) Protected sensitive field. Avoid reading/writing secrets; adapt the flow.
EXECUTION_ERROR runJs Exception (null/CSP/syntax). Short snippets, IIFE, null-guards, no remote imports.

13) Patterns & Mini-Recipes

13.1 Reliable login (idempotent)
await ask('navigate', { url:'https://app.example.com/login', tabKey:'auth' });
// if already signed in, redirect to /dashboard is fine
await ask('waitFor',      { selector:'#email', timeoutMs:20000, tabKey:'auth' });
await ask('domSetValue',  { selector:'#email', value:'user@example.com', tabKey:'auth' });
await ask('domSetValue',  { selector:'#note',  value:'hello world',     tabKey:'auth' });
await ask('domClick',     { selector:'button[type=submit]',             tabKey:'auth' });
await ask('waitFor',      { selector:'.badge.ready', timeoutMs:30000,   tabKey:'auth' });
13.2 Check a radio/checkbox

Many UIs hide the real input; clicking the label is often simpler. Otherwise:

await ask('runJs', { code:`(function(){
  const box = document.querySelector('#tos'); if(!box) return false;
  box.checked = true; box.dispatchEvent(new Event('change',{bubbles:true})); return true;
})()`, tabKey:'auth' });
13.3 Scroll + halo
await ask('runJs', { code:`(function(){
  const el = document.querySelector('#important'); if(!el) return false;
  el.scrollIntoView({behavior:'smooth', block:'center'});
  el.style.transition='box-shadow .2s ease';
  el.style.boxShadow='0 0 0 4px rgba(13,110,253,.35)';
  setTimeout(()=>{ el.style.boxShadow=''; }, 1200);
  return true;
})()`, tabKey:'auth' });
13.4 Extract a counter and assert
const { ok, data } = await ask('runJs', { code:`(function(){
  const n=document.querySelector('#cart-count'); return { result: n?parseInt(n.textContent,10):0 };
})()`, tabKey:'auth' });
if (!ok || data.result < 1) throw new Error('Cart is empty');

14) Pitfalls & Edge Cases

  • Cross-origin iframes: inaccessible by design. Navigate directly to the target origin.
  • Hidden/disabled elements: a click will fail. Meet prerequisites first (required fields, accordions…).
  • Fragile selectors: prefer stable #id or [data-testid].
  • Slow apps: raise timeoutMs (20–30s) and use waitFor.
  • File inputs: no programmatic filling.
Need help writing safe flows?
Try the ChatGPT helper: Remote Tab Opener Copilot.

Next Pages

Page Description
Start Overview, how it works, setup, quick start, and links to all sections.
Detect the Extension Detection contract (ping/pong), install prompts, and graceful fallbacks when the extension isn’t present.
Allow-list & Permissions Deny-by-default model, domain allow-list via popup, and suggested UX to guide users to enable hosts.
LAN / LocalhostHow to safely enable intranet/localhost (flags + allow-list).
Remote Tab Control Open/navigate/focus the controlled tab, read URL/title, select an existing tab, lifecycle & restore patterns.
DOM & Automation Action catalog (type, setValue, click, select, submit, waitFor) and safe interaction tips.
Recipes & Flows Copy-paste flows: resilient login, dashboard checks, table waits, filters, playlists, and allow-list banners.
Events Event stream reference, subscription helpers, live logger widget, and basic metrics/durations collection.
Diagnostics Inline console, error cookbook, QA self-check, and performance tips for troubleshooting flows quickly.
Full API Reference Canonical message envelopes, request/response schemas, error codes, and return shapes for every action.
Compatibility Supported browsers/versions, permissions nuances, CSP/iframe notes, and known limitations or workarounds.
Advanced Patterns Idempotent flows, retries/backoff, state restore after reload, playlist strategies, and robust selectors.
Favorites Star and group URLs, quick actions (open/focus/navigate), export/import sets, and playlist runs from favorites.
Real-World Flows (E2E) Run end-to-end flows from your admin page: helper setup, modern message contract, copy-paste journeys (login, multi-tab, extract), lightweight assertions, best practices, and troubleshooting.