如何在跨域场景下实现GreasyMonkey脚本的JavaScript postMessage通信?
跨域窗口postMessage通信实现方案(GreasyMonkey脚本)
背景
我正在编写一个GreasyMonkey脚本,希望让site-A(例如Google)获取并处理site-B(例如Yandex)的结果,预期最终代码形式如下:
// script on google.com async function test1(){ let targetURL = "https://yandex.com/"; let pWin = window.open(targetURL); let result = await GetAnswer(pWin, "what is A"); alert(result) // "A is not B" function GetAnswer(win, question){ // get search result by postMessage } }
问题
暂时先不考虑async/await部分,在使用postMessage进行通信时遇到以下问题:
- 如果立即发送消息,
site-B接收不到,因为消息在页面就绪前就已发送:
let pWin = window.open(targetURL); pWin.postMessage("search 'what is A'", targetURL);
- 如果尝试在
onload事件中发送消息,由于跨域的pWin无法访问onload事件,这种方式也不生效:
let pWin = window.open(targetURL); pWin.onload=()=>{ pWin.postMessage("search 'what is A'", targetURL); }
我是第一次使用postMessage,需要示例学习如何在非自有跨域站点间实现通信。
实现方案
要解决这个问题,需要分别在site-A和site-B的GreasyMonkey脚本中配合处理,核心逻辑是让site-B加载完成后主动告知site-A,site-A再发送请求,具体如下:
1. site-A(如Google)的脚本代码
// ==UserScript== // @name Site-A 跨域请求脚本 // @match https://www.google.com/* // @grant none // ==/UserScript== (async function test1() { const targetURL = "https://yandex.com/"; const pWin = window.open(targetURL); const question = "what is A"; // 等待site-B的就绪信号,再发送请求 await new Promise(resolve => { function listenReady(e) { if (e.origin === targetURL && e.data.type === "ready") { window.removeEventListener("message", listenReady); resolve(); } } window.addEventListener("message", listenReady); }); // 向site-B发送查询请求 pWin.postMessage({ type: "query", question: question }, targetURL); // 等待site-B返回结果 const result = await new Promise(resolve => { function listenResult(e) { if (e.origin === targetURL && e.data.type === "result") { window.removeEventListener("message", listenResult); resolve(e.data.content); } } window.addEventListener("message", listenResult); }); alert(result); })();
2. site-B(如Yandex)的脚本代码
// ==UserScript== // @name Site-B 跨域响应脚本 // @match https://yandex.com/* // @grant none // ==/UserScript== window.addEventListener("load", () => { // 页面加载完成后,向父窗口(site-A)发送就绪信号 window.opener.postMessage({ type: "ready" }, "https://www.google.com/"); // 直接写site-A域名更安全 }); // 监听site-A发来的查询请求 window.addEventListener("message", e => { // 验证来源,确保是可信的site-A if (e.origin === "https://www.google.com/" && e.data.type === "query") { const question = e.data.question; // 这里模拟处理查询,实际中替换为获取Yandex搜索结果的逻辑 const result = `${question} 的结果:A is not B`; // 将结果返回给site-A e.source.postMessage({ type: "result", content: result }, e.origin); } });
关键注意事项
- origin验证:所有
postMessage的接收方必须验证消息来源(e.origin),避免恶意网站的消息干扰,不要盲目信任所有消息。 - 消息格式约定:用
type字段区分不同类型的消息(如ready、query、result),避免消息处理混乱。 - GreasyMonkey权限:确保脚本的
@match规则正确匹配目标站点,@grant设置为none即可(不需要额外权限)。
内容的提问来源于stack exchange,提问作者Byzod
相关产品推荐
相关产品推荐

