如何在选项卡的主要 window 对象上定义 属性?

How can I define a property on the tab's main window object?

我正在尝试使用 setter 中的代码从内容脚本中定义 window 对象上的 属性 jQuery。这样,当实际的 jQuery 对象被定义时,我可以立即使用它。不过我好像做不对。

目标网站是Outlook.com。这是 Outlook 的网络邮件版本。

我试图将代码直接放在内容脚本中,但即使我将 "all_frames": true 放在清单的 content_scripts 部分(因此代码被注入每一帧),它不工作。

function afterInit(){
  var _jQuery;
  console.log('This gets logged');
  Object.defineProperty(window, 'jQuery', {
    get: function() { return _jQuery; },
    set: function(newValue) {
      _jQuery = $ = newValue;

      console.log('This is never logged!');

      $(document.body).append($('<span>I want to use jQuery here</span>'));
    }
  });
}
afterInit();

我验证了 window.jQuery 之后由实际 jQuery function/object 正确定义,但我的 setter 代码从未执行过。
我还尝试了消息传递:我将带有代码的消息作为字符串发送到后台脚本,并使用 executeScript 在正确的选项卡上执行它,但这也不起作用。

chrome.runtime.sendMessage(
    {action:'jQueryPreInit', value: '('+afterInit.toString()+')();'});

在我的后台脚本中:

chrome.runtime.onMessage.addListener(function(message, sender, callback) {
  switch (message.action){
    case "jQueryPreInit": chrome.tabs.executeScript(sender.tab.id, {code: message.value});
  }
});

如果我在 executeScript 代码中放置 Object.defineProperty 代码之外的其他代码,则可以正常工作。我只有在定义 属性.

时遇到问题

(引自评论)

I want to use the jQuery provided by the page itself. I could try inserting the same jQuery file as Outlook does and hope it gets loaded from cache, but I'd rather just keep my extension as clean as possible, and use what is already available.

您尝试优化扩展程序不可行/不推荐。

首先,由于 the isolation between content script and webpage code,您将无法使用该页面的代码。您无法获得对页面自身 jQuery/$.

的引用

但让我们暂时假设您可以。然后该站点将 jQuery 更新为另一个版本,重命名 jQuery 对象或完全停止使用它,这是您无法控制的。结果:您的分机坏了。这首先是隔离背后的部分原因。

作为上下文隔离的结果,您可以保证 jQuery 的副本与站点上运行的任何内容之间没有冲突。所以您不必担心:使用您的副本,并使用标准 $ 访问它。

将 <100 KB 的文件与您的扩展捆绑在一起作为一次性下载,确保代码在 100% 的时间内可用,并且在最坏的情况下磁盘访问延迟不会减少 "clean",恰恰相反。这是一种常见的做法 enshrined in the docs.


查看您的实际代码,它在内容脚本上下文中执行(无论是通过清单还是 executeScript),而不是在页面上下文中。因此,无论页面做什么,$ 都不会在那里定义。

I verified that window.jQuery is properly defined afterwards by the actual jQuery function/object [...]

我假设您尝试在控制台中执行 window.jQuery;默认情况下,executes it in the page context, not in your content script context(因此,不反映内容脚本上下文的状态 并且不调用您的 getter/setter)。如果您想测试您的内容脚本,您需要将控制台上方上下文下拉列表中的 top 更改为您的扩展程序的上下文。


尽管如此,

When all is said and done, I want to use jQuery's ajaxSuccess function to execute code every time an e-mail is opened in the read pane.

我们遇到了问题。由于内容脚本代码和网页代码是隔离的,因此您的代码永远不会知道 AJAX 在页面副本中执行(无论如何都不会通过 ajaxSuccess)。

可能的行动方案:

  1. 依靠其他方法来检测您想要的事件。也许 monitoring the DOM.
  2. 发生某些事情时注入一些代码into the page itself; the only way to do so is by injecting a <script> tag into the page from the content script. There, you can access the page's copy of jQuery, attach your listener and message your content script
  3. 靠后台页面检测activity你需要用webRequest API。这可能会拦截 AJAX 调用,但不会给你回复内容。

最后说明:这可能不像 AJAX 调用那么简单;也许该页面维护一个 WebSocket 连接以获取实时推送更新。 Tapping into this 比较棘手。

感谢 Xan,我发现只有两种方法可以做到这一点。

第一种方法是将 <script> 元素添加到包含适当代码的 DOM 中。这是关于如何做到这一点的相当广泛的 Whosebug 答案:.

第二个是使用 Javascript 伪 URL 和 window.location 对象。通过分配 window.location 包含 Javascript 的小书签样式 URL,您还可以绕过某些安全措施。在内容脚本中,输入:

location = 'javascript:(' + (function(){
  var _jQuery;
  Object.defineProperty(window, 'jQuery', {
    get: function() { return _jQuery; },
    set: function(newValue) {
      _jQuery = $ = newValue;

      console.log('totally logged!');

      $('<span>jQuery stuff here</span>');

    }
  });
}).toString().replace(/\n/g, ' ')+')();';

I/you 最初未能定义它的原因是因为我们使用的两种代码注入方法导致我们的代码被沙盒化到孤立的世界中:https://developer.chrome.com/extensions/content_scripts#execution-environment。意思是,他们共享页面的 DOM 并可以通过它进行通信,但他们不能直接访问彼此的 window 对象。