chrome-devtools-frontend 1.0.970302 → 1.0.971727
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 +11 -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 +33 -4
- package/front_end/core/host/UserMetrics.ts +4 -1
- package/front_end/core/i18n/locales/en-US.json +21 -3
- package/front_end/core/i18n/locales/en-XL.json +21 -3
- 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 +21 -16
- package/front_end/core/sdk/NetworkManager.ts +17 -3
- package/front_end/core/sdk/NetworkRequest.ts +16 -5
- package/front_end/core/sdk/Resource.ts +10 -10
- package/front_end/core/sdk/ResourceTreeModel.ts +18 -13
- package/front_end/core/sdk/Script.ts +10 -10
- package/front_end/core/sdk/SourceMap.ts +3 -1
- package/front_end/entrypoints/lighthouse_worker/LighthouseService.ts +15 -6
- 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 +21 -0
- 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/StylesSourceMapping.ts +2 -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 +3 -33
- package/front_end/models/persistence/FileSystemWorkspaceBinding.ts +12 -10
- package/front_end/models/persistence/IsolatedFileSystem.ts +26 -22
- package/front_end/models/persistence/NetworkPersistenceManager.ts +8 -4
- package/front_end/models/persistence/PersistenceActions.ts +1 -4
- package/front_end/models/persistence/PlatformFileSystem.ts +6 -5
- 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 +5 -4
- package/front_end/models/workspace/UISourceCode.ts +18 -21
- package/front_end/models/workspace/WorkspaceImpl.ts +3 -1
- package/front_end/panels/application/ServiceWorkerCacheViews.ts +1 -1
- package/front_end/panels/browser_debugger/categorizedBreakpointsSidebarPane.css +0 -1
- 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 +214 -41
- 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 +30 -0
- package/front_end/panels/lighthouse/LighthousePanel.ts +7 -1
- package/front_end/panels/lighthouse/LighthouseProtocolService.ts +6 -1
- package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +0 -5
- package/front_end/panels/lighthouse/LighthouseStartView.ts +2 -2
- package/front_end/panels/lighthouse/LighthouseStartViewFR.ts +39 -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 +5 -5
- package/front_end/panels/snippets/SnippetsQuickOpen.ts +1 -1
- package/front_end/panels/sources/DebuggerPlugin.ts +5 -4
- package/front_end/panels/sources/NavigatorView.ts +10 -6
- package/front_end/panels/sources/ScopeChainSidebarPane.ts +6 -3
- package/front_end/panels/sources/SourcesNavigator.ts +7 -1
- package/front_end/panels/sources/TabbedEditorContainer.ts +9 -0
- 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/ui/legacy/ViewManager.ts +2 -1
- package/front_end/ui/legacy/closeButton.css +0 -1
- 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 -3
- package/front_end/ui/legacy/toolbar.css +28 -3
- package/package.json +1 -1
- 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,7 +1043,7 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
1027
1043
|
if (!url) {
|
1028
1044
|
return false;
|
1029
1045
|
}
|
1030
|
-
const changedLines = this.#
|
1046
|
+
const changedLines = this.#urlToChangeTracker.get(url)?.changedLines;
|
1031
1047
|
if (!changedLines) {
|
1032
1048
|
return false;
|
1033
1049
|
}
|
@@ -1040,6 +1056,10 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
1040
1056
|
}
|
1041
1057
|
|
1042
1058
|
private async refreshChangedLines(uiSourceCode: Workspace.UISourceCode.UISourceCode): Promise<void> {
|
1059
|
+
const changeTracker = this.#urlToChangeTracker.get(uiSourceCode.url());
|
1060
|
+
if (!changeTracker) {
|
1061
|
+
return;
|
1062
|
+
}
|
1043
1063
|
const diff = await WorkspaceDiff.WorkspaceDiff.workspaceDiff().requestDiff(uiSourceCode, {shouldFormatDiff: true});
|
1044
1064
|
const changedLines = new Set<number>();
|
1045
1065
|
if (diff && diff.length > 0) {
|
@@ -1050,7 +1070,24 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
1050
1070
|
}
|
1051
1071
|
}
|
1052
1072
|
}
|
1053
|
-
|
1073
|
+
changeTracker.changedLines = changedLines;
|
1074
|
+
}
|
1075
|
+
|
1076
|
+
private async getFormattedChanges(): Promise<string> {
|
1077
|
+
let allChanges = '';
|
1078
|
+
for (const [url, {uiSourceCode}] of this.#urlToChangeTracker) {
|
1079
|
+
const diff =
|
1080
|
+
await WorkspaceDiff.WorkspaceDiff.workspaceDiff().requestDiff(uiSourceCode, {shouldFormatDiff: true});
|
1081
|
+
if (!diff || diff.length < 2) {
|
1082
|
+
continue;
|
1083
|
+
}
|
1084
|
+
const changes = await formatCSSChangesFromDiff(diff);
|
1085
|
+
if (changes.length > 0) {
|
1086
|
+
allChanges += `/* ${escapeUrlAsCssComment(url)} */\n\n${changes}\n\n`;
|
1087
|
+
}
|
1088
|
+
}
|
1089
|
+
|
1090
|
+
return allChanges;
|
1054
1091
|
}
|
1055
1092
|
|
1056
1093
|
private clipboardCopy(_event: Event): void {
|
@@ -1146,6 +1183,34 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
1146
1183
|
}
|
1147
1184
|
}
|
1148
1185
|
}
|
1186
|
+
|
1187
|
+
private createCopyAllChangesButton(): UI.Toolbar.ToolbarButton {
|
1188
|
+
const copyAllIcon = new IconButton.Icon.Icon();
|
1189
|
+
copyAllIcon.data = {
|
1190
|
+
iconName: 'ic_changes',
|
1191
|
+
color: 'var(--color-text-secondary)',
|
1192
|
+
width: '18px',
|
1193
|
+
height: '18px',
|
1194
|
+
};
|
1195
|
+
const copyAllChangesButton = new UI.Toolbar.ToolbarButton(i18nString(UIStrings.copyAllCSSChanges), copyAllIcon);
|
1196
|
+
// TODO(1296947): implement a dedicated component to share between all copy buttons
|
1197
|
+
copyAllChangesButton.element.setAttribute('data-content', i18nString(UIStrings.copiedToClipboard));
|
1198
|
+
let timeout: number|undefined;
|
1199
|
+
copyAllChangesButton.addEventListener(UI.Toolbar.ToolbarButton.Events.Click, async () => {
|
1200
|
+
const allChanges = await this.getFormattedChanges();
|
1201
|
+
Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(allChanges);
|
1202
|
+
if (timeout) {
|
1203
|
+
clearTimeout(timeout);
|
1204
|
+
timeout = undefined;
|
1205
|
+
}
|
1206
|
+
copyAllChangesButton.element.classList.add('copied-to-clipboard');
|
1207
|
+
timeout = window.setTimeout(() => {
|
1208
|
+
copyAllChangesButton.element.classList.remove('copied-to-clipboard');
|
1209
|
+
timeout = undefined;
|
1210
|
+
}, 2000);
|
1211
|
+
});
|
1212
|
+
return copyAllChangesButton;
|
1213
|
+
}
|
1149
1214
|
}
|
1150
1215
|
|
1151
1216
|
export const enum Events {
|
@@ -1162,6 +1227,108 @@ export type EventTypes = {
|
|
1162
1227
|
[Events.StylesUpdateCompleted]: StylesUpdateCompletedEvent,
|
1163
1228
|
};
|
1164
1229
|
|
1230
|
+
type ChangeTracker = {
|
1231
|
+
uiSourceCode: Workspace.UISourceCode.UISourceCode,
|
1232
|
+
changedLines: Set<number>,
|
1233
|
+
diffChangeCallback: () => Promise<void>,
|
1234
|
+
};
|
1235
|
+
|
1236
|
+
export async function formatCSSChangesFromDiff(diff: Diff.Diff.DiffArray): Promise<string> {
|
1237
|
+
const {originalLines, currentLines, rows} = DiffView.DiffView.buildDiffRows(diff);
|
1238
|
+
|
1239
|
+
const {propertyToSelector: originalPropertyToSelector, ruleToSelector: originalRuleToSelector} =
|
1240
|
+
await buildPropertyRuleMaps(originalLines.join('\n'));
|
1241
|
+
const {propertyToSelector: currentPropertyToSelector, ruleToSelector: currentRuleToSelector} =
|
1242
|
+
await buildPropertyRuleMaps(currentLines.join('\n'));
|
1243
|
+
let changes = '';
|
1244
|
+
let recordedOriginalSelector, recordedCurrentSelector;
|
1245
|
+
for (const {currentLineNumber, originalLineNumber, type} of rows) {
|
1246
|
+
// diff line arrays starts at 0, but line numbers start at 1.
|
1247
|
+
const currentLineIndex = currentLineNumber - 1;
|
1248
|
+
const originalLineIndex = originalLineNumber - 1;
|
1249
|
+
switch (type) {
|
1250
|
+
case DiffView.DiffView.RowType.Deletion: {
|
1251
|
+
const originalLine = originalLines[originalLineIndex].trim();
|
1252
|
+
if (originalRuleToSelector.has(originalLineIndex)) {
|
1253
|
+
changes += `/* ${originalLine} { */\n`;
|
1254
|
+
recordedOriginalSelector = originalLine;
|
1255
|
+
continue;
|
1256
|
+
}
|
1257
|
+
|
1258
|
+
const originalSelector = originalPropertyToSelector.get(originalLineIndex);
|
1259
|
+
if (!originalSelector) {
|
1260
|
+
continue;
|
1261
|
+
}
|
1262
|
+
if (originalSelector !== recordedOriginalSelector && originalSelector !== recordedCurrentSelector) {
|
1263
|
+
if (recordedOriginalSelector || recordedCurrentSelector) {
|
1264
|
+
changes += '}\n\n';
|
1265
|
+
}
|
1266
|
+
changes += `${originalSelector} {\n`;
|
1267
|
+
}
|
1268
|
+
recordedOriginalSelector = originalSelector;
|
1269
|
+
changes += ` /* ${originalLine} */\n`;
|
1270
|
+
break;
|
1271
|
+
}
|
1272
|
+
case DiffView.DiffView.RowType.Addition: {
|
1273
|
+
const currentLine = currentLines[currentLineIndex].trim();
|
1274
|
+
if (currentRuleToSelector.has(currentLineIndex)) {
|
1275
|
+
changes += `${currentLine} {\n`;
|
1276
|
+
recordedCurrentSelector = currentLine;
|
1277
|
+
continue;
|
1278
|
+
}
|
1279
|
+
|
1280
|
+
const currentSelector = currentPropertyToSelector.get(currentLineIndex);
|
1281
|
+
if (!currentSelector) {
|
1282
|
+
continue;
|
1283
|
+
}
|
1284
|
+
if (currentSelector !== recordedOriginalSelector && currentSelector !== recordedCurrentSelector) {
|
1285
|
+
if (recordedOriginalSelector || recordedCurrentSelector) {
|
1286
|
+
changes += '}\n\n';
|
1287
|
+
}
|
1288
|
+
changes += `${currentSelector} {\n`;
|
1289
|
+
}
|
1290
|
+
recordedCurrentSelector = currentSelector;
|
1291
|
+
changes += ` ${currentLine}\n`;
|
1292
|
+
break;
|
1293
|
+
}
|
1294
|
+
default:
|
1295
|
+
break;
|
1296
|
+
}
|
1297
|
+
}
|
1298
|
+
if (changes.length > 0) {
|
1299
|
+
changes += '}';
|
1300
|
+
}
|
1301
|
+
return changes;
|
1302
|
+
}
|
1303
|
+
|
1304
|
+
async function buildPropertyRuleMaps(content: string):
|
1305
|
+
Promise<{propertyToSelector: Map<number, string>, ruleToSelector: Map<number, string>}> {
|
1306
|
+
const rules = await new Promise<Formatter.FormatterWorkerPool.CSSRule[]>(res => {
|
1307
|
+
const rules: Formatter.FormatterWorkerPool.CSSRule[] = [];
|
1308
|
+
Formatter.FormatterWorkerPool.formatterWorkerPool().parseCSS(content, (isLastChunk, currentRules) => {
|
1309
|
+
rules.push(...currentRules);
|
1310
|
+
if (isLastChunk) {
|
1311
|
+
res(rules);
|
1312
|
+
}
|
1313
|
+
});
|
1314
|
+
});
|
1315
|
+
const propertyToSelector = new Map<number, string>();
|
1316
|
+
const ruleToSelector = new Map<number, string>();
|
1317
|
+
for (const rule of rules) {
|
1318
|
+
if ('styleRange' in rule) {
|
1319
|
+
const selector = rule.selectorText.split('\n').pop()?.trim();
|
1320
|
+
if (!selector) {
|
1321
|
+
continue;
|
1322
|
+
}
|
1323
|
+
ruleToSelector.set(rule.styleRange.startLine, selector);
|
1324
|
+
for (const property of rule.properties) {
|
1325
|
+
propertyToSelector.set(property.range.startLine, selector);
|
1326
|
+
}
|
1327
|
+
}
|
1328
|
+
}
|
1329
|
+
return {propertyToSelector, ruleToSelector};
|
1330
|
+
}
|
1331
|
+
|
1165
1332
|
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
|
1166
1333
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
1167
1334
|
export const _maxLinkLength = 23;
|
@@ -2298,9 +2465,9 @@ export class StylePropertiesSection {
|
|
2298
2465
|
return true;
|
2299
2466
|
}
|
2300
2467
|
|
2301
|
-
private editingMediaCommitted(
|
2468
|
+
private async editingMediaCommitted(
|
2302
2469
|
query: SDK.CSSQuery.CSSQuery, element: Element, newContent: string, _oldContent: string,
|
2303
|
-
_context: Context|undefined, _moveDirection: string): void {
|
2470
|
+
_context: Context|undefined, _moveDirection: string): Promise<void> {
|
2304
2471
|
this.parentPane.setEditingStyle(false);
|
2305
2472
|
this.editingMediaFinished(element);
|
2306
2473
|
|
@@ -2308,7 +2475,20 @@ export class StylePropertiesSection {
|
|
2308
2475
|
newContent = newContent.trim();
|
2309
2476
|
}
|
2310
2477
|
|
2311
|
-
|
2478
|
+
// This gets deleted in finishOperation(), which is called both on success and failure.
|
2479
|
+
this.parentPane.setUserOperation(true);
|
2480
|
+
const cssModel = this.parentPane.cssModel();
|
2481
|
+
if (cssModel && query.styleSheetId) {
|
2482
|
+
const range = query.range as TextUtils.TextRange.TextRange;
|
2483
|
+
let success = false;
|
2484
|
+
if (query instanceof SDK.CSSContainerQuery.CSSContainerQuery) {
|
2485
|
+
success = await cssModel.setContainerQueryText(query.styleSheetId, range, newContent);
|
2486
|
+
} else if (query instanceof SDK.CSSSupports.CSSSupports) {
|
2487
|
+
success = await cssModel.setSupportsText(query.styleSheetId, range, newContent);
|
2488
|
+
} else {
|
2489
|
+
success = await cssModel.setMediaText(query.styleSheetId, range, newContent);
|
2490
|
+
}
|
2491
|
+
|
2312
2492
|
if (success) {
|
2313
2493
|
this.matchedStyles.resetActiveProperties();
|
2314
2494
|
this.parentPane.refreshUpdate(this);
|
@@ -2316,16 +2496,6 @@ export class StylePropertiesSection {
|
|
2316
2496
|
this.parentPane.setUserOperation(false);
|
2317
2497
|
this.editingMediaTextCommittedForTest();
|
2318
2498
|
}
|
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
2499
|
}
|
2330
2500
|
|
2331
2501
|
private editingMediaTextCommittedForTest(): void {
|
@@ -3052,6 +3222,14 @@ export function unescapeCssString(input: string): string {
|
|
3052
3222
|
});
|
3053
3223
|
}
|
3054
3224
|
|
3225
|
+
export function escapeUrlAsCssComment(urlText: string): string {
|
3226
|
+
const url = new URL(urlText);
|
3227
|
+
if (url.search) {
|
3228
|
+
return `${url.origin}${url.pathname}${url.search.replaceAll('*/', '*%2F')}${url.hash}`;
|
3229
|
+
}
|
3230
|
+
return url.toString();
|
3231
|
+
}
|
3232
|
+
|
3055
3233
|
export class StylesSidebarPropertyRenderer {
|
3056
3234
|
private rule: SDK.CSSRule.CSSRule|null;
|
3057
3235
|
private node: SDK.DOMModel.DOMNode|null;
|
@@ -3214,13 +3392,8 @@ export class StylesSidebarPropertyRenderer {
|
|
3214
3392
|
// so that we don't have to keep two versions (original vs. trimmed) of URL
|
3215
3393
|
// at the same time, which complicates both StylesSidebarPane and StylePropertyTreeElement.
|
3216
3394
|
bypassURLTrimming: true,
|
3217
|
-
className: undefined,
|
3218
|
-
lineNumber: undefined,
|
3219
|
-
columnNumber: undefined,
|
3220
3395
|
showColumnNumber: false,
|
3221
3396
|
inlineFrameIndex: 0,
|
3222
|
-
maxLength: undefined,
|
3223
|
-
tabStop: undefined,
|
3224
3397
|
}),
|
3225
3398
|
hrefUrl || url);
|
3226
3399
|
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,22 @@ 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 Text for Lighthouse navigation mode.
|
117
|
+
*/
|
118
|
+
navigation: 'Navigation',
|
119
|
+
/**
|
120
|
+
* @description Text for Lighthouse snapshot mode.
|
121
|
+
*/
|
122
|
+
snapshot: 'Snapshot',
|
107
123
|
/**
|
108
124
|
*@description Text for the mobile platform, as opposed to desktop
|
109
125
|
*/
|
@@ -421,6 +437,20 @@ export const RuntimeSettings: RuntimeSetting[] = [
|
|
421
437
|
],
|
422
438
|
learnMore: undefined,
|
423
439
|
},
|
440
|
+
{
|
441
|
+
setting: Common.Settings.Settings.instance().createSetting(
|
442
|
+
'lighthouse.mode', 'navigation', Common.Settings.SettingStorageType.Synced),
|
443
|
+
title: i18nLazyString(UIStrings.lighthouseMode),
|
444
|
+
description: i18nLazyString(UIStrings.runLighthouseInMode),
|
445
|
+
setFlags: (flags: Flags, value: string|boolean): void => {
|
446
|
+
flags.mode = value;
|
447
|
+
},
|
448
|
+
options: [
|
449
|
+
{label: i18nLazyString(UIStrings.navigation), value: 'navigation'},
|
450
|
+
{label: i18nLazyString(UIStrings.snapshot), value: 'snapshot'},
|
451
|
+
],
|
452
|
+
learnMore: undefined,
|
453
|
+
},
|
424
454
|
{
|
425
455
|
// This setting is disabled, but we keep it around to show in the UI.
|
426
456
|
setting: Common.Settings.Settings.instance().createSetting(
|
@@ -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';
|
@@ -20,6 +21,7 @@ import * as LighthouseReport from '../../third_party/lighthouse/report/report.js
|
|
20
21
|
import {LighthouseReportRenderer, LighthouseReportUIFeatures} from './LighthouseReportRenderer.js';
|
21
22
|
import {Item, ReportSelector} from './LighthouseReportSelector.js';
|
22
23
|
import {StartView} from './LighthouseStartView.js';
|
24
|
+
import {StartViewFR} from './LighthouseStartViewFR.js';
|
23
25
|
import {StatusView} from './LighthouseStatusView.js';
|
24
26
|
|
25
27
|
const UIStrings = {
|
@@ -85,7 +87,11 @@ export class LighthousePanel extends UI.Panel.Panel {
|
|
85
87
|
|
86
88
|
this.protocolService = new ProtocolService();
|
87
89
|
this.controller = new LighthouseController(this.protocolService);
|
88
|
-
|
90
|
+
if (Root.Runtime.experiments.isEnabled('lighthousePanelFR')) {
|
91
|
+
this.startView = new StartViewFR(this.controller);
|
92
|
+
} else {
|
93
|
+
this.startView = new StartView(this.controller);
|
94
|
+
}
|
89
95
|
this.statusView = new StatusView(this.controller);
|
90
96
|
|
91
97
|
this.warningText = null;
|
@@ -63,7 +63,12 @@ export class ProtocolService {
|
|
63
63
|
if (!this.targetInfo) {
|
64
64
|
throw new Error('Unable to get target info required for Lighthouse');
|
65
65
|
}
|
66
|
-
|
66
|
+
|
67
|
+
let mode = flags.mode as string;
|
68
|
+
if (mode === 'navigation' && flags.legacyNavigation) {
|
69
|
+
mode = 'legacyNavigation';
|
70
|
+
}
|
71
|
+
|
67
72
|
return this.sendWithResponse(mode, {
|
68
73
|
url: auditURL,
|
69
74
|
categoryIDs,
|
@@ -127,11 +127,6 @@ export class LighthouseReportRenderer extends LighthouseReport.ReportRenderer {
|
|
127
127
|
showColumnNumber: false,
|
128
128
|
inlineFrameIndex: 0,
|
129
129
|
maxLength: MaxLengthForLinks,
|
130
|
-
bypassURLTrimming: undefined,
|
131
|
-
className: undefined,
|
132
|
-
preventClick: undefined,
|
133
|
-
tabStop: undefined,
|
134
|
-
text: undefined,
|
135
130
|
});
|
136
131
|
UI.Tooltip.Tooltip.install(origHTMLElement, '');
|
137
132
|
origHTMLElement.textContent = '';
|
@@ -60,7 +60,7 @@ export class StartView extends UI.Widget.Widget {
|
|
60
60
|
return this.settingsToolbarInternal;
|
61
61
|
}
|
62
62
|
|
63
|
-
|
63
|
+
protected populateRuntimeSettingAsRadio(settingName: string, label: string, parentElement: Element): void {
|
64
64
|
const runtimeSetting = RuntimeSettings.find(item => item.setting.name === settingName);
|
65
65
|
if (!runtimeSetting || !runtimeSetting.options) {
|
66
66
|
throw new Error(`${settingName} is not a setting with options`);
|
@@ -112,7 +112,7 @@ export class StartView extends UI.Widget.Widget {
|
|
112
112
|
UI.ARIAUtils.setAccessibleName(pluginFormElements, i18nString(UIStrings.communityPluginsBeta));
|
113
113
|
}
|
114
114
|
|
115
|
-
|
115
|
+
protected render(): void {
|
116
116
|
this.populateRuntimeSettingAsToolbarCheckbox('lighthouse.legacy_navigation', this.settingsToolbarInternal);
|
117
117
|
this.populateRuntimeSettingAsToolbarCheckbox('lighthouse.clear_storage', this.settingsToolbarInternal);
|
118
118
|
this.populateRuntimeSettingAsToolbarCheckbox('lighthouse.throttling', this.settingsToolbarInternal);
|
@@ -0,0 +1,39 @@
|
|
1
|
+
// Copyright 2022 The Chromium Authors. All rights reserved.
|
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 * as UI from '../../ui/legacy/legacy.js';
|
7
|
+
|
8
|
+
import {StartView} from './LighthouseStartView.js';
|
9
|
+
|
10
|
+
const UIStrings = {
|
11
|
+
/**
|
12
|
+
* @description Text that refers to the Lighthouse mode
|
13
|
+
*/
|
14
|
+
mode: 'Mode',
|
15
|
+
};
|
16
|
+
|
17
|
+
const str_ = i18n.i18n.registerUIStrings('panels/lighthouse/LighthouseStartViewFR.ts', UIStrings);
|
18
|
+
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
19
|
+
|
20
|
+
export class StartViewFR extends StartView {
|
21
|
+
protected render(): void {
|
22
|
+
super.render();
|
23
|
+
const fragment = UI.Fragment.Fragment.build`
|
24
|
+
<div class="lighthouse-form-section">
|
25
|
+
<div class="lighthouse-form-section-label">
|
26
|
+
${i18nString(UIStrings.mode)}
|
27
|
+
</div>
|
28
|
+
<div class="lighthouse-form-elements" $="mode-form-elements"></div>
|
29
|
+
</div>
|
30
|
+
`;
|
31
|
+
|
32
|
+
// Populate the Lighthouse mode
|
33
|
+
const modeFormElements = fragment.$('mode-form-elements');
|
34
|
+
this.populateRuntimeSettingAsRadio('lighthouse.mode', i18nString(UIStrings.mode), modeFormElements);
|
35
|
+
|
36
|
+
const form = this.contentElement.querySelector('form');
|
37
|
+
form?.appendChild(fragment.element());
|
38
|
+
}
|
39
|
+
}
|
@@ -1216,7 +1216,7 @@ export class NetworkRequestNode extends NetworkNode {
|
|
1216
1216
|
} else {
|
1217
1217
|
this.linkifiedInitiatorAnchor = linkifier.linkifyScriptLocation(
|
1218
1218
|
networkManager.target(), initiator.scriptId, initiator.url, initiator.lineNumber,
|
1219
|
-
{columnNumber: initiator.columnNumber, inlineFrameIndex: 0
|
1219
|
+
{columnNumber: initiator.columnNumber, inlineFrameIndex: 0});
|
1220
1220
|
}
|
1221
1221
|
UI.Tooltip.Tooltip.install((this.linkifiedInitiatorAnchor), '');
|
1222
1222
|
cell.appendChild(this.linkifiedInitiatorAnchor);
|
@@ -310,7 +310,7 @@ export class NodeFormatter implements Formatter {
|
|
310
310
|
linkifyNode(node: ProfileDataGridNode): Element|null {
|
311
311
|
const cpuProfilerModel = this.profileView.profileHeader.cpuProfilerModel;
|
312
312
|
const target = cpuProfilerModel ? cpuProfilerModel.target() : null;
|
313
|
-
const options = {className: 'profile-node-file',
|
313
|
+
const options = {className: 'profile-node-file', inlineFrameIndex: 0};
|
314
314
|
return this.profileView.linkifier().maybeLinkifyConsoleCallFrame(target, node.profileNode.callFrame, options);
|
315
315
|
}
|
316
316
|
}
|
@@ -620,9 +620,7 @@ export class NodeFormatter implements Formatter {
|
|
620
620
|
const target = heapProfilerModel ? heapProfilerModel.target() : null;
|
621
621
|
const options = {
|
622
622
|
className: 'profile-node-file',
|
623
|
-
columnNumber: undefined,
|
624
623
|
inlineFrameIndex: 0,
|
625
|
-
tabStop: undefined,
|
626
624
|
};
|
627
625
|
return this.profileView.linkifier().maybeLinkifyConsoleCallFrame(target, node.profileNode.callFrame, options);
|
628
626
|
}
|
@@ -1301,7 +1301,6 @@ export class AllocationGridNode extends HeapSnapshotGridNode {
|
|
1301
1301
|
columnNumber: allocationNode.column - 1,
|
1302
1302
|
inlineFrameIndex: 0,
|
1303
1303
|
className: 'profile-node-file',
|
1304
|
-
tabStop: undefined,
|
1305
1304
|
});
|
1306
1305
|
urlElement.style.maxWidth = '75%';
|
1307
1306
|
cell.insertBefore(urlElement, cell.firstChild);
|
@@ -1887,10 +1887,9 @@ export class HeapAllocationStackView extends UI.Widget.Widget {
|
|
1887
1887
|
continue;
|
1888
1888
|
}
|
1889
1889
|
const target = this.heapProfilerModel ? this.heapProfilerModel.target() : null;
|
1890
|
-
const options = {columnNumber: frame.column - 1};
|
1890
|
+
const options = {columnNumber: frame.column - 1, inlineFrameIndex: 0};
|
1891
1891
|
const urlElement = this.linkifier.linkifyScriptLocation(
|
1892
|
-
target, String(frame.scriptId) as Protocol.Runtime.ScriptId, frame.scriptName, frame.line - 1,
|
1893
|
-
(options as Components.Linkifier.LinkifyOptions));
|
1892
|
+
target, String(frame.scriptId) as Protocol.Runtime.ScriptId, frame.scriptName, frame.line - 1, options);
|
1894
1893
|
frameDiv.appendChild(urlElement);
|
1895
1894
|
stackFrameToURLElement.set(frameDiv, urlElement);
|
1896
1895
|
frameDiv.addEventListener('contextmenu', this.onContextMenu.bind(this, urlElement));
|
@@ -28,12 +28,12 @@ const UIStrings = {
|
|
28
28
|
const str_ = i18n.i18n.registerUIStrings('panels/snippets/ScriptSnippetFileSystem.ts', UIStrings);
|
29
29
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
30
30
|
|
31
|
-
function escapeSnippetName(name: string):
|
32
|
-
return
|
31
|
+
function escapeSnippetName(name: string): Platform.DevToolsPath.EncodedPathString {
|
32
|
+
return Common.ParsedURL.ParsedURL.rawPathToEncodedPathString(name as Platform.DevToolsPath.RawPathString);
|
33
33
|
}
|
34
34
|
|
35
35
|
function unescapeSnippetName(name: string): string {
|
36
|
-
return
|
36
|
+
return Common.ParsedURL.ParsedURL.encodedPathToRawPathString(name as Platform.DevToolsPath.EncodedPathString);
|
37
37
|
}
|
38
38
|
|
39
39
|
export class SnippetFileSystem extends Persistence.PlatformFileSystem.PlatformFileSystem {
|
@@ -47,7 +47,7 @@ export class SnippetFileSystem extends Persistence.PlatformFileSystem.PlatformFi
|
|
47
47
|
this.snippetsSetting = Common.Settings.Settings.instance().createSetting('scriptSnippets', []);
|
48
48
|
}
|
49
49
|
|
50
|
-
initialFilePaths():
|
50
|
+
initialFilePaths(): Platform.DevToolsPath.EncodedPathString[] {
|
51
51
|
const savedSnippets: Snippet[] = this.snippetsSetting.get();
|
52
52
|
return savedSnippets.map(snippet => escapeSnippetName(snippet.name));
|
53
53
|
}
|
@@ -126,7 +126,7 @@ export class SnippetFileSystem extends Persistence.PlatformFileSystem.PlatformFi
|
|
126
126
|
return Common.ResourceType.resourceTypes.Script;
|
127
127
|
}
|
128
128
|
|
129
|
-
tooltipForURL(url:
|
129
|
+
tooltipForURL(url: Platform.DevToolsPath.UrlString): string {
|
130
130
|
return i18nString(UIStrings.linkedTo, {PH1: unescapeSnippetName(url.substring(this.path().length))});
|
131
131
|
}
|
132
132
|
|