在 Firefox 无需重新启动的附加组件中,当新的 window 打开时如何 运行 编码(监听 window 打开)?
In a Firefox restartless add-on, how do I run code when a new window opens (listen for window open)?
我正在开始构建一个无需重新启动的 Firefox 附加组件,但我在设置 bootstrap.js 时遇到了问题。每个人似乎都同意 bootstrap.js 的核心是相当多的样板代码,大致如下:
const Cc = Components.classes;
const Ci = Components.interfaces;
function startup() {
let wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
let windows = wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements()) {
let domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
// then can control what happens with domWindow.document
}
}
function shutdown() {}
function install() {}
function uninstall() {}
此代码有效,我可以控制现有 windows 中的内容。例如,domWindow.alert("text")
成功地在当前打开的每个 window 上创建了一个标准警报说 "text"。
但是,我找不到任何允许我在新 windows 中做事的代码;即在脚本运行后创建的那些。处理新 windows 的创建并获得对它们的控制权的正确方法是什么,以至于我可以在创建时从另一个 "text" 收到另一个警报?
编辑: 使用 nsWindowMediator class 和来自 MDN 的代码示例,我现在有这个:
var windowListener = {
onOpenWindow: function (aWindow) {
try {
let domWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
domWindow.addEventListener("load", function () {
domWindow.removeEventListener("load", arguments.callee, false);
//window has now loaded now do stuff to it
domWindow.alert("text");
}, false);
} catch (err) {
Services.prompt.alert(null, "Error", err);
}
},
onCloseWindow: function (aWindow) {},
onWindowTitleChange: function (aWindow, aTitle) {}
};
function startup(aData, aReason) {
// Load into any existing windows
try {
let wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
let windows = wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements()) {
let domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
loadIntoWindow(domWindow);
}
} catch (err) {
Services.prompt.alert(null, "Error", err);
}
Services.wm.addListener(windowListener);
}
但是,onOpenWindow 调用仍然没有输出 - "text" 警报没有出现,catch 块中的错误警报也没有出现。我可以确认实际上正在输入 onOpenWindow;如果我在 onOpenWindow 的开头放置一个 Services.prompt.alert()
,我会在创建新的 window 时收到警报。不幸的是,我收到无限循环的警报,我不知道为什么。
However, I can't find any code that will allow me to do things in new windows
在使用 XPCOM 对象时,您通常希望研究它们的接口,这些接口通常可以在 MDN 上找到。在这种情况下,您的起点是 nsIWindowMediator,因为这是您在第 5 行中使用的服务。
如您所见,它有一个 addListener
函数,该函数接受一个实现 nsIWindowMediatorListener
的参数。页面上有一个代码示例。
但我们暂时假设没有代码示例。您可以在 MDN 上搜索接口,但未列出。下一步是 searching MXR for the .idl. idl = interface description language
一旦获得接口契约,您就可以或多或少地在 javascript 中实现它,至少对于听众来说是这样。实施您自己的 xpcom 服务将是 a little more complicated.
Searching the addon sdk can often provide some hints too. In this case they don't seem to be using .addListener
, but the file hints at another interesting service, which in turn you can find on MDN: nsIWindowWatcher.
基本上,如果您正在编写无需重新启动的插件,那么您就是在翻遍 Firefox 的内部,并且必须进行一些侦探工作才能找到您需要的确切组件。如果你想要更方便的东西,我会推荐插件 sdk,它提供了更有组织但也更受限制的 commonly used APIs
However, I can't find any code that will allow me to do things in new windows; i.e. those created after the script runs. What is the correct way to handle the creation of new windows and gain control over them, to the point where I could get another "text" alert from one when it is created?
打开每个 window 时对其进行操作的正确方法是使用 addListener()
from nsIWindowMediator. The example code below does this. The nsIWindowMediator is included in Services.jsm and is accessed through Services.wm.addListener(WindowListener)
. In order to use a window listener, you have to pass it an nsIWindowMediatorListener (ref2) 对象。 nsIWindowMediatorListener 包含三个键:onOpenWindow
、onCloseWindow
和onWindowTitleChange
。每个都应定义为一个函数,当适当的事件发生时将调用该函数。
MDN 文档 How to convert an overlay extension to restartless in "Step 9: bootstrap.js" contains an example of a basic bootstrap.js 它将 运行 函数中的代码 loadIntoWindow(window)
用于每个当前打开的 browser window以及将来打开的任何 browser window。我在几个不同的附加组件中使用了由此修改的代码。该示例与您已经使用的代码非常相似。示例是(稍作修改):
const Ci = Components.interfaces;
Components.utils.import("resource://gre/modules/Services.jsm");
function startup(data,reason) {
// Load this add-ons module(s):
Components.utils.import("chrome://myAddon/content/myModule.jsm");
// Do whatever initial startup stuff is needed for this add-on.
// Code is in module just loaded.
myModule.startup();
// Make changes to the Firefox UI to hook in this add-on
forEachOpenWindow(loadIntoWindow);
// Listen for any windows that open in the future
Services.wm.addListener(WindowListener);
}
function shutdown(data,reason) {
if (reason == APP_SHUTDOWN)
return;
// Unload the UI from each window
forEachOpenWindow(unloadFromWindow);
// Stop listening for new windows to open.
Services.wm.removeListener(WindowListener);
// Do whatever shutdown stuff you need to do on add-on disable
myModule.shutdown();
// Unload the module(s) loaded specific to this extension.
// Use the same URL for your module(s) as when loaded:
Components.utils.unload("chrome://myAddon/content/myModule.jsm");
// HACK WARNING: The Addon Manager does not properly clear all add-on related caches
// on update. In order to fully update images and locales, their
// caches need clearing here.
Services.obs.notifyObservers(null, "chrome-flush-caches", null);
}
function install(data,reason) { }
function uninstall(data,reason) { }
function loadIntoWindow(window) {
/* call/move your UI construction function here */
}
function unloadFromWindow(window) {
/* call/move your UI tear down function here */
}
function forEachOpenWindow(todo) {
// Apply a function to all open browser windows
var windows = Services.wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements())
todo(windows.getNext().QueryInterface(Ci.nsIDOMWindow));
}
var WindowListener = {
onOpenWindow: function(xulWindow) {
var window = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
function onWindowLoad() {
window.removeEventListener("load",onWindowLoad);
// Only add UI changes if this is a browser window
if (window.document.documentElement.getAttribute("windowtype")
== "navigator:browser")
loadIntoWindow(window);
}
window.addEventListener("load",onWindowLoad);
},
onCloseWindow: function(xulWindow) { },
onWindowTitleChange: function(xulWindow, newTitle) { }
};
虽然您可能想在 bootstrap.js 代码中做更多的事情,但上面的代码组织得相当好,并保留了所有代码在 loadIntoWindow(window)
内加载到 Firefox UI 并在 unloadFromWindow(window)
内卸载 UI。但是,应该注意的是,一些 UI 元素你应该只 adding/removing 一次(例如 australis 小部件,如按钮)和其他元素(例如直接更改到 Firefox DOM)必须在每个 window.
中添加一次
Unfortunately, I get an infinite loop of alerts and I have no idea why.
此示例与您当前使用的示例之间的显着差异之一是对已打开的 window 类型的测试。这样做是为了让我们只对新打开的 windows 进行操作,它们是 browser windows 而不是所有新打开的 windows:
if (window.document.documentElement.getAttribute("windowtype") == "navigator:browser")
loadIntoWindow(window);
您描述的无限循环 alert()
弹出窗口的问题是由于没有检查以确保您仅在 浏览器 windows. alert()
弹出窗口是 window。因此,您为每个打开的 alert()
window 调用 alert()
,当然,这只会打开另一个 alert()
window,您在其上调用 alert()
.这是你的无限循环。
其他参考资料:
1. Working with windows in chrome code
我正在开始构建一个无需重新启动的 Firefox 附加组件,但我在设置 bootstrap.js 时遇到了问题。每个人似乎都同意 bootstrap.js 的核心是相当多的样板代码,大致如下:
const Cc = Components.classes;
const Ci = Components.interfaces;
function startup() {
let wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
let windows = wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements()) {
let domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
// then can control what happens with domWindow.document
}
}
function shutdown() {}
function install() {}
function uninstall() {}
此代码有效,我可以控制现有 windows 中的内容。例如,domWindow.alert("text")
成功地在当前打开的每个 window 上创建了一个标准警报说 "text"。
但是,我找不到任何允许我在新 windows 中做事的代码;即在脚本运行后创建的那些。处理新 windows 的创建并获得对它们的控制权的正确方法是什么,以至于我可以在创建时从另一个 "text" 收到另一个警报?
编辑: 使用 nsWindowMediator class 和来自 MDN 的代码示例,我现在有这个:
var windowListener = {
onOpenWindow: function (aWindow) {
try {
let domWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
domWindow.addEventListener("load", function () {
domWindow.removeEventListener("load", arguments.callee, false);
//window has now loaded now do stuff to it
domWindow.alert("text");
}, false);
} catch (err) {
Services.prompt.alert(null, "Error", err);
}
},
onCloseWindow: function (aWindow) {},
onWindowTitleChange: function (aWindow, aTitle) {}
};
function startup(aData, aReason) {
// Load into any existing windows
try {
let wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
let windows = wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements()) {
let domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
loadIntoWindow(domWindow);
}
} catch (err) {
Services.prompt.alert(null, "Error", err);
}
Services.wm.addListener(windowListener);
}
但是,onOpenWindow 调用仍然没有输出 - "text" 警报没有出现,catch 块中的错误警报也没有出现。我可以确认实际上正在输入 onOpenWindow;如果我在 onOpenWindow 的开头放置一个 Services.prompt.alert()
,我会在创建新的 window 时收到警报。不幸的是,我收到无限循环的警报,我不知道为什么。
However, I can't find any code that will allow me to do things in new windows
在使用 XPCOM 对象时,您通常希望研究它们的接口,这些接口通常可以在 MDN 上找到。在这种情况下,您的起点是 nsIWindowMediator,因为这是您在第 5 行中使用的服务。
如您所见,它有一个 addListener
函数,该函数接受一个实现 nsIWindowMediatorListener
的参数。页面上有一个代码示例。
但我们暂时假设没有代码示例。您可以在 MDN 上搜索接口,但未列出。下一步是 searching MXR for the .idl. idl = interface description language
一旦获得接口契约,您就可以或多或少地在 javascript 中实现它,至少对于听众来说是这样。实施您自己的 xpcom 服务将是 a little more complicated.
Searching the addon sdk can often provide some hints too. In this case they don't seem to be using .addListener
, but the file hints at another interesting service, which in turn you can find on MDN: nsIWindowWatcher.
基本上,如果您正在编写无需重新启动的插件,那么您就是在翻遍 Firefox 的内部,并且必须进行一些侦探工作才能找到您需要的确切组件。如果你想要更方便的东西,我会推荐插件 sdk,它提供了更有组织但也更受限制的 commonly used APIs
However, I can't find any code that will allow me to do things in new windows; i.e. those created after the script runs. What is the correct way to handle the creation of new windows and gain control over them, to the point where I could get another "text" alert from one when it is created?
打开每个 window 时对其进行操作的正确方法是使用 addListener()
from nsIWindowMediator. The example code below does this. The nsIWindowMediator is included in Services.jsm and is accessed through Services.wm.addListener(WindowListener)
. In order to use a window listener, you have to pass it an nsIWindowMediatorListener (ref2) 对象。 nsIWindowMediatorListener 包含三个键:onOpenWindow
、onCloseWindow
和onWindowTitleChange
。每个都应定义为一个函数,当适当的事件发生时将调用该函数。
MDN 文档 How to convert an overlay extension to restartless in "Step 9: bootstrap.js" contains an example of a basic bootstrap.js 它将 运行 函数中的代码 loadIntoWindow(window)
用于每个当前打开的 browser window以及将来打开的任何 browser window。我在几个不同的附加组件中使用了由此修改的代码。该示例与您已经使用的代码非常相似。示例是(稍作修改):
const Ci = Components.interfaces;
Components.utils.import("resource://gre/modules/Services.jsm");
function startup(data,reason) {
// Load this add-ons module(s):
Components.utils.import("chrome://myAddon/content/myModule.jsm");
// Do whatever initial startup stuff is needed for this add-on.
// Code is in module just loaded.
myModule.startup();
// Make changes to the Firefox UI to hook in this add-on
forEachOpenWindow(loadIntoWindow);
// Listen for any windows that open in the future
Services.wm.addListener(WindowListener);
}
function shutdown(data,reason) {
if (reason == APP_SHUTDOWN)
return;
// Unload the UI from each window
forEachOpenWindow(unloadFromWindow);
// Stop listening for new windows to open.
Services.wm.removeListener(WindowListener);
// Do whatever shutdown stuff you need to do on add-on disable
myModule.shutdown();
// Unload the module(s) loaded specific to this extension.
// Use the same URL for your module(s) as when loaded:
Components.utils.unload("chrome://myAddon/content/myModule.jsm");
// HACK WARNING: The Addon Manager does not properly clear all add-on related caches
// on update. In order to fully update images and locales, their
// caches need clearing here.
Services.obs.notifyObservers(null, "chrome-flush-caches", null);
}
function install(data,reason) { }
function uninstall(data,reason) { }
function loadIntoWindow(window) {
/* call/move your UI construction function here */
}
function unloadFromWindow(window) {
/* call/move your UI tear down function here */
}
function forEachOpenWindow(todo) {
// Apply a function to all open browser windows
var windows = Services.wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements())
todo(windows.getNext().QueryInterface(Ci.nsIDOMWindow));
}
var WindowListener = {
onOpenWindow: function(xulWindow) {
var window = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
function onWindowLoad() {
window.removeEventListener("load",onWindowLoad);
// Only add UI changes if this is a browser window
if (window.document.documentElement.getAttribute("windowtype")
== "navigator:browser")
loadIntoWindow(window);
}
window.addEventListener("load",onWindowLoad);
},
onCloseWindow: function(xulWindow) { },
onWindowTitleChange: function(xulWindow, newTitle) { }
};
虽然您可能想在 bootstrap.js 代码中做更多的事情,但上面的代码组织得相当好,并保留了所有代码在 loadIntoWindow(window)
内加载到 Firefox UI 并在 unloadFromWindow(window)
内卸载 UI。但是,应该注意的是,一些 UI 元素你应该只 adding/removing 一次(例如 australis 小部件,如按钮)和其他元素(例如直接更改到 Firefox DOM)必须在每个 window.
Unfortunately, I get an infinite loop of alerts and I have no idea why.
此示例与您当前使用的示例之间的显着差异之一是对已打开的 window 类型的测试。这样做是为了让我们只对新打开的 windows 进行操作,它们是 browser windows 而不是所有新打开的 windows:
if (window.document.documentElement.getAttribute("windowtype") == "navigator:browser")
loadIntoWindow(window);
您描述的无限循环 alert()
弹出窗口的问题是由于没有检查以确保您仅在 浏览器 windows. alert()
弹出窗口是 window。因此,您为每个打开的 alert()
window 调用 alert()
,当然,这只会打开另一个 alert()
window,您在其上调用 alert()
.这是你的无限循环。
其他参考资料:
1. Working with windows in chrome code