Como criar uma hotkey global para abrir o popup “browserAction” no Firefox (WebExtensions)?

Parece que o Chrome não tem uma API para abrir o pop-up, mas tem um sistema dedicado para fazer isso com uma tecla de atalho: _execute_browser_action nos commands .

A funcionalidade especial de _execute_browser_action em commands não é suportada ( 1 ) pelo Firefox.

O tipo de popup que me interessa é o browserAction , não o pageAction .

Como posso ter o popup browserAction aberto quando uma combinação de atalho de teclado / tecla de atalho é pressionada?

Disponível nativamente nas versões do Firefox> = 52

Esta funcionalidade estará disponível nativamente no Firefox 52, que atualmente é o Firefox Developer Edition (ou seja, o Firefox 52.0a2). Como você sabe, para WebExtensions, você cria uma tecla de atalho global usando a chave _execute_browser_action no object fornecido para a chave de commands . Por exemplo:

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

Abra um pseudo-popup (polyfill esta funcionalidade nas versões mais antigas do Firefox)

Embora a funcionalidade explícita não esteja disponível até o Firefox 52, você pode preencher essa funcionalidade na versão atual do Firefox, definindo um comando personalizado chamado "_execute_browser_action" . Vai parecer um pouco diferente do seu pop-up normal, mas será funcional. Ele estará em um painel, o qual você pode ter que considerar com algum estilo associado que é aplicado somente quando está em um painel em vez de um popup. Também pode haver algumas diferenças na aba ativa quando o painel está aberto. No entanto, o código abaixo, pelo menos, considera isso ao executar consultas com chrome.tabs.query() ou browser.tabs.query() , fazendo com que a resposta seja o que seria esperado se estivesse aberta em um pop-up real em vez de um painel.

O mesmo código continuará funcionando no Firefox 52+. No Firefox 52+, o "_execute_browser_action" ativa diretamente o clique da ação do navegador ou o pop-up.

Para quando você não está usando um pop-up, a principal coisa é que você não use uma function anônima para o ouvinte browserAction.onClicked . Isso permite que a funcionalidade também seja chamada pelo ouvinte commands.onCommand . O commands.onCommand foi introduzido no Firefox 48, então isso deve funcionar em qualquer versão que seja 48+.

Você pode ter alguns problemas com a necessidade de permissions diferentes de activeTab ao usar esse polyfill. Exatamente o que é necessário, se for o caso, dependerá do seu código.

Segue-se uma extensão que faz com que a funcionalidade invocada com um botão de ação do navegador seja executada quando você pressiona o atalho de teclado Alt-Shift-J. Ele irá ativar a function doActionButton() , ou, se um popup for definido, ele irá abrir o seu popup como um painel que irá se comportar de maneira similar a como um popup normalmente se comporta, mas não é perfeito. Ele obtém o nome do arquivo pop-up daquele que está definido atualmente para a guia ativa atual, como seria o caso de clicar no botão browserAction do 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 ainda está em desenvolvimento:

A API WebExtensions ainda está em desenvolvimento. O que está funcionando melhora com cada versão do Firefox. Por enquanto, é melhor você desenvolver e testar seu complemento WebExtension com o Firefox Developer Edition ou Firefox Nightly (para _execute_browser_action ). Você também deve anotar cuidadosamente qual versão do Firefox é necessária para a funcionalidade que deseja usar. Essas informações estão contidas na seção “Compatibilidade do navegador” das páginas de documentação do MDN.


Algumas partes do código nesta questão foram copiadas / modificadas de várias outras respostas minhas.

Suporte para _exectue_browser_action está a caminho: https://bugzilla.mozilla.org/show_bug.cgi?id=1246034

Enquanto isso, tenho certeza de que não é possível.

    Intereting Posts