Reputation:
The question: In Firefox WebExtensions, from arbitrary background origins, how can I open an arbitrary page in a tab, uniquely?
Requirements:
1) Arbitrary background origins. My initial use case is from browser_action contextMenus. However, the technique ought to work from popup, options, or any custom background script. For the moment, not concerned with content pages. Although if the technique works for them too, great.
2) Open an arbitrary page in a tab. My initial use is for the options page, but the technique should work for arbitrary background pages (options, popup, or custom pages).
3) Open uniquely. If it's the first time, open in a new tab. If the tab was previously opened, focus to the existing tab, don't create duplicate tabs.
4) If I close a tab, I need to make sure I remove my reference to the previously opened tab ID.
I've answered my own question with one possible solution.
If the solution could be improved upon, let me know. Thank you.
If the question is a duplicate, I'll remove and post solution elsewhere if appropriate.
Upvotes: 0
Views: 1187
Reputation:
This is an attempt to answer my own question, with comments inline.
This simple example assumes all pagers live in the top level directory, but this is arbitrary and easily changed.
Essentially it consists of four parts:
1) A global object tabIDs
to hold the page names (without '.html'). You could change this to be full path including extension, or keep the page name as a short name and modify technique to use another option like path for the full path name, etc.
2) An async function (to make use of the new await feature) named tabCreate
to determine if one is already open and switch to it or create a new tab.
3) An onRemoved event handler, tabRemove
to handle cleanup of tabIDs
after a tab is closed (if it was one of interest).
4) A usage example, from a context menu item, passing some a page
and a panel
option, which have no use in this question, but are part of another question, and just here for demonstration purposes.
background.js:
// tabID contexts
// global var to keep track of different tabs,
// i.e. options.html, popup.html and so on.
var tabIDs = {
'options': null,
'popup': null,
}
// Requires Firefox 52.0+ to use async/await
// opts correspond to contexts above in tabIDs
// of the form { 'page': 'options' } or { 'page': 'popup' }
// note if using Node.js, this may require v7+ and --harmony_async_await
async function tabCreate ( opts ) {
var tab;
if ( tabIDs[ opts.page ] !== null ) {
// should probably bring window to front first... oops
// ..
// switch to tab
tab = await browser.tabs.update( tabIDs[ opts.page ], { active: true } );
} else {
tab = await browser.tabs.create( {
'url': opts.page + '.html'
} );
tabIDs[ opts.page ] = tab.id;
}
console.log( '**** opts.page = ' + opts.page + ', opts.tab = ' + opts.tab + ', tab.id = ' + tab.id );
}
// When tabs are closed, see if the tabID is in tabIDs,
// and if so, set it to null
function tabRemove ( tabID, removeInfo ) {
console.log( 'Closed TAB ' + tabID );
Object.keys( tabIDs ).forEach( function( key, index ) {
if ( tabIDs[ key ] === tabID ) {
tabIDs[ key ] = null;
return;
}
} );
}
browser.tabs.onRemoved.addListener( tabRemove );
/*
* Context Menus
*/
browser.contextMenus.removeAll( );
browser.contextMenus.create( {
title: 'My Web Extension',
contexts: [ 'browser_action' ],
} );
browser.contextMenus.create( {
title: 'Options',
contexts: [ 'browser_action' ],
onclick: myWebExt_Options
} );
function myWebExt_Options ( ) {
tabCreate( {
'page': 'options',
'panel': 1,
} );
}
Another approach might be to add an event listener to each page, and when closed, send a message back to background.js, but that seems much more complicated with little or no benefit.
Upvotes: 0