Change: detect all kinds of manager in openManage

This commit is contained in:
eight 2020-02-01 23:43:49 +08:00
parent c2ac963232
commit b66b015252
6 changed files with 92 additions and 60 deletions

View File

@ -1,6 +1,8 @@
/* global download prefs openURL FIREFOX CHROME VIVALDI
debounce URLS ignoreChromeError getTab
styleManager msg navigatorUtil iconUtil workerUtil contentScripts sync */
styleManager msg navigatorUtil iconUtil workerUtil contentScripts sync
findExistTab createTab activateTab isTabReplaceable getActiveTab */
'use strict';
// eslint-disable-next-line no-var
@ -412,12 +414,32 @@ function openEditor(params) {
}
function openManage({options = false, search} = {}) {
let url = 'manage.html';
let url = chrome.runtime.getURL('manage.html');
if (search) {
url += `?search=${encodeURIComponent(search)}`;
}
if (options) {
url += '#stylus-options';
}
return openURL({url, currentWindow: null});
return findExistTab({
url,
currentWindow: null,
ignoreHash: true,
ignoreSearch: true
})
.then(tab => {
if (tab) {
return Promise.all([
activateTab(tab),
tab.url !== url && msg.sendTab(tab.id, {method: 'pushState', url})
.catch(console.warn)
]);
}
return getActiveTab().then(tab => {
if (isTabReplaceable(tab, url)) {
return activateTab(tab, {url});
}
return createTab({url});
});
});
}

View File

@ -260,18 +260,6 @@ const APPLY = (() => {
case 'updateCount':
updateCount();
break;
case 'trimHash':
if (IS_OWN_PAGE) {
// FIXME: currently we only do this in our own page. Is it safe to do
// it on all pages?
try {
// history.replaceState(null, null, ' ');
// eslint-disable-next-line no-undef
router.updateHash('');
} catch (err) {}
}
break;
}
}

View File

