javascript - 如何添加右键单击我的 Firefox 扩展图标的功能?

标签 javascript firefox-addon contextmenu firefox-addon-sdk right-click

美好的一天!

我反复搜索,但没有找到解决此问题的任何帮助...

上下文: 我开发了一个非常简单的 Google Chrome 扩展程序:只需点击一下即可向某人发送电子邮件。要配置此功能,此扩展程序上有一个选项页面,用于设置要发送到的电子邮件地址。 My Google Chrome extension is available here ( English translation ,只是文本,不是安装)。

用户要求我为 Firefox 制作此扩展,所以我正在努力!

我读过 cfx 的教程,没问题。但我需要让我的扩展程序响应右键单击工具栏中的扩展程序图标。不在页面上,在我的图标上。

我正在使用 ActionButton(或 ToggleButton)将我的图标添加到此工具栏,但我找不到在右键单击时添加菜单的方法(已经有默认的 Firefox 上下文菜单,但我想添加“选项”。)

如果有人能提供解决方案那就太好了!

(我对 XUL 不熟悉。因此,如果可能的话,请提供仅 JavaScript 的解决方案 ^^ )

PS:我是法国人,所以请原谅我的英语不好

编辑:我已经找到了如何在“package.json”文件中设置首选项,但我想要自己的窗口。 如果我们可以将附加组件管理器中的“选项”按钮“绑定(bind)”到我自己的窗口,那就完美了!

编辑2:因为每个人都不清楚,所以我会在这里详细说明我想要的扩展: - 只需单击(左键单击)图标即可获取当前 URL 并将其发送到邮件地址(确定) - 只需单击即可执行此操作。这个扩展的目标是非常非常简单! - 右键单击​​图标会显示 Firefox 的上下文菜单,我想在此处添加“选项”以显示我的选项页面 - 插件管理器可以在“停用”和“调试”附近有一个“选项”按钮,我希望此选项按钮显示我的选项页面。 => 查看我的选项页面的两种方式:通过右键单击或通过插件管理器,这就是我需要您帮助的原因!

最佳答案

一般用户界面注释

使用右键单击直接激活您的功能与系统范围内使用的通用用户界面相反。右键单击在系统范围内(在某些系统上)用于打开上下文菜单。在 Firefox 的工具栏中,它用于调出工具栏区域的上下文菜单。这是用户在使用右键单击时通常期望发生的情况。您可能最好使用诸如 Shift+左键单击之类的东西,或者让用户定义用于激活您的功能的组合。如果您尝试向上下文菜单添加选项,那么通常可以通过右键单击来访问该选项。

其他附加组件中使用的替代品:

  • 按钮的第二个部分,带有向下箭头。单击向下箭头可打开展开的操作或选项菜单。
  • 当鼠标悬停在按钮上时,使用工具提示显示操作或选项菜单。这是通过将弹出窗口包含在 <tooltip id="myTooltip"></tooltip> 中来创建自定义工具提示来完成的。元素并在按钮中使用 <button tooltip="myTooltip"/> 引用它(tooltip propertytooltip attribute)。

使用右键单击

问题似乎是附加 SDK ActionButton抽象化了您为任意事件提供监听器的能力。它也不允许您访问实际的 event object 通常传递给事件处理程序(监听器)。此外,其 click 事件实际上似乎是 command event ,不是 click eventclick 之间的显着差异之一和一个 command事件是command事件通常不会在右键单击时触发。

您将需要访问 ActionButton 界面之外的按钮,并为 click 添加监听器。事件,然后在您的单击事件处理程序中,您可以选择根据 event.button 的状态执行操作。和event.shiftKey .

根据我发布的内容调整一些代码 an answer for a different question ,您将需要类似的东西(未经过修改测试):

