@@ -19,37 +19,12 @@ import { XhrPerformanceResourceTiming } from './zone-types';
1919
2020/**
2121 * Get Browser's performance resource timing data associated to a XHR.
22- * For this case, some XHR might have two or one performance resource
23- * timings as one of them is CORS pre-flight request and the second is related
24- * to the actual HTTP request.
25- * The algorithm to select performance resource timings related to that xhr is
26- * is composed in general by three steps:
27- *
28- * 1. Filter the Performance Resource Timings by the name (it should match the
29- * XHR URL), additionally, the start/end timings of every performance entry
30- * should fit within the span start/end timings. These filtered performance
31- * resource entries are considered as possible entries associated to the xhr.
32- * Those are possible as there might be more than two entries that pass the
33- * filter.
34- *
35- * 2. As the XHR could cause a CORS pre-flight, we have to look for either
36- * possible pairs of performance resource timings or a single performance
37- * resource entry (a possible pair is when a resource timing entry does not
38- * overlap timings with other resource timing entry. Also, a possible single
39- * resource timing is when that resource timing entry is not paired with any
40- * other entry). Thus, for this step traverse the array of possible resource
41- * entries and for every entry try to pair it with the other possible entries.
42- *
43- * 3. Pick the best performance resource timing for the XHR: Using the possible
44- * performance resource timing entries from previous step, the best entry will
45- * be the one with the minimum gap to the span start/end timings. That is the
46- * substraction between the entry `respondeEnd` value and the span
47- * `endPerfTime` plus the substraction between the entry `startTime` and span
48- * `startPerfTime`. In case it is a tuple, the `startTime` corresponds to the
49- * first entry and the `responseEnd` is from second entry.
50- * The performance resource timing entry with the minimum gap to the span
51- * start/end timings points out that entry is the best fit for the span.
52- *
22+ * Some XHRs might have two or one performance resource timings as one of them
23+ * is the CORS pre-flight request and the second is related to the actual HTTP
24+ * request.
25+ * In overall, the algorithm to get this data takes the best fit for the span,
26+ * this means the closest performance resource timing to the span start/end
27+ * performance times is the returned value.
5328 * @param xhrUrl
5429 * @param span
5530 */
@@ -63,9 +38,17 @@ export function getXhrPerfomanceData(
6338 return bestEntry ;
6439}
6540
66- // Get Performance Resource Timings and filter them by matching the XHR url
67- // with perfomance entry name. Additionally, the entry's start/end
68- // timings must fit with in the span's start/end timings.
41+ /**
42+ * First step for the algorithm. Filter the Performance Resource Timings by the
43+ * name (it should match the XHR URL), additionally, the start/end timings of
44+ * every performance entry should fit within the span start/end timings.
45+ * These filtered performance resource entries are considered as possible
46+ * entries associated to the xhr.
47+ * Those are possible because there might be more than two entries that pass the
48+ * filter.
49+ * @param xhrUrl
50+ * @param span
51+ */
6952export function getPerfResourceEntries (
7053 xhrUrl : string ,
7154 span : Span
@@ -77,64 +60,86 @@ export function getPerfResourceEntries(
7760 ) as PerformanceResourceTiming [ ] ;
7861}
7962
63+ /**
64+ * Second step for the 'Performance resource timings selector algorithm'.
65+ * As the XHR could cause a CORS pre-flight request, we have to look for
66+ * possible performance entries either containing cors preflight timings or not.
67+ * A possible entry with cors data is when a resource timing entry does not
68+ * overlap timings with other resource timing entry. Also, a possible entry
69+ * without cors resource timing is when that resource timing entry is not
70+ * 'paired' with any other entry.
71+ * Thus, for this step traverse the array of resource entries and for every
72+ * entry check if it is a possible performance resource entry.
73+ * @param perfEntries
74+ */
8075export function getPossiblePerfResourceEntries (
8176 perfEntries : PerformanceResourceTiming [ ]
8277) : XhrPerformanceResourceTiming [ ] {
8378 const possiblePerfEntries = new Array < XhrPerformanceResourceTiming > ( ) ;
8479 const pairedEntries = new Set < PerformanceResourceTiming > ( ) ;
85- let perfEntry1 : PerformanceResourceTiming ;
86- let perfEntry2 : PerformanceResourceTiming ;
80+ let entryI : PerformanceResourceTiming ;
81+ let entryJ : PerformanceResourceTiming ;
8782 // As this part of the algorithm traverses the array twice, although,
88- // this array is not big as the performance resource entries is cleared
89- // when there are no more running XHRs.
83+ // this array is not large as the performance resource entries buffer is
84+ // cleared when there are no more running XHRs.
9085 for ( let i = 0 ; i < perfEntries . length ; i ++ ) {
91- perfEntry1 = perfEntries [ i ] ;
86+ entryI = perfEntries [ i ] ;
9287 // Compare every performance entry with its consecutive perfomance entries.
9388 // That way to avoid comparing twice the entries.
9489 for ( let j = i + 1 ; j < perfEntries . length ; j ++ ) {
95- perfEntry2 = perfEntries [ j ] ;
96- if ( ! overlappingPerfResourceTimings ( perfEntry1 , perfEntry2 ) ) {
90+ entryJ = perfEntries [ j ] ;
91+ if ( ! overlappingPerfResourceTimings ( entryI , entryJ ) ) {
9792 // As the entries are not overlapping, that means those timings
9893 // are possible perfomance timings related to the XHR.
99- possiblePerfEntries . push ( [ perfEntry1 , perfEntry2 ] ) ;
100- pairedEntries . add ( perfEntry1 ) ;
101- pairedEntries . add ( perfEntry2 ) ;
94+ possiblePerfEntries . push ( {
95+ corsPreFlightRequest : entryI ,
96+ mainRequest : entryJ ,
97+ } ) ;
98+ pairedEntries . add ( entryI ) ;
99+ pairedEntries . add ( entryJ ) ;
102100 }
103101 }
104- // If the entry1 couldn't be paired with any other resource timing,
105- // add it as a single resource timing. This is possible because this
106- // single entry might be better that the other possible entries.
107- if ( ! pairedEntries . has ( perfEntry1 ) ) {
108- possiblePerfEntries . push ( perfEntry1 as PerformanceResourceTiming ) ;
102+ // If the entry couldn't be paired with any other resource timing,
103+ // add it as a possible resource timing without cors preflight data.
104+ // This is possible because this entry might be better than the other
105+ // possible entries.
106+ if ( ! pairedEntries . has ( entryI ) ) {
107+ possiblePerfEntries . push ( { mainRequest : entryI } ) ;
109108 }
110109 }
111110 return possiblePerfEntries ;
112111}
113112
114- // The best Performance Resource Timing Entry is considered the one with the
115- // minimum gap the span end/start timings. That way we think that it fits
116- // better to the XHR as it is the closest data to the actual XHR.
113+ // Pick the best performance resource timing for the XHR: Using the possible
114+ // performance resource timing entries from previous step, the best entry will
115+ // be the one with the minimum gap to the span start/end timings.
116+ // The performance resource timing entry with the minimum gap to the span
117+ // start/end timings points out that entry is the best fit for the span.
117118function getBestPerfResourceTiming (
118119 perfEntries : XhrPerformanceResourceTiming [ ] ,
119120 span : Span
120121) : XhrPerformanceResourceTiming | undefined {
121122 let minimumGapToSpan = Number . MAX_VALUE ;
122123 let bestPerfEntry : XhrPerformanceResourceTiming | undefined = undefined ;
123- let sumGapsToSpan : number ;
124124 for ( const perfEntry of perfEntries ) {
125- // As a Tuple is in the end an Array, check that perfEntry is instance of
126- // Array is enough to know if this value refers to a Tuple.
127- if ( perfEntry instanceof Array ) {
128- sumGapsToSpan = Math . abs ( perfEntry [ 0 ] . startTime - span . startPerfTime ) ;
129- sumGapsToSpan += Math . abs ( perfEntry [ 1 ] . responseEnd - span . endPerfTime ) ;
125+ let gapToSpan = Math . abs (
126+ perfEntry . mainRequest . responseEnd - span . endPerfTime
127+ ) ;
128+ // If the current entry has cors preflight data use its `startTime` to
129+ // calculate the gap to the span.
130+ if ( perfEntry . corsPreFlightRequest ) {
131+ gapToSpan += Math . abs (
132+ perfEntry . corsPreFlightRequest . startTime - span . startPerfTime
133+ ) ;
130134 } else {
131- sumGapsToSpan = Math . abs ( perfEntry . responseEnd - span . endPerfTime ) ;
132- sumGapsToSpan += Math . abs ( perfEntry . startTime - span . startPerfTime ) ;
135+ gapToSpan += Math . abs (
136+ perfEntry . mainRequest . startTime - span . startPerfTime
137+ ) ;
133138 }
134139 // If there is a new minimum gap to the span, update the minimum and pick
135140 // the current performance entry as the best at this point.
136- if ( sumGapsToSpan < minimumGapToSpan ) {
137- minimumGapToSpan = sumGapsToSpan ;
141+ if ( gapToSpan < minimumGapToSpan ) {
142+ minimumGapToSpan = gapToSpan ;
138143 bestPerfEntry = perfEntry ;
139144 }
140145 }
0 commit comments