1- import MIMEType from "whatwg-mimetype" ;
21import { atob } from "abab" ;
32
3+ const removeLeadingAndTrailingHTTPWhitespace = ( string ) =>
4+ string . replace ( / ^ [ \t \n \r ] + / , "" ) . replace ( / [ \t \n \r ] + $ / , "" ) ;
5+
6+ const removeTrailingHTTPWhitespace = ( string ) =>
7+ string . replace ( / [ \t \n \r ] + $ / , "" ) ;
8+
9+ const isHTTPWhitespaceChar = ( char ) =>
10+ char === " " || char === "\t" || char === "\n" || char === "\r" ;
11+
12+ const solelyContainsHTTPTokenCodePoints = ( string ) =>
13+ / ^ [ - ! # $ % & ' * + . ^ _ ` | ~ A - Z a - z 0 - 9 ] * $ / . test ( string ) ;
14+
15+ const soleyContainsHTTPQuotedStringTokenCodePoints = ( string ) =>
16+ / ^ [ \t \u0020 - \u007E \u0080 - \u00FF ] * $ / . test ( string ) ;
17+
18+ const asciiLowercase = ( string ) =>
19+ string . replace ( / [ A - Z ] / g, ( l ) => l . toLowerCase ( ) ) ;
20+
21+ const collectAnHTTPQuotedString = ( input , position ) => {
22+ let value = "" ;
23+
24+ // eslint-disable-next-line no-param-reassign
25+ position += 1 ;
26+
27+ // eslint-disable-next-line no-constant-condition
28+ while ( true ) {
29+ while (
30+ position < input . length &&
31+ input [ position ] !== '"' &&
32+ input [ position ] !== "\\"
33+ ) {
34+ value += input [ position ] ;
35+ // eslint-disable-next-line no-param-reassign
36+ position += 1 ;
37+ }
38+
39+ if ( position >= input . length ) {
40+ break ;
41+ }
42+
43+ const quoteOrBackslash = input [ position ] ;
44+
45+ // eslint-disable-next-line no-param-reassign
46+ position += 1 ;
47+
48+ if ( quoteOrBackslash === "\\" ) {
49+ if ( position >= input . length ) {
50+ value += "\\" ;
51+ break ;
52+ }
53+
54+ value += input [ position ] ;
55+ // eslint-disable-next-line no-param-reassign
56+ position += 1 ;
57+ } else {
58+ break ;
59+ }
60+ }
61+
62+ return [ value , position ] ;
63+ } ;
64+
465function isASCIIHex ( c ) {
566 return (
667 ( c >= 0x30 && c <= 0x39 ) ||
@@ -56,14 +117,16 @@ export default function parseDataUrl(stringInput) {
56117 const input = parsedUrl . toString ( ) . substring ( 5 ) ;
57118
58119 let position = 0 ;
59- let mimeType = "" ;
120+ let mediaType = "" ;
60121
61122 while ( position < input . length && input [ position ] !== "," ) {
62- mimeType += input [ position ] ;
123+ mediaType += input [ position ] ;
63124 position += 1 ;
64125 }
65126
66- mimeType = mimeType . replace ( / ^ [ \t \n \f \r ] + / , "" ) . replace ( / [ \t \n \f \r ] + $ / , "" ) ;
127+ mediaType = mediaType
128+ . replace ( / ^ [ \t \n \f \r ] + / , "" )
129+ . replace ( / [ \t \n \f \r ] + $ / , "" ) ;
67130
68131 if ( position === input . length ) {
69132 return null ;
@@ -76,7 +139,9 @@ export default function parseDataUrl(stringInput) {
76139 let body = Buffer . from ( percentDecodeBytes ( Buffer . from ( encodedBody , "utf-8" ) ) ) ;
77140
78141 // Can't use /i regexp flag because it isn't restricted to ASCII.
79- const mimeTypeBase64MatchResult = / ( .* ) ; * [ B b ] [ A a ] [ S s ] [ E e ] 6 4 $ / . exec ( mimeType ) ;
142+ const mimeTypeBase64MatchResult = / ( .* ) ; * [ B b ] [ A a ] [ S s ] [ E e ] 6 4 $ / . exec (
143+ mediaType
144+ ) ;
80145
81146 if ( mimeTypeBase64MatchResult ) {
82147 const stringBody = body . toString ( "binary" ) ;
@@ -88,20 +153,141 @@ export default function parseDataUrl(stringInput) {
88153
89154 body = Buffer . from ( asString , "binary" ) ;
90155
91- [ , mimeType ] = mimeTypeBase64MatchResult ;
156+ [ , mediaType ] = mimeTypeBase64MatchResult ;
92157 }
93158
94- if ( mimeType . startsWith ( ";" ) ) {
95- mimeType = `text/plain ${ mimeType } ` ;
159+ if ( mediaType . startsWith ( ";" ) ) {
160+ mediaType = `text/plain ${ mediaType } ` ;
96161 }
97162
98- let mimeTypeRecord ;
163+ const result = {
164+ // eslint-disable-next-line no-undefined
165+ type : undefined ,
166+ // eslint-disable-next-line no-undefined
167+ subtype : undefined ,
168+ parameters : new Map ( ) ,
169+ isBase64 : Boolean ( mimeTypeBase64MatchResult ) ,
170+ body,
171+ } ;
99172
100- try {
101- mimeTypeRecord = new MIMEType ( mimeType ) ;
102- } catch ( e ) {
103- mimeTypeRecord = new MIMEType ( "text/plain;charset=US-ASCII" ) ;
173+ if ( ! mediaType ) {
174+ return result ;
175+ }
176+
177+ const inputMediaType = removeLeadingAndTrailingHTTPWhitespace ( mediaType ) ;
178+
179+ let positionMediaType = 0 ;
180+ let type = "" ;
181+
182+ while (
183+ positionMediaType < inputMediaType . length &&
184+ inputMediaType [ positionMediaType ] !== "/"
185+ ) {
186+ type += inputMediaType [ positionMediaType ] ;
187+ positionMediaType += 1 ;
188+ }
189+
190+ if ( type . length === 0 || ! solelyContainsHTTPTokenCodePoints ( type ) ) {
191+ return result ;
192+ }
193+
194+ if ( positionMediaType >= inputMediaType . length ) {
195+ return result ;
196+ }
197+
198+ // Skips past "/"
199+ positionMediaType += 1 ;
200+
201+ let subtype = "" ;
202+
203+ while (
204+ positionMediaType < inputMediaType . length &&
205+ inputMediaType [ positionMediaType ] !== ";"
206+ ) {
207+ subtype += inputMediaType [ positionMediaType ] ;
208+ positionMediaType += 1 ;
209+ }
210+
211+ subtype = removeTrailingHTTPWhitespace ( subtype ) ;
212+
213+ if ( subtype . length === 0 || ! solelyContainsHTTPTokenCodePoints ( subtype ) ) {
214+ return result ;
215+ }
216+
217+ result . type = asciiLowercase ( type ) ;
218+ result . subtype = asciiLowercase ( subtype ) ;
219+
220+ while ( positionMediaType < inputMediaType . length ) {
221+ // Skip past ";"
222+ positionMediaType += 1 ;
223+
224+ while ( isHTTPWhitespaceChar ( inputMediaType [ positionMediaType ] ) ) {
225+ positionMediaType += 1 ;
226+ }
227+
228+ let parameterName = "" ;
229+
230+ while (
231+ positionMediaType < inputMediaType . length &&
232+ inputMediaType [ positionMediaType ] !== ";" &&
233+ inputMediaType [ positionMediaType ] !== "="
234+ ) {
235+ parameterName += inputMediaType [ positionMediaType ] ;
236+ positionMediaType += 1 ;
237+ }
238+
239+ parameterName = asciiLowercase ( parameterName ) ;
240+
241+ if ( positionMediaType < inputMediaType . length ) {
242+ if ( inputMediaType [ positionMediaType ] === ";" ) {
243+ // eslint-disable-next-line no-continue
244+ continue ;
245+ }
246+
247+ // Skip past "="
248+ positionMediaType += 1 ;
249+ }
250+
251+ let parameterValue = "" ;
252+
253+ if ( inputMediaType [ positionMediaType ] === '"' ) {
254+ [ parameterValue , positionMediaType ] = collectAnHTTPQuotedString (
255+ inputMediaType ,
256+ positionMediaType
257+ ) ;
258+
259+ while (
260+ positionMediaType < inputMediaType . length &&
261+ inputMediaType [ positionMediaType ] !== ";"
262+ ) {
263+ positionMediaType += 1 ;
264+ }
265+ } else {
266+ while (
267+ positionMediaType < inputMediaType . length &&
268+ inputMediaType [ positionMediaType ] !== ";"
269+ ) {
270+ parameterValue += inputMediaType [ positionMediaType ] ;
271+ positionMediaType += 1 ;
272+ }
273+
274+ parameterValue = removeTrailingHTTPWhitespace ( parameterValue ) ;
275+
276+ if ( parameterValue === "" ) {
277+ // eslint-disable-next-line no-continue
278+ continue ;
279+ }
280+ }
281+
282+ if (
283+ parameterName . length > 0 &&
284+ solelyContainsHTTPTokenCodePoints ( parameterName ) &&
285+ soleyContainsHTTPQuotedStringTokenCodePoints ( parameterValue ) &&
286+ ! result . parameters . has ( parameterName )
287+ ) {
288+ result . parameters . set ( parameterName , parameterValue ) ;
289+ }
104290 }
105291
106- return { mimeType : mimeTypeRecord , body } ;
292+ return result ;
107293}
0 commit comments