function loadUi(buttonId) {
    if (window === null || typeof window !== "object") {
       //If you do not already have a window reference, you need to obtain one:
       //  Add a "/" to un-comment the code appropriate for your add-on type.
       /* Add-on SDK:
       var window = require('sdk/window/utils').getMostRecentBrowserWindow();
       //*/
       /* Overlay and bootstrap (from almost any context/scope):
       var window=Components.classes["@mozilla.org/appshell/window-mediator;1"]
                            .getService(Components.interfaces.nsIWindowMediator)
                            .getMostRecentWindow("navigator:browser");
       //*/
    }

    forEachCustomizableUiById(buttonId, loadIntoButton, window);
}

function forEachCustomizableUiById(buttonId ,func, myWindow) {
    let groupWidgetWrap = myWindow.CustomizableUI.getWidget(buttonId);
    groupWidgetWrap.instances.forEach(function(windowUiWidget) {
        //For each button do the load task.
        func(windowUiWidget.node);
    });
}

function loadIntoButton(buttonElement) {
    //Make whatever changes to the button you want to here.
    //You may need to save some information about the original state
    //  of the button.
    buttonElement.addEventListener("click",handleClickEvent,true);
}

function unloadUi(buttonId) {
    if (window === null || typeof window !== "object") {
       //If you do not already have a window reference, you need to obtain one:
       //  Add a "/" to un-comment the code appropriate for your add-on type.
       /* Add-on SDK:
       var window = require('sdk/window/utils').getMostRecentBrowserWindow();
       //*/
       /* Overlay and bootstrap (from almost any context/scope):
       var window=Components.classes["@mozilla.org/appshell/window-mediator;1"]
                            .getService(Components.interfaces.nsIWindowMediator)
                            .getMostRecentWindow("navigator:browser");
       //*/
    }

    forEachCustomizableUiById(buttonId, unloadFromButton, window);
}

function unloadFromButton(buttonElement) {
    //Return the button to its original state
    buttonElement.removeEventListener("click",handleClickEvent,true);
}

function handleClickEvent(event) {
    If( (event.button & 2) == 2 && event.shiftKey){
        event.preventDefault();
        event.stopPropagation();
        doMyThing();
    }
}

function doMyThing() {
    //Whatever it is that you are going to do.
}

正如上面的代码所暗示的,您需要确保在卸载/禁用附加组件时删除监听器。您还需要确保loadUi()当新窗口打开时被调用,以便将您的处理程序添加到新按钮。

添加到上下文菜单

没有直接的方法来更改图标的上下文菜单。上下文菜单的 ID 是 toolbar-context-menu 。您可以做的是将项目添加到上下文菜单,通常为 hidden="true" 。当您收到图标上发生右键单击的事件时,您可以更改 hidden 的状态对于您添加的那些项目。然后在 popuphidden 上调用的事件处理程序中上下文菜单 ( <menupopup id="toolbar-context-menu"> ) 的事件,您可以设置 hidden="true" 的状态对于<menuitem>您已添加到 <menupopup id="toolbar-context-menu"> 的元素在每个浏览器窗口中。

大致如下:

function loadIntoContextMenu(win){
    let doc = win.ownerDocument;
    let contextPopupEl = doc.getElementById("toolbar-context-menu");
    contextPopupEl.insertAdjacentHTML("beforeend", 
          '<menuitem label="My Item A" id="myExtensionPrefix-context-itemA"' 
        +      ' oncommand="doMyThingA();" hidden="true" />'
        + '<menuitem label="My Item B" id="myExtensionPrefix-context-itemB"'
        +      ' oncommand="doMyThingB();" hidden="true" />');
    contextPopupEl.addEventListener("popuphidden",hideMyContextMenuItems,true);
}
function unloadFromContextMenu(win){
    let doc = win.ownerDocument;
    let contextPopupEl = doc.getElementById("toolbar-context-menu");
    let itemA = doc.getElementById("myExtensionPrefix-context-itemA");
    let itemB = doc.getElementById("myExtensionPrefix-context-itemB");
    contextPopupEl.removeChild(itemA);
    contextPopupEl.removeChild(itemB);
    contextPopupEl.removeEventListener("popuphidden",hideContextMenuItems,true);
}
function setHiddenMyContextMenuItems(element,text){
    //The element is the context menu.
    //text is what you want the "hidden" attribute to be set to.
    let child = element.firstChild;
    while(child !== null){
        if(/myExtensionPrefix-context-item[AB]/.test(child.id)){
            child.setAttribute("hidden",text);
        }
        child = child.nextSibling;
    }
}
function showContextMenuItems(event){
    //The target of this event is the button for which you want to change the
    //  context menu.  We need to find the context menu element.
    let contextmenuEl = event.target.ownerDocument
                                    .getElementById("toolbar-context-menu");
    setHiddenMyContextMenuItems(contextmenuEl,"false");
}
function hideContextMenuItems(event){
    //This is called for the popuphidden event of the context menu.
    setHiddenMyContextMenuItems(event.target,"true");
}

