chrome-devtools-frontend 1.0.1593959 → 1.0.1595090
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/contributing/settings-experiments-features.md +25 -5
- package/front_end/core/common/README.md +126 -0
- package/front_end/core/sdk/CSSModel.ts +22 -0
- package/front_end/core/sdk/CSSNavigation.ts +33 -0
- package/front_end/core/sdk/CSSRule.ts +12 -2
- package/front_end/core/sdk/sdk-meta.ts +22 -18
- package/front_end/core/sdk/sdk.ts +2 -0
- package/front_end/design_system_tokens.css +538 -259
- package/front_end/entrypoints/main/main-meta.ts +2 -2
- package/front_end/generated/SupportedCSSProperties.js +12 -0
- package/front_end/models/issues_manager/Issue.ts +1 -0
- package/front_end/models/issues_manager/IssueAggregator.ts +9 -0
- package/front_end/models/issues_manager/IssuesManager.ts +5 -0
- package/front_end/models/issues_manager/SelectivePermissionsInterventionIssue.ts +65 -0
- package/front_end/models/issues_manager/descriptions/selectivePermissionsIntervention.md +7 -0
- package/front_end/models/issues_manager/issues_manager.ts +2 -0
- package/front_end/models/javascript_metadata/NativeFunctions.js +44 -9
- package/front_end/models/persistence/persistence-meta.ts +4 -4
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +12 -1
- package/front_end/panels/ai_assistance/components/ChatInput.ts +37 -30
- package/front_end/panels/ai_assistance/components/ChatView.ts +4 -2
- package/front_end/panels/ai_assistance/components/chatInput.css +26 -1
- package/front_end/panels/application/StorageView.ts +8 -2
- package/front_end/panels/application/preloading/PreloadingView.ts +105 -7
- package/front_end/panels/application/preloading/preloadingView.css +4 -0
- package/front_end/panels/console/ConsoleView.ts +2 -2
- package/front_end/panels/console/console-meta.ts +18 -14
- package/front_end/panels/elements/ComputedStyleWidget.ts +12 -7
- package/front_end/panels/elements/ElementsPanel.ts +1 -1
- package/front_end/panels/elements/ElementsTreeOutline.ts +4 -6
- package/front_end/panels/elements/ImagePreviewPopover.ts +6 -9
- package/front_end/panels/elements/StylePropertiesSection.ts +30 -0
- package/front_end/panels/elements/StylesSidebarPane.ts +13 -7
- package/front_end/panels/elements/elements-meta.ts +12 -8
- package/front_end/panels/issues/AffectedSelectivePermissionsInterventionView.ts +120 -0
- package/front_end/panels/issues/IssueView.ts +2 -0
- package/front_end/panels/issues/IssuesPane.ts +6 -0
- package/front_end/panels/issues/issues.ts +2 -0
- package/front_end/panels/lighthouse/LighthouseStartView.ts +99 -38
- package/front_end/panels/lighthouse/LighthouseTimespanView.ts +53 -14
- package/front_end/panels/lighthouse/RadioSetting.ts +33 -19
- package/front_end/panels/lighthouse/lighthouse.ts +2 -0
- package/front_end/panels/media/PlayerMessagesView.ts +62 -49
- package/front_end/panels/media/media.ts +2 -0
- package/front_end/panels/media/playerMessagesView.css +1 -1
- package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +49 -24
- package/front_end/panels/settings/FrameworkIgnoreListSettingsTab.ts +2 -2
- package/front_end/panels/sources/sources-meta.ts +8 -4
- package/front_end/panels/utils/utils.ts +11 -5
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/lit/lib/async-directive.d.ts +465 -0
- package/front_end/third_party/lit/lib/async-directive.js +23 -0
- package/front_end/third_party/lit/lib/async-directive.js.map +1 -0
- package/front_end/third_party/lit/lib/decorators.d.ts +7 -1
- package/front_end/third_party/lit/lib/decorators.js +2 -2
- package/front_end/third_party/lit/lib/decorators.js.map +1 -1
- package/front_end/third_party/lit/lib/directive.js.map +1 -1
- package/front_end/third_party/lit/lib/directives.d.ts +27 -8
- package/front_end/third_party/lit/lib/directives.js +8 -8
- package/front_end/third_party/lit/lib/directives.js.map +1 -1
- package/front_end/third_party/lit/lib/lit.d.ts +15 -5
- package/front_end/third_party/lit/lib/lit.js +4 -4
- package/front_end/third_party/lit/lib/lit.js.map +1 -1
- package/front_end/third_party/lit/lib/static-html.js +2 -2
- package/front_end/third_party/lit/lib/static-html.js.map +1 -1
- package/front_end/third_party/lit/lit.ts +2 -1
- package/front_end/third_party/lit/rollup.config.mjs +1 -1
- package/front_end/ui/components/markdown_view/MarkdownLinksMap.ts +5 -0
- package/front_end/ui/legacy/components/utils/ImagePreview.ts +27 -23
- package/front_end/ui/lit/lit.ts +1 -0
- package/front_end/ui/visual_logging/Debugging.ts +1 -1
- package/front_end/ui/visual_logging/KnownContextValues.ts +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,120 @@
|
|
|
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 * as i18n from '../../core/i18n/i18n.js';
|
|
6
|
+
import type * as Platform from '../../core/platform/platform.js';
|
|
7
|
+
import type * as SDK from '../../core/sdk/sdk.js';
|
|
8
|
+
import type * as Protocol from '../../generated/protocol.js';
|
|
9
|
+
import * as Bindings from '../../models/bindings/bindings.js';
|
|
10
|
+
import type * as IssuesManager from '../../models/issues_manager/issues_manager.js';
|
|
11
|
+
import * as Components from '../../ui/legacy/components/utils/utils.js';
|
|
12
|
+
import * as UI from '../../ui/legacy/legacy.js';
|
|
13
|
+
import {Directives, html, type LitTemplate, nothing, render} from '../../ui/lit/lit.js';
|
|
14
|
+
|
|
15
|
+
import {AffectedResourcesView} from './AffectedResourcesView.js';
|
|
16
|
+
|
|
17
|
+
const UIStrings = {
|
|
18
|
+
/**
|
|
19
|
+
* @description Label for number of affected resources indication in issue view
|
|
20
|
+
*/
|
|
21
|
+
nViolations: '{n, plural, =1 {# violation} other {# violations}}',
|
|
22
|
+
/**
|
|
23
|
+
* @description Title for the API column in the Selective Permissions Intervention affected resources list
|
|
24
|
+
*/
|
|
25
|
+
api: 'API',
|
|
26
|
+
/**
|
|
27
|
+
* @description Title for the Script column in the Selective Permissions Intervention affected resources list
|
|
28
|
+
*/
|
|
29
|
+
script: 'Script',
|
|
30
|
+
/**
|
|
31
|
+
* @description Title for the Ad Ancestry column in the Selective Permissions Intervention affected resources list
|
|
32
|
+
*/
|
|
33
|
+
adAncestry: 'Ad Ancestry',
|
|
34
|
+
/**
|
|
35
|
+
* @description Text for unknown value
|
|
36
|
+
*/
|
|
37
|
+
unknown: 'unknown',
|
|
38
|
+
/**
|
|
39
|
+
* @description Text for loading state
|
|
40
|
+
*/
|
|
41
|
+
loading: 'loading…',
|
|
42
|
+
} as const;
|
|
43
|
+
const str_ = i18n.i18n.registerUIStrings('panels/issues/AffectedSelectivePermissionsInterventionView.ts', UIStrings);
|
|
44
|
+
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
45
|
+
|
|
46
|
+
export class AffectedSelectivePermissionsInterventionView extends AffectedResourcesView {
|
|
47
|
+
readonly #linkifier = new Components.Linkifier.Linkifier();
|
|
48
|
+
|
|
49
|
+
protected getResourceNameWithCount(count: number): string {
|
|
50
|
+
return i18nString(UIStrings.nViolations, {n: count});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#render(): void {
|
|
54
|
+
const issues = Array.from(this.issue.getSelectivePermissionsInterventionIssues());
|
|
55
|
+
// eslint-disable-next-line @devtools/no-lit-render-outside-of-view
|
|
56
|
+
render(
|
|
57
|
+
html`
|
|
58
|
+
<tr>
|
|
59
|
+
<td class="affected-resource-header">${i18nString(UIStrings.api)}</td>
|
|
60
|
+
<td class="affected-resource-header">${i18nString(UIStrings.script)}</td>
|
|
61
|
+
<td class="affected-resource-header">${i18nString(UIStrings.adAncestry)}</td>
|
|
62
|
+
</tr>
|
|
63
|
+
${issues.map(issue => this.#renderDetail(issue))}
|
|
64
|
+
`,
|
|
65
|
+
this.affectedResources, {host: this});
|
|
66
|
+
this.updateAffectedResourceCount(issues.length);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#renderDetail(issue: IssuesManager.SelectivePermissionsInterventionIssue.SelectivePermissionsInterventionIssue):
|
|
70
|
+
LitTemplate {
|
|
71
|
+
const details = issue.details();
|
|
72
|
+
const issuesModel = issue.model();
|
|
73
|
+
|
|
74
|
+
const stackTracePromise = (details.stackTrace && issuesModel) ?
|
|
75
|
+
this.#resolveStackTrace(details.stackTrace, issuesModel) :
|
|
76
|
+
Promise.resolve(html`<span>${i18nString(UIStrings.unknown)}</span>`);
|
|
77
|
+
|
|
78
|
+
const target = issuesModel ? issuesModel.target() : null;
|
|
79
|
+
|
|
80
|
+
return html`
|
|
81
|
+
<tr class="affected-resource-directive">
|
|
82
|
+
<td>${details.apiName}</td>
|
|
83
|
+
<td>${Directives.until(stackTracePromise, html`<span>${i18nString(UIStrings.loading)}</span>`)}</td>
|
|
84
|
+
<td class="affected-resource-cell">
|
|
85
|
+
<div class="ad-ancestry-list">
|
|
86
|
+
${(details.adAncestry?.adAncestryChain || []).map(script => {
|
|
87
|
+
const link = this.#linkifier.linkifyScriptLocation(
|
|
88
|
+
target, script.scriptId, script.name as Platform.DevToolsPath.UrlString, 0);
|
|
89
|
+
return html`<div>${link}</div>`;
|
|
90
|
+
})}
|
|
91
|
+
${
|
|
92
|
+
details.adAncestry?.rootScriptFilterlistRule ?
|
|
93
|
+
html`<div>Rule: ${details.adAncestry.rootScriptFilterlistRule}</div>` :
|
|
94
|
+
nothing}
|
|
95
|
+
</div>
|
|
96
|
+
</td>
|
|
97
|
+
</tr>
|
|
98
|
+
`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async #resolveStackTrace(stackTrace: Protocol.Runtime.StackTrace, issuesModel: SDK.IssuesModel.IssuesModel):
|
|
102
|
+
Promise<LitTemplate> {
|
|
103
|
+
const debuggerWorkspaceBinding = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance();
|
|
104
|
+
const stackTraceTranslated =
|
|
105
|
+
await debuggerWorkspaceBinding.createStackTraceFromProtocolRuntime(stackTrace, issuesModel.target());
|
|
106
|
+
return html`
|
|
107
|
+
<devtools-widget .widgetConfig=${UI.Widget.widgetConfig(Components.JSPresentationUtils.StackTracePreviewContent, {
|
|
108
|
+
stackTrace: stackTraceTranslated,
|
|
109
|
+
options: {expandable: true},
|
|
110
|
+
})}>
|
|
111
|
+
</devtools-widget>
|
|
112
|
+
`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
update(): void {
|
|
116
|
+
this.requestResolver.clear();
|
|
117
|
+
this.#linkifier.reset();
|
|
118
|
+
this.#render();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -29,6 +29,7 @@ import {AffectedMetadataAllowedSitesView} from './AffectedMetadataAllowedSitesVi
|
|
|
29
29
|
import {AffectedPartitioningBlobURLView} from './AffectedPartitioningBlobURLView.js';
|
|
30
30
|
import {AffectedPermissionElementsView} from './AffectedPermissionElementsView.js';
|
|
31
31
|
import {AffectedItem, AffectedResourcesView, extractShortPath} from './AffectedResourcesView.js';
|
|
32
|
+
import {AffectedSelectivePermissionsInterventionView} from './AffectedSelectivePermissionsInterventionView.js';
|
|
32
33
|
import {AffectedSharedArrayBufferIssueDetailsView} from './AffectedSharedArrayBufferIssueDetailsView.js';
|
|
33
34
|
import {AffectedSourcesView} from './AffectedSourcesView.js';
|
|
34
35
|
import {AffectedTrackingSitesView} from './AffectedTrackingSitesView.js';
|
|
@@ -260,6 +261,7 @@ export class IssueView extends UI.TreeOutline.TreeElement {
|
|
|
260
261
|
new AffectedDescendantsWithinSelectElementView(this, this.#issue, 'disallowed-select-descendants-details'),
|
|
261
262
|
new AffectedPartitioningBlobURLView(this, this.#issue, 'partitioning-blob-url-details'),
|
|
262
263
|
new AffectedPermissionElementsView(this, this.#issue, 'permission-element-elements'),
|
|
264
|
+
new AffectedSelectivePermissionsInterventionView(this, this.#issue, 'selective-permissions-intervention-details'),
|
|
263
265
|
];
|
|
264
266
|
this.#hiddenIssuesMenu = new Components.HideIssuesMenu.HideIssuesMenu();
|
|
265
267
|
this.#aggregatedIssuesCount = null;
|
|
@@ -110,6 +110,10 @@ const UIStrings = {
|
|
|
110
110
|
* @description Category title for a group of permission element issues
|
|
111
111
|
*/
|
|
112
112
|
permissionElement: 'PEPC Element',
|
|
113
|
+
/**
|
|
114
|
+
* @description Category title for the different 'Selective Permissions Intervention' issues.
|
|
115
|
+
*/
|
|
116
|
+
selectivePermissionsIntervention: 'Selective Permissions Intervention',
|
|
113
117
|
} as const;
|
|
114
118
|
const str_ = i18n.i18n.registerUIStrings('panels/issues/IssuesPane.ts', UIStrings);
|
|
115
119
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
@@ -153,6 +157,8 @@ class IssueCategoryView extends UI.TreeOutline.TreeElement {
|
|
|
153
157
|
return i18nString(UIStrings.generic);
|
|
154
158
|
case IssuesManager.Issue.IssueCategory.PERMISSION_ELEMENT:
|
|
155
159
|
return i18nString(UIStrings.permissionElement);
|
|
160
|
+
case IssuesManager.Issue.IssueCategory.SELECTIVE_PERMISSIONS_INTERVENTION:
|
|
161
|
+
return i18nString(UIStrings.selectivePermissionsIntervention);
|
|
156
162
|
case IssuesManager.Issue.IssueCategory.OTHER:
|
|
157
163
|
return i18nString(UIStrings.other);
|
|
158
164
|
}
|
|
@@ -2,11 +2,13 @@
|
|
|
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 AffectedSelectivePermissionsInterventionView from './AffectedSelectivePermissionsInterventionView.js';
|
|
5
6
|
import * as IssueRevealer from './IssueRevealer.js';
|
|
6
7
|
import * as IssuesPane from './IssuesPane.js';
|
|
7
8
|
import * as IssueView from './IssueView.js';
|
|
8
9
|
|
|
9
10
|
export {
|
|
11
|
+
AffectedSelectivePermissionsInterventionView,
|
|
10
12
|
IssueRevealer,
|
|
11
13
|
IssuesPane,
|
|
12
14
|
IssueView,
|
|
@@ -11,6 +11,7 @@ import type * as Platform from '../../core/platform/platform.js';
|
|
|
11
11
|
import * as Buttons from '../../ui/components/buttons/buttons.js';
|
|
12
12
|
import {Link} from '../../ui/kit/kit.js';
|
|
13
13
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
14
|
+
import {Directives, html, render} from '../../ui/lit/lit.js';
|
|
14
15
|
|
|
15
16
|
import {type LighthouseController, type Preset, Presets, RuntimeSettings} from './LighthouseController.js';
|
|
16
17
|
import type {LighthousePanel} from './LighthousePanel.js';
|
|
@@ -55,6 +56,78 @@ const UIStrings = {
|
|
|
55
56
|
const str_ = i18n.i18n.registerUIStrings('panels/lighthouse/LighthouseStartView.ts', UIStrings);
|
|
56
57
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
57
58
|
|
|
59
|
+
const renderStartView = (
|
|
60
|
+
_input: Record<string, never>,
|
|
61
|
+
output: {
|
|
62
|
+
helpText?: HTMLElement,
|
|
63
|
+
warningText?: HTMLElement,
|
|
64
|
+
modeFormElements?: HTMLElement,
|
|
65
|
+
deviceTypeFormElements?: HTMLElement,
|
|
66
|
+
categoriesFormElements?: HTMLElement,
|
|
67
|
+
},
|
|
68
|
+
target: HTMLElement,
|
|
69
|
+
): void => {
|
|
70
|
+
// clang-format off
|
|
71
|
+
render(
|
|
72
|
+
html`
|
|
73
|
+
<form class="lighthouse-start-view">
|
|
74
|
+
<header class="hbox">
|
|
75
|
+
<div class="lighthouse-logo"></div>
|
|
76
|
+
<div class="lighthouse-title">
|
|
77
|
+
${i18nString(UIStrings.generateLighthouseReport)}
|
|
78
|
+
</div>
|
|
79
|
+
<div class="lighthouse-start-button-container"></div>
|
|
80
|
+
</header>
|
|
81
|
+
<div
|
|
82
|
+
${Directives.ref(e => {
|
|
83
|
+
output.helpText = e as HTMLElement;
|
|
84
|
+
})}
|
|
85
|
+
class="lighthouse-help-text hidden"
|
|
86
|
+
></div>
|
|
87
|
+
<div class="lighthouse-options hbox">
|
|
88
|
+
<div class="lighthouse-form-section">
|
|
89
|
+
<div
|
|
90
|
+
class="lighthouse-form-elements"
|
|
91
|
+
${Directives.ref(e => {
|
|
92
|
+
output.modeFormElements = e as HTMLElement;
|
|
93
|
+
})}
|
|
94
|
+
></div>
|
|
95
|
+
</div>
|
|
96
|
+
<div class="lighthouse-form-section">
|
|
97
|
+
<div
|
|
98
|
+
class="lighthouse-form-elements"
|
|
99
|
+
${Directives.ref(e => {
|
|
100
|
+
output.deviceTypeFormElements = e as HTMLElement;
|
|
101
|
+
})}
|
|
102
|
+
></div>
|
|
103
|
+
</div>
|
|
104
|
+
<div class="lighthouse-form-categories">
|
|
105
|
+
<fieldset class="lighthouse-form-section lighthouse-form-categories-fieldset">
|
|
106
|
+
<legend class="lighthouse-form-section-label">
|
|
107
|
+
${i18nString(UIStrings.categories)}
|
|
108
|
+
</legend>
|
|
109
|
+
<div
|
|
110
|
+
class="lighthouse-form-elements"
|
|
111
|
+
${Directives.ref(e => {
|
|
112
|
+
output.categoriesFormElements = e as HTMLElement;
|
|
113
|
+
})}
|
|
114
|
+
></div>
|
|
115
|
+
</fieldset>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
<div
|
|
119
|
+
${Directives.ref(e => {
|
|
120
|
+
output.warningText = e as HTMLElement;
|
|
121
|
+
})}
|
|
122
|
+
class="lighthouse-warning-text hidden"
|
|
123
|
+
></div>
|
|
124
|
+
</form>
|
|
125
|
+
`,
|
|
126
|
+
target,
|
|
127
|
+
);
|
|
128
|
+
// clang-format on
|
|
129
|
+
};
|
|
130
|
+
|
|
58
131
|
export class StartView extends UI.Widget.Widget {
|
|
59
132
|
private controller: LighthouseController;
|
|
60
133
|
private panel: LighthousePanel;
|
|
@@ -145,13 +218,11 @@ export class StartView extends UI.Widget.Widget {
|
|
|
145
218
|
}
|
|
146
219
|
}
|
|
147
220
|
|
|
148
|
-
private populateFormControls(
|
|
221
|
+
private populateFormControls(deviceTypeFormElements: Element, categoryFormElements: Element, mode?: string): void {
|
|
149
222
|
// Populate the device type
|
|
150
|
-
const deviceTypeFormElements = fragment.$('device-type-form-elements');
|
|
151
223
|
this.populateRuntimeSettingAsRadio('lighthouse.device-type', i18nString(UIStrings.device), deviceTypeFormElements);
|
|
152
224
|
|
|
153
225
|
// Populate the categories
|
|
154
|
-
const categoryFormElements = fragment.$('categories-form-elements') as HTMLElement;
|
|
155
226
|
|
|
156
227
|
this.checkboxes = [];
|
|
157
228
|
for (const preset of Presets) {
|
|
@@ -174,44 +245,34 @@ export class StartView extends UI.Widget.Widget {
|
|
|
174
245
|
this.populateRuntimeSettingAsToolbarDropdown('lighthouse.throttling', this.#settingsToolbar);
|
|
175
246
|
|
|
176
247
|
const {mode} = this.controller.getFlags();
|
|
177
|
-
this.populateStartButton(mode);
|
|
178
248
|
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
<div class="lighthouse-form-elements" $="device-type-form-elements"></div>
|
|
193
|
-
</div>
|
|
194
|
-
<div class="lighthouse-form-categories">
|
|
195
|
-
<fieldset class="lighthouse-form-section lighthouse-form-categories-fieldset">
|
|
196
|
-
<legend class="lighthouse-form-section-label">${i18nString(UIStrings.categories)}</legend>
|
|
197
|
-
<div class="lighthouse-form-elements" $="categories-form-elements"></div>
|
|
198
|
-
</fieldset>
|
|
199
|
-
</div>
|
|
200
|
-
</div>
|
|
201
|
-
<div $="warning-text" class="lighthouse-warning-text hidden"></div>
|
|
202
|
-
</form>
|
|
203
|
-
`;
|
|
204
|
-
|
|
205
|
-
this.helpText = fragment.$('help-text');
|
|
206
|
-
this.warningText = fragment.$('warning-text');
|
|
207
|
-
|
|
208
|
-
const modeFormElements = fragment.$('mode-form-elements');
|
|
209
|
-
this.populateRuntimeSettingAsRadio('lighthouse.mode', i18nString(UIStrings.mode), modeFormElements);
|
|
249
|
+
const output = {
|
|
250
|
+
helpText: undefined as HTMLElement | undefined,
|
|
251
|
+
warningText: undefined as HTMLElement | undefined,
|
|
252
|
+
modeFormElements: undefined as HTMLElement | undefined,
|
|
253
|
+
deviceTypeFormElements: undefined as HTMLElement | undefined,
|
|
254
|
+
categoriesFormElements: undefined as HTMLElement | undefined,
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
renderStartView(
|
|
258
|
+
{},
|
|
259
|
+
output,
|
|
260
|
+
this.contentElement,
|
|
261
|
+
);
|
|
210
262
|
|
|
211
|
-
this.
|
|
263
|
+
this.helpText = output.helpText;
|
|
264
|
+
this.warningText = output.warningText;
|
|
212
265
|
|
|
213
|
-
|
|
214
|
-
|
|
266
|
+
const modeFormElements = output.modeFormElements;
|
|
267
|
+
const deviceTypeFormElements = output.deviceTypeFormElements;
|
|
268
|
+
const categoriesFormElements = output.categoriesFormElements;
|
|
269
|
+
|
|
270
|
+
if (!modeFormElements || !deviceTypeFormElements || !categoriesFormElements) {
|
|
271
|
+
throw new Error('Required elements not found in template');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
this.populateRuntimeSettingAsRadio('lighthouse.mode', i18nString(UIStrings.mode), modeFormElements);
|
|
275
|
+
this.populateFormControls(deviceTypeFormElements, categoriesFormElements, mode);
|
|
215
276
|
|
|
216
277
|
this.refresh();
|
|
217
278
|
}
|
|
@@ -7,6 +7,7 @@ import * as i18n from '../../core/i18n/i18n.js';
|
|
|
7
7
|
import * as Geometry from '../../models/geometry/geometry.js';
|
|
8
8
|
import * as Buttons from '../../ui/components/buttons/buttons.js';
|
|
9
9
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
10
|
+
import {Directives, html, render} from '../../ui/lit/lit.js';
|
|
10
11
|
|
|
11
12
|
import lighthouseDialogStyles from './lighthouseDialog.css.js';
|
|
12
13
|
import type {LighthousePanel} from './LighthousePanel.js';
|
|
@@ -37,6 +38,43 @@ const UIStrings = {
|
|
|
37
38
|
const str_ = i18n.i18n.registerUIStrings('panels/lighthouse/LighthouseTimespanView.ts', UIStrings);
|
|
38
39
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
39
40
|
|
|
41
|
+
const renderTimespanView = (
|
|
42
|
+
input: {
|
|
43
|
+
cancelButton: Element,
|
|
44
|
+
endButton: Element,
|
|
45
|
+
},
|
|
46
|
+
output: {
|
|
47
|
+
statusHeader?: HTMLElement,
|
|
48
|
+
contentContainer?: HTMLElement,
|
|
49
|
+
},
|
|
50
|
+
target: ShadowRoot,
|
|
51
|
+
): void => {
|
|
52
|
+
// clang-format off
|
|
53
|
+
render(
|
|
54
|
+
html`
|
|
55
|
+
<div class="lighthouse-view vbox">
|
|
56
|
+
<span
|
|
57
|
+
${Directives.ref(e => {
|
|
58
|
+
output.statusHeader = e as HTMLElement;
|
|
59
|
+
})}
|
|
60
|
+
class="header"
|
|
61
|
+
></span>
|
|
62
|
+
<span
|
|
63
|
+
${Directives.ref(e => {
|
|
64
|
+
output.contentContainer = e as HTMLElement;
|
|
65
|
+
})}
|
|
66
|
+
class="lighthouse-dialog-text"
|
|
67
|
+
></span>
|
|
68
|
+
<div class="lighthouse-action-buttons hbox">
|
|
69
|
+
${input.cancelButton} ${input.endButton}
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
`,
|
|
73
|
+
target,
|
|
74
|
+
);
|
|
75
|
+
// clang-format on
|
|
76
|
+
};
|
|
77
|
+
|
|
40
78
|
export class TimespanView extends UI.Dialog.Dialog {
|
|
41
79
|
private panel: LighthousePanel;
|
|
42
80
|
private statusHeader: Element|null;
|
|
@@ -90,20 +128,21 @@ export class TimespanView extends UI.Dialog.Dialog {
|
|
|
90
128
|
className: 'cancel',
|
|
91
129
|
jslogContext: 'lighthouse.cancel',
|
|
92
130
|
});
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
131
|
+
|
|
132
|
+
const output = {
|
|
133
|
+
statusHeader: undefined as HTMLElement | undefined,
|
|
134
|
+
contentContainer: undefined as HTMLElement | undefined,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
renderTimespanView(
|
|
138
|
+
{
|
|
139
|
+
cancelButton,
|
|
140
|
+
endButton: this.endButton,
|
|
141
|
+
},
|
|
142
|
+
output, dialogRoot);
|
|
143
|
+
|
|
144
|
+
this.statusHeader = output.statusHeader ?? null;
|
|
145
|
+
this.contentContainer = output.contentContainer ?? null;
|
|
107
146
|
|
|
108
147
|
this.setSizeBehavior(UI.GlassPane.SizeBehavior.SET_EXACT_WIDTH_MAX_HEIGHT);
|
|
109
148
|
this.setMaxContentSize(new Geometry.Size(500, 400));
|
|
@@ -2,10 +2,13 @@
|
|
|
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
|
/* eslint-disable @devtools/no-imperative-dom-api */
|
|
5
|
+
/* eslint-disable @devtools/no-lit-render-outside-of-view */
|
|
5
6
|
|
|
6
7
|
import type * as Common from '../../core/common/common.js';
|
|
7
8
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
9
|
+
import {Directives, html, render} from '../../ui/lit/lit.js';
|
|
8
10
|
|
|
11
|
+
const {ifDefined} = Directives;
|
|
9
12
|
interface RadioOption {
|
|
10
13
|
value: string;
|
|
11
14
|
label: () => Common.UIString.LocalizedString;
|
|
@@ -28,25 +31,36 @@ export class RadioSetting {
|
|
|
28
31
|
UI.ARIAUtils.markAsRadioGroup(this.element);
|
|
29
32
|
|
|
30
33
|
this.radioElements = [];
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
34
|
+
// clang-format off
|
|
35
|
+
render(
|
|
36
|
+
html`
|
|
37
|
+
${this.options.map(option => {
|
|
38
|
+
const tooltip = option.tooltip?.() || description;
|
|
39
|
+
return html`
|
|
40
|
+
<label class="lighthouse-radio">
|
|
41
|
+
<input
|
|
42
|
+
type="radio"
|
|
43
|
+
value=${option.value}
|
|
44
|
+
name=${setting.name}
|
|
45
|
+
@change=${this.valueChanged.bind(this)}
|
|
46
|
+
title=${ifDefined(description ? tooltip : undefined)}
|
|
47
|
+
${Directives.ref(el => {
|
|
48
|
+
this.radioElements.push(el as HTMLInputElement);
|
|
49
|
+
}
|
|
50
|
+
)}
|
|
51
|
+
/>
|
|
52
|
+
<span
|
|
53
|
+
class="lighthouse-radio-text"
|
|
54
|
+
title=${ifDefined(description ? tooltip : undefined)}
|
|
55
|
+
>${option.label()}</span
|
|
56
|
+
>
|
|
57
|
+
</label>
|
|
58
|
+
`;
|
|
59
|
+
})}
|
|
60
|
+
`,
|
|
61
|
+
this.element,
|
|
62
|
+
);
|
|
63
|
+
// clang-format on
|
|
50
64
|
|
|
51
65
|
this.ignoreChangeEvents = false;
|
|
52
66
|
this.selectedIndex = -1;
|
|
@@ -11,6 +11,7 @@ import * as LighthouseReportRenderer from './LighthouseReportRenderer.js';
|
|
|
11
11
|
import * as LighthouseReportSelector from './LighthouseReportSelector.js';
|
|
12
12
|
import * as LighthouseStartView from './LighthouseStartView.js';
|
|
13
13
|
import * as LighthouseStatusView from './LighthouseStatusView.js';
|
|
14
|
+
import * as LighthouseTimespanView from './LighthouseTimespanView.js';
|
|
14
15
|
import * as RadioSetting from './RadioSetting.js';
|
|
15
16
|
|
|
16
17
|
export {
|
|
@@ -21,5 +22,6 @@ export {
|
|
|
21
22
|
LighthouseReportSelector,
|
|
22
23
|
LighthouseStartView,
|
|
23
24
|
LighthouseStatusView,
|
|
25
|
+
LighthouseTimespanView,
|
|
24
26
|
RadioSetting,
|
|
25
27
|
};
|
|
@@ -2,6 +2,7 @@
|
|
|
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
|
/* eslint-disable @devtools/no-imperative-dom-api */
|
|
5
|
+
/* eslint-disable @devtools/no-lit-render-outside-of-view */
|
|
5
6
|
|
|
6
7
|
import '../../ui/legacy/legacy.js';
|
|
7
8
|
|
|
@@ -9,6 +10,7 @@ import type * as Common from '../../core/common/common.js';
|
|
|
9
10
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
10
11
|
import type * as Protocol from '../../generated/protocol.js';
|
|
11
12
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
13
|
+
import {html, type LitTemplate, nothing, render} from '../../ui/lit/lit.js';
|
|
12
14
|
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
|
|
13
15
|
|
|
14
16
|
import playerMessagesViewStyles from './playerMessagesView.css.js';
|
|
@@ -339,59 +341,70 @@ export class PlayerMessagesView extends UI.Widget.VBox {
|
|
|
339
341
|
UI.UIUtils.createTextChild(container, message.message);
|
|
340
342
|
}
|
|
341
343
|
|
|
342
|
-
private
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
344
|
+
private renderError(error: Protocol.Media.PlayerError): LitTemplate {
|
|
345
|
+
// clang-format off
|
|
346
|
+
return html`
|
|
347
|
+
<div class="status-error-box">
|
|
348
|
+
<div class="status-error-field-labeled">
|
|
349
|
+
<span class="status-error-field-label"
|
|
350
|
+
>${i18nString(UIStrings.errorGroupLabel)}</span
|
|
351
|
+
>
|
|
352
|
+
<span>${error.errorType}</span>
|
|
353
|
+
</div>
|
|
354
|
+
<div class="status-error-field-labeled">
|
|
355
|
+
<span class="status-error-field-label"
|
|
356
|
+
>${i18nString(UIStrings.errorCodeLabel)}</span
|
|
357
|
+
>
|
|
358
|
+
<span>${error.code}</span>
|
|
359
|
+
</div>
|
|
360
|
+
<div class="status-error-field-labeled">
|
|
361
|
+
${
|
|
362
|
+
Object.keys(error.data).length !== 0
|
|
363
|
+
? html`<span class="status-error-field-label"
|
|
364
|
+
>${i18nString(UIStrings.errorDataLabel)}</span
|
|
365
|
+
>
|
|
366
|
+
<div>
|
|
367
|
+
${Object.entries(error.data).map(
|
|
368
|
+
([key, value]) => html`<div>${key}: ${value}</div>`,
|
|
369
|
+
)}
|
|
370
|
+
</div>`
|
|
371
|
+
: nothing
|
|
372
|
+
}
|
|
373
|
+
</div>
|
|
374
|
+
<div class="status-error-field-labeled">
|
|
375
|
+
${
|
|
376
|
+
error.stack.length !== 0
|
|
377
|
+
? html`<span class="status-error-field-label"
|
|
378
|
+
>${i18nString(UIStrings.errorStackLabel)}</span
|
|
379
|
+
>
|
|
380
|
+
<div>
|
|
381
|
+
${error.stack.map(
|
|
382
|
+
stackEntry =>
|
|
383
|
+
html`<div>${stackEntry.file}:${stackEntry.line}</div>`,
|
|
384
|
+
)}
|
|
385
|
+
</div>`
|
|
386
|
+
: nothing
|
|
387
|
+
}
|
|
388
|
+
</div>
|
|
389
|
+
<div class="status-error-field-labeled">
|
|
390
|
+
${
|
|
391
|
+
error.cause.length !== 0
|
|
392
|
+
? html`
|
|
393
|
+
<span class="status-error-field-label"
|
|
394
|
+
>${i18nString(UIStrings.errorCauseLabel)}</span
|
|
395
|
+
>
|
|
396
|
+
${this.renderError(error.cause[0])}
|
|
397
|
+
`
|
|
398
|
+
: nothing
|
|
399
|
+
}
|
|
400
|
+
</div>
|
|
401
|
+
</div>
|
|
359
402
|
`;
|
|
360
|
-
|
|
361
|
-
entry.$('status-error-group').textContent = i18nString(UIStrings.errorGroupLabel);
|
|
362
|
-
entry.$('status-error-code').textContent = i18nString(UIStrings.errorCodeLabel);
|
|
363
|
-
|
|
364
|
-
if (Object.keys(error.data).length !== 0) {
|
|
365
|
-
const label = entry.$('status-error-data').createChild('span', 'status-error-field-label');
|
|
366
|
-
UI.UIUtils.createTextChild(label, i18nString(UIStrings.errorDataLabel));
|
|
367
|
-
const dataContent = entry.$('status-error-data').createChild('div');
|
|
368
|
-
for (const [key, value] of Object.entries(error.data)) {
|
|
369
|
-
const datumContent = dataContent.createChild('div');
|
|
370
|
-
UI.UIUtils.createTextChild(datumContent, `${key}: ${value}`);
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
if (error.stack.length !== 0) {
|
|
375
|
-
const label = entry.$('status-error-stack').createChild('span', 'status-error-field-label');
|
|
376
|
-
UI.UIUtils.createTextChild(label, i18nString(UIStrings.errorStackLabel));
|
|
377
|
-
const stackContent = entry.$('status-error-stack').createChild('div');
|
|
378
|
-
for (const stackEntry of error.stack) {
|
|
379
|
-
const frameBox = stackContent.createChild('div');
|
|
380
|
-
UI.UIUtils.createTextChild(frameBox, `${stackEntry.file}:${stackEntry.line}`);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
if (error.cause.length !== 0) {
|
|
385
|
-
const label = entry.$('status-error-cause').createChild('span', 'status-error-field-label');
|
|
386
|
-
UI.UIUtils.createTextChild(label, i18nString(UIStrings.errorCauseLabel));
|
|
387
|
-
entry.$('status-error-cause').appendChild(this.errorToDiv(error.cause[0]));
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
return entry.element();
|
|
403
|
+
// clang-format on
|
|
391
404
|
}
|
|
392
405
|
|
|
393
406
|
addError(error: Protocol.Media.PlayerError): void {
|
|
394
407
|
const container = this.bodyPanel.createChild('div', 'media-messages-message-container media-message-error');
|
|
395
|
-
|
|
408
|
+
render(this.renderError(error), container);
|
|
396
409
|
}
|
|
397
410
|
}
|