Refactor: pull out tab-manager and icon-manager

This commit is contained in:
eight04 2020-02-19 10:59:07 +08:00 committed by tophf
parent bef87f200e
commit 73f6e8a964
6 changed files with 173 additions and 132 deletions

View File

@ -1,7 +1,8 @@
/* global download prefs openURL FIREFOX CHROME VIVALDI
debounce URLS ignoreChromeError usercssHelper
styleManager msg navigatorUtil iconUtil workerUtil contentScripts sync
findExistingTab createTab activateTab isTabReplaceable getActiveTab */
/* global download prefs openURL FIREFOX CHROME
URLS ignoreChromeError usercssHelper
styleManager msg navigatorUtil workerUtil contentScripts sync
findExistingTab createTab activateTab isTabReplaceable getActiveTab
iconManager */
'use strict';
@ -49,14 +50,7 @@ window.API_METHODS = Object.assign(window.API_METHODS || {}, {
openEditor,
updateIconBadge(count) {
// TODO: remove once our manifest's minimum_chrome_version is 50+
// Chrome 49 doesn't report own extension pages in webNavigation apparently
// so we do a force update which doesn't use the cache.
if (CHROME && CHROME < 2661 && this.sender.tab.url.startsWith(URLS.ownOrigin)) {
updateIconBadgeForce(this.sender.tab.id, count);
} else {
updateIconBadge(this.sender.tab.id, count);
}
iconManager.updateIconBadge(this.sender.tab.id, count);
return true;
},
@ -87,6 +81,14 @@ var browserCommands, contextMenus;
// register all listeners
msg.on(onRuntimeMessage);
// tell apply.js to refresh styles for non-committed navigation
navigatorUtil.onUrlChange(({tabId, frameId}, type) => {
if (type !== 'committed') {
msg.sendTab(tabId, {method: 'urlChanged'}, {frameId})
.catch(msg.ignoreError);
}
});
if (FIREFOX) {
// FF misses some about:blank iframes so we inject our content script explicitly
navigatorUtil.onDOMContentLoaded(webNavIframeHelperFF, {
@ -106,55 +108,10 @@ if (chrome.commands) {
chrome.commands.onCommand.addListener(command => browserCommands[command]());
}
const tabData = new Map();
const tabDataFor = tabId => {
let data = tabData.get(tabId);
if (!data) tabData.set(tabId, (data = {}));
return data;
};
chrome.tabs.onRemoved.addListener(tabId => tabData.delete(tabId));
chrome.tabs.onReplaced.addListener((added, removed) => tabData.delete(removed));
prefs.subscribe([
'disableAll',
'badgeDisabled',
'badgeNormal',
], () => debounce(refreshIconBadgeColor));
prefs.subscribe([
'show-badge'
], () => debounce(refreshAllIconsBadgeText));
prefs.subscribe([
'disableAll',
'iconset',
], () => debounce(refreshAllIcons));
prefs.initializing.then(() => {
refreshIconBadgeColor();
refreshAllIconsBadgeText();
refreshAllIcons();
});
navigatorUtil.onUrlChange(({tabId, frameId, transitionQualifiers, url}, type) => {
if (type !== 'committed') {
msg.sendTab(tabId, {method: 'urlChanged'}, {frameId})
.catch(msg.ignoreError);
} else if (!frameId) {
if (usercssHelper.testUrl(url) && !`${tabDataFor(tabId).url}`.startsWith(URLS.installUsercss)) {
usercssHelper.openInstallerPage(tabId, url).then(newUrl => {
tabDataFor(tabId).url = newUrl || url;
});
}
// it seems that the tab icon would be reset by navigation. We
// invalidate the cache here so it would be refreshed by `apply.js`.
tabData.set(tabId, {url});
// however, if the tab was swapped in by forward/backward buttons,
// `apply.js` doesn't notify the background to update the icon,
// so we have to refresh it manually.
if (transitionQualifiers.includes('forward_back')) {
msg.sendTab(tabId, {method: 'updateCount'}).catch(msg.ignoreError);
}
// detect usercss and open the installer page
navigatorUtil.onCommitted(({tabId, frameId, url}) => {
if (!frameId && usercssHelper.testUrl(url)) {
usercssHelper.openInstallerPage(tabId, url);
}
});
@ -307,74 +264,6 @@ function webNavIframeHelperFF({tabId, frameId}) {
});
}
function updateIconBadge(tabId, count) {
const tabIcon = tabDataFor(tabId);
if (tabIcon.count === count) {
return;
}
const oldCount = tabIcon.count;
tabIcon.count = count;
refreshIconBadgeText(tabId, tabIcon);
if (Boolean(oldCount) !== Boolean(count)) {
refreshIcon(tabId, tabIcon);
}
}
function updateIconBadgeForce(tabId, count) {
refreshIconBadgeText(tabId, {count});
refreshIcon(tabId, {count});
}
function refreshIconBadgeText(tabId, icon) {
iconUtil.setBadgeText({
text: prefs.get('show-badge') && icon.count ? String(icon.count) : '',
tabId
});
}
function refreshIcon(tabId, icon) {
const disableAll = prefs.get('disableAll');
const iconset = prefs.get('iconset') === 1 ? 'light/' : '';
const postfix = disableAll ? 'x' : !icon.count ? 'w' : '';
const iconType = iconset + postfix;
if (icon.iconType === iconType) {
return;
}
icon.iconType = iconset + postfix;
const sizes = FIREFOX || CHROME >= 2883 && !VIVALDI ? [16, 32] : [19, 38];
iconUtil.setIcon({
path: sizes.reduce(
(obj, size) => {
obj[size] = `/images/icon/${iconset}${size}${postfix}.png`;
return obj;
},
{}
),
tabId
});
}
function refreshIconBadgeColor() {
const color = prefs.get(prefs.get('disableAll') ? 'badgeDisabled' : 'badgeNormal');
iconUtil.setBadgeBackgroundColor({
color
});
}
function refreshAllIcons() {
for (const [tabId, data] of tabData) {
refreshIcon(tabId, data);
}
refreshIcon(null, {}); // default icon
}
function refreshAllIconsBadgeText() {
for (const [tabId, data] of tabData) {
refreshIconBadgeText(tabId, data);
}
}
function onRuntimeMessage(msg, sender) {
if (msg.method !== 'invokeAPI') {
return;

101
background/icon-manager.js Normal file
View File

@ -0,0 +1,101 @@
/* global prefs debounce iconUtil FIREFOX CHROME VIVALDI tabManager */
/* exported iconManager */
'use strict';
const iconManager = (() => {
const ICON_SIZES = FIREFOX || CHROME >= 2883 && !VIVALDI ? [16, 32] : [19, 38];
prefs.subscribe([
'disableAll',
'badgeDisabled',
'badgeNormal',
], () => debounce(refreshIconBadgeColor));
prefs.subscribe([
'show-badge'
], () => debounce(refreshAllIconsBadgeText));
prefs.subscribe([
'disableAll',
'iconset',
], () => debounce(refreshAllIcons));
prefs.initializing.then(() => {
refreshIconBadgeColor();
refreshAllIconsBadgeText();
refreshAllIcons();
});
return {updateIconBadge};
// FIXME: in some cases, we only have to redraw the badge. is it worth a optimization?
function updateIconBadge(tabId, count, force = true) {
tabManager.setMeta(tabId, 'count', count);
refreshIconBadgeText(tabId);
refreshIcon(tabId, force);
}
function refreshIconBadgeText(tabId) {
const count = tabManager.getMeta(tabId, 'count');
iconUtil.setBadgeText({
text: prefs.get('show-badge') && count ? String(count) : '',
tabId
});
}
function getIconName(count = 0) {
const iconset = prefs.get('iconset') === 1 ? 'light/' : '';
const postfix = prefs.get('disableAll') ? 'x' : !count ? 'w' : '';
return `${iconset}$SIZE$${postfix}`;
}
function refreshIcon(tabId, force = false) {
const oldIcon = tabManager.getMeta(tabId, 'icon');
const newIcon = getIconName(tabManager.getMeta(tabId, 'count'));
if (!force && oldIcon === newIcon) {
return;
}
tabManager.setMeta(tabId, 'icon', newIcon);
iconUtil.setIcon({
path: getIconPath(newIcon),
tabId
});
}
function getIconPath(icon) {
return ICON_SIZES.reduce(
(obj, size) => {
obj[size] = `/images/icon/${icon.replace('$SIZE$', size)}.png`;
return obj;
},
{}
);
}
function refreshGlobalIcon() {
iconUtil.setIcon({
path: getIconPath(getIconName())
});
}
function refreshIconBadgeColor() {
const color = prefs.get(prefs.get('disableAll') ? 'badgeDisabled' : 'badgeNormal');
iconUtil.setBadgeBackgroundColor({
color
});
}
function refreshAllIcons() {
for (const tabId of tabManager.list()) {
refreshIcon(tabId);
}
refreshGlobalIcon();
}
function refreshAllIconsBadgeText() {
for (const tabId of tabManager.list()) {
refreshIconBadgeText(tabId);
}
}
})();

48
background/tab-manager.js Normal file
View File

@ -0,0 +1,48 @@
/* global navigatorUtil */
/* exported tabManager */
'use strict';
const tabManager = (() => {
const listeners = [];
const cache = new Map();
chrome.tabs.onRemoved.addListener(tabId => cache.delete(tabId));
chrome.tabs.onReplaced.addListener((added, removed) => cache.delete(removed));
navigatorUtil.onUrlChange(({tabId, frameId, url}) => {
if (frameId) return;
setMeta(tabId, 'url', url);
emitUpdate({tabId, url});
});
return {onUpdate, setMeta, getMeta, list};
function list() {
return cache.keys();
}
function onUpdate(callback) {
listeners.push(callback);
}
function emitUpdate(e) {
for (const callback of listeners) {
try {
callback(e);
} catch (err) {
console.error(err);
}
}
}
function setMeta(tabId, key, value) {
let meta = cache.get(tabId);
if (!meta) {
meta = new Map();
cache.set(tabId, meta);
}
meta.set(key, value);
}
function getMeta(tabId, key) {
return cache.get(tabId).get(key);
}
})();

View File

@ -30,7 +30,8 @@ const usercssHelper = (() => {
testUrl(url) {
return url.includes('.user.') &&
/^(https?|file|ftps?):/.test(url) &&
/\.user\.(css|styl)$/.test(url.split(/[#?]/, 1)[0]);
/\.user\.(css|styl)$/.test(url.split(/[#?]/, 1)[0]) &&
!url.startsWith(URLS.installUsercss);
},
openInstallerPage(tabId, url) {

View File

@ -46,6 +46,8 @@
"background/style-manager.js",
"background/navigator-util.js",
"background/icon-util.js",
"background/tab-manager.js",
"background/icon-manager.js",
"background/background.js",
"background/usercss-helper.js",
"background/style-via-api.js",

View File

@ -34,8 +34,8 @@
"update-transifex": "tx push -s",
"build-vendor": "shx rm -rf vendor/* && node tools/build-vendor",
"zip": "node tools/zip.js",
"start": "web-ext run --bc",
"start-chrome": "web-ext run -t chromium --bc",
"start": "web-ext run",
"start-chrome": "web-ext run -t chromium",
"preversion": "npm test",
"version": "sync-version manifest.json && git add .",
"postversion": "npm run zip && git push --follow-tags"