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
debounce URLS ignoreChromeError getTab
styleManager msg navigatorUtil iconUtil workerUtil contentScripts sync
findExistTab createTab activateTab isTabReplaceable getActiveTab */
findExistingTab createTab activateTab isTabReplaceable getActiveTab */
'use strict';
@ -437,7 +437,7 @@ function openManage({options = false, search} = {}) {
if (options) {
url += '#stylus-options';
}
return findExistTab({
return findExistingTab({
url,
currentWindow: null,
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 updateTab = promisify(chrome.tabs.update.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));
const createWindow = promisify(chrome.windows.create.bind(chrome.windows));
// Android doesn't have 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) {
return new Promise(resolve =>
@ -210,7 +214,7 @@ function urlToMatchPattern(url, ignoreSearch) {
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);
return queryTabs({url: urlToMatchPattern(url, ignoreSearch), currentWindow})
// 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,
* reuses the New Tab page or about:blank if it's focused now
* @param {Object} params
* or just a string e.g. openURL('foo')
* @param {string} params.url
* if relative, it's auto-expanded to the full extension URL
* @param {number} [params.index]
* move the tab to this index in the tab strip, -1 = last
* @param {Boolean} [params.active]
* true to activate the tab (this is the default value in the extensions API),
* false to open in background
* @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
* @param {Object} _
* @param {string} _.url - if relative, it's auto-expanded to the full extension URL
* @param {number} [_.index] move the tab to this index in the tab strip, -1 = last
* @param {number} [_.openerTabId] defaults to the active tab
* @param {Boolean} [_.active=true] `true` to activate the tab
* @param {Boolean|null} [_.currentWindow=true] `null` to check all windows
* @param {Boolean} [_.newWindow=false] `true` to open a new window
* @param {chrome.windows.CreateData} [_.windowPosition] options for chrome.windows.create
* @returns {Promise<chrome.tabs.Tab>} Promise -> opened/activated tab
*/
function openURL(options) {
if (typeof options === 'string') {
options = {url: options};
}
let {
url,
index,
active,
currentWindow = true,
newWindow = false,
windowPosition
} = options;
function openURL({
url,
index,
openerTabId,
active = true,
currentWindow = true,
newWindow = false,
windowPosition,
}) {
if (!url.includes('://')) {
url = chrome.runtime.getURL(url);
}
return findExistTab({url, currentWindow}).then(tab => {
return findExistingTab({url, currentWindow}).then(tab => {
if (tab) {
// update url if only hash is different?
// we can't update URL if !url.includes('#') since it refreshes the page
// FIXME: should we move the tab (i.e. specifying index)?
if (tab.url !== url && tab.url.split('#')[0] === url.split('#')[0] &&
url.includes('#')) {
return activateTab(tab, {url, index});
}
return activateTab(tab, {index});
return activateTab(tab, {
index,
openerTabId,
// when hash is different we can only set `url` if it has # otherwise the tab would reload
url: url !== tab.url && url.includes('#') ? url : undefined,
});
}
if (newWindow) {
return createWindow(Object.assign({url}, windowPosition));
if (newWindow && createWindow) {
return createWindow(Object.assign({url}, windowPosition))
.then(wnd => wnd.tabs[0]);
}
return getActiveTab().then(tab => {
if (isTabReplaceable(tab, url)) {
// don't move the tab in this case
return activateTab(tab, {url});
}
const options = {url, index, active};
// 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 getActiveTab().then((activeTab = {url: ''}) =>
isTabReplaceable(activeTab, url) ?
activateTab(activeTab, {url, openerTabId}) : // not moving the tab
createTabWithOpener(activeTab, {url, index, active}));
});
function createTabWithOpener(openerTab, options) {
const id = openerTabId == null ? openerTab.id : openerTabId;
if (id != null && !openerTab.incognito && openerTabIdSupported) {
options.openerTabId = id;
}
return createTab(options);
}
}
// replace empty tab (NTP or about:blank)
@ -307,14 +299,17 @@ function isTabReplaceable(tab, newUrl) {
return true;
}
function activateTab(tab, {url, index} = {}) {
function activateTab(tab, {url, index, openerTabId} = {}) {
const options = {active: true};
if (url) {
options.url = url;
}
if (openerTabId != null && openerTabIdSupported) {
options.openerTabId = openerTabId;
}
return Promise.all([
updateTab(tab.id, options),
updateWindow(tab.windowId, {focused: true}),
updateWindow && updateWindow(tab.windowId, {focused: true}),
index != null && moveTabs(tab.id, {index})
])
.then(() => tab);