@ -1,7 +1,7 @@
/* exported getActiveTab onTabReady stringAsRegExp getTabRealURL openURL
getStyleWithNoCode tryRegExp sessionStorageHash download deepEqual
closeCurrentTab capitalize CHROME_HAS_BORDER_BUG */
/* global promisify msg */
/* global promisify */
'use strict';
const CHROME = Boolean(chrome.app) && parseInt(navigator.userAgent.match(/Chrom\w+\/(?:\d+\.){2}(\d+)|$/)[1]);
@ -195,21 +195,38 @@ function onTabReady(tabOrId) {
});
}
function urlToMatchPattern(url) {
function urlToMatchPattern(url, ignoreSearch) {
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Match_patterns
if (!/^(http|https|ws|wss|ftp|data|file)$/.test(url.protocol)) {
return undefined;
}
if (ignoreSearch) {
return [
`${url.protocol}//${url.hostname}/${url.pathname}`,
`${url.protocol}//${url.hostname}/${url.pathname}?*`
];
}
// FIXME: is %2f allowed in pathname and search?
return `${url.protocol}//${url.hostname}/${url.pathname}${url.search}`;
}
function findExistTab(url, currentWindow) {
function findExistTab({url, currentWindow, ignoreHash = true, ignoreSearch = false}) {
url = new URL(url);
const normalizedUrl = url.href.split('#')[0];
return queryTabs({url: urlToMatchPattern(url), currentWindow})
return queryTabs({url: urlToMatchPattern(url, ignoreSearch), currentWindow})
// FIXME: is tab.url always normalized?
.then(tabs => tabs.find(tab => tab.url.split('#')[0] === normalizedUrl));
.then(tabs => tabs.find(matchTab));
function matchTab(tab) {
const tabUrl = new URL(tab.url);
return tabUrl.protocol === url.protocol &&
tabUrl.username === url.username &&
tabUrl.password === url.password &&
tabUrl.hostname === url.hostname &&
tabUrl.port === url.port &&
tabUrl.pathname === url.pathname &&
(ignoreSearch || tabUrl.search === url.search) &&
(ignoreHash || tabUrl.hash === url.hash);
}
}
/**
@ -246,49 +263,49 @@ function openURL(options) {
if (!url.includes('://')) {
url = chrome.runtime.getURL(url);
}
return findExistTab(url, currentWindow)
.then(tab => {
if (!tab) {
return getActiveTab().then(maybeReplace);
}
return findExistTab({url, currentWindow}).then(tab => {
if (tab) {
// update url if only hash is different?
if (tab.url !== url && tab.url.split('#')[0] === url.split('#')[0]) {
if (url.includes('#')) {
return activateTab(tab, {url, index});
} else {
// we can't update URL directly since it refresh the page
return Promise.all([
activateTab(tab, {index}),
msg.sendTab(tab.id, {method: 'trimHash'}).catch(console.warn)
]);
}
// we can't update URL if !url.includes('#') since it refreshes the page
if (tab.url !== url && tab.url.split('#')[0] === url.split('#')[0] &&
url.includes('#')) {
return activateTab(tab, {url, index});
}
return activateTab(tab, {index});
});
// update current NTP or about:blank
// except when 'url' is chrome:// or chrome-extension:// in incognito
function maybeReplace(tab) {
const chromeInIncognito = tab && tab.incognito && url.startsWith('chrome');
const emptyTab = tab && URLS.emptyTab.includes(tab.url);
if (emptyTab && !chromeInIncognito) {
return activateTab(tab, {url, index}); // FIXME: should we move current empty tab?
}
if (newWindow) {
return createWindow(Object.assign({url}, windowPosition));
}
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 && (!FIREFOX || FIREFOX >= 57 && chrome.windows) && !chromeInIncognito) {
options.openerTabId = tab.id;
}
return createTab(options);
}
return getActiveTab().then(tab => {
if (isTabReplaceable(tab, url)) {
return activateTab(tab, {url, index});
}
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);
});
});
}
// replace empty tab (NTP or about:blank)
// except when new URL is chrome:// or chrome-extension:// and the empty tab is
// in incognito
function isTabReplaceable(tab, newUrl) {
if (!tab || !URLS.emptyTab.includes(tab.url)) {
return false;
}
// FIXME: why?
if (tab.incognito && newUrl.startsWith('chrome')) {
return false;
}
return true;
}
function activateTab(tab, {url, index}) {
function activateTab(tab, {url, index} = {}) {
const options = {active: true};
if (url) {
options.url = url;
@ -297,7 +314,8 @@ function activateTab(tab, {url, index}) {
updateTab(tab.id, options),
updateWindow(tab.windowId, {focused: true}),
index != null && moveTabs(tab.id, {index})
]);
])
.then(() => tab);
}

View File

@ -1,4 +1,4 @@
/* global deepEqual */
/* global deepEqual msg */
/* exported router */
'use strict';
@ -8,6 +8,12 @@ const router = (() => {
document.addEventListener('DOMContentLoaded', () => update());
window.addEventListener('popstate', () => update());
window.addEventListener('hashchange', () => update());
msg.on(e => {
if (e.method === 'pushState' && e.url !== location.href) {
history.pushState(history.state, null, e.url);
update();
}
});
return {watch, updateSearch, getSearch, updateHash};
function watch(options, callback) {

View File

@ -150,9 +150,9 @@
<script src="js/promisify.js"></script>
<script src="js/dom.js"></script>
<script src="js/messaging.js"></script>
<script src="js/router.js"></script>
<script src="js/prefs.js"></script>
<script src="js/msg.js"></script>
<script src="js/router.js"></script>
<script src="content/style-injector.js"></script>
<script src="content/apply.js"></script>
<script src="js/localization.js"></script>

View File

@ -12,9 +12,7 @@ const filtersSelector = {
let initialized = false;
router.watch({search: ['search']}, ([search]) => {
if (search != null) {
$('#search').value = search;
}
$('#search').value = search || '';
if (!initialized) {
init();
} else {