chrome-devtools-frontend 1.0.970539 → 1.0.971140
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 +3 -0
- 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 +2 -2
- package/front_end/core/host/UserMetrics.ts +3 -1
- package/front_end/core/i18n/locales/en-US.json +21 -0
- package/front_end/core/i18n/locales/en-XL.json +21 -0
- package/front_end/core/platform/UserVisibleError.ts +28 -0
- package/front_end/core/platform/platform.ts +2 -0
- package/front_end/core/sdk/ChildTargetManager.ts +0 -1
- package/front_end/core/sdk/NetworkManager.ts +15 -1
- package/front_end/core/sdk/NetworkRequest.ts +11 -0
- package/front_end/entrypoints/lighthouse_worker/LighthouseService.ts +15 -6
- package/front_end/entrypoints/main/MainImpl.ts +3 -0
- package/front_end/models/bindings/CSSWorkspaceBinding.ts +21 -0
- package/front_end/models/persistence/Automapping.ts +1 -1
- package/front_end/models/persistence/FileSystemWorkspaceBinding.ts +2 -2
- package/front_end/panels/browser_debugger/categorizedBreakpointsSidebarPane.css +0 -1
- package/front_end/panels/elements/ElementsPanel.ts +25 -11
- package/front_end/panels/elements/StylesSidebarPane.ts +198 -23
- package/front_end/panels/elements/elementsTreeOutline.css +0 -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/LighthouseStartView.ts +2 -2
- package/front_end/panels/lighthouse/LighthouseStartViewFR.ts +39 -0
- package/front_end/panels/sensors/sensors.css +0 -1
- package/front_end/panels/sources/NavigatorView.ts +9 -5
- package/front_end/panels/sources/sources-legacy.ts +0 -13
- package/front_end/panels/sources/sourcesView.css +0 -4
- package/front_end/ui/legacy/closeButton.css +0 -1
- 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
@@ -59,6 +59,7 @@ grd_files_release_sources = [
|
|
59
59
|
"front_end/Images/flex-nowrap-icon.svg",
|
60
60
|
"front_end/Images/flex-wrap-icon.svg",
|
61
61
|
"front_end/Images/help_outline.svg",
|
62
|
+
"front_end/Images/ic_changes.svg",
|
62
63
|
"front_end/Images/ic_checkmark_16x16.svg",
|
63
64
|
"front_end/Images/ic_command_go_to_line.svg",
|
64
65
|
"front_end/Images/ic_command_go_to_symbol.svg",
|
@@ -569,6 +570,7 @@ grd_files_debug_sources = [
|
|
569
570
|
"front_end/core/i18n/time-utilities.js",
|
570
571
|
"front_end/core/platform/DevToolsPath.js",
|
571
572
|
"front_end/core/platform/UIString.js",
|
573
|
+
"front_end/core/platform/UserVisibleError.js",
|
572
574
|
"front_end/core/platform/array-utilities.js",
|
573
575
|
"front_end/core/platform/date-utilities.js",
|
574
576
|
"front_end/core/platform/dcheck.js",
|
@@ -1044,6 +1046,7 @@ grd_files_debug_sources = [
|
|
1044
1046
|
"front_end/panels/lighthouse/LighthouseReportSelector.js",
|
1045
1047
|
"front_end/panels/lighthouse/LighthouseReporterTypes.js",
|
1046
1048
|
"front_end/panels/lighthouse/LighthouseStartView.js",
|
1049
|
+
"front_end/panels/lighthouse/LighthouseStartViewFR.js",
|
1047
1050
|
"front_end/panels/lighthouse/LighthouseStatusView.js",
|
1048
1051
|
"front_end/panels/lighthouse/RadioSetting.js",
|
1049
1052
|
"front_end/panels/lighthouse/lighthouseDialog.css.js",
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.75 16.75V6.74572L12.4599 3.25H5.25V16.75H15.75ZM13 2L17 6.25V17C17 17.5523 16.5523 18 16 18H5C4.44772 18 4 17.5523 4 17V3C4 2.44772 4.44772 2 5 2H13Z" fill="black"/>
|
3
|
+
<path d="M11 6H10V8H8V9H10V11H11V9H13V8H11V6Z" fill="black"/>
|
4
|
+
<path d="M13 13L13 14L8 14L8 13L13 13Z" fill="black"/>
|
5
|
+
</svg>
|
@@ -256,9 +256,9 @@ export class ParsedURL {
|
|
256
256
|
return ParsedURL.urlRegexInstance;
|
257
257
|
}
|
258
258
|
|
259
|
-
static extractPath(url: string):
|
259
|
+
static extractPath(url: string): Platform.DevToolsPath.EncodedPathString {
|
260
260
|
const parsedURL = this.fromString(url);
|
261
|
-
return parsedURL ? parsedURL.path : '';
|
261
|
+
return (parsedURL ? parsedURL.path : '') as Platform.DevToolsPath.EncodedPathString;
|
262
262
|
}
|
263
263
|
|
264
264
|
static extractOrigin(url: string): string {
|
@@ -586,7 +586,9 @@ export enum DevtoolsExperiments {
|
|
586
586
|
'bfcacheDisplayTree' = 54,
|
587
587
|
'stylesPaneCSSChanges' = 55,
|
588
588
|
'headerOverrides' = 56,
|
589
|
-
'
|
589
|
+
'lighthousePanelFR' = 57,
|
590
|
+
// Increment this when new experiments are added.
|
591
|
+
'MaxValue' = 58,
|
590
592
|
}
|
591
593
|
/* eslint-enable @typescript-eslint/naming-convention */
|
592
594
|
|
@@ -4835,6 +4835,12 @@
|
|
4835
4835
|
"panels/elements/StylesSidebarPane.ts | constructedStylesheet": {
|
4836
4836
|
"message": "constructed stylesheet"
|
4837
4837
|
},
|
4838
|
+
"panels/elements/StylesSidebarPane.ts | copiedToClipboard": {
|
4839
|
+
"message": "Copied to clipboard"
|
4840
|
+
},
|
4841
|
+
"panels/elements/StylesSidebarPane.ts | copyAllCSSChanges": {
|
4842
|
+
"message": "Copy all the CSS changes"
|
4843
|
+
},
|
4838
4844
|
"panels/elements/StylesSidebarPane.ts | copyAllDeclarations": {
|
4839
4845
|
"message": "Copy all declarations"
|
4840
4846
|
},
|
@@ -5765,6 +5771,9 @@
|
|
5765
5771
|
"panels/lighthouse/LighthouseController.ts | legacyNavigation": {
|
5766
5772
|
"message": "Legacy navigation"
|
5767
5773
|
},
|
5774
|
+
"panels/lighthouse/LighthouseController.ts | lighthouseMode": {
|
5775
|
+
"message": "Lighthouse mode"
|
5776
|
+
},
|
5768
5777
|
"panels/lighthouse/LighthouseController.ts | localStorage": {
|
5769
5778
|
"message": "Local Storage"
|
5770
5779
|
},
|
@@ -5774,6 +5783,9 @@
|
|
5774
5783
|
"panels/lighthouse/LighthouseController.ts | multipleTabsAreBeingControlledBy": {
|
5775
5784
|
"message": "Multiple tabs are being controlled by the same service worker. Close your other tabs on the same origin to audit this page."
|
5776
5785
|
},
|
5786
|
+
"panels/lighthouse/LighthouseController.ts | navigation": {
|
5787
|
+
"message": "Navigation"
|
5788
|
+
},
|
5777
5789
|
"panels/lighthouse/LighthouseController.ts | performance": {
|
5778
5790
|
"message": "Performance"
|
5779
5791
|
},
|
@@ -5786,6 +5798,9 @@
|
|
5786
5798
|
"panels/lighthouse/LighthouseController.ts | resetStorageLocalstorage": {
|
5787
5799
|
"message": "Reset storage (cache, service workers, etc) before auditing. (Good for performance & PWA testing)"
|
5788
5800
|
},
|
5801
|
+
"panels/lighthouse/LighthouseController.ts | runLighthouseInMode": {
|
5802
|
+
"message": "Run Lighthouse in navigation, timespan, or snapshot mode"
|
5803
|
+
},
|
5789
5804
|
"panels/lighthouse/LighthouseController.ts | seo": {
|
5790
5805
|
"message": "SEO"
|
5791
5806
|
},
|
@@ -5795,6 +5810,9 @@
|
|
5795
5810
|
"panels/lighthouse/LighthouseController.ts | simulatedThrottling": {
|
5796
5811
|
"message": "Simulated throttling"
|
5797
5812
|
},
|
5813
|
+
"panels/lighthouse/LighthouseController.ts | snapshot": {
|
5814
|
+
"message": "Snapshot"
|
5815
|
+
},
|
5798
5816
|
"panels/lighthouse/LighthouseController.ts | thereMayBeStoredDataAffectingLoadingPlural": {
|
5799
5817
|
"message": "There may be stored data affecting loading performance in these locations: {PH1}. Audit this page in an incognito window to prevent those resources from affecting your scores."
|
5800
5818
|
},
|
@@ -5861,6 +5879,9 @@
|
|
5861
5879
|
"panels/lighthouse/LighthouseStartView.ts | learnMore": {
|
5862
5880
|
"message": "Learn more"
|
5863
5881
|
},
|
5882
|
+
"panels/lighthouse/LighthouseStartViewFR.ts | mode": {
|
5883
|
+
"message": "Mode"
|
5884
|
+
},
|
5864
5885
|
"panels/lighthouse/LighthouseStatusView.ts | ahSorryWeRanIntoAnError": {
|
5865
5886
|
"message": "Ah, sorry! We ran into an error."
|
5866
5887
|
},
|
@@ -4835,6 +4835,12 @@
|
|
4835
4835
|
"panels/elements/StylesSidebarPane.ts | constructedStylesheet": {
|
4836
4836
|
"message": "ĉón̂śt̂ŕûćt̂éd̂ śt̂ýl̂éŝh́êét̂"
|
4837
4837
|
},
|
4838
|
+
"panels/elements/StylesSidebarPane.ts | copiedToClipboard": {
|
4839
|
+
"message": "Ĉóp̂íêd́ t̂ó ĉĺîṕb̂óâŕd̂"
|
4840
|
+
},
|
4841
|
+
"panels/elements/StylesSidebarPane.ts | copyAllCSSChanges": {
|
4842
|
+
"message": "Ĉóp̂ý âĺl̂ t́ĥé ĈŚŜ ćĥán̂ǵêś"
|
4843
|
+
},
|
4838
4844
|
"panels/elements/StylesSidebarPane.ts | copyAllDeclarations": {
|
4839
4845
|
"message": "Ĉóp̂ý âĺl̂ d́êćl̂ár̂át̂íôńŝ"
|
4840
4846
|
},
|
@@ -5765,6 +5771,9 @@
|
|
5765
5771
|
"panels/lighthouse/LighthouseController.ts | legacyNavigation": {
|
5766
5772
|
"message": "L̂éĝáĉý n̂áv̂íĝát̂íôń"
|
5767
5773
|
},
|
5774
|
+
"panels/lighthouse/LighthouseController.ts | lighthouseMode": {
|
5775
|
+
"message": "L̂íĝh́t̂h́ôúŝé m̂ód̂é"
|
5776
|
+
},
|
5768
5777
|
"panels/lighthouse/LighthouseController.ts | localStorage": {
|
5769
5778
|
"message": "L̂óĉál̂ Śt̂ór̂áĝé"
|
5770
5779
|
},
|
@@ -5774,6 +5783,9 @@
|
|
5774
5783
|
"panels/lighthouse/LighthouseController.ts | multipleTabsAreBeingControlledBy": {
|
5775
5784
|
"message": "M̂úl̂t́îṕl̂é t̂áb̂ś âŕê b́êín̂ǵ ĉón̂t́r̂ól̂ĺêd́ b̂ý t̂h́ê śâḿê service worker. Ćl̂óŝé ŷóûŕ ôt́ĥér̂ t́âb́ŝ ón̂ t́ĥé ŝám̂é ôŕîǵîń t̂ó âúd̂ít̂ t́ĥíŝ ṕâǵê."
|
5776
5785
|
},
|
5786
|
+
"panels/lighthouse/LighthouseController.ts | navigation": {
|
5787
|
+
"message": "N̂áv̂íĝát̂íôń"
|
5788
|
+
},
|
5777
5789
|
"panels/lighthouse/LighthouseController.ts | performance": {
|
5778
5790
|
"message": "P̂ér̂f́ôŕm̂án̂ćê"
|
5779
5791
|
},
|
@@ -5786,6 +5798,9 @@
|
|
5786
5798
|
"panels/lighthouse/LighthouseController.ts | resetStorageLocalstorage": {
|
5787
5799
|
"message": "R̂éŝét̂ śt̂ór̂áĝé (cache, service workers, êt́ĉ) b́êf́ôŕê áûd́ît́îńĝ. (Ǵôód̂ f́ôŕ p̂ér̂f́ôŕm̂án̂ćê & PWA t́êśt̂ín̂ǵ)"
|
5788
5800
|
},
|
5801
|
+
"panels/lighthouse/LighthouseController.ts | runLighthouseInMode": {
|
5802
|
+
"message": "R̂ún̂ Ĺîǵĥt́ĥóûśê ín̂ ńâv́îǵât́îón̂, t́îḿêśp̂án̂, ór̂ śn̂áp̂śĥót̂ ḿôd́ê"
|
5803
|
+
},
|
5789
5804
|
"panels/lighthouse/LighthouseController.ts | seo": {
|
5790
5805
|
"message": "ŜÉÔ"
|
5791
5806
|
},
|
@@ -5795,6 +5810,9 @@
|
|
5795
5810
|
"panels/lighthouse/LighthouseController.ts | simulatedThrottling": {
|
5796
5811
|
"message": "Ŝím̂úl̂át̂éd̂ t́ĥŕôt́t̂ĺîńĝ"
|
5797
5812
|
},
|
5813
|
+
"panels/lighthouse/LighthouseController.ts | snapshot": {
|
5814
|
+
"message": "Ŝńâṕŝh́ôt́"
|
5815
|
+
},
|
5798
5816
|
"panels/lighthouse/LighthouseController.ts | thereMayBeStoredDataAffectingLoadingPlural": {
|
5799
5817
|
"message": "T̂h́êŕê ḿâý b̂é ŝt́ôŕêd́ d̂át̂á âf́f̂éĉt́îńĝ ĺôád̂ín̂ǵ p̂ér̂f́ôŕm̂án̂ćê ín̂ t́ĥéŝé l̂óĉát̂íôńŝ: {PH1}. Áûd́ît́ t̂h́îś p̂áĝé îń âń îńĉóĝńît́ô ẃîńd̂óŵ t́ô ṕr̂év̂én̂t́ t̂h́ôśê ŕêśôúr̂ćêś f̂ŕôḿ âf́f̂éĉt́îńĝ ýôúr̂ śĉór̂éŝ."
|
5800
5818
|
},
|
@@ -5861,6 +5879,9 @@
|
|
5861
5879
|
"panels/lighthouse/LighthouseStartView.ts | learnMore": {
|
5862
5880
|
"message": "L̂éâŕn̂ ḿôŕê"
|
5863
5881
|
},
|
5882
|
+
"panels/lighthouse/LighthouseStartViewFR.ts | mode": {
|
5883
|
+
"message": "M̂ód̂é"
|
5884
|
+
},
|
5864
5885
|
"panels/lighthouse/LighthouseStatusView.ts | ahSorryWeRanIntoAnError": {
|
5865
5886
|
"message": "Âh́, ŝór̂ŕŷ! Ẃê ŕâń îńt̂ó âń êŕr̂ór̂."
|
5866
5887
|
},
|
@@ -0,0 +1,28 @@
|
|
1
|
+
// Copyright 2021 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 type {LocalizedString} from './UIString.js';
|
6
|
+
|
7
|
+
/**
|
8
|
+
* Represents an error that might become visible to the user. Where errors
|
9
|
+
* might be surfaced to the user (such as by displaying the message to the
|
10
|
+
* console), this class should be used to enforce that the message is
|
11
|
+
* localized on the way in.
|
12
|
+
*/
|
13
|
+
export class UserVisibleError extends Error {
|
14
|
+
readonly message: LocalizedString;
|
15
|
+
|
16
|
+
constructor(message: LocalizedString) {
|
17
|
+
super(message);
|
18
|
+
this.message = message;
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
export function isUserVisibleError(error: unknown): error is UserVisibleError {
|
23
|
+
if (typeof error === 'object' && error !== null) {
|
24
|
+
return error instanceof UserVisibleError;
|
25
|
+
}
|
26
|
+
|
27
|
+
return false;
|
28
|
+
}
|
@@ -38,6 +38,7 @@ import * as SetUtilities from './set-utilities.js';
|
|
38
38
|
import * as StringUtilities from './string-utilities.js';
|
39
39
|
import * as TypeScriptUtilities from './typescript-utilities.js';
|
40
40
|
import * as UIString from './UIString.js';
|
41
|
+
import * as UserVisibleError from './UserVisibleError.js';
|
41
42
|
|
42
43
|
export {DCHECK} from './dcheck.js';
|
43
44
|
/* `assertNotNull` also need to be exposed, as TypeScript does not
|
@@ -57,4 +58,5 @@ export {
|
|
57
58
|
StringUtilities,
|
58
59
|
TypeScriptUtilities,
|
59
60
|
UIString,
|
61
|
+
UserVisibleError,
|
60
62
|
};
|
@@ -140,7 +140,6 @@ export class ChildTargetManager extends SDKModel<EventTypes> implements Protocol
|
|
140
140
|
} else if (targetInfo.type === 'auction_worklet') {
|
141
141
|
type = Type.AuctionWorklet;
|
142
142
|
}
|
143
|
-
|
144
143
|
const target = this.#targetManager.createTarget(
|
145
144
|
targetInfo.targetId, targetName, type, this.#parentTarget, sessionId, undefined, undefined, targetInfo);
|
146
145
|
target.setInspectedURL(this.#parentTarget.inspectedURL());
|
@@ -412,6 +412,16 @@ export class NetworkDispatcher implements ProtocolProxyApi.NetworkDispatcher {
|
|
412
412
|
* once it is created in `requestWillBeSent`.
|
413
413
|
*/
|
414
414
|
this.#requestIdToTrustTokenEvent = new Map();
|
415
|
+
|
416
|
+
MultitargetNetworkManager.instance().addEventListener(
|
417
|
+
MultitargetNetworkManager.Events.RequestIntercepted, this.#markAsIntercepted.bind(this));
|
418
|
+
}
|
419
|
+
|
420
|
+
#markAsIntercepted(event: Common.EventTarget.EventTargetEvent<string>): void {
|
421
|
+
const request = this.requestForURL(event.data);
|
422
|
+
if (request) {
|
423
|
+
request.setWasIntercepted(true);
|
424
|
+
}
|
415
425
|
}
|
416
426
|
|
417
427
|
private headersMapToHeadersArray(headersMap: Protocol.Network.Headers): NameValue[] {
|
@@ -446,7 +456,7 @@ export class NetworkDispatcher implements ProtocolProxyApi.NetworkDispatcher {
|
|
446
456
|
if (!networkRequest.statusText) {
|
447
457
|
networkRequest.statusText = response.statusText;
|
448
458
|
}
|
449
|
-
if (!networkRequest.hasExtraResponseInfo()) {
|
459
|
+
if (!networkRequest.hasExtraResponseInfo() || networkRequest.wasIntercepted()) {
|
450
460
|
networkRequest.responseHeaders = this.headersMapToHeadersArray(response.headers);
|
451
461
|
}
|
452
462
|
|
@@ -1401,6 +1411,8 @@ export class MultitargetNetworkManager extends Common.ObjectWrapper.ObjectWrappe
|
|
1401
1411
|
for (const requestInterceptor of this.#urlsForRequestInterceptor.keysArray()) {
|
1402
1412
|
await requestInterceptor(interceptedRequest);
|
1403
1413
|
if (interceptedRequest.hasResponded()) {
|
1414
|
+
this.dispatchEventToListeners(
|
1415
|
+
MultitargetNetworkManager.Events.RequestIntercepted, interceptedRequest.request.url);
|
1404
1416
|
return;
|
1405
1417
|
}
|
1406
1418
|
}
|
@@ -1467,6 +1479,7 @@ export namespace MultitargetNetworkManager {
|
|
1467
1479
|
UserAgentChanged = 'UserAgentChanged',
|
1468
1480
|
InterceptorsChanged = 'InterceptorsChanged',
|
1469
1481
|
AcceptedEncodingsChanged = 'AcceptedEncodingsChanged',
|
1482
|
+
RequestIntercepted = 'RequestIntercepted',
|
1470
1483
|
}
|
1471
1484
|
|
1472
1485
|
export type EventTypes = {
|
@@ -1475,6 +1488,7 @@ export namespace MultitargetNetworkManager {
|
|
1475
1488
|
[Events.UserAgentChanged]: void,
|
1476
1489
|
[Events.InterceptorsChanged]: void,
|
1477
1490
|
[Events.AcceptedEncodingsChanged]: void,
|
1491
|
+
[Events.RequestIntercepted]: string,
|
1478
1492
|
};
|
1479
1493
|
}
|
1480
1494
|
|
@@ -290,6 +290,7 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper<EventType
|
|
290
290
|
#parsedQueryParameters?: NameValue[];
|
291
291
|
#contentDataProvider?: (() => Promise<ContentData>);
|
292
292
|
#isSameSiteInternal: boolean|null;
|
293
|
+
#wasIntercepted: boolean;
|
293
294
|
|
294
295
|
private constructor(
|
295
296
|
requestId: string, backendRequestId: Protocol.Network.RequestId|undefined, url: string, documentURL: string,
|
@@ -363,6 +364,8 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper<EventType
|
|
363
364
|
|
364
365
|
this.localizedFailDescription = null;
|
365
366
|
this.#isSameSiteInternal = null;
|
367
|
+
|
368
|
+
this.#wasIntercepted = false;
|
366
369
|
}
|
367
370
|
|
368
371
|
static create(
|
@@ -964,6 +967,14 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper<EventType
|
|
964
967
|
return this.#responseHeaderValues[headerName];
|
965
968
|
}
|
966
969
|
|
970
|
+
wasIntercepted(): boolean {
|
971
|
+
return this.#wasIntercepted;
|
972
|
+
}
|
973
|
+
|
974
|
+
setWasIntercepted(wasIntercepted: boolean): void {
|
975
|
+
this.#wasIntercepted = wasIntercepted;
|
976
|
+
}
|
977
|
+
|
967
978
|
get responseCookies(): Cookie[] {
|
968
979
|
if (!this.#responseCookiesInternal) {
|
969
980
|
this.#responseCookiesInternal =
|
@@ -103,7 +103,7 @@ async function start(method: string, params: any): Promise<unknown> {
|
|
103
103
|
const url = params.url;
|
104
104
|
|
105
105
|
// Handle legacy Lighthouse runner path.
|
106
|
-
if (method === '
|
106
|
+
if (method === 'legacyNavigation') {
|
107
107
|
// @ts-expect-error https://github.com/GoogleChrome/lighthouse/issues/11628
|
108
108
|
const connection = self.setUpWorkerConnection(port);
|
109
109
|
// @ts-expect-error https://github.com/GoogleChrome/lighthouse/issues/11628
|
@@ -115,14 +115,21 @@ async function start(method: string, params: any): Promise<unknown> {
|
|
115
115
|
puppeteerConnection =
|
116
116
|
await Puppeteer.PuppeteerConnection.getPuppeteerConnection(rawConnection, mainFrameId, mainTargetId);
|
117
117
|
|
118
|
+
if (method === 'snapshot') {
|
119
|
+
// @ts-expect-error https://github.com/GoogleChrome/lighthouse/issues/11628
|
120
|
+
return await self.runLighthouseSnapshot({
|
121
|
+
url,
|
122
|
+
config,
|
123
|
+
page: puppeteerConnection.page,
|
124
|
+
});
|
125
|
+
}
|
126
|
+
|
118
127
|
// @ts-expect-error https://github.com/GoogleChrome/lighthouse/issues/11628
|
119
|
-
|
128
|
+
return await self.runLighthouseNavigation({
|
120
129
|
url,
|
121
130
|
config,
|
122
131
|
page: puppeteerConnection.page,
|
123
132
|
});
|
124
|
-
|
125
|
-
return result;
|
126
133
|
} catch (err) {
|
127
134
|
return ({
|
128
135
|
fatal: true,
|
@@ -179,8 +186,10 @@ function notifyFrontendViaWorkerMessage(method: string, params: any): void {
|
|
179
186
|
self.onmessage = async(event: MessageEvent): Promise<void> => {
|
180
187
|
const messageFromFrontend = JSON.parse(event.data);
|
181
188
|
switch (messageFromFrontend.method) {
|
182
|
-
case '
|
183
|
-
case '
|
189
|
+
case 'navigation':
|
190
|
+
case 'timespan':
|
191
|
+
case 'snapshot':
|
192
|
+
case 'legacyNavigation': {
|
184
193
|
const result = await start(messageFromFrontend.method, messageFromFrontend.params);
|
185
194
|
self.postMessage(JSON.stringify({id: messageFromFrontend.id, result}));
|
186
195
|
break;
|
@@ -376,6 +376,9 @@ export class MainImpl {
|
|
376
376
|
Root.Runtime.experiments.register(
|
377
377
|
Root.Runtime.ExperimentName.HEADER_OVERRIDES, 'Local overrides for response headers');
|
378
378
|
|
379
|
+
// New Lighthouse panel with timespan and snapshot mode
|
380
|
+
Root.Runtime.experiments.register('lighthousePanelFR', 'Use Lighthouse panel with timespan and snapshot modes');
|
381
|
+
|
379
382
|
Root.Runtime.experiments.enableExperimentsByDefault([
|
380
383
|
'sourceOrderViewer',
|
381
384
|
'hideIssuesFeature',
|
@@ -100,6 +100,27 @@ export class CSSWorkspaceBinding implements SDK.TargetManager.SDKModelObserver<S
|
|
100
100
|
return locationPromise;
|
101
101
|
}
|
102
102
|
|
103
|
+
propertyRawLocation(cssProperty: SDK.CSSProperty.CSSProperty, forName: boolean): SDK.CSSModel.CSSLocation|null {
|
104
|
+
const style = cssProperty.ownerStyle;
|
105
|
+
if (!style || style.type !== SDK.CSSStyleDeclaration.Type.Regular || !style.styleSheetId) {
|
106
|
+
return null;
|
107
|
+
}
|
108
|
+
const header = style.cssModel().styleSheetHeaderForId(style.styleSheetId);
|
109
|
+
if (!header) {
|
110
|
+
return null;
|
111
|
+
}
|
112
|
+
|
113
|
+
const range = forName ? cssProperty.nameRange() : cssProperty.valueRange();
|
114
|
+
if (!range) {
|
115
|
+
return null;
|
116
|
+
}
|
117
|
+
|
118
|
+
const lineNumber = range.startLine;
|
119
|
+
const columnNumber = range.startColumn;
|
120
|
+
return new SDK.CSSModel.CSSLocation(
|
121
|
+
header, header.lineNumberInSource(lineNumber), header.columnNumberInSource(lineNumber, columnNumber));
|
122
|
+
}
|
123
|
+
|
103
124
|
propertyUILocation(cssProperty: SDK.CSSProperty.CSSProperty, forName: boolean): Workspace.UISourceCode.UILocation
|
104
125
|
|null {
|
105
126
|
const style = cssProperty.ownerStyle;
|
@@ -59,9 +59,9 @@ export class FileSystemWorkspaceBinding {
|
|
59
59
|
return fileSystemPath;
|
60
60
|
}
|
61
61
|
|
62
|
-
static relativePath(uiSourceCode: Workspace.UISourceCode.UISourceCode):
|
62
|
+
static relativePath(uiSourceCode: Workspace.UISourceCode.UISourceCode): Platform.DevToolsPath.EncodedPathString[] {
|
63
63
|
const baseURL = (uiSourceCode.project() as FileSystem).fileSystemBaseURL;
|
64
|
-
return uiSourceCode.url().substring(baseURL.length).split('/');
|
64
|
+
return uiSourceCode.url().substring(baseURL.length).split('/') as Platform.DevToolsPath.EncodedPathString[];
|
65
65
|
}
|
66
66
|
|
67
67
|
static tooltipForUISourceCode(uiSourceCode: Workspace.UISourceCode.UISourceCode): string {
|
@@ -35,6 +35,7 @@
|
|
35
35
|
import * as Common from '../../core/common/common.js';
|
36
36
|
import * as Host from '../../core/host/host.js';
|
37
37
|
import * as i18n from '../../core/i18n/i18n.js';
|
38
|
+
import * as Platform from '../../core/platform/platform.js';
|
38
39
|
import * as Root from '../../core/root/root.js';
|
39
40
|
import * as SDK from '../../core/sdk/sdk.js';
|
40
41
|
import * as Extensions from '../../models/extensions/extensions.js';
|
@@ -1269,9 +1270,22 @@ export class DOMNodeRevealer implements Common.Revealer.Revealer {
|
|
1269
1270
|
const panel = ElementsPanel.instance();
|
1270
1271
|
panel.pendingNodeReveal = true;
|
1271
1272
|
|
1272
|
-
return new Promise(revealPromise)
|
1273
|
+
return (new Promise<void>(revealPromise)).catch((reason: Error) => {
|
1274
|
+
let message: string;
|
1275
|
+
if (Platform.UserVisibleError.isUserVisibleError(reason)) {
|
1276
|
+
message = reason.message;
|
1277
|
+
} else {
|
1278
|
+
message = i18nString(UIStrings.nodeCannotBeFoundInTheCurrent);
|
1279
|
+
}
|
1273
1280
|
|
1274
|
-
|
1281
|
+
Common.Console.Console.instance().warn(message);
|
1282
|
+
// Blink tests expect an exception to be raised and unhandled here to detect that the node
|
1283
|
+
// was actually not successfully viewed.
|
1284
|
+
throw reason;
|
1285
|
+
});
|
1286
|
+
|
1287
|
+
function revealPromise(
|
1288
|
+
resolve: () => void, reject: (arg0: Platform.UserVisibleError.UserVisibleError) => void): void {
|
1275
1289
|
if (node instanceof SDK.DOMModel.DOMNode) {
|
1276
1290
|
onNodeResolved((node as SDK.DOMModel.DOMNode));
|
1277
1291
|
} else if (node instanceof SDK.DOMModel.DeferredDOMNode) {
|
@@ -1281,10 +1295,12 @@ export class DOMNodeRevealer implements Common.Revealer.Revealer {
|
|
1281
1295
|
if (domModel) {
|
1282
1296
|
void domModel.pushObjectAsNodeToFrontend(node).then(checkRemoteObjectThenReveal);
|
1283
1297
|
} else {
|
1284
|
-
|
1298
|
+
const msg = i18nString(UIStrings.nodeCannotBeFoundInTheCurrent);
|
1299
|
+
reject(new Platform.UserVisibleError.UserVisibleError(msg));
|
1285
1300
|
}
|
1286
1301
|
} else {
|
1287
|
-
|
1302
|
+
const msg = i18nString(UIStrings.theRemoteObjectCouldNotBe);
|
1303
|
+
reject(new Platform.UserVisibleError.UserVisibleError(msg));
|
1288
1304
|
panel.pendingNodeReveal = false;
|
1289
1305
|
}
|
1290
1306
|
|
@@ -1304,8 +1320,7 @@ export class DOMNodeRevealer implements Common.Revealer.Revealer {
|
|
1304
1320
|
const isDocument = node instanceof SDK.DOMModel.DOMDocument;
|
1305
1321
|
if (!isDocument && isDetached) {
|
1306
1322
|
const msg = i18nString(UIStrings.nodeCannotBeFoundInTheCurrent);
|
1307
|
-
|
1308
|
-
reject(new Error(msg));
|
1323
|
+
reject(new Platform.UserVisibleError.UserVisibleError(msg));
|
1309
1324
|
return;
|
1310
1325
|
}
|
1311
1326
|
|
@@ -1313,14 +1328,14 @@ export class DOMNodeRevealer implements Common.Revealer.Revealer {
|
|
1313
1328
|
void panel.revealAndSelectNode(resolvedNode, !omitFocus).then(resolve);
|
1314
1329
|
return;
|
1315
1330
|
}
|
1316
|
-
|
1331
|
+
const msg = i18nString(UIStrings.nodeCannotBeFoundInTheCurrent);
|
1332
|
+
reject(new Platform.UserVisibleError.UserVisibleError(msg));
|
1317
1333
|
}
|
1318
1334
|
|
1319
1335
|
function checkRemoteObjectThenReveal(resolvedNode: SDK.DOMModel.DOMNode|null): void {
|
1320
1336
|
if (!resolvedNode) {
|
1321
1337
|
const msg = i18nString(UIStrings.theRemoteObjectCouldNotBe);
|
1322
|
-
|
1323
|
-
reject(new Error(msg));
|
1338
|
+
reject(new Platform.UserVisibleError.UserVisibleError(msg));
|
1324
1339
|
return;
|
1325
1340
|
}
|
1326
1341
|
onNodeResolved(resolvedNode);
|
@@ -1329,8 +1344,7 @@ export class DOMNodeRevealer implements Common.Revealer.Revealer {
|
|
1329
1344
|
function checkDeferredDOMNodeThenReveal(resolvedNode: SDK.DOMModel.DOMNode|null): void {
|
1330
1345
|
if (!resolvedNode) {
|
1331
1346
|
const msg = i18nString(UIStrings.theDeferredDomNodeCouldNotBe);
|
1332
|
-
|
1333
|
-
reject(new Error(msg));
|
1347
|
+
reject(new Platform.UserVisibleError.UserVisibleError(msg));
|
1334
1348
|
return;
|
1335
1349
|
}
|
1336
1350
|
onNodeResolved(resolvedNode);
|
@@ -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;
|
@@ -3052,6 +3219,14 @@ export function unescapeCssString(input: string): string {
|
|
3052
3219
|
});
|
3053
3220
|
}
|
3054
3221
|
|
3222
|
+
export function escapeUrlAsCssComment(urlText: string): string {
|
3223
|
+
const url = new URL(urlText);
|
3224
|
+
if (url.search) {
|
3225
|
+
return `${url.origin}${url.pathname}${url.search.replaceAll('*/', '*%2F')}${url.hash}`;
|
3226
|
+
}
|
3227
|
+
return url.toString();
|
3228
|
+
}
|
3229
|
+
|
3055
3230
|
export class StylesSidebarPropertyRenderer {
|
3056
3231
|
private rule: SDK.CSSRule.CSSRule|null;
|
3057
3232
|
private node: SDK.DOMModel.DOMNode|null;
|
@@ -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,
|
@@ -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
|
+
}
|
@@ -428,9 +428,12 @@ export class NavigatorView extends UI.Widget.VBox implements SDK.TargetManager.O
|
|
428
428
|
const isFromSourceMap = uiSourceCode.contentType().isFromSourceMap();
|
429
429
|
let path;
|
430
430
|
if (uiSourceCode.project().type() === Workspace.Workspace.projectTypes.FileSystem) {
|
431
|
-
path =
|
431
|
+
path =
|
432
|
+
Persistence.FileSystemWorkspaceBinding.FileSystemWorkspaceBinding.relativePath(uiSourceCode).slice(0, -1) as
|
433
|
+
Platform.DevToolsPath.EncodedPathString[];
|
432
434
|
} else {
|
433
|
-
path = Common.ParsedURL.ParsedURL.extractPath(uiSourceCode.url()).split('/').slice(1, -1)
|
435
|
+
path = Common.ParsedURL.ParsedURL.extractPath(uiSourceCode.url()).split('/').slice(1, -1) as
|
436
|
+
Platform.DevToolsPath.EncodedPathString[];
|
434
437
|
}
|
435
438
|
|
436
439
|
const project = uiSourceCode.project();
|
@@ -503,7 +506,8 @@ export class NavigatorView extends UI.Widget.VBox implements SDK.TargetManager.O
|
|
503
506
|
const commonPrefix = reversedIndex.longestPrefix(reversedPath, false /* fullWordOnly */);
|
504
507
|
reversedIndex.add(reversedPath);
|
505
508
|
const prefixPath = reversedPath.substring(0, commonPrefix.length + 1);
|
506
|
-
const path =
|
509
|
+
const path = Common.ParsedURL.ParsedURL.encodedPathToRawPathString(
|
510
|
+
encoder.decode(Platform.StringUtilities.reverse(prefixPath)) as Platform.DevToolsPath.EncodedPathString);
|
507
511
|
|
508
512
|
const fileSystemNode = this.rootNode.child(project.id());
|
509
513
|
if (fileSystemNode) {
|
@@ -539,7 +543,7 @@ export class NavigatorView extends UI.Widget.VBox implements SDK.TargetManager.O
|
|
539
543
|
private folderNode(
|
540
544
|
uiSourceCode: Workspace.UISourceCode.UISourceCode, project: Workspace.Workspace.Project,
|
541
545
|
target: SDK.Target.Target|null, frame: SDK.ResourceTreeModel.ResourceTreeFrame|null, projectOrigin: string,
|
542
|
-
path:
|
546
|
+
path: Platform.DevToolsPath.EncodedPathString[], fromSourceMap: boolean): NavigatorTreeNode {
|
543
547
|
if (Snippets.ScriptSnippetFileSystem.isSnippetsUISourceCode(uiSourceCode)) {
|
544
548
|
return this.rootNode;
|
545
549
|
}
|
@@ -568,7 +572,7 @@ export class NavigatorView extends UI.Widget.VBox implements SDK.TargetManager.O
|
|
568
572
|
if (project.type() === Workspace.Workspace.projectTypes.FileSystem) {
|
569
573
|
type = Types.FileSystemFolder;
|
570
574
|
}
|
571
|
-
const name = path[path.length - 1];
|
575
|
+
const name = Common.ParsedURL.ParsedURL.encodedPathToRawPathString(path[path.length - 1]);
|
572
576
|
|
573
577
|
folderNode = new NavigatorFolderTreeNode(this, project, folderId, type, folderPath, name);
|
574
578
|
this.subfolderNodes.set(folderId, folderNode);
|
@@ -139,26 +139,13 @@ Sources.SnippetsPlugin = SourcesModule.SnippetsPlugin.SnippetsPlugin;
|
|
139
139
|
|
140
140
|
Sources.SourceMapNamesResolver = {};
|
141
141
|
|
142
|
-
Sources.SourceMapNamesResolver.setScopeResolvedForTest = SourcesModule.SourceMapNamesResolver.setScopeResolvedForTest;
|
143
|
-
|
144
142
|
// Tests can override this global symbol and therefore can't be exported
|
145
143
|
Object.defineProperty(Sources.SourceMapNamesResolver, '_scopeResolvedForTest', {
|
146
144
|
get: SourcesModule.SourceMapNamesResolver.getScopeResolvedForTest,
|
147
145
|
set: SourcesModule.SourceMapNamesResolver.setScopeResolvedForTest,
|
148
146
|
});
|
149
147
|
|
150
|
-
Sources.SourceMapNamesResolver._scopeIdentifiers = SourcesModule.SourceMapNamesResolver.scopeIdentifiers;
|
151
|
-
Sources.SourceMapNamesResolver._resolveScope = SourcesModule.SourceMapNamesResolver.resolveScope;
|
152
|
-
Sources.SourceMapNamesResolver._allVariablesInCallFrame = SourcesModule.SourceMapNamesResolver.allVariablesInCallFrame;
|
153
148
|
Sources.SourceMapNamesResolver.resolveExpression = SourcesModule.SourceMapNamesResolver.resolveExpression;
|
154
|
-
Sources.SourceMapNamesResolver.resolveThisObject = SourcesModule.SourceMapNamesResolver.resolveThisObject;
|
155
|
-
Sources.SourceMapNamesResolver.resolveScopeInObject = SourcesModule.SourceMapNamesResolver.resolveScopeInObject;
|
156
|
-
|
157
|
-
/** @constructor */
|
158
|
-
Sources.SourceMapNamesResolver.Identifier = SourcesModule.SourceMapNamesResolver.Identifier;
|
159
|
-
|
160
|
-
/** @constructor */
|
161
|
-
Sources.SourceMapNamesResolver.RemoteObject = SourcesModule.SourceMapNamesResolver.RemoteObject;
|
162
149
|
|
163
150
|
/** @constructor */
|
164
151
|
Sources.NetworkNavigatorView = SourcesModule.SourcesNavigator.NetworkNavigatorView;
|
@@ -117,7 +117,6 @@
|
|
117
117
|
height: 26px;
|
118
118
|
line-height: 16px;
|
119
119
|
white-space: nowrap;
|
120
|
-
cursor: default;
|
121
120
|
display: flex;
|
122
121
|
align-items: center;
|
123
122
|
color: var(--color-text-secondary);
|
@@ -194,7 +193,6 @@
|
|
194
193
|
.tabbed-pane-header-tabs-drop-down-container {
|
195
194
|
float: left;
|
196
195
|
opacity: 80%;
|
197
|
-
cursor: pointer;
|
198
196
|
display: flex;
|
199
197
|
align-items: center;
|
200
198
|
height: 100%;
|
@@ -347,7 +345,6 @@
|
|
347
345
|
|
348
346
|
width: 14px;
|
349
347
|
height: 14px;
|
350
|
-
cursor: default;
|
351
348
|
display: flex;
|
352
349
|
align-items: center;
|
353
350
|
justify-content: center;
|
@@ -7,12 +7,14 @@
|
|
7
7
|
:host {
|
8
8
|
flex: none;
|
9
9
|
padding: 0 2px;
|
10
|
+
|
11
|
+
--toolbar-height: 26px;
|
10
12
|
}
|
11
13
|
|
12
14
|
.toolbar-shadow {
|
13
15
|
position: relative;
|
14
16
|
white-space: nowrap;
|
15
|
-
height:
|
17
|
+
height: var(--toolbar-height);
|
16
18
|
overflow: hidden;
|
17
19
|
z-index: 12;
|
18
20
|
display: flex;
|
@@ -43,7 +45,7 @@
|
|
43
45
|
align-items: center;
|
44
46
|
justify-content: center;
|
45
47
|
padding: 0;
|
46
|
-
height:
|
48
|
+
height: var(--toolbar-height);
|
47
49
|
border: none;
|
48
50
|
white-space: pre;
|
49
51
|
}
|
@@ -55,7 +57,7 @@
|
|
55
57
|
|
56
58
|
.toolbar-shadow.vertical .toolbar-item {
|
57
59
|
height: auto;
|
58
|
-
min-height:
|
60
|
+
min-height: var(--toolbar-height);
|
59
61
|
white-space: normal;
|
60
62
|
}
|
61
63
|
|
@@ -175,6 +177,29 @@ it. */
|
|
175
177
|
margin-inline-end: 28px;
|
176
178
|
}
|
177
179
|
|
180
|
+
.toolbar-button.copied-to-clipboard::after {
|
181
|
+
content: attr(data-content);
|
182
|
+
position: fixed;
|
183
|
+
top: var(--toolbar-height);
|
184
|
+
padding: 3px 5px;
|
185
|
+
color: var(--color-text-secondary);
|
186
|
+
background: var(--color-background-elevation-1);
|
187
|
+
animation: 2s fade-out;
|
188
|
+
font-weight: normal;
|
189
|
+
border: 1px solid var(--color-details-hairline);
|
190
|
+
border-radius: 3px;
|
191
|
+
}
|
192
|
+
|
193
|
+
@keyframes fade-out {
|
194
|
+
from {
|
195
|
+
opacity: 100%;
|
196
|
+
}
|
197
|
+
|
198
|
+
to {
|
199
|
+
opacity: 0%;
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
178
203
|
.toolbar-button.toolbar-state-on .toolbar-glyph {
|
179
204
|
background-color: var(--color-primary);
|
180
205
|
}
|
package/package.json
CHANGED
package/scripts/npm_test.js
CHANGED
@@ -91,7 +91,7 @@ function runTests(buildDirectoryPath, useDebugDevtools) {
|
|
91
91
|
|
92
92
|
if (IS_DEBUG_ENABLED) {
|
93
93
|
testArgs.push('--additional-driver-flag=--remote-debugging-port=9222');
|
94
|
-
testArgs.push('--
|
94
|
+
testArgs.push('--timeout-ms=6000000');
|
95
95
|
console.log('\n=============================================');
|
96
96
|
const unitTest = testArgs.find(arg => arg.includes('http/tests/devtools/unit/'));
|
97
97
|
if (unitTest) {
|