fdbfb23547
* parserlib: fast section extraction, tweaks and speedups
* csslint: "simple-not" rule
* csslint: enable and fix "selector-newline" rule
* simplify db: resolve with result
* simplify download()
* remove noCode param as it wastes more time/memory on copying
* styleManager: switch style<->data names to reflect their actual contents
* inline method bodies to avoid indirection and enable better autocomplete/hint/jump support in IDE
* upgrade getEventKeyName to handle mouse clicks
* don't trust location.href as it hides text fragment
* getAllKeys is implemented since Chrome48, FF44
* allow recoverable css errors + async'ify usercss.js
* openManage: unminimize windows
* remove the obsolete Chrome pre-65 workaround
* fix temporal dead zone in apply.js
* ff bug workaround for simple editor window
* consistent window scrolling in scrollToEditor and jumpToPos
* rework waitForSelector and collapsible <details>
* blank paint frame workaround for new Chrome
* extract stuff from edit.js and load on demand
* simplify regexpTester::isShown
* move MozDocMapper to sections-util.js
* extract fitSelectBox()
* initialize router earlier
* use helpPopup.close()
* fix autofocus in popups, follow-up to 5bb1b5ef
* clone objects in prefs.get() + cosmetics
* reuse getAll result for INC
182 lines
5.1 KiB
JavaScript
182 lines
5.1 KiB
JavaScript
/* global API msg */// msg.js
|
|
/* global addAPI bgReady */// common.js
|
|
/* global createWorker */// worker-util.js
|
|
/* global prefs */
|
|
/* global styleMan */
|
|
/* global syncMan */
|
|
/* global updateMan */
|
|
/* global usercssMan */
|
|
/* global
|
|
FIREFOX
|
|
URLS
|
|
activateTab
|
|
download
|
|
findExistingTab
|
|
getActiveTab
|
|
isTabReplaceable
|
|
openURL
|
|
*/ // toolbox.js
|
|
'use strict';
|
|
|
|
//#region API
|
|
|
|
addAPI(/** @namespace API */ {
|
|
|
|
styles: styleMan,
|
|
sync: syncMan,
|
|
updater: updateMan,
|
|
usercss: usercssMan,
|
|
/** @type {BackgroundWorker} */
|
|
worker: createWorker({url: '/background/background-worker'}),
|
|
|
|
download(url, opts) {
|
|
return typeof url === 'string' && url.startsWith(URLS.uso) &&
|
|
this.sender.url.startsWith(URLS.uso) &&
|
|
download(url, opts || {});
|
|
},
|
|
|
|
/** @returns {string} */
|
|
getTabUrlPrefix() {
|
|
return this.sender.tab.url.match(/^([\w-]+:\/+[^/#]+)/)[1];
|
|
},
|
|
|
|
/**
|
|
* Opens the editor or activates an existing tab
|
|
* @param {{
|
|
id?: number
|
|
domain?: string
|
|
'url-prefix'?: string
|
|
}} params
|
|
* @returns {Promise<chrome.tabs.Tab>}
|
|
*/
|
|
async openEditor(params) {
|
|
const u = new URL(chrome.runtime.getURL('edit.html'));
|
|
u.search = new URLSearchParams(params);
|
|
const wnd = prefs.get('openEditInWindow');
|
|
const wndPos = wnd && prefs.get('windowPosition');
|
|
const wndBase = wnd && prefs.get('openEditInWindow.popup') ? {type: 'popup'} : {};
|
|
const ffBug = wnd && FIREFOX; // https://bugzil.la/1271047
|
|
const tab = await openURL({
|
|
url: `${u}`,
|
|
currentWindow: null,
|
|
newWindow: Object.assign(wndBase, !ffBug && wndPos),
|
|
});
|
|
if (ffBug) await browser.windows.update(tab.windowId, wndPos);
|
|
return tab;
|
|
},
|
|
|
|
/** @returns {Promise<chrome.tabs.Tab>} */
|
|
async openManage({options = false, search, searchMode} = {}) {
|
|
let url = chrome.runtime.getURL('manage.html');
|
|
if (search) {
|
|
url += `?search=${encodeURIComponent(search)}&searchMode=${searchMode}`;
|
|
}
|
|
if (options) {
|
|
url += '#stylus-options';
|
|
}
|
|
let tab = await findExistingTab({
|
|
url,
|
|
currentWindow: null,
|
|
ignoreHash: true,
|
|
ignoreSearch: true,
|
|
});
|
|
if (tab) {
|
|
await activateTab(tab);
|
|
if (url !== (tab.pendingUrl || tab.url)) {
|
|
await msg.sendTab(tab.id, {method: 'pushState', url}).catch(console.error);
|
|
}
|
|
return tab;
|
|
}
|
|
tab = await getActiveTab();
|
|
return isTabReplaceable(tab, url)
|
|
? activateTab(tab, {url})
|
|
: browser.tabs.create({url}).then(activateTab); // activateTab unminimizes the window
|
|
},
|
|
|
|
/**
|
|
* Same as openURL, the only extra prop in `opts` is `message` - it'll be sent
|
|
* when the tab is ready, which is needed in the popup, otherwise another
|
|
* extension could force the tab to open in foreground thus auto-closing the
|
|
* popup (in Chrome at least) and preventing the sendMessage code from running
|
|
* @returns {Promise<chrome.tabs.Tab>}
|
|
*/
|
|
async openURL(opts) {
|
|
const tab = await openURL(opts);
|
|
if (opts.message) {
|
|
await onTabReady(tab);
|
|
await msg.sendTab(tab.id, opts.message);
|
|
}
|
|
return tab;
|
|
function onTabReady(tab) {
|
|
return new Promise((resolve, reject) =>
|
|
setTimeout(function ping(numTries = 10, delay = 100) {
|
|
msg.sendTab(tab.id, {method: 'ping'})
|
|
.catch(() => false)
|
|
.then(pong => pong
|
|
? resolve(tab)
|
|
: numTries && setTimeout(ping, delay, numTries - 1, delay * 1.5) ||
|
|
reject('timeout'));
|
|
}));
|
|
}
|
|
},
|
|
|
|
prefs: {
|
|
getValues: () => prefs.__values, // will be deepCopy'd by apiHandler
|
|
set: prefs.set,
|
|
},
|
|
});
|
|
|
|
//#endregion
|
|
//#region Events
|
|
|
|
const browserCommands = {
|
|
openManage: () => API.openManage(),
|
|
openOptions: () => API.openManage({options: true}),
|
|
reload: () => chrome.runtime.reload(),
|
|
styleDisableAll(info) {
|
|
prefs.set('disableAll', info ? info.checked : !prefs.get('disableAll'));
|
|
},
|
|
};
|
|
|
|
if (chrome.commands) {
|
|
chrome.commands.onCommand.addListener(id => browserCommands[id]());
|
|
}
|
|
|
|
chrome.runtime.onInstalled.addListener(({reason, previousVersion}) => {
|
|
if (reason === 'update') {
|
|
const [a, b, c] = (previousVersion || '').split('.');
|
|
if (a <= 1 && b <= 5 && c <= 13) { // 1.5.13
|
|
require(['/background/remove-unused-storage']);
|
|
}
|
|
}
|
|
});
|
|
|
|
msg.on((msg, sender) => {
|
|
if (msg.method === 'invokeAPI') {
|
|
let res = msg.path.reduce((res, name) => res && res[name], API);
|
|
if (!res) throw new Error(`Unknown API.${msg.path.join('.')}`);
|
|
res = res.apply({msg, sender}, msg.args);
|
|
return res === undefined ? null : res;
|
|
}
|
|
});
|
|
|
|
//#endregion
|
|
|
|
Promise.all([
|
|
bgReady.styles,
|
|
/* These are loaded conditionally.
|
|
Each item uses `require` individually so IDE can jump to the source and track usage. */
|
|
FIREFOX &&
|
|
require(['/background/style-via-api']),
|
|
FIREFOX && ((browser.commands || {}).update) &&
|
|
require(['/background/browser-cmd-hotkeys']),
|
|
!FIREFOX &&
|
|
require(['/background/content-scripts']),
|
|
chrome.contextMenus &&
|
|
require(['/background/context-menus']),
|
|
]).then(() => {
|
|
bgReady._resolveAll();
|
|
msg.isBgReady = true;
|
|
msg.broadcast({method: 'backgroundReady'});
|
|
});
|