//Change the handleClickEvent function in the code within the earlier section: 
function handleClickEvent(event) {
    If( (event.button & 2) == 2){
        //don't prevent propagation, nor the default as the context menu
        //  showing is desired.
        showContextMenuItems(event);
    }
}

再说一遍,我还没有测试过这个。它应该展示一种实现您愿望的方法。

但是,考虑到我们正在讨论上下文菜单,最好使用 contextmenu 事件而不是 click事件并测试右键单击。在这种情况下,我们会将上面的一些函数更改为以下内容:

function loadIntoButton(buttonElement) {
    //Make whatever changes to the button you want to here.
    buttonElement.addEventListener("contextmenu",handleContextmenuEvent,true);
}

function handleContextmenuEvent(event) {
    showContextMenuItems(event);
}

您可以通过使用 nsIWindowMediator 获取每个打开的主浏览器窗口。以下函数from MDN ,将为每个打开的窗口运行您传递给它的函数一次:

Components.utils.import("resource://gre/modules/Services.jsm");
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(Components.interfaces.nsIDOMWindow));
    }
}

在附加 SDK 中:

function forEachOpenWindow(todo)  // Apply a function to all open browser windows
    var windows = require("sdk/windows");
    for (let window of windows.browserWindows) {
      todo(windows.getNext().QueryInterface(Components.interfaces.nsIDOMWindow));
    }
}

您可以添加一个调用 loadIntoContextMenu 的监听器对于具有以下代码的新窗口(还有 from MDN ):

Components.utils.import("resource://gre/modules/Services.jsm");
Services.wm.addListener(WindowListener);
var WindowListener =
{
    onOpenWindow: function(xulWindow)
    {
        var window = xulWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                              .getInterface(Components.interfaces.nsIDOMWindow);
        function onWindowLoad()
        {
            window.removeEventListener("load",onWindowLoad);
            if (window.document.documentElement.getAttribute("windowtype") == "navigator:browser"){
                loadIntoContextMenu(window);
                //It would be better to only do this for the current window, but
                //  it does not hurt to do it to all of them again.
                loadUi(buttonId);
            }
        }
        window.addEventListener("load",onWindowLoad);
    },
    onCloseWindow: function(xulWindow) { },
    onWindowTitleChange: function(xulWindow, newTitle) { }
};

关于javascript - 如何添加右键单击我的 Firefox 扩展图标的功能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26389963/

相关文章:

javascript - 通过表单和 XMLHttpRequest 将文件异步上传到服务器

google-chrome-extension - Rapportive 等 Gmail 插件如何工作?

javascript - anchor 标记事件未触发

ios - 如何向应用程序添加上下文菜单

javascript - 循环对于 IE7/8 来说太慢了

javascript - 如何检测页面上的链接 PDF 并显示使用 jquery 下载 Adob​​e 阅读器的消息?

带有模板字符串的 Javascript URL

javascript - 如何调用 firefox 扩展内容目录中的 bat 文件

css - 在 firefox 中隔离特定页面元素的 css

c# - 仅在特定 GridViewColumn 中右键单击时显示上下文菜单