@paulirish/trace_engine 0.0.31 → 0.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -10
- package/analyze-trace.mjs +9 -10
- package/core/platform/ArrayUtilities.js +1 -0
- package/core/platform/ArrayUtilities.js.map +1 -1
- package/core/platform/DevToolsPath.d.ts +1 -1
- package/core/platform/DevToolsPath.js.map +1 -1
- package/core/platform/MimeType.js +4 -2
- package/core/platform/MimeType.js.map +1 -1
- package/core/platform/NumberUtilities.js +8 -0
- package/core/platform/NumberUtilities.js.map +1 -1
- package/core/platform/ServerTiming.d.ts +31 -0
- package/core/platform/ServerTiming.js +212 -0
- package/core/platform/ServerTiming.js.map +1 -0
- package/core/platform/Timing.d.ts +1 -1
- package/core/platform/Timing.js.map +1 -1
- package/core/platform/TypescriptUtilities.d.ts +3 -0
- package/core/platform/TypescriptUtilities.js.map +1 -1
- package/core/platform/UIString.d.ts +1 -1
- package/core/platform/UIString.js.map +1 -1
- package/core/platform/UserVisibleError.d.ts +1 -1
- package/core/platform/UserVisibleError.js.map +1 -1
- package/core/platform/platform-tsconfig.json +1 -1
- package/core/platform/platform.d.ts +2 -2
- package/core/platform/platform.js +2 -2
- package/core/platform/platform.js.map +1 -1
- package/generated/protocol.d.ts +258 -14
- package/models/trace/LanternComputationData.d.ts +4 -4
- package/models/trace/LanternComputationData.js +22 -23
- package/models/trace/LanternComputationData.js.map +1 -1
- package/models/trace/ModelImpl.d.ts +11 -12
- package/models/trace/ModelImpl.js +22 -33
- package/models/trace/ModelImpl.js.map +1 -1
- package/models/trace/Processor.d.ts +21 -12
- package/models/trace/Processor.js +148 -67
- package/models/trace/Processor.js.map +1 -1
- package/models/trace/TracingManager.js.map +1 -1
- package/models/trace/extras/FetchNodes.d.ts +8 -8
- package/models/trace/extras/FetchNodes.js +16 -11
- package/models/trace/extras/FetchNodes.js.map +1 -1
- package/models/trace/extras/FilmStrip.d.ts +2 -2
- package/models/trace/extras/FilmStrip.js +8 -8
- package/models/trace/extras/FilmStrip.js.map +1 -1
- package/models/trace/extras/MainThreadActivity.d.ts +1 -1
- package/models/trace/extras/MainThreadActivity.js +1 -1
- package/models/trace/extras/MainThreadActivity.js.map +1 -1
- package/models/trace/extras/Metadata.js +2 -2
- package/models/trace/extras/Metadata.js.map +1 -1
- package/models/trace/extras/URLForEntry.d.ts +9 -1
- package/models/trace/extras/URLForEntry.js +18 -10
- package/models/trace/extras/URLForEntry.js.map +1 -1
- package/models/trace/extras/extras.js +1 -1
- package/models/trace/handlers/AnimationHandler.d.ts +2 -2
- package/models/trace/handlers/AnimationHandler.js +1 -1
- package/models/trace/handlers/AnimationHandler.js.map +1 -1
- package/models/trace/handlers/AuctionWorkletsHandler.d.ts +2 -2
- package/models/trace/handlers/AuctionWorkletsHandler.js +11 -11
- package/models/trace/handlers/AuctionWorkletsHandler.js.map +1 -1
- package/models/trace/handlers/ExtensionTraceDataHandler.d.ts +7 -5
- package/models/trace/handlers/ExtensionTraceDataHandler.js +16 -10
- package/models/trace/handlers/ExtensionTraceDataHandler.js.map +1 -1
- package/models/trace/handlers/FramesHandler.d.ts +24 -19
- package/models/trace/handlers/FramesHandler.js +46 -25
- package/models/trace/handlers/FramesHandler.js.map +1 -1
- package/models/trace/handlers/GPUHandler.d.ts +4 -4
- package/models/trace/handlers/GPUHandler.js +3 -3
- package/models/trace/handlers/GPUHandler.js.map +1 -1
- package/models/trace/handlers/ImagePaintingHandler.d.ts +3 -3
- package/models/trace/handlers/ImagePaintingHandler.js +6 -8
- package/models/trace/handlers/ImagePaintingHandler.js.map +1 -1
- package/models/trace/handlers/InitiatorsHandler.d.ts +3 -3
- package/models/trace/handlers/InitiatorsHandler.js +14 -14
- package/models/trace/handlers/InitiatorsHandler.js.map +1 -1
- package/models/trace/handlers/InvalidationsHandler.d.ts +4 -2
- package/models/trace/handlers/InvalidationsHandler.js +29 -11
- package/models/trace/handlers/InvalidationsHandler.js.map +1 -1
- package/models/trace/handlers/LargestImagePaintHandler.d.ts +2 -2
- package/models/trace/handlers/LargestImagePaintHandler.js +1 -1
- package/models/trace/handlers/LargestImagePaintHandler.js.map +1 -1
- package/models/trace/handlers/LargestTextPaintHandler.d.ts +2 -2
- package/models/trace/handlers/LargestTextPaintHandler.js +1 -1
- package/models/trace/handlers/LargestTextPaintHandler.js.map +1 -1
- package/models/trace/handlers/LayerTreeHandler.d.ts +6 -6
- package/models/trace/handlers/LayerTreeHandler.js +6 -6
- package/models/trace/handlers/LayerTreeHandler.js.map +1 -1
- package/models/trace/handlers/LayoutShiftsHandler.d.ts +12 -20
- package/models/trace/handlers/LayoutShiftsHandler.js +73 -12
- package/models/trace/handlers/LayoutShiftsHandler.js.map +1 -1
- package/models/trace/handlers/MemoryHandler.d.ts +2 -2
- package/models/trace/handlers/MemoryHandler.js +1 -1
- package/models/trace/handlers/MemoryHandler.js.map +1 -1
- package/models/trace/handlers/MetaHandler.d.ts +15 -14
- package/models/trace/handlers/MetaHandler.js +32 -30
- package/models/trace/handlers/MetaHandler.js.map +1 -1
- package/models/trace/handlers/ModelHandlers.d.ts +1 -1
- package/models/trace/handlers/ModelHandlers.js +1 -1
- package/models/trace/handlers/ModelHandlers.js.map +1 -1
- package/models/trace/handlers/NetworkRequestsHandler.d.ts +13 -12
- package/models/trace/handlers/NetworkRequestsHandler.js +68 -66
- package/models/trace/handlers/NetworkRequestsHandler.js.map +1 -1
- package/models/trace/handlers/PageFramesHandler.d.ts +2 -2
- package/models/trace/handlers/PageFramesHandler.js +2 -2
- package/models/trace/handlers/PageFramesHandler.js.map +1 -1
- package/models/trace/handlers/PageLoadMetricsHandler.d.ts +7 -7
- package/models/trace/handlers/PageLoadMetricsHandler.js +21 -24
- package/models/trace/handlers/PageLoadMetricsHandler.js.map +1 -1
- package/models/trace/handlers/RendererHandler.d.ts +19 -19
- package/models/trace/handlers/RendererHandler.js +5 -5
- package/models/trace/handlers/RendererHandler.js.map +1 -1
- package/models/trace/handlers/SamplesHandler.d.ts +6 -6
- package/models/trace/handlers/SamplesHandler.js +4 -4
- package/models/trace/handlers/SamplesHandler.js.map +1 -1
- package/models/trace/handlers/ScreenshotsHandler.d.ts +6 -4
- package/models/trace/handlers/ScreenshotsHandler.js +11 -9
- package/models/trace/handlers/ScreenshotsHandler.js.map +1 -1
- package/models/trace/handlers/SelectorStatsHandler.d.ts +3 -3
- package/models/trace/handlers/SelectorStatsHandler.js +2 -2
- package/models/trace/handlers/SelectorStatsHandler.js.map +1 -1
- package/models/trace/handlers/ServerTimingsHandler.d.ts +10 -0
- package/models/trace/handlers/ServerTimingsHandler.js +118 -0
- package/models/trace/handlers/ServerTimingsHandler.js.map +1 -0
- package/models/trace/handlers/Threads.d.ts +7 -7
- package/models/trace/handlers/Threads.js +5 -5
- package/models/trace/handlers/Threads.js.map +1 -1
- package/models/trace/handlers/UserInteractionsHandler.d.ts +13 -11
- package/models/trace/handlers/UserInteractionsHandler.js +13 -7
- package/models/trace/handlers/UserInteractionsHandler.js.map +1 -1
- package/models/trace/handlers/UserTimingsHandler.d.ts +5 -5
- package/models/trace/handlers/UserTimingsHandler.js +52 -9
- package/models/trace/handlers/UserTimingsHandler.js.map +1 -1
- package/models/trace/handlers/WarningsHandler.d.ts +5 -5
- package/models/trace/handlers/WarningsHandler.js +4 -5
- package/models/trace/handlers/WarningsHandler.js.map +1 -1
- package/models/trace/handlers/WorkersHandler.d.ts +4 -4
- package/models/trace/handlers/WorkersHandler.js +1 -1
- package/models/trace/handlers/WorkersHandler.js.map +1 -1
- package/models/trace/handlers/handlers-tsconfig.json +1 -1
- package/models/trace/handlers/types.d.ts +7 -7
- package/models/trace/handlers/types.js.map +1 -1
- package/models/trace/helpers/Extensions.d.ts +5 -1
- package/models/trace/helpers/Extensions.js +5 -3
- package/models/trace/helpers/Extensions.js.map +1 -1
- package/models/trace/helpers/Network.d.ts +2 -2
- package/models/trace/helpers/Network.js +19 -2
- package/models/trace/helpers/Network.js.map +1 -1
- package/models/trace/helpers/SamplesIntegrator.d.ts +5 -5
- package/models/trace/helpers/SamplesIntegrator.js +10 -11
- package/models/trace/helpers/SamplesIntegrator.js.map +1 -1
- package/models/trace/helpers/SyntheticEvents.d.ts +8 -14
- package/models/trace/helpers/SyntheticEvents.js +20 -31
- package/models/trace/helpers/SyntheticEvents.js.map +1 -1
- package/models/trace/helpers/Timing.d.ts +16 -5
- package/models/trace/helpers/Timing.js +33 -7
- package/models/trace/helpers/Timing.js.map +1 -1
- package/models/trace/helpers/Trace.d.ts +46 -33
- package/models/trace/helpers/Trace.js +53 -38
- package/models/trace/helpers/Trace.js.map +1 -1
- package/models/trace/helpers/TreeHelpers.d.ts +30 -8
- package/models/trace/helpers/TreeHelpers.js +91 -23
- package/models/trace/helpers/TreeHelpers.js.map +1 -1
- package/models/trace/insights/Common.d.ts +8 -2
- package/models/trace/insights/Common.js +33 -7
- package/models/trace/insights/Common.js.map +1 -1
- package/models/trace/insights/CumulativeLayoutShift.d.ts +34 -13
- package/models/trace/insights/CumulativeLayoutShift.js +151 -59
- package/models/trace/insights/CumulativeLayoutShift.js.map +1 -1
- package/models/trace/insights/DocumentLatency.d.ts +9 -4
- package/models/trace/insights/DocumentLatency.js +82 -7
- package/models/trace/insights/DocumentLatency.js.map +1 -1
- package/models/trace/insights/FontDisplay.d.ts +11 -0
- package/models/trace/insights/FontDisplay.js +44 -0
- package/models/trace/insights/FontDisplay.js.map +1 -0
- package/models/trace/insights/InsightRunners.d.ts +3 -0
- package/models/trace/insights/InsightRunners.js +3 -0
- package/models/trace/insights/InsightRunners.js.map +1 -1
- package/models/trace/insights/InteractionToNextPaint.d.ts +4 -5
- package/models/trace/insights/InteractionToNextPaint.js +5 -3
- package/models/trace/insights/InteractionToNextPaint.js.map +1 -1
- package/models/trace/insights/LargestContentfulPaint.d.ts +20 -7
- package/models/trace/insights/LargestContentfulPaint.js +57 -37
- package/models/trace/insights/LargestContentfulPaint.js.map +1 -1
- package/models/trace/insights/RenderBlocking.d.ts +3 -3
- package/models/trace/insights/RenderBlocking.js +29 -24
- package/models/trace/insights/RenderBlocking.js.map +1 -1
- package/models/trace/insights/SlowCSSSelector.d.ts +11 -0
- package/models/trace/insights/SlowCSSSelector.js +67 -0
- package/models/trace/insights/SlowCSSSelector.js.map +1 -0
- package/models/trace/insights/ThirdPartyWeb.d.ts +18 -0
- package/models/trace/insights/ThirdPartyWeb.js +174 -0
- package/models/trace/insights/ThirdPartyWeb.js.map +1 -0
- package/models/trace/insights/Viewport.d.ts +5 -2
- package/models/trace/insights/Viewport.js +14 -9
- package/models/trace/insights/Viewport.js.map +1 -1
- package/models/trace/insights/insights-tsconfig.json +9 -0
- package/models/trace/insights/insights.d.ts +1 -0
- package/models/trace/insights/insights.js +1 -0
- package/models/trace/insights/insights.js.map +1 -1
- package/models/trace/insights/types.d.ts +43 -25
- package/models/trace/insights/types.js.map +1 -1
- package/models/trace/lantern/core/NetworkAnalyzer.d.ts +6 -6
- package/models/trace/lantern/core/NetworkAnalyzer.js +12 -12
- package/models/trace/lantern/core/NetworkAnalyzer.js.map +1 -1
- package/models/trace/lantern/graph/BaseNode.d.ts +4 -4
- package/models/trace/lantern/graph/BaseNode.js +21 -21
- package/models/trace/lantern/graph/BaseNode.js.map +1 -1
- package/models/trace/lantern/graph/CPUNode.d.ts +1 -1
- package/models/trace/lantern/graph/CPUNode.js +5 -5
- package/models/trace/lantern/graph/CPUNode.js.map +1 -1
- package/models/trace/lantern/graph/PageDependencyGraph.d.ts +4 -4
- package/models/trace/lantern/graph/PageDependencyGraph.js +5 -5
- package/models/trace/lantern/graph/PageDependencyGraph.js.map +1 -1
- package/models/trace/lantern/simulation/ConnectionPool.d.ts +7 -7
- package/models/trace/lantern/simulation/ConnectionPool.js +26 -26
- package/models/trace/lantern/simulation/ConnectionPool.js.map +1 -1
- package/models/trace/lantern/simulation/DNSCache.d.ts +3 -3
- package/models/trace/lantern/simulation/DNSCache.js +11 -11
- package/models/trace/lantern/simulation/DNSCache.js.map +1 -1
- package/models/trace/lantern/simulation/SimulationTimingMap.d.ts +1 -1
- package/models/trace/lantern/simulation/SimulationTimingMap.js +15 -15
- package/models/trace/lantern/simulation/SimulationTimingMap.js.map +1 -1
- package/models/trace/lantern/simulation/Simulator.d.ts +28 -28
- package/models/trace/lantern/simulation/Simulator.js +113 -113
- package/models/trace/lantern/simulation/Simulator.js.map +1 -1
- package/models/trace/lantern/simulation/TCPConnection.d.ts +9 -9
- package/models/trace/lantern/simulation/TCPConnection.js +36 -36
- package/models/trace/lantern/simulation/TCPConnection.js.map +1 -1
- package/models/trace/root-causes/LayoutShift.d.ts +13 -13
- package/models/trace/root-causes/LayoutShift.js +7 -25
- package/models/trace/root-causes/LayoutShift.js.map +1 -1
- package/models/trace/types/Configuration.d.ts +16 -0
- package/models/trace/types/Configuration.js +1 -0
- package/models/trace/types/Configuration.js.map +1 -1
- package/models/trace/types/Extensions.d.ts +9 -12
- package/models/trace/types/Extensions.js +2 -1
- package/models/trace/types/Extensions.js.map +1 -1
- package/models/trace/types/File.d.ts +68 -25
- package/models/trace/types/File.js +15 -3
- package/models/trace/types/File.js.map +1 -1
- package/models/trace/types/TraceEvents.d.ts +819 -730
- package/models/trace/types/TraceEvents.js +270 -280
- package/models/trace/types/TraceEvents.js.map +1 -1
- package/models/trace/types/types.d.ts +1 -1
- package/models/trace/types/types.js +1 -1
- package/models/trace/types/types.js.map +1 -1
- package/package.json +4 -2
- package/test/test-trace-engine.mjs +47 -2
- package/third_party/third-party-web/third-party-web.js +1 -0
- package/core/platform/PromiseUtilities.d.ts +0 -10
- package/core/platform/PromiseUtilities.js +0 -18
- package/core/platform/PromiseUtilities.js.map +0 -1
- package/core/platform/SetUtilities.d.ts +0 -2
- package/core/platform/SetUtilities.js +0 -23
- package/core/platform/SetUtilities.js.map +0 -1
- package/models/trace/EntriesFilter.d.ts +0 -72
- package/models/trace/EntriesFilter.js +0 -296
- package/models/trace/EntriesFilter.js.map +0 -1
- package/models/trace/LegacyTracingModel.js.map +0 -1
- package/models/trace/handlers/EnhancedTracesHandler.d.ts +0 -48
- package/models/trace/handlers/EnhancedTracesHandler.js +0 -165
- package/models/trace/handlers/EnhancedTracesHandler.js.map +0 -1
- package/models/trace/lantern/BaseNode.d.ts +0 -91
- package/models/trace/lantern/BaseNode.js +0 -268
- package/models/trace/lantern/BaseNode.js.map +0 -1
- package/models/trace/lantern/CPUNode.d.ts +0 -24
- package/models/trace/lantern/CPUNode.js +0 -64
- package/models/trace/lantern/CPUNode.js.map +0 -1
- package/models/trace/lantern/LanternError.d.ts +0 -3
- package/models/trace/lantern/LanternError.js +0 -7
- package/models/trace/lantern/LanternError.js.map +0 -1
- package/models/trace/lantern/MetricsModule.d.ts +0 -11
- package/models/trace/lantern/MetricsModule.js +0 -14
- package/models/trace/lantern/MetricsModule.js.map +0 -1
- package/models/trace/lantern/NetworkNode.d.ts +0 -22
- package/models/trace/lantern/NetworkNode.js +0 -83
- package/models/trace/lantern/NetworkNode.js.map +0 -1
- package/models/trace/lantern/PageDependencyGraph.d.ts +0 -43
- package/models/trace/lantern/PageDependencyGraph.js +0 -509
- package/models/trace/lantern/PageDependencyGraph.js.map +0 -1
- package/models/trace/lantern/SimulationModule.d.ts +0 -17
- package/models/trace/lantern/SimulationModule.js +0 -13
- package/models/trace/lantern/SimulationModule.js.map +0 -1
- package/models/trace/lantern/simulation/NetworkAnalyzer.d.ts +0 -112
- package/models/trace/lantern/simulation/NetworkAnalyzer.js +0 -486
- package/models/trace/lantern/simulation/NetworkAnalyzer.js.map +0 -1
|
@@ -4,23 +4,23 @@
|
|
|
4
4
|
const INITIAL_CONGESTION_WINDOW = 10;
|
|
5
5
|
const TCP_SEGMENT_SIZE = 1460;
|
|
6
6
|
class TCPConnection {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
warmed;
|
|
8
|
+
ssl;
|
|
9
|
+
h2;
|
|
10
|
+
rtt;
|
|
11
|
+
throughput;
|
|
12
|
+
serverLatency;
|
|
13
13
|
_congestionWindow;
|
|
14
|
-
|
|
14
|
+
h2OverflowBytesDownloaded;
|
|
15
15
|
constructor(rtt, throughput, serverLatency = 0, ssl = true, h2 = false) {
|
|
16
|
-
this.
|
|
17
|
-
this.
|
|
18
|
-
this.
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
21
|
-
this.
|
|
16
|
+
this.warmed = false;
|
|
17
|
+
this.ssl = ssl;
|
|
18
|
+
this.h2 = h2;
|
|
19
|
+
this.rtt = rtt;
|
|
20
|
+
this.throughput = throughput;
|
|
21
|
+
this.serverLatency = serverLatency;
|
|
22
22
|
this._congestionWindow = INITIAL_CONGESTION_WINDOW;
|
|
23
|
-
this.
|
|
23
|
+
this.h2OverflowBytesDownloaded = 0;
|
|
24
24
|
}
|
|
25
25
|
static maximumSaturatedConnections(rtt, availableThroughput) {
|
|
26
26
|
const roundTripsPerSecond = 1000 / rtt;
|
|
@@ -29,26 +29,26 @@ class TCPConnection {
|
|
|
29
29
|
const minimumThroughputRequiredPerRequest = bytesPerSecond * 8;
|
|
30
30
|
return Math.floor(availableThroughput / minimumThroughputRequiredPerRequest);
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
const bytesPerSecond = this.
|
|
34
|
-
const secondsPerRoundTrip = this.
|
|
32
|
+
computeMaximumCongestionWindowInSegments() {
|
|
33
|
+
const bytesPerSecond = this.throughput / 8;
|
|
34
|
+
const secondsPerRoundTrip = this.rtt / 1000;
|
|
35
35
|
const bytesPerRoundTrip = bytesPerSecond * secondsPerRoundTrip;
|
|
36
36
|
return Math.floor(bytesPerRoundTrip / TCP_SEGMENT_SIZE);
|
|
37
37
|
}
|
|
38
38
|
setThroughput(throughput) {
|
|
39
|
-
this.
|
|
39
|
+
this.throughput = throughput;
|
|
40
40
|
}
|
|
41
41
|
setCongestionWindow(congestion) {
|
|
42
42
|
this._congestionWindow = congestion;
|
|
43
43
|
}
|
|
44
44
|
setWarmed(warmed) {
|
|
45
|
-
this.
|
|
45
|
+
this.warmed = warmed;
|
|
46
46
|
}
|
|
47
47
|
isWarm() {
|
|
48
|
-
return this.
|
|
48
|
+
return this.warmed;
|
|
49
49
|
}
|
|
50
50
|
isH2() {
|
|
51
|
-
return this.
|
|
51
|
+
return this.h2;
|
|
52
52
|
}
|
|
53
53
|
get congestionWindow() {
|
|
54
54
|
return this._congestionWindow;
|
|
@@ -58,13 +58,13 @@ class TCPConnection {
|
|
|
58
58
|
* applies to H2 connections.
|
|
59
59
|
*/
|
|
60
60
|
setH2OverflowBytesDownloaded(bytes) {
|
|
61
|
-
if (!this.
|
|
61
|
+
if (!this.h2) {
|
|
62
62
|
return;
|
|
63
63
|
}
|
|
64
|
-
this.
|
|
64
|
+
this.h2OverflowBytesDownloaded = bytes;
|
|
65
65
|
}
|
|
66
66
|
clone() {
|
|
67
|
-
return Object.assign(new TCPConnection(this.
|
|
67
|
+
return Object.assign(new TCPConnection(this.rtt, this.throughput), this);
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
70
70
|
* Simulates a network download of a particular number of bytes over an optional maximum amount of time
|
|
@@ -75,14 +75,14 @@ class TCPConnection {
|
|
|
75
75
|
*/
|
|
76
76
|
simulateDownloadUntil(bytesToDownload, options) {
|
|
77
77
|
const { timeAlreadyElapsed = 0, maximumTimeToElapse = Infinity, dnsResolutionTime = 0 } = options || {};
|
|
78
|
-
if (this.
|
|
79
|
-
bytesToDownload -= this.
|
|
78
|
+
if (this.warmed && this.h2) {
|
|
79
|
+
bytesToDownload -= this.h2OverflowBytesDownloaded;
|
|
80
80
|
}
|
|
81
|
-
const twoWayLatency = this.
|
|
81
|
+
const twoWayLatency = this.rtt;
|
|
82
82
|
const oneWayLatency = twoWayLatency / 2;
|
|
83
|
-
const maximumCongestionWindow = this.
|
|
83
|
+
const maximumCongestionWindow = this.computeMaximumCongestionWindowInSegments();
|
|
84
84
|
let handshakeAndRequest = oneWayLatency;
|
|
85
|
-
if (!this.
|
|
85
|
+
if (!this.warmed) {
|
|
86
86
|
handshakeAndRequest =
|
|
87
87
|
// DNS lookup
|
|
88
88
|
dnsResolutionTime +
|
|
@@ -93,11 +93,11 @@ class TCPConnection {
|
|
|
93
93
|
// ACK + initial request
|
|
94
94
|
oneWayLatency +
|
|
95
95
|
// ClientHello/ServerHello assuming TLS False Start is enabled (https://istlsfastyet.com/#server-performance).
|
|
96
|
-
(this.
|
|
96
|
+
(this.ssl ? twoWayLatency : 0);
|
|
97
97
|
}
|
|
98
98
|
let roundTrips = Math.ceil(handshakeAndRequest / twoWayLatency);
|
|
99
|
-
let timeToFirstByte = handshakeAndRequest + this.
|
|
100
|
-
if (this.
|
|
99
|
+
let timeToFirstByte = handshakeAndRequest + this.serverLatency + oneWayLatency;
|
|
100
|
+
if (this.warmed && this.h2) {
|
|
101
101
|
timeToFirstByte = 0;
|
|
102
102
|
}
|
|
103
103
|
const timeElapsedForTTFB = Math.max(timeToFirstByte - timeAlreadyElapsed, 0);
|
|
@@ -121,18 +121,18 @@ class TCPConnection {
|
|
|
121
121
|
bytesRemaining -= bytesDownloadedInWindow;
|
|
122
122
|
}
|
|
123
123
|
const timeElapsed = timeElapsedForTTFB + downloadTimeElapsed;
|
|
124
|
-
const extraBytesDownloaded = this.
|
|
124
|
+
const extraBytesDownloaded = this.h2 ? Math.max(totalBytesDownloaded - bytesToDownload, 0) : 0;
|
|
125
125
|
const bytesDownloaded = Math.max(Math.min(totalBytesDownloaded, bytesToDownload), 0);
|
|
126
126
|
let connectionTiming;
|
|
127
|
-
if (!this.
|
|
127
|
+
if (!this.warmed) {
|
|
128
128
|
connectionTiming = {
|
|
129
129
|
dnsResolutionTime,
|
|
130
130
|
connectionTime: handshakeAndRequest - dnsResolutionTime,
|
|
131
|
-
sslTime: this.
|
|
131
|
+
sslTime: this.ssl ? twoWayLatency : undefined,
|
|
132
132
|
timeToFirstByte,
|
|
133
133
|
};
|
|
134
134
|
}
|
|
135
|
-
else if (this.
|
|
135
|
+
else if (this.h2) {
|
|
136
136
|
// TODO: timing information currently difficult to model for warm h2 connections.
|
|
137
137
|
connectionTiming = {
|
|
138
138
|
timeToFirstByte,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TCPConnection.js","sourceRoot":"","sources":["../../../../../../../../front_end/models/trace/lantern/simulation/TCPConnection.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport {type ConnectionTiming} from './SimulationTimingMap.js';\n\ninterface DownloadOptions {\n dnsResolutionTime?: number;\n timeAlreadyElapsed?: number;\n maximumTimeToElapse?: number;\n}\n\ninterface DownloadResults {\n roundTrips: number;\n timeElapsed: number;\n bytesDownloaded: number;\n extraBytesDownloaded: number;\n congestionWindow: number;\n connectionTiming: ConnectionTiming;\n}\n\nconst INITIAL_CONGESTION_WINDOW = 10;\nconst TCP_SEGMENT_SIZE = 1460;\n\nclass TCPConnection {\n _warmed: boolean;\n _ssl: boolean;\n _h2: boolean;\n _rtt: number;\n _throughput: number;\n _serverLatency: number;\n _congestionWindow: number;\n _h2OverflowBytesDownloaded: number;\n\n constructor(rtt: number, throughput: number, serverLatency = 0, ssl = true, h2 = false) {\n this._warmed = false;\n this._ssl = ssl;\n this._h2 = h2;\n this._rtt = rtt;\n this._throughput = throughput;\n this._serverLatency = serverLatency;\n this._congestionWindow = INITIAL_CONGESTION_WINDOW;\n this._h2OverflowBytesDownloaded = 0;\n }\n\n static maximumSaturatedConnections(rtt: number, availableThroughput: number): number {\n const roundTripsPerSecond = 1000 / rtt;\n const bytesPerRoundTrip = TCP_SEGMENT_SIZE;\n const bytesPerSecond = roundTripsPerSecond * bytesPerRoundTrip;\n const minimumThroughputRequiredPerRequest = bytesPerSecond * 8;\n return Math.floor(availableThroughput / minimumThroughputRequiredPerRequest);\n }\n\n _computeMaximumCongestionWindowInSegments(): number {\n const bytesPerSecond = this._throughput / 8;\n const secondsPerRoundTrip = this._rtt / 1000;\n const bytesPerRoundTrip = bytesPerSecond * secondsPerRoundTrip;\n return Math.floor(bytesPerRoundTrip / TCP_SEGMENT_SIZE);\n }\n\n setThroughput(throughput: number): void {\n this._throughput = throughput;\n }\n\n setCongestionWindow(congestion: number): void {\n this._congestionWindow = congestion;\n }\n\n setWarmed(warmed: boolean): void {\n this._warmed = warmed;\n }\n\n isWarm(): boolean {\n return this._warmed;\n }\n\n isH2(): boolean {\n return this._h2;\n }\n\n get congestionWindow(): number {\n return this._congestionWindow;\n }\n\n /**\n * Sets the number of excess bytes that are available to this connection on future downloads, only\n * applies to H2 connections.\n */\n setH2OverflowBytesDownloaded(bytes: number): void {\n if (!this._h2) {\n return;\n }\n this._h2OverflowBytesDownloaded = bytes;\n }\n\n clone(): TCPConnection {\n return Object.assign(new TCPConnection(this._rtt, this._throughput), this);\n }\n\n /**\n * Simulates a network download of a particular number of bytes over an optional maximum amount of time\n * and returns information about the ending state.\n *\n * See https://hpbn.co/building-blocks-of-tcp/#three-way-handshake and\n * https://hpbn.co/transport-layer-security-tls/#tls-handshake for details.\n */\n simulateDownloadUntil(bytesToDownload: number, options?: DownloadOptions): DownloadResults {\n const {timeAlreadyElapsed = 0, maximumTimeToElapse = Infinity, dnsResolutionTime = 0} = options || {};\n\n if (this._warmed && this._h2) {\n bytesToDownload -= this._h2OverflowBytesDownloaded;\n }\n const twoWayLatency = this._rtt;\n const oneWayLatency = twoWayLatency / 2;\n const maximumCongestionWindow = this._computeMaximumCongestionWindowInSegments();\n\n let handshakeAndRequest = oneWayLatency;\n if (!this._warmed) {\n handshakeAndRequest =\n // DNS lookup\n dnsResolutionTime +\n // SYN\n oneWayLatency +\n // SYN ACK\n oneWayLatency +\n // ACK + initial request\n oneWayLatency +\n // ClientHello/ServerHello assuming TLS False Start is enabled (https://istlsfastyet.com/#server-performance).\n (this._ssl ? twoWayLatency : 0);\n }\n\n let roundTrips = Math.ceil(handshakeAndRequest / twoWayLatency);\n let timeToFirstByte = handshakeAndRequest + this._serverLatency + oneWayLatency;\n if (this._warmed && this._h2) {\n timeToFirstByte = 0;\n }\n\n const timeElapsedForTTFB = Math.max(timeToFirstByte - timeAlreadyElapsed, 0);\n const maximumDownloadTimeToElapse = maximumTimeToElapse - timeElapsedForTTFB;\n\n let congestionWindow = Math.min(this._congestionWindow, maximumCongestionWindow);\n let totalBytesDownloaded = 0;\n if (timeElapsedForTTFB > 0) {\n totalBytesDownloaded = congestionWindow * TCP_SEGMENT_SIZE;\n } else {\n roundTrips = 0;\n }\n\n let downloadTimeElapsed = 0;\n let bytesRemaining = bytesToDownload - totalBytesDownloaded;\n while (bytesRemaining > 0 && downloadTimeElapsed <= maximumDownloadTimeToElapse) {\n roundTrips++;\n downloadTimeElapsed += twoWayLatency;\n congestionWindow = Math.max(Math.min(maximumCongestionWindow, congestionWindow * 2), 1);\n\n const bytesDownloadedInWindow = congestionWindow * TCP_SEGMENT_SIZE;\n totalBytesDownloaded += bytesDownloadedInWindow;\n bytesRemaining -= bytesDownloadedInWindow;\n }\n\n const timeElapsed = timeElapsedForTTFB + downloadTimeElapsed;\n const extraBytesDownloaded = this._h2 ? Math.max(totalBytesDownloaded - bytesToDownload, 0) : 0;\n const bytesDownloaded = Math.max(Math.min(totalBytesDownloaded, bytesToDownload), 0);\n\n let connectionTiming: ConnectionTiming;\n if (!this._warmed) {\n connectionTiming = {\n dnsResolutionTime,\n connectionTime: handshakeAndRequest - dnsResolutionTime,\n sslTime: this._ssl ? twoWayLatency : undefined,\n timeToFirstByte,\n };\n } else if (this._h2) {\n // TODO: timing information currently difficult to model for warm h2 connections.\n connectionTiming = {\n timeToFirstByte,\n };\n } else {\n connectionTiming = {\n connectionTime: handshakeAndRequest,\n timeToFirstByte,\n };\n }\n\n return {\n roundTrips,\n timeElapsed,\n bytesDownloaded,\n extraBytesDownloaded,\n congestionWindow,\n connectionTiming,\n };\n }\n}\n\nexport {TCPConnection};\n"]}
|
|
1
|
+
{"version":3,"file":"TCPConnection.js","sourceRoot":"","sources":["../../../../../../../../front_end/models/trace/lantern/simulation/TCPConnection.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport type {ConnectionTiming} from './SimulationTimingMap.js';\n\ninterface DownloadOptions {\n dnsResolutionTime?: number;\n timeAlreadyElapsed?: number;\n maximumTimeToElapse?: number;\n}\n\ninterface DownloadResults {\n roundTrips: number;\n timeElapsed: number;\n bytesDownloaded: number;\n extraBytesDownloaded: number;\n congestionWindow: number;\n connectionTiming: ConnectionTiming;\n}\n\nconst INITIAL_CONGESTION_WINDOW = 10;\nconst TCP_SEGMENT_SIZE = 1460;\n\nclass TCPConnection {\n warmed: boolean;\n ssl: boolean;\n h2: boolean;\n rtt: number;\n throughput: number;\n serverLatency: number;\n _congestionWindow: number;\n h2OverflowBytesDownloaded: number;\n\n constructor(rtt: number, throughput: number, serverLatency = 0, ssl = true, h2 = false) {\n this.warmed = false;\n this.ssl = ssl;\n this.h2 = h2;\n this.rtt = rtt;\n this.throughput = throughput;\n this.serverLatency = serverLatency;\n this._congestionWindow = INITIAL_CONGESTION_WINDOW;\n this.h2OverflowBytesDownloaded = 0;\n }\n\n static maximumSaturatedConnections(rtt: number, availableThroughput: number): number {\n const roundTripsPerSecond = 1000 / rtt;\n const bytesPerRoundTrip = TCP_SEGMENT_SIZE;\n const bytesPerSecond = roundTripsPerSecond * bytesPerRoundTrip;\n const minimumThroughputRequiredPerRequest = bytesPerSecond * 8;\n return Math.floor(availableThroughput / minimumThroughputRequiredPerRequest);\n }\n\n computeMaximumCongestionWindowInSegments(): number {\n const bytesPerSecond = this.throughput / 8;\n const secondsPerRoundTrip = this.rtt / 1000;\n const bytesPerRoundTrip = bytesPerSecond * secondsPerRoundTrip;\n return Math.floor(bytesPerRoundTrip / TCP_SEGMENT_SIZE);\n }\n\n setThroughput(throughput: number): void {\n this.throughput = throughput;\n }\n\n setCongestionWindow(congestion: number): void {\n this._congestionWindow = congestion;\n }\n\n setWarmed(warmed: boolean): void {\n this.warmed = warmed;\n }\n\n isWarm(): boolean {\n return this.warmed;\n }\n\n isH2(): boolean {\n return this.h2;\n }\n\n get congestionWindow(): number {\n return this._congestionWindow;\n }\n\n /**\n * Sets the number of excess bytes that are available to this connection on future downloads, only\n * applies to H2 connections.\n */\n setH2OverflowBytesDownloaded(bytes: number): void {\n if (!this.h2) {\n return;\n }\n this.h2OverflowBytesDownloaded = bytes;\n }\n\n clone(): TCPConnection {\n return Object.assign(new TCPConnection(this.rtt, this.throughput), this);\n }\n\n /**\n * Simulates a network download of a particular number of bytes over an optional maximum amount of time\n * and returns information about the ending state.\n *\n * See https://hpbn.co/building-blocks-of-tcp/#three-way-handshake and\n * https://hpbn.co/transport-layer-security-tls/#tls-handshake for details.\n */\n simulateDownloadUntil(bytesToDownload: number, options?: DownloadOptions): DownloadResults {\n const {timeAlreadyElapsed = 0, maximumTimeToElapse = Infinity, dnsResolutionTime = 0} = options || {};\n\n if (this.warmed && this.h2) {\n bytesToDownload -= this.h2OverflowBytesDownloaded;\n }\n const twoWayLatency = this.rtt;\n const oneWayLatency = twoWayLatency / 2;\n const maximumCongestionWindow = this.computeMaximumCongestionWindowInSegments();\n\n let handshakeAndRequest = oneWayLatency;\n if (!this.warmed) {\n handshakeAndRequest =\n // DNS lookup\n dnsResolutionTime +\n // SYN\n oneWayLatency +\n // SYN ACK\n oneWayLatency +\n // ACK + initial request\n oneWayLatency +\n // ClientHello/ServerHello assuming TLS False Start is enabled (https://istlsfastyet.com/#server-performance).\n (this.ssl ? twoWayLatency : 0);\n }\n\n let roundTrips = Math.ceil(handshakeAndRequest / twoWayLatency);\n let timeToFirstByte = handshakeAndRequest + this.serverLatency + oneWayLatency;\n if (this.warmed && this.h2) {\n timeToFirstByte = 0;\n }\n\n const timeElapsedForTTFB = Math.max(timeToFirstByte - timeAlreadyElapsed, 0);\n const maximumDownloadTimeToElapse = maximumTimeToElapse - timeElapsedForTTFB;\n\n let congestionWindow = Math.min(this._congestionWindow, maximumCongestionWindow);\n let totalBytesDownloaded = 0;\n if (timeElapsedForTTFB > 0) {\n totalBytesDownloaded = congestionWindow * TCP_SEGMENT_SIZE;\n } else {\n roundTrips = 0;\n }\n\n let downloadTimeElapsed = 0;\n let bytesRemaining = bytesToDownload - totalBytesDownloaded;\n while (bytesRemaining > 0 && downloadTimeElapsed <= maximumDownloadTimeToElapse) {\n roundTrips++;\n downloadTimeElapsed += twoWayLatency;\n congestionWindow = Math.max(Math.min(maximumCongestionWindow, congestionWindow * 2), 1);\n\n const bytesDownloadedInWindow = congestionWindow * TCP_SEGMENT_SIZE;\n totalBytesDownloaded += bytesDownloadedInWindow;\n bytesRemaining -= bytesDownloadedInWindow;\n }\n\n const timeElapsed = timeElapsedForTTFB + downloadTimeElapsed;\n const extraBytesDownloaded = this.h2 ? Math.max(totalBytesDownloaded - bytesToDownload, 0) : 0;\n const bytesDownloaded = Math.max(Math.min(totalBytesDownloaded, bytesToDownload), 0);\n\n let connectionTiming: ConnectionTiming;\n if (!this.warmed) {\n connectionTiming = {\n dnsResolutionTime,\n connectionTime: handshakeAndRequest - dnsResolutionTime,\n sslTime: this.ssl ? twoWayLatency : undefined,\n timeToFirstByte,\n };\n } else if (this.h2) {\n // TODO: timing information currently difficult to model for warm h2 connections.\n connectionTiming = {\n timeToFirstByte,\n };\n } else {\n connectionTiming = {\n connectionTime: handshakeAndRequest,\n timeToFirstByte,\n };\n }\n\n return {\n roundTrips,\n timeElapsed,\n bytesDownloaded,\n extraBytesDownloaded,\n congestionWindow,\n connectionTiming,\n };\n }\n}\n\nexport {TCPConnection};\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type * as Protocol from '../../../generated/protocol.js';
|
|
2
|
-
import {
|
|
2
|
+
import type { ParsedTrace } from '../handlers/types.js';
|
|
3
3
|
import * as Types from '../types/types.js';
|
|
4
|
-
import {
|
|
4
|
+
import type { RootCauseProtocolInterface } from './RootCauses.js';
|
|
5
5
|
export type CSSDimensions = {
|
|
6
6
|
width?: string;
|
|
7
7
|
height?: string;
|
|
@@ -16,7 +16,7 @@ export interface InjectedIframe {
|
|
|
16
16
|
iframe: Protocol.DOM.Node;
|
|
17
17
|
}
|
|
18
18
|
export interface RootCauseRequest {
|
|
19
|
-
request: Types.
|
|
19
|
+
request: Types.Events.SyntheticNetworkRequest;
|
|
20
20
|
initiator?: Protocol.Network.Initiator;
|
|
21
21
|
}
|
|
22
22
|
export interface FontChange extends RootCauseRequest {
|
|
@@ -29,7 +29,7 @@ export interface LayoutShiftRootCausesData {
|
|
|
29
29
|
iframes: InjectedIframe[];
|
|
30
30
|
fontChanges: FontChange[];
|
|
31
31
|
renderBlockingRequests: RenderBlockingRequest[];
|
|
32
|
-
scriptStackTrace: Types.
|
|
32
|
+
scriptStackTrace: Types.Events.CallFrame[];
|
|
33
33
|
}
|
|
34
34
|
interface Options {
|
|
35
35
|
/** Checking iframe root causes can be an expensive operation, so it is disabled by default. */
|
|
@@ -46,18 +46,18 @@ export declare class LayoutShiftRootCauses {
|
|
|
46
46
|
* events the first time that it's called. That then populates the cache for
|
|
47
47
|
* each shift, so any subsequent calls are just a constant lookup.
|
|
48
48
|
*/
|
|
49
|
-
rootCausesForEvent(modelData:
|
|
49
|
+
rootCausesForEvent(modelData: ParsedTrace, event: Types.Events.LayoutShift): Promise<Readonly<LayoutShiftRootCausesData> | null>;
|
|
50
50
|
/**
|
|
51
51
|
* Determines potential root causes for shifts
|
|
52
52
|
*/
|
|
53
|
-
blameShifts(layoutShifts: Types.
|
|
53
|
+
blameShifts(layoutShifts: Types.Events.LayoutShift[], modelData: ParsedTrace): Promise<void>;
|
|
54
54
|
/**
|
|
55
55
|
* "LayoutInvalidations" are a set of trace events dispatched in Blink under the name
|
|
56
56
|
* "layoutInvalidationTracking", which track invalidations on the "Layout"stage of the
|
|
57
57
|
* rendering pipeline. This function utilizes this event to flag potential root causes
|
|
58
58
|
* to layout shifts.
|
|
59
59
|
*/
|
|
60
|
-
linkShiftsToLayoutInvalidations(layoutShifts: Types.
|
|
60
|
+
linkShiftsToLayoutInvalidations(layoutShifts: Types.Events.LayoutShift[], modelData: ParsedTrace): Promise<void>;
|
|
61
61
|
/**
|
|
62
62
|
* For every shift looks up the initiator of its corresponding Layout event. This initiator
|
|
63
63
|
* is assigned by the RendererHandler and contains the stack trace of the point in a script
|
|
@@ -66,23 +66,23 @@ export declare class LayoutShiftRootCauses {
|
|
|
66
66
|
* Note that a Layout cannot always be linked to a script, in that case, we cannot add a
|
|
67
67
|
* "script causing reflow" as a potential root cause to the corresponding shift.
|
|
68
68
|
*/
|
|
69
|
-
linkShiftsToLayoutEvents(layoutShifts: Types.
|
|
69
|
+
linkShiftsToLayoutEvents(layoutShifts: Types.Events.LayoutShift[], modelData: ParsedTrace): void;
|
|
70
70
|
/**
|
|
71
71
|
* Given a LayoutInvalidation trace event, determines if it was dispatched
|
|
72
72
|
* because a media element without dimensions was resized.
|
|
73
73
|
*/
|
|
74
|
-
getUnsizedMediaRootCause(layoutInvalidation: Types.
|
|
74
|
+
getUnsizedMediaRootCause(layoutInvalidation: Types.Events.LayoutInvalidationTracking, layoutInvalidationNodeId: Protocol.DOM.NodeId): Promise<UnsizedMedia | null>;
|
|
75
75
|
/**
|
|
76
76
|
* Given a LayoutInvalidation trace event, determines if it was dispatched
|
|
77
77
|
* because a node, which is an ancestor to an iframe, was injected.
|
|
78
78
|
*/
|
|
79
|
-
getIframeRootCause(layoutInvalidation: Types.
|
|
79
|
+
getIframeRootCause(layoutInvalidation: Types.Events.LayoutInvalidationTracking, layoutInvalidationNodeId: Protocol.DOM.NodeId): Promise<InjectedIframe | null>;
|
|
80
80
|
getNodeDetails(nodeId: Protocol.DOM.NodeId): Promise<Protocol.DOM.Node | null>;
|
|
81
81
|
/**
|
|
82
82
|
* Given a layout invalidation event and a sorted array, returns the subset of requests that arrived within a
|
|
83
83
|
* 500ms window before the layout invalidation.
|
|
84
84
|
*/
|
|
85
|
-
requestsInInvalidationWindow(layoutInvalidation: Types.
|
|
85
|
+
requestsInInvalidationWindow(layoutInvalidation: Types.Events.LayoutInvalidationTracking | Types.Events.ScheduleStyleInvalidationTracking, modelData: ParsedTrace): RootCauseRequest[];
|
|
86
86
|
/**
|
|
87
87
|
* Given a LayoutInvalidation trace event, determines if it was dispatched
|
|
88
88
|
* because fonts were changed and if so returns the information of all network
|
|
@@ -92,7 +92,7 @@ export declare class LayoutShiftRootCauses {
|
|
|
92
92
|
* are not processed and the cached network requests for the prepaint is
|
|
93
93
|
* returned instead.
|
|
94
94
|
*/
|
|
95
|
-
getFontChangeRootCause(layoutInvalidation: Types.
|
|
95
|
+
getFontChangeRootCause(layoutInvalidation: Types.Events.LayoutInvalidationTracking | Types.Events.ScheduleStyleInvalidationTracking, nextPrePaint: Types.Events.PrePaint, modelData: ParsedTrace): FontChange[] | null;
|
|
96
96
|
/**
|
|
97
97
|
* Given the requests that arrived within a 500ms window before the layout invalidation, returns the font
|
|
98
98
|
* requests of them.
|
|
@@ -105,7 +105,7 @@ export declare class LayoutShiftRootCauses {
|
|
|
105
105
|
* that correspond to the same prepaint are not processed and the cached network requests for the prepaint is
|
|
106
106
|
* returned instead.
|
|
107
107
|
*/
|
|
108
|
-
getRenderBlockRootCause(layoutInvalidation: Types.
|
|
108
|
+
getRenderBlockRootCause(layoutInvalidation: Types.Events.LayoutInvalidationTracking | Types.Events.ScheduleStyleInvalidationTracking, nextPrePaint: Types.Events.PrePaint, modelData: ParsedTrace): RenderBlockingRequest[] | null;
|
|
109
109
|
/**
|
|
110
110
|
* Returns a function that retrieves the active value of a given
|
|
111
111
|
* CSS property within the matched styles of the param node.
|
|
@@ -17,25 +17,8 @@ function setDefaultValue(map, shift) {
|
|
|
17
17
|
};
|
|
18
18
|
});
|
|
19
19
|
}
|
|
20
|
-
// Important: we purposefully treat `potentially_blocking` as
|
|
21
|
-
// non-render-blocking here because:
|
|
22
|
-
// 1. An async script can run on the main thread at any point, including before
|
|
23
|
-
// the page is loaded
|
|
24
|
-
// 2. An async script will never block the parsing and rendering process of the
|
|
25
|
-
// browser.
|
|
26
|
-
// 3. Therefore, from a developer's point of view, there is nothing more they
|
|
27
|
-
// can do if they've put `async` on, and within the context of Insights, we
|
|
28
|
-
// shouldn't report an async script as render blocking.
|
|
29
|
-
// In the future we may want to consider suggesting the use of `defer` over
|
|
30
|
-
// `async`, as it doesn't have this concern, but for now we'll allow `async`
|
|
31
|
-
// and not report it as an issue.
|
|
32
|
-
const NON_RENDER_BLOCKING_VALUES = new Set([
|
|
33
|
-
'non_blocking',
|
|
34
|
-
'potentially_blocking',
|
|
35
|
-
]);
|
|
36
20
|
function networkRequestIsRenderBlockingInFrame(event, frameId) {
|
|
37
|
-
|
|
38
|
-
return isRenderBlocking && event.args.data.frame === frameId;
|
|
21
|
+
return event.args.data.frame === frameId && Helpers.Network.isSyntheticNetworkRequestEventRenderBlocking(event);
|
|
39
22
|
}
|
|
40
23
|
export class LayoutShiftRootCauses {
|
|
41
24
|
#protocolInterface;
|
|
@@ -118,8 +101,7 @@ export class LayoutShiftRootCauses {
|
|
|
118
101
|
const layoutInvalidationNodeId = nodeIdsByBackendIdMap.get(layoutInvalidation.args.data.nodeId);
|
|
119
102
|
let unsizedMediaRootCause = null;
|
|
120
103
|
let iframeRootCause = null;
|
|
121
|
-
if (layoutInvalidationNodeId !== undefined &&
|
|
122
|
-
Types.TraceEvents.isTraceEventLayoutInvalidationTracking(layoutInvalidation)) {
|
|
104
|
+
if (layoutInvalidationNodeId !== undefined && Types.Events.isLayoutInvalidationTracking(layoutInvalidation)) {
|
|
123
105
|
unsizedMediaRootCause = await this.getUnsizedMediaRootCause(layoutInvalidation, layoutInvalidationNodeId);
|
|
124
106
|
iframeRootCause = await this.getIframeRootCause(layoutInvalidation, layoutInvalidationNodeId);
|
|
125
107
|
}
|
|
@@ -172,7 +154,7 @@ export class LayoutShiftRootCauses {
|
|
|
172
154
|
const shiftsByPrePaint = getShiftsByPrePaintEvents(layoutShifts, prePaintEvents);
|
|
173
155
|
const eventTriggersLayout = ({ name }) => {
|
|
174
156
|
const knownName = name;
|
|
175
|
-
return knownName === "Layout" /* Types.
|
|
157
|
+
return knownName === "Layout" /* Types.Events.Name.LAYOUT */;
|
|
176
158
|
};
|
|
177
159
|
const layoutEvents = modelData.Renderer.allTraceEntries.filter(eventTriggersLayout);
|
|
178
160
|
for (const layout of layoutEvents) {
|
|
@@ -217,7 +199,7 @@ export class LayoutShiftRootCauses {
|
|
|
217
199
|
*/
|
|
218
200
|
async getUnsizedMediaRootCause(layoutInvalidation, layoutInvalidationNodeId) {
|
|
219
201
|
// Filter events to resizes only.
|
|
220
|
-
if (layoutInvalidation.args.data.reason !== "Size changed" /* Types.
|
|
202
|
+
if (layoutInvalidation.args.data.reason !== "Size changed" /* Types.Events.LayoutInvalidationReason.SIZE_CHANGED */) {
|
|
221
203
|
return null;
|
|
222
204
|
}
|
|
223
205
|
const layoutInvalidationNode = await this.getNodeDetails(layoutInvalidationNodeId);
|
|
@@ -245,8 +227,8 @@ export class LayoutShiftRootCauses {
|
|
|
245
227
|
return null;
|
|
246
228
|
}
|
|
247
229
|
if (!layoutInvalidation.args.data.nodeName?.startsWith('IFRAME') &&
|
|
248
|
-
layoutInvalidation.args.data.reason !== "Style changed" /* Types.
|
|
249
|
-
layoutInvalidation.args.data.reason !== "Added to layout" /* Types.
|
|
230
|
+
layoutInvalidation.args.data.reason !== "Style changed" /* Types.Events.LayoutInvalidationReason.STYLE_CHANGED */ &&
|
|
231
|
+
layoutInvalidation.args.data.reason !== "Added to layout" /* Types.Events.LayoutInvalidationReason.ADDED_TO_LAYOUT */) {
|
|
250
232
|
return null;
|
|
251
233
|
}
|
|
252
234
|
const layoutInvalidationNode = await this.getNodeDetails(layoutInvalidationNodeId);
|
|
@@ -311,7 +293,7 @@ export class LayoutShiftRootCauses {
|
|
|
311
293
|
* returned instead.
|
|
312
294
|
*/
|
|
313
295
|
getFontChangeRootCause(layoutInvalidation, nextPrePaint, modelData) {
|
|
314
|
-
if (layoutInvalidation.args.data.reason !== "Fonts changed" /* Types.
|
|
296
|
+
if (layoutInvalidation.args.data.reason !== "Fonts changed" /* Types.Events.LayoutInvalidationReason.FONTS_CHANGED */) {
|
|
315
297
|
return null;
|
|
316
298
|
}
|
|
317
299
|
// Prevent computing the result of this function multiple times per PrePaint event.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LayoutShift.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/root-causes/LayoutShift.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2023 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as Platform from '../../../core/platform/platform.js';\nimport type * as Protocol from '../../../generated/protocol.js';\nimport {type TraceParseData} from '../handlers/types.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nimport {type RootCauseProtocolInterface} from './RootCauses.js';\n\nexport type CSSDimensions = {\n width?: string,\n height?: string,\n aspectRatio?: string,\n};\n\nexport interface UnsizedMedia {\n node: Protocol.DOM.Node;\n authoredDimensions?: CSSDimensions;\n computedDimensions: CSSDimensions;\n}\n\nexport interface InjectedIframe {\n iframe: Protocol.DOM.Node;\n}\n\nexport interface RootCauseRequest {\n request: Types.TraceEvents.SyntheticNetworkRequest;\n initiator?: Protocol.Network.Initiator;\n}\n\nexport interface FontChange extends RootCauseRequest {\n fontFace: Protocol.CSS.FontFace;\n}\n\nexport interface RenderBlockingRequest extends RootCauseRequest {}\n\nexport interface LayoutShiftRootCausesData {\n unsizedMedia: UnsizedMedia[];\n iframes: InjectedIframe[];\n fontChanges: FontChange[];\n renderBlockingRequests: RenderBlockingRequest[];\n scriptStackTrace: Types.TraceEvents.TraceEventCallFrame[];\n}\n\nconst fontRequestsByPrePaint = new Map<Types.TraceEvents.TraceEventPrePaint, FontChange[]|null>();\nconst renderBlocksByPrePaint = new Map<Types.TraceEvents.TraceEventPrePaint, RenderBlockingRequest[]|null>();\n\nfunction setDefaultValue(\n map: Map<Types.TraceEvents.TraceEventLayoutShift, LayoutShiftRootCausesData>,\n shift: Types.TraceEvents.TraceEventLayoutShift): void {\n Platform.MapUtilities.getWithDefault(map, shift, () => {\n return {\n unsizedMedia: [],\n iframes: [],\n fontChanges: [],\n renderBlockingRequests: [],\n scriptStackTrace: [],\n };\n });\n}\n\n// Important: we purposefully treat `potentially_blocking` as\n// non-render-blocking here because:\n// 1. An async script can run on the main thread at any point, including before\n// the page is loaded\n// 2. An async script will never block the parsing and rendering process of the\n// browser.\n// 3. Therefore, from a developer's point of view, there is nothing more they\n// can do if they've put `async` on, and within the context of Insights, we\n// shouldn't report an async script as render blocking.\n// In the future we may want to consider suggesting the use of `defer` over\n// `async`, as it doesn't have this concern, but for now we'll allow `async`\n// and not report it as an issue.\nconst NON_RENDER_BLOCKING_VALUES = new Set<Types.TraceEvents.RenderBlocking>([\n 'non_blocking',\n 'potentially_blocking',\n]);\nfunction networkRequestIsRenderBlockingInFrame(\n event: Types.TraceEvents.SyntheticNetworkRequest, frameId: string): boolean {\n const isRenderBlocking = !NON_RENDER_BLOCKING_VALUES.has(event.args.data.renderBlocking);\n return isRenderBlocking && event.args.data.frame === frameId;\n}\n\ninterface Options {\n /** Checking iframe root causes can be an expensive operation, so it is disabled by default. */\n enableIframeRootCauses?: boolean;\n}\n\nexport class LayoutShiftRootCauses {\n #protocolInterface: RootCauseProtocolInterface;\n #rootCauseCacheMap = new Map<Types.TraceEvents.TraceEventLayoutShift, LayoutShiftRootCausesData>();\n #nodeDetailsCache = new Map<Protocol.DOM.NodeId, Protocol.DOM.Node|null>();\n #iframeRootCausesEnabled: boolean;\n\n constructor(protocolInterface: RootCauseProtocolInterface, options?: Options) {\n this.#protocolInterface = protocolInterface;\n this.#iframeRootCausesEnabled = options?.enableIframeRootCauses ?? false;\n }\n\n /**\n * Calculates the potential root causes for a given layout shift event. Once\n * calculated, this data is cached.\n * Note: because you need all layout shift data at once to calculate these\n * correctly, this function will parse the root causes for _all_ layout shift\n * events the first time that it's called. That then populates the cache for\n * each shift, so any subsequent calls are just a constant lookup.\n */\n async rootCausesForEvent(modelData: TraceParseData, event: Types.TraceEvents.TraceEventLayoutShift):\n Promise<Readonly<LayoutShiftRootCausesData>|null> {\n const cachedResult = this.#rootCauseCacheMap.get(event);\n if (cachedResult) {\n return cachedResult;\n }\n\n const allLayoutShifts = modelData.LayoutShifts.clusters.flatMap(cluster => cluster.events);\n // Make sure a value in the cache is set even for shifts that don't have a root cause,\n // so that we don't have to recompute when no root causes are found. In case a cause\n // for a shift is found, the default value is replaced.\n allLayoutShifts.forEach(shift => setDefaultValue(this.#rootCauseCacheMap, shift));\n\n // Populate the cache\n await this.blameShifts(\n allLayoutShifts,\n modelData,\n );\n\n const resultForEvent = this.#rootCauseCacheMap.get(event);\n if (!resultForEvent) {\n // No root causes available for this layout shift.\n return null;\n }\n return resultForEvent;\n }\n\n /**\n * Determines potential root causes for shifts\n */\n async blameShifts(\n layoutShifts: Types.TraceEvents.TraceEventLayoutShift[],\n modelData: TraceParseData,\n ): Promise<void> {\n await this.linkShiftsToLayoutInvalidations(layoutShifts, modelData);\n this.linkShiftsToLayoutEvents(layoutShifts, modelData);\n }\n\n /**\n * \"LayoutInvalidations\" are a set of trace events dispatched in Blink under the name\n * \"layoutInvalidationTracking\", which track invalidations on the \"Layout\"stage of the\n * rendering pipeline. This function utilizes this event to flag potential root causes\n * to layout shifts.\n */\n async linkShiftsToLayoutInvalidations(\n layoutShifts: Types.TraceEvents.TraceEventLayoutShift[], modelData: TraceParseData): Promise<void> {\n const {prePaintEvents, layoutInvalidationEvents, scheduleStyleInvalidationEvents, backendNodeIds} =\n modelData.LayoutShifts;\n\n // For the purposes of determining root causes of layout shifts, we\n // consider scheduleStyleInvalidationTracking and\n // LayoutInvalidationTracking events as events that could have been the\n // cause of the layout shift.\n const eventsForLayoutInvalidation: Array<Types.TraceEvents.TraceEventLayoutInvalidationTracking|\n Types.TraceEvents.TraceEventScheduleStyleInvalidationTracking> =\n [...layoutInvalidationEvents, ...scheduleStyleInvalidationEvents];\n\n const nodes = await this.#protocolInterface.pushNodesByBackendIdsToFrontend(backendNodeIds);\n const nodeIdsByBackendIdMap = new Map<Protocol.DOM.BackendNodeId, Protocol.DOM.NodeId>();\n for (let i = 0; i < backendNodeIds.length; i++) {\n nodeIdsByBackendIdMap.set(backendNodeIds[i], nodes[i]);\n }\n\n // Maps from PrePaint events to LayoutShifts that occured in each one.\n const shiftsByPrePaint = getShiftsByPrePaintEvents(layoutShifts, prePaintEvents);\n for (const layoutInvalidation of eventsForLayoutInvalidation) {\n // Get the first PrePaint event that happened after the current LayoutInvalidation event.\n const nextPrePaintIndex = Platform.ArrayUtilities.nearestIndexFromBeginning(\n prePaintEvents, prePaint => prePaint.ts > layoutInvalidation.ts);\n if (nextPrePaintIndex === null) {\n // No PrePaint event registered after this LayoutInvalidation. Continue.\n continue;\n }\n const nextPrePaint = prePaintEvents[nextPrePaintIndex];\n const subsequentShifts = shiftsByPrePaint.get(nextPrePaint);\n if (!subsequentShifts) {\n // The PrePaint after the current LayoutInvalidation doesn't contain shifts.\n continue;\n }\n const fontChangeRootCause = this.getFontChangeRootCause(layoutInvalidation, nextPrePaint, modelData);\n const renderBlockRootCause = this.getRenderBlockRootCause(layoutInvalidation, nextPrePaint, modelData);\n const layoutInvalidationNodeId = nodeIdsByBackendIdMap.get(layoutInvalidation.args.data.nodeId);\n let unsizedMediaRootCause: UnsizedMedia|null = null;\n let iframeRootCause: InjectedIframe|null = null;\n if (layoutInvalidationNodeId !== undefined &&\n Types.TraceEvents.isTraceEventLayoutInvalidationTracking(layoutInvalidation)) {\n unsizedMediaRootCause = await this.getUnsizedMediaRootCause(layoutInvalidation, layoutInvalidationNodeId);\n iframeRootCause = await this.getIframeRootCause(layoutInvalidation, layoutInvalidationNodeId);\n }\n\n if (!unsizedMediaRootCause && !iframeRootCause && !fontChangeRootCause && !renderBlockRootCause) {\n continue;\n }\n\n // Add found potential root causes to all the shifts in this PrePaint and populate the cache.\n for (const shift of subsequentShifts) {\n const rootCausesForShift = Platform.MapUtilities.getWithDefault(this.#rootCauseCacheMap, shift, () => {\n return {\n unsizedMedia: [],\n iframes: [],\n fontChanges: [],\n renderBlockingRequests: [],\n scriptStackTrace: [],\n };\n });\n if (unsizedMediaRootCause &&\n !rootCausesForShift.unsizedMedia.some(media => media.node.nodeId === unsizedMediaRootCause?.node.nodeId) &&\n shift.args.frame === layoutInvalidation.args.data.frame) {\n rootCausesForShift.unsizedMedia.push(unsizedMediaRootCause);\n }\n if (iframeRootCause &&\n !rootCausesForShift.iframes.some(\n injectedIframe => injectedIframe.iframe.nodeId === iframeRootCause?.iframe.nodeId)) {\n rootCausesForShift.iframes.push(iframeRootCause);\n }\n if (fontChangeRootCause) {\n // Unlike other root causes, we calculate fonts causing a shift only once,\n // which means we assign the built array instead of appending new objects\n // to it.\n rootCausesForShift.fontChanges = fontChangeRootCause;\n }\n if (renderBlockRootCause) {\n rootCausesForShift.renderBlockingRequests = renderBlockRootCause;\n }\n }\n }\n }\n\n /**\n * For every shift looks up the initiator of its corresponding Layout event. This initiator\n * is assigned by the RendererHandler and contains the stack trace of the point in a script\n * that caused a style recalculation or a relayout. This stack trace is added to the shift's\n * potential root causes.\n * Note that a Layout cannot always be linked to a script, in that case, we cannot add a\n * \"script causing reflow\" as a potential root cause to the corresponding shift.\n */\n linkShiftsToLayoutEvents(layoutShifts: Types.TraceEvents.TraceEventLayoutShift[], modelData: TraceParseData): void {\n const {prePaintEvents} = modelData.LayoutShifts;\n // Maps from PrePaint events to LayoutShifts that occured in each one.\n const shiftsByPrePaint = getShiftsByPrePaintEvents(layoutShifts, prePaintEvents);\n\n const eventTriggersLayout = ({name}: {name: string}): boolean => {\n const knownName = name as Types.TraceEvents.KnownEventName;\n return knownName === Types.TraceEvents.KnownEventName.Layout;\n };\n const layoutEvents = modelData.Renderer.allTraceEntries.filter(eventTriggersLayout);\n for (const layout of layoutEvents) {\n // Get the first PrePaint event that happened after the current layout event.\n const nextPrePaintIndex = Platform.ArrayUtilities.nearestIndexFromBeginning(\n prePaintEvents, prePaint => prePaint.ts > layout.ts + (layout.dur || 0));\n if (nextPrePaintIndex === null) {\n // No PrePaint event registered after this LayoutInvalidation. Continue.\n continue;\n }\n const nextPrePaint = prePaintEvents[nextPrePaintIndex];\n const subsequentShifts = shiftsByPrePaint.get(nextPrePaint);\n if (!subsequentShifts) {\n // The PrePaint after the current LayoutInvalidation doesn't contain shifts.\n continue;\n }\n const layoutNode = modelData.Renderer.entryToNode.get(layout);\n const initiator = layoutNode ? modelData.Initiators.eventToInitiator.get(layoutNode.entry) : null;\n const stackTrace = initiator?.args?.data?.stackTrace;\n if (!stackTrace) {\n continue;\n }\n // Add found potential root causes to all the shifts in this PrePaint and populate the cache.\n for (const shift of subsequentShifts) {\n const rootCausesForShift = Platform.MapUtilities.getWithDefault(this.#rootCauseCacheMap, shift, () => {\n return {\n unsizedMedia: [],\n iframes: [],\n fontChanges: [],\n renderBlockingRequests: [],\n scriptStackTrace: [],\n };\n });\n if (rootCausesForShift.scriptStackTrace.length === 0) {\n rootCausesForShift.scriptStackTrace = stackTrace;\n }\n }\n }\n }\n\n /**\n * Given a LayoutInvalidation trace event, determines if it was dispatched\n * because a media element without dimensions was resized.\n */\n async getUnsizedMediaRootCause(\n layoutInvalidation: Types.TraceEvents.TraceEventLayoutInvalidationTracking,\n layoutInvalidationNodeId: Protocol.DOM.NodeId): Promise<UnsizedMedia|null> {\n // Filter events to resizes only.\n if (layoutInvalidation.args.data.reason !== Types.TraceEvents.LayoutInvalidationReason.SIZE_CHANGED) {\n return null;\n }\n\n const layoutInvalidationNode = await this.getNodeDetails(layoutInvalidationNodeId);\n if (!layoutInvalidationNode) {\n return null;\n }\n\n const computedStylesList = await this.#protocolInterface.getComputedStyleForNode(layoutInvalidationNode.nodeId);\n const computedStyles = new Map(computedStylesList.map(item => [item.name, item.value]));\n if (computedStyles && !(await nodeIsUnfixedMedia(layoutInvalidationNode, computedStyles))) {\n return null;\n }\n const authoredDimensions = await this.getNodeAuthoredDimensions(layoutInvalidationNode);\n if (dimensionsAreExplicit(authoredDimensions)) {\n return null;\n }\n const computedDimensions = computedStyles ? getNodeComputedDimensions(computedStyles) : {};\n\n return {node: layoutInvalidationNode, authoredDimensions, computedDimensions};\n }\n\n /**\n * Given a LayoutInvalidation trace event, determines if it was dispatched\n * because a node, which is an ancestor to an iframe, was injected.\n */\n async getIframeRootCause(\n layoutInvalidation: Types.TraceEvents.TraceEventLayoutInvalidationTracking,\n layoutInvalidationNodeId: Protocol.DOM.NodeId): Promise<InjectedIframe|null> {\n if (!this.#iframeRootCausesEnabled) {\n return null;\n }\n\n if (!layoutInvalidation.args.data.nodeName?.startsWith('IFRAME') &&\n layoutInvalidation.args.data.reason !== Types.TraceEvents.LayoutInvalidationReason.STYLE_CHANGED &&\n layoutInvalidation.args.data.reason !== Types.TraceEvents.LayoutInvalidationReason.ADDED_TO_LAYOUT) {\n return null;\n }\n\n const layoutInvalidationNode = await this.getNodeDetails(layoutInvalidationNodeId);\n if (!layoutInvalidationNode) {\n return null;\n }\n\n const iframe = firstIframeInDOMTree(layoutInvalidationNode);\n if (!iframe) {\n return null;\n }\n return {iframe};\n }\n\n async getNodeDetails(nodeId: Protocol.DOM.NodeId): Promise<Protocol.DOM.Node|null> {\n let nodeDetails = this.#nodeDetailsCache.get(nodeId);\n if (nodeDetails !== undefined) {\n return nodeDetails;\n }\n\n nodeDetails = await this.#protocolInterface.getNode(nodeId);\n this.#nodeDetailsCache.set(nodeId, nodeDetails);\n\n return nodeDetails;\n }\n\n /**\n * Given a layout invalidation event and a sorted array, returns the subset of requests that arrived within a\n * 500ms window before the layout invalidation.\n */\n requestsInInvalidationWindow(\n layoutInvalidation: Types.TraceEvents.TraceEventLayoutInvalidationTracking|\n Types.TraceEvents.TraceEventScheduleStyleInvalidationTracking,\n modelData: TraceParseData): RootCauseRequest[] {\n const requestsSortedByEndTime = modelData.NetworkRequests.byTime.sort((req1, req2) => {\n const req1EndTime = req1.ts + req1.dur;\n const req2EndTime = req2.ts + req2.dur;\n return req1EndTime - req2EndTime;\n });\n\n const lastRequestIndex = Platform.ArrayUtilities.nearestIndexFromEnd(\n requestsSortedByEndTime, request => request.ts + request.dur < layoutInvalidation.ts);\n if (lastRequestIndex === null) {\n return [];\n }\n\n const MAX_DELTA_FOR_FONT_REQUEST = Helpers.Timing.secondsToMicroseconds(Types.Timing.Seconds(0.5));\n\n const requestsInInvalidationWindow: RootCauseRequest[] = [];\n\n // Get all requests finished within the valid window.\n for (let i = lastRequestIndex; i > -1; i--) {\n const previousRequest = requestsSortedByEndTime[i];\n const previousRequestEndTime = previousRequest.ts + previousRequest.dur;\n if (layoutInvalidation.ts - previousRequestEndTime < MAX_DELTA_FOR_FONT_REQUEST) {\n const requestInInvalidationWindow: RootCauseRequest = {request: previousRequest};\n\n const initiator = this.#protocolInterface.getInitiatorForRequest(\n previousRequest.args.data.url as Platform.DevToolsPath.UrlString);\n requestInInvalidationWindow.initiator = initiator || undefined;\n requestsInInvalidationWindow.push(requestInInvalidationWindow);\n } else {\n // No more requests fit in the time window.\n break;\n }\n }\n return requestsInInvalidationWindow;\n }\n\n /**\n * Given a LayoutInvalidation trace event, determines if it was dispatched\n * because fonts were changed and if so returns the information of all network\n * request with which the fonts were possibly fetched, if any. The computed\n * network requests are cached for the corresponding prepaint event, meaning\n * that other LayoutInvalidation events that correspond to the same prepaint\n * are not processed and the cached network requests for the prepaint is\n * returned instead.\n */\n getFontChangeRootCause(\n layoutInvalidation: Types.TraceEvents.TraceEventLayoutInvalidationTracking|\n Types.TraceEvents.TraceEventScheduleStyleInvalidationTracking,\n nextPrePaint: Types.TraceEvents.TraceEventPrePaint, modelData: TraceParseData): FontChange[]|null {\n if (layoutInvalidation.args.data.reason !== Types.TraceEvents.LayoutInvalidationReason.FONTS_CHANGED) {\n return null;\n }\n // Prevent computing the result of this function multiple times per PrePaint event.\n const fontRequestsForPrepaint = fontRequestsByPrePaint.get(nextPrePaint);\n if (fontRequestsForPrepaint !== undefined) {\n return fontRequestsForPrepaint;\n }\n\n const fontRequestsInThisPrepaint =\n this.getFontRequestsInInvalidationWindow(this.requestsInInvalidationWindow(layoutInvalidation, modelData));\n fontRequestsByPrePaint.set(nextPrePaint, fontRequestsInThisPrepaint);\n return fontRequestsInThisPrepaint;\n }\n\n /**\n * Given the requests that arrived within a 500ms window before the layout invalidation, returns the font\n * requests of them.\n */\n getFontRequestsInInvalidationWindow(requestsInInvalidationWindow: RootCauseRequest[]): FontChange[] {\n const fontRequests: FontChange[] = [];\n\n // Get all requests finished within the valid window.\n for (let i = 0; i < requestsInInvalidationWindow.length; i++) {\n const fontRequest = requestsInInvalidationWindow[i] as FontChange;\n if (!fontRequest.request.args.data.mimeType.startsWith('font')) {\n continue;\n }\n\n const fontFace = this.#protocolInterface.fontFaceForSource(fontRequest.request.args.data.url);\n if (!fontFace || fontFace.fontDisplay === 'optional') {\n // Setting font-display to optional is part of what the developer\n // can do to avoid layout shifts due to FOIT/FOUT, as such we cannot\n // suggest any actionable insight here.\n continue;\n }\n fontRequest.fontFace = fontFace;\n fontRequests.push(fontRequest);\n }\n return fontRequests;\n }\n\n /**\n * Given a LayoutInvalidation trace event, determines if it arrived within a 500ms window before the layout\n * invalidation and if so returns the information of all network request, if any. The computed network\n * requests are cached for the corresponding prepaint event, meaning that other LayoutInvalidation events\n * that correspond to the same prepaint are not processed and the cached network requests for the prepaint is\n * returned instead.\n */\n getRenderBlockRootCause(\n layoutInvalidation: Types.TraceEvents.TraceEventLayoutInvalidationTracking|\n Types.TraceEvents.TraceEventScheduleStyleInvalidationTracking,\n nextPrePaint: Types.TraceEvents.TraceEventPrePaint, modelData: TraceParseData): RenderBlockingRequest[]|null {\n // Prevent computing the result of this function multiple times per PrePaint event.\n const renderBlocksInPrepaint = renderBlocksByPrePaint.get(nextPrePaint);\n if (renderBlocksInPrepaint !== undefined) {\n return renderBlocksInPrepaint;\n }\n\n const renderBlocksInThisPrepaint =\n getRenderBlockRequestsInInvalidationWindow(this.requestsInInvalidationWindow(layoutInvalidation, modelData));\n renderBlocksByPrePaint.set(nextPrePaint, renderBlocksInThisPrepaint);\n return renderBlocksInThisPrepaint;\n }\n\n /**\n * Returns a function that retrieves the active value of a given\n * CSS property within the matched styles of the param node.\n * The first occurence within the matched styles is returned and the\n * value is looked up in the following order, which follows CSS\n * specificity:\n * 1. Inline styles.\n * 2. CSS rules matching this node, from all applicable stylesheets.\n * 3. Attribute defined styles.\n */\n async nodeMatchedStylesPropertyGetter(node: Protocol.DOM.Node): Promise<((property: string) => string | null)> {\n const response = await this.#protocolInterface.getMatchedStylesForNode(node.nodeId);\n\n function cssPropertyValueGetter(cssProperty: string): string|null {\n let prop = response.inlineStyle?.cssProperties.find(prop => prop.name === cssProperty);\n if (prop) {\n return prop.value;\n }\n\n for (const {rule} of response.matchedCSSRules || []) {\n const prop = rule.style.cssProperties.find(prop => prop.name === cssProperty);\n if (prop) {\n return prop.value;\n }\n }\n\n prop = response.attributesStyle?.cssProperties.find(prop => prop.name === cssProperty);\n if (prop) {\n return prop.value;\n }\n\n return null;\n }\n return cssPropertyValueGetter;\n }\n\n /**\n * Returns the CSS dimensions set to the node from its matched styles.\n */\n async getNodeAuthoredDimensions(node: Protocol.DOM.Node): Promise<CSSDimensions> {\n const authoredDimensions: CSSDimensions = {};\n\n const cssMatchedRulesGetter = await this.nodeMatchedStylesPropertyGetter(node);\n if (!cssMatchedRulesGetter) {\n return authoredDimensions;\n }\n\n const attributesFlat = node.attributes || [];\n const attributes = [];\n for (let i = 0; i < attributesFlat.length; i += 2) {\n attributes.push({name: attributesFlat[i], value: attributesFlat[i + 1]});\n }\n\n const htmlHeight = attributes.find(attr => attr.name === 'height' && htmlAttributeIsExplicit(attr));\n const htmlWidth = attributes.find(attr => attr.name === 'width' && htmlAttributeIsExplicit(attr));\n\n const cssExplicitAspectRatio = cssMatchedRulesGetter('aspect-ratio') || undefined;\n\n if (htmlHeight && htmlWidth && cssExplicitAspectRatio) {\n return {height: htmlHeight.value, width: htmlWidth.value, aspectRatio: cssExplicitAspectRatio};\n }\n\n const cssHeight = cssMatchedRulesGetter('height') || undefined;\n const cssWidth = cssMatchedRulesGetter('width') || undefined;\n return {height: cssHeight, width: cssWidth, aspectRatio: cssExplicitAspectRatio};\n }\n}\n\n/**\n * Given the requests that arrived within a 500ms window before the layout invalidation, returns the render\n * block requests of them.\n */\nfunction getRenderBlockRequestsInInvalidationWindow(requestsInInvalidationWindow: RootCauseRequest[]):\n RenderBlockingRequest[] {\n const renderBlockingRequests: RenderBlockingRequest[] = [];\n\n // Get all requests finished within the valid window.\n for (let i = 0; i < requestsInInvalidationWindow.length; i++) {\n const mainFrameId = requestsInInvalidationWindow[i].request.args.data.frame;\n if (!networkRequestIsRenderBlockingInFrame(requestsInInvalidationWindow[i].request, mainFrameId)) {\n continue;\n }\n renderBlockingRequests.push(requestsInInvalidationWindow[i] as RenderBlockingRequest);\n }\n return renderBlockingRequests;\n}\n\nfunction firstIframeInDOMTree(root: Protocol.DOM.Node): Protocol.DOM.Node|null {\n if (root.nodeName === 'IFRAME') {\n return root;\n }\n const children = root.children;\n if (!children) {\n return null;\n }\n for (const child of children) {\n const iFrameInChild = firstIframeInDOMTree(child);\n if (iFrameInChild) {\n return iFrameInChild;\n }\n }\n return null;\n}\n\nfunction cssPropertyIsExplicitlySet(propertyValue: string): boolean {\n return !['auto', 'initial', 'unset', 'inherit'].includes(propertyValue);\n}\n\nfunction htmlAttributeIsExplicit(attr: {value: string}): boolean {\n return parseInt(attr.value, 10) >= 0;\n}\n\nfunction computedStyleHasBackroundImage(computedStyle: Map<string, string>): boolean {\n const CSS_URL_REGEX = /^url\\(\"([^\"]+)\"\\)$/;\n const backgroundImage = computedStyle.get('background-image');\n if (!backgroundImage) {\n return false;\n }\n return CSS_URL_REGEX.test(backgroundImage);\n}\n\nfunction computedStyleHasFixedPosition(computedStyle: Map<string, string>): boolean {\n const position = computedStyle.get('position');\n if (!position) {\n return false;\n }\n return position === 'fixed' || position === 'absolute';\n}\n\nfunction getNodeComputedDimensions(computedStyle: Map<string, string>): CSSDimensions {\n const computedDimensions: CSSDimensions = {};\n computedDimensions.height = computedStyle.get('height');\n computedDimensions.width = computedStyle.get('width');\n computedDimensions.aspectRatio = computedStyle.get('aspect-ratio');\n return computedDimensions;\n}\n\n/**\n * Determines if a node is a media element and is not fixed positioned\n * (i.e. \"position: fixed;\" or \"position: absolute;\")\n */\nasync function nodeIsUnfixedMedia(node: Protocol.DOM.Node, computedStyle: Map<string, string>): Promise<boolean> {\n const localName = node.localName;\n const isBackgroundImage = computedStyleHasBackroundImage(computedStyle);\n if (localName !== 'img' && localName !== 'video' && !isBackgroundImage) {\n // Not a media element.\n return false;\n }\n const isFixed = computedStyleHasFixedPosition(computedStyle);\n return !isFixed;\n}\n\n/**\n * Determines if a CSS dimensions object explicitly defines both width and height\n * (i.e. not set to auto, inherit, etc.)\n */\nfunction dimensionsAreExplicit(dimensions: CSSDimensions): boolean {\n const {height, width, aspectRatio} = dimensions;\n\n const explicitHeight = Boolean(height && cssPropertyIsExplicitlySet(height));\n const explicitWidth = Boolean(width && cssPropertyIsExplicitlySet(width));\n const explicitAspectRatio = Boolean(aspectRatio && cssPropertyIsExplicitlySet(aspectRatio));\n\n const explicitWithAR = (explicitHeight || explicitWidth) && explicitAspectRatio;\n return (explicitHeight && explicitWidth) || explicitWithAR;\n}\n\n/**\n * Given an array of layout shift and PrePaint events, returns a mapping from\n * PrePaint events to layout shifts dispatched within it.\n */\nfunction getShiftsByPrePaintEvents(\n layoutShifts: Types.TraceEvents.TraceEventLayoutShift[],\n prePaintEvents: Types.TraceEvents.TraceEventPrePaint[],\n ): Map<Types.TraceEvents.TraceEventPrePaint, Types.TraceEvents.TraceEventLayoutShift[]> {\n // Maps from PrePaint events to LayoutShifts that occured in each one.\n const shiftsByPrePaint = new Map<Types.TraceEvents.TraceEventPrePaint, Types.TraceEvents.TraceEventLayoutShift[]>();\n\n // Associate all shifts to their corresponding PrePaint.\n for (const prePaintEvent of prePaintEvents) {\n const firstShiftIndex =\n Platform.ArrayUtilities.nearestIndexFromBeginning(layoutShifts, shift => shift.ts >= prePaintEvent.ts);\n if (firstShiftIndex === null) {\n // No layout shifts registered after this PrePaint start. Continue.\n continue;\n }\n for (let i = firstShiftIndex; i < layoutShifts.length; i++) {\n const shift = layoutShifts[i];\n if (shift.ts >= prePaintEvent.ts && shift.ts <= prePaintEvent.ts + prePaintEvent.dur) {\n const shiftsInPrePaint = Platform.MapUtilities.getWithDefault(shiftsByPrePaint, prePaintEvent, () => []);\n shiftsInPrePaint.push(shift);\n }\n if (shift.ts > prePaintEvent.ts + prePaintEvent.dur) {\n // Reached the end of this PrePaint. Continue to the next one.\n break;\n }\n }\n }\n return shiftsByPrePaint;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"LayoutShift.js","sourceRoot":"","sources":["../../../../../../../front_end/models/trace/root-causes/LayoutShift.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2023 The Chromium Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style license that can be\n// found in the LICENSE file.\n\nimport * as Platform from '../../../core/platform/platform.js';\nimport type * as Protocol from '../../../generated/protocol.js';\nimport type {ParsedTrace} from '../handlers/types.js';\nimport * as Helpers from '../helpers/helpers.js';\nimport * as Types from '../types/types.js';\n\nimport type {RootCauseProtocolInterface} from './RootCauses.js';\n\nexport type CSSDimensions = {\n width?: string,\n height?: string,\n aspectRatio?: string,\n};\n\nexport interface UnsizedMedia {\n node: Protocol.DOM.Node;\n authoredDimensions?: CSSDimensions;\n computedDimensions: CSSDimensions;\n}\n\nexport interface InjectedIframe {\n iframe: Protocol.DOM.Node;\n}\n\nexport interface RootCauseRequest {\n request: Types.Events.SyntheticNetworkRequest;\n initiator?: Protocol.Network.Initiator;\n}\n\nexport interface FontChange extends RootCauseRequest {\n fontFace: Protocol.CSS.FontFace;\n}\n\nexport interface RenderBlockingRequest extends RootCauseRequest {}\n\nexport interface LayoutShiftRootCausesData {\n unsizedMedia: UnsizedMedia[];\n iframes: InjectedIframe[];\n fontChanges: FontChange[];\n renderBlockingRequests: RenderBlockingRequest[];\n scriptStackTrace: Types.Events.CallFrame[];\n}\n\nconst fontRequestsByPrePaint = new Map<Types.Events.PrePaint, FontChange[]|null>();\nconst renderBlocksByPrePaint = new Map<Types.Events.PrePaint, RenderBlockingRequest[]|null>();\n\nfunction setDefaultValue(\n map: Map<Types.Events.LayoutShift, LayoutShiftRootCausesData>, shift: Types.Events.LayoutShift): void {\n Platform.MapUtilities.getWithDefault(map, shift, () => {\n return {\n unsizedMedia: [],\n iframes: [],\n fontChanges: [],\n renderBlockingRequests: [],\n scriptStackTrace: [],\n };\n });\n}\n\nfunction networkRequestIsRenderBlockingInFrame(event: Types.Events.SyntheticNetworkRequest, frameId: string): boolean {\n return event.args.data.frame === frameId && Helpers.Network.isSyntheticNetworkRequestEventRenderBlocking(event);\n}\n\ninterface Options {\n /** Checking iframe root causes can be an expensive operation, so it is disabled by default. */\n enableIframeRootCauses?: boolean;\n}\n\nexport class LayoutShiftRootCauses {\n #protocolInterface: RootCauseProtocolInterface;\n #rootCauseCacheMap = new Map<Types.Events.LayoutShift, LayoutShiftRootCausesData>();\n #nodeDetailsCache = new Map<Protocol.DOM.NodeId, Protocol.DOM.Node|null>();\n #iframeRootCausesEnabled: boolean;\n\n constructor(protocolInterface: RootCauseProtocolInterface, options?: Options) {\n this.#protocolInterface = protocolInterface;\n this.#iframeRootCausesEnabled = options?.enableIframeRootCauses ?? false;\n }\n\n /**\n * Calculates the potential root causes for a given layout shift event. Once\n * calculated, this data is cached.\n * Note: because you need all layout shift data at once to calculate these\n * correctly, this function will parse the root causes for _all_ layout shift\n * events the first time that it's called. That then populates the cache for\n * each shift, so any subsequent calls are just a constant lookup.\n */\n async rootCausesForEvent(modelData: ParsedTrace, event: Types.Events.LayoutShift):\n Promise<Readonly<LayoutShiftRootCausesData>|null> {\n const cachedResult = this.#rootCauseCacheMap.get(event);\n if (cachedResult) {\n return cachedResult;\n }\n\n const allLayoutShifts = modelData.LayoutShifts.clusters.flatMap(cluster => cluster.events);\n // Make sure a value in the cache is set even for shifts that don't have a root cause,\n // so that we don't have to recompute when no root causes are found. In case a cause\n // for a shift is found, the default value is replaced.\n allLayoutShifts.forEach(shift => setDefaultValue(this.#rootCauseCacheMap, shift));\n\n // Populate the cache\n await this.blameShifts(\n allLayoutShifts,\n modelData,\n );\n\n const resultForEvent = this.#rootCauseCacheMap.get(event);\n if (!resultForEvent) {\n // No root causes available for this layout shift.\n return null;\n }\n return resultForEvent;\n }\n\n /**\n * Determines potential root causes for shifts\n */\n async blameShifts(\n layoutShifts: Types.Events.LayoutShift[],\n modelData: ParsedTrace,\n ): Promise<void> {\n await this.linkShiftsToLayoutInvalidations(layoutShifts, modelData);\n this.linkShiftsToLayoutEvents(layoutShifts, modelData);\n }\n\n /**\n * \"LayoutInvalidations\" are a set of trace events dispatched in Blink under the name\n * \"layoutInvalidationTracking\", which track invalidations on the \"Layout\"stage of the\n * rendering pipeline. This function utilizes this event to flag potential root causes\n * to layout shifts.\n */\n async linkShiftsToLayoutInvalidations(layoutShifts: Types.Events.LayoutShift[], modelData: ParsedTrace):\n Promise<void> {\n const {prePaintEvents, layoutInvalidationEvents, scheduleStyleInvalidationEvents, backendNodeIds} =\n modelData.LayoutShifts;\n\n // For the purposes of determining root causes of layout shifts, we\n // consider scheduleStyleInvalidationTracking and\n // LayoutInvalidationTracking events as events that could have been the\n // cause of the layout shift.\n const eventsForLayoutInvalidation:\n Array<Types.Events.LayoutInvalidationTracking|Types.Events.ScheduleStyleInvalidationTracking> =\n [...layoutInvalidationEvents, ...scheduleStyleInvalidationEvents];\n\n const nodes = await this.#protocolInterface.pushNodesByBackendIdsToFrontend(backendNodeIds);\n const nodeIdsByBackendIdMap = new Map<Protocol.DOM.BackendNodeId, Protocol.DOM.NodeId>();\n for (let i = 0; i < backendNodeIds.length; i++) {\n nodeIdsByBackendIdMap.set(backendNodeIds[i], nodes[i]);\n }\n\n // Maps from PrePaint events to LayoutShifts that occured in each one.\n const shiftsByPrePaint = getShiftsByPrePaintEvents(layoutShifts, prePaintEvents);\n for (const layoutInvalidation of eventsForLayoutInvalidation) {\n // Get the first PrePaint event that happened after the current LayoutInvalidation event.\n const nextPrePaintIndex = Platform.ArrayUtilities.nearestIndexFromBeginning(\n prePaintEvents, prePaint => prePaint.ts > layoutInvalidation.ts);\n if (nextPrePaintIndex === null) {\n // No PrePaint event registered after this LayoutInvalidation. Continue.\n continue;\n }\n const nextPrePaint = prePaintEvents[nextPrePaintIndex];\n const subsequentShifts = shiftsByPrePaint.get(nextPrePaint);\n if (!subsequentShifts) {\n // The PrePaint after the current LayoutInvalidation doesn't contain shifts.\n continue;\n }\n const fontChangeRootCause = this.getFontChangeRootCause(layoutInvalidation, nextPrePaint, modelData);\n const renderBlockRootCause = this.getRenderBlockRootCause(layoutInvalidation, nextPrePaint, modelData);\n const layoutInvalidationNodeId = nodeIdsByBackendIdMap.get(layoutInvalidation.args.data.nodeId);\n let unsizedMediaRootCause: UnsizedMedia|null = null;\n let iframeRootCause: InjectedIframe|null = null;\n if (layoutInvalidationNodeId !== undefined && Types.Events.isLayoutInvalidationTracking(layoutInvalidation)) {\n unsizedMediaRootCause = await this.getUnsizedMediaRootCause(layoutInvalidation, layoutInvalidationNodeId);\n iframeRootCause = await this.getIframeRootCause(layoutInvalidation, layoutInvalidationNodeId);\n }\n\n if (!unsizedMediaRootCause && !iframeRootCause && !fontChangeRootCause && !renderBlockRootCause) {\n continue;\n }\n\n // Add found potential root causes to all the shifts in this PrePaint and populate the cache.\n for (const shift of subsequentShifts) {\n const rootCausesForShift = Platform.MapUtilities.getWithDefault(this.#rootCauseCacheMap, shift, () => {\n return {\n unsizedMedia: [],\n iframes: [],\n fontChanges: [],\n renderBlockingRequests: [],\n scriptStackTrace: [],\n };\n });\n if (unsizedMediaRootCause &&\n !rootCausesForShift.unsizedMedia.some(media => media.node.nodeId === unsizedMediaRootCause?.node.nodeId) &&\n shift.args.frame === layoutInvalidation.args.data.frame) {\n rootCausesForShift.unsizedMedia.push(unsizedMediaRootCause);\n }\n if (iframeRootCause &&\n !rootCausesForShift.iframes.some(\n injectedIframe => injectedIframe.iframe.nodeId === iframeRootCause?.iframe.nodeId)) {\n rootCausesForShift.iframes.push(iframeRootCause);\n }\n if (fontChangeRootCause) {\n // Unlike other root causes, we calculate fonts causing a shift only once,\n // which means we assign the built array instead of appending new objects\n // to it.\n rootCausesForShift.fontChanges = fontChangeRootCause;\n }\n if (renderBlockRootCause) {\n rootCausesForShift.renderBlockingRequests = renderBlockRootCause;\n }\n }\n }\n }\n\n /**\n * For every shift looks up the initiator of its corresponding Layout event. This initiator\n * is assigned by the RendererHandler and contains the stack trace of the point in a script\n * that caused a style recalculation or a relayout. This stack trace is added to the shift's\n * potential root causes.\n * Note that a Layout cannot always be linked to a script, in that case, we cannot add a\n * \"script causing reflow\" as a potential root cause to the corresponding shift.\n */\n linkShiftsToLayoutEvents(layoutShifts: Types.Events.LayoutShift[], modelData: ParsedTrace): void {\n const {prePaintEvents} = modelData.LayoutShifts;\n // Maps from PrePaint events to LayoutShifts that occured in each one.\n const shiftsByPrePaint = getShiftsByPrePaintEvents(layoutShifts, prePaintEvents);\n\n const eventTriggersLayout = ({name}: {name: string}): boolean => {\n const knownName = name as Types.Events.Name;\n return knownName === Types.Events.Name.LAYOUT;\n };\n const layoutEvents = modelData.Renderer.allTraceEntries.filter(eventTriggersLayout);\n for (const layout of layoutEvents) {\n // Get the first PrePaint event that happened after the current layout event.\n const nextPrePaintIndex = Platform.ArrayUtilities.nearestIndexFromBeginning(\n prePaintEvents, prePaint => prePaint.ts > layout.ts + (layout.dur || 0));\n if (nextPrePaintIndex === null) {\n // No PrePaint event registered after this LayoutInvalidation. Continue.\n continue;\n }\n const nextPrePaint = prePaintEvents[nextPrePaintIndex];\n const subsequentShifts = shiftsByPrePaint.get(nextPrePaint);\n if (!subsequentShifts) {\n // The PrePaint after the current LayoutInvalidation doesn't contain shifts.\n continue;\n }\n const layoutNode = modelData.Renderer.entryToNode.get(layout);\n const initiator = layoutNode ? modelData.Initiators.eventToInitiator.get(layoutNode.entry) : null;\n const stackTrace = initiator?.args?.data?.stackTrace;\n if (!stackTrace) {\n continue;\n }\n // Add found potential root causes to all the shifts in this PrePaint and populate the cache.\n for (const shift of subsequentShifts) {\n const rootCausesForShift = Platform.MapUtilities.getWithDefault(this.#rootCauseCacheMap, shift, () => {\n return {\n unsizedMedia: [],\n iframes: [],\n fontChanges: [],\n renderBlockingRequests: [],\n scriptStackTrace: [],\n };\n });\n if (rootCausesForShift.scriptStackTrace.length === 0) {\n rootCausesForShift.scriptStackTrace = stackTrace;\n }\n }\n }\n }\n\n /**\n * Given a LayoutInvalidation trace event, determines if it was dispatched\n * because a media element without dimensions was resized.\n */\n async getUnsizedMediaRootCause(\n layoutInvalidation: Types.Events.LayoutInvalidationTracking,\n layoutInvalidationNodeId: Protocol.DOM.NodeId): Promise<UnsizedMedia|null> {\n // Filter events to resizes only.\n if (layoutInvalidation.args.data.reason !== Types.Events.LayoutInvalidationReason.SIZE_CHANGED) {\n return null;\n }\n\n const layoutInvalidationNode = await this.getNodeDetails(layoutInvalidationNodeId);\n if (!layoutInvalidationNode) {\n return null;\n }\n\n const computedStylesList = await this.#protocolInterface.getComputedStyleForNode(layoutInvalidationNode.nodeId);\n const computedStyles = new Map(computedStylesList.map(item => [item.name, item.value]));\n if (computedStyles && !(await nodeIsUnfixedMedia(layoutInvalidationNode, computedStyles))) {\n return null;\n }\n const authoredDimensions = await this.getNodeAuthoredDimensions(layoutInvalidationNode);\n if (dimensionsAreExplicit(authoredDimensions)) {\n return null;\n }\n const computedDimensions = computedStyles ? getNodeComputedDimensions(computedStyles) : {};\n\n return {node: layoutInvalidationNode, authoredDimensions, computedDimensions};\n }\n\n /**\n * Given a LayoutInvalidation trace event, determines if it was dispatched\n * because a node, which is an ancestor to an iframe, was injected.\n */\n async getIframeRootCause(\n layoutInvalidation: Types.Events.LayoutInvalidationTracking,\n layoutInvalidationNodeId: Protocol.DOM.NodeId): Promise<InjectedIframe|null> {\n if (!this.#iframeRootCausesEnabled) {\n return null;\n }\n\n if (!layoutInvalidation.args.data.nodeName?.startsWith('IFRAME') &&\n layoutInvalidation.args.data.reason !== Types.Events.LayoutInvalidationReason.STYLE_CHANGED &&\n layoutInvalidation.args.data.reason !== Types.Events.LayoutInvalidationReason.ADDED_TO_LAYOUT) {\n return null;\n }\n\n const layoutInvalidationNode = await this.getNodeDetails(layoutInvalidationNodeId);\n if (!layoutInvalidationNode) {\n return null;\n }\n\n const iframe = firstIframeInDOMTree(layoutInvalidationNode);\n if (!iframe) {\n return null;\n }\n return {iframe};\n }\n\n async getNodeDetails(nodeId: Protocol.DOM.NodeId): Promise<Protocol.DOM.Node|null> {\n let nodeDetails = this.#nodeDetailsCache.get(nodeId);\n if (nodeDetails !== undefined) {\n return nodeDetails;\n }\n\n nodeDetails = await this.#protocolInterface.getNode(nodeId);\n this.#nodeDetailsCache.set(nodeId, nodeDetails);\n\n return nodeDetails;\n }\n\n /**\n * Given a layout invalidation event and a sorted array, returns the subset of requests that arrived within a\n * 500ms window before the layout invalidation.\n */\n requestsInInvalidationWindow(\n layoutInvalidation: Types.Events.LayoutInvalidationTracking|Types.Events.ScheduleStyleInvalidationTracking,\n modelData: ParsedTrace): RootCauseRequest[] {\n const requestsSortedByEndTime = modelData.NetworkRequests.byTime.sort((req1, req2) => {\n const req1EndTime = req1.ts + req1.dur;\n const req2EndTime = req2.ts + req2.dur;\n return req1EndTime - req2EndTime;\n });\n\n const lastRequestIndex = Platform.ArrayUtilities.nearestIndexFromEnd(\n requestsSortedByEndTime, request => request.ts + request.dur < layoutInvalidation.ts);\n if (lastRequestIndex === null) {\n return [];\n }\n\n const MAX_DELTA_FOR_FONT_REQUEST = Helpers.Timing.secondsToMicroseconds(Types.Timing.Seconds(0.5));\n\n const requestsInInvalidationWindow: RootCauseRequest[] = [];\n\n // Get all requests finished within the valid window.\n for (let i = lastRequestIndex; i > -1; i--) {\n const previousRequest = requestsSortedByEndTime[i];\n const previousRequestEndTime = previousRequest.ts + previousRequest.dur;\n if (layoutInvalidation.ts - previousRequestEndTime < MAX_DELTA_FOR_FONT_REQUEST) {\n const requestInInvalidationWindow: RootCauseRequest = {request: previousRequest};\n\n const initiator = this.#protocolInterface.getInitiatorForRequest(\n previousRequest.args.data.url as Platform.DevToolsPath.UrlString);\n requestInInvalidationWindow.initiator = initiator || undefined;\n requestsInInvalidationWindow.push(requestInInvalidationWindow);\n } else {\n // No more requests fit in the time window.\n break;\n }\n }\n return requestsInInvalidationWindow;\n }\n\n /**\n * Given a LayoutInvalidation trace event, determines if it was dispatched\n * because fonts were changed and if so returns the information of all network\n * request with which the fonts were possibly fetched, if any. The computed\n * network requests are cached for the corresponding prepaint event, meaning\n * that other LayoutInvalidation events that correspond to the same prepaint\n * are not processed and the cached network requests for the prepaint is\n * returned instead.\n */\n getFontChangeRootCause(\n layoutInvalidation: Types.Events.LayoutInvalidationTracking|Types.Events.ScheduleStyleInvalidationTracking,\n nextPrePaint: Types.Events.PrePaint, modelData: ParsedTrace): FontChange[]|null {\n if (layoutInvalidation.args.data.reason !== Types.Events.LayoutInvalidationReason.FONTS_CHANGED) {\n return null;\n }\n // Prevent computing the result of this function multiple times per PrePaint event.\n const fontRequestsForPrepaint = fontRequestsByPrePaint.get(nextPrePaint);\n if (fontRequestsForPrepaint !== undefined) {\n return fontRequestsForPrepaint;\n }\n\n const fontRequestsInThisPrepaint =\n this.getFontRequestsInInvalidationWindow(this.requestsInInvalidationWindow(layoutInvalidation, modelData));\n fontRequestsByPrePaint.set(nextPrePaint, fontRequestsInThisPrepaint);\n return fontRequestsInThisPrepaint;\n }\n\n /**\n * Given the requests that arrived within a 500ms window before the layout invalidation, returns the font\n * requests of them.\n */\n getFontRequestsInInvalidationWindow(requestsInInvalidationWindow: RootCauseRequest[]): FontChange[] {\n const fontRequests: FontChange[] = [];\n\n // Get all requests finished within the valid window.\n for (let i = 0; i < requestsInInvalidationWindow.length; i++) {\n const fontRequest = requestsInInvalidationWindow[i] as FontChange;\n if (!fontRequest.request.args.data.mimeType.startsWith('font')) {\n continue;\n }\n\n const fontFace = this.#protocolInterface.fontFaceForSource(fontRequest.request.args.data.url);\n if (!fontFace || fontFace.fontDisplay === 'optional') {\n // Setting font-display to optional is part of what the developer\n // can do to avoid layout shifts due to FOIT/FOUT, as such we cannot\n // suggest any actionable insight here.\n continue;\n }\n fontRequest.fontFace = fontFace;\n fontRequests.push(fontRequest);\n }\n return fontRequests;\n }\n\n /**\n * Given a LayoutInvalidation trace event, determines if it arrived within a 500ms window before the layout\n * invalidation and if so returns the information of all network request, if any. The computed network\n * requests are cached for the corresponding prepaint event, meaning that other LayoutInvalidation events\n * that correspond to the same prepaint are not processed and the cached network requests for the prepaint is\n * returned instead.\n */\n getRenderBlockRootCause(\n layoutInvalidation: Types.Events.LayoutInvalidationTracking|Types.Events.ScheduleStyleInvalidationTracking,\n nextPrePaint: Types.Events.PrePaint, modelData: ParsedTrace): RenderBlockingRequest[]|null {\n // Prevent computing the result of this function multiple times per PrePaint event.\n const renderBlocksInPrepaint = renderBlocksByPrePaint.get(nextPrePaint);\n if (renderBlocksInPrepaint !== undefined) {\n return renderBlocksInPrepaint;\n }\n\n const renderBlocksInThisPrepaint =\n getRenderBlockRequestsInInvalidationWindow(this.requestsInInvalidationWindow(layoutInvalidation, modelData));\n renderBlocksByPrePaint.set(nextPrePaint, renderBlocksInThisPrepaint);\n return renderBlocksInThisPrepaint;\n }\n\n /**\n * Returns a function that retrieves the active value of a given\n * CSS property within the matched styles of the param node.\n * The first occurence within the matched styles is returned and the\n * value is looked up in the following order, which follows CSS\n * specificity:\n * 1. Inline styles.\n * 2. CSS rules matching this node, from all applicable stylesheets.\n * 3. Attribute defined styles.\n */\n async nodeMatchedStylesPropertyGetter(node: Protocol.DOM.Node): Promise<((property: string) => string | null)> {\n const response = await this.#protocolInterface.getMatchedStylesForNode(node.nodeId);\n\n function cssPropertyValueGetter(cssProperty: string): string|null {\n let prop = response.inlineStyle?.cssProperties.find(prop => prop.name === cssProperty);\n if (prop) {\n return prop.value;\n }\n\n for (const {rule} of response.matchedCSSRules || []) {\n const prop = rule.style.cssProperties.find(prop => prop.name === cssProperty);\n if (prop) {\n return prop.value;\n }\n }\n\n prop = response.attributesStyle?.cssProperties.find(prop => prop.name === cssProperty);\n if (prop) {\n return prop.value;\n }\n\n return null;\n }\n return cssPropertyValueGetter;\n }\n\n /**\n * Returns the CSS dimensions set to the node from its matched styles.\n */\n async getNodeAuthoredDimensions(node: Protocol.DOM.Node): Promise<CSSDimensions> {\n const authoredDimensions: CSSDimensions = {};\n\n const cssMatchedRulesGetter = await this.nodeMatchedStylesPropertyGetter(node);\n if (!cssMatchedRulesGetter) {\n return authoredDimensions;\n }\n\n const attributesFlat = node.attributes || [];\n const attributes = [];\n for (let i = 0; i < attributesFlat.length; i += 2) {\n attributes.push({name: attributesFlat[i], value: attributesFlat[i + 1]});\n }\n\n const htmlHeight = attributes.find(attr => attr.name === 'height' && htmlAttributeIsExplicit(attr));\n const htmlWidth = attributes.find(attr => attr.name === 'width' && htmlAttributeIsExplicit(attr));\n\n const cssExplicitAspectRatio = cssMatchedRulesGetter('aspect-ratio') || undefined;\n\n if (htmlHeight && htmlWidth && cssExplicitAspectRatio) {\n return {height: htmlHeight.value, width: htmlWidth.value, aspectRatio: cssExplicitAspectRatio};\n }\n\n const cssHeight = cssMatchedRulesGetter('height') || undefined;\n const cssWidth = cssMatchedRulesGetter('width') || undefined;\n return {height: cssHeight, width: cssWidth, aspectRatio: cssExplicitAspectRatio};\n }\n}\n\n/**\n * Given the requests that arrived within a 500ms window before the layout invalidation, returns the render\n * block requests of them.\n */\nfunction getRenderBlockRequestsInInvalidationWindow(requestsInInvalidationWindow: RootCauseRequest[]):\n RenderBlockingRequest[] {\n const renderBlockingRequests: RenderBlockingRequest[] = [];\n\n // Get all requests finished within the valid window.\n for (let i = 0; i < requestsInInvalidationWindow.length; i++) {\n const mainFrameId = requestsInInvalidationWindow[i].request.args.data.frame;\n if (!networkRequestIsRenderBlockingInFrame(requestsInInvalidationWindow[i].request, mainFrameId)) {\n continue;\n }\n renderBlockingRequests.push(requestsInInvalidationWindow[i] as RenderBlockingRequest);\n }\n return renderBlockingRequests;\n}\n\nfunction firstIframeInDOMTree(root: Protocol.DOM.Node): Protocol.DOM.Node|null {\n if (root.nodeName === 'IFRAME') {\n return root;\n }\n const children = root.children;\n if (!children) {\n return null;\n }\n for (const child of children) {\n const iFrameInChild = firstIframeInDOMTree(child);\n if (iFrameInChild) {\n return iFrameInChild;\n }\n }\n return null;\n}\n\nfunction cssPropertyIsExplicitlySet(propertyValue: string): boolean {\n return !['auto', 'initial', 'unset', 'inherit'].includes(propertyValue);\n}\n\nfunction htmlAttributeIsExplicit(attr: {value: string}): boolean {\n return parseInt(attr.value, 10) >= 0;\n}\n\nfunction computedStyleHasBackroundImage(computedStyle: Map<string, string>): boolean {\n const CSS_URL_REGEX = /^url\\(\"([^\"]+)\"\\)$/;\n const backgroundImage = computedStyle.get('background-image');\n if (!backgroundImage) {\n return false;\n }\n return CSS_URL_REGEX.test(backgroundImage);\n}\n\nfunction computedStyleHasFixedPosition(computedStyle: Map<string, string>): boolean {\n const position = computedStyle.get('position');\n if (!position) {\n return false;\n }\n return position === 'fixed' || position === 'absolute';\n}\n\nfunction getNodeComputedDimensions(computedStyle: Map<string, string>): CSSDimensions {\n const computedDimensions: CSSDimensions = {};\n computedDimensions.height = computedStyle.get('height');\n computedDimensions.width = computedStyle.get('width');\n computedDimensions.aspectRatio = computedStyle.get('aspect-ratio');\n return computedDimensions;\n}\n\n/**\n * Determines if a node is a media element and is not fixed positioned\n * (i.e. \"position: fixed;\" or \"position: absolute;\")\n */\nasync function nodeIsUnfixedMedia(node: Protocol.DOM.Node, computedStyle: Map<string, string>): Promise<boolean> {\n const localName = node.localName;\n const isBackgroundImage = computedStyleHasBackroundImage(computedStyle);\n if (localName !== 'img' && localName !== 'video' && !isBackgroundImage) {\n // Not a media element.\n return false;\n }\n const isFixed = computedStyleHasFixedPosition(computedStyle);\n return !isFixed;\n}\n\n/**\n * Determines if a CSS dimensions object explicitly defines both width and height\n * (i.e. not set to auto, inherit, etc.)\n */\nfunction dimensionsAreExplicit(dimensions: CSSDimensions): boolean {\n const {height, width, aspectRatio} = dimensions;\n\n const explicitHeight = Boolean(height && cssPropertyIsExplicitlySet(height));\n const explicitWidth = Boolean(width && cssPropertyIsExplicitlySet(width));\n const explicitAspectRatio = Boolean(aspectRatio && cssPropertyIsExplicitlySet(aspectRatio));\n\n const explicitWithAR = (explicitHeight || explicitWidth) && explicitAspectRatio;\n return (explicitHeight && explicitWidth) || explicitWithAR;\n}\n\n/**\n * Given an array of layout shift and PrePaint events, returns a mapping from\n * PrePaint events to layout shifts dispatched within it.\n */\nfunction getShiftsByPrePaintEvents(\n layoutShifts: Types.Events.LayoutShift[],\n prePaintEvents: Types.Events.PrePaint[],\n ): Map<Types.Events.PrePaint, Types.Events.LayoutShift[]> {\n // Maps from PrePaint events to LayoutShifts that occured in each one.\n const shiftsByPrePaint = new Map<Types.Events.PrePaint, Types.Events.LayoutShift[]>();\n\n // Associate all shifts to their corresponding PrePaint.\n for (const prePaintEvent of prePaintEvents) {\n const firstShiftIndex =\n Platform.ArrayUtilities.nearestIndexFromBeginning(layoutShifts, shift => shift.ts >= prePaintEvent.ts);\n if (firstShiftIndex === null) {\n // No layout shifts registered after this PrePaint start. Continue.\n continue;\n }\n for (let i = firstShiftIndex; i < layoutShifts.length; i++) {\n const shift = layoutShifts[i];\n if (shift.ts >= prePaintEvent.ts && shift.ts <= prePaintEvent.ts + prePaintEvent.dur) {\n const shiftsInPrePaint = Platform.MapUtilities.getWithDefault(shiftsByPrePaint, prePaintEvent, () => []);\n shiftsInPrePaint.push(shift);\n }\n if (shift.ts > prePaintEvent.ts + prePaintEvent.dur) {\n // Reached the end of this PrePaint. Continue to the next one.\n break;\n }\n }\n }\n return shiftsByPrePaint;\n}\n"]}
|
|
@@ -11,6 +11,22 @@ export type Configuration = {
|
|
|
11
11
|
* Extra detail for RPP developers (eg Trace Event json in Summary, and individual JS Sample events)
|
|
12
12
|
*/
|
|
13
13
|
debugMode: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* How many invalidation events will be stored for a layout (or similar) event.
|
|
16
|
+
* On large sites with a lot of DOM there can be thousands of invalidations
|
|
17
|
+
* associated with any given event. It is not useful to show the user 1000s of
|
|
18
|
+
* invalidations in the UI, but it is also expensive for us to hold onto them
|
|
19
|
+
* all, and it helps prevents OOM issues when running in NodeJS
|
|
20
|
+
* [https://github.com/GoogleChrome/lighthouse/issues/16111].
|
|
21
|
+
* Therefore, instead, we store only the latest 20 per event. We do also store
|
|
22
|
+
* the total count, so we can show that, but we'll only ever hold on to the
|
|
23
|
+
* last 20 invalidations (in DESC trace order - so the latest 20 in the trace file)
|
|
24
|
+
*
|
|
25
|
+
* If you set this to 0, we will skip the Invalidations processing entirely.
|
|
26
|
+
* 0 effectively disables the InvalidationsHandler and it will not even
|
|
27
|
+
* attempt to gather or track invalidations.
|
|
28
|
+
*/
|
|
29
|
+
maxInvalidationEventsPerEvent: number;
|
|
14
30
|
};
|
|
15
31
|
export declare const defaults: () => Configuration;
|
|
16
32
|
/**
|