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 /* 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 */
'use strict'; 'use strict';
// eslint-disable-next-line no-var // eslint-disable-next-line no-var
@ -412,12 +414,32 @@ function openEditor(params) {
} }
function openManage({options = false, search} = {}) { function openManage({options = false, search} = {}) {
let url = 'manage.html'; let url = chrome.runtime.getURL('manage.html');
if (search) { if (search) {
url += `?search=${encodeURIComponent(search)}`; url += `?search=${encodeURIComponent(search)}`;
} }
if (options) { if (options) {
url += '#stylus-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': case 'updateCount':
updateCount(); updateCount();
break; 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 /* exported getActiveTab onTabReady stringAsRegExp getTabRealURL openURL
getStyleWithNoCode tryRegExp sessionStorageHash download deepEqual getStyleWithNoCode tryRegExp sessionStorageHash download deepEqual
closeCurrentTab capitalize CHROME_HAS_BORDER_BUG */ closeCurrentTab capitalize CHROME_HAS_BORDER_BUG */
/* global promisify msg */ /* global promisify */
'use strict'; 'use strict';
const CHROME = Boolean(chrome.app) && parseInt(navigator.userAgent.match(/Chrom\w+\/(?:\d+\.){2}(\d+)|$/)[1]); 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 // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Match_patterns
if (!/^(http|https|ws|wss|ftp|data|file)$/.test(url.protocol)) { if (!/^(http|https|ws|wss|ftp|data|file)$/.test(url.protocol)) {
return undefined; 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? // FIXME: is %2f allowed in pathname and search?
return `${url.protocol}//${url.hostname}/${url.pathname}${url.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); url = new URL(url);
const normalizedUrl = url.href.split('#')[0]; return queryTabs({url: urlToMatchPattern(url, ignoreSearch), currentWindow})
return queryTabs({url: urlToMatchPattern(url), currentWindow})
// FIXME: is tab.url always normalized? // 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('://')) { if (!url.includes('://')) {
url = chrome.runtime.getURL(url); url = chrome.runtime.getURL(url);
} }
return findExistTab(url, currentWindow) return findExistTab({url, currentWindow}).then(tab => {
.then(tab => { if (tab) {
if (!tab) {
return getActiveTab().then(maybeReplace);
}
// update url if only hash is different? // update url if only hash is different?
if (tab.url !== url && tab.url.split('#')[0] === url.split('#')[0]) { // we can't update URL if !url.includes('#') since it refreshes the page
if (url.includes('#')) { if (tab.url !== url && tab.url.split('#')[0] === url.split('#')[0] &&
return activateTab(tab, {url, index}); url.includes('#')) {
} else { return activateTab(tab, {url, index});
// 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)
]);
}
} }
return activateTab(tab, {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) { if (newWindow) {
return createWindow(Object.assign({url}, windowPosition)); return createWindow(Object.assign({url}, windowPosition));
} }
const options = {url, index, active}; return getActiveTab().then(tab => {
// FF57+ supports openerTabId, but not in Android (indicated by the absence of chrome.windows) if (isTabReplaceable(tab, url)) {
// FIXME: is it safe to assume that the current tab is the opener? return activateTab(tab, {url, index});
if (tab && (!FIREFOX || FIREFOX >= 57 && chrome.windows) && !chromeInIncognito) { }
options.openerTabId = tab.id; const options = {url, index, active};
} // FF57+ supports openerTabId, but not in Android (indicated by the absence of chrome.windows)
return createTab(options); // 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}; const options = {active: true};
if (url) { if (url) {
options.url = url; options.url = url;
@ -297,7 +314,8 @@ function activateTab(tab, {url, index}) {
updateTab(tab.id, options), updateTab(tab.id, options),
updateWindow(tab.windowId, {focused: true}), updateWindow(tab.windowId, {focused: true}),
index != null && moveTabs(tab.id, {index}) index != null && moveTabs(tab.id, {index})
]); ])
.then(() => tab);
} }

View File

@ -1,4 +1,4 @@
/* global deepEqual */ /* global deepEqual msg */
/* exported router */ /* exported router */
'use strict'; 'use strict';
@ -8,6 +8,12 @@ const router = (() => {
document.addEventListener('DOMContentLoaded', () => update()); document.addEventListener('DOMContentLoaded', () => update());
window.addEventListener('popstate', () => update()); window.addEventListener('popstate', () => update());
window.addEventListener('hashchange', () => 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}; return {watch, updateSearch, getSearch, updateHash};
function watch(options, callback) { function watch(options, callback) {

View File

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

View File

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