繁体   English   中英

如何在Firefox(WebExtensions)中创建用于打开“browserAction”弹出窗口的全局热键?

[英]How to create a global hotkey for opening the “browserAction” popup in Firefox (WebExtensions)?

似乎Chrome没有用于打开弹出窗口的API,但是有一个专门的系统可以使用热键执行它: commands _execute_browser_action键。

Firefox不支持( 1commands_execute_browser_action的特殊功能。

我关心的弹出窗口类型是browserAction ,而不是pageAction

当按下键盘快捷键/热键组合时,如何打开browserAction弹出窗口?

可在Firefox版本> = 52中本机提供

此功能将在Firefox 52中本机提供,Firefox 52目前是Firefox Developer Edition (即Firefox 52.0a2)。 如您所知,对于WebExtensions,您可以使用为commands键提供的对象中的_execute_browser_action键创建全局热键。 例如:

"commands":{
    "_execute_browser_action": {
        "suggested_key": {
            "default": "Alt+Shift+J"
        }
    }
}

打开伪弹出窗口(在旧版本的Firefox中填充此功能)

虽然显式功能在Firefox 52之前不可用,但您可以通过定义名为"_execute_browser_action"的自定义命令在当前版本的Firefox中"_execute_browser_action"此功能。 它会看起来与普通的弹出窗口有点不同,但它会起作用。 它将在一个面板中,您可能需要考虑一些相关的样式,仅当它在面板而不是弹出窗口时才应用。 面板打开时,活动标签的内容可能会有所不同。 但是,下面的代码至少考虑到使用chrome.tabs.query()browser.tabs.query()执行查询时,通过使响应成为在真正的弹出窗口中打开而不是小组。

相同的代码将继续在Firefox 52+上运行。 在Firefox 52+上, "_execute_browser_action"直接激活浏览器操作点击或弹出窗口。

因为当您不使用弹出窗口时,主要的是您不对browserAction.onClicked侦听器使用匿名函数。 这允许commands.onCommand监听器也调用该功能。 在Firefox 48中引入了commands.onCommand ,因此这适用于48+以上的任何版本。

使用此activeTab时,您可能在使用activeTab以外的activeTab时遇到一些问题。 究竟需要什么,如果有的话,将取决于您的代码。

以下是一个扩展,当您按下键盘快捷键Alt-Shift-J时,会导致使用浏览器操作按钮调用的功能。 它将激活doActionButton()函数,或者,如果定义了弹出窗口,它将打开弹出窗口作为一个面板,其行为类似于弹出窗口的正常行为,但它并不完美。 它从当前为活动选项卡定义的弹出文件的名称中获取弹出文件的名称,就像单击browserAction按钮一样。

manifest.json

{
    "description": "Polyfill browserAction keyboard shortcut, including popups.",
    "manifest_version": 2,
    "name": "Polyfill browserAction keyboard shortcut",
    "version": "0.1",

    "background": {
        "scripts": [
            "background.js"
        ]
    },

    "browser_action": {
        "default_icon": {
            "32": "myIcon.png"
        },
        "default_title": "Open popup",
        "default_popup": "popup.html"
    },

    "commands": {
        "_execute_browser_action": {
            "suggested_key": {
                "default": "Alt+Shift+J"
            }
        }
    }
}

background.js

chrome.browserAction.onClicked.addListener(doActionButton);

function doActionButton(tab){
    console.log('Action Button clicked. Tab:',tab);
}

chrome.commands.onCommand.addListener(function(command) {
    //Polyfill the Browser Action button
    if(command === '_execute_browser_action') {
        chrome.tabs.query({active:true,currentWindow:true},function(tabs){
            //Get the popup for the current tab
            chrome.browserAction.getPopup({tabId:tabs[0].id},function(popupFile){
                if(popupFile){
                    openPopup(tabs[0],popupFile);
                } else {
                    //There is no popup defined, so we do what is supposed to be done for
                    //  the browserAction button.
                    doActionButton(tabs[0]);
                }
            });
        });
        return;
    } //else
});

//popupWindowId can be true, false, or the popup's window Id.
var popupWindowId = false;
var lastFocusedWin;
var lastActiveTab;
function openPopup(tab,popupFile){
    chrome.windows.getLastFocused(function(win){
        lastFocusedWin=win;
        if(popupWindowId === false){
            //This prevents user from pressing the button quickly multiple times in a row.
            popupWindowId = true;
            lastActiveTab = tab;
            chrome.windows.create({ 
                url: popupFile, 
                type: 'popup',
            },function(win){
                popupWindowId = win.id;
                //Poll for the view of the window ID. Poll every 50ms for a
                //  maximum of 20 times (1 second). Then do a second set of polling to
                //  accommodate slower machines.
                //  Testing on a single moderately fast machine indicated the view 
                //  was available after, at most, the second 50ms delay.
                waitForWindowId(popupWindowId,50,20,actOnPopupViewFound,do2ndWaitForWinId);
            });
            return;
        }else if(typeof popupWindowId === 'number'){
            //The window is open, and the user pressed the hotkey combo.
            //  Close the window (as happens for a browserAction popup).
            closePopup();
        }
    });
}

function closePopup(){
    if(typeof popupWindowId === 'number'){
        chrome.windows.remove(popupWindowId,function(){
            popupWindowId = false;
        });
    }
}

chrome.windows.onRemoved.addListener(function(winId){
    if(popupWindowId === winId){
        popupWindowId = false;
    }
});

chrome.windows.onFocusChanged.addListener(function(winId){
    //If the focus is no longer the popup, then close the popup.
    if(typeof popupWindowId === 'number'){
        if(popupWindowId !== winId){
            closePopup();
        }
    } else if(popupWindowId){
    }
});

function actOnPopupViewFound(view){
    //Make tabs.query act as if the panel is a popup.
    if(typeof view.chrome === 'object'){
        view.chrome.tabs.query = fakeTabsQuery;
    }
    if(typeof view.browser === 'object'){
        view.browser.tabs.query = fakeTabsQuery;
    }
    view.document.addEventListener('DOMContentLoaded',function(ev){
        let boundRec = view.document.body.getBoundingClientRect();
        updatePopupWindow({
            width:boundRec.width + 20,
            height:boundRec.height + 40
        });
    });
    updatePopupWindow({});
}

function updatePopupWindow(opt){
    let width,height;
    if(opt){
        width =typeof opt.width  === 'number'?opt.width :400;
        height=typeof opt.height === 'number'?opt.height:300;
    }
    //By the time we get here it is too late to find the window for which we
    //  are trying to open the popup.
    let left = lastFocusedWin.left + lastFocusedWin.width - (width +40);
    let top = lastFocusedWin.top + 85; //Just a value that works in the default case.
    let updateInfo = {
        width:width,
        height:height,
        top:top,
        left:left
    };
    chrome.windows.update(popupWindowId,updateInfo);
}

function waitForWindowId(id,delay,maxTries,foundCallback,notFoundCallback) {
    if(maxTries--<=0){
        if(typeof notFoundCallback === 'function'){
            notFoundCallback(id,foundCallback);
        }
        return;
    }
    let views = chrome.extension.getViews({windowId:id});
    if(views.length > 0){
        if(typeof foundCallback === 'function'){
            foundCallback(views[0]);
        }
    } else {
        setTimeout(waitForWindowId,delay,id,delay,maxTries,foundCallback,notFoundCallback);
    }
}

function do2ndWaitForWinId(winId,foundCallback){
    //Poll for the view of the window ID. Poll every 500ms for a
    //  maximum of 40 times (20 seconds). 
    waitForWindowId(winId,500,40,foundCallback,windowViewNotFound);
}

function windowViewNotFound(winId,foundCallback){
    //Did not find the view for the window. Do what you want here.
    //  Currently fail quietly.
}

function fakeTabsQuery(options,callback){
    //This fakes the response of chrome.tabs.query and browser.tabs.query, which in
    //  a browser action popup returns the tab that is active in the window which
    //  was the current window when the popup was opened. We need to emulate this
    //  in the popup as panel.
    //The popup is also stripped from responses if the response contains multiple
    //  tabs.
    let origCallback = callback;
    function stripPopupWinFromResponse(tabs){
        return tabs.filter(tab=>{
            return tab.windowId !== popupWindowId;
        });
    }
    function stripPopupWinFromResponseIfMultiple(tabs){
        if(tabs.length>1){
            return stripPopupWinFromResponse(tabs);
        }else{
            return tabs;
        }
    }
    function callbackWithStrippedTabs(tabs){
        origCallback(stripPopupWinFromResponseIfMultiple(tabs));
    }
    if(options.currentWindow || options.lastFocusedWindow){
        //Make the query use the window which was active prior to the panel being
        //  opened.
        delete options.currentWindow;
        delete options.lastFocusedWindow;
        options.windowId = lastActiveTab.windowId;
    }
    if(typeof callback === 'function') {
        callback = callbackWithStrippedTabs;
        chrome.tabs.query.apply(this,arguments);
        return;
    }else{
        return browser.tabs.query.apply(this,arguments)
                                 .then(stripPopupWinFromResponseIfMultiple);
    }
}

WebExtensions仍处于开发阶段:

WebExtensions API仍处于开发阶段。 每个版本的Firefox都有所改进。 目前,您可能最好使用Firefox Developer EditionFirefox Nightly (用于_execute_browser_action )开发和测试WebExtension插件。 您还应该仔细注意您希望使用的功能所需的Firefox版本。 此信息包含在MDN文档页面的“浏览器兼容性”部分中。


此问题中代码的某些部分已从我的其他各种答案中复制/修改。

_exectue_browser_action支持正在进行中: https_exectue_browser_action

同时我很确定这是不可能的。

_exectue_browser_action_execute_page_action_execute_sidebar_action实施: 特殊快捷方式

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM