chrome-devtools-frontend 1.0.1666631 → 1.0.1667564
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/front_end/core/host/AidaClient.ts +6 -4
- package/front_end/core/host/InspectorFrontendHostAPI.ts +1 -0
- package/front_end/core/host/InspectorFrontendHostStub.ts +3 -3
- package/front_end/core/host/UserMetrics.ts +49 -1
- package/front_end/core/sdk/CSSMatchedStyles.ts +2 -5
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +12 -0
- package/front_end/core/sdk/Connections.ts +1 -1
- package/front_end/core/sdk/DOMModel.test.api.ts +104 -0
- package/front_end/core/sdk/DOMModel.ts +23 -11
- package/front_end/core/sdk/DebuggerModel.ts +9 -9
- package/front_end/core/sdk/IsolateManager.ts +1 -1
- package/front_end/core/sdk/NetworkManager.ts +105 -4
- package/front_end/core/sdk/OverlayModel.ts +15 -14
- package/front_end/core/sdk/RemoteObject.ts +1 -1
- package/front_end/devtools_compatibility.js +1 -0
- package/front_end/entrypoints/main/MainImpl.ts +5 -2
- package/front_end/entrypoints/main/SimpleApp.ts +9 -3
- package/front_end/foundation/Universe.ts +6 -0
- package/front_end/models/ai_assistance/contexts/PerformanceTraceContext.ts +23 -10
- package/front_end/models/computed_style/ComputedStyleModel.ts +4 -7
- package/front_end/models/crux-manager/CrUXManager.ts +4 -0
- package/front_end/models/extensions/ExtensionAPI.ts +3 -3
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +2 -2
- package/front_end/models/live-metrics/LiveMetrics.ts +59 -19
- package/front_end/models/live-metrics/web-vitals-injected/README.md +1 -1
- package/front_end/models/live-metrics/web-vitals-injected/spec/spec.ts +6 -1
- package/front_end/models/live-metrics/web-vitals-injected/web-vitals-injected.ts +11 -3
- package/front_end/models/logs/LogManager.ts +1 -1
- package/front_end/models/persistence/FileSystemWorkspaceBinding.ts +1 -1
- package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +6 -7
- package/front_end/models/workspace_diff/WorkspaceDiff.ts +13 -14
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +15 -7
- package/front_end/panels/application/BackgroundServiceView.ts +18 -20
- package/front_end/panels/autofill/AutofillView.ts +17 -17
- package/front_end/panels/autofill/autofillView.css +7 -8
- package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +31 -31
- package/front_end/panels/browser_debugger/DOMBreakpointsSidebarPane.ts +15 -15
- package/front_end/panels/browser_debugger/XHRBreakpointsSidebarPane.ts +12 -12
- package/front_end/panels/browser_debugger/browser_debugger-meta.ts +27 -27
- package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +14 -6
- package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +14 -6
- package/front_end/panels/common/AiCodeCompletionTeaser.ts +13 -6
- package/front_end/panels/common/GeminiRebrandPromoDialog.ts +1 -1
- package/front_end/panels/console/ConsoleInsightTeaser.ts +13 -6
- package/front_end/panels/console/ConsoleView.ts +6 -6
- package/front_end/panels/console_counters/WarningErrorCounter.ts +4 -4
- package/front_end/panels/coverage/CoverageModel.ts +11 -1
- package/front_end/panels/elements/StylePropertiesSection.ts +50 -17
- package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +18 -7
- package/front_end/panels/emulation/AdvancedApp.ts +18 -8
- package/front_end/panels/event_listeners/EventListenersView.ts +8 -8
- package/front_end/panels/explain/components/ConsoleInsight.ts +15 -7
- package/front_end/panels/network/NetworkLogView.ts +13 -14
- package/front_end/panels/screencast/ScreencastApp.ts +12 -6
- package/front_end/panels/screencast/ScreencastView.ts +37 -11
- package/front_end/panels/sensors/locationsSettingsTab.css +4 -0
- package/front_end/panels/settings/AISettingsTab.ts +13 -6
- package/front_end/panels/sources/DebuggerPlugin.ts +13 -2
- package/front_end/panels/timeline/components/LiveMetricsView.ts +14 -1
- package/front_end/panels/timeline/components/cpuThrottlingSelector.css +4 -0
- package/front_end/panels/timeline/components/liveMetricsView.css +28 -0
- package/front_end/panels/timeline/timeline-meta.ts +13 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/menus/menu.css +4 -0
- package/front_end/ui/components/menus/menuItem.css +4 -0
- package/front_end/ui/components/menus/selectMenu.css +4 -0
- package/front_end/ui/components/menus/selectMenuButton.css +4 -0
- package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +21 -10
- package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +22 -10
- package/front_end/ui/components/text_editor/config.ts +7 -0
- package/front_end/ui/legacy/AppProvider.ts +2 -1
- package/front_end/ui/legacy/RootView.ts +8 -1
- package/front_end/ui/legacy/UniverseRequestEvent.ts +19 -0
- package/front_end/ui/legacy/Widget.ts +49 -6
- package/front_end/ui/legacy/inspectorCommon.css +1 -1
- package/front_end/ui/legacy/legacy.ts +2 -0
- package/front_end/ui/visual_logging/KnownContextValues.ts +2 -0
- package/package.json +1 -1
|
@@ -456,6 +456,10 @@ export class HostConfigTracker extends Common.ObjectWrapper.ObjectWrapper<EventT
|
|
|
456
456
|
#pollTimer?: ReturnType<typeof setTimeout>;
|
|
457
457
|
#aidaAvailability?: AidaAccessPreconditions;
|
|
458
458
|
|
|
459
|
+
get aidaAvailability(): AidaAccessPreconditions|undefined {
|
|
460
|
+
return this.#aidaAvailability;
|
|
461
|
+
}
|
|
462
|
+
|
|
459
463
|
static instance({forceNew}: {
|
|
460
464
|
forceNew: boolean,
|
|
461
465
|
} = {forceNew: false}): HostConfigTracker {
|
|
@@ -507,9 +511,7 @@ export class HostConfigTracker extends Common.ObjectWrapper.ObjectWrapper<EventT
|
|
|
507
511
|
const config =
|
|
508
512
|
await new Promise<Root.Runtime.HostConfig>(resolve => InspectorFrontendHostInstance.getHostConfig(resolve));
|
|
509
513
|
Object.assign(Root.Runtime.hostConfig, config);
|
|
510
|
-
|
|
511
|
-
// `await AidaClient.checkAccessPreconditions()` does not need to be called again in the event handlers.
|
|
512
|
-
this.dispatchEventToListeners(Events.AIDA_AVAILABILITY_CHANGED);
|
|
514
|
+
this.dispatchEventToListeners(Events.AIDA_AVAILABILITY_CHANGED, currentAidaAvailability);
|
|
513
515
|
}
|
|
514
516
|
}
|
|
515
517
|
}
|
|
@@ -519,7 +521,7 @@ export const enum Events {
|
|
|
519
521
|
}
|
|
520
522
|
|
|
521
523
|
export interface EventTypes {
|
|
522
|
-
[Events.AIDA_AVAILABILITY_CHANGED]:
|
|
524
|
+
[Events.AIDA_AVAILABILITY_CHANGED]: AidaAccessPreconditions;
|
|
523
525
|
}
|
|
524
526
|
|
|
525
527
|
export function isQuotaError(...inputs: Array<string|undefined>): boolean {
|
|
@@ -556,5 +556,6 @@ export const enum EnumeratedHistogram {
|
|
|
556
556
|
LighthouseCategoryUsed = 'DevTools.LighthouseCategoryUsed',
|
|
557
557
|
SwatchActivated = 'DevTools.SwatchActivated',
|
|
558
558
|
BuiltInAiAvailability = 'DevTools.BuiltInAiAvailability',
|
|
559
|
+
ResendRequest = 'DevTools.ResendRequest',
|
|
559
560
|
// LINT.ThenChange(/front_end/devtools_compatibility.js:EnumeratedHistogram)
|
|
560
561
|
}
|
|
@@ -118,15 +118,15 @@ export class InspectorFrontendHostStub implements InspectorFrontendHostAPI {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
setIsDocked(_isDocked: boolean, callback: () => void): void {
|
|
121
|
-
|
|
121
|
+
globalThis.setTimeout(callback, 0);
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
showSurvey(_trigger: string, callback: (arg0: ShowSurveyResult) => void): void {
|
|
125
|
-
|
|
125
|
+
globalThis.setTimeout(() => callback({surveyShown: false}), 0);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
canShowSurvey(_trigger: string, callback: (arg0: CanShowSurveyResult) => void): void {
|
|
129
|
-
|
|
129
|
+
globalThis.setTimeout(() => callback({canShowSurvey: false}), 0);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
/**
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
|
+
import * as Common from '../common/common.js';
|
|
6
|
+
|
|
5
7
|
import {InspectorFrontendHostInstance} from './InspectorFrontendHost.js';
|
|
6
8
|
import {EnumeratedHistogram} from './InspectorFrontendHostAPI.js';
|
|
7
9
|
|
|
@@ -29,6 +31,11 @@ export class UserMetrics {
|
|
|
29
31
|
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.ActionTaken, action, Action.MAX_VALUE);
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
resendRequest(resourceType: ResendRequestType): void {
|
|
35
|
+
InspectorFrontendHostInstance.recordEnumeratedHistogram(EnumeratedHistogram.ResendRequest, resourceType,
|
|
36
|
+
ResendRequestType.MAX_VALUE);
|
|
37
|
+
}
|
|
38
|
+
|
|
32
39
|
keybindSetSettingChanged(keybindSet: string): void {
|
|
33
40
|
const value = KeybindSetSettings[keybindSet as keyof typeof KeybindSetSettings] || 0;
|
|
34
41
|
InspectorFrontendHostInstance.recordEnumeratedHistogram(
|
|
@@ -253,7 +260,6 @@ export class UserMetrics {
|
|
|
253
260
|
InspectorFrontendHostInstance.recordPerformanceHistogram(
|
|
254
261
|
'DevTools.Insights.ShortTeaserGenerationTime', timeInMilliseconds);
|
|
255
262
|
}
|
|
256
|
-
|
|
257
263
|
}
|
|
258
264
|
|
|
259
265
|
/**
|
|
@@ -1182,3 +1188,45 @@ export const enum BuiltInAiAvailability {
|
|
|
1182
1188
|
DISABLED_NO_GPU = 9,
|
|
1183
1189
|
MAX_VALUE = 10,
|
|
1184
1190
|
}
|
|
1191
|
+
|
|
1192
|
+
export const enum ResendRequestType {
|
|
1193
|
+
XHR = 0,
|
|
1194
|
+
FETCH = 1,
|
|
1195
|
+
SCRIPT = 2,
|
|
1196
|
+
STYLESHEET = 3,
|
|
1197
|
+
IMAGE = 4,
|
|
1198
|
+
MEDIA = 5,
|
|
1199
|
+
FONT = 6,
|
|
1200
|
+
WASM = 7,
|
|
1201
|
+
MANIFEST = 8,
|
|
1202
|
+
TEXT_TRACK = 9,
|
|
1203
|
+
SOURCE_MAP_SCRIPT = 10,
|
|
1204
|
+
SOURCE_MAP_STYLE_SHEET = 11,
|
|
1205
|
+
DOCUMENT = 12,
|
|
1206
|
+
PREFETCH = 13,
|
|
1207
|
+
PING = 14,
|
|
1208
|
+
OTHER = 15,
|
|
1209
|
+
MAX_VALUE = 16,
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
const resendRequestTypeMap = new Map<Common.ResourceType.ResourceType, ResendRequestType>([
|
|
1213
|
+
[Common.ResourceType.resourceTypes.XHR, ResendRequestType.XHR],
|
|
1214
|
+
[Common.ResourceType.resourceTypes.Fetch, ResendRequestType.FETCH],
|
|
1215
|
+
[Common.ResourceType.resourceTypes.Script, ResendRequestType.SCRIPT],
|
|
1216
|
+
[Common.ResourceType.resourceTypes.Stylesheet, ResendRequestType.STYLESHEET],
|
|
1217
|
+
[Common.ResourceType.resourceTypes.Image, ResendRequestType.IMAGE],
|
|
1218
|
+
[Common.ResourceType.resourceTypes.Media, ResendRequestType.MEDIA],
|
|
1219
|
+
[Common.ResourceType.resourceTypes.Font, ResendRequestType.FONT],
|
|
1220
|
+
[Common.ResourceType.resourceTypes.Wasm, ResendRequestType.WASM],
|
|
1221
|
+
[Common.ResourceType.resourceTypes.Manifest, ResendRequestType.MANIFEST],
|
|
1222
|
+
[Common.ResourceType.resourceTypes.TextTrack, ResendRequestType.TEXT_TRACK],
|
|
1223
|
+
[Common.ResourceType.resourceTypes.SourceMapScript, ResendRequestType.SOURCE_MAP_SCRIPT],
|
|
1224
|
+
[Common.ResourceType.resourceTypes.SourceMapStyleSheet, ResendRequestType.SOURCE_MAP_STYLE_SHEET],
|
|
1225
|
+
[Common.ResourceType.resourceTypes.Document, ResendRequestType.DOCUMENT],
|
|
1226
|
+
[Common.ResourceType.resourceTypes.Prefetch, ResendRequestType.PREFETCH],
|
|
1227
|
+
[Common.ResourceType.resourceTypes.Ping, ResendRequestType.PING],
|
|
1228
|
+
]);
|
|
1229
|
+
|
|
1230
|
+
export function resendRequestType(resourceType: Common.ResourceType.ResourceType): ResendRequestType {
|
|
1231
|
+
return resendRequestTypeMap.get(resourceType) ?? ResendRequestType.OTHER;
|
|
1232
|
+
}
|
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
CSSStyleRule,
|
|
53
53
|
} from './CSSRule.js';
|
|
54
54
|
import {CSSStyleDeclaration, Type} from './CSSStyleDeclaration.js';
|
|
55
|
-
import type
|
|
55
|
+
import {type DOMNode, NodeType} from './DOMModel.js';
|
|
56
56
|
|
|
57
57
|
function containsStyle(styles: CSSStyleDeclaration[]|Set<CSSStyleDeclaration>, query: CSSStyleDeclaration): boolean {
|
|
58
58
|
if (!query.styleSheetId || !query.range) {
|
|
@@ -456,7 +456,7 @@ export class CSSMatchedStyles {
|
|
|
456
456
|
}
|
|
457
457
|
|
|
458
458
|
// Inline style takes precedence over regular and inherited rules.
|
|
459
|
-
if (inlinePayload && this.#node.nodeType() ===
|
|
459
|
+
if (inlinePayload && this.#node.nodeType() === NodeType.ELEMENT_NODE) {
|
|
460
460
|
const style = new CSSStyleDeclaration(this.#cssModel, null, inlinePayload, Type.Inline);
|
|
461
461
|
this.#nodeForStyle.set(style, this.#node);
|
|
462
462
|
nodeStyles.push(style);
|
|
@@ -905,9 +905,6 @@ export class CSSMatchedStyles {
|
|
|
905
905
|
|
|
906
906
|
const matchCascade = (cascade: DOMInheritanceCascade): CSSStyleRule|null => {
|
|
907
907
|
for (const style of cascade.styles()) {
|
|
908
|
-
if (this.isInherited(style)) {
|
|
909
|
-
continue;
|
|
910
|
-
}
|
|
911
908
|
const parentRule = style.parentRule;
|
|
912
909
|
if (!(parentRule instanceof CSSStyleRule)) {
|
|
913
910
|
continue;
|
|
@@ -243,6 +243,18 @@ function getCssEvaluationElement(): HTMLElement {
|
|
|
243
243
|
return cssEvaluationElement;
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
+
/**
|
|
247
|
+
* If a test calls localEvalCSS, an element is created on demand for this
|
|
248
|
+
* purpose. This element is not removed from the DOM and will leak between tests
|
|
249
|
+
* if not removed.
|
|
250
|
+
*/
|
|
251
|
+
export function removeCSSEvaluationElement(): void {
|
|
252
|
+
if (cssEvaluationElement) {
|
|
253
|
+
document.body.removeChild(cssEvaluationElement);
|
|
254
|
+
cssEvaluationElement = null;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
246
258
|
/**
|
|
247
259
|
* These functions use an element in the frontend to evaluate CSS. The advantage
|
|
248
260
|
* of this is that it is synchronous and doesn't require a CDP method. The
|
|
@@ -189,7 +189,7 @@ export class StubTransport implements ProtocolClient.ConnectionTransport.Connect
|
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
sendRawMessage(message: string): void {
|
|
192
|
-
|
|
192
|
+
globalThis.setTimeout(this.respondWithError.bind(this, message), 0);
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
private respondWithError(message: string): void {
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Copyright 2026 The Chromium Authors
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
import {assert} from 'chai';
|
|
6
|
+
|
|
7
|
+
import type * as Common from '../common/common.js';
|
|
8
|
+
|
|
9
|
+
// eslint-disable-next-line @devtools/es-modules-import
|
|
10
|
+
import * as SDK from './sdk.js';
|
|
11
|
+
|
|
12
|
+
function findNode(node: SDK.DOMModel.DOMNode, predicate: (node: SDK.DOMModel.DOMNode) => boolean): SDK.DOMModel.DOMNode|
|
|
13
|
+
null {
|
|
14
|
+
if (predicate(node)) {
|
|
15
|
+
return node;
|
|
16
|
+
}
|
|
17
|
+
const children = node.children();
|
|
18
|
+
if (children) {
|
|
19
|
+
for (const child of children) {
|
|
20
|
+
const result = findNode(child, predicate);
|
|
21
|
+
if (result) {
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe('DOMModel API Test', () => {
|
|
30
|
+
it('generates attribute updated event only when attribute is actually changed', async ({inspectedPage, universe}) => {
|
|
31
|
+
const primaryTarget = universe.targetManager.primaryPageTarget();
|
|
32
|
+
assert.isNotNull(primaryTarget);
|
|
33
|
+
|
|
34
|
+
const domModel = primaryTarget.model(SDK.DOMModel.DOMModel);
|
|
35
|
+
assert.isNotNull(domModel);
|
|
36
|
+
|
|
37
|
+
await inspectedPage.goToHtml(`
|
|
38
|
+
<div id="container">
|
|
39
|
+
<div id="node-set-new-value" style="color:red"></div>
|
|
40
|
+
<div id="node-set-same-value" style="color:red"></div>
|
|
41
|
+
</div>
|
|
42
|
+
`);
|
|
43
|
+
|
|
44
|
+
const documentNode = await domModel.requestDocument();
|
|
45
|
+
assert.isNotNull(documentNode);
|
|
46
|
+
|
|
47
|
+
// Retrieve the subtree to populate the DOMModel cache.
|
|
48
|
+
await documentNode.getSubtree(5, true);
|
|
49
|
+
|
|
50
|
+
const container = findNode(documentNode, n => n.getAttribute('id') === 'container');
|
|
51
|
+
assert.isNotNull(container);
|
|
52
|
+
|
|
53
|
+
const nodeSetNewValue = findNode(documentNode, n => n.getAttribute('id') === 'node-set-new-value');
|
|
54
|
+
assert.isNotNull(nodeSetNewValue);
|
|
55
|
+
|
|
56
|
+
const nodeSetSameValue = findNode(documentNode, n => n.getAttribute('id') === 'node-set-same-value');
|
|
57
|
+
assert.isNotNull(nodeSetSameValue);
|
|
58
|
+
|
|
59
|
+
if (!nodeSetNewValue || !nodeSetSameValue) {
|
|
60
|
+
assert.fail('Could not find test nodes');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Verifies that setting a new style attribute value triggers the AttrModified event.
|
|
64
|
+
let attrModifiedPromise = domModel.once(SDK.DOMModel.Events.AttrModified);
|
|
65
|
+
|
|
66
|
+
await inspectedPage.evaluate(() => {
|
|
67
|
+
(document.getElementById('node-set-new-value') as HTMLElement).style.setProperty('color', 'blue');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
let eventData = await attrModifiedPromise;
|
|
71
|
+
assert.strictEqual(eventData.node, nodeSetNewValue);
|
|
72
|
+
assert.strictEqual(eventData.name, 'style');
|
|
73
|
+
assert.strictEqual(nodeSetNewValue.getAttribute('style'), 'color: blue;');
|
|
74
|
+
|
|
75
|
+
// Verifies that setting the style attribute to the same value does not trigger the AttrModified event.
|
|
76
|
+
let attrModifiedFired = false;
|
|
77
|
+
const listener =
|
|
78
|
+
(event: Common.EventTarget
|
|
79
|
+
.EventTargetEvent<SDK.DOMModel.EventTypes[SDK.DOMModel.Events.AttrModified], SDK.DOMModel.EventTypes>):
|
|
80
|
+
void => {
|
|
81
|
+
if (event.data.node === nodeSetSameValue) {
|
|
82
|
+
attrModifiedFired = true;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
domModel.addEventListener(SDK.DOMModel.Events.AttrModified, listener);
|
|
86
|
+
|
|
87
|
+
await inspectedPage.evaluate(() => {
|
|
88
|
+
(document.getElementById('node-set-same-value') as HTMLElement).style.setProperty('color', 'red');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Flushes pending events by triggering a style change on the new value node.
|
|
92
|
+
attrModifiedPromise = domModel.once(SDK.DOMModel.Events.AttrModified);
|
|
93
|
+
|
|
94
|
+
await inspectedPage.evaluate(() => {
|
|
95
|
+
(document.getElementById('node-set-new-value') as HTMLElement).style.setProperty('color', 'green');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
eventData = await attrModifiedPromise;
|
|
99
|
+
assert.strictEqual(eventData.node, nodeSetNewValue);
|
|
100
|
+
assert.isFalse(attrModifiedFired, 'AttrModified should not have fired for same value');
|
|
101
|
+
|
|
102
|
+
domModel.removeEventListener(SDK.DOMModel.Events.AttrModified, listener);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -50,6 +50,18 @@ import {SDKModel} from './SDKModel.js';
|
|
|
50
50
|
import {Capability, type Target} from './Target.js';
|
|
51
51
|
import type {TargetManager} from './TargetManager.js';
|
|
52
52
|
|
|
53
|
+
export const enum NodeType {
|
|
54
|
+
ELEMENT_NODE = 1,
|
|
55
|
+
ATTRIBUTE_NODE = 2,
|
|
56
|
+
TEXT_NODE = 3,
|
|
57
|
+
CDATA_SECTION_NODE = 4,
|
|
58
|
+
PROCESSING_INSTRUCTION_NODE = 7,
|
|
59
|
+
COMMENT_NODE = 8,
|
|
60
|
+
DOCUMENT_NODE = 9,
|
|
61
|
+
DOCUMENT_TYPE_NODE = 10,
|
|
62
|
+
DOCUMENT_FRAGMENT_NODE = 11,
|
|
63
|
+
}
|
|
64
|
+
|
|
53
65
|
/** Keep this list in sync with https://w3c.github.io/aria/#state_prop_def **/
|
|
54
66
|
export const ARIA_ATTRIBUTES = new Set<string>([
|
|
55
67
|
'role',
|
|
@@ -297,7 +309,7 @@ export class DOMNode extends Common.ObjectWrapper.ObjectWrapper<DOMNodeEventType
|
|
|
297
309
|
this.#adProvenance = payload.adProvenance;
|
|
298
310
|
}
|
|
299
311
|
|
|
300
|
-
if (this.#nodeType ===
|
|
312
|
+
if (this.#nodeType === NodeType.ELEMENT_NODE) {
|
|
301
313
|
// HTML and BODY from internal iframes should not overwrite top-level ones.
|
|
302
314
|
if (this.ownerDocument && !this.ownerDocument.documentElement && this.#nodeName === 'HTML') {
|
|
303
315
|
this.ownerDocument.documentElement = this;
|
|
@@ -305,11 +317,11 @@ export class DOMNode extends Common.ObjectWrapper.ObjectWrapper<DOMNodeEventType
|
|
|
305
317
|
if (this.ownerDocument && !this.ownerDocument.body && this.#nodeName === 'BODY') {
|
|
306
318
|
this.ownerDocument.body = this;
|
|
307
319
|
}
|
|
308
|
-
} else if (this.#nodeType ===
|
|
320
|
+
} else if (this.#nodeType === NodeType.DOCUMENT_TYPE_NODE) {
|
|
309
321
|
this.publicId = payload.publicId;
|
|
310
322
|
this.systemId = payload.systemId;
|
|
311
323
|
this.internalSubset = payload.internalSubset;
|
|
312
|
-
} else if (this.#nodeType ===
|
|
324
|
+
} else if (this.#nodeType === NodeType.ATTRIBUTE_NODE) {
|
|
313
325
|
this.name = payload.name;
|
|
314
326
|
this.value = payload.value;
|
|
315
327
|
}
|
|
@@ -354,7 +366,7 @@ export class DOMNode extends Common.ObjectWrapper.ObjectWrapper<DOMNodeEventType
|
|
|
354
366
|
}
|
|
355
367
|
|
|
356
368
|
isRootNode(): boolean {
|
|
357
|
-
if (this.nodeType() ===
|
|
369
|
+
if (this.nodeType() === NodeType.ELEMENT_NODE && this.nodeName() === 'HTML') {
|
|
358
370
|
return true;
|
|
359
371
|
}
|
|
360
372
|
return false;
|
|
@@ -762,7 +774,7 @@ export class DOMNode extends Common.ObjectWrapper.ObjectWrapper<DOMNodeEventType
|
|
|
762
774
|
if (node.isShadowRoot()) {
|
|
763
775
|
return node.shadowRootType() === DOMNode.ShadowRootTypes.UserAgent ? 'u' : 'a';
|
|
764
776
|
}
|
|
765
|
-
if (node.nodeType() ===
|
|
777
|
+
if (node.nodeType() === NodeType.DOCUMENT_NODE) {
|
|
766
778
|
return 'd';
|
|
767
779
|
}
|
|
768
780
|
return null;
|
|
@@ -1018,7 +1030,7 @@ export class DOMNode extends Common.ObjectWrapper.ObjectWrapper<DOMNodeEventType
|
|
|
1018
1030
|
}
|
|
1019
1031
|
|
|
1020
1032
|
isCustomElement(): boolean {
|
|
1021
|
-
if (this.nodeType() !==
|
|
1033
|
+
if (this.nodeType() !== NodeType.ELEMENT_NODE || this.isXMLNode()) {
|
|
1022
1034
|
return false;
|
|
1023
1035
|
}
|
|
1024
1036
|
const localName = this.localName() || this.nodeName().toLowerCase();
|
|
@@ -1158,11 +1170,11 @@ export class DOMNode extends Common.ObjectWrapper.ObjectWrapper<DOMNodeEventType
|
|
|
1158
1170
|
|
|
1159
1171
|
enclosingElementOrSelf(): DOMNode|null {
|
|
1160
1172
|
let node: DOMNode|null = this;
|
|
1161
|
-
if (node && node.nodeType() ===
|
|
1173
|
+
if (node && node.nodeType() === NodeType.TEXT_NODE && node.parentNode) {
|
|
1162
1174
|
node = node.parentNode;
|
|
1163
1175
|
}
|
|
1164
1176
|
|
|
1165
|
-
if (node && node.nodeType() !==
|
|
1177
|
+
if (node && node.nodeType() !== NodeType.ELEMENT_NODE) {
|
|
1166
1178
|
node = null;
|
|
1167
1179
|
}
|
|
1168
1180
|
return node;
|
|
@@ -1223,7 +1235,7 @@ export class DOMNode extends Common.ObjectWrapper.ObjectWrapper<DOMNodeEventType
|
|
|
1223
1235
|
|
|
1224
1236
|
simpleSelector(): string {
|
|
1225
1237
|
const lowerCaseName = this.localName() || this.nodeName().toLowerCase();
|
|
1226
|
-
if (this.nodeType() !==
|
|
1238
|
+
if (this.nodeType() !== NodeType.ELEMENT_NODE) {
|
|
1227
1239
|
return lowerCaseName;
|
|
1228
1240
|
}
|
|
1229
1241
|
const type = this.getAttribute('type');
|
|
@@ -1474,7 +1486,7 @@ export class DOMModel extends SDKModel<EventTypes> {
|
|
|
1474
1486
|
#lastMutationId!: number;
|
|
1475
1487
|
#pendingDocumentRequestPromise: Promise<DOMDocument|null>|null = null;
|
|
1476
1488
|
#frameOwnerNode?: DOMNode|null;
|
|
1477
|
-
#loadNodeAttributesTimeout?:
|
|
1489
|
+
#loadNodeAttributesTimeout?: ReturnType<typeof setTimeout>;
|
|
1478
1490
|
#searchId?: string;
|
|
1479
1491
|
#topLayerThrottler = new Common.Throttler.Throttler(100);
|
|
1480
1492
|
#topLayerNodes: DOMNode[] = [];
|
|
@@ -1663,7 +1675,7 @@ export class DOMModel extends SDKModel<EventTypes> {
|
|
|
1663
1675
|
inlineStyleInvalidated(nodeIds: Protocol.DOM.NodeId[]): void {
|
|
1664
1676
|
nodeIds.forEach(nodeId => this.#attributeLoadNodeIds.add(nodeId));
|
|
1665
1677
|
if (!this.#loadNodeAttributesTimeout) {
|
|
1666
|
-
this.#loadNodeAttributesTimeout =
|
|
1678
|
+
this.#loadNodeAttributesTimeout = globalThis.setTimeout(this.loadNodeAttributes.bind(this), 20);
|
|
1667
1679
|
}
|
|
1668
1680
|
}
|
|
1669
1681
|
|
|
@@ -144,7 +144,7 @@ export class DebuggerModel extends SDKModel<EventTypes> {
|
|
|
144
144
|
#selectedCallFrame: CallFrame|null = null;
|
|
145
145
|
#debuggerEnabled = false;
|
|
146
146
|
#debuggerId: string|null = null;
|
|
147
|
-
#skipAllPausesTimeout
|
|
147
|
+
#skipAllPausesTimeout?: ReturnType<typeof setTimeout>;
|
|
148
148
|
#beforePausedCallback: ((arg0: DebuggerPausedDetails, stepOver: Location|null) => Promise<boolean>)|null = null;
|
|
149
149
|
#computeAutoStepRangesCallback: ((arg0: StepMode, arg1: CallFrame) => Promise<Array<{
|
|
150
150
|
start: Location,
|
|
@@ -337,20 +337,20 @@ export class DebuggerModel extends SDKModel<EventTypes> {
|
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
private skipAllPauses(skip: boolean): void {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
this.#skipAllPausesTimeout = 0;
|
|
343
|
-
}
|
|
340
|
+
clearTimeout(this.#skipAllPausesTimeout);
|
|
341
|
+
|
|
344
342
|
void this.agent.invoke_setSkipAllPauses({skip});
|
|
345
343
|
}
|
|
346
344
|
|
|
347
345
|
skipAllPausesUntilReloadOrTimeout(timeout: number): void {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
}
|
|
346
|
+
clearTimeout(this.#skipAllPausesTimeout);
|
|
347
|
+
|
|
351
348
|
void this.agent.invoke_setSkipAllPauses({skip: true});
|
|
352
349
|
// If reload happens before the timeout, the flag will be already unset and the timeout callback won't change anything.
|
|
353
|
-
this.#skipAllPausesTimeout =
|
|
350
|
+
this.#skipAllPausesTimeout = globalThis.setTimeout(
|
|
351
|
+
this.skipAllPauses.bind(this, false),
|
|
352
|
+
timeout,
|
|
353
|
+
);
|
|
354
354
|
}
|
|
355
355
|
|
|
356
356
|
private pauseOnExceptionStateChanged(): void {
|
|
@@ -121,7 +121,7 @@ export class IsolateManager extends Common.ObjectWrapper.ObjectWrapper<EventType
|
|
|
121
121
|
const pollId = this.#pollId;
|
|
122
122
|
while (pollId === this.#pollId) {
|
|
123
123
|
await Promise.all(Array.from(this.isolates(), isolate => isolate.update()));
|
|
124
|
-
await new Promise(r =>
|
|
124
|
+
await new Promise(r => globalThis.setTimeout(r, PollIntervalMs));
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import type * as ProtocolProxyApi from '../../generated/protocol-proxy-api.js';
|
|
6
6
|
import * as Protocol from '../../generated/protocol.js';
|
|
7
7
|
import * as Common from '../common/common.js';
|
|
8
|
+
import * as Host from '../host/host.js';
|
|
8
9
|
import * as i18n from '../i18n/i18n.js';
|
|
9
10
|
import * as Platform from '../platform/platform.js';
|
|
10
11
|
import * as Root from '../root/root.js';
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
type NameValue,
|
|
24
25
|
NetworkRequest,
|
|
25
26
|
} from './NetworkRequest.js';
|
|
27
|
+
import {type ExecutionContext, RuntimeModel} from './RuntimeModel.js';
|
|
26
28
|
import {SDKModel} from './SDKModel.js';
|
|
27
29
|
import {Capability, type Target} from './Target.js';
|
|
28
30
|
import {type SDKModelObserver, TargetManager} from './TargetManager.js';
|
|
@@ -116,6 +118,22 @@ const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined
|
|
|
116
118
|
|
|
117
119
|
const requestToManagerMap = new WeakMap<NetworkRequest, NetworkManager>();
|
|
118
120
|
|
|
121
|
+
/** Resource types eligible for resend with full fidelity. */
|
|
122
|
+
const FULL_FIDELITY_RESEND_TYPES = new Set([
|
|
123
|
+
Common.ResourceType.resourceTypes.XHR,
|
|
124
|
+
Common.ResourceType.resourceTypes.Fetch,
|
|
125
|
+
Common.ResourceType.resourceTypes.Script,
|
|
126
|
+
Common.ResourceType.resourceTypes.Stylesheet,
|
|
127
|
+
Common.ResourceType.resourceTypes.Image,
|
|
128
|
+
Common.ResourceType.resourceTypes.Media,
|
|
129
|
+
Common.ResourceType.resourceTypes.Font,
|
|
130
|
+
Common.ResourceType.resourceTypes.Wasm,
|
|
131
|
+
Common.ResourceType.resourceTypes.Manifest,
|
|
132
|
+
Common.ResourceType.resourceTypes.TextTrack,
|
|
133
|
+
Common.ResourceType.resourceTypes.SourceMapScript,
|
|
134
|
+
Common.ResourceType.resourceTypes.SourceMapStyleSheet,
|
|
135
|
+
]);
|
|
136
|
+
|
|
119
137
|
const CONNECTION_TYPES = new Map([
|
|
120
138
|
['2g', Protocol.Network.ConnectionType.Cellular2g],
|
|
121
139
|
['3g', Protocol.Network.ConnectionType.Cellular3g],
|
|
@@ -193,18 +211,101 @@ export class NetworkManager extends SDKModel<EventTypes> {
|
|
|
193
211
|
return requestToManagerMap.get(request) || null;
|
|
194
212
|
}
|
|
195
213
|
|
|
196
|
-
static
|
|
197
|
-
|
|
198
|
-
|
|
214
|
+
static canResendRequest(request: NetworkRequest): boolean {
|
|
215
|
+
if (!requestToManagerMap.get(request) || !request.backendRequestId() || request.isRedirect()) {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
return FULL_FIDELITY_RESEND_TYPES.has(request.resourceType());
|
|
199
219
|
}
|
|
200
220
|
|
|
201
221
|
static replayRequest(request: NetworkRequest): void {
|
|
222
|
+
void NetworkManager.resendRequest(request);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
static async resendRequest(request: NetworkRequest): Promise<void> {
|
|
202
226
|
const manager = requestToManagerMap.get(request);
|
|
203
227
|
const requestId = request.backendRequestId();
|
|
204
228
|
if (!manager || !requestId || request.isRedirect()) {
|
|
205
229
|
return;
|
|
206
230
|
}
|
|
207
|
-
|
|
231
|
+
|
|
232
|
+
Host.userMetrics.resendRequest(Host.UserMetrics.resendRequestType(request.resourceType()));
|
|
233
|
+
|
|
234
|
+
// XHR requests use the existing CDP replay mechanism.
|
|
235
|
+
if (request.resourceType() === Common.ResourceType.resourceTypes.XHR) {
|
|
236
|
+
void manager.#networkAgent.invoke_replayXHR({requestId});
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// All other eligible types use fetch via Runtime.evaluate.
|
|
241
|
+
const target = manager.target();
|
|
242
|
+
const runtimeModel = target.model(RuntimeModel);
|
|
243
|
+
if (!runtimeModel) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Resolve execution context: prefer the frame's default context.
|
|
248
|
+
let executionContext: ExecutionContext|null = null;
|
|
249
|
+
const frameId = request.frameId;
|
|
250
|
+
if (frameId) {
|
|
251
|
+
executionContext = runtimeModel.executionContexts().find(ctx => ctx.frameId === frameId && ctx.isDefault) ?? null;
|
|
252
|
+
}
|
|
253
|
+
const usesFallbackContext = !executionContext;
|
|
254
|
+
if (!executionContext) {
|
|
255
|
+
executionContext = runtimeModel.defaultExecutionContext();
|
|
256
|
+
}
|
|
257
|
+
if (!executionContext) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (usesFallbackContext) {
|
|
262
|
+
runtimeModel.target().targetManager().getConsole().warn(
|
|
263
|
+
'Resend: original execution context unavailable, using top-level context.');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Build the fetch expression.
|
|
267
|
+
const method = request.requestMethod;
|
|
268
|
+
const url = request.url();
|
|
269
|
+
const headers: Array<[string, string]> = [];
|
|
270
|
+
for (const {name, value} of request.requestHeaders()) {
|
|
271
|
+
// Skip HTTP/2+ pseudo-headers (e.g. :authority, :method, :path, :scheme).
|
|
272
|
+
if (name.startsWith(':')) {
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
// Skip headers the browser sets automatically for fetch.
|
|
276
|
+
const lower = name.toLowerCase();
|
|
277
|
+
if (lower === 'host' || lower === 'connection' || lower === 'content-length' || lower === 'cookie' ||
|
|
278
|
+
lower === 'origin' || lower === 'referer') {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
headers.push([name, value]);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const body = await request.requestFormData();
|
|
285
|
+
const fetchOptions: {method: string, headers: Array<[string, string]>, credentials: string, body?: string} = {
|
|
286
|
+
method,
|
|
287
|
+
headers,
|
|
288
|
+
credentials: 'include',
|
|
289
|
+
};
|
|
290
|
+
const isGetOrHead = method === 'GET' || method === 'HEAD';
|
|
291
|
+
if (body && !isGetOrHead) {
|
|
292
|
+
fetchOptions.body = body;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const expression = `fetch(${JSON.stringify(url)}, ${JSON.stringify(fetchOptions)})`;
|
|
296
|
+
const response = await target.runtimeAgent().invoke_evaluate({
|
|
297
|
+
expression,
|
|
298
|
+
// Use uniqueContextId if available, otherwise fall back to contextId.
|
|
299
|
+
...(executionContext.uniqueId ? {uniqueContextId: executionContext.uniqueId} : {contextId: executionContext.id}),
|
|
300
|
+
silent: false,
|
|
301
|
+
awaitPromise: true,
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
if (response.getError() || response.exceptionDetails) {
|
|
305
|
+
const errorText = response.getError() || response.exceptionDetails?.exception?.description ||
|
|
306
|
+
response.exceptionDetails?.text || 'Unknown error';
|
|
307
|
+
runtimeModel.target().targetManager().getConsole().error(`Resend failed for ${url}: ${errorText}`);
|
|
308
|
+
}
|
|
208
309
|
}
|
|
209
310
|
|
|
210
311
|
static async searchInRequest(request: NetworkRequest, query: string, caseSensitive: boolean, isRegex: boolean):
|
|
@@ -86,7 +86,7 @@ export class OverlayModel extends SDKModel<EventTypes> implements ProtocolProxyA
|
|
|
86
86
|
overlayAgent: ProtocolProxyApi.OverlayApi;
|
|
87
87
|
readonly #debuggerModel: DebuggerModel|null;
|
|
88
88
|
#inspectModeEnabled = false;
|
|
89
|
-
#hideHighlightTimeout
|
|
89
|
+
#hideHighlightTimeout?: ReturnType<typeof setTimeout>;
|
|
90
90
|
#defaultHighlighter: Highlighter;
|
|
91
91
|
#highlighter: Highlighter;
|
|
92
92
|
#showPaintRectsSetting: Common.Settings.Setting<boolean>;
|
|
@@ -331,14 +331,13 @@ export class OverlayModel extends SDKModel<EventTypes> implements ProtocolProxyA
|
|
|
331
331
|
// overlay, so that it is not cleared by the highlight
|
|
332
332
|
return;
|
|
333
333
|
}
|
|
334
|
-
if (this.#hideHighlightTimeout) {
|
|
335
334
|
clearTimeout(this.#hideHighlightTimeout);
|
|
336
|
-
this.#hideHighlightTimeout =
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
335
|
+
this.#hideHighlightTimeout = undefined;
|
|
336
|
+
|
|
337
|
+
const highlightConfig = this.buildHighlightConfig(mode);
|
|
338
|
+
if (typeof showInfo !== 'undefined') {
|
|
339
|
+
highlightConfig.showInfo = showInfo;
|
|
340
|
+
}
|
|
342
341
|
this.#highlighter.highlightInOverlay(data, highlightConfig);
|
|
343
342
|
}
|
|
344
343
|
|
|
@@ -486,17 +485,19 @@ export class OverlayModel extends SDKModel<EventTypes> implements ProtocolProxyA
|
|
|
486
485
|
}
|
|
487
486
|
|
|
488
487
|
private delayedHideHighlight(delay: number): void {
|
|
489
|
-
if (this.#hideHighlightTimeout ===
|
|
490
|
-
this.#hideHighlightTimeout =
|
|
488
|
+
if (this.#hideHighlightTimeout === undefined) {
|
|
489
|
+
this.#hideHighlightTimeout = globalThis.setTimeout(
|
|
490
|
+
() => this.highlightInOverlay({clear: true}),
|
|
491
|
+
delay,
|
|
492
|
+
);
|
|
491
493
|
}
|
|
492
494
|
}
|
|
493
495
|
|
|
494
496
|
highlightFrame(frameId: Protocol.Page.FrameId): void {
|
|
495
|
-
if (this.#hideHighlightTimeout) {
|
|
496
497
|
clearTimeout(this.#hideHighlightTimeout);
|
|
497
|
-
this.#hideHighlightTimeout =
|
|
498
|
-
|
|
499
|
-
|
|
498
|
+
this.#hideHighlightTimeout = undefined;
|
|
499
|
+
|
|
500
|
+
this.#highlighter.highlightFrame(frameId);
|
|
500
501
|
}
|
|
501
502
|
|
|
502
503
|
showHingeForDualScreen(hinge: Hinge|null): void {
|
|
@@ -868,7 +868,7 @@ export class LocalJSONObject extends RemoteObject {
|
|
|
868
868
|
Promise<GetPropertiesResult> {
|
|
869
869
|
function isArrayIndex(name: string): boolean {
|
|
870
870
|
const index = Number(name) >>> 0;
|
|
871
|
-
return String(index) === name;
|
|
871
|
+
return String(index) === name && index < 4294967295;
|
|
872
872
|
}
|
|
873
873
|
|
|
874
874
|
let properties = this.children();
|
|
@@ -445,6 +445,7 @@
|
|
|
445
445
|
SyncSetting: 'DevTools.SyncSetting',
|
|
446
446
|
SwatchActivated: 'DevTools.SwatchActivated',
|
|
447
447
|
BuiltInAiAvailability: 'DevTools.BuiltInAiAvailability',
|
|
448
|
+
ResendRequest: 'DevTools.ResendRequest',
|
|
448
449
|
// LINT.ThenChange(/front_end/core/host/InspectorFrontendHostAPI.ts:EnumeratedHistogram)
|
|
449
450
|
};
|
|
450
451
|
|