一个用户脚本的两个实例如何在帧之间进行通信

How can two instances of a userscript communicate between frames?

本文关键字:通信 之间 两个 一个 用户 脚本 实例      更新时间:2024-06-19

参考在网页和iframe中运行相同JavaScript的技术,如以下答案所述:

例如,假设您在domain_A.com:上有此页面

<html>
<body>
    <iframe src="http://domain_B.com/SomePage.htm"></iframe>
</body>
</html>

如果你这样设置@match指令:

// @match http://domain_A.com/*
// @match http://domain_B.com/*

然后,您的脚本将运行两次——一次在主页上,一次在iframe上,就像它是一个独立的页面一样。


让脚本的两个实例相互通信的选项是什么?

这将是同步实例所必需的。例如,让iframe脚本实例仅在网页脚本实例完成后执行其任务,反之亦然。

这两个脚本实例可以使用postMessage()进行通信。关于:

这将是同步实例所必需的,因此例如,只有在网页完成后,iframe才能执行其任务,反之亦然。

这就是另一个答案
但是Chrome在向扩展提供框架/iframe方面存在缺陷因此,要解决这些错误,必须注入调用postMessage()的代码。

下面的脚本显示了如何操作。它:

  • 在iframe和包含页面中运行
  • 处理跨域iframe
  • 它演示了具有以下逻辑的脚本间控制:
    1. 容器页面设置为侦听来自iframe的消息
    2. iframe设置为侦听来自容器页面的消息
    3. iframe将第一条消息发送到容器页面
    4. 当容器页面接收到该消息时,它会将另一条消息发送回iframe

安装此脚本(由于CertainPerformance的更新,多年来目标站点发生了变化):

// ==UserScript==
// @name        _Cross iframe, cross-domain, interscript communication
// @include     http://fiddle.jshell.net/2nmfk5qs/*
// @include     http://puppylinux.com/
// @grant       none
// ==/UserScript==
/* eslint-disable no-multi-spaces */
if (window.top === window.self) return;
console.log ("Script start...");
if (window.location.href.includes('fiddle')) {
    console.log ("Userscript is in the MAIN page.");
    //--- Setup to process messages from the GM instance running on the iFrame:
    window.addEventListener ("message", receiveMessageFromFrame, false);
    console.log ("Waiting for Message 1, from iframe...");
}
else {
    console.log ("Userscript is in the FRAMED page.");
    //--- Double-check that this iframe is on the expected domain:
    if (/puppylinux'.com/i.test (location.host) ) {
        window.addEventListener ("message", receiveMessageFromContainer, false);
        //--- Send the first message to the containing page.
        sendMessageFromAnIframe (
            "***Message 1, from iframe***", "http://fiddle.jshell.net"
        );
        console.log ("Waiting for Message 2, from containing page...");
    }
}
function receiveMessageFromFrame (event) {
    if (event.origin != "http://puppylinux.com")    return;
    console.log ('The container page received the message, "' + event.data + '".');
    //--- Send message 2, back to the iframe.
    sendMessageToAnIframe (
        "#testIframe",
        "***Message 2, from the container page***",
        "http://puppylinux.com"
    );
}
function receiveMessageFromContainer (event) {
    if (event.origin != "http://fiddle.jshell.net")    return;
    console.log ('The iframe received the message, "' + event.data + '".');
}
/*--- Because of bugs in how Chrome presents frames to extensions, we must inject
    the messaging code. See bug 20773 and others.
    frames, top, self.parent, contentWindow, etc. are all improperly undefined
    when we need them.  See Firefox and other browsers for the correct behavior.
*/
function sendMessageFromAnIframe (message, targetDomain) {
    var scriptNode          = document.createElement ('script');
    scriptNode.textContent  = 'parent.postMessage ("' + message
                            + '", "' + targetDomain + '");'
                            ;
    document.body.appendChild (scriptNode);
}
function sendMessageToAnIframe (cssSelector, message, targetDomain) {
    function findIframeAndMessageIt (cssSelector, message, targetDomain) {
        var targetIframe    = document.querySelector (cssSelector)
        if (targetIframe) {
            targetIframe.contentWindow.postMessage (message, targetDomain);
        }
    }
    var scriptNode          = document.createElement ('script');
    scriptNode.textContent  = findIframeAndMessageIt.toString ()
                            + 'findIframeAndMessageIt ("' + cssSelector
                            + '", "' + message
                            + '", "' + targetDomain + '");'
                            ;
    document.body.appendChild (scriptNode);
}
console.log ("Script end");


然后访问jsFiddle上的这个测试页面

您将在javascript控制台中看到这一点:

脚本启动。。。Userscript在MAIN页面中。正在等待来自iframe的消息1。。。脚本结束脚本启动。。。Userscript位于FRAMED页面中。正在等待来自包含页面的消息2。。。脚本结束容器页面收到消息"***消息1,来自iframe***"。iframe从容器页面***收到消息"***消息2"。