@@ -353,23 +353,27 @@ export function parseSrc(input) {
353353 throw new Error ( "Must be non-empty" ) ;
354354 }
355355
356- let startOffset = 0 ;
357- let value = input ;
356+ let start = 0 ;
357+ for ( ; start < input . length && isASCIIWhitespace ( input [ start ] ) ; start ++ ) ;
358358
359- while ( isASCIIWhitespace ( value . substring ( 0 , 1 ) ) ) {
360- startOffset += 1 ;
361- value = value . substring ( 1 , value . length ) ;
359+ if ( start === input . length ) {
360+ throw new Error ( "Must be non-empty" ) ;
362361 }
363362
364- while ( isASCIIWhitespace ( value . substring ( value . length - 1 , value . length ) ) ) {
365- value = value . substring ( 0 , value . length - 1 ) ;
366- }
363+ let end = input . length - 1 ;
364+ for ( ; end > - 1 && isASCIIWhitespace ( input [ end ] ) ; end -- ) ;
365+ end += 1 ;
367366
368- if ( ! value ) {
369- throw new Error ( "Must be non-empty" ) ;
367+ let value = input ;
368+ if ( start !== 0 || end !== value . length ) {
369+ value = value . substring ( start , end ) ;
370+
371+ if ( ! value ) {
372+ throw new Error ( "Must be non-empty" ) ;
373+ }
370374 }
371375
372- return { value, startOffset } ;
376+ return { value, startOffset : start } ;
373377}
374378
375379const WINDOWS_ABS_PATH_REGEXP = / ^ [ a - z A - Z ] : [ \\ / ] | ^ \\ \\ / ;
@@ -572,7 +576,7 @@ function linkHrefFilter(tag, attribute, attributes) {
572576
573577 rel = rel . toLowerCase ( ) ;
574578
575- const usedRels = rel . split ( " " ) . filter ( ( value ) => value ) ;
579+ const usedRels = rel . split ( " " ) . filter ( Boolean ) ;
576580 const allowedRels = [
577581 "stylesheet" ,
578582 "icon" ,
@@ -585,7 +589,7 @@ function linkHrefFilter(tag, attribute, attributes) {
585589 "preload" ,
586590 ] ;
587591
588- return allowedRels . filter ( ( value ) => usedRels . includes ( value ) ) . length > 0 ;
592+ return allowedRels . some ( ( value ) => usedRels . includes ( value ) ) ;
589593}
590594
591595const META = new Map ( [
@@ -695,7 +699,16 @@ export function srcType(options) {
695699 ) ;
696700 }
697701
698- source = c0ControlCodesExclude ( source ) ;
702+ try {
703+ source = c0ControlCodesExclude ( source ) ;
704+ } catch ( error ) {
705+ throw new HtmlSourceError (
706+ `Bad value for attribute "${ options . attribute } " on element "${ options . tag } ": ${ error . message } ` ,
707+ options . attributeStartOffset ,
708+ options . attributeEndOffset ,
709+ options . html
710+ ) ;
711+ }
699712
700713 if ( ! isUrlRequestable ( source . value ) ) {
701714 return [ ] ;
@@ -726,7 +739,16 @@ export function srcsetType(options) {
726739 sourceSet . forEach ( ( sourceItem ) => {
727740 let { source } = sourceItem ;
728741
729- source = c0ControlCodesExclude ( source ) ;
742+ try {
743+ source = c0ControlCodesExclude ( source ) ;
744+ } catch ( error ) {
745+ throw new HtmlSourceError (
746+ `Bad value for attribute "${ options . attribute } " on element "${ options . tag } ": ${ error . message } ` ,
747+ options . attributeStartOffset ,
748+ options . attributeEndOffset ,
749+ options . html
750+ ) ;
751+ }
730752
731753 if ( ! isUrlRequestable ( source . value ) ) {
732754 return false ;
@@ -778,7 +800,16 @@ function metaContentType(options) {
778800 ) ;
779801 }
780802
781- source = c0ControlCodesExclude ( source ) ;
803+ try {
804+ source = c0ControlCodesExclude ( source ) ;
805+ } catch ( error ) {
806+ throw new HtmlSourceError (
807+ `Bad value for attribute "icon-uri" on element "${ options . tag } ": ${ error . message } ` ,
808+ options . attributeStartOffset ,
809+ options . attributeEndOffset ,
810+ options . html
811+ ) ;
812+ }
782813
783814 ( { value } = source ) ;
784815 startOffset += source . startOffset ;
@@ -805,7 +836,7 @@ function metaContentType(options) {
805836// let source;
806837//
807838// try {
808- // source = parseSrc (options.value);
839+ // source = trimASCIIWhitespace (options.value);
809840// } catch (error) {
810841// throw new HtmlSourceError(
811842// `Bad value for attribute "${options.attribute}" on element "${options.tag}": ${error.message}`,
@@ -815,7 +846,16 @@ function metaContentType(options) {
815846// );
816847// }
817848//
818- // source = c0ControlCodesExclude(source);
849+ // try {
850+ // source = c0ControlCodesExclude(source);
851+ // } catch (error) {
852+ // throw new HtmlSourceError(
853+ // `Bad value for attribute "${options.attribute}" on element "${options.tag}": ${error.message}`,
854+ // options.attributeStartOffset,
855+ // options.attributeEndOffset,
856+ // options.html
857+ // );
858+ // }
819859//
820860// if (!isUrlRequestable(source.value)) {
821861// return [];
@@ -1062,17 +1102,13 @@ function normalizeSourcesList(sources) {
10621102 for ( const source of sources ) {
10631103 if ( source === "..." ) {
10641104 for ( const [ tag , attributes ] of defaultSources . entries ( ) ) {
1065- let newAttributes ;
1066-
10671105 const existingAttributes = result . get ( tag ) ;
10681106
10691107 if ( existingAttributes ) {
1070- newAttributes = new Map ( [ ... existingAttributes , ... attributes ] ) ;
1108+ attributes . forEach ( ( [ k , v ] ) => existingAttributes . set ( k , v ) ) ;
10711109 } else {
1072- newAttributes = new Map ( attributes ) ;
1110+ result . set ( tag , new Map ( attributes ) ) ;
10731111 }
1074-
1075- result . set ( tag , newAttributes ) ;
10761112 }
10771113
10781114 // eslint-disable-next-line no-continue
@@ -1084,10 +1120,6 @@ function normalizeSourcesList(sources) {
10841120 tag = tag . toLowerCase ( ) ;
10851121 attribute = attribute . toLowerCase ( ) ;
10861122
1087- if ( ! result . has ( tag ) ) {
1088- result . set ( tag , new Map ( ) ) ;
1089- }
1090-
10911123 let typeFn ;
10921124
10931125 // eslint-disable-next-line default-case
@@ -1100,7 +1132,14 @@ function normalizeSourcesList(sources) {
11001132 break ;
11011133 }
11021134
1103- result . get ( tag ) . set ( attribute , {
1135+ let attrMap = result . get ( tag ) ;
1136+
1137+ if ( ! attrMap ) {
1138+ attrMap = new Map ( ) ;
1139+ result . set ( tag , attrMap ) ;
1140+ }
1141+
1142+ attrMap . set ( attribute , {
11041143 type : typeFn ,
11051144 filter : source . filter ,
11061145 } ) ;
@@ -1265,26 +1304,32 @@ function isASCIIC0group(character) {
12651304}
12661305
12671306export function c0ControlCodesExclude ( source ) {
1268- let { value, startOffset } = source ;
1307+ let { value } = source ;
12691308
12701309 if ( ! value ) {
12711310 throw new Error ( "Must be non-empty" ) ;
12721311 }
12731312
1274- while ( isASCIIC0group ( value . substring ( 0 , 1 ) ) ) {
1275- startOffset += 1 ;
1276- value = value . substring ( 1 , value . length ) ;
1277- }
1313+ let start = 0 ;
1314+ for ( ; start < value . length && isASCIIC0group ( value [ start ] ) ; start ++ ) ;
12781315
1279- while ( isASCIIC0group ( value . substring ( value . length - 1 , value . length ) ) ) {
1280- value = value . substring ( 0 , value . length - 1 ) ;
1316+ if ( start === value . length ) {
1317+ throw new Error ( "Must be non-empty" ) ;
12811318 }
12821319
1283- if ( ! value ) {
1284- throw new Error ( "Must be non-empty" ) ;
1320+ let end = value . length - 1 ;
1321+ for ( ; end > - 1 && isASCIIC0group ( value [ end ] ) ; end -- ) ;
1322+ end += 1 ;
1323+
1324+ if ( start !== 0 || end !== value . length ) {
1325+ value = value . substring ( start , end ) ;
1326+
1327+ if ( ! value ) {
1328+ throw new Error ( "Must be non-empty" ) ;
1329+ }
12851330 }
12861331
1287- return { value, startOffset } ;
1332+ return { value, startOffset : source . startOffset + start } ;
12881333}
12891334
12901335export function traverse ( root , callback ) {
0 commit comments