Как два экземпляра пользовательского скрипта могут обмениваться данными между кадрами?

Обратитесь к методу запуска одного и того же JavaScript как на веб-странице, так и в iframe, как описано в этом ответе:

Например, предположим, что у вас есть эта страница по адресу 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 выполняет свою задачу только после завершения экземпляра сценария веб-страницы, и наоборот.


person Exilas    schedule 01.08.2012    source источник


Ответы (1)


Два экземпляра сценария могут взаимодействовать друг с другом с помощью postMessage(). Касательно:

Это потребуется для синхронизации экземпляров, например, чтобы iframe выполнял свою задачу только после завершения веб-страницы, и наоборот.

Это то, что показано в этом другом ответе.
Но В Chrome есть ошибки в представлении фреймов/iframe для расширений Итак, чтобы обойти эти ошибки, вы должны внедрить код, вызывающий postMessage().

Следующий сценарий показывает, как это сделать. Это:

  • Работает как в iframe, так и в содержащей его странице.
  • Обрабатывает междоменные фреймы.
  • It demonstrates inter-script control having the following logic:
    1. The container page sets up to listen for messages from the 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:

Script start...
Userscript is in the MAIN page.
Waiting for Message 1, from iframe...
Script end
Script start...
Userscript is in the FRAMED page.
Waiting for Message 2, from containing page...
Script end
The container page received the message, "***Message 1, from iframe***".
The iframe received the message, "***Message 2, from the container page***".
person Brock Adams    schedule 02.08.2012
comment
Брок, я только что вернулся из отпуска и увидел твой ответ. Спасибо большое, изучу подробно и попробую применить к своему делу. - person Exilas; 21.08.2012
comment
1) На данный момент можно ли использовать postMessage в Chrome? Вы упомянули, что код нужно будет ввести, потому что у расширений Chrome есть проблемы. Это все еще так? 2) В качестве дополнения к исходному вопросу об использовании postMessage в пользовательских скриптах, возможно ли, чтобы сам веб-сайт перехватывал использование postMessage в пользовательском скрипте? - person Edge; 13.03.2015
comment
@Edge: Да и да (но если вы использовали скрипт в Tampermonkey и установили @grant none, инъекция может не понадобиться). И да, во многих случаях веб-страница может перехватывать сообщения postMessages. Я еще не видел настоящего сайта, который был бы таким анальным/параноидальным. - person Brock Adams; 13.03.2015
comment
Пример больше не работает - похоже, что JSFiddle изменили свое представление, так что собственно код находится в iframe, несмотря ни на что (поэтому даже переход к .../show/light приводит к странице с... встроенным iframe, ссылающимся на .../show/light), поэтому тест window.top ведет себя не так, как ожидалось. Puppylinux также больше не использует www. - person CertainPerformance; 09.04.2019
comment
gist.github.com/CertainPerformance/ работает на fiddle.jshell.net/2nmfk5qs/show/light работает над ТМ - person CertainPerformance; 09.04.2019
comment
Спасибо, @CertainPerformance. Обновил ответ с вашими исправлениями. В настоящее время я бы, вероятно, использовал вместо этого GM_addValueChangeListener (теперь, когда он существует), поскольку postMessage был несколько неудобным. - person Brock Adams; 10.04.2019