Browse Source

update icon and tooltip for blacklisted/noninjectable URLs

tophf 6 years ago
parent
commit
de1f53ba36

+ 6 - 0
src/_locales/en/messages.yml

@@ -142,6 +142,12 @@ extDescription:
 extName:
   description: Name of this extension.
   message: Violentmonkey
+failureReasonBlacklisted:
+  description: Shown for blacklisted URLs (in the extension icon tooltip, in the popup)
+  message: Blacklisted in Violentmonkey's settings
+failureReasonNoninjectable:
+  description: Shown for URLs that cannot be processed (same places as failureBlacklistedUrl)
+  message: "Violentmonkey cannot access this page\n(common examples: browser UI or an extension)"
 filterAlphabeticalOrder:
   description: Label for option to sort scripts in alphabetical order.
   message: alphabetical order

+ 1 - 0
src/background/index.js

@@ -236,4 +236,5 @@ initialize()
   sync.initialize();
   resetBlacklist();
   autoCheckRemove();
+  global.dispatchEvent(new Event('backgroundInitialized'));
 });

+ 71 - 28
src/background/utils/icon.js

@@ -1,5 +1,8 @@
-import { noop } from '#/common';
+import { i18n, noop } from '#/common';
+import { INJECTABLE_TAB_URL_RE } from '#/common/consts';
+import { forEachTab } from './message';
 import { getOption, hookOptions } from './options';
+import { testBlacklist } from './tester';
 
 // Firefox Android does not support such APIs, use noop
 
@@ -7,6 +10,7 @@ const browserAction = [
   'setIcon',
   'setBadgeText',
   'setBadgeBackgroundColor',
+  'setTitle',
 ].reduce((actions, key) => {
   const fn = browser.browserAction[key];
   actions[key] = fn ? fn.bind(browser.browserAction) : noop;
@@ -14,16 +18,49 @@ const browserAction = [
 }, {});
 
 const badges = {};
+let isApplied;
+let showBadge;
+let titleBlacklisted;
+let titleNoninjectable;
 
 hookOptions((changes) => {
-  if ('isApplied' in changes) setIcon(changes.isApplied);
-  if ('showBadge' in changes) updateBadges();
+  if ('isApplied' in changes) {
+    isApplied = changes.isApplied;
+    setIcon();
+  }
+  if ('showBadge' in changes) {
+    showBadge = changes.showBadge;
+    forEachTab(updateBadge);
+  }
+  if ('blacklist' in changes) {
+    forEachTab(updateState);
+  }
+});
+
+global.addEventListener('backgroundInitialized', function onInit(e) {
+  global.removeEventListener(e.type, onInit);
+  isApplied = getOption('isApplied');
+  showBadge = getOption('showBadge');
+  titleBlacklisted = i18n('failureReasonBlacklisted');
+  titleNoninjectable = i18n('failureReasonNoninjectable');
+  forEachTab(updateState);
+});
+
+browser.tabs.onRemoved.addListener((id) => {
+  delete badges[id];
+});
+
+browser.tabs.onUpdated.addListener((tabId, info, tab) => {
+  if (info.status === 'loading') {
+    updateState(tab, info.url);
+  }
 });
 
 export function setBadge({ ids, reset }, src) {
   const srcTab = src.tab || {};
-  let data = !reset && badges[srcTab.id];
-  if (!data) {
+  let data = badges[srcTab.id];
+  if (data && data.blocked) return;
+  if (!data || reset) {
     data = {
       number: 0,
       unique: 0,
@@ -42,39 +79,45 @@ export function setBadge({ ids, reset }, src) {
     color: '#808',
     tabId: srcTab.id,
   });
-  updateBadge(srcTab.id);
+  updateBadge(srcTab, data);
 }
-function updateBadge(tabId) {
-  const data = badges[tabId];
-  if (data) {
-    const showBadge = getOption('showBadge');
-    let text;
+
+function updateBadge(tab, data = badges[tab.id]) {
+  if (!data) return;
+  let text;
+  if (!data.blocked) {
     if (showBadge === 'total') text = data.number;
     else if (showBadge) text = data.unique;
-    browserAction.setBadgeText({
-      text: `${text || ''}`,
-      tabId,
-    });
   }
-}
-function updateBadges() {
-  browser.tabs.query({})
-  .then((tabs) => {
-    tabs.forEach((tab) => {
-      updateBadge(tab.id);
-    });
+  browserAction.setBadgeText({
+    text: `${text || ''}`,
+    tabId: tab.id,
   });
 }
-browser.tabs.onRemoved.addListener((id) => {
-  delete badges[id];
-});
 
-function setIcon(isApplied) {
+function updateState(tab, url = tab.url) {
+  const tabId = tab.id;
+  const injectable = INJECTABLE_TAB_URL_RE.test(url);
+  const blacklisted = injectable ? testBlacklist(url) : undefined;
+  const title = blacklisted && titleBlacklisted || !injectable && titleNoninjectable || '';
+  // if the user unblacklisted this previously blocked tab in settings,
+  // but didn't reload the tab yet, we need to restore the icon and the title
+  if (title || (badges[tabId] || {}).blocked) {
+    browserAction.setTitle({ title, tabId });
+    const data = title ? { blocked: true } : {};
+    badges[tabId] = data;
+    setIcon(tab, data);
+    updateBadge(tab, data);
+  }
+}
+
+function setIcon(tab = {}, data = {}) {
   // modern Chrome and Firefox use 16/32, other browsers may still use 19/38 (e.g. Vivaldi)
+  const mod = data.blocked && 'b' || !isApplied && 'w' || '';
   browserAction.setIcon({
     path: Object.assign({}, ...[16, 19, 32, 38].map(n => ({
-      [n]: `/public/images/icon${n}${isApplied ? '' : 'w'}.png`,
+      [n]: `/public/images/icon${n}${mod}.png`,
     }))),
+    tabId: tab.id,
   });
 }
-setIcon(getOption('isApplied'));

+ 2 - 0
src/common/consts.js

@@ -10,3 +10,5 @@ export const CMD_SCRIPT_UPDATE = 'UpdateScript';
 // The SPACE must be on the same line and specifically \x20 as \s would also match \r\n\t
 // Note: when there's no valid metablock, an empty string is matched for convenience
 export const METABLOCK_RE = /(?:^|\n)\s*\/\/\x20==UserScript==([\s\S]*?\n)\s*\/\/\x20==\/UserScript==|$/;
+
+export const INJECTABLE_TAB_URL_RE = /^(https?|file|ftps?):/;

BIN
src/public/images/icon16b.png


BIN
src/public/images/icon19b.png


BIN
src/public/images/icon32b.png


BIN
src/public/images/icon38b.png