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

TamperMonkey - message between scripts on different subdomains

我有两个脚本。每个 运行 在我们公司的不同子域上 "Example.com".

Script #1 -- house.example.com
Script #2 -- bob.fred.example.com

相同的域,不同的子域。

当特定元素出现在 house.example.com 上时,我需要向 bob.fred.example.com

上的脚本 运行ning 发送消息

由于 Google 扩展可以在扩展之间交换消息,因此必须有一种方法可以使用 TamperMonkey 在同一扩展内、脚本之间交换消息——尤其是当它们 运行 在同一秒时——级域。

谁能指出我正确的方向?一两个例子就值他们的黄金重量。


更新: 尽管 Gothdo 引用 Javascript communication between browser tabs/windows 包含这个问题的答案,但他没有考虑到涉及的跨源策略。 None 该引用问题的答案为跨源浏览器选项卡通信提供了明确的答案,这是该问题的重点。 我现在已经研究并解决了这个问题,从许多 SO 和非 SO 来源获得了想法。如果这个问题被重新打开,我会post我的解决方案。

您可以使用GM_getValueGM_setValue & GM_addValueChangeListener实现cross-tab用户脚本通信。

在您的用户脚本中添加以下行 header。

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

以下粗略的代码行将简化 cross-tab 用户脚本通信。

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 也与以前相同的值。

我最终用于 tab-to-tab 同一域中子域之间通信的方法是通过 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()

子域 B 上的选项卡 B 将 从子域 A 上的选项卡 A 接收 邮件:

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

TabA 会像这样 SEND 消息到 Tab B:

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

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

当然,在这两个选项卡上,消息传递系统都在一个 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 headers 如下所示:

// ==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==

对于延迟发布此解决方案向所有人致歉。在问题被错误地标记为重复之后,问题花了很长时间才成为 re-opened,生活还在继续。 . .

使用 @grant 启用沙箱,这有时会导致在 Greasemonkey 上尝试与复杂页面对象交互时出现困难。

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

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

// ==UserScript==
// @name             0 Cross-tab example
// @include          /^https://example\.com\/$/
// @include          /^https://Whosebug\.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://whosebug.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);
}

这导致:

如果您想要双向通信,而不仅仅是单向通信,请让两个父页面都为 单个 目标域(例如 example.com).要与其他选项卡通信,post 向子 iframe 发送消息。让子 iframe 监听消息,当看到时,post 一个 BroadcastChannel 消息与所有其他 iframe 进行通信。当 iframe 收到 BroadcastChannel 消息时,使用 postMessage.

将其转发给父级 window
// ==UserScript==
// @name             0 Cross-tab example
// @include          /^https://example\.com\/$/
// @include          /^https://(?:Whosebug|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://whosebug.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://whosebug.com/') {
    setTimeout(() => {
      console.log('Whosebug posting message to iframe');
      iframe.contentWindow.postMessage('Message from Whosebug', '*');
    }, 2000);
  }
}

在上面的代码中,Stack Overflow 上的一个选项卡向 Stack Exchange 上的一个选项卡发送了一条消息。结果截图: