@@ -595,8 +595,10 @@ var FilterContainer = function() {
595595 this . selectorCacheTimer = null ;
596596 this . reHasUnicode = / [ ^ \x00 - \x7F ] / ;
597597 this . reClassOrIdSelector = / ^ [ # . ] [ \w - ] + $ / ;
598- this . rePlainSelector = / ^ [ # . ] [ \w - ] + / ;
598+ this . rePlainSelector = / ^ [ # . ] [ \w \\ - ] + / ;
599+ this . rePlainSelectorEscaped = / ^ [ # . ] (?: \\ [ 0 - 9 A - F a - f ] + | \\ .| \w | - ) + / ;
599600 this . rePlainSelectorEx = / ^ [ ^ # . \[ ( ] + ( [ # . ] [ \w - ] + ) / ;
601+ this . reEscapeSequence = / \\ ( [ 0 - 9 A - F a - f ] + | .) / g;
600602 this . reHighLow = / ^ [ a - z ] * \[ (?: a l t | t i t l e ) = " [ ^ " ] + " \] $ / ;
601603 this . reHighMedium = / ^ \[ h r e f \^ = " h t t p s ? : \/ \/ ( [ ^ " ] { 8 } ) [ ^ " ] * " \] $ / ;
602604 this . reScriptSelector = / ^ s c r i p t : ( c o n t a i n s | i n j e c t ) \( ( .+ ) \) $ / ;
@@ -786,6 +788,40 @@ FilterContainer.prototype.isValidSelector = (function() {
786788
787789/******************************************************************************/
788790
791+ // https://github.com/gorhill/uBlock/issues/1668
792+ // The key must be literal: unescape escaped CSS before extracting key.
793+ // It's an uncommon case, so it's best to unescape only when needed.
794+
795+ FilterContainer . prototype . keyFromSelector = function ( selector ) {
796+ var matches = this . rePlainSelector . exec ( selector ) ;
797+ if ( matches === null ) { return ; }
798+ var key = matches [ 0 ] ;
799+ if ( key . indexOf ( '\\' ) === - 1 ) {
800+ return key ;
801+ }
802+ key = '' ;
803+ matches = this . rePlainSelectorEscaped . exec ( selector ) ;
804+ if ( matches === null ) { return ; }
805+ var escaped = matches [ 0 ] ,
806+ beg = 0 ;
807+ this . reEscapeSequence . lastIndex = 0 ;
808+ for ( ; ; ) {
809+ matches = this . reEscapeSequence . exec ( escaped ) ;
810+ if ( matches === null ) {
811+ return key + escaped . slice ( beg ) ;
812+ }
813+ key += escaped . slice ( beg , matches . index ) ;
814+ beg = this . reEscapeSequence . lastIndex ;
815+ if ( matches [ 1 ] . length === 1 ) {
816+ key += matches [ 1 ] ;
817+ } else {
818+ key += String . fromCharCode ( parseInt ( matches [ 1 ] , 16 ) ) ;
819+ }
820+ }
821+ } ;
822+
823+ /******************************************************************************/
824+
789825FilterContainer . prototype . compile = function ( s , out ) {
790826 var parsed = this . parser . parse ( s ) ;
791827 if ( parsed . cosmetic === false ) {
@@ -846,23 +882,21 @@ FilterContainer.prototype.compileGenericSelector = function(parsed, out) {
846882FilterContainer . prototype . compileGenericHideSelector = function ( parsed , out ) {
847883 var selector = parsed . suffix ,
848884 type = selector . charAt ( 0 ) ,
849- matches ;
885+ key , matches ;
850886
851887 if ( type === '#' || type === '.' ) {
852- matches = this . rePlainSelector . exec ( selector ) ;
853- if ( matches === null ) {
854- return ;
855- }
888+ key = this . keyFromSelector ( selector ) ;
889+ if ( key === undefined ) { return ; }
856890 // Single-CSS rule: no need to test for whether the selector
857891 // is valid, the regex took care of this. Most generic selector falls
858892 // into that category.
859- if ( matches [ 0 ] === selector ) {
860- out . push ( 'c\vlg\v' + matches [ 0 ] ) ;
893+ if ( key === selector ) {
894+ out . push ( 'c\vlg\v' + key ) ;
861895 return ;
862896 }
863- // Many- CSS rules
897+ // Composite CSS rule.
864898 if ( this . isValidSelector ( selector ) ) {
865- out . push ( 'c\vlg+\v' + matches [ 0 ] + '\v' + selector ) ;
899+ out . push ( 'c\vlg+\v' + key + '\v' + selector ) ;
866900 }
867901 return ;
868902 }
0 commit comments