Simplify exclusions (#724)
* Change: exclusion should match urlWithoutParams Revert to eight04's initial two commits in #681 which make exclusion toggles domain and singular URLs only, plus reincorporate the js menu height calculation. * Change: drop excludeStyleByUrlRedundant plus menu height * menu item text * Make exclusion rules work like match pattern and handle invalid URLs * Exclude rules in tooltips * Remove leftover code * Cross-browser overflow consistency
This commit is contained in:
parent
19c71868a0
commit
514fa3204f
|
@ -321,9 +321,6 @@
|
||||||
"excludeStyleByUrlLabel": {
|
"excludeStyleByUrlLabel": {
|
||||||
"message": "Exclude the current URL"
|
"message": "Exclude the current URL"
|
||||||
},
|
},
|
||||||
"excludeStyleByUrlRedundant": {
|
|
||||||
"message": "The current URL is the domain page"
|
|
||||||
},
|
|
||||||
"exportLabel": {
|
"exportLabel": {
|
||||||
"message": "Export",
|
"message": "Export",
|
||||||
"description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)"
|
"description": "Label for the button to export a style ('edit' page) or all styles ('manage' page)"
|
||||||
|
|
|
@ -43,7 +43,22 @@ const styleManager = (() => {
|
||||||
const BAD_MATCHER = {test: () => false};
|
const BAD_MATCHER = {test: () => false};
|
||||||
const compileRe = createCompiler(text => `^(${text})$`);
|
const compileRe = createCompiler(text => `^(${text})$`);
|
||||||
const compileSloppyRe = createCompiler(text => `^${text}$`);
|
const compileSloppyRe = createCompiler(text => `^${text}$`);
|
||||||
const compileExclusion = createCompiler(buildGlob);
|
const compileExclusion = createCompiler(buildExclusion);
|
||||||
|
|
||||||
|
const DUMMY_URL = {
|
||||||
|
hash: '',
|
||||||
|
host: '',
|
||||||
|
hostname: '',
|
||||||
|
href: '',
|
||||||
|
origin: '',
|
||||||
|
password: '',
|
||||||
|
pathname: '',
|
||||||
|
port: '',
|
||||||
|
protocol: '',
|
||||||
|
search: '',
|
||||||
|
searchParams: new URLSearchParams(),
|
||||||
|
username: ''
|
||||||
|
};
|
||||||
|
|
||||||
handleLivePreviewConnections();
|
handleLivePreviewConnections();
|
||||||
|
|
||||||
|
@ -280,7 +295,7 @@ const styleManager = (() => {
|
||||||
cache.maybeMatch.add(data.id);
|
cache.maybeMatch.add(data.id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const code = getAppliedCode(url, data);
|
const code = getAppliedCode(createMatchQuery(url), data);
|
||||||
if (!code) {
|
if (!code) {
|
||||||
excluded.add(url);
|
excluded.add(url);
|
||||||
delete cache.sections[data.id];
|
delete cache.sections[data.id];
|
||||||
|
@ -346,11 +361,12 @@ const styleManager = (() => {
|
||||||
const result = [];
|
const result = [];
|
||||||
const datas = !id ? [...styles.values()].map(s => s.data) :
|
const datas = !id ? [...styles.values()].map(s => s.data) :
|
||||||
styles.has(id) ? [styles.get(id).data] : [];
|
styles.has(id) ? [styles.get(id).data] : [];
|
||||||
|
const query = createMatchQuery(url);
|
||||||
for (const data of datas) {
|
for (const data of datas) {
|
||||||
let excluded = false;
|
let excluded = false;
|
||||||
let sloppy = false;
|
let sloppy = false;
|
||||||
let sectionMatched = false;
|
let sectionMatched = false;
|
||||||
const match = urlMatchStyle(url, data);
|
const match = urlMatchStyle(query, data);
|
||||||
// TODO: enable this when the function starts returning false
|
// TODO: enable this when the function starts returning false
|
||||||
// if (match === false) {
|
// if (match === false) {
|
||||||
// continue;
|
// continue;
|
||||||
|
@ -362,7 +378,7 @@ const styleManager = (() => {
|
||||||
if (styleCodeEmpty(section.code)) {
|
if (styleCodeEmpty(section.code)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const match = urlMatchSection(url, section);
|
const match = urlMatchSection(query, section);
|
||||||
if (match) {
|
if (match) {
|
||||||
if (match === 'sloppy') {
|
if (match === 'sloppy') {
|
||||||
sloppy = true;
|
sloppy = true;
|
||||||
|
@ -407,8 +423,9 @@ const styleManager = (() => {
|
||||||
return cache.sections;
|
return cache.sections;
|
||||||
|
|
||||||
function buildCache(styleList) {
|
function buildCache(styleList) {
|
||||||
|
const query = createMatchQuery(url);
|
||||||
for (const {appliesTo, data, preview} of styleList) {
|
for (const {appliesTo, data, preview} of styleList) {
|
||||||
const code = getAppliedCode(url, preview || data);
|
const code = getAppliedCode(query, preview || data);
|
||||||
if (code) {
|
if (code) {
|
||||||
cache.sections[data.id] = {
|
cache.sections[data.id] = {
|
||||||
id: data.id,
|
id: data.id,
|
||||||
|
@ -420,13 +437,13 @@ const styleManager = (() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAppliedCode(url, data) {
|
function getAppliedCode(query, data) {
|
||||||
if (urlMatchStyle(url, data) !== true) {
|
if (urlMatchStyle(query, data) !== true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const code = [];
|
const code = [];
|
||||||
for (const section of data.sections) {
|
for (const section of data.sections) {
|
||||||
if (urlMatchSection(url, section) === true && !styleCodeEmpty(section.code)) {
|
if (urlMatchSection(query, section) === true && !styleCodeEmpty(section.code)) {
|
||||||
code.push(section.code);
|
code.push(section.code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -452,8 +469,11 @@ const styleManager = (() => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function urlMatchStyle(url, style) {
|
function urlMatchStyle(query, style) {
|
||||||
if (style.exclusions && style.exclusions.some(e => compileExclusion(e).test(url))) {
|
if (
|
||||||
|
style.exclusions &&
|
||||||
|
style.exclusions.some(e => compileExclusion(e).test(query.urlWithoutParams))
|
||||||
|
) {
|
||||||
return 'excluded';
|
return 'excluded';
|
||||||
}
|
}
|
||||||
if (!style.enabled) {
|
if (!style.enabled) {
|
||||||
|
@ -462,12 +482,14 @@ const styleManager = (() => {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function urlMatchSection(url, section) {
|
function urlMatchSection(query, section) {
|
||||||
const domain = getDomain(url);
|
if (
|
||||||
if (section.domains && section.domains.some(d => d === domain || domain.endsWith(`.${d}`))) {
|
section.domains &&
|
||||||
|
section.domains.some(d => d === query.domain || query.domain.endsWith(`.${d}`))
|
||||||
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (section.urlPrefixes && section.urlPrefixes.some(p => url.startsWith(p))) {
|
if (section.urlPrefixes && section.urlPrefixes.some(p => query.url.startsWith(p))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// as per spec the fragment portion is ignored in @-moz-document:
|
// as per spec the fragment portion is ignored in @-moz-document:
|
||||||
|
@ -475,12 +497,12 @@ const styleManager = (() => {
|
||||||
// but the spec is outdated and doesn't account for SPA sites
|
// but the spec is outdated and doesn't account for SPA sites
|
||||||
// so we only respect it for `url()` function
|
// so we only respect it for `url()` function
|
||||||
if (section.urls && (
|
if (section.urls && (
|
||||||
section.urls.includes(url) ||
|
section.urls.includes(query.url) ||
|
||||||
section.urls.includes(getUrlNoHash(url))
|
section.urls.includes(query.urlWithoutHash)
|
||||||
)) {
|
)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (section.regexps && section.regexps.some(r => compileRe(r).test(url))) {
|
if (section.regexps && section.regexps.some(r => compileRe(r).test(query.url))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@ -489,7 +511,7 @@ const styleManager = (() => {
|
||||||
We'll detect styles that abuse the bug by finding the sections that
|
We'll detect styles that abuse the bug by finding the sections that
|
||||||
would have been applied by Stylish but not by us as we follow the spec.
|
would have been applied by Stylish but not by us as we follow the spec.
|
||||||
*/
|
*/
|
||||||
if (section.regexps && section.regexps.some(r => compileSloppyRe(r).test(url))) {
|
if (section.regexps && section.regexps.some(r => compileSloppyRe(r).test(query.url))) {
|
||||||
return 'sloppy';
|
return 'sloppy';
|
||||||
}
|
}
|
||||||
// TODO: check for invalid regexps?
|
// TODO: check for invalid regexps?
|
||||||
|
@ -522,16 +544,22 @@ const styleManager = (() => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildGlob(text) {
|
function compileGlob(text) {
|
||||||
return '^' + escapeRegExp(text).replace(/\\\\\\\*|\\\*/g, m => m.length > 2 ? m : '.*') + '$';
|
return escapeRegExp(text).replace(/\\\\\\\*|\\\*/g, m => m.length > 2 ? m : '.*');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDomain(url) {
|
function buildExclusion(text) {
|
||||||
return url.match(/^[\w-]+:\/+(?:[\w:-]+@)?([^:/#]+)/)[1];
|
// match pattern
|
||||||
}
|
const match = text.match(/^(\*|[\w-]+):\/\/(\*\.)?([\w.]+\/.*)/);
|
||||||
|
if (!match) {
|
||||||
function getUrlNoHash(url) {
|
return '^' + compileGlob(text) + '$';
|
||||||
return url.split('#')[0];
|
}
|
||||||
|
return '^' +
|
||||||
|
(match[1] === '*' ? '[\\w-]+' : match[1]) +
|
||||||
|
'://' +
|
||||||
|
(match[2] ? '(?:[\\w.]+\\.)?' : '') +
|
||||||
|
compileGlob(match[3]) +
|
||||||
|
'$';
|
||||||
}
|
}
|
||||||
|
|
||||||
// The md5Url provided by USO includes a duplicate "update" subdomain (see #523),
|
// The md5Url provided by USO includes a duplicate "update" subdomain (see #523),
|
||||||
|
@ -541,4 +569,41 @@ const styleManager = (() => {
|
||||||
style.md5Url = style.md5Url.replace('update.update.userstyles', 'update.userstyles');
|
style.md5Url = style.md5Url.replace('update.update.userstyles', 'update.userstyles');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createMatchQuery(url) {
|
||||||
|
let urlWithoutHash;
|
||||||
|
let urlWithoutParams;
|
||||||
|
let domain;
|
||||||
|
return {
|
||||||
|
url,
|
||||||
|
get urlWithoutHash() {
|
||||||
|
if (!urlWithoutHash) {
|
||||||
|
urlWithoutHash = url.split('#')[0];
|
||||||
|
}
|
||||||
|
return urlWithoutHash;
|
||||||
|
},
|
||||||
|
get urlWithoutParams() {
|
||||||
|
if (!urlWithoutParams) {
|
||||||
|
const u = createURL(url);
|
||||||
|
urlWithoutParams = u.origin + u.pathname;
|
||||||
|
}
|
||||||
|
return urlWithoutParams;
|
||||||
|
},
|
||||||
|
get domain() {
|
||||||
|
if (!domain) {
|
||||||
|
const u = createURL(url);
|
||||||
|
domain = u.hostname;
|
||||||
|
}
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createURL(url) {
|
||||||
|
try {
|
||||||
|
return new URL(url);
|
||||||
|
} catch (err) {
|
||||||
|
return DUMMY_URL;
|
||||||
|
}
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -26,6 +26,10 @@ body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html, body:not(.search-results-shown) {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.firefox body {
|
.firefox body {
|
||||||
color: #000;
|
color: #000;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
|
@ -327,8 +331,7 @@ a.configure[target="_blank"] .svg-icon.config {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
.entry.menu-active .menu {
|
.entry.menu-active .menu {
|
||||||
/* FIXME: avoid hard coded height */
|
height: var(--menu-height, 0px);
|
||||||
height: 72px;
|
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
/* accessibility */
|
/* accessibility */
|
||||||
|
@ -336,12 +339,12 @@ a.configure[target="_blank"] .svg-icon.config {
|
||||||
display: none;
|
display: none;
|
||||||
border: none;
|
border: none;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 0 0 20px;
|
padding: 3px 0 3px 20px;
|
||||||
height: 24px;
|
|
||||||
background: none;
|
background: none;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
flex: none;
|
||||||
}
|
}
|
||||||
.entry.menu-active.accessible-items .menu-item {
|
.entry.menu-active .menu-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.entry .menu-item.delete {
|
.entry .menu-item.delete {
|
||||||
|
@ -360,6 +363,7 @@ a.configure[target="_blank"] .svg-icon.config {
|
||||||
}
|
}
|
||||||
.entry .menu-icon {
|
.entry .menu-icon {
|
||||||
width: 26px;
|
width: 26px;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.entry .menu-icon > * {
|
.entry .menu-icon > * {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -335,16 +335,10 @@ function createStyleElement(style) {
|
||||||
entry.classList.toggle('regexp-partial', style.sloppy);
|
entry.classList.toggle('regexp-partial', style.sloppy);
|
||||||
|
|
||||||
$('.exclude-by-domain-checkbox', entry).checked = styleExcluded(style, 'domain');
|
$('.exclude-by-domain-checkbox', entry).checked = styleExcluded(style, 'domain');
|
||||||
|
$('.exclude-by-url-checkbox', entry).checked = styleExcluded(style, 'url');
|
||||||
|
|
||||||
const excludeByUrlCheckbox = $('.exclude-by-url-checkbox', entry);
|
$('.exclude-by-domain', entry).title = getExcludeRule('domain');
|
||||||
const isRedundant = getExcludeRule('domain') === getExcludeRule('url');
|
$('.exclude-by-url', entry).title = getExcludeRule('url');
|
||||||
excludeByUrlCheckbox.checked = !isRedundant && styleExcluded(style, 'url');
|
|
||||||
excludeByUrlCheckbox.disabled = isRedundant;
|
|
||||||
|
|
||||||
const excludeByUrlLabel = $('.exclude-by-url', entry);
|
|
||||||
excludeByUrlLabel.classList.toggle('disabled', isRedundant);
|
|
||||||
excludeByUrlLabel.title = isRedundant ?
|
|
||||||
chrome.i18n.getMessage('excludeStyleByUrlRedundant') : '';
|
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
@ -358,10 +352,16 @@ function styleExcluded({exclusions}, type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getExcludeRule(type) {
|
function getExcludeRule(type) {
|
||||||
|
const u = new URL(tabURL);
|
||||||
if (type === 'domain') {
|
if (type === 'domain') {
|
||||||
return new URL(tabURL).origin + '/*';
|
return u.origin + '/*';
|
||||||
}
|
}
|
||||||
return tabURL + '*';
|
// current page
|
||||||
|
return escapeGlob(u.origin + u.pathname);
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeGlob(text) {
|
||||||
|
return text.replace(/\*/g, '\\*');
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.assign(handleEvent, {
|
Object.assign(handleEvent, {
|
||||||
|
@ -399,9 +399,8 @@ Object.assign(handleEvent, {
|
||||||
toggleMenu(event) {
|
toggleMenu(event) {
|
||||||
const entry = handleEvent.getClickedStyleElement(event);
|
const entry = handleEvent.getClickedStyleElement(event);
|
||||||
entry.classList.toggle('menu-active');
|
entry.classList.toggle('menu-active');
|
||||||
setTimeout(() => {
|
const menu = entry.querySelector('.menu');
|
||||||
entry.classList.toggle('accessible-items');
|
menu.style.setProperty('--menu-height', menu.scrollHeight + 'px');
|
||||||
}, 250);
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user