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

css*_*hus 8 javascript jquery greasemonkey tampermonkey

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

Script #1 -- house.example.com
Script #2 -- bob.fred.example.com
Run Code Online (Sandbox Code Playgroud)

相同的域,不同的子域.

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

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

谁能指出我正确的方向?一两个例子就值得用黄金来衡量.


更新:尽管Gothdo将浏览器标签/窗口之间的Javascript通信引用为包含此问题的答案,但他没有考虑所涉及的跨源策略.该引用问题中的答案都没有为跨源浏览器选项卡通信提供明确的答案,这是该问题的主要观点.我现在已经研究并解决了这个问题,从一些SO和非SO来源获取想法.如果重新打开这个问题,我会发布我的解决方案.

Eyu*_*lDK 6

您可以使用GM_getValue,GM_setValue及GM_addValueChangeListener实现跨选项卡用户脚本通信。

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

// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addValueChangeListener
Run Code Online (Sandbox Code Playgroud)

以下几行粗代码将简化跨表用户脚本的通信。

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));
}
Run Code Online (Sandbox Code Playgroud)

因此,您要做的就是发送和接收消息。

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

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


Cer*_*nce 4

使用@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);
}
Run Code Online (Sandbox Code Playgroud)

这导致:

在此输入图像描述

如果您想要双向通信,而不仅仅是单向通信,请让两个父页面创建一个指向单个目标域(例如 to example.com)的子 iframe。要与其他选项卡通信,请将消息发布到子 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);
  }
}
Run Code Online (Sandbox Code Playgroud)

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

在此输入图像描述