chrome-devtools-frontend 1.0.1642845 → 1.0.1642899
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/SECURITY.md +1 -0
- package/front_end/core/host/UserMetrics.ts +2 -1
- package/front_end/core/sdk/CSSMatchedStyles.ts +55 -26
- package/front_end/core/sdk/CSSRule.ts +1 -0
- package/front_end/core/sdk/DebuggerModel.ts +5 -0
- package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +4 -3
- package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +4 -3
- package/front_end/models/ai_assistance/AiAgent2.ts +80 -16
- package/front_end/models/ai_assistance/AiConversation.ts +3 -2
- package/front_end/models/ai_assistance/README.md +8 -0
- package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +50 -35
- package/front_end/models/ai_assistance/agents/AiAgent.ts +16 -0
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +2 -2
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +195 -147
- package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +0 -25
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +24 -305
- package/front_end/models/ai_assistance/ai_assistance.ts +8 -0
- package/front_end/models/ai_assistance/contexts/DOMNodeContext.snapshot.txt +51 -0
- package/front_end/models/ai_assistance/contexts/DOMNodeContext.ts +200 -0
- package/front_end/models/ai_assistance/skills/styling.md +36 -2
- package/front_end/models/ai_assistance/tools/GetStyles.ts +137 -0
- package/front_end/models/ai_assistance/tools/Tool.ts +55 -0
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +34 -0
- package/front_end/models/lighthouse/LighthouseReporterTypes.ts +5 -0
- package/front_end/models/live-metrics/LiveMetrics.ts +24 -13
- package/front_end/models/stack_trace/DetailedErrorStackParser.ts +2 -2
- package/front_end/models/stack_trace/StackTrace.ts +4 -1
- package/front_end/models/stack_trace/StackTraceImpl.ts +9 -2
- package/front_end/models/stack_trace/StackTraceModel.ts +17 -4
- package/front_end/models/stack_trace/Trie.ts +1 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +19 -15
- package/front_end/panels/ai_assistance/ai_assistance-meta.ts +16 -0
- package/front_end/panels/ai_assistance/components/ChatInput.ts +2 -2
- package/front_end/panels/application/DOMStorageItemsView.ts +4 -0
- package/front_end/panels/application/KeyValueStorageItemsView.ts +39 -7
- package/front_end/panels/common/ExtensionServer.ts +26 -15
- package/front_end/panels/elements/StandaloneStylesContainer.ts +1 -1
- package/front_end/panels/elements/StylePropertiesSection.ts +8 -0
- package/front_end/panels/elements/StylePropertyHighlighter.ts +4 -2
- package/front_end/panels/elements/StylePropertyTreeElement.ts +6 -5
- package/front_end/panels/elements/StylesContainer.ts +1 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +4 -4
- package/front_end/panels/layer_viewer/PaintProfilerView.ts +106 -132
- package/front_end/panels/lighthouse/LighthousePanel.ts +4 -3
- package/front_end/panels/network/NetworkLogView.ts +3 -0
- package/front_end/panels/network/networkLogView.css +0 -15
- package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +36 -3
- package/front_end/ui/legacy/components/data_grid/dataGridAiButton.css +20 -0
- package/front_end/ui/legacy/components/utils/Linkifier.ts +19 -4
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/package.json +1 -1
|
@@ -28,8 +28,13 @@
|
|
|
28
28
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
29
29
|
*/
|
|
30
30
|
/* eslint no-return-assign: "off" */
|
|
31
|
+
import '../../ui/components/buttons/buttons.js';
|
|
32
|
+
|
|
31
33
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
34
|
+
import * as AIAssistance from '../../models/ai_assistance/ai_assistance.js';
|
|
32
35
|
import * as Geometry from '../../models/geometry/geometry.js';
|
|
36
|
+
// eslint-disable-next-line @devtools/es-modules-import
|
|
37
|
+
import dataGridAiButtonStyles from '../../ui/legacy/components/data_grid/dataGridAiButton.css.js';
|
|
33
38
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
34
39
|
import {Directives as LitDirectives, html, nothing, render} from '../../ui/lit/lit.js';
|
|
35
40
|
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
|
|
@@ -37,11 +42,13 @@ import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
|
|
|
37
42
|
import * as ApplicationComponents from './components/components.js';
|
|
38
43
|
import {StorageItemsToolbar} from './StorageItemsToolbar.js';
|
|
39
44
|
|
|
45
|
+
const STORAGE_FLOATING_BUTTON_ACTION_ID = 'ai-assistance.storage-floating-button';
|
|
46
|
+
|
|
40
47
|
const {ARIAUtils} = UI;
|
|
41
48
|
const {EmptyWidget} = UI.EmptyWidget;
|
|
42
49
|
const {VBox, widget} = UI.Widget;
|
|
43
50
|
const {Size} = Geometry;
|
|
44
|
-
const {repeat} = LitDirectives;
|
|
51
|
+
const {repeat, ifDefined} = LitDirectives;
|
|
45
52
|
|
|
46
53
|
type Widget = UI.Widget.Widget;
|
|
47
54
|
type VBox = UI.Widget.VBox;
|
|
@@ -87,6 +94,9 @@ export interface ViewInput {
|
|
|
87
94
|
onDeleteAll: () => void;
|
|
88
95
|
jslog?: string;
|
|
89
96
|
classes?: string[];
|
|
97
|
+
aiButtonTitle?: string;
|
|
98
|
+
showAiButton?: boolean;
|
|
99
|
+
onAiButtonClick?: (item: {key: string, value: string}, event: Event) => void;
|
|
90
100
|
}
|
|
91
101
|
|
|
92
102
|
interface ViewOutput {
|
|
@@ -150,6 +160,7 @@ export abstract class KeyValueStorageItemsView extends UI.Widget.VBox {
|
|
|
150
160
|
@deselect=${() => input.onSelect(null)}
|
|
151
161
|
>
|
|
152
162
|
<table>
|
|
163
|
+
${input.showAiButton ? html`<style>${dataGridAiButtonStyles}</style>`: nothing}
|
|
153
164
|
<tr>
|
|
154
165
|
<th id="key" sortable ?editable=${input.editable}>
|
|
155
166
|
${i18nString(UIStrings.key)}
|
|
@@ -165,7 +176,15 @@ export abstract class KeyValueStorageItemsView extends UI.Widget.VBox {
|
|
|
165
176
|
input.onEdit(item.key, item.value, e.detail.columnId, e.detail.valueBeforeEditing, e.detail.newText)}
|
|
166
177
|
@delete=${() => input.onDelete(item.key)}
|
|
167
178
|
selected=${(input.selectedKey === item.key) || nothing}>
|
|
168
|
-
<td>${
|
|
179
|
+
<td>${input.showAiButton ? html`
|
|
180
|
+
<span class="ai-button-container">
|
|
181
|
+
<devtools-floating-button
|
|
182
|
+
icon-name=${AIAssistance.AiUtils.getIconName()}
|
|
183
|
+
title=${ifDefined(input.aiButtonTitle)}
|
|
184
|
+
@click=${(e: Event) => input.onAiButtonClick?.(item, e)}
|
|
185
|
+
></devtools-floating-button>
|
|
186
|
+
</span>
|
|
187
|
+
` : nothing}${item.key}</td>
|
|
169
188
|
<td>${item.value.substr(0, MAX_VALUE_LENGTH)}</td>
|
|
170
189
|
</tr>`)}
|
|
171
190
|
<tr placeholder></tr>
|
|
@@ -217,15 +236,24 @@ export abstract class KeyValueStorageItemsView extends UI.Widget.VBox {
|
|
|
217
236
|
preview: this.#preview,
|
|
218
237
|
jslog: this.#jslog,
|
|
219
238
|
classes: this.#classes,
|
|
239
|
+
showAiButton: this.isAiButtonEnabled(),
|
|
240
|
+
aiButtonTitle: this.isAiButtonEnabled() &&
|
|
241
|
+
UI.ActionRegistry.ActionRegistry.instance().hasAction(STORAGE_FLOATING_BUTTON_ACTION_ID) ?
|
|
242
|
+
UI.ActionRegistry.ActionRegistry.instance().getAction(STORAGE_FLOATING_BUTTON_ACTION_ID).title() :
|
|
243
|
+
undefined,
|
|
220
244
|
onSelect: (item: {key: string, value: string}|null) => {
|
|
221
245
|
this.#toolbar?.setCanDeleteSelected(Boolean(item));
|
|
222
|
-
|
|
223
|
-
void this.#previewEntry(null);
|
|
224
|
-
} else {
|
|
225
|
-
void this.#previewEntry(item);
|
|
226
|
-
}
|
|
246
|
+
void this.#previewEntry(item);
|
|
227
247
|
this.selectedItemChanged(item);
|
|
228
248
|
},
|
|
249
|
+
onAiButtonClick: (item: {key: string, value: string}, event: Event) => {
|
|
250
|
+
event.stopPropagation();
|
|
251
|
+
viewInput.onSelect(item);
|
|
252
|
+
const actionRegistry = UI.ActionRegistry.ActionRegistry.instance();
|
|
253
|
+
if (actionRegistry.hasAction(STORAGE_FLOATING_BUTTON_ACTION_ID)) {
|
|
254
|
+
void actionRegistry.getAction(STORAGE_FLOATING_BUTTON_ACTION_ID).execute();
|
|
255
|
+
}
|
|
256
|
+
},
|
|
229
257
|
|
|
230
258
|
onSort: (ascending: boolean) => {
|
|
231
259
|
this.#isSortOrderAscending = ascending;
|
|
@@ -252,6 +280,10 @@ export abstract class KeyValueStorageItemsView extends UI.Widget.VBox {
|
|
|
252
280
|
this.#view(viewInput, viewOutput, this.contentElement);
|
|
253
281
|
}
|
|
254
282
|
|
|
283
|
+
protected isAiButtonEnabled(): boolean {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
|
|
255
287
|
protected get toolbar(): StorageItemsToolbar|undefined {
|
|
256
288
|
return this.#toolbar;
|
|
257
289
|
}
|
|
@@ -27,7 +27,13 @@ import * as ThemeSupport from '../../ui/legacy/theme_support/theme_support.js';
|
|
|
27
27
|
import {ExtensionButton, ExtensionPanel, ExtensionSidebarPane} from './ExtensionPanel.js';
|
|
28
28
|
|
|
29
29
|
const extensionOrigins = new WeakMap<MessagePort, Platform.DevToolsPath.UrlString>();
|
|
30
|
-
const
|
|
30
|
+
const kForbiddenSchemes = [
|
|
31
|
+
'chrome:',
|
|
32
|
+
'chrome-untrusted:',
|
|
33
|
+
'chrome-error:',
|
|
34
|
+
'chrome-search:',
|
|
35
|
+
'devtools:',
|
|
36
|
+
];
|
|
31
37
|
|
|
32
38
|
declare global {
|
|
33
39
|
interface Window {
|
|
@@ -78,7 +84,6 @@ export class HostsPolicy {
|
|
|
78
84
|
}
|
|
79
85
|
|
|
80
86
|
class RegisteredExtension {
|
|
81
|
-
openResourceScheme: null|string = null;
|
|
82
87
|
constructor(readonly name: string, readonly hostsPolicy: HostsPolicy, readonly allowFileAccess: boolean) {
|
|
83
88
|
}
|
|
84
89
|
|
|
@@ -91,8 +96,11 @@ class RegisteredExtension {
|
|
|
91
96
|
return false;
|
|
92
97
|
}
|
|
93
98
|
|
|
94
|
-
|
|
95
|
-
|
|
99
|
+
let parsedURL;
|
|
100
|
+
try {
|
|
101
|
+
parsedURL = new URL(inspectedURL);
|
|
102
|
+
} catch {
|
|
103
|
+
return false;
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
if (!ExtensionServer.canInspectURL(inspectedURL)) {
|
|
@@ -104,12 +112,6 @@ class RegisteredExtension {
|
|
|
104
112
|
}
|
|
105
113
|
|
|
106
114
|
if (!this.allowFileAccess) {
|
|
107
|
-
let parsedURL;
|
|
108
|
-
try {
|
|
109
|
-
parsedURL = new URL(inspectedURL);
|
|
110
|
-
} catch {
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
115
|
return parsedURL.protocol !== 'file:';
|
|
114
116
|
}
|
|
115
117
|
|
|
@@ -863,18 +865,27 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
|
|
|
863
865
|
if (!extension) {
|
|
864
866
|
throw new Error('Received a message from an unregistered extension');
|
|
865
867
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
+
let validScheme = message.urlScheme;
|
|
869
|
+
if (validScheme) {
|
|
870
|
+
try {
|
|
871
|
+
const urlToParse = validScheme.replace(/:?(\/\/)?$/, '') + '://test';
|
|
872
|
+
validScheme = new URL(urlToParse).protocol;
|
|
873
|
+
} catch {
|
|
874
|
+
return this.status.E_BADARG('urlScheme', 'Invalid scheme');
|
|
875
|
+
}
|
|
876
|
+
if (kForbiddenSchemes.includes(validScheme) || validScheme === 'file:') {
|
|
877
|
+
return this.status.E_BADARG('urlScheme', 'Scheme is forbidden');
|
|
878
|
+
}
|
|
868
879
|
}
|
|
869
880
|
const extensionOrigin = this.getExtensionOrigin(port);
|
|
870
881
|
const {name} = extension;
|
|
871
882
|
const registration = {
|
|
872
883
|
title: name,
|
|
873
884
|
origin: extensionOrigin,
|
|
874
|
-
scheme:
|
|
885
|
+
scheme: validScheme,
|
|
875
886
|
handler: this.handleOpenURL.bind(this, port),
|
|
876
887
|
shouldHandleOpenResource: (url: Platform.DevToolsPath.UrlString, schemes: Set<string>) =>
|
|
877
|
-
Components.Linkifier.Linkifier.shouldHandleOpenResource(
|
|
888
|
+
Components.Linkifier.Linkifier.shouldHandleOpenResource(validScheme || null, url, schemes),
|
|
878
889
|
};
|
|
879
890
|
if (message.handlerPresent) {
|
|
880
891
|
Components.Linkifier.Linkifier.registerLinkHandler(registration);
|
|
@@ -1645,7 +1656,7 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
|
|
|
1645
1656
|
return false;
|
|
1646
1657
|
}
|
|
1647
1658
|
|
|
1648
|
-
if (
|
|
1659
|
+
if (kForbiddenSchemes.includes(parsedURL.protocol)) {
|
|
1649
1660
|
return false;
|
|
1650
1661
|
}
|
|
1651
1662
|
|
|
@@ -276,7 +276,7 @@ export class StandaloneStylesContainer extends Common.ObjectWrapper.eventMixin<E
|
|
|
276
276
|
return null;
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
-
jumpToFunctionDefinition(_functionName: string): void {
|
|
279
|
+
jumpToFunctionDefinition(_functionName: string, _treeScopeDistance: number): void {
|
|
280
280
|
}
|
|
281
281
|
|
|
282
282
|
continueEditingElement(_sectionIndex: number, _propertyIndex: number): void {
|
|
@@ -379,6 +379,14 @@ export class StylePropertiesSection {
|
|
|
379
379
|
return this.sectionIdx;
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
+
treeScopeDistance(): number {
|
|
383
|
+
const treeScope = this.styleInternal.parentRule?.treeScope;
|
|
384
|
+
if (!treeScope) {
|
|
385
|
+
return -1;
|
|
386
|
+
}
|
|
387
|
+
return SDK.CSSMatchedStyles.distanceToTreeScope(this.matchedStyles.node(), treeScope);
|
|
388
|
+
}
|
|
389
|
+
|
|
382
390
|
static createRuleOriginNode(
|
|
383
391
|
matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles, linkifier: Components.Linkifier.Linkifier,
|
|
384
392
|
rule: SDK.CSSRule.CSSRule|null): LitTemplate {
|
|
@@ -51,9 +51,11 @@ export class StylePropertyHighlighter {
|
|
|
51
51
|
PanelUtils.highlightElement(block.titleElement() as HTMLElement);
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
findAndHighlightSection(sectionName: string, blockName: string): void {
|
|
54
|
+
findAndHighlightSection(sectionName: string, blockName: string, treeScopeDistance = -1): void {
|
|
55
55
|
const block = this.styleSidebarPane.getSectionBlockByName(blockName);
|
|
56
|
-
const section = block?.sections.find(
|
|
56
|
+
const section = block?.sections.find(
|
|
57
|
+
section => section.headerText() === sectionName &&
|
|
58
|
+
(treeScopeDistance === -1 || section.treeScopeDistance() === treeScopeDistance));
|
|
57
59
|
if (!section || !block) {
|
|
58
60
|
return;
|
|
59
61
|
}
|
|
@@ -2661,10 +2661,11 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
2661
2661
|
return container;
|
|
2662
2662
|
}
|
|
2663
2663
|
|
|
2664
|
-
#getLinkableFunction(
|
|
2665
|
-
|
|
2664
|
+
#getLinkableFunction(
|
|
2665
|
+
functionName: string, matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles,
|
|
2666
|
+
property: SDK.CSSProperty.CSSProperty): InlineEditor.LinkSwatch.LinkSwatch {
|
|
2666
2667
|
const swatch = new InlineEditor.LinkSwatch.LinkSwatch();
|
|
2667
|
-
const registeredFunction = matchedStyles.getRegisteredFunction(functionName);
|
|
2668
|
+
const {registeredFunction, treeScopeDistance} = matchedStyles.getRegisteredFunction(functionName, property);
|
|
2668
2669
|
const isDefined = Boolean(registeredFunction);
|
|
2669
2670
|
swatch.data = {
|
|
2670
2671
|
jslogContext: 'css-function',
|
|
@@ -2675,7 +2676,7 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
2675
2676
|
if (!registeredFunction) {
|
|
2676
2677
|
return;
|
|
2677
2678
|
}
|
|
2678
|
-
this.#stylesContainer.jumpToFunctionDefinition(registeredFunction);
|
|
2679
|
+
this.#stylesContainer.jumpToFunctionDefinition(registeredFunction, treeScopeDistance);
|
|
2679
2680
|
},
|
|
2680
2681
|
};
|
|
2681
2682
|
return swatch;
|
|
@@ -2696,7 +2697,7 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
2696
2697
|
const tooltipId = this.getTooltipId(`${functionName}-trace`);
|
|
2697
2698
|
// clang-format off
|
|
2698
2699
|
return html`
|
|
2699
|
-
<span tabIndex=-1 class=tracing-anchor aria-details=${tooltipId}>${functionName.startsWith('--') ? this.#getLinkableFunction(functionName, matchedStyles) : functionName}</span>
|
|
2700
|
+
<span tabIndex=-1 class=tracing-anchor aria-details=${tooltipId}>${functionName.startsWith('--') ? this.#getLinkableFunction(functionName, matchedStyles, context.property) : functionName}</span>
|
|
2700
2701
|
<devtools-tooltip
|
|
2701
2702
|
id=${tooltipId}
|
|
2702
2703
|
use-hotkey
|
|
@@ -37,7 +37,7 @@ export interface StylesContainer {
|
|
|
37
37
|
computedValue: string|null): ElementsComponents.CSSVariableValueView.CSSVariableValueView;
|
|
38
38
|
getVariableParserError(matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles, variableName: string):
|
|
39
39
|
ElementsComponents.CSSVariableValueView.CSSVariableParserError|null;
|
|
40
|
-
jumpToFunctionDefinition(functionName: string): void;
|
|
40
|
+
jumpToFunctionDefinition(functionName: string, treeScopeDistance: number): void;
|
|
41
41
|
continueEditingElement(sectionIndex: number, propertyIndex: number): void;
|
|
42
42
|
revealProperty(cssProperty: SDK.CSSProperty.CSSProperty): void;
|
|
43
43
|
resetFocus(): void;
|
|
@@ -344,16 +344,16 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
|
344
344
|
}
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
-
jumpToSection(sectionName: string, blockName: string): void {
|
|
348
|
-
this.decorator.findAndHighlightSection(sectionName, blockName);
|
|
347
|
+
jumpToSection(sectionName: string, blockName: string, treeScopeDistance?: number): void {
|
|
348
|
+
this.decorator.findAndHighlightSection(sectionName, blockName, treeScopeDistance);
|
|
349
349
|
}
|
|
350
350
|
|
|
351
351
|
jumpToSectionBlock(section: string): void {
|
|
352
352
|
this.decorator.findAndHighlightSectionBlock(section);
|
|
353
353
|
}
|
|
354
354
|
|
|
355
|
-
jumpToFunctionDefinition(functionName: string): void {
|
|
356
|
-
this.jumpToSection(functionName, FUNCTION_SECTION_NAME);
|
|
355
|
+
jumpToFunctionDefinition(functionName: string, treeScopeDistance: number): void {
|
|
356
|
+
this.jumpToSection(functionName, FUNCTION_SECTION_NAME, treeScopeDistance);
|
|
357
357
|
}
|
|
358
358
|
|
|
359
359
|
jumpToFontPaletteDefinition(paletteName: string): void {
|
|
@@ -11,9 +11,13 @@ import type * as SDK from '../../core/sdk/sdk.js';
|
|
|
11
11
|
import type * as Protocol from '../../generated/protocol.js';
|
|
12
12
|
import * as PerfUI from '../../ui/legacy/components/perf_ui/perf_ui.js';
|
|
13
13
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
14
|
+
import * as Lit from '../../ui/lit/lit.js';
|
|
14
15
|
|
|
15
16
|
import paintProfilerStyles from './paintProfiler.css.js';
|
|
16
17
|
|
|
18
|
+
const {html, render, nothing} = Lit;
|
|
19
|
+
const {repeat} = Lit.Directives;
|
|
20
|
+
|
|
17
21
|
const UIStrings = {
|
|
18
22
|
/**
|
|
19
23
|
* @description Text to indicate the progress of a profile
|
|
@@ -397,161 +401,131 @@ export interface EventTypes {
|
|
|
397
401
|
[Events.WINDOW_CHANGED]: void;
|
|
398
402
|
}
|
|
399
403
|
|
|
400
|
-
export
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
private readonly treeItemCache: Map<SDK.PaintProfiler.PaintProfilerLogItem, LogTreeElement>;
|
|
404
|
-
private selectionWindow?: {left: number, right: number}|null;
|
|
405
|
-
constructor() {
|
|
406
|
-
super();
|
|
407
|
-
this.setMinimumSize(100, 25);
|
|
408
|
-
this.element.classList.add('overflow-auto');
|
|
409
|
-
|
|
410
|
-
this.treeOutline = new UI.TreeOutline.TreeOutlineInShadow();
|
|
411
|
-
UI.ARIAUtils.setLabel(this.treeOutline.contentElement, i18nString(UIStrings.commandLog));
|
|
412
|
-
this.element.appendChild(this.treeOutline.element);
|
|
413
|
-
this.setDefaultFocusedElement(this.treeOutline.contentElement);
|
|
414
|
-
|
|
415
|
-
this.log = [];
|
|
416
|
-
this.treeItemCache = new Map();
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
setCommandLog(log: SDK.PaintProfiler.PaintProfilerLogItem[]): void {
|
|
420
|
-
this.log = log;
|
|
404
|
+
export interface CommandLogViewInput {
|
|
405
|
+
visibleLogItems: SDK.PaintProfiler.PaintProfilerLogItem[];
|
|
406
|
+
}
|
|
421
407
|
|
|
422
|
-
|
|
408
|
+
function paramToString(param: SDK.PaintProfiler.RawPaintProfilerLogItemParamValue, name: string): string {
|
|
409
|
+
if (typeof param !== 'object') {
|
|
410
|
+
return typeof param === 'string' && param.length > 100 ? name : JSON.stringify(param);
|
|
423
411
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
}
|
|
431
|
-
|
|
412
|
+
let str = '';
|
|
413
|
+
let keyCount = 0;
|
|
414
|
+
for (const key in param) {
|
|
415
|
+
const paramKey = param[key];
|
|
416
|
+
if (++keyCount > 4 || typeof paramKey === 'object' || (typeof paramKey === 'string' && paramKey.length > 100)) {
|
|
417
|
+
return name;
|
|
418
|
+
}
|
|
419
|
+
if (str) {
|
|
420
|
+
str += ', ';
|
|
432
421
|
}
|
|
433
|
-
|
|
422
|
+
str += paramKey;
|
|
434
423
|
}
|
|
424
|
+
return str;
|
|
425
|
+
}
|
|
435
426
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
427
|
+
function paramsToString(params: SDK.PaintProfiler.RawPaintProfilerLogItemParams|null): string {
|
|
428
|
+
let str = '';
|
|
429
|
+
for (const key in params) {
|
|
430
|
+
if (str) {
|
|
431
|
+
str += ', ';
|
|
432
|
+
}
|
|
433
|
+
str += paramToString(params[key], key);
|
|
439
434
|
}
|
|
435
|
+
return str;
|
|
436
|
+
}
|
|
440
437
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
438
|
+
function renderProperty(name: string, value: SDK.PaintProfiler.RawPaintProfilerLogItemParamValue): Lit.LitTemplate {
|
|
439
|
+
const isObject = value !== null && typeof value === 'object';
|
|
440
|
+
|
|
441
|
+
// clang-format off
|
|
442
|
+
return html`
|
|
443
|
+
<li role="treeitem">
|
|
444
|
+
<span>${name}: </span>${
|
|
445
|
+
isObject ? html`
|
|
446
|
+
<ul role="group">
|
|
447
|
+
${Object.entries(value).map(([key, val]) => renderProperty(key, val))}
|
|
448
|
+
</ul>` : html`
|
|
449
|
+
<span>${JSON.stringify(value)}</span>`
|
|
451
450
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
const child = root.lastChild() as LogTreeElement;
|
|
456
|
-
if (!child || child.logItem.commandIndex < this.selectionWindow.right) {
|
|
457
|
-
break;
|
|
458
|
-
}
|
|
459
|
-
root.removeChildAtIndex(root.children().length - 1);
|
|
460
|
-
}
|
|
461
|
-
for (let i = this.selectionWindow.left, right = this.selectionWindow.right; i < right; ++i) {
|
|
462
|
-
this.appendLogItem(this.log[i]);
|
|
463
|
-
}
|
|
464
|
-
return Promise.resolve();
|
|
465
|
-
}
|
|
451
|
+
</li>
|
|
452
|
+
`;
|
|
453
|
+
// clang-format on
|
|
466
454
|
}
|
|
467
455
|
|
|
468
|
-
|
|
469
|
-
|
|
456
|
+
function renderLogItem(logItem: SDK.PaintProfiler.PaintProfilerLogItem): Lit.LitTemplate {
|
|
457
|
+
const hasParams = Boolean(logItem.params && Object.keys(logItem.params).length > 0);
|
|
458
|
+
const titleText = logItem.method + '(' + paramsToString(logItem.params) + ')';
|
|
459
|
+
|
|
460
|
+
// clang-format off
|
|
461
|
+
return html`
|
|
462
|
+
<li role="treeitem">
|
|
463
|
+
${titleText}
|
|
464
|
+
${hasParams ? html`
|
|
465
|
+
<ul role="group">
|
|
466
|
+
${Object.entries(logItem.params || {}).map(([key, val]) => renderProperty(key, val))}
|
|
467
|
+
</ul>` : nothing}
|
|
468
|
+
</li>
|
|
469
|
+
`;
|
|
470
|
+
// clang-format on
|
|
471
|
+
}
|
|
470
472
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
473
|
+
// clang-format off
|
|
474
|
+
export const COMMAND_LOG_DEFAULT_VIEW = (input: CommandLogViewInput, _output: undefined, target: HTMLElement): void => {
|
|
475
|
+
render(html`
|
|
476
|
+
<div class="overflow-auto flex-auto vbox">
|
|
477
|
+
<devtools-tree
|
|
478
|
+
autofocus
|
|
479
|
+
aria-label=${i18nString(UIStrings.commandLog)}
|
|
480
|
+
.template=${html`
|
|
481
|
+
<ul role="tree">
|
|
482
|
+
${repeat(input.visibleLogItems,
|
|
483
|
+
item => item.commandIndex,
|
|
484
|
+
item => renderLogItem(item))}
|
|
485
|
+
</ul>`}>
|
|
486
|
+
</devtools-tree>
|
|
487
|
+
</div>`,
|
|
488
|
+
target);
|
|
489
|
+
};
|
|
490
|
+
// clang-format on
|
|
491
|
+
|
|
492
|
+
type CommandLogView = typeof COMMAND_LOG_DEFAULT_VIEW;
|
|
475
493
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
}
|
|
494
|
+
export class PaintProfilerCommandLogView extends UI.Widget.VBox {
|
|
495
|
+
private log: SDK.PaintProfiler.PaintProfilerLogItem[];
|
|
496
|
+
private selectionWindow?: {left: number, right: number}|null;
|
|
497
|
+
readonly #view: CommandLogView;
|
|
479
498
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
}
|
|
499
|
+
constructor(element?: HTMLElement, view: CommandLogView = COMMAND_LOG_DEFAULT_VIEW) {
|
|
500
|
+
super(element);
|
|
501
|
+
this.#view = view;
|
|
502
|
+
this.setMinimumSize(100, 25);
|
|
485
503
|
|
|
486
|
-
|
|
487
|
-
if (typeof param !== 'object') {
|
|
488
|
-
return typeof param === 'string' && param.length > 100 ? name : JSON.stringify(param);
|
|
489
|
-
}
|
|
490
|
-
let str = '';
|
|
491
|
-
let keyCount = 0;
|
|
492
|
-
for (const key in param) {
|
|
493
|
-
const paramKey = param[key];
|
|
494
|
-
if (++keyCount > 4 || paramKey === 'object' || (paramKey === 'string' && paramKey.length > 100)) {
|
|
495
|
-
return name;
|
|
496
|
-
}
|
|
497
|
-
if (str) {
|
|
498
|
-
str += ', ';
|
|
499
|
-
}
|
|
500
|
-
str += paramKey;
|
|
501
|
-
}
|
|
502
|
-
return str;
|
|
504
|
+
this.log = [];
|
|
503
505
|
}
|
|
504
506
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
if (str) {
|
|
509
|
-
str += ', ';
|
|
510
|
-
}
|
|
511
|
-
str += this.paramToString(params[key], key);
|
|
512
|
-
}
|
|
513
|
-
return str;
|
|
507
|
+
override wasShown(): void {
|
|
508
|
+
super.wasShown();
|
|
509
|
+
this.requestUpdate();
|
|
514
510
|
}
|
|
515
511
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
this.title = title;
|
|
512
|
+
setCommandLog(log: SDK.PaintProfiler.PaintProfilerLogItem[]): void {
|
|
513
|
+
this.log = log;
|
|
514
|
+
this.updateWindow({left: 0, right: this.log.length});
|
|
520
515
|
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
export class LogPropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
524
|
-
private property: {name: string, value: SDK.PaintProfiler.RawPaintProfilerLogItemParamValue};
|
|
525
516
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
this.
|
|
517
|
+
updateWindow(selectionWindow: {left: number, right: number}|null): void {
|
|
518
|
+
this.selectionWindow = selectionWindow;
|
|
519
|
+
this.requestUpdate();
|
|
529
520
|
}
|
|
530
521
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
element.appendChild(treeElement);
|
|
536
|
-
if (value && typeof value === 'object') {
|
|
537
|
-
for (const property in value) {
|
|
538
|
-
LogPropertyTreeElement.appendLogPropertyItem(treeElement, property, value[property]);
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
}
|
|
522
|
+
override performUpdate(): Promise<void> {
|
|
523
|
+
const visibleLogItems = this.selectionWindow && this.log.length ?
|
|
524
|
+
this.log.slice(this.selectionWindow.left, this.selectionWindow.right) :
|
|
525
|
+
[];
|
|
542
526
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
const nameElement = title.createChild('span', 'name');
|
|
546
|
-
nameElement.textContent = this.property.name;
|
|
547
|
-
const separatorElement = title.createChild('span', 'separator');
|
|
548
|
-
separatorElement.textContent = ': ';
|
|
549
|
-
if (this.property.value === null || typeof this.property.value !== 'object') {
|
|
550
|
-
const valueElement = title.createChild('span', 'value');
|
|
551
|
-
valueElement.textContent = JSON.stringify(this.property.value);
|
|
552
|
-
valueElement.classList.add('cm-js-' + (this.property.value === null ? 'null' : typeof this.property.value));
|
|
553
|
-
}
|
|
554
|
-
this.title = title;
|
|
527
|
+
this.#view({visibleLogItems}, undefined, this.contentElement);
|
|
528
|
+
return Promise.resolve();
|
|
555
529
|
}
|
|
556
530
|
}
|
|
557
531
|
|
|
@@ -374,11 +374,12 @@ export class LighthousePanel extends UI.Panel.Panel {
|
|
|
374
374
|
}
|
|
375
375
|
|
|
376
376
|
private loadedFromFile(report: string): void {
|
|
377
|
-
const data = JSON.parse(report);
|
|
378
|
-
if (!data
|
|
377
|
+
const data = JSON.parse(report) as LighthouseModel.ReporterTypes.ReportJSON;
|
|
378
|
+
if (!data.lighthouseVersion) {
|
|
379
379
|
return;
|
|
380
380
|
}
|
|
381
|
-
|
|
381
|
+
data.isImported = true;
|
|
382
|
+
this.buildReportUI(data);
|
|
382
383
|
}
|
|
383
384
|
|
|
384
385
|
override elementsToRestoreScrollPositionsFor(): Element[] {
|
|
@@ -54,6 +54,8 @@ import * as Adorners from '../../ui/components/adorners/adorners.js';
|
|
|
54
54
|
import * as Buttons from '../../ui/components/buttons/buttons.js';
|
|
55
55
|
import * as RenderCoordinator from '../../ui/components/render_coordinator/render_coordinator.js';
|
|
56
56
|
import * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js';
|
|
57
|
+
// eslint-disable-next-line @devtools/es-modules-import
|
|
58
|
+
import dataGridAiButtonStyles from '../../ui/legacy/components/data_grid/dataGridAiButton.css.js';
|
|
57
59
|
import * as PerfUI from '../../ui/legacy/components/perf_ui/perf_ui.js';
|
|
58
60
|
import * as Components from '../../ui/legacy/components/utils/utils.js';
|
|
59
61
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
@@ -560,6 +562,7 @@ export class NetworkLogView extends Common.ObjectWrapper.eventMixin<EventTypes,
|
|
|
560
562
|
networkLogLargeRowsSetting: Common.Settings.Setting<boolean>) {
|
|
561
563
|
super();
|
|
562
564
|
this.registerRequiredCSS(networkLogViewStyles);
|
|
565
|
+
this.registerRequiredCSS(dataGridAiButtonStyles);
|
|
563
566
|
this.setMinimumSize(50, 64);
|
|
564
567
|
|
|
565
568
|
this.element.id = 'network-container';
|
|
@@ -268,21 +268,6 @@ td.time-column {
|
|
|
268
268
|
vertical-align: sub;
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
-
.data-grid-data-grid-node .ai-button-container {
|
|
272
|
-
display: none;
|
|
273
|
-
float: right;
|
|
274
|
-
|
|
275
|
-
devtools-floating-button {
|
|
276
|
-
position: absolute;
|
|
277
|
-
z-index: 999;
|
|
278
|
-
margin-left: -17px;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
.data-grid-data-grid-node:hover .ai-button-container {
|
|
283
|
-
display: inline-flex;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
271
|
.image-network-icon-preview {
|
|
287
272
|
inset: 0;
|
|
288
273
|
margin: auto;
|