Add: navigator-util
This commit is contained in:
parent
b5107b78a5
commit
5b3b4e680f
|
@ -38,13 +38,14 @@ window.API_METHODS = Object.assign(window.API_METHODS || {}, {
|
|||
// in the foreground thus auto-closing the popup (in Chrome)
|
||||
openURL,
|
||||
|
||||
// FIXME: who use this?
|
||||
closeTab: (msg, sender, respond) => {
|
||||
chrome.tabs.remove(msg.tabId || sender.tab.id, () => {
|
||||
if (chrome.runtime.lastError && msg.tabId !== sender.tab.id) {
|
||||
respond(new Error(chrome.runtime.lastError.message));
|
||||
}
|
||||
});
|
||||
return KEEP_CHANNEL_OPEN;
|
||||
return true;
|
||||
},
|
||||
|
||||
optionsCustomizeHotkeys() {
|
||||
|
@ -71,6 +72,7 @@ if (FIREFOX) {
|
|||
const frameId = port.sender.frameId;
|
||||
const options = tryJSONparse(port.name.slice(MSG_GET_STYLES_LEN));
|
||||
port.disconnect();
|
||||
// FIXME: getStylesFallback?
|
||||
getStyles(options).then(styles => {
|
||||
if (!styles.length) return;
|
||||
chrome.tabs.executeScript(tabId, {
|
||||
|
@ -87,39 +89,28 @@ if (FIREFOX) {
|
|||
});
|
||||
}
|
||||
|
||||
{
|
||||
const listener =
|
||||
URLS.chromeProtectsNTP
|
||||
? webNavigationListenerChrome
|
||||
: webNavigationListener;
|
||||
|
||||
chrome.webNavigation.onBeforeNavigate.addListener(data =>
|
||||
listener(null, data));
|
||||
|
||||
chrome.webNavigation.onCommitted.addListener(data =>
|
||||
listener('styleApply', data));
|
||||
|
||||
chrome.webNavigation.onHistoryStateUpdated.addListener(data =>
|
||||
listener('styleReplaceAll', data));
|
||||
|
||||
chrome.webNavigation.onReferenceFragmentUpdated.addListener(data =>
|
||||
listener('styleReplaceAll', data));
|
||||
|
||||
if (FIREFOX) {
|
||||
// FF applies page CSP even to content scripts, https://bugzil.la/1267027
|
||||
chrome.webNavigation.onCommitted.addListener(webNavUsercssInstallerFF, {
|
||||
url: [
|
||||
{hostSuffix: '.githubusercontent.com', urlSuffix: '.user.css'},
|
||||
{hostSuffix: '.githubusercontent.com', urlSuffix: '.user.styl'},
|
||||
]
|
||||
});
|
||||
// FF misses some about:blank iframes so we inject our content script explicitly
|
||||
chrome.webNavigation.onDOMContentLoaded.addListener(webNavIframeHelperFF, {
|
||||
url: [
|
||||
{urlEquals: 'about:blank'},
|
||||
]
|
||||
});
|
||||
navigatorUtil.onUrlChange(({tabId, frameId}, type) => {
|
||||
if (type === 'committed') {
|
||||
// styles would be updated when content script is injected.
|
||||
return;
|
||||
}
|
||||
msg.sendTab(tabId, {method: 'urlChanged'}, {frameId});
|
||||
});
|
||||
|
||||
if (FIREFOX) {
|
||||
// FF applies page CSP even to content scripts, https://bugzil.la/1267027
|
||||
navigatorUtil.onCommitted(webNavUsercssInstallerFF, {
|
||||
url: [
|
||||
{hostSuffix: '.githubusercontent.com', urlSuffix: '.user.css'},
|
||||
{hostSuffix: '.githubusercontent.com', urlSuffix: '.user.styl'},
|
||||
]
|
||||
});
|
||||
// FF misses some about:blank iframes so we inject our content script explicitly
|
||||
navigatorUtil.onDOMContentLoaded(webNavIframeHelperFF, {
|
||||
url: [
|
||||
{urlEquals: 'about:blank'},
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
if (chrome.contextMenus) {
|
||||
|
@ -149,6 +140,8 @@ prefs.subscribe(['iconset'], () =>
|
|||
styles: {},
|
||||
}));
|
||||
|
||||
chrome.navigator.
|
||||
|
||||
// *************************************************************************
|
||||
chrome.runtime.onInstalled.addListener(({reason}) => {
|
||||
if (reason !== 'update') return;
|
||||
|
@ -297,74 +290,7 @@ window.addEventListener('storageReady', function _() {
|
|||
}));
|
||||
});
|
||||
|
||||
// *************************************************************************
|
||||
{
|
||||
const getStylesForFrame = (msg, sender) => {
|
||||
const stylesTask = getStyles(msg);
|
||||
if (!sender || !sender.frameId) return stylesTask;
|
||||
return Promise.all([
|
||||
stylesTask,
|
||||
getTab(sender.tab.id),
|
||||
]).then(([styles, tab]) => {
|
||||
if (tab) styles.exposeIframes = tab.url.replace(/(\/\/[^/]*).*/, '$1');
|
||||
return styles;
|
||||
});
|
||||
};
|
||||
const updateAPI = (_, enabled) => {
|
||||
window.API_METHODS.getStylesForFrame = enabled ? getStylesForFrame : getStyles;
|
||||
};
|
||||
prefs.subscribe(['exposeIframes'], updateAPI);
|
||||
updateAPI(null, prefs.readOnlyValues.exposeIframes);
|
||||
}
|
||||
|
||||
// *************************************************************************
|
||||
|
||||
function webNavigationListener(method, {url, tabId, frameId}) {
|
||||
Promise.all([
|
||||
getStyles({matchUrl: url, asHash: true}),
|
||||
frameId && prefs.readOnlyValues.exposeIframes && getTab(tabId),
|
||||
]).then(([styles, tab]) => {
|
||||
if (method && URLS.supported(url) && tabId >= 0) {
|
||||
if (method === 'styleApply') {
|
||||
handleCssTransitionBug({tabId, frameId, url, styles});
|
||||
}
|
||||
if (tab) styles.exposeIframes = tab.url.replace(/(\/\/[^/]*).*/, '$1');
|
||||
msg.sendTab(
|
||||
tabId,
|
||||
{
|
||||
method,
|
||||
// ping own page so it retrieves the styles directly
|
||||
styles: url.startsWith(URLS.ownOrigin) ? 'DIY' : styles,
|
||||
},
|
||||
{frameId}
|
||||
);
|
||||
}
|
||||
// main page frame id is 0
|
||||
if (frameId === 0) {
|
||||
tabIcons.delete(tabId);
|
||||
updateIcon({tab: {id: tabId, url}, styles});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function webNavigationListenerChrome(method, data) {
|
||||
// Chrome 61.0.3161+ doesn't run content scripts on NTP
|
||||
if (
|
||||
!data.url.startsWith('https://www.google.') ||
|
||||
!data.url.includes('/_/chrome/newtab?')
|
||||
) {
|
||||
webNavigationListener(method, data);
|
||||
return;
|
||||
}
|
||||
getTab(data.tabId).then(tab => {
|
||||
if (tab.url === 'chrome://newtab/') {
|
||||
data.url = tab.url;
|
||||
}
|
||||
webNavigationListener(method, data);
|
||||
});
|
||||
}
|
||||
|
||||
// FIXME: implement exposeIframes in apply.js
|
||||
|
||||
function webNavUsercssInstallerFF(data) {
|
||||
const {tabId} = data;
|
||||
|
|
68
background/navigator-util.js
Normal file
68
background/navigator-util.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
'use strict';
|
||||
|
||||
const navigatorUtil = (() => {
|
||||
const handler = {
|
||||
urlChange: null
|
||||
};
|
||||
let listeners;
|
||||
const tabGet = promisify(chrome.tabs.get.bind(chrome.tabs));
|
||||
return extendNative({onUrlChange});
|
||||
|
||||
function onUrlChange(fn) {
|
||||
initUrlChange();
|
||||
handler.urlChange.push(fn);
|
||||
}
|
||||
|
||||
function initUrlChange() {
|
||||
if (!handler.urlChange) {
|
||||
return;
|
||||
}
|
||||
handler.urlChange = [];
|
||||
|
||||
chrome.webNavigation.onCommitted.addListener(data =>
|
||||
fixNTPUrl(data)
|
||||
.then(() => executeCallbacks(handler.urlChange, data, 'committed'));
|
||||
|
||||
chrome.webNavigation.onHistoryStateUpdated.addListener(data =>
|
||||
fixNTPUrl(data)
|
||||
.then(() => executeCallbacks(handler.urlChange, data, 'historyStateUpdated'));
|
||||
|
||||
chrome.webNavigation.onReferenceFragmentUpdated.addListener(data =>
|
||||
fixNTPUrl(data)
|
||||
.then(() => executeCallbacks(handler.urlChange, data, 'referenceFragmentUpdated'));
|
||||
}
|
||||
|
||||
function fixNTPUrl(data) {
|
||||
if (
|
||||
!CHROME ||
|
||||
!URLS.chromeProtectsNTP ||
|
||||
!data.url.startsWith('https://www.google.') ||
|
||||
!data.url.includes('/_/chrome/newtab?')
|
||||
) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return tabGet(data.tabId)
|
||||
.then(tab => {
|
||||
if (tab.url === 'chrome://newtab/') {
|
||||
data.url = tab.url;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function executeCallbacks(callbacks, data, type) {
|
||||
for (const cb of callbacks) {
|
||||
cb(data, type);
|
||||
}
|
||||
}
|
||||
|
||||
function extendNative(target) {
|
||||
return new Proxy(target, {
|
||||
get: (target, prop) => {
|
||||
if (target[prop]) {
|
||||
return target[prop];
|
||||
}
|
||||
return chrome.webNavigation[prop].addListener.bind(chrome.webNavigation[prop]);
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
|
@ -1,226 +0,0 @@
|
|||
/*
|
||||
global API_METHODS cachedStyles
|
||||
global getStyles filterStyles invalidateCache normalizeStyleSections
|
||||
global updateIcon
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
(() => {
|
||||
const previewFromTabs = new Map();
|
||||
|
||||
/**
|
||||
* When style id and state is provided, only that style is propagated.
|
||||
* Otherwise all styles are replaced and the toolbar icon is updated.
|
||||
* @param {Object} [msg]
|
||||
* @param {{id:Number, enabled?:Boolean, sections?: (Array|String)}} [msg.style] -
|
||||
* style to propagate
|
||||
* @param {Boolean} [msg.codeIsUpdated]
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
API_METHODS.refreshAllTabs = (msg = {}) =>
|
||||
Promise.all([
|
||||
queryTabs(),
|
||||
maybeParseUsercss(msg),
|
||||
getStyles(),
|
||||
]).then(([tabs, style]) =>
|
||||
new Promise(resolve => {
|
||||
if (style) msg.style.sections = normalizeStyleSections(style);
|
||||
run(tabs, msg, resolve);
|
||||
}));
|
||||
|
||||
|
||||
function run(tabs, msg, resolve) {
|
||||
const {style, codeIsUpdated, refreshOwnTabs} = msg;
|
||||
|
||||
// the style was updated/saved so we need to remove the old copy of the original style
|
||||
if (msg.method === 'styleUpdated' && msg.reason !== 'editPreview') {
|
||||
for (const [tabId, original] of previewFromTabs.entries()) {
|
||||
if (style.id === original.id) {
|
||||
previewFromTabs.delete(tabId);
|
||||
}
|
||||
}
|
||||
if (!previewFromTabs.size) {
|
||||
unregisterTabListeners();
|
||||
}
|
||||
}
|
||||
|
||||
if (!style) {
|
||||
msg = {method: 'styleReplaceAll'};
|
||||
|
||||
// live preview puts the code in cachedStyles, saves the original in previewFromTabs,
|
||||
// and if preview is being disabled, but the style is already deleted, we bail out
|
||||
} else if (msg.reason === 'editPreview' && !updateCache(msg)) {
|
||||
return;
|
||||
|
||||
// simple style update:
|
||||
// * if disabled, apply.js will remove the element
|
||||
// * if toggled and code is unchanged, apply.js will toggle the element
|
||||
} else if (!style.enabled || codeIsUpdated === false) {
|
||||
msg = {
|
||||
method: 'styleUpdated',
|
||||
reason: msg.reason,
|
||||
style: {
|
||||
id: style.id,
|
||||
enabled: style.enabled,
|
||||
},
|
||||
codeIsUpdated,
|
||||
};
|
||||
|
||||
// live preview normal operation, the new code is already in cachedStyles
|
||||
} else {
|
||||
msg.method = 'styleApply';
|
||||
msg.style = {id: msg.style.id};
|
||||
}
|
||||
|
||||
if (!tabs || !tabs.length) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
const last = tabs[tabs.length - 1];
|
||||
for (const tab of tabs) {
|
||||
if (FIREFOX && !tab.width) continue;
|
||||
if (refreshOwnTabs === false && tab.url.startsWith(URLS.ownOrigin)) continue;
|
||||
chrome.webNavigation.getAllFrames({tabId: tab.id}, frames =>
|
||||
refreshFrame(tab, frames, msg, tab === last && resolve));
|
||||
}
|
||||
}
|
||||
|
||||
function refreshFrame(tab, frames, msg, resolve) {
|
||||
ignoreChromeError();
|
||||
if (!frames || !frames.length) {
|
||||
frames = [{
|
||||
frameId: 0,
|
||||
url: tab.url,
|
||||
}];
|
||||
}
|
||||
msg.tabId = tab.id;
|
||||
const styleId = msg.style && msg.style.id;
|
||||
|
||||
for (const frame of frames) {
|
||||
|
||||
const styles = filterStyles({
|
||||
matchUrl: getFrameUrl(frame, frames),
|
||||
asHash: true,
|
||||
id: styleId,
|
||||
});
|
||||
|
||||
msg = Object.assign({}, msg);
|
||||
msg.frameId = frame.frameId;
|
||||
|
||||
if (msg.method !== 'styleUpdated') {
|
||||
msg.styles = styles;
|
||||
}
|
||||
|
||||
if (msg.method === 'styleApply' && !styles.length) {
|
||||
// remove the style from a previously matching frame
|
||||
invokeOrPostpone(tab.active, sendMessage, {
|
||||
method: 'styleUpdated',
|
||||
reason: 'editPreview',
|
||||
style: {
|
||||
id: styleId,
|
||||
enabled: false,
|
||||
},
|
||||
tabId: tab.id,
|
||||
frameId: frame.frameId,
|
||||
}, ignoreChromeError);
|
||||
} else {
|
||||
invokeOrPostpone(tab.active, sendMessage, msg, ignoreChromeError);
|
||||
}
|
||||
|
||||
if (!frame.frameId) {
|
||||
setTimeout(updateIcon, 0, {
|
||||
tab,
|
||||
styles: msg.method === 'styleReplaceAll' ? styles : undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (resolve) resolve();
|
||||
}
|
||||
|
||||
|
||||
function getFrameUrl(frame, frames) {
|
||||
while (frame.url === 'about:blank' && frame.frameId > 0) {
|
||||
const parent = frames.find(f => f.frameId === frame.parentFrameId);
|
||||
if (!parent) break;
|
||||
frame.url = parent.url;
|
||||
frame = parent;
|
||||
}
|
||||
return (frame || frames[0]).url;
|
||||
}
|
||||
|
||||
|
||||
function maybeParseUsercss({style}) {
|
||||
if (style && typeof style.sections === 'string') {
|
||||
return API_METHODS.parseUsercss({sourceCode: style.sections});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function updateCache(msg) {
|
||||
const {style, tabId, restoring} = msg;
|
||||
const spoofed = !restoring && previewFromTabs.get(tabId);
|
||||
const original = cachedStyles.byId.get(style.id);
|
||||
|
||||
if (style.sections && !restoring) {
|
||||
if (!previewFromTabs.size) {
|
||||
registerTabListeners();
|
||||
}
|
||||
if (!spoofed) {
|
||||
previewFromTabs.set(tabId, Object.assign({}, original));
|
||||
}
|
||||
|
||||
} else {
|
||||
previewFromTabs.delete(tabId);
|
||||
if (!previewFromTabs.size) {
|
||||
unregisterTabListeners();
|
||||
}
|
||||
if (!original) {
|
||||
return;
|
||||
}
|
||||
if (!restoring) {
|
||||
msg.style = spoofed || original;
|
||||
}
|
||||
}
|
||||
invalidateCache({updated: msg.style});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
function registerTabListeners() {
|
||||
chrome.tabs.onRemoved.addListener(onTabRemoved);
|
||||
chrome.tabs.onReplaced.addListener(onTabReplaced);
|
||||
chrome.webNavigation.onCommitted.addListener(onTabNavigated);
|
||||
}
|
||||
|
||||
|
||||
function unregisterTabListeners() {
|
||||
chrome.tabs.onRemoved.removeListener(onTabRemoved);
|
||||
chrome.tabs.onReplaced.removeListener(onTabReplaced);
|
||||
chrome.webNavigation.onCommitted.removeListener(onTabNavigated);
|
||||
}
|
||||
|
||||
|
||||
function onTabRemoved(tabId) {
|
||||
const style = previewFromTabs.get(tabId);
|
||||
if (style) {
|
||||
API_METHODS.refreshAllTabs({
|
||||
style,
|
||||
tabId,
|
||||
reason: 'editPreview',
|
||||
restoring: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function onTabReplaced(addedTabId, removedTabId) {
|
||||
onTabRemoved(removedTabId);
|
||||
}
|
||||
|
||||
|
||||
function onTabNavigated({tabId}) {
|
||||
onTabRemoved(tabId);
|
||||
}
|
||||
})();
|
|
@ -154,11 +154,12 @@ const styleManager = (() => {
|
|||
}
|
||||
|
||||
function ensurePrepared(methods) {
|
||||
for (const [name, fn] in Object.entries(methods)) {
|
||||
methods[name] = (...args) =>
|
||||
const prepared = {};
|
||||
for (const [name, fn] of Object.entries(methods)) {
|
||||
prepared[name] = (...args) =>
|
||||
preparing.then(() => fn(...args));
|
||||
}
|
||||
return methods;
|
||||
return prepared;
|
||||
}
|
||||
|
||||
function createNewStyle() {
|
||||
|
|
|
@ -38,16 +38,14 @@ API_METHODS.styleViaAPI = !CHROME && (() => {
|
|||
if (id === null && !ignoreUrlCheck && frameStyles.url === url) {
|
||||
return NOP;
|
||||
}
|
||||
return getStyles({id, matchUrl: url, asHash: true}).then(styles => {
|
||||
const filter = {enabled: true};
|
||||
if (id !== null) {
|
||||
filter.id = id;
|
||||
}
|
||||
return styleManager.getSectionsByUrl(url, filter).then(sections => {
|
||||
const tasks = [];
|
||||
for (const styleId in styles) {
|
||||
if (isNaN(parseInt(styleId))) {
|
||||
continue;
|
||||
}
|
||||
// shallow-extract code from the sections array in order to reuse references
|
||||
// in other places whereas the combined string gets garbage-collected
|
||||
const styleSections = styles[styleId].map(section => section.code);
|
||||
const code = styleSections.join('\n');
|
||||
for (const section of Object.values(sections)) {
|
||||
const code = section.code;
|
||||
if (!code) {
|
||||
delete frameStyles[styleId];
|
||||
continue;
|
||||
|
@ -55,7 +53,7 @@ API_METHODS.styleViaAPI = !CHROME && (() => {
|
|||
if (code === (frameStyles[styleId] || []).join('\n')) {
|
||||
continue;
|
||||
}
|
||||
frameStyles[styleId] = styleSections;
|
||||
frameStyles[styleId] = [code];
|
||||
tasks.push(
|
||||
browser.tabs.insertCSS(tab.id, {
|
||||
code,
|
||||
|
|
|
@ -51,7 +51,7 @@ global API_METHODS
|
|||
checkingAll = true;
|
||||
retrying.clear();
|
||||
const port = observe && chrome.runtime.connect({name: 'updater'});
|
||||
return getStyles({}).then(styles => {
|
||||
return styleManager.getAllStyles().then(styles => {
|
||||
styles = styles.filter(style => style.updateUrl);
|
||||
if (port) port.postMessage({count: styles.length});
|
||||
log('');
|
||||
|
|
|
@ -147,6 +147,10 @@
|
|||
}
|
||||
break;
|
||||
|
||||
case 'urlChanged':
|
||||
// TODO
|
||||
break;
|
||||
|
||||
case 'ping':
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -5,9 +5,6 @@ global onRuntimeMessage applyOnMessage
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
// keep message channel open for sendResponse in chrome.runtime.onMessage listener
|
||||
const KEEP_CHANNEL_OPEN = true;
|
||||
|
||||
const CHROME = Boolean(chrome.app) && parseInt(navigator.userAgent.match(/Chrom\w+\/(?:\d+\.){2}(\d+)|$/)[1]);
|
||||
const OPERA = Boolean(chrome.app) && parseFloat(navigator.userAgent.match(/\bOPR\/(\d+\.\d+)|$/)[1]);
|
||||
const VIVALDI = Boolean(chrome.app) && navigator.userAgent.includes('Vivaldi');
|
||||
|
@ -72,14 +69,9 @@ const URLS = {
|
|||
),
|
||||
};
|
||||
|
||||
let BG = chrome.extension.getBackgroundPage();
|
||||
if (BG && !BG.getStyles && BG !== window) {
|
||||
// own page like editor/manage is being loaded on browser startup
|
||||
// before the background page has been fully initialized;
|
||||
// it'll be resolved in onBackgroundReady() instead
|
||||
BG = null;
|
||||
}
|
||||
if (!BG || BG !== window) {
|
||||
const IS_BG = chrome.extension.getBackgroundPage && chrome.extension.getBackgroundPage() === window;
|
||||
|
||||
if (!IS_BG) {
|
||||
if (FIREFOX) {
|
||||
document.documentElement.classList.add('firefox');
|
||||
} else if (OPERA) {
|
||||
|
@ -93,8 +85,10 @@ if (!BG || BG !== window) {
|
|||
getActiveTab().then(tab =>
|
||||
window.API.updateIcon({tab}));
|
||||
}
|
||||
} else if (!BG.API_METHODS) {
|
||||
BG.API_METHODS = {};
|
||||
}
|
||||
|
||||
if (IS_BG) {
|
||||
window.API_METHODS = {};
|
||||
}
|
||||
|
||||
const FIREFOX_NO_DOM_STORAGE = FIREFOX && !tryCatch(() => localStorage);
|
||||
|
@ -104,32 +98,6 @@ if (FIREFOX_NO_DOM_STORAGE) {
|
|||
Object.defineProperty(window, 'sessionStorage', {value: {}});
|
||||
}
|
||||
|
||||
function sendMessage(msg, callback) {
|
||||
/*
|
||||
Promise mode [default]:
|
||||
- rejects on receiving {__ERROR__: message} created by background.js::onRuntimeMessage
|
||||
- automatically suppresses chrome.runtime.lastError because it's autogenerated
|
||||
by browserAction.setText which lacks a callback param in chrome API
|
||||
Standard callback mode:
|
||||
- enabled by passing a second param
|
||||
*/
|
||||
const {tabId, frameId} = msg;
|
||||
const fn = tabId >= 0 ? chrome.tabs.sendMessage : chrome.runtime.sendMessage;
|
||||
const args = tabId >= 0 ? [tabId, msg, {frameId}] : [msg];
|
||||
if (callback) {
|
||||
fn(...args, callback);
|
||||
} else {
|
||||
return new Promise((resolve, reject) => {
|
||||
fn(...args, r => {
|
||||
const err = r && r.__ERROR__;
|
||||
(err ? reject : resolve)(err || r);
|
||||
ignoreChromeError();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function queryTabs(options = {}) {
|
||||
return new Promise(resolve =>
|
||||
chrome.tabs.query(options, tabs =>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
// eslint-disable-next-line no-var
|
||||
var prefs = new function Prefs() {
|
||||
const BG = undefined;
|
||||
const defaults = {
|
||||
'openEditInWindow': false, // new editor opens in a own browser window
|
||||
'windowPosition': {}, // detached window position
|
||||
|
|
|
@ -36,12 +36,12 @@
|
|||
"js/cache.js",
|
||||
"background/db.js",
|
||||
"background/style-manager.js",
|
||||
"background/navigator-util.js",
|
||||
"background/background.js",
|
||||
"background/usercss-helper.js",
|
||||
"background/style-via-api.js",
|
||||
"background/search-db.js",
|
||||
"background/update.js",
|
||||
"background/refresh-all-tabs.js",
|
||||
"background/openusercss-api.js",
|
||||
"vendor/semver-bundle/semver.js",
|
||||
"vendor-overwrites/colorpicker/colorconverter.js"
|
||||
|
|
Loading…
Reference in New Issue
Block a user