You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在跨域场景下实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 19:25:23