TamperMonkey - 不同子域上的脚本之间的消息

时间:2016-12-12 23:47:35

标签: javascript jquery greasemonkey tampermonkey

我有两个脚本。每个都在我们公司"Example.com".

的不同子域上运行
Script #1 -- house.example.com
Script #2 -- bob.fred.example.com

相同的域名,不同的子域名。

house.example.com上出现特定元素时,我需要向bob.fred.example.com上运行的脚本发送消息

由于Google扩展程序可以在扩展程序之间交换消息,因此必须有一种方法可以让TamperMonkey在脚本之间的相同扩展名内交换消息 - 特别是如果它们在同一个二级域上运行。

有人能指出我正确的方向吗?一两个例子就值得用金子来衡量。

更新尽管Gothdo将Javascript communication between browser tabs/windows引用为包含此问题的答案,但他没有考虑所涉及的跨域政策。 该引用问题中的答案都没有为跨域浏览器标签通信提供明确答案,这是此问题的主要内容。 我现在已经研究并解决了这个问题问题,从一些SO和非SO来源获取想法。如果重新打开这个问题,我会发布我的解决方案。

3 个答案:

答案 0 :(得分:5)

您可以使用GM_getValueGM_setValue& GM_addValueChangeListener实现交叉表用户脚本通信。

在用户脚本标题中添加以下行。

// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addValueChangeListener

以下粗略代码行将简化交叉表用户脚本的通信。

function GM_onMessage(label, callback) {
  GM_addValueChangeListener(label, function() {
    callback.apply(undefined, arguments[2]);
  });
}

function GM_sendMessage(label) {
  GM_setValue(label, Array.from(arguments).slice(1));
}

所以你需要做的就是发送和接收消息。

GM_onMessage('_.unique.name.greetings', function(src, message) {
  console.log('[onMessage]', src, '=>', message);
});
GM_sendMessage('_.unique.name.greetings', 'hello', window.location.href);

注意如果发送的消息与以前相同,则发送消息可能不会触发您的回调。这是由于GM_addValueChangeListener未触发,因为值未发生变化,即即使调用GM_setValue也与之前的值相同。

答案 1 :(得分:1)

使用@grant启用沙箱,在尝试与Greasemonkey上的复杂页面对象进行交互时,有时会造成困难。

如果您不想 启用带有@grant的沙箱,则另一个选择是让用户脚本将iframe创建到另一个域,然后向其中发布消息。在另一个域的iframe中,侦听消息。收到消息后,请使用BroadcastChannel将消息发送到该其他域上的每个其他选项卡,并且运行用户脚本的其他选项卡可以打开相同的BroadcastChannel并收听消息。

例如,要在stackoverflow.com上创建一个用户脚本,以便将消息发送到在example.com上其他选项卡中运行的用户脚本:

// ==UserScript==
// @name             0 Cross-tab example
// @include          /^https://example\.com\/$/
// @include          /^https://stackoverflow\.com\/$/
// @grant            none
// ==/UserScript==

if (window.location.href === 'https://example.com/') {
  const broadcastChannel = new BroadcastChannel('exampleUserscript');
  if (window.top !== window) {
    // We're in the iframe:
    window.addEventListener('message', (e) => {
      if (e.origin === 'https://stackoverflow.com') {
        broadcastChannel.postMessage(e.data);
      }
    });
  } else {
    // We're on a top-level tab:
    broadcastChannel.addEventListener('message', (e) => {
      console.log('Got message', e.data);
    });
  }
} else {
  // We're on Stack Overflow:
  const iframe = document.body.appendChild(document.createElement('iframe'));
  iframe.style.display = 'none';
  iframe.src = 'https://example.com';

  setTimeout(() => {
    iframe.contentWindow.postMessage('Sending message from Stack Overflow', '*');
  }, 2000);
}

结果是:

enter image description here

