我维护了一个似乎与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
结果的能力?
答案 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()
。utils.mayLoad
)。throw new Error()
try-catch只是一个可靠的黑客,可以获取当前的URI(ex.fileName
),以便以后允许在加载脚本时指定相对路径。