[英]How to create a global hotkey for opening the “browserAction” popup in Firefox (WebExtensions)?
似乎Chrome沒有用於打開彈出窗口的API,但是有一個專門的系統可以使用熱鍵執行它: commands
_execute_browser_action
鍵。
Firefox不支持( 1 ) commands
中_execute_browser_action
的特殊功能。
我關心的彈出窗口類型是browserAction
,而不是pageAction
。
當按下鍵盤快捷鍵/熱鍵組合時,如何打開browserAction
彈出窗口?
此功能將在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 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 API仍處於開發階段。 每個版本的Firefox都有所改進。 目前,您可能最好使用Firefox Developer Edition或Firefox 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.