From b66b01525259d5cc2f7afc415a2c4ec67ee19795 Mon Sep 17 00:00:00 2001 From: eight Date: Sat, 1 Feb 2020 23:43:49 +0800 Subject: [PATCH] Change: detect all kinds of manager in openManage --- background/background.js | 28 ++++++++++-- content/apply.js | 12 ----- js/messaging.js | 98 ++++++++++++++++++++++++---------------- js/router.js | 8 +++- manage.html | 2 +- manage/filters.js | 4 +- 6 files changed, 92 insertions(+), 60 deletions(-) diff --git a/background/background.js b/background/background.js index 5bc6535a..b14da773 100644 --- a/background/background.js +++ b/background/background.js @@ -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}); + }); + }); } diff --git a/content/apply.js b/content/apply.js index 291673c5..7b35b1fc 100644 --- a/content/apply.js +++ b/content/apply.js @@ -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; } } diff --git a/js/messaging.js b/js/messaging.js index e12afd3e..c2e4f704 100644 --- a/js/messaging.js +++ b/js/messaging.js @@ -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); } diff --git a/js/router.js b/js/router.js index eea8cd25..175affb5 100644 --- a/js/router.js +++ b/js/router.js @@ -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) { diff --git a/manage.html b/manage.html index 1134343a..05903e91 100644 --- a/manage.html +++ b/manage.html @@ -150,9 +150,9 @@ - + diff --git a/manage/filters.js b/manage/filters.js index 04212cbc..d027b815 100644 --- a/manage/filters.js +++ b/manage/filters.js @@ -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 {