Firefox 22中的NS_ERROR_XPC_BAD_OP_ON_WN_PROTO异常

时间:2013-05-30 21:49:54

标签: javascript firefox-addon

我维护了一个似乎与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结果的能力?

2 个答案:

答案 0 :(得分:0)

只是我的两分钱 -

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

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

既然你说除了调用setTimeout之外没有做任何事情,我想这是同样的问题。尝试添加窗口。在那之前。

祝你好运!

答案 1 :(得分: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),以便以后允许在加载脚本时指定相对路径。