diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 4d2e9786..dfee7509 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -294,6 +294,10 @@ "message": "Favicons in applies-to column", "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" }, + "manageFaviconsHelp": { + "message": "Stylus asks for chrome://favicon permission to retrieve the icons from browser cache. For non-cached icons Stylus uses external service https://www.google.com/s2/favicons", + "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" + }, "manageMaxTargets": { "message": "Number of applies-to items", "description": "Label for the numeric input box to limit max number of applies-to targets in the new UI on manage page" diff --git a/manage.css b/manage.css index dbab0ef9..a71894e3 100644 --- a/manage.css +++ b/manage.css @@ -61,10 +61,15 @@ a:hover { transition: fill .5s; width: 20px; height: 20px; +} + +.svg-icon, +.svg-icon.info:hover { fill: #000; } -.svg-icon:hover { +.svg-icon:hover, +.svg-icon.info { fill: #666; } @@ -73,6 +78,12 @@ a:hover { height: 16px; } +.svg-icon.info { + width: 14px; + height: 16px; + margin-left: .5ex; +} + .homepage { margin-left: 0.1em; margin-right: 0.1em; @@ -373,10 +384,11 @@ summary { display: initial; } -#newUIoptions label { +#newUIoptions > * { display: flex; align-items: center; margin-bottom: auto; + flex-wrap: wrap; } #newUIoptions input[type="number"] { @@ -388,6 +400,13 @@ input[id^="manage.newUI"] { margin-left: 0; } +#faviconsHelp { + overflow-y: auto; + font-size: 90%; + padding: 1ex 0 2ex 16px; +} + + /* Default, no update buttons */ .update, .check-update { diff --git a/manage.html b/manage.html index 32b8b92a..ba05f28e 100644 --- a/manage.html +++ b/manage.html @@ -149,7 +149,14 @@

- +
+ + + + + + +

diff --git a/manage.js b/manage.js index 79039377..500b342f 100644 --- a/manage.js +++ b/manage.js @@ -14,7 +14,10 @@ const newUI = { newUI.renderClass(); const TARGET_TYPES = ['domains', 'urls', 'urlPrefixes', 'regexps']; -const GET_FAVICON_URL = 'https://www.google.com/s2/favicons?domain='; +const GET_FAVICON_URL = { + builtin: 'chrome://favicon/size/16@2x/', + external: 'https://www.google.com/s2/favicons?domain=', +}; const OWN_ICON = chrome.runtime.getManifest().icons['16']; const handleEvent = {}; @@ -74,7 +77,7 @@ function initGlobalEvents() { checkbox.onchange = () => installed.classList.toggle(className, checkbox.checked); } - enforceInputRange($('#manage.newUI.favicons')); + enforceInputRange($('#manage.newUI.targets')); setupLivePrefs([ 'manage.onlyEnabled', @@ -84,8 +87,28 @@ function initGlobalEvents() { 'manage.newUI.targets', ]); - $$('[id^="manage.newUI"]') - .forEach(el => (el.oninput = (el.onchange = switchUI))); + $('#manage.newUI').onchange = switchUI; + $('#manage.newUI.targets').oninput = switchUI; + $('#manage.newUI.targets').onchange = switchUI; + $('#manage.newUI.favicons').onchange = function() { + if (!this.checked) { + switchUI(); + return; + } + if (this.disabled) { + return; + } + this.disabled = true; + onPermissionsGranted({origins: ['chrome://favicon/']}).then( + switchUI, + () => (this.checked = false) + ).then( + () => (this.disabled = false) + ); + }; + $$('[data-toggle-on-click]').forEach(el => { + el.onclick = () => $(el.dataset.toggleOnClick).classList.toggle('hidden'); + }); switchUI({styleOnly: true}); } @@ -183,12 +206,12 @@ function createStyleElement({style, name}) { } else if (newUI.favicons) { let favicon = ''; if (type == 'domains') { - favicon = GET_FAVICON_URL + targetValue; + favicon = 'http://' + targetValue; } else if (targetValue.startsWith('chrome-extension:')) { favicon = OWN_ICON; } else if (type != 'regexps') { - favicon = targetValue.includes('://') && targetValue.match(/^.*?:\/\/([^/]+)/); - favicon = favicon ? GET_FAVICON_URL + favicon[1] : ''; + favicon = targetValue.includes('://') && targetValue.match(/^.*?:\/\/[^/]+/); + favicon = favicon ? favicon[0] : ''; } if (favicon) { element.appendChild(document.createElement('img')).dataset.src = favicon; @@ -331,10 +354,13 @@ Object.assign(handleEvent, { loadFavicons(container = installed) { for (const img of container.getElementsByTagName('img')) { - if (img.dataset.src) { - img.src = img.dataset.src; - delete img.dataset.src; + const src = img.dataset.src; + if (!src) { + continue; } + img.src = src == OWN_ICON ? src + : GET_FAVICON_URL.builtin + (src.includes('://') ? src : 'http://' + src); + delete img.dataset.src; } } }); @@ -682,3 +708,22 @@ function findNextElement(style) { } return elements[elements[a].styleNameLowerCase <= nameLLC ? a + 1 : a]; } + + +function onPermissionsGranted(permissions) { + return new Promise((resolve, reject) => { + chrome.permissions.contains(permissions, alreadyGranted => { + if (alreadyGranted) { + resolve(); + } else { + chrome.permissions.request(permissions, granted => { + if (granted) { + resolve(); + } else { + reject(); + } + }); + } + }); + }); +} diff --git a/manifest.json b/manifest.json index eb283982..f8301da2 100644 --- a/manifest.json +++ b/manifest.json @@ -15,8 +15,10 @@ "tabs", "webNavigation", "contextMenus", - "storage", - "*://*/*" + "storage" + ], + "optional_permissions": [ + "chrome://favicon/" ], "background": { "scripts": ["messaging.js", "storage.js", "prefs.js", "background.js", "update.js"] diff --git a/prefs.js b/prefs.js index a47e9b6c..f3e817d6 100644 --- a/prefs.js +++ b/prefs.js @@ -17,7 +17,7 @@ var prefs = new function Prefs() { 'manage.onlyEnabled': false, // display only enabled styles 'manage.onlyEdited': false, // display only styles created locally 'manage.newUI': true, // use the new compact layout - 'manage.newUI.favicons': true, // show favicons for the sites in applies-to + 'manage.newUI.favicons': false, // show favicons for the sites in applies-to 'manage.newUI.targets': 3, // max number of applies-to targets visible: 0 = none 'editor.options': {}, // CodeMirror.defaults.*