fix usage of openerTabId in openURL + cosmetics (#859)

* fix usage of openerTabId in openURL + cosmetics

* fixups

* fixup
This commit is contained in:
tophf 2020-02-21 01:54:54 +03:00 committed by GitHub
parent 8380a674b9
commit df8c258c84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 54 additions and 59 deletions

View File

@ -1,7 +1,7 @@
/* global download prefs openURL FIREFOX CHROME VIVALDI /* global download prefs openURL FIREFOX CHROME VIVALDI
debounce URLS ignoreChromeError getTab debounce URLS ignoreChromeError getTab
styleManager msg navigatorUtil iconUtil workerUtil contentScripts sync styleManager msg navigatorUtil iconUtil workerUtil contentScripts sync
findExistTab createTab activateTab isTabReplaceable getActiveTab */ findExistingTab createTab activateTab isTabReplaceable getActiveTab */
'use strict'; 'use strict';
@ -437,7 +437,7 @@ function openManage({options = false, search} = {}) {
if (options) { if (options) {
url += '#stylus-options'; url += '#stylus-options';
} }
return findExistTab({ return findExistingTab({
url, url,
currentWindow: null, currentWindow: null,
ignoreHash: true, ignoreHash: true,

View File

@ -97,9 +97,13 @@ const createTab = promisify(chrome.tabs.create.bind(chrome.tabs));
const queryTabs = promisify(chrome.tabs.query.bind(chrome.tabs)); const queryTabs = promisify(chrome.tabs.query.bind(chrome.tabs));
const updateTab = promisify(chrome.tabs.update.bind(chrome.tabs)); const updateTab = promisify(chrome.tabs.update.bind(chrome.tabs));
const moveTabs = promisify(chrome.tabs.move.bind(chrome.tabs)); const moveTabs = promisify(chrome.tabs.move.bind(chrome.tabs));
// FIXME: is it possible that chrome.windows is undefined?
const updateWindow = promisify(chrome.windows.update.bind(chrome.windows)); // Android doesn't have chrome.windows
const createWindow = promisify(chrome.windows.create.bind(chrome.windows)); const updateWindow = chrome.windows && promisify(chrome.windows.update.bind(chrome.windows));
const createWindow = chrome.windows && promisify(chrome.windows.create.bind(chrome.windows));
// FF57+ supports openerTabId, but not in Android
// (detecting FF57 by the feature it added, not navigator.ua which may be spoofed in about:config)
const openerTabIdSupported = (!FIREFOX || window.AbortController) && chrome.windows != null;
function getTab(id) { function getTab(id) {
return new Promise(resolve => return new Promise(resolve =>
@ -210,7 +214,7 @@ function urlToMatchPattern(url, ignoreSearch) {
return `${url.protocol}//${url.hostname}/${url.pathname}${url.search}`; return `${url.protocol}//${url.hostname}/${url.pathname}${url.search}`;
} }
function findExistTab({url, currentWindow, ignoreHash = true, ignoreSearch = false}) { function findExistingTab({url, currentWindow, ignoreHash = true, ignoreSearch = false}) {
url = new URL(url); url = new URL(url);
return queryTabs({url: urlToMatchPattern(url, ignoreSearch), currentWindow}) return queryTabs({url: urlToMatchPattern(url, ignoreSearch), currentWindow})
// FIXME: is tab.url always normalized? // FIXME: is tab.url always normalized?
@ -232,65 +236,53 @@ function findExistTab({url, currentWindow, ignoreHash = true, ignoreSearch = fal
/** /**
* Opens a tab or activates an existing one, * Opens a tab or activates an existing one,
* reuses the New Tab page or about:blank if it's focused now * reuses the New Tab page or about:blank if it's focused now
* @param {Object} params * @param {Object} _
* or just a string e.g. openURL('foo') * @param {string} _.url - if relative, it's auto-expanded to the full extension URL
* @param {string} params.url * @param {number} [_.index] move the tab to this index in the tab strip, -1 = last
* if relative, it's auto-expanded to the full extension URL * @param {number} [_.openerTabId] defaults to the active tab
* @param {number} [params.index] * @param {Boolean} [_.active=true] `true` to activate the tab
* move the tab to this index in the tab strip, -1 = last * @param {Boolean|null} [_.currentWindow=true] `null` to check all windows
* @param {Boolean} [params.active] * @param {Boolean} [_.newWindow=false] `true` to open a new window
* true to activate the tab (this is the default value in the extensions API), * @param {chrome.windows.CreateData} [_.windowPosition] options for chrome.windows.create
* false to open in background * @returns {Promise<chrome.tabs.Tab>} Promise -> opened/activated tab
* @param {?Boolean} [params.currentWindow]
* pass null to check all windows
* @param {any} [params.message]
* JSONifiable data to be sent to the tab via sendMessage()
* @returns {Promise<Tab>} Promise that resolves to the opened/activated tab
*/ */
function openURL(options) { function openURL({
if (typeof options === 'string') {
options = {url: options};
}
let {
url, url,
index, index,
active, openerTabId,
active = true,
currentWindow = true, currentWindow = true,
newWindow = false, newWindow = false,
windowPosition windowPosition,
} = options; }) {
if (!url.includes('://')) { if (!url.includes('://')) {
url = chrome.runtime.getURL(url); url = chrome.runtime.getURL(url);
} }
return findExistTab({url, currentWindow}).then(tab => { return findExistingTab({url, currentWindow}).then(tab => {
if (tab) { if (tab) {
// update url if only hash is different? return activateTab(tab, {
// we can't update URL if !url.includes('#') since it refreshes the page index,
// FIXME: should we move the tab (i.e. specifying index)? openerTabId,
if (tab.url !== url && tab.url.split('#')[0] === url.split('#')[0] && // when hash is different we can only set `url` if it has # otherwise the tab would reload
url.includes('#')) { url: url !== tab.url && url.includes('#') ? url : undefined,
return activateTab(tab, {url, index}); });
} }
return activateTab(tab, {index}); if (newWindow && createWindow) {
return createWindow(Object.assign({url}, windowPosition))
.then(wnd => wnd.tabs[0]);
} }
if (newWindow) { return getActiveTab().then((activeTab = {url: ''}) =>
return createWindow(Object.assign({url}, windowPosition)); isTabReplaceable(activeTab, url) ?
} activateTab(activeTab, {url, openerTabId}) : // not moving the tab
return getActiveTab().then(tab => { createTabWithOpener(activeTab, {url, index, active}));
if (isTabReplaceable(tab, url)) { });
// don't move the tab in this case function createTabWithOpener(openerTab, options) {
return activateTab(tab, {url}); const id = openerTabId == null ? openerTab.id : openerTabId;
} if (id != null && !openerTab.incognito && openerTabIdSupported) {
const options = {url, index, active}; options.openerTabId = id;
// FF57+ supports openerTabId, but not in Android (indicated by the absence of chrome.windows)
// FIXME: is it safe to assume that the current tab is the opener?
if (tab && !tab.incognito && (!FIREFOX || FIREFOX >= 57 && chrome.windows)) {
options.openerTabId = tab.id;
} }
return createTab(options); return createTab(options);
}); }
});
} }
// replace empty tab (NTP or about:blank) // replace empty tab (NTP or about:blank)
@ -307,14 +299,17 @@ function isTabReplaceable(tab, newUrl) {
return true; return true;
} }
function activateTab(tab, {url, index} = {}) { function activateTab(tab, {url, index, openerTabId} = {}) {
const options = {active: true}; const options = {active: true};
if (url) { if (url) {
options.url = url; options.url = url;
} }
if (openerTabId != null && openerTabIdSupported) {
options.openerTabId = openerTabId;
}
return Promise.all([ return Promise.all([
updateTab(tab.id, options), updateTab(tab.id, options),
updateWindow(tab.windowId, {focused: true}), updateWindow && updateWindow(tab.windowId, {focused: true}),
index != null && moveTabs(tab.id, {index}) index != null && moveTabs(tab.id, {index})
]) ])
.then(() => tab); .then(() => tab);