chrome-devtools-frontend 1.0.970539 → 1.0.972865
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/config/gni/devtools_grd_files.gni +12 -7
- package/config/gni/devtools_image_files.gni +1 -0
- package/front_end/Images/src/ic_changes.svg +5 -0
- package/front_end/core/common/ParsedURL.ts +11 -5
- package/front_end/core/host/UserMetrics.ts +4 -1
- package/front_end/core/i18n/locales/en-US.json +45 -0
- package/front_end/core/i18n/locales/en-XL.json +45 -0
- package/front_end/core/platform/UserVisibleError.ts +28 -0
- package/front_end/core/platform/platform.ts +2 -0
- package/front_end/core/sdk/CSSModel.ts +21 -0
- package/front_end/core/sdk/CSSStyleSheetHeader.ts +10 -10
- package/front_end/core/sdk/ChildTargetManager.ts +0 -1
- package/front_end/core/sdk/CompilerSourceMappingContentProvider.ts +6 -4
- package/front_end/core/sdk/DOMDebuggerModel.ts +4 -3
- package/front_end/core/sdk/DebuggerModel.ts +17 -16
- package/front_end/core/sdk/NetworkManager.ts +32 -10
- package/front_end/core/sdk/NetworkRequest.ts +29 -16
- package/front_end/core/sdk/Resource.ts +10 -10
- package/front_end/core/sdk/ResourceTreeModel.ts +16 -13
- package/front_end/core/sdk/Script.ts +10 -10
- package/front_end/core/sdk/SourceMap.ts +8 -6
- package/front_end/entrypoints/lighthouse_worker/LighthouseService.ts +35 -12
- package/front_end/entrypoints/main/MainImpl.ts +8 -0
- package/front_end/generated/InspectorBackendCommands.js +14 -8
- package/front_end/generated/SupportedCSSProperties.js +2 -0
- package/front_end/generated/protocol-mapping.d.ts +5 -0
- package/front_end/generated/protocol-proxy-api.d.ts +5 -0
- package/front_end/generated/protocol.ts +20 -12
- package/front_end/models/bindings/BreakpointManager.ts +7 -5
- package/front_end/models/bindings/CSSWorkspaceBinding.ts +10 -3
- package/front_end/models/bindings/DebuggerLanguagePlugins.ts +2 -1
- package/front_end/models/bindings/ResourceMapping.ts +2 -1
- package/front_end/models/bindings/ResourceScriptMapping.ts +2 -1
- package/front_end/models/bindings/SASSSourceMapping.ts +4 -3
- package/front_end/models/bindings/StylesSourceMapping.ts +2 -1
- package/front_end/models/har/HARFormat.ts +4 -2
- package/front_end/models/har/Importer.ts +0 -1
- package/front_end/models/issues_manager/FederatedAuthRequestIssue.ts +12 -12
- package/front_end/models/issues_manager/descriptions/{federatedAuthRequestClientIdMetadataHttpNotFound.md → federatedAuthRequestClientMetadataHttpNotFound.md} +0 -0
- package/front_end/models/issues_manager/descriptions/{federatedAuthRequestClientIdMetadataInvalidResponse.md → federatedAuthRequestClientMetadataInvalidResponse.md} +0 -0
- package/front_end/models/issues_manager/descriptions/{federatedAuthRequestClientIdMetadataNoResponse.md → federatedAuthRequestClientMetadataNoResponse.md} +0 -0
- package/front_end/models/issues_manager/descriptions/federatedAuthRequestManifestHttpNotFound.md +1 -0
- package/front_end/models/issues_manager/descriptions/federatedAuthRequestManifestInvalidResponse.md +1 -0
- package/front_end/models/issues_manager/descriptions/federatedAuthRequestManifestNoResponse.md +1 -0
- package/front_end/models/persistence/Automapping.ts +1 -1
- package/front_end/models/persistence/FileSystemWorkspaceBinding.ts +7 -7
- package/front_end/models/persistence/IsolatedFileSystem.ts +10 -13
- package/front_end/models/persistence/PersistenceActions.ts +1 -4
- package/front_end/models/persistence/PlatformFileSystem.ts +3 -3
- package/front_end/{panels/sources/SourceMapNamesResolver.ts → models/source_map_scopes/NamesResolver.ts} +5 -5
- package/front_end/models/source_map_scopes/source_map_scopes.ts +7 -0
- package/front_end/models/text_utils/ContentProvider.ts +2 -1
- package/front_end/models/text_utils/StaticContentProvider.ts +7 -5
- package/front_end/models/workspace/UISourceCode.ts +7 -7
- package/front_end/models/workspace/WorkspaceImpl.ts +1 -3
- package/front_end/models/workspace_diff/WorkspaceDiff.ts +20 -8
- package/front_end/panels/application/ServiceWorkerCacheViews.ts +3 -2
- package/front_end/panels/browser_debugger/categorizedBreakpointsSidebarPane.css +0 -1
- package/front_end/panels/changes/ChangesView.ts +4 -4
- package/front_end/panels/console/ConsolePrompt.ts +25 -2
- package/front_end/panels/console/ConsoleViewMessage.ts +41 -8
- package/front_end/panels/coverage/CoverageModel.ts +1 -1
- package/front_end/panels/elements/ElementsPanel.ts +25 -11
- package/front_end/panels/elements/ElementsTreeElement.ts +0 -6
- package/front_end/panels/elements/StylesSidebarPane.ts +236 -51
- package/front_end/panels/elements/elementsTreeOutline.css +0 -1
- package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
- package/front_end/panels/issues/AffectedSourcesView.ts +1 -1
- package/front_end/panels/lighthouse/LighthouseController.ts +41 -0
- package/front_end/panels/lighthouse/LighthousePanel.ts +64 -9
- package/front_end/panels/lighthouse/LighthouseProtocolService.ts +32 -4
- package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +0 -5
- package/front_end/panels/lighthouse/LighthouseStartView.ts +8 -4
- package/front_end/panels/lighthouse/LighthouseStartViewFR.ts +100 -0
- package/front_end/panels/lighthouse/LighthouseTimespanView.ts +99 -0
- package/front_end/panels/network/NetworkDataGridNode.ts +1 -1
- package/front_end/panels/profiler/CPUProfileView.ts +1 -1
- package/front_end/panels/profiler/HeapProfileView.ts +0 -2
- package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +0 -1
- package/front_end/panels/profiler/HeapSnapshotView.ts +2 -3
- package/front_end/panels/sensors/sensors.css +0 -1
- package/front_end/panels/snippets/ScriptSnippetFileSystem.ts +1 -1
- package/front_end/panels/sources/DebuggerPlugin.ts +5 -4
- package/front_end/panels/sources/NavigatorView.ts +14 -10
- package/front_end/panels/sources/ScopeChainSidebarPane.ts +6 -3
- package/front_end/panels/sources/SourcesNavigator.ts +7 -1
- package/front_end/panels/sources/sources-legacy.ts +5 -16
- package/front_end/panels/sources/sources.ts +0 -2
- package/front_end/panels/sources/sourcesView.css +0 -4
- package/front_end/panels/timeline/TimelineUIUtils.ts +4 -47
- package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/json.js +2 -1
- package/front_end/third_party/codemirror.next/codemirror.next.d.ts +3 -1
- package/front_end/third_party/codemirror.next/package.json +9 -9
- package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +1047 -1158
- package/front_end/third_party/lighthouse/locales/en-US.json +7 -1
- package/front_end/third_party/lighthouse/locales/en-XL.json +7 -1
- package/front_end/third_party/lighthouse/report/bundle.d.ts +0 -3
- package/front_end/third_party/lighthouse/report/bundle.js +38 -24
- package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +1 -1
- package/front_end/ui/legacy/ViewManager.ts +2 -1
- package/front_end/ui/legacy/closeButton.css +0 -1
- package/front_end/ui/legacy/components/source_frame/BinaryResourceViewFactory.ts +7 -4
- package/front_end/ui/legacy/components/source_frame/FontView.ts +1 -1
- package/front_end/ui/legacy/components/source_frame/ImageView.ts +1 -1
- package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +2 -3
- package/front_end/ui/legacy/components/utils/Linkifier.ts +20 -59
- package/front_end/ui/legacy/tabbedPane.css +0 -2
- package/front_end/ui/legacy/toolbar.css +28 -3
- package/package.json +1 -1
- package/scripts/hosted_mode/server.js +13 -0
- package/scripts/npm_test.js +1 -1
- package/front_end/models/issues_manager/descriptions/federatedAuthRequestWellKnownHttpNotFound.md +0 -1
- package/front_end/models/issues_manager/descriptions/federatedAuthRequestWellKnownInvalidResponse.md +0 -1
- package/front_end/models/issues_manager/descriptions/federatedAuthRequestWellKnownNoResponse.md +0 -1
@@ -39,9 +39,11 @@ import * as Root from '../../core/root/root.js';
|
|
39
39
|
import * as SDK from '../../core/sdk/sdk.js';
|
40
40
|
import * as Protocol from '../../generated/protocol.js';
|
41
41
|
import * as Bindings from '../../models/bindings/bindings.js';
|
42
|
+
import * as Formatter from '../../models/formatter/formatter.js';
|
42
43
|
import * as TextUtils from '../../models/text_utils/text_utils.js';
|
43
44
|
import * as Workspace from '../../models/workspace/workspace.js';
|
44
45
|
import * as WorkspaceDiff from '../../models/workspace_diff/workspace_diff.js';
|
46
|
+
import type * as Diff from '../../third_party/diff/diff.js';
|
45
47
|
import * as DiffView from '../../ui/components/diff_view/diff_view.js';
|
46
48
|
import * as IconButton from '../../ui/components/icon_button/icon_button.js';
|
47
49
|
import * as InlineEditor from '../../ui/legacy/components/inline_editor/inline_editor.js';
|
@@ -177,6 +179,14 @@ const UIStrings = {
|
|
177
179
|
*@description Text that is announced by the screen reader when the user focuses on an input field for editing the name of a CSS selector in the Styles panel
|
178
180
|
*/
|
179
181
|
cssSelector: '`CSS` selector',
|
182
|
+
/**
|
183
|
+
*@description Tooltip text that appears when hovering over the css changes button in the Styles Sidebar Pane of the Elements panel
|
184
|
+
*/
|
185
|
+
copyAllCSSChanges: 'Copy all the CSS changes',
|
186
|
+
/**
|
187
|
+
*@description Tooltip text that appears after clicking on the copy CSS changes button
|
188
|
+
*/
|
189
|
+
copiedToClipboard: 'Copied to clipboard',
|
180
190
|
};
|
181
191
|
|
182
192
|
const str_ = i18n.i18n.registerUIStrings('panels/elements/StylesSidebarPane.ts', UIStrings);
|
@@ -236,8 +246,7 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
236
246
|
private readonly resizeThrottler: Common.Throttler.Throttler;
|
237
247
|
private readonly imagePreviewPopover: ImagePreviewPopover;
|
238
248
|
activeCSSAngle: InlineEditor.CSSAngle.CSSAngle|null;
|
239
|
-
#
|
240
|
-
#uiSourceCodeToDiffCallbacks: Map<Workspace.UISourceCode.UISourceCode, () => void> = new Map();
|
249
|
+
#urlToChangeTracker: Map<string, ChangeTracker> = new Map();
|
241
250
|
|
242
251
|
static instance(): StylesSidebarPane {
|
243
252
|
if (!_stylesSidebarPaneInstance) {
|
@@ -609,6 +618,9 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
609
618
|
await this.innerRebuildUpdate(matchedStyles);
|
610
619
|
if (!this.initialUpdateCompleted) {
|
611
620
|
this.initialUpdateCompleted = true;
|
621
|
+
if (Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.STYLES_PANE_CSS_CHANGES)) {
|
622
|
+
this.appendToolbarItem(this.createCopyAllChangesButton());
|
623
|
+
}
|
612
624
|
this.dispatchEventToListeners(Events.InitialUpdateCompleted);
|
613
625
|
}
|
614
626
|
|
@@ -841,12 +853,14 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
841
853
|
const blocks = [new SectionBlock(null)];
|
842
854
|
let sectionIdx = 0;
|
843
855
|
let lastParentNode: SDK.DOMModel.DOMNode|null = null;
|
844
|
-
|
845
|
-
this.resetChangedLinesTracking();
|
846
|
-
}
|
856
|
+
const refreshedURLs = new Set<string>();
|
847
857
|
for (const style of matchedStyles.nodeStyles()) {
|
848
858
|
if (Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.STYLES_PANE_CSS_CHANGES) && style.parentRule) {
|
849
|
-
|
859
|
+
const url = style.parentRule.resourceURL();
|
860
|
+
if (url && !refreshedURLs.has(url)) {
|
861
|
+
await this.trackURLForChanges(url);
|
862
|
+
refreshedURLs.add(url);
|
863
|
+
}
|
850
864
|
}
|
851
865
|
|
852
866
|
const parentNode = matchedStyles.isInherited(style) ? matchedStyles.nodeForStyle(style) : null;
|
@@ -1001,25 +1015,27 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
1001
1015
|
return sections;
|
1002
1016
|
}
|
1003
1017
|
|
1004
|
-
|
1005
|
-
this.#
|
1006
|
-
|
1007
|
-
WorkspaceDiff.WorkspaceDiff.workspaceDiff().unsubscribeFromDiffChange(
|
1018
|
+
async trackURLForChanges(url: string): Promise<void> {
|
1019
|
+
const currentTracker = this.#urlToChangeTracker.get(url);
|
1020
|
+
if (currentTracker) {
|
1021
|
+
WorkspaceDiff.WorkspaceDiff.workspaceDiff().unsubscribeFromDiffChange(
|
1022
|
+
currentTracker.uiSourceCode, currentTracker.diffChangeCallback);
|
1008
1023
|
}
|
1009
|
-
this.#uiSourceCodeToDiffCallbacks.clear();
|
1010
|
-
}
|
1011
1024
|
|
1012
|
-
|
1013
|
-
if (!url || this.#changedLinesByURLs.has(url)) {
|
1014
|
-
return;
|
1015
|
-
}
|
1025
|
+
// We get a refreshed uiSourceCode each time because the underlying instance may be recreated.
|
1016
1026
|
const uiSourceCode = Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(url);
|
1017
|
-
if (uiSourceCode) {
|
1018
|
-
|
1019
|
-
const callback = this.refreshChangedLines.bind(this, uiSourceCode);
|
1020
|
-
WorkspaceDiff.WorkspaceDiff.workspaceDiff().subscribeToDiffChange(uiSourceCode, callback);
|
1021
|
-
this.#uiSourceCodeToDiffCallbacks.set(uiSourceCode, callback);
|
1027
|
+
if (!uiSourceCode) {
|
1028
|
+
return;
|
1022
1029
|
}
|
1030
|
+
const diffChangeCallback = this.refreshChangedLines.bind(this, uiSourceCode);
|
1031
|
+
WorkspaceDiff.WorkspaceDiff.workspaceDiff().subscribeToDiffChange(uiSourceCode, diffChangeCallback);
|
1032
|
+
const newTracker = {
|
1033
|
+
uiSourceCode,
|
1034
|
+
changedLines: new Set<number>(),
|
1035
|
+
diffChangeCallback,
|
1036
|
+
};
|
1037
|
+
this.#urlToChangeTracker.set(url, newTracker);
|
1038
|
+
await this.refreshChangedLines(newTracker.uiSourceCode);
|
1023
1039
|
}
|
1024
1040
|
|
1025
1041
|
isPropertyChanged(property: SDK.CSSProperty.CSSProperty): boolean {
|
@@ -1027,30 +1043,62 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
1027
1043
|
if (!url) {
|
1028
1044
|
return false;
|
1029
1045
|
}
|
1030
|
-
const
|
1031
|
-
if (!
|
1046
|
+
const changeTracker = this.#urlToChangeTracker.get(url);
|
1047
|
+
if (!changeTracker) {
|
1032
1048
|
return false;
|
1033
1049
|
}
|
1050
|
+
const {changedLines, formattedCurrentMapping} = changeTracker;
|
1034
1051
|
const uiLocation = Bindings.CSSWorkspaceBinding.CSSWorkspaceBinding.instance().propertyUILocation(property, true);
|
1035
1052
|
if (!uiLocation) {
|
1036
1053
|
return false;
|
1037
1054
|
}
|
1038
|
-
|
1039
|
-
|
1055
|
+
if (!formattedCurrentMapping) {
|
1056
|
+
// UILocation's lineNumber starts at 0, but changedLines start at 1.
|
1057
|
+
return changedLines.has(uiLocation.lineNumber + 1);
|
1058
|
+
}
|
1059
|
+
const formattedLineNumber =
|
1060
|
+
formattedCurrentMapping.originalToFormatted(uiLocation.lineNumber, uiLocation.columnNumber)[0];
|
1061
|
+
return changedLines.has(formattedLineNumber + 1);
|
1040
1062
|
}
|
1041
1063
|
|
1042
1064
|
private async refreshChangedLines(uiSourceCode: Workspace.UISourceCode.UISourceCode): Promise<void> {
|
1043
|
-
const
|
1065
|
+
const changeTracker = this.#urlToChangeTracker.get(uiSourceCode.url());
|
1066
|
+
if (!changeTracker) {
|
1067
|
+
return;
|
1068
|
+
}
|
1069
|
+
const diffResponse =
|
1070
|
+
await WorkspaceDiff.WorkspaceDiff.workspaceDiff().requestDiff(uiSourceCode, {shouldFormatDiff: true});
|
1044
1071
|
const changedLines = new Set<number>();
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1072
|
+
changeTracker.changedLines = changedLines;
|
1073
|
+
if (!diffResponse) {
|
1074
|
+
return;
|
1075
|
+
}
|
1076
|
+
const {diff, formattedCurrentMapping} = diffResponse;
|
1077
|
+
const {rows} = DiffView.DiffView.buildDiffRows(diff);
|
1078
|
+
for (const row of rows) {
|
1079
|
+
if (row.type === DiffView.DiffView.RowType.Addition) {
|
1080
|
+
changedLines.add(row.currentLineNumber);
|
1051
1081
|
}
|
1052
1082
|
}
|
1053
|
-
|
1083
|
+
changeTracker.formattedCurrentMapping = formattedCurrentMapping;
|
1084
|
+
}
|
1085
|
+
|
1086
|
+
private async getFormattedChanges(): Promise<string> {
|
1087
|
+
let allChanges = '';
|
1088
|
+
for (const [url, {uiSourceCode}] of this.#urlToChangeTracker) {
|
1089
|
+
const diffResponse =
|
1090
|
+
await WorkspaceDiff.WorkspaceDiff.workspaceDiff().requestDiff(uiSourceCode, {shouldFormatDiff: true});
|
1091
|
+
// Diff array with real diff will contain at least 2 lines.
|
1092
|
+
if (!diffResponse || diffResponse?.diff.length < 2) {
|
1093
|
+
continue;
|
1094
|
+
}
|
1095
|
+
const changes = await formatCSSChangesFromDiff(diffResponse.diff);
|
1096
|
+
if (changes.length > 0) {
|
1097
|
+
allChanges += `/* ${escapeUrlAsCssComment(url)} */\n\n${changes}\n\n`;
|
1098
|
+
}
|
1099
|
+
}
|
1100
|
+
|
1101
|
+
return allChanges;
|
1054
1102
|
}
|
1055
1103
|
|
1056
1104
|
private clipboardCopy(_event: Event): void {
|
@@ -1146,6 +1194,34 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
1146
1194
|
}
|
1147
1195
|
}
|
1148
1196
|
}
|
1197
|
+
|
1198
|
+
private createCopyAllChangesButton(): UI.Toolbar.ToolbarButton {
|
1199
|
+
const copyAllIcon = new IconButton.Icon.Icon();
|
1200
|
+
copyAllIcon.data = {
|
1201
|
+
iconName: 'ic_changes',
|
1202
|
+
color: 'var(--color-text-secondary)',
|
1203
|
+
width: '18px',
|
1204
|
+
height: '18px',
|
1205
|
+
};
|
1206
|
+
const copyAllChangesButton = new UI.Toolbar.ToolbarButton(i18nString(UIStrings.copyAllCSSChanges), copyAllIcon);
|
1207
|
+
// TODO(1296947): implement a dedicated component to share between all copy buttons
|
1208
|
+
copyAllChangesButton.element.setAttribute('data-content', i18nString(UIStrings.copiedToClipboard));
|
1209
|
+
let timeout: number|undefined;
|
1210
|
+
copyAllChangesButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, async () => {
|
1211
|
+
const allChanges = await this.getFormattedChanges();
|
1212
|
+
Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(allChanges);
|
1213
|
+
if (timeout) {
|
1214
|
+
clearTimeout(timeout);
|
1215
|
+
timeout = undefined;
|
1216
|
+
}
|
1217
|
+
copyAllChangesButton.element.classList.add('copied-to-clipboard');
|
1218
|
+
timeout = window.setTimeout(() => {
|
1219
|
+
copyAllChangesButton.element.classList.remove('copied-to-clipboard');
|
1220
|
+
timeout = undefined;
|
1221
|
+
}, 2000);
|
1222
|
+
});
|
1223
|
+
return copyAllChangesButton;
|
1224
|
+
}
|
1149
1225
|
}
|
1150
1226
|
|
1151
1227
|
export const enum Events {
|
@@ -1162,6 +1238,109 @@ export type EventTypes = {
|
|
1162
1238
|
[Events.StylesUpdateCompleted]: StylesUpdateCompletedEvent,
|
1163
1239
|
};
|
1164
1240
|
|
1241
|
+
type ChangeTracker = {
|
1242
|
+
uiSourceCode: Workspace.UISourceCode.UISourceCode,
|
1243
|
+
changedLines: Set<number>,
|
1244
|
+
diffChangeCallback: () => Promise<void>,
|
1245
|
+
formattedCurrentMapping?: Formatter.ScriptFormatter.FormatterSourceMapping,
|
1246
|
+
};
|
1247
|
+
|
1248
|
+
export async function formatCSSChangesFromDiff(diff: Diff.Diff.DiffArray): Promise<string> {
|
1249
|
+
const {originalLines, currentLines, rows} = DiffView.DiffView.buildDiffRows(diff);
|
1250
|
+
|
1251
|
+
const {propertyToSelector: originalPropertyToSelector, ruleToSelector: originalRuleToSelector} =
|
1252
|
+
await buildPropertyRuleMaps(originalLines.join('\n'));
|
1253
|
+
const {propertyToSelector: currentPropertyToSelector, ruleToSelector: currentRuleToSelector} =
|
1254
|
+
await buildPropertyRuleMaps(currentLines.join('\n'));
|
1255
|
+
let changes = '';
|
1256
|
+
let recordedOriginalSelector, recordedCurrentSelector;
|
1257
|
+
for (const {currentLineNumber, originalLineNumber, type} of rows) {
|
1258
|
+
// diff line arrays starts at 0, but line numbers start at 1.
|
1259
|
+
const currentLineIndex = currentLineNumber - 1;
|
1260
|
+
const originalLineIndex = originalLineNumber - 1;
|
1261
|
+
switch (type) {
|
1262
|
+
case DiffView.DiffView.RowType.Deletion: {
|
1263
|
+
const originalLine = originalLines[originalLineIndex].trim();
|
1264
|
+
if (originalRuleToSelector.has(originalLineIndex)) {
|
1265
|
+
changes += `/* ${originalLine} { */\n`;
|
1266
|
+
recordedOriginalSelector = originalLine;
|
1267
|
+
continue;
|
1268
|
+
}
|
1269
|
+
|
1270
|
+
const originalSelector = originalPropertyToSelector.get(originalLineIndex);
|
1271
|
+
if (!originalSelector) {
|
1272
|
+
continue;
|
1273
|
+
}
|
1274
|
+
if (originalSelector !== recordedOriginalSelector && originalSelector !== recordedCurrentSelector) {
|
1275
|
+
if (recordedOriginalSelector || recordedCurrentSelector) {
|
1276
|
+
changes += '}\n\n';
|
1277
|
+
}
|
1278
|
+
changes += `${originalSelector} {\n`;
|
1279
|
+
}
|
1280
|
+
recordedOriginalSelector = originalSelector;
|
1281
|
+
changes += ` /* ${originalLine} */\n`;
|
1282
|
+
break;
|
1283
|
+
}
|
1284
|
+
case DiffView.DiffView.RowType.Addition: {
|
1285
|
+
const currentLine = currentLines[currentLineIndex].trim();
|
1286
|
+
if (currentRuleToSelector.has(currentLineIndex)) {
|
1287
|
+
changes += `${currentLine} {\n`;
|
1288
|
+
recordedCurrentSelector = currentLine;
|
1289
|
+
continue;
|
1290
|
+
}
|
1291
|
+
|
1292
|
+
const currentSelector = currentPropertyToSelector.get(currentLineIndex);
|
1293
|
+
if (!currentSelector) {
|
1294
|
+
continue;
|
1295
|
+
}
|
1296
|
+
if (currentSelector !== recordedOriginalSelector && currentSelector !== recordedCurrentSelector) {
|
1297
|
+
if (recordedOriginalSelector || recordedCurrentSelector) {
|
1298
|
+
changes += '}\n\n';
|
1299
|
+
}
|
1300
|
+
changes += `${currentSelector} {\n`;
|
1301
|
+
}
|
1302
|
+
recordedCurrentSelector = currentSelector;
|
1303
|
+
changes += ` ${currentLine}\n`;
|
1304
|
+
break;
|
1305
|
+
}
|
1306
|
+
default:
|
1307
|
+
break;
|
1308
|
+
}
|
1309
|
+
}
|
1310
|
+
if (changes.length > 0) {
|
1311
|
+
changes += '}';
|
1312
|
+
}
|
1313
|
+
return changes;
|
1314
|
+
}
|
1315
|
+
|
1316
|
+
async function buildPropertyRuleMaps(content: string):
|
1317
|
+
Promise<{propertyToSelector: Map<number, string>, ruleToSelector: Map<number, string>}> {
|
1318
|
+
const rules = await new Promise<Formatter.FormatterWorkerPool.CSSRule[]>(res => {
|
1319
|
+
const rules: Formatter.FormatterWorkerPool.CSSRule[] = [];
|
1320
|
+
Formatter.FormatterWorkerPool.formatterWorkerPool().parseCSS(content, (isLastChunk, currentRules) => {
|
1321
|
+
rules.push(...currentRules);
|
1322
|
+
if (isLastChunk) {
|
1323
|
+
res(rules);
|
1324
|
+
}
|
1325
|
+
});
|
1326
|
+
});
|
1327
|
+
const propertyToSelector = new Map<number, string>();
|
1328
|
+
const ruleToSelector = new Map<number, string>();
|
1329
|
+
for (const rule of rules) {
|
1330
|
+
if ('styleRange' in rule) {
|
1331
|
+
const selector = rule.selectorText.split('\n').pop()?.trim();
|
1332
|
+
if (!selector) {
|
1333
|
+
continue;
|
1334
|
+
}
|
1335
|
+
ruleToSelector.set(rule.styleRange.startLine, selector);
|
1336
|
+
for (const property of rule.properties) {
|
1337
|
+
propertyToSelector.set(property.range.startLine, selector);
|
1338
|
+
}
|
1339
|
+
}
|
1340
|
+
}
|
1341
|
+
return {propertyToSelector, ruleToSelector};
|
1342
|
+
}
|
1343
|
+
|
1165
1344
|
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
|
1166
1345
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
1167
1346
|
export const _maxLinkLength = 23;
|
@@ -2298,9 +2477,9 @@ export class StylePropertiesSection {
|
|
2298
2477
|
return true;
|
2299
2478
|
}
|
2300
2479
|
|
2301
|
-
private editingMediaCommitted(
|
2480
|
+
private async editingMediaCommitted(
|
2302
2481
|
query: SDK.CSSQuery.CSSQuery, element: Element, newContent: string, _oldContent: string,
|
2303
|
-
_context: Context|undefined, _moveDirection: string): void {
|
2482
|
+
_context: Context|undefined, _moveDirection: string): Promise<void> {
|
2304
2483
|
this.parentPane.setEditingStyle(false);
|
2305
2484
|
this.editingMediaFinished(element);
|
2306
2485
|
|
@@ -2308,7 +2487,20 @@ export class StylePropertiesSection {
|
|
2308
2487
|
newContent = newContent.trim();
|
2309
2488
|
}
|
2310
2489
|
|
2311
|
-
|
2490
|
+
// This gets deleted in finishOperation(), which is called both on success and failure.
|
2491
|
+
this.parentPane.setUserOperation(true);
|
2492
|
+
const cssModel = this.parentPane.cssModel();
|
2493
|
+
if (cssModel && query.styleSheetId) {
|
2494
|
+
const range = query.range as TextUtils.TextRange.TextRange;
|
2495
|
+
let success = false;
|
2496
|
+
if (query instanceof SDK.CSSContainerQuery.CSSContainerQuery) {
|
2497
|
+
success = await cssModel.setContainerQueryText(query.styleSheetId, range, newContent);
|
2498
|
+
} else if (query instanceof SDK.CSSSupports.CSSSupports) {
|
2499
|
+
success = await cssModel.setSupportsText(query.styleSheetId, range, newContent);
|
2500
|
+
} else {
|
2501
|
+
success = await cssModel.setMediaText(query.styleSheetId, range, newContent);
|
2502
|
+
}
|
2503
|
+
|
2312
2504
|
if (success) {
|
2313
2505
|
this.matchedStyles.resetActiveProperties();
|
2314
2506
|
this.parentPane.refreshUpdate(this);
|
@@ -2316,16 +2508,6 @@ export class StylePropertiesSection {
|
|
2316
2508
|
this.parentPane.setUserOperation(false);
|
2317
2509
|
this.editingMediaTextCommittedForTest();
|
2318
2510
|
}
|
2319
|
-
|
2320
|
-
// This gets deleted in finishOperation(), which is called both on success and failure.
|
2321
|
-
this.parentPane.setUserOperation(true);
|
2322
|
-
const cssModel = this.parentPane.cssModel();
|
2323
|
-
if (cssModel && query.styleSheetId) {
|
2324
|
-
const setQueryText =
|
2325
|
-
query instanceof SDK.CSSMedia.CSSMedia ? cssModel.setMediaText : cssModel.setContainerQueryText;
|
2326
|
-
void setQueryText.call(cssModel, query.styleSheetId, (query.range as TextUtils.TextRange.TextRange), newContent)
|
2327
|
-
.then(userCallback.bind(this));
|
2328
|
-
}
|
2329
2511
|
}
|
2330
2512
|
|
2331
2513
|
private editingMediaTextCommittedForTest(): void {
|
@@ -3052,6 +3234,14 @@ export function unescapeCssString(input: string): string {
|
|
3052
3234
|
});
|
3053
3235
|
}
|
3054
3236
|
|
3237
|
+
export function escapeUrlAsCssComment(urlText: string): string {
|
3238
|
+
const url = new URL(urlText);
|
3239
|
+
if (url.search) {
|
3240
|
+
return `${url.origin}${url.pathname}${url.search.replaceAll('*/', '*%2F')}${url.hash}`;
|
3241
|
+
}
|
3242
|
+
return url.toString();
|
3243
|
+
}
|
3244
|
+
|
3055
3245
|
export class StylesSidebarPropertyRenderer {
|
3056
3246
|
private rule: SDK.CSSRule.CSSRule|null;
|
3057
3247
|
private node: SDK.DOMModel.DOMNode|null;
|
@@ -3214,13 +3404,8 @@ export class StylesSidebarPropertyRenderer {
|
|
3214
3404
|
// so that we don't have to keep two versions (original vs. trimmed) of URL
|
3215
3405
|
// at the same time, which complicates both StylesSidebarPane and StylePropertyTreeElement.
|
3216
3406
|
bypassURLTrimming: true,
|
3217
|
-
className: undefined,
|
3218
|
-
lineNumber: undefined,
|
3219
|
-
columnNumber: undefined,
|
3220
3407
|
showColumnNumber: false,
|
3221
3408
|
inlineFrameIndex: 0,
|
3222
|
-
maxLength: undefined,
|
3223
|
-
tabStop: undefined,
|
3224
3409
|
}),
|
3225
3410
|
hrefUrl || url);
|
3226
3411
|
container.appendChild(link);
|
@@ -256,7 +256,7 @@ export abstract class AffectedResourcesView extends UI.TreeOutline.TreeElement {
|
|
256
256
|
const linkifier = new Components.Linkifier.Linkifier(maxLengthForDisplayedURLs);
|
257
257
|
const sourceAnchor = linkifier.linkifyScriptLocation(
|
258
258
|
target || null, sourceLocation.scriptId || null, sourceLocation.url, sourceLocation.lineNumber,
|
259
|
-
{columnNumber: sourceLocation.columnNumber, inlineFrameIndex: 0
|
259
|
+
{columnNumber: sourceLocation.columnNumber, inlineFrameIndex: 0});
|
260
260
|
sourceCodeLocation.appendChild(sourceAnchor);
|
261
261
|
}
|
262
262
|
element.appendChild(sourceCodeLocation);
|
@@ -36,7 +36,7 @@ export class AffectedSourcesView extends AffectedResourcesView {
|
|
36
36
|
const cellElement = document.createElement('td');
|
37
37
|
// TODO(chromium:1072331): Check feasibility of plumping through scriptId for `linkifyScriptLocation`
|
38
38
|
// to support source maps and formatted scripts.
|
39
|
-
const linkifierURLOptions =
|
39
|
+
const linkifierURLOptions = {columnNumber, lineNumber, tabStop: true, showColumnNumber: false, inlineFrameIndex: 0};
|
40
40
|
// An element created with linkifyURL can subscribe to the events
|
41
41
|
// 'click' neither 'keydown' if that key is the 'Enter' key.
|
42
42
|
// Also, this element has a context menu, so we should be able to
|
@@ -104,6 +104,26 @@ const UIStrings = {
|
|
104
104
|
*@description Tooltip text of checkbox to emulate mobile device behavior when running audits in Lighthouse
|
105
105
|
*/
|
106
106
|
applyMobileEmulationDuring: 'Apply mobile emulation during auditing',
|
107
|
+
/**
|
108
|
+
* @description ARIA label for a radio button input to select the Lighthouse mode.
|
109
|
+
*/
|
110
|
+
lighthouseMode: 'Lighthouse mode',
|
111
|
+
/**
|
112
|
+
* @description Tooltip text of a radio button to select the Lighthouse mode.
|
113
|
+
*/
|
114
|
+
runLighthouseInMode: 'Run Lighthouse in navigation, timespan, or snapshot mode',
|
115
|
+
/**
|
116
|
+
* @description Label of a radio option for a Lighthouse mode that audits a page navigation.
|
117
|
+
*/
|
118
|
+
navigation: 'Navigation',
|
119
|
+
/**
|
120
|
+
* @description Label of a radio option for a Lighthouse mode that audits user interactions over a period of time.
|
121
|
+
*/
|
122
|
+
timespan: 'Timespan',
|
123
|
+
/**
|
124
|
+
* @description Label of a radio option for a Lighthouse mode that audits the current page state.
|
125
|
+
*/
|
126
|
+
snapshot: 'Snapshot',
|
107
127
|
/**
|
108
128
|
*@description Text for the mobile platform, as opposed to desktop
|
109
129
|
*/
|
@@ -289,6 +309,7 @@ export class LighthouseController extends Common.ObjectWrapper.ObjectWrapper<Eve
|
|
289
309
|
internalDisableDeviceScreenEmulation: boolean,
|
290
310
|
emulatedFormFactor: (string|undefined),
|
291
311
|
legacyNavigation: boolean,
|
312
|
+
mode: string,
|
292
313
|
} {
|
293
314
|
const flags = {
|
294
315
|
// DevTools handles all the emulation. This tells Lighthouse to not bother with emulation.
|
@@ -301,6 +322,7 @@ export class LighthouseController extends Common.ObjectWrapper.ObjectWrapper<Eve
|
|
301
322
|
internalDisableDeviceScreenEmulation: boolean,
|
302
323
|
emulatedFormFactor: (string | undefined),
|
303
324
|
legacyNavigation: boolean,
|
325
|
+
mode: string,
|
304
326
|
};
|
305
327
|
}
|
306
328
|
|
@@ -421,6 +443,21 @@ export const RuntimeSettings: RuntimeSetting[] = [
|
|
421
443
|
],
|
422
444
|
learnMore: undefined,
|
423
445
|
},
|
446
|
+
{
|
447
|
+
setting: Common.Settings.Settings.instance().createSetting(
|
448
|
+
'lighthouse.mode', 'navigation', Common.Settings.SettingStorageType.Synced),
|
449
|
+
title: i18nLazyString(UIStrings.lighthouseMode),
|
450
|
+
description: i18nLazyString(UIStrings.runLighthouseInMode),
|
451
|
+
setFlags: (flags: Flags, value: string|boolean): void => {
|
452
|
+
flags.mode = value;
|
453
|
+
},
|
454
|
+
options: [
|
455
|
+
{label: i18nLazyString(UIStrings.navigation), value: 'navigation'},
|
456
|
+
{label: i18nLazyString(UIStrings.timespan), value: 'timespan'},
|
457
|
+
{label: i18nLazyString(UIStrings.snapshot), value: 'snapshot'},
|
458
|
+
],
|
459
|
+
learnMore: undefined,
|
460
|
+
},
|
424
461
|
{
|
425
462
|
// This setting is disabled, but we keep it around to show in the UI.
|
426
463
|
setting: Common.Settings.Settings.instance().createSetting(
|
@@ -465,6 +502,8 @@ export enum Events {
|
|
465
502
|
PageAuditabilityChanged = 'PageAuditabilityChanged',
|
466
503
|
PageWarningsChanged = 'PageWarningsChanged',
|
467
504
|
AuditProgressChanged = 'AuditProgressChanged',
|
505
|
+
RequestLighthouseTimespanStart = 'RequestLighthouseTimespanStart',
|
506
|
+
RequestLighthouseTimespanEnd = 'RequestLighthouseTimespanEnd',
|
468
507
|
RequestLighthouseStart = 'RequestLighthouseStart',
|
469
508
|
RequestLighthouseCancel = 'RequestLighthouseCancel',
|
470
509
|
}
|
@@ -485,6 +524,8 @@ export type EventTypes = {
|
|
485
524
|
[Events.PageAuditabilityChanged]: PageAuditabilityChangedEvent,
|
486
525
|
[Events.PageWarningsChanged]: PageWarningsChangedEvent,
|
487
526
|
[Events.AuditProgressChanged]: AuditProgressChangedEvent,
|
527
|
+
[Events.RequestLighthouseTimespanStart]: boolean,
|
528
|
+
[Events.RequestLighthouseTimespanEnd]: boolean,
|
488
529
|
[Events.RequestLighthouseStart]: boolean,
|
489
530
|
[Events.RequestLighthouseCancel]: void,
|
490
531
|
};
|
@@ -5,6 +5,7 @@
|
|
5
5
|
import * as Common from '../../core/common/common.js';
|
6
6
|
import * as Host from '../../core/host/host.js';
|
7
7
|
import * as i18n from '../../core/i18n/i18n.js';
|
8
|
+
import * as Root from '../../core/root/root.js';
|
8
9
|
import * as SDK from '../../core/sdk/sdk.js';
|
9
10
|
import * as EmulationModel from '../../models/emulation/emulation.js';
|
10
11
|
import * as UI from '../../ui/legacy/legacy.js';
|
@@ -13,6 +14,7 @@ import * as Emulation from '../emulation/emulation.js';
|
|
13
14
|
import type {AuditProgressChangedEvent, PageAuditabilityChangedEvent, PageWarningsChangedEvent} from './LighthouseController.js';
|
14
15
|
import {Events, LighthouseController} from './LighthouseController.js';
|
15
16
|
import lighthousePanelStyles from './lighthousePanel.css.js';
|
17
|
+
import type {LighthouseRun} from './LighthouseProtocolService.js';
|
16
18
|
import {ProtocolService} from './LighthouseProtocolService.js';
|
17
19
|
|
18
20
|
import type {ReportJSON, RunnerResultArtifacts} from './LighthouseReporterTypes.js';
|
@@ -20,7 +22,9 @@ import * as LighthouseReport from '../../third_party/lighthouse/report/report.js
|
|
20
22
|
import {LighthouseReportRenderer, LighthouseReportUIFeatures} from './LighthouseReportRenderer.js';
|
21
23
|
import {Item, ReportSelector} from './LighthouseReportSelector.js';
|
22
24
|
import {StartView} from './LighthouseStartView.js';
|
25
|
+
import {StartViewFR} from './LighthouseStartViewFR.js';
|
23
26
|
import {StatusView} from './LighthouseStatusView.js';
|
27
|
+
import {TimespanView} from './LighthouseTimespanView.js';
|
24
28
|
|
25
29
|
const UIStrings = {
|
26
30
|
/**
|
@@ -63,6 +67,7 @@ export class LighthousePanel extends UI.Panel.Panel {
|
|
63
67
|
private readonly controller: LighthouseController;
|
64
68
|
private readonly startView: StartView;
|
65
69
|
private readonly statusView: StatusView;
|
70
|
+
private readonly timespanView: TimespanView|null;
|
66
71
|
private warningText: Nullable<string>;
|
67
72
|
private unauditableExplanation: Nullable<string>;
|
68
73
|
private readonly cachedRenderedReports: Map<ReportJSON, HTMLElement>;
|
@@ -79,13 +84,20 @@ export class LighthousePanel extends UI.Panel.Panel {
|
|
79
84
|
network: {conditions: SDK.NetworkManager.Conditions},
|
80
85
|
};
|
81
86
|
private isLHAttached?: boolean;
|
87
|
+
private currentLighthouseRun?: LighthouseRun;
|
82
88
|
|
83
89
|
private constructor() {
|
84
90
|
super('lighthouse');
|
85
91
|
|
86
92
|
this.protocolService = new ProtocolService();
|
87
93
|
this.controller = new LighthouseController(this.protocolService);
|
88
|
-
|
94
|
+
if (Root.Runtime.experiments.isEnabled('lighthousePanelFR')) {
|
95
|
+
this.startView = new StartViewFR(this.controller);
|
96
|
+
this.timespanView = new TimespanView(this.controller);
|
97
|
+
} else {
|
98
|
+
this.startView = new StartView(this.controller);
|
99
|
+
this.timespanView = null;
|
100
|
+
}
|
89
101
|
this.statusView = new StatusView(this.controller);
|
90
102
|
|
91
103
|
this.warningText = null;
|
@@ -99,12 +111,10 @@ export class LighthousePanel extends UI.Panel.Panel {
|
|
99
111
|
this.controller.addEventListener(Events.PageAuditabilityChanged, this.refreshStartAuditUI.bind(this));
|
100
112
|
this.controller.addEventListener(Events.PageWarningsChanged, this.refreshWarningsUI.bind(this));
|
101
113
|
this.controller.addEventListener(Events.AuditProgressChanged, this.refreshStatusUI.bind(this));
|
102
|
-
this.controller.addEventListener(Events.
|
103
|
-
|
104
|
-
|
105
|
-
this.controller.addEventListener(Events.RequestLighthouseCancel,
|
106
|
-
void this.cancelLighthouse();
|
107
|
-
});
|
114
|
+
this.controller.addEventListener(Events.RequestLighthouseTimespanStart, this.onLighthouseTimespanStart.bind(this));
|
115
|
+
this.controller.addEventListener(Events.RequestLighthouseTimespanEnd, this.onLighthouseTimespanEnd.bind(this));
|
116
|
+
this.controller.addEventListener(Events.RequestLighthouseStart, this.onLighthouseStart.bind(this));
|
117
|
+
this.controller.addEventListener(Events.RequestLighthouseCancel, this.onLighthouseCancel.bind(this));
|
108
118
|
|
109
119
|
this.renderToolbar();
|
110
120
|
this.auditResultsElement = this.contentElement.createChild('div', 'lighthouse-results-container');
|
@@ -126,6 +136,27 @@ export class LighthousePanel extends UI.Panel.Panel {
|
|
126
136
|
return Events;
|
127
137
|
}
|
128
138
|
|
139
|
+
private async onLighthouseTimespanStart(): Promise<void> {
|
140
|
+
this.timespanView?.show(this.contentElement);
|
141
|
+
await this.startLighthouse();
|
142
|
+
this.timespanView?.ready();
|
143
|
+
}
|
144
|
+
|
145
|
+
private async onLighthouseTimespanEnd(): Promise<void> {
|
146
|
+
this.timespanView?.hide();
|
147
|
+
await this.collectLighthouseResults();
|
148
|
+
}
|
149
|
+
|
150
|
+
private async onLighthouseStart(): Promise<void> {
|
151
|
+
await this.startLighthouse();
|
152
|
+
await this.collectLighthouseResults();
|
153
|
+
}
|
154
|
+
|
155
|
+
private async onLighthouseCancel(): Promise<void> {
|
156
|
+
this.timespanView?.hide();
|
157
|
+
void this.cancelLighthouse();
|
158
|
+
}
|
159
|
+
|
129
160
|
private refreshWarningsUI(evt: Common.EventTarget.EventTargetEvent<PageWarningsChangedEvent>): void {
|
130
161
|
// PageWarningsChanged fires multiple times during an audit, which we want to ignore.
|
131
162
|
if (this.isLHAttached) {
|
@@ -142,6 +173,8 @@ export class LighthousePanel extends UI.Panel.Panel {
|
|
142
173
|
return;
|
143
174
|
}
|
144
175
|
|
176
|
+
this.startView.updateStartButton();
|
177
|
+
|
145
178
|
this.unauditableExplanation = evt.data.helpText;
|
146
179
|
this.startView.setUnauditableExplanation(evt.data.helpText);
|
147
180
|
this.startView.setStartButtonEnabled(!evt.data.helpText);
|
@@ -342,11 +375,30 @@ export class LighthousePanel extends UI.Panel.Panel {
|
|
342
375
|
const categoryIDs = this.controller.getCategoryIDs();
|
343
376
|
const flags = this.controller.getFlags();
|
344
377
|
|
378
|
+
this.currentLighthouseRun = {inspectedURL, categoryIDs, flags};
|
379
|
+
|
345
380
|
await this.setupEmulationAndProtocolConnection();
|
346
381
|
|
347
|
-
|
382
|
+
if (flags.mode === 'timespan') {
|
383
|
+
await this.protocolService.startTimespan(this.currentLighthouseRun);
|
384
|
+
}
|
385
|
+
|
386
|
+
} catch (err) {
|
387
|
+
await this.resetEmulationAndProtocolConnection();
|
388
|
+
if (err instanceof Error) {
|
389
|
+
this.statusView.renderBugReport(err);
|
390
|
+
}
|
391
|
+
}
|
392
|
+
}
|
393
|
+
|
394
|
+
private async collectLighthouseResults(): Promise<void> {
|
395
|
+
try {
|
396
|
+
if (!this.currentLighthouseRun) {
|
397
|
+
throw new Error('Lighthouse is not started');
|
398
|
+
}
|
399
|
+
this.renderStatusView(this.currentLighthouseRun.inspectedURL);
|
348
400
|
|
349
|
-
const lighthouseResponse = await this.protocolService.
|
401
|
+
const lighthouseResponse = await this.protocolService.collectLighthouseResults(this.currentLighthouseRun);
|
350
402
|
|
351
403
|
if (lighthouseResponse && lighthouseResponse.fatal) {
|
352
404
|
const error = new Error(lighthouseResponse.message);
|
@@ -369,10 +421,13 @@ export class LighthousePanel extends UI.Panel.Panel {
|
|
369
421
|
if (err instanceof Error) {
|
370
422
|
this.statusView.renderBugReport(err);
|
371
423
|
}
|
424
|
+
} finally {
|
425
|
+
this.currentLighthouseRun = undefined;
|
372
426
|
}
|
373
427
|
}
|
374
428
|
|
375
429
|
private async cancelLighthouse(): Promise<void> {
|
430
|
+
this.currentLighthouseRun = undefined;
|
376
431
|
this.statusView.updateStatus(i18nString(UIStrings.cancelling));
|
377
432
|
await this.resetEmulationAndProtocolConnection();
|
378
433
|
this.renderStartView();
|