1- /*global Tokens, TokenStreamBase*/
1+ /*global PropertyValuePart, Tokens, TokenStreamBase*/
22
33var h = / ^ [ 0 - 9 a - f A - F ] $ / ,
4- // nonascii = /^[\u00A0-\uFFFF]$/,
4+ nonascii = / ^ [ \u00A0 - \uFFFF ] $ / ,
55 nl = / \n | \r \n | \r | \f / ,
66 whitespace = / \u0009 | \u000a | \u000c | \u000d | \u0020 / ;
77
@@ -221,7 +221,7 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
221221 */
222222 case "\\" :
223223 if ( / [ ^ \r \n \f ] / . test ( reader . peek ( ) ) ) {
224- token = this . identOrFunctionToken ( c , startLine , startCol ) ;
224+ token = this . identOrFunctionToken ( this . readEscape ( c , true ) , startLine , startCol ) ;
225225 } else {
226226 token = this . charToken ( c , startLine , startCol ) ;
227227 }
@@ -521,18 +521,22 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
521521 var reader = this . _reader ,
522522 ident = this . readName ( first ) ,
523523 tt = Tokens . IDENT ,
524- uriFns = [ "url(" , "url-prefix(" , "domain(" ] ;
524+ uriFns = [ "url(" , "url-prefix(" , "domain(" ] ,
525+ uri ;
525526
526527 //if there's a left paren immediately after, it's a URI or function
527528 if ( reader . peek ( ) === "(" ) {
528529 ident += reader . read ( ) ;
529530 if ( uriFns . indexOf ( ident . toLowerCase ( ) ) > - 1 ) {
530- tt = Tokens . URI ;
531- ident = this . readURI ( ident ) ;
532-
533- //didn't find a valid URL or there's no closing paren
534- if ( uriFns . indexOf ( ident . toLowerCase ( ) ) > - 1 ) {
531+ reader . mark ( ) ;
532+ uri = this . readURI ( ident ) ;
533+ if ( uri === null ) {
534+ //didn't find a valid URL or there's no closing paren
535+ reader . reset ( ) ;
535536 tt = Tokens . FUNCTION ;
537+ } else {
538+ tt = Tokens . URI ;
539+ ident = uri ;
536540 }
537541 } else {
538542 tt = Tokens . FUNCTION ;
@@ -876,18 +880,20 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
876880
877881 return number ;
878882 } ,
883+
884+ // returns null w/o resetting reader if string is invalid.
879885 readString : function ( ) {
880886 var token = this . stringToken ( this . _reader . read ( ) , 0 , 0 ) ;
881- return token . type === Tokens . INVALID ? "" : token . value ;
887+ return token . type === Tokens . INVALID ? null : token . value ;
882888 } ,
889+
890+ // returns null w/o resetting reader if URI is invalid.
883891 readURI : function ( first ) {
884892 var reader = this . _reader ,
885893 uri = first ,
886894 inner = "" ,
887895 c = reader . peek ( ) ;
888896
889- reader . mark ( ) ;
890-
891897 //skip whitespace before
892898 while ( c && isWhitespace ( c ) ) {
893899 reader . read ( ) ;
@@ -897,8 +903,11 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
897903 //it's a string
898904 if ( c === "'" || c === "\"" ) {
899905 inner = this . readString ( ) ;
906+ if ( inner !== null ) {
907+ inner = PropertyValuePart . parseString ( inner ) ;
908+ }
900909 } else {
901- inner = this . readURL ( ) ;
910+ inner = this . readUnquotedURL ( ) ;
902911 }
903912
904913 c = reader . peek ( ) ;
@@ -910,46 +919,60 @@ TokenStream.prototype = mix(new TokenStreamBase(), {
910919 }
911920
912921 //if there was no inner value or the next character isn't closing paren, it's not a URI
913- if ( inner === "" || c !== ")" ) {
914- uri = first ;
915- reader . reset ( ) ;
922+ if ( inner === null || c !== ")" ) {
923+ uri = null ;
916924 } else {
917- uri += inner + reader . read ( ) ;
925+ // Ensure argument to URL is always double-quoted
926+ // (This simplifies later processing in PropertyValuePart.)
927+ uri += PropertyValuePart . serializeString ( inner ) + reader . read ( ) ;
918928 }
919929
920930 return uri ;
921931 } ,
922- readURL : function ( ) {
932+ // This method never fails, although it may return an empty string.
933+ readUnquotedURL : function ( first ) {
923934 var reader = this . _reader ,
924- url = "" ,
925- c = reader . peek ( ) ;
935+ url = first || "" ,
936+ c ;
926937
927- //TODO: Check for escape and nonascii
928- while ( / ^ [ ! # $ % & \\ * - ~ ] $ / . test ( c ) ) {
929- url += reader . read ( ) ;
930- c = reader . peek ( ) ;
938+ for ( c = reader . peek ( ) ; c ; c = reader . peek ( ) ) {
939+ // Note that the grammar at
940+ // https://www.w3.org/TR/CSS2/grammar.html#scanner
941+ // incorrectly includes the backslash character in the
942+ // `url` production, although it is correctly omitted in
943+ // the `baduri1` production.
944+ if ( nonascii . test ( c ) || / ^ [ \- ! # $ % & * -\[ \] - ~ ] $ / . test ( c ) ) {
945+ url += c ;
946+ reader . read ( ) ;
947+ } else if ( c === '\\' ) {
948+ if ( / ^ [ ^ \r \n \f ] $ / . test ( reader . peek ( 2 ) ) ) {
949+ url += this . readEscape ( reader . read ( ) , true ) ;
950+ } else {
951+ break ; // bad escape sequence.
952+ }
953+ } else {
954+ break ; // bad character
955+ }
931956 }
932957
933958 return url ;
934-
935959 } ,
960+
936961 readName : function ( first ) {
937962 var reader = this . _reader ,
938963 ident = first || "" ,
939- c = reader . peek ( ) ;
964+ c ;
940965
941- while ( true ) {
966+ for ( c = reader . peek ( ) ; c ; c = reader . peek ( ) ) {
942967 if ( c === "\\" ) {
943968 if ( / ^ [ ^ \r \n \f ] $ / . test ( reader . peek ( 2 ) ) ) {
944969 ident += this . readEscape ( reader . read ( ) , true ) ;
945- c = reader . peek ( ) ;
946970 } else {
947971 // Bad escape sequence.
948972 break ;
949973 }
950- } else if ( c && isNameChar ( c ) ) {
974+ } else if ( isNameChar ( c ) ) {
951975 ident += reader . read ( ) ;
952- c = reader . peek ( ) ;
953976 } else {
954977 break ;
955978 }
0 commit comments