我发现最简单的方法是编写一个专门构建的 Firefox 扩展!
Step 1.我不想做一堆不必要的 XUL/addon 相关的事情; “引导”(或无需重新启动)扩展只需要一个install.rdf
文件来识别插件,以及bootstrap.js
文件来实现引导接口。
引导扩展:https://developer.mozilla.org/en-US/docs/Extensions/Bootstrapped_extensions https://developer.mozilla.org/en-US/docs/Extensions/Bootstrapped_extensions
好例子:http://blog.fpmurphy.com/2011/02/firefox-4-restartless-add-ons.html http://blog.fpmurphy.com/2011/02/firefox-4-restartless-add-ons.html
bootstrap接口的实现非常简单:
const path = '/PATH/TO/EXTERNAL/CODE.js';
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
var loaderSvc = Cc["@mozilla.org/moz/jssubscript-loader;1"];
.getService(Ci.mozIJSSubScriptLoader);
function install() {}
function uninstall() {}
function shutdown(data, reason) {}
function startup(data, reason) { loaderSvc.loadSubScript("file://"+path); }
您可以通过输入来编译扩展install.rdf
and bootstrap.js
进入新 zip 文件的顶层,并将 zip 文件扩展名重命名为.xpi
.
Step 2.为了拥有可重复的生产和测试环境,我发现最简单的方法是使用专用于自动化任务的配置文件启动 Firefox:
- 启动 Firefox 配置文件管理器:
firefox -ProfileManager
- 创建一个新的配置文件,指定位置以便于重复使用(我将我的配置文件称为
testing-profile
),然后退出配置文件管理器。
- 从中删除新的配置文件
profiles.ini
在您用户的 mozilla 配置中(这样就不会干扰正常浏览)。
- 使用该配置文件启动 Firefox:
firefox -profile /path/to/testing-profile
- 从文件系统(而不是 addons.mozilla.org)安装扩展。
- 执行准备配置文件所需的任何其他操作。 (例如:我需要添加第 3 方证书并允许相关域的弹出窗口。)
- 留个单
about:blank
打开选项卡,然后退出 Firefox。
- 快照配置文件:
tar cvf testing-profile-snapshot.tar /path/to/testing-profile
从那时起,每次运行自动化时,我都会解压testing-profile-snapshot.tar
超过现有的testing-profile
文件夹并运行firefox -profile /path/to/testing-profile about:blank
使用“原始”配置文件。
Step 3.所以现在当我启动 Firefox 时testing-profile
它将“包含”外部代码/PATH/TO/EXTERNAL/CODE.js
在每次启动时。
NOTE:我发现我必须移动/PATH/TO/EXTERNAL/
步骤期间别处的文件夹2如上所述,因为外部 JavaScript 代码将被缓存(!!!- 在开发过程中不需要)在配置文件内(即:在下次启动时不会看到对外部代码的更改)。
外部代码具有特权,可以使用任何 Mozilla 平台 API。然而,存在一个时机的问题。包含外部代码(并因此执行)的时刻是没有 Chrome 窗口对象(因此没有DOMWindow
对象)仍然存在。
所以我们需要等待,直到有一个有用的DOMWindow
object:
// useful services.
Cu.import("resource://gre/modules/Services.jsm");
var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader);
var wmSvc = Cc["@mozilla.org/appshell/window-mediator;1"]
.getService(Ci.nsIWindowMediator);
var logSvc = Cc["@mozilla.org/consoleservice;1"]
.getService(Ci.nsIConsoleService);
// "user" code entry point.
function user_code() {
// your code here!
// window, gBrowser, etc work as per MozRepl!
}
// get the gBrowser, first (about:blank) domWindow,
// and set up common globals.
var done_startup = 0;
var windowListener;
function do_startup(win) {
if (done_startup) return;
done_startup = 1;
wm.removeListener(windowListener);
var browserEnum = wm.getEnumerator("navigator:browser");
var browserWin = browserEnum.getNext();
var tabbrowser = browserWin.gBrowser;
var currentBrowser = tabbrowser.getBrowserAtIndex(0);
var domWindow = currentBrowser.contentWindow;
window = domWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
gBrowser = window.gBrowser;
setTimeout = window.setTimeout;
setInterval = window.setInterval;
alert = function(message) {
Services.prompt.alert(null, "alert", message);
};
console = {
log: function(message) {
logSvc.logStringMessage(message);
}
};
// the first domWindow will finish loading a little later than gBrowser...
gBrowser.addEventListener('load', function() {
gBrowser.removeEventListener('load', arguments.callee, true);
user_code();
}, true);
}
// window listener implementation
windowListener = {
onWindowTitleChange: function(aWindow, aTitle) {},
onCloseWindow: function(aWindow) {},
onOpenWindow: function(aWindow) {
var win = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
win.addEventListener("load", function(aEvent) {
win.removeEventListener("load", arguments.callee, false);
if (aEvent.originalTarget.nodeName != "#document") return;
do_startup();
}
};
// CODE ENTRY POINT!
wm.addListener(windowListener);
Step 4.所有这些代码都在“全局”范围内执行。如果您稍后需要加载其他 JavaScript 文件(例如:jQuery),请调用loadSubscript
明确地在null
(全球!)范围
function some_user_code() {
loader.loadSubScript.call(null,"file:///PATH/TO/SOME/CODE.js");
loader.loadSubScript.call(null,"http://HOST/PATH/TO/jquery.js");
$ = jQuery = window.$;
}
现在我们可以使用jQuery
on any DOMWindow
通过传递<DOMWindow>.document
作为选择器调用的第二个参数!