在Firefox restartless插件中,当一个新窗口打开时,我如何运行代码(监听窗口打开)



我开始构建一个无重启的Firefox插件,但我在设置bootstrap.js时遇到了问题。每个人似乎都同意bootstrap.jsp的核心几乎是样板代码,大致如下:

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() {}

这段代码很有效,我可以控制现有窗口中的内容。例如,domWindow.alert("text")成功地在当前打开的每个窗口上创建了一个表示"文本"的标准警报。

然而,我找不到任何允许我在新窗口中执行操作的代码;即在脚本运行之后创建的那些。处理新窗口的创建并获得对它们的控制的正确方法是什么,以至于我可以在创建新窗口时从中获得另一个"文本"警报?

编辑:使用nsWindowMediator类和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调用仍然没有输出——catch块中没有出现"text"警报,也没有出现错误警报。我可以确认onOpenWindow实际上正在被输入;如果我在onOpenWindow的开头放了一个Services.prompt.alert(),那么在创建新窗口时就会收到警报。不幸的是,我收到了无限循环的警报,我不知道为什么。

然而,我找不到任何允许我在新窗口中执行操作的代码;即在脚本运行之后创建的那些。处理新窗口的创建并获得对它们的控制的正确方法是什么,以至于我可以在创建新窗口时从中获得另一个"文本"警报?

在每个窗口打开时,对其执行操作的正确方法是使用nsIWindowMediator中的addListener()。下面的示例代码可以做到这一点。nsIWindowMediator包含在Services.jsm中,可通过Services.wm.addListener(WindowListener)访问。为了使用窗口侦听器,您必须向它传递一个nsIWindowMediatorListener(ref2)对象。nsIWindowMediatorListener包含三个键:onOpenWindowonCloseWindowonWindowTitleChange。每一个都应该定义为一个函数,当适当的事件发生时将调用该函数。

MDN文档"步骤9:bootstrap.js"中的"如何将覆盖扩展转换为restartless"包含一个基本bootstrap.js的示例,该示例将为当前打开的浏览器窗口和将来打开的任何浏览器窗口运行函数loadIntoWindow(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元素您应该只添加/删除一次(例如,australis小部件,如按钮),而其他元素(例如,对Firefox DOM的直接更改)必须在每个窗口中添加一次。

不幸的是,我收到了无限循环的警报,我不知道为什么。

此示例与您当前使用的示例之间的一个显著差异是对已打开的窗口类型的测试。这样做是为了让我们只对新打开的窗口采取行动,这些窗口是浏览器窗口,而不是所有新打开的窗户:

if (window.document.documentElement.getAttribute("windowtype") == "navigator:browser")
    loadIntoWindow(window);

您所描述的获得alert()弹出窗口的无限循环的问题是由于没有进行检查以确保您只在浏览器窗口上执行操作而导致的。alert()弹出窗口是一个窗口。因此,您为打开的每个alert()窗口调用alert(),当然,这只会打开另一个调用alert()alert()窗口。这是你的无限循环。

其他参考资料:
1.使用铬代码中的窗口

然而,我找不到任何允许我在新窗口中做事情的代码

当使用XPCOM对象时,您通常希望研究它们的接口,这些接口通常在MDN上找到。在这种情况下,您的起点将是nsIWindowMediator,因为这是您在第5行中使用的服务。

正如您所看到的,它有一个addListener函数,它接受一个实现nsIWindowMediatorListener的参数。页面上有一个代码示例。

但是,让我们假设目前还没有一个代码示例。您可以在MDN上搜索接口,但它没有列出。下一步是在MXR中搜索.idl。idl=接口描述语言

一旦你得到了接口契约,你就可以或多或少地用javascript实现它,至少对监听器来说是这样。实现您自己的xpcom服务会稍微复杂一些。

搜索插件sdk通常也可以提供一些提示。在这种情况下,他们似乎没有使用.addListener,但该文件提示了另一个有趣的服务,您可以在MDN:nsIWindowWatcher上找到它。

基本上,如果你正在编写无重启插件,你就要在firefox的内脏中翻找,必须做一些侦探工作才能找到你需要的确切组件。如果你想要更方便的东西,我推荐addon-sdk,它提供了一组更有序但也更受限制的常用API

最新更新