【问题标题】:In a Firefox restartless add-on, how do I run code when a new window opens (listen for window open)?在 Firefox 无需重启的附加组件中,如何在新窗口打开时运行代码(监听打开的窗口)?
【发布时间】:2015-03-21 01:14:59
【问题描述】:

我开始构建一个无需重启的 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() {}

此代码有效,我可以控制现有窗口中的内容。例如,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 块中的错误警报也没有出现。我可以确认实际上正在输入 onOpenWindow;如果我在 onOpenWindow 的开头放置一个Services.prompt.alert(),我会在创建新窗口时收到警报。不幸的是,我收到了无限循环的警报,我不知道为什么。

【问题讨论】:

  • 这显示了一个关于如何弄乱窗口的简单演示:github.com/Noitidart/_ff-addon-demo-BootstrapHotkey 这个为所有窗口添加了一个热键
  • 你为什么要使用那些alerts?我们生活在 21 世纪,我们有 console.logdump()
  • 还有the debugger
  • 您所做的编辑大大改变了问题的要求(从“我如何在新窗口打开时运行代码?”更改为“With此处包含的代码,为什么我会有一个无限循环的警报?”。另外一次,问一个 new 问题而不是将您的问题更改为有意义的问题会更合适不同。鉴于它们是相关的,从新问题链接到这个问题是合适的。

标签: javascript firefox-addon firefox-addon-restartless


【解决方案1】:

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

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

Step 9: bootstrap.js”中的 MDN 文档 How to convert an overlay extension to restartless 包含一个基本 bootstrap.js 的示例,它将为每个当前打开的 浏览器 窗口和任何浏览器 窗口将在未来打开。我在几个不同的附加组件中使用了由此修改的代码。该示例与您已经使用的代码基本相似。示例为(稍作修改):

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 代码中执行更多操作,但上面的代码组织得相当好,并将所有代码都加载到 @ 中的 Firefox UI 中987654337@ 并在unloadFromWindow(window) 中卸载 UI。但是,应该注意的是,某些 UI 元素应该只添加/删除一次(例如 australis 小部件,如按钮),而其他元素(例如直接更改 Firefox DOM)必须在每个窗口中添加一次。

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

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

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

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

其他参考:
1.Working with windows in chrome code

【讨论】:

  • 所以我尝试复制粘贴您的代码,唯一的变化是我在启动时注释掉了 myModule 代码并在 loadIntoWindow 函数中添加了Services.prompt.alert(null, "title", "Hey2!"); window.alert("Hey!");。启动时,所有窗口都会正确触发两个警报。但是,当我打开一个新窗口时,只有“Hey2!”警报火灾。有什么想法吗?
  • 我还尝试添加其他一些使用窗口范围的东西,比如事件处理程序。他们对所有打开的窗口做出正确反应,但在新窗口上什么也不做。从窗口侦听器调用 loadIntoWindow 时范围是否有所不同?
【解决方案2】:

但是,我找不到任何可以让我在新窗口中执行操作的代码

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

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

但我们暂时假设没有代码示例。您可以在 MDN 上搜索该接口,但未列出。下一步将是searching MXR for the .idlidl = interface description language

一旦你获得了接口契约,你就可以或多或少地在 javascript 中实现它,至少对于听众来说是这样。实现您自己的 xpcom 服务将是 a little more complicated

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

基本上,如果您正在编写无需重启的插件,那么您就是在翻找 Firefox 的内脏,并且必须做一些侦探工作才能找到您需要的确切组件。如果你想要更方便的东西,我会推荐插件 sdk,它提供了一组更有条理但也更受限制的commonly used APIs

【讨论】:

    猜你喜欢
    • 2016-07-02
    • 2011-08-17
    • 1970-01-01
    • 2014-03-24
    • 1970-01-01
    • 1970-01-01
    • 2011-08-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多