chrome-devtools-frontend 1.0.1590494 → 1.0.1591204
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/docs/ui_engineering.md +33 -0
- package/front_end/core/common/Gzip.ts +17 -2
- package/front_end/core/host/UserMetrics.ts +2 -1
- package/front_end/core/root/ExperimentNames.ts +1 -0
- package/front_end/core/root/Runtime.ts +5 -0
- package/front_end/core/sdk/EmulationModel.ts +6 -1
- package/front_end/core/sdk/NetworkManager.ts +49 -2
- package/front_end/core/sdk/NetworkRequest.ts +4 -0
- package/front_end/core/sdk/sdk-meta.ts +26 -0
- package/front_end/entrypoints/inspector_main/RenderingOptions.ts +34 -5
- package/front_end/entrypoints/main/MainImpl.ts +8 -0
- package/front_end/generated/InspectorBackendCommands.ts +1 -0
- package/front_end/generated/protocol-mapping.d.ts +10 -0
- package/front_end/generated/protocol-proxy-api.d.ts +8 -0
- package/front_end/generated/protocol.ts +4 -0
- package/front_end/models/javascript_metadata/NativeFunctions.js +5 -1
- package/front_end/panels/ai_assistance/README.md +14 -0
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +45 -27
- package/front_end/panels/ai_assistance/components/chatMessage.css +6 -0
- package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +25 -46
- package/front_end/panels/elements/ComputedStyleWidget.ts +32 -11
- package/front_end/panels/elements/StylePropertyTreeElement.ts +1 -1
- package/front_end/panels/network/RequestPayloadView.ts +7 -6
- package/front_end/panels/search/SearchResultsPane.ts +2 -1
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/legacy/Treeoutline.ts +39 -0
- package/front_end/ui/legacy/components/source_frame/XMLView.ts +20 -5
- package/front_end/ui/visual_logging/Debugging.ts +51 -34
- package/front_end/ui/visual_logging/KnownContextValues.ts +5 -1
- package/front_end/ui/visual_logging/LoggingEvents.ts +5 -2
- package/package.json +1 -1
package/docs/ui_engineering.md
CHANGED
|
@@ -1383,6 +1383,39 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
1383
1383
|
};
|
|
1384
1384
|
```
|
|
1385
1385
|
|
|
1386
|
+
In a more complex case you might want to skip rendering of the collapsed nodes
|
|
1387
|
+
subtrees and temporarily expand nodes to show search matches. This could be done
|
|
1388
|
+
as follows:
|
|
1389
|
+
|
|
1390
|
+
```typescript
|
|
1391
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
|
1392
|
+
render(html`
|
|
1393
|
+
<div>
|
|
1394
|
+
<devtools-tree .template=${html`
|
|
1395
|
+
<ul role="tree">
|
|
1396
|
+
${input.topLevelNodes.map(node => html`
|
|
1397
|
+
<li role="treeitem" ?open=${containsSearchResult(node)}>
|
|
1398
|
+
${node.name}
|
|
1399
|
+
${node.children.length ? html`
|
|
1400
|
+
<ul role="group">
|
|
1401
|
+
${ifExpanded(node.children.map(renderChild))}
|
|
1402
|
+
</ul>
|
|
1403
|
+
` : nothing}
|
|
1404
|
+
</li>`)}
|
|
1405
|
+
</ul>
|
|
1406
|
+
`}></devtools-tree>
|
|
1407
|
+
</div>`,
|
|
1408
|
+
target, {host: input});
|
|
1409
|
+
};
|
|
1410
|
+
```
|
|
1411
|
+
|
|
1412
|
+
Here `open` attribute overrides the expansion state set by the user. Once `open`
|
|
1413
|
+
attribute is removed, the expansion state is reversed to the last state set by
|
|
1414
|
+
the user.
|
|
1415
|
+
|
|
1416
|
+
`ifExpanded` is a lit directive provided on `UI.TreeOutline` and it makes its
|
|
1417
|
+
argument render only if the current tree node is expanded.
|
|
1418
|
+
|
|
1386
1419
|
## Refactoring UI.Toolbar.Provider
|
|
1387
1420
|
|
|
1388
1421
|
As part of the migration, sometimes classes need to be broken up into smaller pieces. Classes implementing
|
|
@@ -37,11 +37,26 @@ export async function fileToString(file: File): Promise<string> {
|
|
|
37
37
|
* Decompress a gzipped ArrayBuffer to a string.
|
|
38
38
|
* Consider using `arrayBufferToString` instead, which can handle both gzipped and plain text buffers.
|
|
39
39
|
*/
|
|
40
|
-
export async function decompress(gzippedBuffer: ArrayBufferLike): Promise<string> {
|
|
40
|
+
export async function decompress(gzippedBuffer: ArrayBufferLike, charset = 'utf-8'): Promise<string> {
|
|
41
41
|
const buffer = await gzipCodec(gzippedBuffer, new DecompressionStream('gzip'));
|
|
42
|
-
const str = new TextDecoder(
|
|
42
|
+
const str = new TextDecoder(charset).decode(buffer);
|
|
43
43
|
return str;
|
|
44
44
|
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Decompress a deflate-encoded ArrayBuffer to a string.
|
|
48
|
+
* Tries 'deflate' (zlib wrapper) first, then falls back to 'deflate-raw'.
|
|
49
|
+
*/
|
|
50
|
+
export async function decompressDeflate(buffer: ArrayBufferLike, charset = 'utf-8'): Promise<string> {
|
|
51
|
+
let decompressedBuffer: ArrayBuffer;
|
|
52
|
+
try {
|
|
53
|
+
decompressedBuffer = await gzipCodec(buffer, new DecompressionStream('deflate'));
|
|
54
|
+
} catch {
|
|
55
|
+
// Try deflate-raw format if zlib-wrapped deflate fails.
|
|
56
|
+
decompressedBuffer = await gzipCodec(buffer, new DecompressionStream('deflate-raw'));
|
|
57
|
+
}
|
|
58
|
+
return new TextDecoder(charset).decode(decompressedBuffer);
|
|
59
|
+
}
|
|
45
60
|
export async function compress(str: string): Promise<ArrayBuffer> {
|
|
46
61
|
const encoded = new TextEncoder().encode(str);
|
|
47
62
|
const buffer = await gzipCodec(encoded, new CompressionStream('gzip'));
|
|
@@ -825,10 +825,11 @@ export enum DevtoolsExperiments {
|
|
|
825
825
|
'use-source-map-scopes' = 76,
|
|
826
826
|
'timeline-show-postmessage-events' = 86,
|
|
827
827
|
'timeline-debug-mode' = 93,
|
|
828
|
+
'durable-messages' = 110,
|
|
828
829
|
/* eslint-enable @typescript-eslint/naming-convention */
|
|
829
830
|
|
|
830
831
|
// Increment this when new experiments are added.
|
|
831
|
-
MAX_VALUE =
|
|
832
|
+
MAX_VALUE = 111,
|
|
832
833
|
}
|
|
833
834
|
|
|
834
835
|
/** Update DevToolsIssuesPanelIssueExpanded from tools/metrics/histograms/enums.xml if new enum is added. **/
|
|
@@ -23,6 +23,7 @@ export enum ExperimentName {
|
|
|
23
23
|
USE_SOURCE_MAP_SCOPES = 'use-source-map-scopes',
|
|
24
24
|
TIMELINE_SHOW_POST_MESSAGE_EVENTS = 'timeline-show-postmessage-events',
|
|
25
25
|
TIMELINE_DEBUG_MODE = 'timeline-debug-mode',
|
|
26
|
+
DURABLE_MESSAGES = 'durable-messages',
|
|
26
27
|
// Adding or removing an entry from this enum?
|
|
27
28
|
// You will need to update:
|
|
28
29
|
// 1. DevToolsExperiments enum in host/UserMetrics.ts
|
|
@@ -534,6 +534,10 @@ export interface HostConfigAnimationStylesInStylesTab {
|
|
|
534
534
|
enabled: boolean;
|
|
535
535
|
}
|
|
536
536
|
|
|
537
|
+
export interface HostConfigJpegXlImageFormat {
|
|
538
|
+
enabled: boolean;
|
|
539
|
+
}
|
|
540
|
+
|
|
537
541
|
export interface HostConfigThirdPartyCookieControls {
|
|
538
542
|
thirdPartyCookieRestrictionEnabled: boolean;
|
|
539
543
|
thirdPartyCookieMetadataEnabled: boolean;
|
|
@@ -641,6 +645,7 @@ export type HostConfig = Platform.TypeScriptUtilities.RecursivePartial<{
|
|
|
641
645
|
isOffTheRecord: boolean,
|
|
642
646
|
devToolsEnableOriginBoundCookies: HostConfigEnableOriginBoundCookies,
|
|
643
647
|
devToolsAnimationStylesInStylesTab: HostConfigAnimationStylesInStylesTab,
|
|
648
|
+
devToolsJpegXlImageFormat: HostConfigJpegXlImageFormat,
|
|
644
649
|
thirdPartyCookieControls: HostConfigThirdPartyCookieControls,
|
|
645
650
|
devToolsAiGeneratedTimelineLabels: AiGeneratedTimelineLabels,
|
|
646
651
|
devToolsAllowPopoverForcing: AllowPopoverForcing,
|
|
@@ -191,6 +191,7 @@ export class EmulationModel extends SDKModel<void> {
|
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
const avifFormatDisabledSetting = Common.Settings.Settings.instance().moduleSetting('avif-format-disabled');
|
|
194
|
+
const jpegXlFormatDisabledSetting = Common.Settings.Settings.instance().moduleSetting('jpeg-xl-format-disabled');
|
|
194
195
|
const webpFormatDisabledSetting = Common.Settings.Settings.instance().moduleSetting('webp-format-disabled');
|
|
195
196
|
|
|
196
197
|
const updateDisabledImageFormats = (): void => {
|
|
@@ -198,6 +199,9 @@ export class EmulationModel extends SDKModel<void> {
|
|
|
198
199
|
if (avifFormatDisabledSetting.get()) {
|
|
199
200
|
types.push(Protocol.Emulation.DisabledImageType.Avif);
|
|
200
201
|
}
|
|
202
|
+
if (jpegXlFormatDisabledSetting.get()) {
|
|
203
|
+
types.push(Protocol.Emulation.DisabledImageType.Jxl);
|
|
204
|
+
}
|
|
201
205
|
if (webpFormatDisabledSetting.get()) {
|
|
202
206
|
types.push(Protocol.Emulation.DisabledImageType.Webp);
|
|
203
207
|
}
|
|
@@ -205,9 +209,10 @@ export class EmulationModel extends SDKModel<void> {
|
|
|
205
209
|
};
|
|
206
210
|
|
|
207
211
|
avifFormatDisabledSetting.addChangeListener(updateDisabledImageFormats);
|
|
212
|
+
jpegXlFormatDisabledSetting.addChangeListener(updateDisabledImageFormats);
|
|
208
213
|
webpFormatDisabledSetting.addChangeListener(updateDisabledImageFormats);
|
|
209
214
|
|
|
210
|
-
if (avifFormatDisabledSetting.get() || webpFormatDisabledSetting.get()) {
|
|
215
|
+
if (avifFormatDisabledSetting.get() || jpegXlFormatDisabledSetting.get() || webpFormatDisabledSetting.get()) {
|
|
211
216
|
updateDisabledImageFormats();
|
|
212
217
|
}
|
|
213
218
|
|
|
@@ -277,13 +277,56 @@ export class NetworkManager extends SDKModel<EventTypes> {
|
|
|
277
277
|
return null;
|
|
278
278
|
}
|
|
279
279
|
try {
|
|
280
|
-
const {postData} = await manager.#networkAgent.invoke_getRequestPostData({requestId});
|
|
280
|
+
const {postData, base64Encoded} = await manager.#networkAgent.invoke_getRequestPostData({requestId});
|
|
281
|
+
if (base64Encoded && postData) {
|
|
282
|
+
// Decode base64 to get raw bytes as an ArrayBuffer.
|
|
283
|
+
const binaryString = window.atob(postData);
|
|
284
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
285
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
286
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Extract charset from request Content-Type header, defaulting to utf-8.
|
|
290
|
+
const requestContentType = request.requestContentType();
|
|
291
|
+
const charset =
|
|
292
|
+
requestContentType ? Platform.MimeType.parseContentType(requestContentType).charset ?? 'utf-8' : 'utf-8';
|
|
293
|
+
|
|
294
|
+
// If the request body is compressed, attempt to decompress it.
|
|
295
|
+
const contentEncoding = request.requestContentEncoding()?.toLowerCase();
|
|
296
|
+
if (contentEncoding) {
|
|
297
|
+
const decompressed = await NetworkManager.#tryDecompressBody(bytes.buffer, contentEncoding, charset);
|
|
298
|
+
if (decompressed !== null) {
|
|
299
|
+
return decompressed;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Not compressed or decompression not applicable -- decode as text.
|
|
304
|
+
return new TextDecoder(charset).decode(bytes);
|
|
305
|
+
}
|
|
281
306
|
return postData;
|
|
282
307
|
} catch (e) {
|
|
283
308
|
return e.message;
|
|
284
309
|
}
|
|
285
310
|
}
|
|
286
311
|
|
|
312
|
+
/**
|
|
313
|
+
* Attempts to decompress a compressed request body.
|
|
314
|
+
* Returns the decompressed string, or null if decompression is not applicable.
|
|
315
|
+
*/
|
|
316
|
+
static async #tryDecompressBody(buffer: ArrayBuffer, encoding: string, charset: string): Promise<string|null> {
|
|
317
|
+
try {
|
|
318
|
+
if (encoding.includes('gzip') && Common.Gzip.isGzip(buffer)) {
|
|
319
|
+
return await Common.Gzip.decompress(buffer, charset);
|
|
320
|
+
}
|
|
321
|
+
if (encoding.includes('deflate')) {
|
|
322
|
+
return await Common.Gzip.decompressDeflate(buffer, charset);
|
|
323
|
+
}
|
|
324
|
+
} catch (e) {
|
|
325
|
+
console.warn('Failed to decompress request body:', e);
|
|
326
|
+
}
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
|
|
287
330
|
static connectionType(conditions: Conditions): Protocol.Network.ConnectionType {
|
|
288
331
|
if (!conditions.download && !conditions.upload) {
|
|
289
332
|
return Protocol.Network.ConnectionType.None;
|
|
@@ -567,7 +610,11 @@ export class NetworkDispatcher implements ProtocolProxyApi.NetworkDispatcher {
|
|
|
567
610
|
private updateNetworkRequestWithRequest(networkRequest: NetworkRequest, request: Protocol.Network.Request): void {
|
|
568
611
|
networkRequest.requestMethod = request.method;
|
|
569
612
|
networkRequest.setRequestHeaders(this.headersMapToHeadersArray(request.headers));
|
|
570
|
-
|
|
613
|
+
// If the request body is compressed, discard the inline postData which is
|
|
614
|
+
// garbled (binary-as-text). The getRequestPostData command will provide
|
|
615
|
+
// properly base64-encoded data that we can decompress.
|
|
616
|
+
const isCompressed = Boolean(networkRequest.requestContentEncoding());
|
|
617
|
+
networkRequest.setRequestFormData(Boolean(request.hasPostData), isCompressed ? null : (request.postData || null));
|
|
571
618
|
networkRequest.setInitialPriority(request.initialPriority);
|
|
572
619
|
networkRequest.mixedContentType = request.mixedContentType || Protocol.Security.MixedContentType.None;
|
|
573
620
|
networkRequest.setReferrerPolicy(request.referrerPolicy);
|
|
@@ -1471,6 +1471,10 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper<EventType
|
|
|
1471
1471
|
return this.requestHeaderValue('Content-Type');
|
|
1472
1472
|
}
|
|
1473
1473
|
|
|
1474
|
+
requestContentEncoding(): string|undefined {
|
|
1475
|
+
return this.requestHeaderValue('Content-Encoding');
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1474
1478
|
hasErrorStatusCode(): boolean {
|
|
1475
1479
|
return this.statusCode >= 400;
|
|
1476
1480
|
}
|
|
@@ -313,6 +313,14 @@ const UIStrings = {
|
|
|
313
313
|
* @description Title of a setting that enables AVIF format
|
|
314
314
|
*/
|
|
315
315
|
enableAvifFormat: 'Enable `AVIF` format',
|
|
316
|
+
/**
|
|
317
|
+
* @description Title of a setting that disables JPEG XL format
|
|
318
|
+
*/
|
|
319
|
+
disableJpegXlFormat: 'Disable `JPEG XL` format',
|
|
320
|
+
/**
|
|
321
|
+
* @description Title of a setting that enables JPEG XL format
|
|
322
|
+
*/
|
|
323
|
+
enableJpegXlFormat: 'Enable `JPEG XL` format',
|
|
316
324
|
/**
|
|
317
325
|
* @description Title of a setting that disables WebP format
|
|
318
326
|
*/
|
|
@@ -1072,6 +1080,24 @@ Common.Settings.registerSettingExtension({
|
|
|
1072
1080
|
defaultValue: false,
|
|
1073
1081
|
});
|
|
1074
1082
|
|
|
1083
|
+
Common.Settings.registerSettingExtension({
|
|
1084
|
+
category: Common.Settings.SettingCategory.RENDERING,
|
|
1085
|
+
settingName: 'jpeg-xl-format-disabled',
|
|
1086
|
+
settingType: Common.Settings.SettingType.BOOLEAN,
|
|
1087
|
+
storageType: Common.Settings.SettingStorageType.SESSION,
|
|
1088
|
+
options: [
|
|
1089
|
+
{
|
|
1090
|
+
value: true,
|
|
1091
|
+
title: i18nLazyString(UIStrings.disableJpegXlFormat),
|
|
1092
|
+
},
|
|
1093
|
+
{
|
|
1094
|
+
value: false,
|
|
1095
|
+
title: i18nLazyString(UIStrings.enableJpegXlFormat),
|
|
1096
|
+
},
|
|
1097
|
+
],
|
|
1098
|
+
defaultValue: false,
|
|
1099
|
+
});
|
|
1100
|
+
|
|
1075
1101
|
Common.Settings.registerSettingExtension({
|
|
1076
1102
|
category: Common.Settings.SettingCategory.RENDERING,
|
|
1077
1103
|
settingName: 'webp-format-disabled',
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import * as Common from '../../core/common/common.js';
|
|
8
8
|
import * as Host from '../../core/host/host.js';
|
|
9
9
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
10
|
+
import * as Root from '../../core/root/root.js';
|
|
10
11
|
import * as SettingsUI from '../../ui/legacy/components/settings_ui/settings_ui.js';
|
|
11
12
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
12
13
|
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
|
|
@@ -145,10 +146,14 @@ const UIStrings = {
|
|
|
145
146
|
*/
|
|
146
147
|
disableAvifImageFormat: 'Disable `AVIF` image format',
|
|
147
148
|
/**
|
|
148
|
-
* @description Explanation text for
|
|
149
|
-
* format' settings in the Rendering tool.
|
|
149
|
+
* @description Explanation text for the image format disabling settings in the Rendering tool.
|
|
150
150
|
*/
|
|
151
151
|
requiresAPageReloadToApplyAnd: 'Requires a page reload to apply and disables caching for image requests.',
|
|
152
|
+
/**
|
|
153
|
+
* @description The name of a checkbox setting in the Rendering tool. This setting disables the
|
|
154
|
+
* page from loading images with the JPEG XL format.
|
|
155
|
+
*/
|
|
156
|
+
disableJpegXlImageFormat: 'Disable `JPEG XL` image format',
|
|
152
157
|
/**
|
|
153
158
|
* @description The name of a checkbox setting in the Rendering tool. This setting disables the
|
|
154
159
|
* page from loading images with the WebP format.
|
|
@@ -184,7 +189,13 @@ const supportsPrefersContrast = (): boolean => {
|
|
|
184
189
|
return window.matchMedia(query).matches;
|
|
185
190
|
};
|
|
186
191
|
|
|
192
|
+
const supportsJpegXl = (): boolean => {
|
|
193
|
+
return Boolean(Root.Runtime.hostConfig.devToolsJpegXlImageFormat?.enabled);
|
|
194
|
+
};
|
|
195
|
+
|
|
187
196
|
export class RenderingOptionsView extends UI.Widget.VBox {
|
|
197
|
+
#jpegXlCheckboxAdded = false;
|
|
198
|
+
|
|
188
199
|
constructor() {
|
|
189
200
|
super({useShadowDom: true});
|
|
190
201
|
this.registerRequiredCSS(renderingOptionsStyles);
|
|
@@ -266,13 +277,19 @@ export class RenderingOptionsView extends UI.Widget.VBox {
|
|
|
266
277
|
|
|
267
278
|
this.contentElement.createChild('div').classList.add('panel-section-separator');
|
|
268
279
|
|
|
280
|
+
const avifFormatDisabledSetting = Common.Settings.Settings.instance().moduleSetting('avif-format-disabled');
|
|
281
|
+
const jpegXlFormatDisabledSetting = Common.Settings.Settings.instance().moduleSetting('jpeg-xl-format-disabled');
|
|
282
|
+
const webpFormatDisabledSetting = Common.Settings.Settings.instance().moduleSetting('webp-format-disabled');
|
|
283
|
+
|
|
269
284
|
this.#appendCheckbox(
|
|
270
285
|
i18nString(UIStrings.disableAvifImageFormat), i18nString(UIStrings.requiresAPageReloadToApplyAnd),
|
|
271
|
-
|
|
286
|
+
avifFormatDisabledSetting);
|
|
272
287
|
|
|
273
|
-
this.#appendCheckbox(
|
|
288
|
+
const webpCheckbox = this.#appendCheckbox(
|
|
274
289
|
i18nString(UIStrings.disableWebpImageFormat), i18nString(UIStrings.requiresAPageReloadToApplyAnd),
|
|
275
|
-
|
|
290
|
+
webpFormatDisabledSetting);
|
|
291
|
+
|
|
292
|
+
this.#appendJpegXlCheckboxWhenSupported(webpCheckbox, jpegXlFormatDisabledSetting);
|
|
276
293
|
|
|
277
294
|
this.contentElement.createChild('div').classList.add('panel-section-separator');
|
|
278
295
|
}
|
|
@@ -286,6 +303,18 @@ export class RenderingOptionsView extends UI.Widget.VBox {
|
|
|
286
303
|
return checkbox;
|
|
287
304
|
}
|
|
288
305
|
|
|
306
|
+
#appendJpegXlCheckboxWhenSupported(
|
|
307
|
+
webpCheckbox: UI.UIUtils.CheckboxLabel, jpegXlFormatDisabledSetting: Common.Settings.Setting<boolean>): void {
|
|
308
|
+
if (this.#jpegXlCheckboxAdded || !supportsJpegXl()) {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
this.#jpegXlCheckboxAdded = true;
|
|
313
|
+
webpCheckbox.before(this.#appendCheckbox(
|
|
314
|
+
i18nString(UIStrings.disableJpegXlImageFormat), i18nString(UIStrings.requiresAPageReloadToApplyAnd),
|
|
315
|
+
jpegXlFormatDisabledSetting));
|
|
316
|
+
}
|
|
317
|
+
|
|
289
318
|
#appendSelect(label: string, setting: Common.Settings.Setting<unknown>): void {
|
|
290
319
|
const control = SettingsUI.SettingsUI.createControlForSetting(setting, label);
|
|
291
320
|
if (control) {
|
|
@@ -423,6 +423,14 @@ export class MainImpl {
|
|
|
423
423
|
'Performance panel: show postMessage dispatch and handling flows',
|
|
424
424
|
);
|
|
425
425
|
|
|
426
|
+
Root.Runtime.experiments.registerHostExperiment({
|
|
427
|
+
name: Root.ExperimentNames.ExperimentName.DURABLE_MESSAGES,
|
|
428
|
+
title: 'Durable Messages',
|
|
429
|
+
aboutFlag: 'devtools-enable-durable-messages',
|
|
430
|
+
isEnabled: Root.Runtime.hostConfig.devToolsEnableDurableMessages?.enabled ?? false,
|
|
431
|
+
requiresChromeRestart: false,
|
|
432
|
+
});
|
|
433
|
+
|
|
426
434
|
Root.Runtime.experiments.enableExperimentsByDefault([
|
|
427
435
|
Root.ExperimentNames.ExperimentName.FULL_ACCESSIBILITY_TREE,
|
|
428
436
|
Root.ExperimentNames.ExperimentName.USE_SOURCE_MAP_SCOPES,
|
|
@@ -557,6 +557,7 @@ inspectorBackend.registerCommand("Emulation.setSmallViewportHeightDifferenceOver
|
|
|
557
557
|
inspectorBackend.registerCommand("Emulation.getScreenInfos", [], ["screenInfos"], "Returns device's screen configuration. In headful mode, the physical screens configuration is returned, whereas in headless mode, a virtual headless screen configuration is provided instead.");
|
|
558
558
|
inspectorBackend.registerCommand("Emulation.addScreen", [{"name": "left", "type": "number", "optional": false, "description": "Offset of the left edge of the screen in pixels.", "typeRef": null}, {"name": "top", "type": "number", "optional": false, "description": "Offset of the top edge of the screen in pixels.", "typeRef": null}, {"name": "width", "type": "number", "optional": false, "description": "The width of the screen in pixels.", "typeRef": null}, {"name": "height", "type": "number", "optional": false, "description": "The height of the screen in pixels.", "typeRef": null}, {"name": "workAreaInsets", "type": "object", "optional": true, "description": "Specifies the screen's work area. Default is entire screen.", "typeRef": "Emulation.WorkAreaInsets"}, {"name": "devicePixelRatio", "type": "number", "optional": true, "description": "Specifies the screen's device pixel ratio. Default is 1.", "typeRef": null}, {"name": "rotation", "type": "number", "optional": true, "description": "Specifies the screen's rotation angle. Available values are 0, 90, 180 and 270. Default is 0.", "typeRef": null}, {"name": "colorDepth", "type": "number", "optional": true, "description": "Specifies the screen's color depth in bits. Default is 24.", "typeRef": null}, {"name": "label", "type": "string", "optional": true, "description": "Specifies the descriptive label for the screen. Default is none.", "typeRef": null}, {"name": "isInternal", "type": "boolean", "optional": true, "description": "Indicates whether the screen is internal to the device or external, attached to the device. Default is false.", "typeRef": null}], ["screenInfo"], "Add a new screen to the device. Only supported in headless mode.");
|
|
559
559
|
inspectorBackend.registerCommand("Emulation.removeScreen", [{"name": "screenId", "type": "string", "optional": false, "description": "", "typeRef": "Emulation.ScreenId"}], [], "Remove screen from the device. Only supported in headless mode.");
|
|
560
|
+
inspectorBackend.registerCommand("Emulation.setPrimaryScreen", [{"name": "screenId", "type": "string", "optional": false, "description": "", "typeRef": "Emulation.ScreenId"}], [], "Set primary screen. Only supported in headless mode. Note that this changes the coordinate system origin to the top-left of the new primary screen, updating the bounds and work areas of all existing screens accordingly.");
|
|
560
561
|
inspectorBackend.registerType("Emulation.SafeAreaInsets", [{"name": "top", "type": "number", "optional": true, "description": "Overrides safe-area-inset-top.", "typeRef": null}, {"name": "topMax", "type": "number", "optional": true, "description": "Overrides safe-area-max-inset-top.", "typeRef": null}, {"name": "left", "type": "number", "optional": true, "description": "Overrides safe-area-inset-left.", "typeRef": null}, {"name": "leftMax", "type": "number", "optional": true, "description": "Overrides safe-area-max-inset-left.", "typeRef": null}, {"name": "bottom", "type": "number", "optional": true, "description": "Overrides safe-area-inset-bottom.", "typeRef": null}, {"name": "bottomMax", "type": "number", "optional": true, "description": "Overrides safe-area-max-inset-bottom.", "typeRef": null}, {"name": "right", "type": "number", "optional": true, "description": "Overrides safe-area-inset-right.", "typeRef": null}, {"name": "rightMax", "type": "number", "optional": true, "description": "Overrides safe-area-max-inset-right.", "typeRef": null}]);
|
|
561
562
|
inspectorBackend.registerType("Emulation.ScreenOrientation", [{"name": "type", "type": "string", "optional": false, "description": "Orientation type.", "typeRef": null}, {"name": "angle", "type": "number", "optional": false, "description": "Orientation angle.", "typeRef": null}]);
|
|
562
563
|
inspectorBackend.registerType("Emulation.DisplayFeature", [{"name": "orientation", "type": "string", "optional": false, "description": "Orientation of a display feature in relation to screen", "typeRef": null}, {"name": "offset", "type": "number", "optional": false, "description": "The offset from the screen origin in either the x (for vertical orientation) or y (for horizontal orientation) direction.", "typeRef": null}, {"name": "maskLength", "type": "number", "optional": false, "description": "A display feature may mask content such that it is not physically displayed - this length along with the offset describes this area. A display feature that only splits content will have a 0 mask_length.", "typeRef": null}]);
|
|
@@ -2782,6 +2782,16 @@ export namespace ProtocolMapping {
|
|
|
2782
2782
|
paramsType: [Protocol.Emulation.RemoveScreenRequest];
|
|
2783
2783
|
returnType: void;
|
|
2784
2784
|
};
|
|
2785
|
+
/**
|
|
2786
|
+
* Set primary screen. Only supported in headless mode.
|
|
2787
|
+
* Note that this changes the coordinate system origin to the top-left
|
|
2788
|
+
* of the new primary screen, updating the bounds and work areas
|
|
2789
|
+
* of all existing screens accordingly.
|
|
2790
|
+
*/
|
|
2791
|
+
'Emulation.setPrimaryScreen': {
|
|
2792
|
+
paramsType: [Protocol.Emulation.SetPrimaryScreenRequest];
|
|
2793
|
+
returnType: void;
|
|
2794
|
+
};
|
|
2785
2795
|
/**
|
|
2786
2796
|
* Sets breakpoint on particular native event.
|
|
2787
2797
|
*/
|
|
@@ -1852,6 +1852,14 @@ declare namespace ProtocolProxyApi {
|
|
|
1852
1852
|
*/
|
|
1853
1853
|
invoke_removeScreen(params: Protocol.Emulation.RemoveScreenRequest): Promise<Protocol.ProtocolResponseWithError>;
|
|
1854
1854
|
|
|
1855
|
+
/**
|
|
1856
|
+
* Set primary screen. Only supported in headless mode.
|
|
1857
|
+
* Note that this changes the coordinate system origin to the top-left
|
|
1858
|
+
* of the new primary screen, updating the bounds and work areas
|
|
1859
|
+
* of all existing screens accordingly.
|
|
1860
|
+
*/
|
|
1861
|
+
invoke_setPrimaryScreen(params: Protocol.Emulation.SetPrimaryScreenRequest): Promise<Protocol.ProtocolResponseWithError>;
|
|
1862
|
+
|
|
1855
1863
|
}
|
|
1856
1864
|
export interface EmulationDispatcher {
|
|
1857
1865
|
/**
|
|
@@ -6716,7 +6716,7 @@ export const NativeFunctions = [
|
|
|
6716
6716
|
signatures: [["?options"]]
|
|
6717
6717
|
},
|
|
6718
6718
|
{
|
|
6719
|
-
name: "
|
|
6719
|
+
name: "createValueRange",
|
|
6720
6720
|
signatures: [["start","end"]]
|
|
6721
6721
|
},
|
|
6722
6722
|
{
|
|
@@ -7021,6 +7021,10 @@ export const NativeFunctions = [
|
|
|
7021
7021
|
name: "Highlight",
|
|
7022
7022
|
signatures: [["...initRanges"]]
|
|
7023
7023
|
},
|
|
7024
|
+
{
|
|
7025
|
+
name: "CanvasPaintEvent",
|
|
7026
|
+
signatures: [["type","?eventInitDict"]]
|
|
7027
|
+
},
|
|
7024
7028
|
{
|
|
7025
7029
|
name: "getElementTransform",
|
|
7026
7030
|
signatures: [["element","draw_transform"]]
|
|
@@ -69,3 +69,17 @@ The key to the walkthrough feature lies in the `parts` array of a `ModelChatMess
|
|
|
69
69
|
- `text`: The markdown-formatted text of the answer.
|
|
70
70
|
|
|
71
71
|
A single `ModelChatMessage` will typically have **zero or more `StepPart`s** followed by **one `AnswerPart`**. This structure allows the UI to first show the final answer and provide the option to progressively disclose the "thinking" steps that led to it via the `WalkthroughView`.
|
|
72
|
+
|
|
73
|
+
## Understanding Step Loading States
|
|
74
|
+
|
|
75
|
+
There are two distinct `isLoading` properties related to the AI's response generation, serving different purposes:
|
|
76
|
+
|
|
77
|
+
1. **`step.isLoading`**: This property resides within a single `Step` object and indicates the immediate parsing status of that specific step from the AI's response stream. It becomes `true` when a new step begins (e.g., "Querying...") and is set to `false` almost immediately once the first piece of meaningful information for that step (like a "thought" or a "code" block) is received. Essentially, it's a short-lived internal parsing state.
|
|
78
|
+
|
|
79
|
+
2. **Conversation `isLoading`** (passed down to the `ChatMessage` component): This top-level property reflects the overall state of the entire conversation's response generation process. It is set to `true` as soon as the user submits a query and remains `true` until the AI has completed its entire thought process, including all steps, and has provided the final answer.
|
|
80
|
+
|
|
81
|
+
The spinner displayed next to a step in the UI is intentionally tied to the **conversation `isLoading`** property, not the individual `step.isLoading`. This design choice provides a better user experience by:
|
|
82
|
+
|
|
83
|
+
* **Providing Continuous Feedback**: Using the conversation's `isLoading` ensures a persistent visual indicator that the AI is actively working on the query, even if individual steps are quickly parsed. This prevents the UI from appearing unresponsive.
|
|
84
|
+
* **Avoiding a "Stuck" Feeling**: If the spinner were tied to `step.isLoading`, it would flicker on and off rapidly, potentially making the user feel that the AI has stopped processing or is stuck.
|
|
85
|
+
* **Clear Progress Visualization**: The spinner is dynamically moved to the *last* active step in the list as long as the overall conversation is loading. Once a step is completed, it receives a checkmark, and the spinner moves to the next active step.
|
|
@@ -386,12 +386,15 @@ function renderTextAsMarkdown(text: string, markdownRenderer: MarkdownLitRendere
|
|
|
386
386
|
// clang-format on
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
+
function titleForStep(step: Step): string {
|
|
390
|
+
return step.title ?? `${lockedString(UIStringsNotTranslate.investigating)}…`;
|
|
391
|
+
}
|
|
392
|
+
|
|
389
393
|
function renderTitle(step: Step): Lit.LitTemplate {
|
|
390
394
|
const paused =
|
|
391
395
|
step.sideEffect ? html`<span class="paused">${lockedString(UIStringsNotTranslate.paused)}: </span>` : Lit.nothing;
|
|
392
|
-
const actionTitle = step.title ?? `${lockedString(UIStringsNotTranslate.investigating)}…`;
|
|
393
396
|
|
|
394
|
-
return html`<span class="title">${paused}${
|
|
397
|
+
return html`<span class="title">${paused}${titleForStep(step)}</span>`;
|
|
395
398
|
}
|
|
396
399
|
|
|
397
400
|
function renderStepCode(step: Step): Lit.LitTemplate {
|
|
@@ -470,6 +473,41 @@ function renderStepDetails({
|
|
|
470
473
|
// clang-format on
|
|
471
474
|
}
|
|
472
475
|
|
|
476
|
+
function renderWalkthroughSidebarButton(
|
|
477
|
+
input: ChatMessageViewInput,
|
|
478
|
+
lastStep: Step,
|
|
479
|
+
): Lit.LitTemplate {
|
|
480
|
+
const {message, walkthrough} = input;
|
|
481
|
+
if (walkthrough.isInlined) {
|
|
482
|
+
return Lit.nothing;
|
|
483
|
+
}
|
|
484
|
+
const title = input.isLoading ? titleForStep(lastStep) : lockedString(UIStringsNotTranslate.showThinking);
|
|
485
|
+
|
|
486
|
+
// clang-format off
|
|
487
|
+
return html`
|
|
488
|
+
<div class="walkthrough-toggle-container">
|
|
489
|
+
${input.isLoading ? html`<devtools-spinner></devtools-spinner>` : Lit.nothing}
|
|
490
|
+
<devtools-button
|
|
491
|
+
.variant=${Buttons.Button.Variant.OUTLINED}
|
|
492
|
+
.size=${Buttons.Button.Size.SMALL}
|
|
493
|
+
.title=${lastStep.isLoading ? titleForStep(lastStep) : lockedString(UIStringsNotTranslate.showThinking)}
|
|
494
|
+
.jslogContext=${walkthrough.isExpanded ? 'ai-hide-walkthrough-sidebar' : 'ai-show-walkthrough-sidebar'}
|
|
495
|
+
data-show-walkthrough
|
|
496
|
+
@click=${() => {
|
|
497
|
+
if(walkthrough.isExpanded) {
|
|
498
|
+
walkthrough.onToggle(false);
|
|
499
|
+
} else {
|
|
500
|
+
// Can't just toggle the visibility here; we need to ensure we
|
|
501
|
+
// update the state with this message as the user could have had
|
|
502
|
+
// the walkthrough open with an alternative message.
|
|
503
|
+
walkthrough.onOpen(message as ModelChatMessage);
|
|
504
|
+
}
|
|
505
|
+
}}
|
|
506
|
+
>${title}</devtools-button>
|
|
507
|
+
</div>
|
|
508
|
+
`;
|
|
509
|
+
// clang-format on
|
|
510
|
+
}
|
|
473
511
|
/**
|
|
474
512
|
* Responsible for rendering the AI Walkthrough UI. This can take different
|
|
475
513
|
* shapes and involve different parts depending on if the walkthrough is
|
|
@@ -477,36 +515,16 @@ function renderStepDetails({
|
|
|
477
515
|
* view them.
|
|
478
516
|
*/
|
|
479
517
|
function renderWalkthroughUI(input: ChatMessageViewInput, steps: Step[]): Lit.LitTemplate {
|
|
480
|
-
|
|
518
|
+
const lastStep = steps.at(-1);
|
|
519
|
+
if (!lastStep) {
|
|
520
|
+
// No steps = no walkthrough UI in the chat view.
|
|
481
521
|
return Lit.nothing;
|
|
482
522
|
}
|
|
483
|
-
|
|
484
523
|
const sideEffectSteps = steps.filter(s => s.sideEffect);
|
|
485
|
-
|
|
486
524
|
// If the walkthrough is in the sidebar, we render a button into the
|
|
487
525
|
// ChatView to open it.
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
<div class="walkthrough-toggle-container">
|
|
491
|
-
<devtools-button
|
|
492
|
-
.variant=${Buttons.Button.Variant.OUTLINED}
|
|
493
|
-
.size=${Buttons.Button.Size.SMALL}
|
|
494
|
-
.title=${lockedString(UIStringsNotTranslate.showThinking)}
|
|
495
|
-
.jslogContext=${'ai-show-walkthrough'}
|
|
496
|
-
@click=${() => {
|
|
497
|
-
if(input.walkthrough.isExpanded) {
|
|
498
|
-
input.walkthrough.onToggle(false);
|
|
499
|
-
} else {
|
|
500
|
-
// Can't just toggle the visibility here; we need to ensure we
|
|
501
|
-
// update the state with this message as the user could have had
|
|
502
|
-
// the walkthrough open with an alternative message.
|
|
503
|
-
input.walkthrough.onOpen(input.message as ModelChatMessage);
|
|
504
|
-
}
|
|
505
|
-
}}
|
|
506
|
-
>${lockedString(UIStringsNotTranslate.showThinking)}</devtools-button>
|
|
507
|
-
</div>
|
|
508
|
-
` : Lit.nothing;
|
|
509
|
-
// clang-format on
|
|
526
|
+
const openWalkThroughSidebarButton =
|
|
527
|
+
!input.walkthrough.isInlined ? renderWalkthroughSidebarButton(input, lastStep) : Lit.nothing;
|
|
510
528
|
|
|
511
529
|
// If there are side-effect steps, and the walkthrough is not open, we render
|
|
512
530
|
// those inline so that the user can see them and approve them.
|
|
@@ -127,10 +127,8 @@ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
|
127
127
|
const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
|
|
128
128
|
|
|
129
129
|
const {html, render} = Lit;
|
|
130
|
+
const {ifExpanded} = UI.TreeOutline;
|
|
130
131
|
|
|
131
|
-
interface ViewOutput {
|
|
132
|
-
userExpandedCategories: Set<SDK.CategorizedBreakpoint.Category>;
|
|
133
|
-
}
|
|
134
132
|
interface ViewInput {
|
|
135
133
|
onFilterChanged: (filterText: string|null) => void;
|
|
136
134
|
onBreakpointChange: (breakpoint: SDK.CategorizedBreakpoint.CategorizedBreakpoint, enabled: boolean) => void;
|
|
@@ -139,14 +137,14 @@ interface ViewInput {
|
|
|
139
137
|
onSpaceKeyDown: () => void;
|
|
140
138
|
|
|
141
139
|
filterText: string|null;
|
|
142
|
-
|
|
140
|
+
onExpandCollapse: () => void;
|
|
143
141
|
highlightedItem: SDK.CategorizedBreakpoint.CategorizedBreakpoint|null;
|
|
144
142
|
categories: Map<SDK.CategorizedBreakpoint.Category, SDK.CategorizedBreakpoint.CategorizedBreakpoint[]>;
|
|
145
143
|
sortedCategoryNames: SDK.CategorizedBreakpoint.Category[];
|
|
146
144
|
}
|
|
147
145
|
|
|
148
146
|
export type View = typeof DEFAULT_VIEW;
|
|
149
|
-
export const DEFAULT_VIEW = (input: ViewInput, output:
|
|
147
|
+
export const DEFAULT_VIEW = (input: ViewInput, output: undefined, target: HTMLElement): void => {
|
|
150
148
|
const shouldExpandCategory = (breakpoints: SDK.CategorizedBreakpoint.CategorizedBreakpoint[]): boolean =>
|
|
151
149
|
Boolean(input.filterText) || (input.highlightedItem && breakpoints.includes(input.highlightedItem)) ||
|
|
152
150
|
breakpoints.some(breakpoint => breakpoint.enabled());
|
|
@@ -193,23 +191,6 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
|
|
|
193
191
|
'source-code': true,
|
|
194
192
|
'breakpoint-hit': input.highlightedItem === breakpoint,
|
|
195
193
|
});
|
|
196
|
-
const onExpand =
|
|
197
|
-
(category: SDK.CategorizedBreakpoint.Category, {detail: {expanded}}: UI.TreeOutline.TreeViewElement.ExpandEvent):
|
|
198
|
-
void => {
|
|
199
|
-
const breakpoints = category && input.categories.get(category);
|
|
200
|
-
if (!breakpoints) {
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
if (shouldExpandCategory(breakpoints)) {
|
|
204
|
-
// Basically ignore expand/collapse when the category is expanded by default.
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
if (expanded) {
|
|
208
|
-
output.userExpandedCategories.add(category);
|
|
209
|
-
} else {
|
|
210
|
-
output.userExpandedCategories.delete(category);
|
|
211
|
-
}
|
|
212
|
-
};
|
|
213
194
|
|
|
214
195
|
const onKeyDown = (e: KeyboardEvent): void => {
|
|
215
196
|
if (e.key === ' ') {
|
|
@@ -232,13 +213,13 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
|
|
|
232
213
|
<ul role="tree">
|
|
233
214
|
${filteredCategories.map(([category, breakpoints]) => html`
|
|
234
215
|
<li @select=${() => input.onItemSelected(category)}
|
|
235
|
-
@expand=${(
|
|
216
|
+
@expand=${() => input.onExpandCollapse()}
|
|
236
217
|
role="treeitem"
|
|
237
218
|
jslog-context=${category}
|
|
238
219
|
aria-checked=${breakpoints.some(breakpoint => breakpoint.enabled())
|
|
239
220
|
? breakpoints.some(breakpoint => !breakpoint.enabled()) ? 'mixed' : true
|
|
240
221
|
: false}
|
|
241
|
-
?open=${shouldExpandCategory(breakpoints)
|
|
222
|
+
?open=${shouldExpandCategory(breakpoints)}>
|
|
242
223
|
<style>${categorizedBreakpointsSidebarPaneStyles}</style>
|
|
243
224
|
<devtools-checkbox
|
|
244
225
|
class="small"
|
|
@@ -250,22 +231,22 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
|
|
|
250
231
|
@change=${(e: Event) => onCheckboxClicked(e, category)}
|
|
251
232
|
>${getLocalizedCategory(category)}</devtools-checkbox>
|
|
252
233
|
<ul role="group">
|
|
253
|
-
${breakpoints.map(breakpoint => html`
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
234
|
+
${ifExpanded(html`${breakpoints.map(breakpoint => html`
|
|
235
|
+
<li @select=${() => input.onItemSelected(breakpoint)}
|
|
236
|
+
role="treeitem"
|
|
237
|
+
aria-checked=${breakpoint.enabled()}
|
|
238
|
+
jslog-context=${Platform.StringUtilities.toKebabCase(breakpoint.name)}>
|
|
239
|
+
<div ?hidden=${breakpoint !== input.highlightedItem} class="breakpoint-hit-marker"></div>
|
|
240
|
+
<devtools-checkbox
|
|
241
|
+
class=${classes(breakpoint)}
|
|
242
|
+
tabIndex=-1
|
|
243
|
+
title=${Sources.CategorizedBreakpointL10n.getLocalizedBreakpointName(breakpoint.name)}
|
|
244
|
+
?checked=${breakpoint.enabled()}
|
|
245
|
+
aria-description=${breakpoint === input.highlightedItem ? i18nString(UIStrings.breakpointHit)
|
|
246
|
+
: Lit.nothing}
|
|
247
|
+
@change=${(e: Event) => onCheckboxClicked(e, breakpoint)}
|
|
248
|
+
>${Sources.CategorizedBreakpointL10n.getLocalizedBreakpointName(breakpoint.name)}</devtools-checkbox>
|
|
249
|
+
</li>`)}`)}
|
|
269
250
|
</ul>
|
|
270
251
|
</li>`)}
|
|
271
252
|
</ul>`}>
|
|
@@ -282,7 +263,6 @@ export abstract class CategorizedBreakpointsSidebarPane extends UI.Widget.VBox {
|
|
|
282
263
|
#highlightedItem: SDK.CategorizedBreakpoint.CategorizedBreakpoint|null = null;
|
|
283
264
|
#filterText: string|null = null;
|
|
284
265
|
#view: View;
|
|
285
|
-
#userExpandedCategories = new Set<SDK.CategorizedBreakpoint.Category>();
|
|
286
266
|
#selectedItem: SDK.CategorizedBreakpoint.Category|SDK.CategorizedBreakpoint.CategorizedBreakpoint|null = null;
|
|
287
267
|
constructor(
|
|
288
268
|
breakpoints: SDK.CategorizedBreakpoint.CategorizedBreakpoint[], jslog: string, viewId: string,
|
|
@@ -375,12 +355,11 @@ export abstract class CategorizedBreakpointsSidebarPane extends UI.Widget.VBox {
|
|
|
375
355
|
sortedCategoryNames: this.#sortedCategories,
|
|
376
356
|
categories: this.categories,
|
|
377
357
|
highlightedItem: this.#highlightedItem,
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
userExpandedCategories: this.#userExpandedCategories,
|
|
358
|
+
onExpandCollapse: () => {
|
|
359
|
+
this.requestUpdate();
|
|
360
|
+
},
|
|
382
361
|
};
|
|
383
|
-
this.#view(input,
|
|
362
|
+
this.#view(input, undefined, this.contentElement);
|
|
384
363
|
}
|
|
385
364
|
}
|
|
386
365
|
|
|
@@ -299,7 +299,7 @@ export class ComputedStyleWidget extends UI.Widget.VBox {
|
|
|
299
299
|
#matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles|null = null;
|
|
300
300
|
private readonly showInheritedComputedStylePropertiesSetting: Common.Settings.Setting<boolean>;
|
|
301
301
|
private readonly groupComputedStylesSetting: Common.Settings.Setting<boolean>;
|
|
302
|
-
private filterRegex: RegExp|null;
|
|
302
|
+
private filterRegex: RegExp|null = null;
|
|
303
303
|
private readonly linkifier: Components.Linkifier.Linkifier;
|
|
304
304
|
private readonly imagePreviewPopover: ImagePreviewPopover;
|
|
305
305
|
/**
|
|
@@ -324,8 +324,15 @@ export class ComputedStyleWidget extends UI.Widget.VBox {
|
|
|
324
324
|
#computedStylesTree = new TreeOutline.TreeOutline.TreeOutline<ComputedStyleData>();
|
|
325
325
|
#treeData?: TreeOutline.TreeOutline.TreeOutlineData<ComputedStyleData>;
|
|
326
326
|
readonly #view: View;
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* TODO(b/407751272): the state here is confusing (3 instance variables relating to filtering).
|
|
330
|
+
* There is also a bug where the Toolbar Input's regex flag cannot be
|
|
331
|
+
* controlled, so if you set a regex filter here, the toolbar might not
|
|
332
|
+
* reflect it.
|
|
333
|
+
*/
|
|
327
334
|
#filterText = '';
|
|
328
|
-
#
|
|
335
|
+
#filterIsRegex = false;
|
|
329
336
|
#includeToolbar = true;
|
|
330
337
|
|
|
331
338
|
constructor() {
|
|
@@ -361,12 +368,31 @@ export class ComputedStyleWidget extends UI.Widget.VBox {
|
|
|
361
368
|
this.#computedStylesTree.classList.toggle('computed-narrow', isNarrow);
|
|
362
369
|
}
|
|
363
370
|
|
|
371
|
+
get filterText(): string {
|
|
372
|
+
return this.#filterText;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
get filterIsRegex(): boolean {
|
|
376
|
+
return this.#filterIsRegex;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
set filterText(newFilter: RegExp|string) {
|
|
380
|
+
if (typeof newFilter === 'string') {
|
|
381
|
+
this.#filterText = newFilter;
|
|
382
|
+
this.#filterIsRegex = false;
|
|
383
|
+
} else {
|
|
384
|
+
this.#filterText = newFilter.source;
|
|
385
|
+
this.#filterIsRegex = true;
|
|
386
|
+
}
|
|
387
|
+
this.requestUpdate();
|
|
388
|
+
}
|
|
389
|
+
|
|
364
390
|
get includeToolbar(): boolean {
|
|
365
391
|
return this.#includeToolbar;
|
|
366
392
|
}
|
|
367
393
|
|
|
368
|
-
set includeToolbar(
|
|
369
|
-
this.#includeToolbar =
|
|
394
|
+
set includeToolbar(inc: boolean) {
|
|
395
|
+
this.#includeToolbar = inc;
|
|
370
396
|
this.requestUpdate();
|
|
371
397
|
}
|
|
372
398
|
|
|
@@ -668,7 +694,7 @@ export class ComputedStyleWidget extends UI.Widget.VBox {
|
|
|
668
694
|
if (!text) {
|
|
669
695
|
return null;
|
|
670
696
|
}
|
|
671
|
-
if (this.#
|
|
697
|
+
if (this.#filterIsRegex) {
|
|
672
698
|
try {
|
|
673
699
|
return new RegExp(text, 'i');
|
|
674
700
|
} catch {
|
|
@@ -679,7 +705,7 @@ export class ComputedStyleWidget extends UI.Widget.VBox {
|
|
|
679
705
|
}
|
|
680
706
|
|
|
681
707
|
private async onRegexToggled(): Promise<void> {
|
|
682
|
-
this.#
|
|
708
|
+
this.#filterIsRegex = !this.#filterIsRegex;
|
|
683
709
|
await this.filterComputedStyles(this.#buildFilterRegex(this.#filterText));
|
|
684
710
|
}
|
|
685
711
|
|
|
@@ -701,11 +727,6 @@ export class ComputedStyleWidget extends UI.Widget.VBox {
|
|
|
701
727
|
return this.filterAlphabeticalList();
|
|
702
728
|
}
|
|
703
729
|
|
|
704
|
-
setFilterInput(text: string): void {
|
|
705
|
-
this.#filterText = text;
|
|
706
|
-
this.requestUpdate();
|
|
707
|
-
}
|
|
708
|
-
|
|
709
730
|
private nodeFilter(node: TreeOutline.TreeOutlineUtils.TreeNode<ComputedStyleData>): boolean {
|
|
710
731
|
const regex = this.filterRegex;
|
|
711
732
|
const data = node.treeNodeData;
|
|
@@ -2896,7 +2896,7 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
2896
2896
|
const regex = new RegExp(propertyNamePattern, 'i');
|
|
2897
2897
|
await computedStyleWidget.filterComputedStyles(regex);
|
|
2898
2898
|
|
|
2899
|
-
computedStyleWidget.
|
|
2899
|
+
computedStyleWidget.filterText = this.property.name;
|
|
2900
2900
|
}
|
|
2901
2901
|
|
|
2902
2902
|
private copyCssDeclarationAsJs(): void {
|
|
@@ -52,6 +52,7 @@ import {ShowMoreDetailsWidget} from './ShowMoreDetailsWidget.js';
|
|
|
52
52
|
|
|
53
53
|
const {classMap} = Directives;
|
|
54
54
|
const {widgetConfig} = UI.Widget;
|
|
55
|
+
const {ifExpanded} = UI.TreeOutline;
|
|
55
56
|
const UIStrings = {
|
|
56
57
|
/**
|
|
57
58
|
* @description A context menu item Payload View of the Network panel to copy a parsed value.
|
|
@@ -239,8 +240,8 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
239
240
|
${input.decodeRequestParameters ? i18nString(UIStrings.viewUrlEncoded) : i18nString(UIStrings.viewDecoded)}
|
|
240
241
|
</devtools-button>
|
|
241
242
|
<ul role=group>
|
|
242
|
-
${input.viewQueryParamSource ? createSourceText(input.queryString ?? '')
|
|
243
|
-
|
|
243
|
+
${ifExpanded(input.viewQueryParamSource ? createSourceText(input.queryString ?? '')
|
|
244
|
+
: createParsedParams(input.queryParameters ?? []))}
|
|
244
245
|
</ul>
|
|
245
246
|
</li>
|
|
246
247
|
<li
|
|
@@ -263,8 +264,8 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
263
264
|
${input.decodeRequestParameters ? i18nString(UIStrings.viewUrlEncoded) : i18nString(UIStrings.viewDecoded)}
|
|
264
265
|
</devtools-button>
|
|
265
266
|
<ul role=group>
|
|
266
|
-
${input.viewFormParamSource ? createSourceText(input.formData ?? '')
|
|
267
|
-
|
|
267
|
+
${ifExpanded(input.viewFormParamSource ? createSourceText(input.formData ?? '')
|
|
268
|
+
: createParsedParams(input.formParameters ?? []))}
|
|
268
269
|
</ul>
|
|
269
270
|
</li>
|
|
270
271
|
<li
|
|
@@ -280,8 +281,8 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
280
281
|
<div class="selection fill"></div>${i18nString(UIStrings.requestPayload)}${
|
|
281
282
|
createViewSourceToggle(input.viewJSONPayloadSource, input.setViewJSONPayloadSource)}
|
|
282
283
|
<ul role=group>
|
|
283
|
-
${!parsedFormData || input.viewJSONPayloadSource ? createSourceText(input.formData ?? '')
|
|
284
|
-
|
|
284
|
+
${ifExpanded(!parsedFormData || input.viewJSONPayloadSource ? createSourceText(input.formData ?? '')
|
|
285
|
+
: createPayload(parsedFormData))}
|
|
285
286
|
</ul>
|
|
286
287
|
</li>
|
|
287
288
|
</ul>
|
|
@@ -31,6 +31,7 @@ const UIStrings = {
|
|
|
31
31
|
} as const;
|
|
32
32
|
const str_ = i18n.i18n.registerUIStrings('panels/search/SearchResultsPane.ts', UIStrings);
|
|
33
33
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
34
|
+
const {ifExpanded} = UI.TreeOutline;
|
|
34
35
|
|
|
35
36
|
interface SearchMatch {
|
|
36
37
|
lineContent: string;
|
|
@@ -74,7 +75,7 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
|
|
|
74
75
|
<style>${searchResultsPaneStyles}</style>
|
|
75
76
|
${renderSearchResult(searchResult)}
|
|
76
77
|
<ul role="group">
|
|
77
|
-
${renderSearchMatches(searchResult, matches, onSelectMatch, onShowMoreMatches)}
|
|
78
|
+
${ifExpanded(renderSearchMatches(searchResult, matches, onSelectMatch, onShowMoreMatches))}
|
|
78
79
|
</ul>
|
|
79
80
|
</li>`)}
|
|
80
81
|
</ul>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Name: Dependencies sourced from the upstream `chromium` repository
|
|
2
2
|
URL: https://chromium.googlesource.com/chromium/src
|
|
3
3
|
Version: N/A
|
|
4
|
-
Revision:
|
|
4
|
+
Revision: 68abf80f61173a7def1da1fad60f72a52ea0531e
|
|
5
5
|
Update Mechanism: Manual (https://crbug.com/428069060)
|
|
6
6
|
License: BSD-3-Clause
|
|
7
7
|
License File: LICENSE
|
|
@@ -1927,6 +1927,45 @@ export namespace TreeViewElement {
|
|
|
1927
1927
|
}
|
|
1928
1928
|
}
|
|
1929
1929
|
|
|
1930
|
+
export const ifExpanded = Lit.Directive.directive(class extends Lit.Directive.Directive {
|
|
1931
|
+
#partInfo: {type: Lit.Directive.PartType, startNode: Node};
|
|
1932
|
+
constructor(partInfo: Lit.Directive.PartInfo) {
|
|
1933
|
+
if (partInfo.type !== Lit.Directive.PartType.CHILD) {
|
|
1934
|
+
throw new Error('ifExpanded directive must be used in a child node');
|
|
1935
|
+
}
|
|
1936
|
+
super(partInfo);
|
|
1937
|
+
this.#partInfo = partInfo as {type: Lit.Directive.PartType, startNode: Node};
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
render(content: Lit.LitTemplate|Iterable<Lit.LitTemplate>): Lit.LitTemplate|Iterable<Lit.LitTemplate> {
|
|
1941
|
+
return this.#isInExpandedRow(this.#partInfo.startNode) ? content : Lit.nothing;
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
#isInExpandedRow(element: Node|null|undefined): boolean {
|
|
1945
|
+
if (!element) {
|
|
1946
|
+
return false;
|
|
1947
|
+
}
|
|
1948
|
+
if (!(element instanceof HTMLElement)) {
|
|
1949
|
+
element = element.parentNode;
|
|
1950
|
+
}
|
|
1951
|
+
if (!(element instanceof HTMLElement)) {
|
|
1952
|
+
return false;
|
|
1953
|
+
}
|
|
1954
|
+
element = element.closest('li[role="treeitem"]') ?? undefined;
|
|
1955
|
+
if (!(element instanceof HTMLLIElement)) {
|
|
1956
|
+
return false;
|
|
1957
|
+
}
|
|
1958
|
+
if (hasBooleanAttribute(element, 'open')) {
|
|
1959
|
+
return true;
|
|
1960
|
+
}
|
|
1961
|
+
const node = TreeViewTreeElement.get(element);
|
|
1962
|
+
if (!node) {
|
|
1963
|
+
return false;
|
|
1964
|
+
}
|
|
1965
|
+
return node.expanded;
|
|
1966
|
+
}
|
|
1967
|
+
});
|
|
1968
|
+
|
|
1930
1969
|
export class TreeElementWrapper extends HTMLElement {
|
|
1931
1970
|
#treeElement?: TreeElement;
|
|
1932
1971
|
set treeElement(treeElement: TreeElement) {
|
|
@@ -19,9 +19,12 @@ const UIStrings = {
|
|
|
19
19
|
*/
|
|
20
20
|
find: 'Find',
|
|
21
21
|
} as const;
|
|
22
|
+
|
|
22
23
|
const str_ = i18n.i18n.registerUIStrings('ui/legacy/components/source_frame/XMLView.ts', UIStrings);
|
|
23
24
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
24
25
|
const {render, html} = Lit;
|
|
26
|
+
const {ifExpanded} = UI.TreeOutline;
|
|
27
|
+
|
|
25
28
|
function* attributes(element: Element): Generator<Attr> {
|
|
26
29
|
for (let i = 0; i < element.attributes.length; ++i) {
|
|
27
30
|
const attributeNode = element.attributes.item(i);
|
|
@@ -135,23 +138,35 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
135
138
|
return {highlights, selected};
|
|
136
139
|
}
|
|
137
140
|
|
|
138
|
-
function layOutNode(node: XMLTreeViewNode
|
|
141
|
+
function layOutNode(node: XMLTreeViewNode): Lit.LitTemplate {
|
|
139
142
|
const onExpand = (event: UI.TreeOutline.TreeViewElement.ExpandEvent): void =>
|
|
140
143
|
input.onExpand(node, event.detail.expanded);
|
|
141
144
|
const {highlights, selected} = highlight(node, /* closeTag=*/ false);
|
|
142
145
|
|
|
146
|
+
const containsSearchResult = (node: XMLTreeViewNode): boolean => {
|
|
147
|
+
if (node === input.jumpToNextSearchResult?.node) {
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
for (const child of node.children()) {
|
|
151
|
+
if (containsSearchResult(child)) {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return false;
|
|
156
|
+
};
|
|
157
|
+
|
|
143
158
|
// clang-format off
|
|
144
159
|
return html`
|
|
145
160
|
<li role="treeitem"
|
|
146
161
|
?selected=${input.jumpToNextSearchResult?.node === node}
|
|
147
162
|
@expand=${onExpand}
|
|
148
|
-
?open=${node
|
|
163
|
+
?open=${containsSearchResult(node)}>
|
|
149
164
|
<devtools-highlight ranges=${highlights} current-range=${selected}>
|
|
150
165
|
${htmlView(node)}
|
|
151
166
|
</devtools-highlight>
|
|
152
167
|
${node.children().length ? html`
|
|
153
168
|
<ul role="group">
|
|
154
|
-
${
|
|
169
|
+
${ifExpanded(subtree(node))}
|
|
155
170
|
</ul>` : Lit.nothing}
|
|
156
171
|
</li>`;
|
|
157
172
|
// clang-format on
|
|
@@ -165,7 +180,7 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
165
180
|
const {highlights, selected} = highlight(treeNode, /* closeTag=*/ true);
|
|
166
181
|
// clang-format off
|
|
167
182
|
return html`
|
|
168
|
-
${children.map(child => layOutNode(child
|
|
183
|
+
${children.map(child => layOutNode(child))}
|
|
169
184
|
${treeNode.node instanceof Element ? html`
|
|
170
185
|
<li role="treeitem">
|
|
171
186
|
<devtools-highlight ranges=${highlights} current-range=${selected}>
|
|
@@ -184,7 +199,7 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
184
199
|
class="shadow-xml-view source-code"
|
|
185
200
|
.template=${html`
|
|
186
201
|
<ul role="tree">
|
|
187
|
-
${input.xml.children().map(node => layOutNode(node
|
|
202
|
+
${input.xml.children().map(node => layOutNode(node))}
|
|
188
203
|
</ul>`}
|
|
189
204
|
></devtools-tree>`,
|
|
190
205
|
// clang-format on
|
|
@@ -821,50 +821,67 @@ ${JSON.stringify(allLogs, null, 2)}
|
|
|
821
821
|
|
|
822
822
|
let numMatchedEvents = 0;
|
|
823
823
|
|
|
824
|
+
function recordUnmatchedEvent(
|
|
825
|
+
pendingExpectation: PendingEventExpectation, actualEvent: TestLogEntry, expectedEvent: TestLogEntry,
|
|
826
|
+
matchedImpressions: Set<string>): void {
|
|
827
|
+
const unmatched = {...actualEvent};
|
|
828
|
+
if ('impressions' in unmatched && 'impressions' in expectedEvent) {
|
|
829
|
+
unmatched.impressions = unmatched.impressions.filter(impression => {
|
|
830
|
+
const matched = expectedEvent.impressions.includes(impression);
|
|
831
|
+
if (matched) {
|
|
832
|
+
matchedImpressions.add(impression);
|
|
833
|
+
}
|
|
834
|
+
return !matched;
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
pendingExpectation.unmatchedEvents.push(unmatched);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
function processMissingEvents(
|
|
841
|
+
pendingExpectation: PendingEventExpectation, expectedEventIndex: number, matchedImpressions: Set<string>): void {
|
|
842
|
+
pendingExpectation.missingEvents = pendingExpectation.expectedEvents.slice(expectedEventIndex);
|
|
843
|
+
for (const event of pendingExpectation.missingEvents) {
|
|
844
|
+
if ('impressions' in event) {
|
|
845
|
+
event.impressions = event.impressions.filter(impression => !matchedImpressions.has(impression));
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
pendingExpectation.missingEvents =
|
|
849
|
+
pendingExpectation.missingEvents.filter(event => !('impressions' in event) || event.impressions.length > 0);
|
|
850
|
+
}
|
|
851
|
+
|
|
824
852
|
function checkPendingEventExpectation(): void {
|
|
825
853
|
if (!pendingEventExpectation) {
|
|
826
854
|
return;
|
|
827
855
|
}
|
|
828
|
-
const actualEvents =
|
|
829
|
-
let
|
|
856
|
+
const actualEvents = veDebugEventsLog as TestLogEntry[];
|
|
857
|
+
let actualEventIndex = 0;
|
|
858
|
+
let matchStarted = false;
|
|
830
859
|
const matchedImpressions = new Set<string>();
|
|
831
860
|
pendingEventExpectation.unmatchedEvents = [];
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
pendingEventExpectation.missingEvents = pendingEventExpectation.missingEvents.filter(
|
|
843
|
-
event => !('impressions' in event) || event.impressions.length > 0);
|
|
844
|
-
return;
|
|
845
|
-
}
|
|
846
|
-
if (!compareVeEvents(actualEvents[i], expectedEvent)) {
|
|
847
|
-
if (partialMatch) {
|
|
848
|
-
const unmatched = {...actualEvents[i]};
|
|
849
|
-
if ('impressions' in unmatched && 'impressions' in expectedEvent) {
|
|
850
|
-
unmatched.impressions = unmatched.impressions.filter(impression => {
|
|
851
|
-
const matched = expectedEvent.impressions.includes(impression);
|
|
852
|
-
if (matched) {
|
|
853
|
-
matchedImpressions.add(impression);
|
|
854
|
-
}
|
|
855
|
-
return !matched;
|
|
856
|
-
});
|
|
857
|
-
}
|
|
858
|
-
pendingEventExpectation.unmatchedEvents.push(unmatched);
|
|
859
|
-
}
|
|
860
|
-
actualEvents.splice(i, 1);
|
|
861
|
-
} else {
|
|
862
|
-
partialMatch = true;
|
|
861
|
+
|
|
862
|
+
for (let expectedEventIndex = 0; expectedEventIndex < pendingEventExpectation.expectedEvents.length;
|
|
863
|
+
++expectedEventIndex) {
|
|
864
|
+
const expectedEvent = pendingEventExpectation.expectedEvents[expectedEventIndex];
|
|
865
|
+
let found = false;
|
|
866
|
+
while (actualEventIndex < actualEvents.length) {
|
|
867
|
+
if (compareVeEvents(actualEvents[actualEventIndex], expectedEvent)) {
|
|
868
|
+
found = true;
|
|
869
|
+
matchStarted = true;
|
|
870
|
+
actualEventIndex++;
|
|
863
871
|
break;
|
|
864
872
|
}
|
|
873
|
+
if (matchStarted) {
|
|
874
|
+
recordUnmatchedEvent(
|
|
875
|
+
pendingEventExpectation, actualEvents[actualEventIndex], expectedEvent, matchedImpressions);
|
|
876
|
+
}
|
|
877
|
+
actualEventIndex++;
|
|
878
|
+
}
|
|
879
|
+
if (!found) {
|
|
880
|
+
processMissingEvents(pendingEventExpectation, expectedEventIndex, matchedImpressions);
|
|
881
|
+
return;
|
|
865
882
|
}
|
|
866
883
|
}
|
|
867
|
-
numMatchedEvents =
|
|
884
|
+
numMatchedEvents = actualEventIndex;
|
|
868
885
|
pendingEventExpectation.success();
|
|
869
886
|
}
|
|
870
887
|
|
|
@@ -360,7 +360,8 @@ export const knownContextValues = new Set([
|
|
|
360
360
|
'ai-code-generation-upgrade-dialog.manage-in-settings',
|
|
361
361
|
'ai-code-generation-used',
|
|
362
362
|
'ai-explorer',
|
|
363
|
-
'ai-
|
|
363
|
+
'ai-hide-walkthrough-sidebar',
|
|
364
|
+
'ai-show-walkthrough-sidebar',
|
|
364
365
|
'ai_assistance',
|
|
365
366
|
'align-content',
|
|
366
367
|
'align-content-center',
|
|
@@ -1377,6 +1378,7 @@ export const knownContextValues = new Set([
|
|
|
1377
1378
|
'drjones.sources-panel-context.performance',
|
|
1378
1379
|
'drjones.sources-panel-context.script',
|
|
1379
1380
|
'drop',
|
|
1381
|
+
'durable-messages',
|
|
1380
1382
|
'duration',
|
|
1381
1383
|
'durationchange',
|
|
1382
1384
|
'dynamic-local-setting',
|
|
@@ -2055,6 +2057,8 @@ export const knownContextValues = new Set([
|
|
|
2055
2057
|
'java-script-disabled-true',
|
|
2056
2058
|
'javascript',
|
|
2057
2059
|
'javascript-context',
|
|
2060
|
+
'jpeg-xl-format-disabled',
|
|
2061
|
+
'jpeg-xl-format-disabled-true',
|
|
2058
2062
|
'jpg-header',
|
|
2059
2063
|
'js-event-listeners',
|
|
2060
2064
|
'js-heap-total-size',
|
|
@@ -40,8 +40,11 @@ export const logResize = (loggable: Loggable, size: DOMRect): void => {
|
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
42
|
loggingState.size = size;
|
|
43
|
-
const resizeEvent: Host.InspectorFrontendHostAPI
|
|
44
|
-
|
|
43
|
+
const resizeEvent: Host.InspectorFrontendHostAPI.ResizeEvent = {
|
|
44
|
+
veid: loggingState.veid,
|
|
45
|
+
width: Math.round(loggingState.size.width),
|
|
46
|
+
height: Math.round(loggingState.size.height)
|
|
47
|
+
};
|
|
45
48
|
Host.InspectorFrontendHost.InspectorFrontendHostInstance.recordResize(resizeEvent);
|
|
46
49
|
processEventForDebugging('Resize', loggingState, {width: Math.round(size.width), height: Math.round(size.height)});
|
|
47
50
|
};
|
package/package.json
CHANGED