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 || / \b e x t e n s i o n \b / . test ( r ) ) &&
668+ compileRe ( r ) . test ( this . url ) ;
669+ }
670+ /** @this {MatchQuery} */
671+ function urlMatchRegexpSloppy ( r ) {
672+ return ( ! this . isOwnPage || / \b e x t e n s i o n \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 ) {
0 commit comments