@@ -139,17 +139,24 @@ vAPI.proceduralCosmeticFiltering = (function() {
139
139
140
140
if ( selector [ 0 ] == ">" ) {
141
141
selector = ":scope" + selector ;
142
-
143
- if ( scopeSupported ) {
144
- return all ? subtree . querySelectorAll ( selector ) :
145
- subtree . querySelector ( selector ) ;
142
+ if ( scopeSupported ) {
143
+ try {
144
+ return all ? subtree . querySelectorAll ( selector ) :
145
+ subtree . querySelector ( selector ) ;
146
+ } catch ( e ) {
147
+ return null ;
148
+ }
149
+ }
150
+ if ( scopeSupported == null )
151
+ return tryQuerySelector ( subtree , selector , all ) ;
152
+ return null ;
153
+ }
154
+ try {
155
+ return all ? subtree . querySelectorAll ( selector ) :
156
+ subtree . querySelector ( selector ) ;
157
+ } catch ( e ) {
158
+ return null ;
146
159
}
147
- if ( scopeSupported == null )
148
- return tryQuerySelector ( subtree , selector , all ) ;
149
- return null ;
150
- }
151
- return all ? subtree . querySelectorAll ( selector ) :
152
- subtree . querySelector ( selector ) ;
153
160
}
154
161
/* Scriptlet below borrowed from: https://github.com/adblockplus/adblockpluscore/blob/3e16d3602509b2dbb2238ab1ebcbc5e5b5993862/lib/common.js#L40 */
155
162
function filterToRegExp ( text , captureAll = false ) {
@@ -276,7 +283,13 @@ vAPI.proceduralCosmeticFiltering = (function() {
276
283
if ( typeof styleFilter === 'undefined' ) {
277
284
continue ;
278
285
}
279
- selectors . push ( styleFilter [ filter ] ) ;
286
+ if ( styleFilter . hasOwnProperty ( filter ) ) {
287
+ if ( Array . isArray ( styleFilter [ filter ] ) ) {
288
+ selectors . push ( ...styleFilter [ filter ] ) ;
289
+ } else {
290
+ selectors . push ( styleFilter [ filter ] ) ;
291
+ }
292
+ }
280
293
}
281
294
return selectors ;
282
295
} ;
@@ -352,10 +365,10 @@ vAPI.proceduralCosmeticFiltering = (function() {
352
365
}
353
366
return combineSelectors ;
354
367
}
355
- var hasSelector = function ( hasSelector , prefix ) {
368
+ var hasSelector = function ( hasSelector , prefix , hasParallelSiblingSelector = false ) {
356
369
this . prefix = prefix ;
357
370
this . _innerSelectors = hasSelector ;
358
- this . hasParallelSiblingSelector = false ;
371
+ this . hasParallelSiblingSelector = hasParallelSiblingSelector ;
359
372
this . dependsOnDOM = true ;
360
373
}
361
374
hasSelector . prototype = {
@@ -373,6 +386,7 @@ vAPI.proceduralCosmeticFiltering = (function() {
373
386
} ,
374
387
getSelectors : function ( rootnode , selectors , targets ) {
375
388
var nodes = prime ( rootnode , this . prefix ) ;
389
+ if ( nodes == null ) return ;
376
390
var matchSelector = [ ] ;
377
391
var lastRoot = null ;
378
392
for ( var node of nodes ) {
@@ -401,10 +415,10 @@ vAPI.proceduralCosmeticFiltering = (function() {
401
415
return ;
402
416
}
403
417
}
404
- var containSelector = function ( selectorText , prefix ) {
418
+ var containSelector = function ( selectorText , prefix , hasParallelSiblingSelector = false ) {
405
419
this . prefix = prefix ;
406
420
this . _regexp = makeRegExpParameter ( selectorText ) ;
407
- this . hasParallelSiblingSelector = false ;
421
+ this . hasParallelSiblingSelector = hasParallelSiblingSelector ;
408
422
this . dependsOnCharacterData = true ;
409
423
this . dependsOnDOM = true ;
410
424
}
@@ -413,6 +427,7 @@ vAPI.proceduralCosmeticFiltering = (function() {
413
427
var matchSelector = [ ] ;
414
428
let lastRoot = null ;
415
429
var nodes = prime ( rootnode , this . prefix ) ;
430
+ if ( nodes == null ) return ;
416
431
for ( var node of nodes ) {
417
432
if ( lastRoot && lastRoot . contains ( node ) && ! this . hasParallelSiblingSelector ) {
418
433
continue ;
@@ -431,6 +446,29 @@ vAPI.proceduralCosmeticFiltering = (function() {
431
446
return ;
432
447
}
433
448
}
449
+ var matchCSSSelector = function ( selectorText , prefix , temp , pseudoClass ) {
450
+ this . prefix = prefix ;
451
+ this . pseudoClass = pseudoClass ;
452
+ let matches = / ( [ ^ : ] + ) : ( .* ) / g. exec ( selectorText ) ;
453
+ this . propertName = matches [ 1 ] ;
454
+ this . _regexp = makeRegExpParameter ( matches [ 2 ] . trim ( ) ) ;
455
+ }
456
+ matchCSSSelector . prototype = {
457
+ getSelectors : function ( rootnode , selectors ) {
458
+ var matchSelector = [ ] ;
459
+ var nodes = prime ( rootnode , this . prefix ) ;
460
+ if ( nodes == null ) return ;
461
+ for ( var node of nodes ) {
462
+ const style = window . getComputedStyle ( node , this . pseudoClass ) ;
463
+ if ( style !== null && this . _regexp && this . _regexp . test ( style [ this . propertName ] ) ) {
464
+ matchSelector . push ( makeSelector ( node ) ) ;
465
+ }
466
+ }
467
+ if ( matchSelector . length > 0 )
468
+ selectors . set ( 'contains' , matchSelector ) ;
469
+ return ;
470
+ }
471
+ }
434
472
var plainSelector = function ( selectorText ) {
435
473
this . selector = selectorText ;
436
474
this . maybeContainsSiblingCombinators = / [ ~ + ] / . test ( selectorText ) ;
@@ -443,10 +481,10 @@ vAPI.proceduralCosmeticFiltering = (function() {
443
481
return ;
444
482
}
445
483
}
446
- var propsSelector = function ( propertyExpression , prefix ) {
484
+ var propsSelector = function ( propertyExpression , prefix , hasParallelSiblingSelector = false ) {
447
485
this . prefix = prefix ;
448
486
this . propertyExpression = propertyExpression ;
449
- this . hasParallelSiblingSelector = false ;
487
+ this . hasParallelSiblingSelector = hasParallelSiblingSelector ;
450
488
this . dependsOnStyles = true ;
451
489
this . dependsOnDOM = true ;
452
490
styleObserver . registerStyleFilter ( propertyExpression ) ;
@@ -455,6 +493,7 @@ vAPI.proceduralCosmeticFiltering = (function() {
455
493
getSelectors : function ( rootnode , selectors , targets ) {
456
494
var matchSelector = [ ] ;
457
495
var nodes = prime ( rootnode , this . prefix ) ;
496
+ if ( nodes == null ) return ;
458
497
var styleSelectors = styleObserver . getSelector ( this . propertyExpression ) ;
459
498
if ( styleSelectors . length === 0 )
460
499
return ;
@@ -478,80 +517,53 @@ vAPI.proceduralCosmeticFiltering = (function() {
478
517
}
479
518
}
480
519
var proceduralSelector = function ( ) {
481
- this . operatorMap = new Map ( [ [ 'has' , hasSelector ] , [ ' contains', containSelector ] , [ ' properties', propsSelector ] ] ) ;
520
+ this . operatorMap = new Map ( [ [ 'plain' , plainSelector ] , [ '-abp- has', hasSelector ] , [ 'has' , hasSelector ] , [ '-abp- contains', containSelector ] , [ 'contains' , containSelector ] , [ 'matches-css' , matchCSSSelector ] , [ 'matches-css-after' , matchCSSSelector ] , [ 'matches-css-before' , matchCSSSelector ] , [ '-abp- properties', propsSelector ] ] ) ;
482
521
this . patterns = [ ] ;
483
522
}
484
523
proceduralSelector . prototype = {
485
- parseProcedure : function ( expression , prefix = "" ) {
524
+ buildFunction ( s ) {
486
525
let tasks = [ ] ;
487
- let matches = abpSelectorRegexp . exec ( expression ) ;
488
- if ( ! matches ) {
489
- return [ new plainSelector ( expression ) ] ;
490
- }
491
- var prefix = expression . substring ( 0 , matches . index ) ;
492
- let remaining = expression . substring ( matches . index + matches [ 0 ] . length ) ;
493
- let parsed = this . parseContent ( remaining , 0 ) ;
494
- let selectorText = parsed . text ;
495
- if ( matches [ 1 ] == "properties" ) {
496
- tasks . push ( new ( this . operatorMap . get ( matches [ 1 ] ) ) ( selectorText , prefix ) ) ;
497
- }
498
- else if ( matches [ 1 ] == "has" ) {
499
- let procSelector = this . parseProcedure ( selectorText , prefix ) ;
500
- tasks . push ( new ( this . operatorMap . get ( matches [ 1 ] ) ) ( procSelector , prefix ) ) ;
501
- }
502
- else if ( matches [ 1 ] == "contains" ) {
503
- tasks . push ( new ( this . operatorMap . get ( matches [ 1 ] ) ) ( selectorText , prefix ) ) ;
504
- }
505
- else {
506
- console . error ( new SyntaxError ( "Failed to parse uBlock." ) ) ;
507
- return null ;
508
- }
509
- let suffixtext = remaining . substring ( parsed . end + 1 ) ;
510
- if ( suffixtext != "" ) {
511
- let suffix = this . parseProcedure ( suffixtext ) ;
512
- if ( suffix . length == 1 && suffix [ 0 ] instanceof plainSelector && suffix [ 0 ] . maybeContainsSiblingCombinators ) {
513
- for ( let task of tasks ) {
514
- if ( task instanceof hasSelector || task instanceof containSelector || task instanceof proceduralSelector ) {
515
- task . hasParallelSiblingSelector = true ;
516
- }
526
+ let selectorText = "" ;
527
+ for ( var property in s ) {
528
+ let cls = new ( this . operatorMap . get ( property ) ) ( s [ property ] . st , s [ property ] . pf , s [ property ] . pss , s [ property ] . ps ) ;
529
+ if ( property == "plain" ) {
530
+ tasks . push ( cls ) ;
531
+ selectorText += s [ property ] . st ;
532
+ return [ tasks , selectorText ] ;
533
+ }
534
+ selectorText += s [ property ] . pf + ':' + property + '(' ;
535
+ let inner = s [ property ] . _is
536
+ if ( inner ) {
537
+ cls [ "_innerSelectors" ] = [ ] ;
538
+ for ( let i = 0 ; i < inner . length ; i ++ ) {
539
+ let [ innerTasks , innerSelectorText ] = this . buildFunction ( inner [ i ] ) ;
540
+ selectorText += innerSelectorText ;
541
+ cls [ "_innerSelectors" ] . push ( ...innerTasks ) ;
517
542
}
543
+ } else {
544
+ selectorText += s [ property ] . st ;
518
545
}
519
- tasks . push ( ...suffix ) ;
546
+ selectorText += ")" ;
547
+ tasks . push ( cls ) ;
520
548
}
521
- return tasks ;
549
+ return [ tasks , selectorText ] ;
522
550
} ,
523
- parseContent : function ( content , startIndex ) {
524
- let parens = 1 ;
525
- let quote = null ;
526
- let i = startIndex ;
527
- for ( ; i < content . length ; i ++ ) {
528
- let c = content [ i ] ;
529
- if ( c == "\\" ) {
530
- i ++ ;
531
- }
532
- else if ( quote ) {
533
- if ( c == quote )
534
- quote = null ;
535
- }
536
- else if ( c == "'" || c == '"' )
537
- quote = c ;
538
- else if ( c == "(" )
539
- parens ++ ;
540
- else if ( c == ")" ) {
541
- parens -- ;
542
- if ( parens == 0 )
543
- break ;
544
- }
551
+ decompile ( s ) {
552
+ let ss = JSON . parse ( s ) ;
553
+ let tasks = [ ] ;
554
+ let selectorText = "" ;
555
+ for ( let i = 0 ; i < ss . length ; i ++ ) {
556
+ let [ innerTasks , innerSelectorText ] = this . buildFunction ( ss [ i ] ) ;
557
+ tasks . push ( ...innerTasks ) ;
558
+ selectorText += innerSelectorText ;
545
559
}
546
- if ( parens > 0 )
547
- return null ;
548
- return { text : content . substring ( startIndex , i ) , end : i } ;
549
- } ,
560
+ return [ tasks , selectorText ] ;
561
+ } ,
550
562
applyPatterns : function ( patterns ) {
551
563
this . patterns = [ ] ;
552
564
for ( let selector of patterns ) {
553
- var tasks = this . parseProcedure ( selector , "" ) ;
554
- this . patterns . push ( [ selector , tasks ] ) ;
565
+ let [ tasks , selectorText ] = this . decompile ( selector ) ;
566
+ this . patterns . push ( [ selectorText , tasks ] ) ;
555
567
}
556
568
if ( this . patterns . length > 0 ) {
557
569
this . processPattern ( ) ;
@@ -571,7 +583,7 @@ vAPI.proceduralCosmeticFiltering = (function() {
571
583
styleObserver . readStyleSheet ( stylesheet ) ;
572
584
}
573
585
574
- var matchSelector = [ ] ;
586
+ var matchSelector = [ ] ;
575
587
var matchProcSelector = [ ] ;
576
588
let mutationTargets = this . extractMutationTargets ( mutations ) ;
577
589
var mutations = mutations ;
@@ -686,24 +698,6 @@ vAPI.proceduralCosmeticFiltering = (function() {
686
698
//this.processPattern([stylesheet]);
687
699
objQueueProcessing . add ( [ stylesheet ] ) ;
688
700
}
689
- /*var hideElements = function(selectors) {
690
- // https://github.com/uBlockAdmin/uBlock/issues/207
691
- // Do not call querySelectorAll() using invalid CSS selectors
692
- if ( selectors.length === 0 ) {
693
- return;
694
- }
695
- if ( document.body === null ) {
696
- return;
697
- }
698
- // https://github.com/uBlockAdmin/uBlock/issues/158
699
- // Using CSSStyleDeclaration.setProperty is more reliable
700
- var elems = document.querySelectorAll(selectors);
701
- var i = elems.length;
702
- while ( i-- ) {
703
- elems[i].style.setProperty('display', 'none', 'important');
704
- }
705
- messager.send({ what: 'cosmeticFiltersActivated' });
706
- };*/
707
701
return new proceduralSelector ( ) ;
708
702
} ) ( ) ;
709
703
0 commit comments