@@ -154,35 +154,52 @@ function getGroupForViewportWidth( viewportWidth, urlMetricGroupStatuses ) {
154
154
* @param {string } currentETag - Current ETag.
155
155
* @param {string } currentUrl - Current URL.
156
156
* @param {URLMetricGroupStatus } urlMetricGroupStatus - URL Metric group status.
157
- * @return {Promise<string> } Session storage key.
157
+ * @param {Logger } logger - Logger.
158
+ * @return {Promise<string|null> } Session storage key for the current URL or null if crypto is not available or caused an error.
158
159
*/
159
160
async function getAlreadySubmittedSessionStorageKey (
160
161
currentETag ,
161
162
currentUrl ,
162
- urlMetricGroupStatus
163
+ urlMetricGroupStatus ,
164
+ { warn, error }
163
165
) {
164
- const message = [
165
- currentETag ,
166
- currentUrl ,
167
- urlMetricGroupStatus . minimumViewportWidth ,
168
- urlMetricGroupStatus . maximumViewportWidth || '' ,
169
- ] . join ( '-' ) ;
166
+ if ( ! window . crypto || ! window . crypto . subtle ) {
167
+ warn (
168
+ 'Unable to generate sessionStorage key for already-submitted URL since crypto is not available, likely due to to the page not being served via HTTPS.'
169
+ ) ;
170
+ return null ;
171
+ }
170
172
171
- /*
172
- * Note that the components are hashed for a couple of reasons:
173
- *
174
- * 1. It results in a consistent length string devoid of any special characters that could cause problems.
175
- * 2. Since the key includes the URL, hashing it avoids potential privacy concerns where the sessionStorage is
176
- * examined to see which URLs the client went to.
177
- *
178
- * The SHA-1 algorithm is chosen since it is the fastest and there is no need for cryptographic security.
179
- */
180
- const msgBuffer = new TextEncoder ( ) . encode ( message ) ;
181
- const hashBuffer = await crypto . subtle . digest ( 'SHA-1' , msgBuffer ) ;
182
- const hashHex = Array . from ( new Uint8Array ( hashBuffer ) )
183
- . map ( ( b ) => b . toString ( 16 ) . padStart ( 2 , '0' ) )
184
- . join ( '' ) ;
185
- return `odSubmitted-${ hashHex } ` ;
173
+ try {
174
+ const message = [
175
+ currentETag ,
176
+ currentUrl ,
177
+ urlMetricGroupStatus . minimumViewportWidth ,
178
+ urlMetricGroupStatus . maximumViewportWidth || '' ,
179
+ ] . join ( '-' ) ;
180
+
181
+ /*
182
+ * Note that the components are hashed for a couple of reasons:
183
+ *
184
+ * 1. It results in a consistent length string devoid of any special characters that could cause problems.
185
+ * 2. Since the key includes the URL, hashing it avoids potential privacy concerns where the sessionStorage is
186
+ * examined to see which URLs the client went to.
187
+ *
188
+ * The SHA-1 algorithm is chosen since it is the fastest and there is no need for cryptographic security.
189
+ */
190
+ const msgBuffer = new TextEncoder ( ) . encode ( message ) ;
191
+ const hashBuffer = await crypto . subtle . digest ( 'SHA-1' , msgBuffer ) ;
192
+ const hashHex = Array . from ( new Uint8Array ( hashBuffer ) )
193
+ . map ( ( b ) => b . toString ( 16 ) . padStart ( 2 , '0' ) )
194
+ . join ( '' ) ;
195
+ return `odSubmitted-${ hashHex } ` ;
196
+ } catch ( err ) {
197
+ error (
198
+ 'Unable to generate sessionStorage key for already-submitted URL due to error:' ,
199
+ err
200
+ ) ;
201
+ return null ;
202
+ }
186
203
}
187
204
188
205
/**
@@ -354,7 +371,8 @@ export default async function detect( {
354
371
webVitalsLibrarySrc,
355
372
urlMetricGroupCollection,
356
373
} ) {
357
- const { log, warn, error } = createLogger ( isDebug , consoleLogPrefix ) ;
374
+ const logger = createLogger ( isDebug , consoleLogPrefix ) ;
375
+ const { log, warn, error } = logger ;
358
376
359
377
if ( isDebug ) {
360
378
const allUrlMetrics = /** @type Array<UrlMetricDebugData> */ [ ] ;
@@ -396,9 +414,13 @@ export default async function detect( {
396
414
await getAlreadySubmittedSessionStorageKey (
397
415
currentETag ,
398
416
currentUrl ,
399
- urlMetricGroupStatus
417
+ urlMetricGroupStatus ,
418
+ logger
400
419
) ;
401
- if ( alreadySubmittedSessionStorageKey in sessionStorage ) {
420
+ if (
421
+ null !== alreadySubmittedSessionStorageKey &&
422
+ alreadySubmittedSessionStorageKey in sessionStorage
423
+ ) {
402
424
const previousVisitTime = parseInt (
403
425
sessionStorage . getItem ( alreadySubmittedSessionStorageKey ) ,
404
426
10
@@ -801,10 +823,12 @@ export default async function detect( {
801
823
setStorageLock ( getCurrentTime ( ) ) ;
802
824
803
825
// Remember that the URL Metric was submitted for this URL to avoid having multiple entries submitted by the same client.
804
- sessionStorage . setItem (
805
- alreadySubmittedSessionStorageKey ,
806
- String ( getCurrentTime ( ) )
807
- ) ;
826
+ if ( null !== alreadySubmittedSessionStorageKey ) {
827
+ sessionStorage . setItem (
828
+ alreadySubmittedSessionStorageKey ,
829
+ String ( getCurrentTime ( ) )
830
+ ) ;
831
+ }
808
832
809
833
const message = `Sending URL Metric (${ jsonBody . length . toLocaleString ( ) } bytes, ${ Math . round (
810
834
percentOfBudget
0 commit comments