3

我维护了一个似乎与 Firefox 22 有问题的插件。有一个使用 loadFrameScript 的 JavaScript 模块,它反过来使用 mozIJSSubScriptLoader 注入一些库。loadFrameScript 引入的文件如下所示:

// Create a JS sub-script loader.
var loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
        .getService(Components.interfaces.mozIJSSubScriptLoader);

// Create a context object.
var executionContext = Object.create(content);

// Load the libraries.
loader.loadSubScript("chrome://my-package/content/libs/jquery.js", executionContext);
loader.loadSubScript("chrome://my-package/content/logic.js", executionContext);

但是,加载 jQuery 的行为会引发异常:

错误:NS_ERROR_XPC_BAD_OP_ON_WN_PROTO:对 WrappedNative 原型对象的非法操作源文件:chrome://my-package/content/libs/jquery.js 行:829

看起来 jQuery 并没有在那条线上做任何疯狂的事情,只是调用了 setTimeout。谷歌搜索这条消息,我在Scriptish扩展中发现了类似的情况,但没有解决方案。我不知道我应该做些什么不同的事情,或者是什么改变破坏了我在 Firefox 22 中加载 jQuery 的方式。有没有更好的方法来引入 jQuery?

更新

这确实是最严重的问题。我放弃了使用该executionContext对象,因为我什至不记得我为什么首先使用它,并且 jQuery 加载到内容中只是花花公子。

loader.loadSubScript("chrome://my-package/content/libs/jquery.js", content);
loader.loadSubScript("chrome://my-package/content/logic.js", content);

但是,现在其他也加载到的脚本content无法使用sendAsyncMessage. 我想这是有道理的,因为它是一个没有插件 API 的全新范围​​,但现在我不确定如何读取页面 DOM。如何将我的逻辑和 jQuery 加载到content并仍然保留结果的能力sendAsyncMessage

4

2 回答 2

0

您首先使用的可能原因executionContext是,否则内容将直接定义在 上content,这可能与网站、其他附加组件冲突和/或泄漏到网站。所以最好在你加载你的东西的窗口周围有一个包装器。

我刚刚编写了一个基于框架脚本的最小“内容脚本”加载器。没什么,但应该完成工作。我在 FX 24 上验证了 jquery 可以在其中工作,并且这些内容不会泄漏到内容窗口中。

// Frame scripts share a scope, so better not mess them up ;)
(function() {
  "use strict";
  const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
  const utils = {};
  try {
    throw new Error();
  }
  catch (ex) {
    let url = ex.fileName.replace(/\/[^\/]*?$/, "/");
    const ssm = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
    Object.defineProperties(utils, {
      "url": {
        enumerable: true,
        value: function(fn) {
          return url + fn;
        }
      },
      "mayLoad": {
        enumerable: true,
        value: function(o) {
          let node = (o.document || o);
          let window = (o.ownerDocument || o).defaultView || o;
          try {
            return window.location != "about:blank" &&
              !ssm.isSystemPrincipal(node.nodePrincipal);
          }
          catch (ex) {
            Cu.reportError(ex);
            return false;
          }
        }
      },
    });
    Object.freeze(utils);
  }
  try {
    const loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
      .getService(Ci.mozIJSSubScriptLoader);

    // Create a context object for each window that get's loaded.
    // Or use DOMWindowCreated, like the add-on manager does to install
    // the InstallTrigger.
    addEventListener("DOMContentLoaded", function(e) {
      let window = e.target.defaultView;
      if (!utils.mayLoad(window)) {
        // Refuse to load in chrome (system) pages.
        return;
      }

      // Need to create our context in the window scope (compartment).
      // The reason to create a wrapper/context it in the first place
      // is to avoid clashes with other add-ons, the website itself,
      // etc.
      let executionContext = Cu.createObjectIn(window);
      // Wire up the window to be the prototype.
      executionContext.__proto__ = window;
      // Add some useful stuff you want the "content scripts" to have
      // access to.
      Object.defineProperties(executionContext, {
        "sendAsyncMessage": {
          enumerable: true,
          value: sendAsyncMessage.bind(null)
        },
        "reportError": {
          enumerable: true,
          value: Cu.reportError.bind(Cu)
        },
        "doSomething": {
          enumerable: true,
          value: function(arg) {
            Cu.reportError("did something " + arg);
          }
        },
        "loadScript": {
          enumerable: true,
          value: function(fn) {
            loader.loadSubScript(utils.url(fn), executionContext);
          }
        }
      });
      // Normalize the properties, i.e. move them over to the correct
      // window scope (compartment);
      Cu.makeObjectPropsNormal(executionContext);

      // Load initial scripts
      executionContext.loadScript("test.js");
    });
  }
  catch (ex) {
    content.console.error(ex);
  }
})();

关键点是:

  • 用于Cu.createObjectIn(window)获得正确的范围(Spidermonkey 中的隔间)并避免NS_ERROR_XPC_BAD_OP_ON_WN_PROTO异常。
  • Cu.makeObjectPropsNormal()如果您在上下文中定义其他内容,请使用。
  • 不要试图将东西注入 chrome 特权窗口 ( utils.mayLoad)。
  • throw new Error()try-catch 只是获取当前 URI ( ) 的可靠技巧,以便ex.fileName以后在加载脚本时允许指定相对路径。
于 2013-10-29T19:15:31.027 回答
0

只是我的两分钱-

我还在维护一个遇到问题的扩展。对我来说,解决方案实际上与脚本中指示的相同 - 使用 window.xxxx 而不是直接引用该方法。

例如,之前的其中一行代码直接调用 setTimeout(),在我将其更改为 window.setTimeout() 之后,代码就可以工作了。

既然您说该行除了调用 setTimeout 之外没有做任何事情,我想这是同一个问题。尝试添加窗口。在那个电话之前。

祝你好运!

于 2013-07-03T21:00:57.150 回答