1

I'm trying to define the property jQuery on the window object from a content script with code in the setter. That way, when the actual jQuery object is defined, I can use it right away. I seem to be unable to get it right, though.

The target website is Outlook.com. That's the webmail version of Outlook.

I tried to put the code in the content script directly, but even if I put "all_frames": true in the content_scripts section of the manifest (so the code gets injected into every frame), it isn't working.

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();

I verified that window.jQuery is properly defined afterwards by the actual jQuery function/object, but my setter code is never executed.
I also tried it with message passing: I send a message with the code as a string to a background script, and use executeScript to execute it on the correct tab, but this also doesn't work.

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

And in my background script:

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

If I put something else than the Object.defineProperty code in the executeScript code, that works fine. I only have problems defining the property.

4

2 回答 2

2

(引用来自评论)

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

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

首先,由于内容脚本和网页代码之间的隔离,您无论如何都无法使用页面的代码。您无法获得对页面自己jQuery/的引用$

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

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

将 <100 KB 的文件与您的扩展程序捆绑为一次性下载,以确保代码 100% 的时间可用,并且在最坏的情况下磁盘访问延迟不会降低它的“干净”,恰恰相反。这是一种常见的做法,并在 docs 中得到体现


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

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

我假设您尝试window.jQuery在控制台中执行;默认情况下,它在页面上下文中执行它,而不是在您的内容脚本上下文中(因此,不反映内容脚本上下文的状态并且不调用您的 getter/setter)。如果您想测试您的内容脚本,您需要top在控制台上方的上下文下拉菜单中更改为您的扩展程序的上下文。


然而,所有这一切,

总而言之,每次在阅读窗格中打开电子邮件时,我都想使用 jQuery 的 ajaxSuccess 函数来执行代码。

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

可能的行动方案:

  1. 依靠其他方法来检测您想要的事件。也许监视 DOM
  2. 将一些代码注入页面本身;这样做的唯一方法是将<script>标签从内容脚本注入页面。在那里,您可以访问页面的 jQuery 副本,附加您的侦听器并在发生某些事情时向您的内容脚本发送消息。
  3. 依靠后台页面来检测您需要使用webRequestAPI的活动。这可能会拦截 AJAX 调用,但不会给您回复内容。

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

于 2016-05-19T10:25:04.980 回答
1

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

第一种是向<script>DOM 添加一个包含适当代码的元素。这是关于如何做到这一点的相当广泛的 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对象。

于 2016-05-19T12:16:16.127 回答