@@ -338,31 +338,82 @@ function getPdfFilenameFromUrl(url, defaultFilename = "document.pdf") {
338338 warn ( 'getPdfFilenameFromUrl: ignore "data:"-URL for performance reasons.' ) ;
339339 return defaultFilename ;
340340 }
341- const reURI = / ^ (?: (?: [ ^ : ] + : ) ? \/ \/ [ ^ / ] + ) ? ( [ ^ ? # ] * ) ( \? [ ^ # ] * ) ? ( # .* ) ? $ / ;
342- // SCHEME HOST 1.PATH 2.QUERY 3.REF
343- // Pattern to get last matching NAME.pdf
344- const reFilename = / [ ^ / ? # = ] + \. p d f \b (? ! .* \. p d f \b ) / i;
345- const splitURI = reURI . exec ( url ) ;
346- let suggestedFilename =
347- reFilename . exec ( splitURI [ 1 ] ) ||
348- reFilename . exec ( splitURI [ 2 ] ) ||
349- reFilename . exec ( splitURI [ 3 ] ) ;
350- if ( suggestedFilename ) {
351- suggestedFilename = suggestedFilename [ 0 ] ;
352- if ( suggestedFilename . includes ( "%" ) ) {
353- // URL-encoded %2Fpath%2Fto%2Ffile.pdf should be file.pdf
341+
342+ const getURL = urlString => {
343+ try {
344+ return new URL ( urlString ) ;
345+ } catch {
354346 try {
355- suggestedFilename = reFilename . exec (
356- decodeURIComponent ( suggestedFilename )
357- ) [ 0 ] ;
347+ return new URL ( decodeURIComponent ( urlString ) ) ;
358348 } catch {
359- // Possible (extremely rare) errors:
360- // URIError "Malformed URI", e.g. for "%AA.pdf"
361- // TypeError "null has no properties", e.g. for "%2F.pdf"
349+ try {
350+ // Attempt to parse the URL using the document's base URI.
351+ return new URL ( urlString , "https://foo.bar" ) ;
352+ } catch {
353+ try {
354+ return new URL ( decodeURIComponent ( urlString ) , "https://foo.bar" ) ;
355+ } catch {
356+ return null ;
357+ }
358+ }
359+ }
360+ }
361+ } ;
362+
363+ const newURL = getURL ( url ) ;
364+ if ( ! newURL ) {
365+ // If the URL is invalid, return the default filename.
366+ return defaultFilename ;
367+ }
368+
369+ const decode = name => {
370+ try {
371+ let decoded = decodeURIComponent ( name ) ;
372+ if ( decoded . includes ( "/" ) ) {
373+ decoded = decoded . split ( "/" ) . at ( - 1 ) ;
374+ if ( decoded . test ( / ^ \. p d f $ / i) ) {
375+ return decoded ;
376+ }
377+ return name ;
378+ }
379+ return decoded ;
380+ } catch {
381+ return name ;
382+ }
383+ } ;
384+
385+ const pdfRegex = / \. p d f $ / i;
386+ const filename = newURL . pathname . split ( "/" ) . at ( - 1 ) ;
387+ if ( pdfRegex . test ( filename ) ) {
388+ return decode ( filename ) ;
389+ }
390+
391+ if ( newURL . searchParams . size > 0 ) {
392+ const values = Array . from ( newURL . searchParams . values ( ) ) . reverse ( ) ;
393+ for ( const value of values ) {
394+ if ( pdfRegex . test ( value ) ) {
395+ // If any of the search parameters ends with ".pdf", return it.
396+ return decode ( value ) ;
397+ }
398+ }
399+ const keys = Array . from ( newURL . searchParams . keys ( ) ) . reverse ( ) ;
400+ for ( const key of keys ) {
401+ if ( pdfRegex . test ( key ) ) {
402+ // If any of the search parameter keys ends with ".pdf", return it.
403+ return decode ( key ) ;
362404 }
363405 }
364406 }
365- return suggestedFilename || defaultFilename ;
407+
408+ if ( newURL . hash ) {
409+ const reFilename = / [ ^ / ? # = ] + \. p d f \b (? ! .* \. p d f \b ) / i;
410+ const hashFilename = reFilename . exec ( newURL . hash ) ;
411+ if ( hashFilename ) {
412+ return decode ( hashFilename [ 0 ] ) ;
413+ }
414+ }
415+
416+ return defaultFilename ;
366417}
367418
368419class StatTimer {
0 commit comments