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

时间:2016-05-19 09:00:12

标签: javascript google-chrome-extension scope defineproperty

我尝试使用setter中的代码从内容脚本中定义jQuery对象上的属性window。这样,当定义实际的jQuery对象时,我可以立即使用它。不过,我似乎无法做到这一点。

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

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

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,但我的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});
  }
});

如果我在Object.defineProperty代码中添加了除executeScript代码之外的其他代码,那么这样可以正常工作。我只有定义属性的问题。

2 个答案:

答案 0 :(得分:2)

(引用来自评论)

  

我想使用页面本身提供的jQuery。我可以尝试插入与Outlook相同的jQuery文件,并希望它从缓存中加载,但我宁愿保持我的扩展尽可能干净,并使用已有的。

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

首先,由于the isolation between content script and webpage code,您无法使用网页代码。您无法获取对网页自己的jQuery / $

的引用

但是,让我们暂时假设你可以。然后该站点将jQuery更新为另一个版本,重命名jQuery对象或完全停止使用它,这超出了您的控制范围。结果:您的扩展程序已损坏。部分地,这首先是隔离背后的理由。

由于上下文隔离,您可以保证jQuery副本与站点上运行的任何内容之间不会发生冲突。因此,您无需担心:使用您的副本,并使用标准$来访问它。

将一个&lt; 100 KB文件与您的扩展程序捆绑在一起作为一次性下载,确保代码在100%的时间内可用,并且在最坏的情况下,磁盘访问延迟并不会使它变得不那么“干净”,恰恰相反。这是一种常见做法,enshrined in the docs

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

  

我验证了window.jQuery之后被实际的jQuery函数/对象正确定义[...]

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

然而,所有这些,

  

完成所有操作后,我希望每次在阅读窗格中打开电子邮件时都使用jQuery的ajaxSuccess函数来执行代码。

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

可能的行动方案:

  1. 依靠其他方法来检测您想要的事件。也许是monitoring the DOM
  2. 注入一些代码into the page itself;唯一的方法是从内容脚本向页面中注入<script>标记。在那里,您可以访问页面的jQuery副本,在发生事件时附加您的监听器和message your content script
  3. 依靠后台页面检测webRequest API所需的活动。这可能会拦截AJAX调用,但不会给你回复内容。
  4. 最后注意:这可能不像AJAX调用那么简单;也许该页面维护一个WebSocket连接以获得实时推送更新。 Tapping into this比较棘手。

答案 1 :(得分:1)

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

第一种方法是向包含适当代码的DOM添加<script>元素。这是一个非常广泛的StackOverflow答案,如何做到这一点:https://stackoverflow.com/a/9517879/125938

第二种是使用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, ' ')+')();';

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