如果您要双向通信,而不仅仅是单向通信,请两个父页面都将子iframe创建到单个目标域(例如,到example.com)。要与其他标签进行交流,请向子iframe发布一条消息。让子iframe监听消息,并在看到时发布BroadcastChannel消息以与所有其他iframe进行通信。当iframe接收到BroadcastChannel消息时,请使用postMessage将其中继到父窗口。

// ==UserScript==
// @name             0 Cross-tab example
// @include          /^https://example\.com\/$/
// @include          /^https://(?:stackoverflow|stackexchange)\.com\/$/
// @grant            none
// ==/UserScript==

if (window.location.href === 'https://example.com/') {
  const broadcastChannel = new BroadcastChannel('exampleUserscript');
  if (window.top !== window) {
    // We're in an iframe:
    window.addEventListener('message', (e) => {
      console.log('iframe received message from top window');
      if (e.origin === 'https://stackoverflow.com' || e.origin === 'https://stackexchange.com') {
        broadcastChannel.postMessage(e.data);
      }
    });
    broadcastChannel.addEventListener('message', (e) => {
      console.log('iframe received message from BroadcastChannel');
      window.top.postMessage(e.data, '*');
    });
  }
} else {
  // We're on Stack Overflow or Stack Exchange
  const iframe = document.body.appendChild(document.createElement('iframe'));
  iframe.style.display = 'none';
  iframe.src = 'https://example.com';
  window.addEventListener('message', (e) => {
    if (e.origin === 'https://example.com') {
      console.log(`Top window ${window.origin} received message from iframe:`, e.data);
    }
  });
  if (window.location.href === 'https://stackoverflow.com/') {
    setTimeout(() => {
      console.log('stackoverflow posting message to iframe');
      iframe.contentWindow.postMessage('Message from stackoverflow', '*');
    }, 2000);
  }
}

在上面的代码中,Stack Overflow上的选项卡将消息发送到Stack Exchange上的选项卡。结果截图:

enter image description here

答案 2 :(得分:-1)

我最终用于同一域上子域之间的标签到标签通信的方法是通过javascript cookie传递信息。 (我也尝试过使用localStorage,但在子域之间无效。)

场景:子域A上的选项卡A将向子域B上的选项卡B发送消息:

代码看起来像这样:

function getCookie(cooVal) {
    var cname = cooVal+ '=';
    var ca = document.cookie.split(';');
    for (var i=0; i < ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1,c.length);
        if (c.indexOf(cname) === 0) {
            return c.substring(cname.length, c.length);
        }
    }
    return null;
} //END getcookie()

subDomainB上的Tab B将从子域A上的TabA RECEIVE 消息<:p>

function checkIncomingCIQ(){
    var acciq = getCookie('acciq');
    var jdlwc = getCookie('jdlwc');
}

TabA将发送消息发送到标签B,如下所示:

document.cookie="acciq=5; domain=.example.com; path=/";
document.cookie="jdlwc=fubar; domain=.example.com; path=/";

对于任何想知道的人,是的,子域名可以互相发送消息 - 这不仅仅是单向通信。只需在另一个方向复制相同的场景。

当然,在两个选项卡上,消息传递系统都在一个javascript循环中,如下所示:

(function foreverloop(i) {
    //Do all my stuff - send/receive the cookies, do stuff with the values, etc
    setTimeout(function() {
        foreverloop(++i);
    },2000);
}(0)); //END foreverloop

两个标签上的TM标题如下所示:

// ==UserScript==
// @namespace    abcd.tops.example.com
// @match        *://abcd.tops.example.*/*
// @grant        none
// @require     http://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js
// ==/UserScript==
and
// ==UserScript==
// @namespace    http://mysubdomain.example.com/callcenter/
// @match        *://*.example.com/callcenter/
// @grant        none
// @require     http://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js
// ==/UserScript==

向所有人推迟发布此解决方案的道歉。花了很长时间才把这个问题重新打开,因为它被错误地标记为重复生命。 。