Refactor: openURL
This commit is contained in:
parent
24552269fe
commit
9c8cb9b928
|
@ -400,11 +400,13 @@ function onRuntimeMessage(msg, sender) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: popup.js also open editor but it doesn't use this API.
|
// FIXME: popup.js also open editor but it doesn't use this API.
|
||||||
function openEditor({id}) {
|
function openEditor(params) {
|
||||||
let url = '/edit.html';
|
const searchParams = new URLSearchParams(); // FIXME: use url.searchParams when Chrome >= 51
|
||||||
if (id) {
|
for (const key in params) {
|
||||||
url += `?id=${id}`;
|
searchParams.set(key, params[key]);
|
||||||
}
|
}
|
||||||
|
const search = searchParams.toString();
|
||||||
|
const url = chrome.runtime.getURL('edit.html') + (search && `?${search}`);
|
||||||
if (chrome.windows && prefs.get('openEditInWindow')) {
|
if (chrome.windows && prefs.get('openEditInWindow')) {
|
||||||
chrome.windows.create(Object.assign({url}, prefs.get('windowPosition')));
|
chrome.windows.create(Object.assign({url}, prefs.get('windowPosition')));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -53,7 +53,7 @@ const contentScripts = (() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
function injectToAllTabs() {
|
function injectToAllTabs() {
|
||||||
return queryTabs().then(tabs => {
|
return queryTabs({}).then(tabs => {
|
||||||
for (const tab of tabs) {
|
for (const tab of tabs) {
|
||||||
// skip lazy-loaded aka unloaded tabs that seem to start loading on message in FF
|
// skip lazy-loaded aka unloaded tabs that seem to start loading on message in FF
|
||||||
if (tab.width) {
|
if (tab.width) {
|
||||||
|
|
|
@ -66,7 +66,7 @@ const regExpTester = (() => {
|
||||||
return rxData;
|
return rxData;
|
||||||
});
|
});
|
||||||
const getMatchInfo = m => m && {text: m[0], pos: m.index};
|
const getMatchInfo = m => m && {text: m[0], pos: m.index};
|
||||||
queryTabs().then(tabs => {
|
queryTabs({}).then(tabs => {
|
||||||
const supported = tabs.map(tab => tab.url)
|
const supported = tabs.map(tab => tab.url)
|
||||||
.filter(url => URLS.supported(url));
|
.filter(url => URLS.supported(url));
|
||||||
const unique = [...new Set(supported).values()];
|
const unique = [...new Set(supported).values()];
|
||||||
|
|
148
js/messaging.js
148
js/messaging.js
|
@ -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 */
|
closeCurrentTab capitalize CHROME_HAS_BORDER_BUG */
|
||||||
/* global prefs */
|
/* 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]);
|
||||||
|
@ -93,12 +93,12 @@ if (IS_BG) {
|
||||||
// Object.defineProperty(window, 'localStorage', {value: {}});
|
// Object.defineProperty(window, 'localStorage', {value: {}});
|
||||||
// Object.defineProperty(window, 'sessionStorage', {value: {}});
|
// Object.defineProperty(window, 'sessionStorage', {value: {}});
|
||||||
|
|
||||||
function queryTabs(options = {}) {
|
const createTab = promisify(chrome.tabs.create.bind(chrome.tabs));
|
||||||
return new Promise(resolve =>
|
const queryTabs = promisify(chrome.tabs.query.bind(chrome.tabs));
|
||||||
chrome.tabs.query(options, tabs =>
|
const updateTab = promisify(chrome.tabs.update.bind(chrome.tabs));
|
||||||
resolve(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));
|
||||||
|
|
||||||
function getTab(id) {
|
function getTab(id) {
|
||||||
return new Promise(resolve =>
|
return new Promise(resolve =>
|
||||||
|
@ -194,6 +194,22 @@ function onTabReady(tabOrId) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function urlToMatchPattern(url) {
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
// FIXME: is %2f allowed in pathname and search?
|
||||||
|
return `${url.protocol}//${url.hostname}/${url.pathname}${url.search}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findExistTab(url, currentWindow) {
|
||||||
|
url = new URL(url);
|
||||||
|
const normalizedUrl = url.href.split('#')[0];
|
||||||
|
return queryTabs({url: urlToMatchPattern(url), currentWindow})
|
||||||
|
// FIXME: is tab.url always normalized?
|
||||||
|
.then(tabs => tabs.find(tab => tab.url.split('#')[0] === normalizedUrl));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a tab or activates an existing one,
|
* Opens a tab or activates an existing one,
|
||||||
|
@ -213,75 +229,31 @@ function onTabReady(tabOrId) {
|
||||||
* JSONifiable data to be sent to the tab via sendMessage()
|
* JSONifiable data to be sent to the tab via sendMessage()
|
||||||
* @returns {Promise<Tab>} Promise that resolves to the opened/activated tab
|
* @returns {Promise<Tab>} Promise that resolves to the opened/activated tab
|
||||||
*/
|
*/
|
||||||
function openURL({
|
function openURL(options) {
|
||||||
// https://github.com/eslint/eslint/issues/10639
|
if (typeof options === 'string') {
|
||||||
// eslint-disable-next-line no-undef
|
options = {url: options};
|
||||||
url = arguments[0],
|
|
||||||
index,
|
|
||||||
active,
|
|
||||||
currentWindow = true,
|
|
||||||
}) {
|
|
||||||
url = url.includes('://') ? url : chrome.runtime.getURL(url);
|
|
||||||
// [some] chromium forks don't handle their fake branded protocols
|
|
||||||
url = url.replace(/^(opera|vivaldi)/, 'chrome');
|
|
||||||
const editMatch = /edit\.html/.test(url);
|
|
||||||
// ignore filtered manager URLs with params & edit URLs created from popup on manager page
|
|
||||||
const manageMatch = !editMatch ? /manage\.html(#stylus-options)?$/.test(url) : null;
|
|
||||||
// FF doesn't handle moz-extension:// URLs (bug)
|
|
||||||
// FF decodes %2F in encoded parameters (bug)
|
|
||||||
// API doesn't handle the hash-fragment part
|
|
||||||
const urlQuery =
|
|
||||||
url.startsWith('moz-extension') ||
|
|
||||||
url.startsWith('chrome:') ?
|
|
||||||
undefined :
|
|
||||||
FIREFOX && url.includes('%2F') ?
|
|
||||||
url.replace(/%2F.*/, '*').replace(/#.*/, '') :
|
|
||||||
url.replace(/#.*/, '');
|
|
||||||
|
|
||||||
return manageMatch || editMatch ? queryTabs().then(maybeSwitch) :
|
|
||||||
queryTabs({url: urlQuery, currentWindow}).then(maybeSwitch);
|
|
||||||
|
|
||||||
function maybeSwitch(tabs = []) {
|
|
||||||
const urlWithSlash = url + '/';
|
|
||||||
const urlFF = FIREFOX && url.replace(/%2F/g, '/');
|
|
||||||
const urlOptions = manageMatch ? URLS.ownOrigin + 'manage.html#stylus-options' : null;
|
|
||||||
const urlManage = manageMatch ? URLS.ownOrigin + 'manage.html' : null;
|
|
||||||
const tab = tabs.find(({url: u}) => u === url || u === urlFF || u === urlWithSlash ||
|
|
||||||
u === urlOptions || u === urlManage);
|
|
||||||
if (!tab && prefs.get('openEditInWindow') && chrome.windows && editMatch) {
|
|
||||||
chrome.windows.create(
|
|
||||||
Object.assign({
|
|
||||||
url: url
|
|
||||||
}, prefs.get('windowPosition', {}))
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (manageMatch) {
|
|
||||||
if (tab) {
|
|
||||||
const toggleOptions = url === urlOptions ? 'options-open' : 'options-close';
|
|
||||||
chrome.tabs.sendMessage(tab.id, {
|
|
||||||
'name': 'options',
|
|
||||||
'data': toggleOptions
|
|
||||||
});
|
|
||||||
}
|
|
||||||
getActiveTab()
|
|
||||||
.then(currentTab => {
|
|
||||||
if (!(tab && FIREFOX && currentTab.windowId !== tab.windowId)) {
|
|
||||||
chrome.runtime.sendMessage({
|
|
||||||
'name': 'popup',
|
|
||||||
'data': 'close-popup'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!tab) {
|
|
||||||
return getActiveTab().then(maybeReplace);
|
|
||||||
}
|
|
||||||
if (index !== undefined && tab.index !== index) {
|
|
||||||
chrome.tabs.move(tab.id, {index});
|
|
||||||
}
|
|
||||||
return activateTab(tab);
|
|
||||||
}
|
}
|
||||||
|
let {
|
||||||
|
url,
|
||||||
|
index,
|
||||||
|
active,
|
||||||
|
currentWindow = true,
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
if (!url.includes('://')) {
|
||||||
|
url = chrome.runtime.getURL(url);
|
||||||
|
}
|
||||||
|
return findExistTab(url, currentWindow)
|
||||||
|
.then(tab => {
|
||||||
|
if (!tab) {
|
||||||
|
return getActiveTab().then(maybeReplace);
|
||||||
|
}
|
||||||
|
// update url if only hash is different
|
||||||
|
if (tab.url !== url && tab.url.split('#')[0] === url.split('#')[0]) {
|
||||||
|
return activateTab(tab, {url, index});
|
||||||
|
}
|
||||||
|
return activateTab(tab, {index});
|
||||||
|
});
|
||||||
|
|
||||||
// update current NTP or about:blank
|
// update current NTP or about:blank
|
||||||
// except when 'url' is chrome:// or chrome-extension:// in incognito
|
// except when 'url' is chrome:// or chrome-extension:// in incognito
|
||||||
|
@ -289,29 +261,29 @@ function openURL({
|
||||||
const chromeInIncognito = tab && tab.incognito && url.startsWith('chrome');
|
const chromeInIncognito = tab && tab.incognito && url.startsWith('chrome');
|
||||||
const emptyTab = tab && URLS.emptyTab.includes(tab.url);
|
const emptyTab = tab && URLS.emptyTab.includes(tab.url);
|
||||||
if (emptyTab && !chromeInIncognito) {
|
if (emptyTab && !chromeInIncognito) {
|
||||||
return new Promise(resolve =>
|
return activateTab(tab, {url, index}); // FIXME: should we move current empty tab?
|
||||||
chrome.tabs.update({url}, resolve));
|
|
||||||
}
|
}
|
||||||
const options = {url, index, active};
|
const options = {url, index, active};
|
||||||
// FF57+ supports openerTabId, but not in Android (indicated by the absence of chrome.windows)
|
// 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) {
|
if (tab && (!FIREFOX || FIREFOX >= 57 && chrome.windows) && !chromeInIncognito) {
|
||||||
options.openerTabId = tab.id;
|
options.openerTabId = tab.id;
|
||||||
}
|
}
|
||||||
return new Promise(resolve =>
|
return createTab(options);
|
||||||
chrome.tabs.create(options, resolve));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function activateTab(tab) {
|
function activateTab(tab, {url, index}) {
|
||||||
|
const options = {active: true};
|
||||||
|
if (url) {
|
||||||
|
options.url = url;
|
||||||
|
}
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
new Promise(resolve => {
|
updateTab(tab.id, options),
|
||||||
chrome.tabs.update(tab.id, {active: true}, resolve);
|
updateWindow(tab.windowId, {focused: true}),
|
||||||
}),
|
index != null && moveTabs(tab.id, {index})
|
||||||
chrome.windows && new Promise(resolve => {
|
]);
|
||||||
chrome.windows.update(tab.windowId, {focused: true}, resolve);
|
|
||||||
}),
|
|
||||||
]).then(([tab]) => tab);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
|
|
||||||
<script src="js/polyfill.js"></script>
|
<script src="js/polyfill.js"></script>
|
||||||
<script src="js/dom.js"></script>
|
<script src="js/dom.js"></script>
|
||||||
<script src="js/messaging.js"></script>
|
|
||||||
<script src="js/promisify.js"></script>
|
<script src="js/promisify.js"></script>
|
||||||
|
<script src="js/messaging.js"></script>
|
||||||
<script src="js/msg.js"></script>
|
<script src="js/msg.js"></script>
|
||||||
<script src="js/localization.js"></script>
|
<script src="js/localization.js"></script>
|
||||||
<script src="js/prefs.js"></script>
|
<script src="js/prefs.js"></script>
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body id="stylus-options">
|
<body id="stylus-options">
|
||||||
|
|
||||||
<div id="options-header">
|
<div id="options-header">
|
||||||
<div id="options-title">
|
<div id="options-title">
|
||||||
<div id="options-close-icon"><svg viewBox="0 0 20 20" class="svg-icon"><path d="M11.69,10l4.55,4.55-1.69,1.69L10,11.69,5.45,16.23,3.77,14.55,8.31,10,3.77,5.45,5.45,3.77,10,8.31l4.55-4.55,1.69,1.69Z"></path></svg></div>
|
<div id="options-close-icon"><svg viewBox="0 0 20 20" class="svg-icon"><path d="M11.69,10l4.55,4.55-1.69,1.69L10,11.69,5.45,16.23,3.77,14.55,8.31,10,3.77,5.45,5.45,3.77,10,8.31l4.55-4.55,1.69,1.69Z"></path></svg></div>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* global configDialog hotkeys onTabReady msg
|
/* global configDialog hotkeys onTabReady msg
|
||||||
getActiveTab FIREFOX getTabRealURL URLS API onDOMready $ $$ prefs CHROME
|
getActiveTab FIREFOX getTabRealURL URLS API onDOMready $ $$ prefs
|
||||||
setupLivePrefs template t $create animateElement
|
setupLivePrefs template t $create animateElement
|
||||||
tryJSONparse debounce CHROME_HAS_BORDER_BUG */
|
tryJSONparse debounce CHROME_HAS_BORDER_BUG */
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ function initPopup() {
|
||||||
? new URL(tabURL).pathname.slice(1)
|
? new URL(tabURL).pathname.slice(1)
|
||||||
// this URL
|
// this URL
|
||||||
: t('writeStyleForURL').replace(/ /g, '\u00a0'),
|
: t('writeStyleForURL').replace(/ /g, '\u00a0'),
|
||||||
onclick: handleEvent.openLink,
|
onclick: e => handleEvent.openEditor(e, {'url-prefix': tabURL}),
|
||||||
});
|
});
|
||||||
if (prefs.get('popup.breadcrumbs')) {
|
if (prefs.get('popup.breadcrumbs')) {
|
||||||
urlLink.onmouseenter =
|
urlLink.onmouseenter =
|
||||||
|
@ -210,7 +210,7 @@ function initPopup() {
|
||||||
href: 'edit.html?domain=' + encodeURIComponent(domain),
|
href: 'edit.html?domain=' + encodeURIComponent(domain),
|
||||||
textContent: numParts > 2 ? domain.split('.')[0] : domain,
|
textContent: numParts > 2 ? domain.split('.')[0] : domain,
|
||||||
title: `domain("${domain}")`,
|
title: `domain("${domain}")`,
|
||||||
onclick: handleEvent.openLink,
|
onclick: e => handleEvent.openEditor(e, {domain}),
|
||||||
});
|
});
|
||||||
domainLink.setAttribute('subdomain', numParts > 1 ? 'true' : '');
|
domainLink.setAttribute('subdomain', numParts > 1 ? 'true' : '');
|
||||||
matchTargets.appendChild(domainLink);
|
matchTargets.appendChild(domainLink);
|
||||||
|
@ -296,7 +296,7 @@ function createStyleElement(style) {
|
||||||
const editLink = $('.style-edit-link', entry);
|
const editLink = $('.style-edit-link', entry);
|
||||||
Object.assign(editLink, {
|
Object.assign(editLink, {
|
||||||
href: editLink.getAttribute('href') + style.id,
|
href: editLink.getAttribute('href') + style.id,
|
||||||
onclick: handleEvent.openLink,
|
onclick: e => handleEvent.openEditor(e, {id: style.id}),
|
||||||
});
|
});
|
||||||
const styleName = $('.style-name', entry);
|
const styleName = $('.style-name', entry);
|
||||||
Object.assign(styleName, {
|
Object.assign(styleName, {
|
||||||
|
@ -535,9 +535,9 @@ Object.assign(handleEvent, {
|
||||||
$('#regexp-explanation').remove();
|
$('#regexp-explanation').remove();
|
||||||
},
|
},
|
||||||
|
|
||||||
openLink(event) {
|
openEditor(event, options) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
API.openURL({url: this.href});
|
API.openEditor(options);
|
||||||
if (!(FIREFOX && prefs.get('openEditInWindow'))) window.close();
|
if (!(FIREFOX && prefs.get('openEditInWindow'))) window.close();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user