diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 3c5964f3..87a5a533 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -315,6 +315,15 @@ "message": "Enable", "description": "Label for the button to enable a style" }, + "excludeStyleByDomainLabel": { + "message": "Exclude the current domain" + }, + "excludeStyleByUrlLabel": { + "message": "Exclude the current URL" + }, + "excludeStyleByUrlRedundant": { + "message": "The current URL is the domain page" + }, "exportLabel": { "message": "Export", "description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)" @@ -1026,6 +1035,10 @@ "message": "Stylus failed to parse usercss:", "description": "The error message to show when stylus failed to parse usercss" }, + "popupAutoResort": { + "message": "Resort styles in popup after toggling", + "description": "Label for the checkbox controlling popup resorting." + }, "popupBorders": { "message": "Add white borders on the sides" }, @@ -1044,6 +1057,10 @@ "message": "Shift-click or right-click opens manager with styles applicable for current site", "description": "Tooltip for the 'Manage' button in the popup." }, + "popupMenuButtonTooltip": { + "message": "Action menu", + "description": "Tooltip for menu button in popup." + }, "popupOpenEditInWindow": { "message": "Open editor in a new window", "description": "Label for the checkbox controlling 'edit' action behavior in the popup." @@ -1056,10 +1073,6 @@ "message": "Styles before commands", "description": "Label for the checkbox controlling section order in the popup." }, - "popupAutoResort": { - "message": "Resort styles in popup after toggling", - "description": "Label for the checkbox controlling popup resorting." - }, "prefShowBadge": { "message": "Number of styles active for the current site", "description": "Label for the checkbox controlling toolbar badge text." diff --git a/background/background.js b/background/background.js index 2f2835d5..70dc6ba4 100644 --- a/background/background.js +++ b/background/background.js @@ -22,6 +22,11 @@ window.API_METHODS = Object.assign(window.API_METHODS || {}, { styleExists: styleManager.styleExists, toggleStyle: styleManager.toggleStyle, + addInclusion: styleManager.addInclusion, + removeInclusion: styleManager.removeInclusion, + addExclusion: styleManager.addExclusion, + removeExclusion: styleManager.removeExclusion, + getTabUrlPrefix() { return this.sender.tab.url.match(/^([\w-]+:\/+[^/#]+)/)[1]; }, @@ -42,9 +47,11 @@ window.API_METHODS = Object.assign(window.API_METHODS || {}, { // 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)) { - return updateIconBadgeForce(this.sender.tab.id, count); + updateIconBadgeForce(this.sender.tab.id, count); + } else { + updateIconBadge(this.sender.tab.id, count); } - return updateIconBadge(this.sender.tab.id, count); + return true; }, // exposed for stuff that requires followup sendMessage() like popup::openSettings @@ -79,8 +86,8 @@ 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'}, + {pathSuffix: '.user.css'}, + {pathSuffix: '.user.styl'}, ] }); // FF misses some about:blank iframes so we inject our content script explicitly diff --git a/background/style-manager.js b/background/style-manager.js index b12b4231..1560f13e 100644 --- a/background/style-manager.js +++ b/background/style-manager.js @@ -57,10 +57,13 @@ const styleManager = (() => { importStyle, importMany, toggleStyle, - setStyleExclusions, getAllStyles, // used by import-export getStylesByUrl, // used by popup styleExists, + addExclusion, + removeExclusion, + addInclusion, + removeInclusion }); function handleLivePreviewConnections() { @@ -92,6 +95,11 @@ const styleManager = (() => { }); } + function escapeRegExp(text) { + // https://github.com/lodash/lodash/blob/0843bd46ef805dd03c0c8d804630804f3ba0ca3c/lodash.js#L152 + return text.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&'); + } + function get(id, noCode = false) { const data = styles.get(id).data; return noCode ? getStyleWithNoCode(data) : data; @@ -177,14 +185,51 @@ const styleManager = (() => { } else { data = Object.assign(createNewStyle(), data); } + data.updateDate = Date.now(); return saveStyle(data) .then(newData => handleSave(newData, 'editSave')); } - function setStyleExclusions(id, exclusions) { - const data = Object.assign({}, styles.get(id).data, {exclusions}); + function addIncludeExclude(id, rule, type) { + const data = Object.assign({}, styles.get(id).data); + if (!data[type]) { + data[type] = []; + } + if (data[type].includes(rule)) { + throw new Error('The rule already exists'); + } + data[type] = data[type].concat([rule]); return saveStyle(data) - .then(newData => handleSave(newData, 'exclusions')); + .then(newData => handleSave(newData, 'styleSettings')); + } + + function removeIncludeExclude(id, rule, type) { + const data = Object.assign({}, styles.get(id).data); + if (!data[type]) { + return; + } + if (!data[type].includes(rule)) { + return; + } + data[type] = data[type].filter(r => r !== rule); + return saveStyle(data) + .then(newData => handleSave(newData, 'styleSettings')); + } + + function addExclusion(id, rule) { + return addIncludeExclude(id, rule, 'exclusions'); + } + + function removeExclusion(id, rule) { + return removeIncludeExclude(id, rule, 'exclusions'); + } + + function addInclusion(id, rule) { + return addIncludeExclude(id, rule, 'inclusions'); + } + + function removeInclusion(id, rule) { + return removeIncludeExclude(id, rule, 'inclusions'); } function deleteStyle(id) { @@ -478,14 +523,7 @@ const styleManager = (() => { } function buildGlob(text) { - const prefix = text[0] === '^' ? '' : '\\b'; - const suffix = text[text.length - 1] === '$' ? '' : '\\b'; - return `${prefix}${escape(text)}${suffix}`; - - function escape(text) { - // FIXME: using .* everywhere is slow - return text.replace(/[.*]/g, m => m === '.' ? '\\.' : '.*'); - } + return '^' + escapeRegExp(text).replace(/\\\\\\\*|\\\*/g, m => m.length > 2 ? m : '.*') + '$'; } function getDomain(url) { diff --git a/content/apply.js b/content/apply.js index 4d5b5f5f..e91e3f97 100644 --- a/content/apply.js +++ b/content/apply.js @@ -304,7 +304,7 @@ const APPLY = (() => { method: 'invokeAPI', name: 'updateIconBadge', args: [styleInjector.list.length] - }).catch(msg.ignoreError); + }).catch(console.error); } function rootReady() { diff --git a/edit/codemirror-default.js b/edit/codemirror-default.js index d0f6b8c2..edc7af28 100644 --- a/edit/codemirror-default.js +++ b/edit/codemirror-default.js @@ -157,10 +157,18 @@ 'text-align-all': true, 'contain': true, + 'mask-image': true, 'mix-blend-mode': true, + 'rotate': true, 'isolation': true, 'zoom': true, + // https://www.w3.org/TR/css-round-display-1/ + 'border-boundary': true, + 'shape': true, + 'shape-inside': true, + 'viewport-fit': true, + // nonstandard https://compat.spec.whatwg.org/ 'box-reflect': true, 'text-fill-color': true, @@ -171,6 +179,7 @@ }); Object.assign(CodeMirror.mimeModes['text/css'].valueKeywords, { 'isolate': true, + 'rect': true, 'recto': true, 'verso': true, }); diff --git a/edit/sections-editor.js b/edit/sections-editor.js index 7e2f8063..99772f2b 100644 --- a/edit/sections-editor.js +++ b/edit/sections-editor.js @@ -197,6 +197,7 @@ function createSectionsEditor({style, onTitleChanged}) { dirty.modify('enabled', style.enabled, newValue); style.enabled = newValue; enabledEl.checked = newValue; + updateLivePreview(); } function nextEditor(cm, cycle = true) { @@ -565,6 +566,7 @@ function createSectionsEditor({style, onTitleChanged}) { $('#heading').textContent = t('editStyleHeading'); } livePreview.show(Boolean(style.id)); + updateLivePreview(); }); function reinit() { diff --git a/install-usercss/install-usercss.js b/install-usercss/install-usercss.js index 889da8b8..afbd6419 100644 --- a/install-usercss/install-usercss.js +++ b/install-usercss/install-usercss.js @@ -307,9 +307,9 @@ // set updateUrl const checker = $('.set-update-url input[type=checkbox]'); - // prefer the installation URL unless drag'n'dropped on the manage page + // only use the installation URL if not specified in usercss const installationUrl = (params.get('updateUrl') || '').replace(/^blob.+/, ''); - const updateUrl = new URL(installationUrl || style.updateUrl || DUMMY_URL); + const updateUrl = new URL(style.updateUrl || installationUrl || DUMMY_URL); if (dup && dup.updateUrl === updateUrl.href) { checker.checked = true; // there is no way to "unset" updateUrl, you can only overwrite it. @@ -335,7 +335,7 @@ // live reload const setLiveReload = $('.live-reload input[type=checkbox]'); - if (updateUrl.protocol !== 'file:') { + if (!installationUrl || !installationUrl.startsWith('file:')) { setLiveReload.parentNode.remove(); } else { setLiveReload.addEventListener('change', () => { diff --git a/js/usercss.js b/js/usercss.js index 360743c0..deebcbbb 100644 --- a/js/usercss.js +++ b/js/usercss.js @@ -7,7 +7,7 @@ const usercss = (() => { author: undefined, description: undefined, homepageURL: 'url', - // updateURL: 'updateUrl', + updateURL: 'updateUrl', name: undefined, }; const RX_META = /\/\*!?\s*==userstyle==[\s\S]*?==\/userstyle==\s*\*\//i; diff --git a/popup.html b/popup.html index 8d605552..adbcc13b 100644 --- a/popup.html +++ b/popup.html @@ -24,25 +24,55 @@