Messaging protocol
RTO_form_api.js or the shipped helper layers. Read this page when you need the exact message shape or want to debug an edge case deeply.Overview
RTO uses window.postMessage to exchange messages between your page and the extension. The helper RTO_form_api.js handles:
- Generating
requestIdvalues - Waiting for the matching
*Resultmessage - Timeouts (
TIMEOUT)
RTO_form_api.js unless you are building your own wrapper.External add-on protocol — introduced in v8.4.0, current in v8.5.0
RTO v8.4.0 introduced direct inter-extension messages from trusted official add-ons. The protocol remains current in RTO v8.5.0. The add-on is identified by the browser extension runtime, then RTO Original applies its Trust Policy before returning capabilities or accepting commands.
Modern allow-list flow
In 8.4.0, a page should still ask through allowlistAddRequest.
The plugin then prompts on the matching master tab, and the master tab answers with either allowlistAddConfirm or allowlistAddReject.
| Message | Direction | Role |
|---|---|---|
allowlistAddRequest |
page → extension | Ask for a host to be added from a normal page. |
promptAllowlistAdd |
extension → master tab | Visible prompt sent to the master tab UI. |
allowlistAddConfirm |
master tab → extension | Approve the pending request. |
allowlistAddReject |
master tab → extension | Reject the pending request. |
allowlistAddRequestResult |
extension → page | Final success or failure returned to the requesting page. |
allowlistAdd is no longer the normal public flow.
It is intentionally blocked unless the plugin/internal UI performs the confirmation step.
Message envelope
Outgoing messages usually contain:
origin: a page-side marker (docs default:window.RTO_PAGE_ORIGIN)type: for example"detect","openTab", or"command"requestId: unique ID to match replies
The extension responds with type + "Result" (example: detectResult, commandResult).
origin value is a protocol marker, not a security boundary. Security is enforced by the extension (allow-list + controlled tabs).
For raw code, always filter messages strictly (see examples).
Raw detect example
// Raw detect (without RTO_form_api.js)
(function(){
const requestId = "detect_" + Date.now() + "_" + Math.random().toString(16).slice(2);
const pageOrigin = window.RTO_PAGE_ORIGIN || "rto-docs";
let t;
function done(){
if(t) clearTimeout(t);
window.removeEventListener("message", onMsg);
}
function onMsg(ev){
const d = ev.data;
if(ev.source !== window) return; // same window only
if(!d || typeof d !== "object") return;
if(d.origin !== pageOrigin) return; // protocol marker must match
if(d.requestId !== requestId) return;
if(d.type !== "detectResult") return;
done();
console.log("detect result:", d);
}
window.addEventListener("message", onMsg);
t = setTimeout(function(){
done();
console.warn("detect timeout");
}, 1500);
window.postMessage({
origin: pageOrigin,
type: "detect",
requestId: requestId
}, "*");
})();
Raw command example
DOM actions are sent as type:"command" with an action name and a payload object.
// Raw command (example: getText)
(function(){
const requestId = "cmd_" + Date.now() + "_" + Math.random().toString(16).slice(2);
const pageOrigin = window.RTO_PAGE_ORIGIN || "rto-docs";
let t;
function done(){
if(t) clearTimeout(t);
window.removeEventListener("message", onMsg);
}
function onMsg(ev){
const d = ev.data;
if(ev.source !== window) return;
if(!d || typeof d !== "object") return;
if(d.origin !== pageOrigin) return;
if(d.requestId !== requestId) return;
if(d.type !== "commandResult") return;
done();
console.log("command result:", d);
}
window.addEventListener("message", onMsg);
t = setTimeout(function(){
done();
console.warn("command timeout");
}, 5000);
window.postMessage({
origin: pageOrigin,
type: "command",
requestId: requestId,
action: "getText",
payload: {
tabKey: "demoTab",
selector: "h1"
}
}, "*");
})();