44 */
55define ( [ ] , function ( ) {
66 async function checkFavicon ( source ) {
7+ if ( typeof Promise . any !== 'function' ) {
8+ Promise . any = ( promises ) => {
9+ return new Promise ( ( resolve , reject ) => {
10+ let hasResolved = false ;
11+ let processedPromises = 0 ;
12+ const rejectionReasons = [ ] ;
13+ const resolveOnce = ( value ) => {
14+ if ( ! hasResolved ) {
15+ hasResolved = true ;
16+ resolve ( value ) ;
17+ }
18+ } ;
19+ const rejectionCheck = ( reason ) => {
20+ rejectionReasons . push ( reason ) ;
21+ if ( rejectionReasons . length >= processedPromises ) {
22+ reject ( rejectionReasons ) ;
23+ }
24+ } ;
25+ for ( const promise of promises ) {
26+ processedPromises ++ ;
27+ promise . then ( ( result ) => {
28+ resolveOnce ( result ) ;
29+ } ) . catch ( ( reason ) => {
30+ rejectionCheck ( reason ) ;
31+ } ) ;
32+ }
33+ } ) ;
34+ } ;
35+ }
36+
737 return new Promise ( ( resolve , reject ) => {
838 const baseUrl = new URL ( source . get ( 'base' ) ) ;
939
@@ -24,60 +54,72 @@ define([], function () {
2454 reject ( 'timeout' ) ;
2555 } ;
2656 xhr . onloadend = ( ) => {
27- if ( xhr . readyState !== 4 ) {
57+ if ( xhr . readyState !== XMLHttpRequest . DONE ) {
2858 reject ( 'network error' ) ;
2959 return ;
3060 }
3161 if ( xhr . status !== 200 ) {
32- reject ( 'non-200' ) ;
62+ reject ( 'Encountered non-200 response trying to parse ' + baseUrl . origin ) ;
3363 return ;
3464 }
3565 const baseDocumentContents = xhr . responseText . replace ( / < b o d y ( .* ?) < \/ b o d y > / gm, '' ) ;
3666 const baseDocument = new DOMParser ( ) . parseFromString ( baseDocumentContents , 'text/html' ) ;
37- const iconLinks = [ ...baseDocument . querySelectorAll ( 'link[rel*="icon"][href]' ) ] ;
67+ const linkElements = [ ...baseDocument . querySelectorAll ( 'link[rel*="icon"][href]' ) ] ;
68+
69+ const links = [ ] ;
3870
39- iconLinks . some ( ( link ) => {
40- favicon = link . getAttribute ( 'href' ) ;
71+ linkElements . forEach ( ( link ) => {
72+ const favicon = link . getAttribute ( 'href' ) ;
4173 if ( ! favicon ) {
42- return false ;
74+ return ;
4375 }
4476 if ( favicon . includes ( 'svg' ) ) {
45- return false ;
77+ return ;
4678 }
47- if ( favicon . startsWith ( '/' ) ) {
48- const prefix = favicon . startsWith ( '//' ) ? 'https:' : baseUrl . origin ;
49- resolve ( prefix + favicon ) ;
50- return true ;
79+ if ( favicon . startsWith ( 'http' ) ) {
80+ links . push ( favicon ) ;
5181 }
52- if ( ! favicon . startsWith ( 'http' ) ) {
53- resolve ( baseUrl . origin + '/' + favicon ) ;
54- return true ;
82+ if ( favicon . startsWith ( '//' ) ) {
83+ links . push ( baseUrl . protocol + favicon ) ;
84+ return ;
85+ }
86+ if ( favicon . startsWith ( 'data' ) ) {
87+ links . push ( favicon ) ;
88+ return ;
89+ }
90+ if ( favicon . startsWith ( '/' ) ) {
91+ links . push ( baseUrl . origin + favicon ) ;
92+ return ;
5593 }
5694
57- resolve ( favicon ) ;
95+ links . push ( baseUrl . origin + '/' + favicon ) ;
5896 } ) ;
5997
60- resolve ( baseUrl . origin + '/favicon.ico' ) ;
98+ links . push ( baseUrl . origin + '/favicon.ico' ) ;
99+
100+ resolve ( links ) ;
61101 } ;
62102
63- xhr . open ( 'GET' , baseUrl ) ;
103+ xhr . open ( 'GET' , baseUrl . origin ) ;
64104 xhr . timeout = 1000 * 30 ;
65105 xhr . send ( ) ;
66106 } ) ;
67107 }
68108
69109 getFaviconAddress ( baseUrl )
70- . then ( ( faviconAddress ) => {
71- toDataURI ( faviconAddress )
72- . then ( response => {
73- resolve ( response ) ;
74- } )
75- . catch ( ( ) => {
76- reject ( ) ;
77- } ) ;
110+ . then ( ( faviconAddresses ) => {
111+ const promises = faviconAddresses . map ( ( favicon ) => {
112+ return toDataURI ( favicon ) ;
113+ } ) ;
114+ Promise . any ( promises )
115+ . then ( ( dataURI ) => {
116+ resolve ( dataURI ) ;
117+ } ) . catch ( ( errors ) => {
118+ reject ( errors ) ;
119+ } ) ;
78120 } )
79- . catch ( ( ) => {
80- reject ( ) ;
121+ . catch ( ( error ) => {
122+ reject ( error ) ;
81123 } ) ;
82124 } ) ;
83125 }
@@ -89,32 +131,38 @@ define([], function () {
89131 // * @constructor
90132 // * @extends Object
91133 // */
92- function toDataURI ( url ) {
93- return new Promise ( function ( resolve , reject ) {
134+ function toDataURI ( favicon ) {
135+ return new Promise ( ( resolve , reject ) => {
136+ if ( favicon . startsWith ( 'data' ) ) {
137+ resolve ( favicon ) ;
138+ }
94139 const xhr = new window . XMLHttpRequest ( ) ;
95140 xhr . responseType = 'arraybuffer' ;
96- xhr . onerror = function ( ) {
97- reject ( '[modules/toDataURI] XMLHttpRequest error on' , url ) ;
141+ xhr . onerror = ( ) => {
142+ reject ( '[modules/toDataURI] error on: ' + favicon ) ;
98143 } ;
99144 xhr . ontimeout = ( ) => {
100145 reject ( 'timeout' ) ;
101146 } ;
102147 xhr . onloadend = function ( ) {
103148 if ( xhr . readyState !== XMLHttpRequest . DONE ) {
104- reject ( '[modules/toDataURI] XMLHttpRequest error on' , url ) ;
149+ reject ( '[modules/toDataURI] network error on: ' + favicon ) ;
105150 return ;
106151 }
107152 if ( xhr . status !== 200 ) {
108- reject ( '[modules/toDataURI] XMLHttpRequest error on' , url ) ;
153+ reject ( '[modules/toDataURI] non-200 on: ' + favicon ) ;
109154 return ;
110155 }
111156 const type = xhr . getResponseHeader ( 'content-type' ) ;
112157 if ( ! ~ type . indexOf ( 'image' ) || xhr . response . byteLength < 10 ) {
113- reject ( '[modules/toDataURI] Not an image on' , url ) ;
158+ reject ( '[modules/toDataURI] Not an image on: ' + favicon ) ;
114159 return ;
115160 }
116- let expires = 0 ;
161+ const imgData = 'data:' + type + ';base64,' + AB2B64 ( xhr . response ) ;
162+
117163 const expiresHeader = xhr . getResponseHeader ( 'expires' ) ;
164+
165+ let expires = 0 ;
118166 if ( expiresHeader ) {
119167 expires = parseInt ( Math . round ( ( new Date ( expiresHeader ) ) . getTime ( ) / 1000 ) ) ;
120168 } else {
@@ -127,10 +175,9 @@ define([], function () {
127175 expires = parseInt ( Math . round ( ( new Date ( ) ) . getTime ( ) / 1000 ) ) + maxAge ;
128176 }
129177
130- const imgData = 'data:' + type + ';base64,' + AB2B64 ( xhr . response ) ;
131178 resolve ( { favicon : imgData , faviconExpires : expires } ) ;
132179 } ;
133- xhr . open ( 'GET' , url ) ;
180+ xhr . open ( 'GET' , favicon ) ;
134181 xhr . timeout = 1000 * 30 ;
135182 xhr . send ( ) ;
136183 } ) ;
0 commit comments