How to create a global hotkey to open a browserAction popup in Firefox (WebExtensions)?

Chrome doesn't seem to have an API to open a popup, but it does have a special system for this with a hotkey: the _execute_browser_action key in commands .

The special _execute_browser_action functionality in commands not supported by ( 1 ) Firefox.

The type of popup I care about is browserAction , not pageAction .

How can I open a browserAction popup when a key combination or key combination is pressed?

+5
source share
2 answers

Available initially in versions of Firefox> = 52

This functionality will be available in Firefox 52, which is currently Firefox Developer Edition (i.e. Firefox 52.0a2). As you know, for WebExtensions you create a global hotkey with the _execute_browser_action key in the object supplied for the _execute_browser_action key. For instance:

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

Open a pseudo popup (polyfill this feature in older versions of Firefox)

While explicit functionality is not available until Firefox 52, you can polyfill use this function in the current version of Firefox by specifying a user command named "_execute_browser_action" . It will look a little different than a regular popup, but it will work. It will be in the panel, which you may have to consider with some associated style, which applies only when it is on the panel instead of a popup. There may also be some differences in the fact that the tab is active when your panel is open. However, the code below at least takes this into account when executing queries using chrome.tabs.query() or browser.tabs.query() , making the answer as expected if it were opened in a real popup instead of panels.

The same code will continue to work in Firefox 52+. In Firefox 52+, the _execute_browser_action function activates a browser click action or pop-up window.

If you are not using a popup, the main thing is that you do not use an anonymous function for the browserAction.onClicked listener. This allows functionality to also be invoked by the commands.onCommand listener. commands.onCommand was introduced in Firefox 48, so this should work on any version that is 48+.

Using this polyfill may cause problems with rights other than activeTab . Exactly what is needed, in any case, will depend on your code.

The following is an extension that invokes functionality using a browser action button when you press Alt-Shift-J. It either activates the doActionButton() function, or, if the popup is defined, it will open your popup in the form of a panel that will behave similarly to the way the popup works, but it is not perfect. It gets the name of the pop-up file from the one that is currently defined for the currently active tab, as it would be when the browserAction button was 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 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 is still under development:

The WebExtensions API is developing a lot. Which improves the work with each version of Firefox. At the moment, it's probably best for you to develop and test a WebExtension add-in with Firefox Developer Edition or Firefox Nightly (for _execute_browser_action ). You should also carefully monitor which version of Firefox is required for the functionality you want to use. This information is contained in the "Browser Compatibility" section of the MDN documentation pages.


Some parts of the code in this question have been copied / modified from my other other answers.

+4
source

Support for _exectue_browser_action is on the way: https://bugzilla.mozilla.org/show_bug.cgi?id=1246034

Meanwhile, I am quite sure that this is impossible.

+1
source

Source: https://habr.com/ru/post/1014573/


All Articles