javascript firefox-webextensions

javascript - ¿Cómo crear una tecla de acceso rápido global para abrir la ventana emergente "browserAction" en Firefox(WebExtensions)?



firefox-webextensions (3)

Parece que Chrome no tiene una API para abrir la ventana emergente, pero tiene un sistema dedicado para hacerlo con una tecla de _execute_browser_action rápido: _execute_browser_action en los commands .

Firefox no admite la funcionalidad especial de _execute_browser_action en los commands ( 1 ).

El tipo de ventana emergente que me interesa es browserAction , no pageAction .

¿Cómo puedo tener abierta la browserAction emergente browserAction cuando se presiona una combinación de teclas de acceso directo / combinación de teclas


Disponible de forma nativa en versiones de Firefox> = 52

Esta funcionalidad estará disponible de forma nativa en Firefox 52, que actualmente es Firefox Developer Edition (es decir, Firefox 52.0a2). Como sabe, para WebExtensions, puede crear una tecla de _execute_browser_action rápido global utilizando la tecla _execute_browser_action dentro del objeto suministrado para la tecla de commands . Por ejemplo:

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

Abra una pseudo-ventana emergente (polyfill esta funcionalidad en las versiones anteriores de Firefox)

Si bien la funcionalidad explícita no estará disponible hasta Firefox 52, puede rellenar esta funcionalidad en la versión actual de Firefox, definiendo un comando personalizado que se llama "_execute_browser_action" . Se verá un poco diferente a su ventana emergente normal, pero será funcional. Estará en un panel, que es posible que tenga que tener en cuenta con un estilo asociado que se aplica solo cuando está en un panel en lugar de una ventana emergente. También puede haber algunas diferencias en lo que es la pestaña activa cuando su panel está abierto. Sin embargo, el código siguiente al menos da cuenta de eso al realizar consultas con chrome.tabs.query() o browser.tabs.query() , al hacer que la respuesta sea lo que se esperaría si estuviera abierto en una ventana emergente real en lugar de un panel.

El mismo código seguirá funcionando en Firefox 52+. En Firefox 52+, la "_execute_browser_action" activa directamente el clic de acción del navegador o ventana emergente.

Para cuando no está utilizando una ventana emergente, lo principal es que no utilice una función anónima para el escucha browserAction.onClicked . Esto permite que la funcionalidad también sea llamada por los oyentes de command.onCommand. El commands.onCommand se introdujo en Firefox 48, por lo que debería funcionar en cualquier versión que sea 48+.

Puede tener algunos problemas con la necesidad de permisos distintos de activeTab al usar este polyfill. Lo que se necesita exactamente, en todo caso, dependerá de su código.

La siguiente es una extensión que hace que la funcionalidad invocada con un botón de acción del navegador se ejecute cuando presiona el método abreviado de teclado Alt-Shift-J. doActionButton() función doActionButton() o, si se define una ventana emergente, abrirá su ventana emergente como un panel que se comportará de manera similar a como se comporta normalmente una ventana emergente, pero no es perfecta. Obtiene el nombre del archivo emergente del que está actualmente definido para la pestaña activa actual, como sería el caso de hacer clic en el botón de browserAction navegador.

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 todavía está en desarrollo:

La API de WebExtensions todavía está en desarrollo. Lo que funciona mejora con cada versión de Firefox. Por ahora, probablemente sea mejor desarrollar y probar su complemento WebExtension con Firefox Developer Edition o Firefox Nightly (para _execute_browser_action ). También debe anotar cuidadosamente qué versión de Firefox se requiere para la funcionalidad que desea utilizar. Esta información está contenida en la sección "Compatibilidad del navegador" de las páginas de documentación de MDN.

Algunas partes del código en esta pregunta se han copiado / modificado de varias otras respuestas mías.