Skip to content

Commit bf3dd03

Browse files
committed
don't apply global section to Stylus pages
unless it was intentionally targeted via url(), url-prefix(), or regexp(). The regexp must contain the word "extension" without quotes.
1 parent 3489b51 commit bf3dd03

File tree

2 files changed

+65
-67
lines changed

2 files changed

+65
-67
lines changed

background/style-manager.js

Lines changed: 65 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* global API msg */// msg.js
22
/* global CHROME URLS deepEqual isEmptyObj mapObj stringAsRegExp tryRegExp tryURL */// toolbox.js
33
/* global bgReady createCache uuidIndex */// common.js
4-
/* global calcStyleDigest styleCodeEmpty styleSectionGlobal */// sections-util.js
4+
/* global calcStyleDigest styleCodeEmpty */// sections-util.js
55
/* global db */
66
/* global prefs */
77
/* global tabMan */
@@ -74,6 +74,29 @@ const styleMan = (() => {
7474
_rev: 0,
7575
};
7676
uuidIndex.addCustomId(orderWrap, {set: setOrder});
77+
78+
class MatchQuery {
79+
constructor(url) {
80+
this.url = url;
81+
}
82+
get urlWithoutHash() {
83+
return this._set('urlWithoutHash', this.url.split('#', 1)[0]);
84+
}
85+
get urlWithoutParams() {
86+
return this._set('urlWithoutParams', this.url.split(/[?#]/, 1)[0]);
87+
}
88+
get domain() {
89+
return this._set('domain', tryURL(this.url).hostname);
90+
}
91+
get isOwnPage() {
92+
return this._set('isOwnPage', this.url.startsWith(URLS.ownOrigin));
93+
}
94+
_set(name, value) {
95+
Object.defineProperty(this, name, {value});
96+
return value;
97+
}
98+
}
99+
77100
/** @type {Promise|boolean} will be `true` to avoid wasting a microtask tick on each `await` */
78101
let ready = Promise.all([init(), prefs.ready]);
79102

@@ -226,7 +249,7 @@ const styleMan = (() => {
226249
const styles = id
227250
? [id2style(id)].filter(Boolean)
228251
: getAllAsArray();
229-
const query = createMatchQuery(url);
252+
const query = new MatchQuery(url);
230253
for (const style of styles) {
231254
let excluded = false;
232255
let excludedScheme = false;
@@ -248,10 +271,7 @@ const styleMan = (() => {
248271
excludedScheme = true;
249272
}
250273
for (const section of style.sections) {
251-
if (styleSectionGlobal(section) && styleCodeEmpty(section.code)) {
252-
continue;
253-
}
254-
const match = urlMatchSection(query, section);
274+
const match = urlMatchSection(query, section, true);
255275
if (match) {
256276
if (match === 'sloppy') {
257277
sloppy = true;
@@ -431,7 +451,7 @@ const styleMan = (() => {
431451
cache.maybeMatch.add(id);
432452
continue;
433453
}
434-
const code = getAppliedCode(createMatchQuery(url), style);
454+
const code = getAppliedCode(new MatchQuery(url), style);
435455
if (code) {
436456
updated.add(url);
437457
buildCacheEntry(cache, style, code);
@@ -601,40 +621,56 @@ const styleMan = (() => {
601621
return true;
602622
}
603623

604-
function urlMatchSection(query, section) {
624+
function urlMatchSection(query, section, skipEmptyGlobal) {
625+
let dd, ddL, pp, ppL, rr, rrL, uu, uuL;
605626
if (
606-
section.domains &&
607-
section.domains.some(d => d === query.domain || query.domain.endsWith(`.${d}`))
627+
(dd = section.domains) && (ddL = dd.length) && dd.some(urlMatchDomain, query) ||
628+
(pp = section.urlPrefixes) && (ppL = pp.length) && pp.some(urlMatchPrefix, query) ||
629+
/* Per the specification the fragment portion is ignored in @-moz-document:
630+
https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#url-of-doc
631+
but the spec is outdated and doesn't account for SPA sites,
632+
so we only respect it for `url()` function */
633+
(uu = section.urls) && (uuL = uu.length) && (
634+
uu.includes(query.url) ||
635+
uu.includes(query.urlWithoutHash)
636+
) ||
637+
(rr = section.regexps) && (rrL = rr.length) && rr.some(urlMatchRegexp, query)
608638
) {
609639
return true;
610640
}
611-
if (section.urlPrefixes && section.urlPrefixes.some(p => p && query.url.startsWith(p))) {
612-
return true;
613-
}
614-
// as per spec the fragment portion is ignored in @-moz-document:
615-
// https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#url-of-doc
616-
// but the spec is outdated and doesn't account for SPA sites
617-
// so we only respect it for `url()` function
618-
if (section.urls && (
619-
section.urls.includes(query.url) ||
620-
section.urls.includes(query.urlWithoutHash)
621-
)) {
622-
return true;
623-
}
624-
if (section.regexps && section.regexps.some(r => compileRe(r).test(query.url))) {
625-
return true;
626-
}
627641
/*
628642
According to CSS4 @document specification the entire URL must match.
629643
Stylish-for-Chrome implemented it incorrectly since the very beginning.
630644
We'll detect styles that abuse the bug by finding the sections that
631645
would have been applied by Stylish but not by us as we follow the spec.
632646
*/
633-
if (section.regexps && section.regexps.some(r => compileSloppyRe(r).test(query.url))) {
647+
if (rrL && rr.some(urlMatchRegexpSloppy, query)) {
634648
return 'sloppy';
635649
}
636650
// TODO: check for invalid regexps?
637-
return styleSectionGlobal(section);
651+
return !rrL && !ppL && !uuL && !ddL &&
652+
!query.isOwnPage && // We allow only intentionally targeted sections for own pages
653+
(!skipEmptyGlobal || !styleCodeEmpty(section.code));
654+
}
655+
/** @this {MatchQuery} */
656+
function urlMatchDomain(d) {
657+
const _d = this.domain;
658+
return d === _d ||
659+
_d[_d.length - d.length - 1] === '.' && _d.endsWith(d);
660+
}
661+
/** @this {MatchQuery} */
662+
function urlMatchPrefix(p) {
663+
return p && this.url.startsWith(p);
664+
}
665+
/** @this {MatchQuery} */
666+
function urlMatchRegexp(r) {
667+
return (!this.isOwnPage || /\bextension\b/.test(r)) &&
668+
compileRe(r).test(this.url);
669+
}
670+
/** @this {MatchQuery} */
671+
function urlMatchRegexpSloppy(r) {
672+
return (!this.isOwnPage || /\bextension\b/.test(r)) &&
673+
compileSloppyRe(r).test(this.url);
638674
}
639675

640676
function createCompiler(compile) {
@@ -674,37 +710,8 @@ const styleMan = (() => {
674710
'$';
675711
}
676712

677-
function createMatchQuery(url) {
678-
let urlWithoutHash;
679-
let urlWithoutParams;
680-
let domain;
681-
return {
682-
url,
683-
get urlWithoutHash() {
684-
if (!urlWithoutHash) {
685-
urlWithoutHash = url.split('#')[0];
686-
}
687-
return urlWithoutHash;
688-
},
689-
get urlWithoutParams() {
690-
if (!urlWithoutParams) {
691-
const u = tryURL(url);
692-
urlWithoutParams = u.origin + u.pathname;
693-
}
694-
return urlWithoutParams;
695-
},
696-
get domain() {
697-
if (!domain) {
698-
const u = tryURL(url);
699-
domain = u.hostname;
700-
}
701-
return domain;
702-
},
703-
};
704-
}
705-
706713
function buildCache(cache, url, styleList) {
707-
const query = createMatchQuery(url);
714+
const query = new MatchQuery(url);
708715
for (const {style, appliesTo, preview} of styleList) {
709716
const code = getAppliedCode(query, preview || style);
710717
if (code) {

js/sections-util.js

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
MozDocMapper
66
styleCodeEmpty
77
styleJSONseemsValid
8-
styleSectionGlobal
98
styleSectionsEqual
109
*/
1110

@@ -83,14 +82,6 @@ function styleCodeEmpty(code) {
8382
return false;
8483
}
8584

86-
/** Checks if section is global i.e. has no targets at all */
87-
function styleSectionGlobal(section) {
88-
return (!section.regexps || !section.regexps.length) &&
89-
(!section.urlPrefixes || !section.urlPrefixes.length) &&
90-
(!section.urls || !section.urls.length) &&
91-
(!section.domains || !section.domains.length);
92-
}
93-
9485
/**
9586
* The sections are checked in successive order because it matters when many sections
9687
* match the same URL and they have rules with the same CSS specificity

0 commit comments

Comments
 (0)