chrome-devtools-frontend 1.0.978673 → 1.0.979150
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/front_end/.eslintrc.js +1 -0
- package/front_end/core/sdk/CSSStyleSheetHeader.ts +21 -9
- package/front_end/entrypoints/formatter_worker/FormatterActions.ts +1 -0
- package/front_end/entrypoints/formatter_worker/FormatterWorker.ts +2 -2
- package/front_end/entrypoints/formatter_worker/Substitute.ts +8 -3
- package/front_end/entrypoints/formatter_worker/formatter_worker-entrypoint.ts +7 -1
- package/front_end/models/formatter/FormatterWorkerPool.ts +11 -3
- package/front_end/panels/console/ConsolePrompt.ts +8 -6
- package/front_end/panels/elements/StylesSidebarPane.ts +7 -11
- package/front_end/panels/elements/components/computedStyleProperty.css +1 -0
- package/front_end/panels/elements/components/computedStyleTrace.css +1 -0
- package/front_end/panels/webauthn/webauthnPane.css +6 -2
- package/package.json +1 -1
- package/scripts/eslint_rules/lib/no_importing_images_from_src.js +61 -0
- package/scripts/eslint_rules/tests/no_importing_images_from_src_test.js +40 -0
package/front_end/.eslintrc.js
CHANGED
@@ -27,6 +27,7 @@ module.exports = {
|
|
27
27
|
'files': ['*.ts'],
|
28
28
|
'rules': {
|
29
29
|
'@typescript-eslint/explicit-function-return-type': 2,
|
30
|
+
'rulesdir/no_importing_images_from_src': 2,
|
30
31
|
'rulesdir/enforce_custom_event_names': 2,
|
31
32
|
'rulesdir/set_data_type_reference': 2,
|
32
33
|
'rulesdir/lit_html_data_as_type': 2,
|
@@ -6,6 +6,7 @@ import * as TextUtils from '../../models/text_utils/text_utils.js';
|
|
6
6
|
import * as Common from '../common/common.js';
|
7
7
|
import * as i18n from '../i18n/i18n.js';
|
8
8
|
import type * as Platform from '../platform/platform.js';
|
9
|
+
import * as Root from '../root/root.js';
|
9
10
|
import type * as Protocol from '../../generated/protocol.js';
|
10
11
|
|
11
12
|
import type {CSSModel} from './CSSModel.js';
|
@@ -104,27 +105,38 @@ export class CSSStyleSheetHeader implements TextUtils.ContentProvider.ContentPro
|
|
104
105
|
}
|
105
106
|
|
106
107
|
resourceURL(): Platform.DevToolsPath.UrlString {
|
107
|
-
|
108
|
+
const url = this.isViaInspector() ? this.viaInspectorResourceURL() : this.sourceURL;
|
109
|
+
if (!url && Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.STYLES_PANE_CSS_CHANGES)) {
|
110
|
+
return this.dynamicStyleURL();
|
111
|
+
}
|
112
|
+
return url;
|
108
113
|
}
|
109
114
|
|
110
|
-
private
|
115
|
+
private getFrameURLPath(): string {
|
111
116
|
const model = this.#cssModelInternal.target().model(ResourceTreeModel);
|
112
117
|
console.assert(Boolean(model));
|
113
118
|
if (!model) {
|
114
|
-
return ''
|
119
|
+
return '';
|
115
120
|
}
|
116
121
|
const frame = model.frameForId(this.frameId);
|
117
122
|
if (!frame) {
|
118
|
-
return ''
|
123
|
+
return '';
|
119
124
|
}
|
120
125
|
console.assert(Boolean(frame));
|
121
126
|
const parsedURL = new Common.ParsedURL.ParsedURL(frame.url);
|
122
|
-
let
|
123
|
-
if (!
|
124
|
-
|
127
|
+
let urlPath = parsedURL.host + parsedURL.folderPathComponents;
|
128
|
+
if (!urlPath.endsWith('/')) {
|
129
|
+
urlPath += '/';
|
125
130
|
}
|
126
|
-
|
127
|
-
|
131
|
+
return urlPath;
|
132
|
+
}
|
133
|
+
|
134
|
+
private viaInspectorResourceURL(): Platform.DevToolsPath.UrlString {
|
135
|
+
return `inspector://${this.getFrameURLPath()}inspector-stylesheet` as Platform.DevToolsPath.UrlString;
|
136
|
+
}
|
137
|
+
|
138
|
+
private dynamicStyleURL(): Platform.DevToolsPath.UrlString {
|
139
|
+
return `stylesheet://${this.getFrameURLPath()}style#${this.id}` as Platform.DevToolsPath.UrlString;
|
128
140
|
}
|
129
141
|
|
130
142
|
lineNumberInSource(lineNumberInStyleSheet: number): number {
|
@@ -8,6 +8,7 @@ export const enum FormatterActions {
|
|
8
8
|
HTML_OUTLINE = 'htmlOutline',
|
9
9
|
JAVASCRIPT_OUTLINE = 'javaScriptOutline',
|
10
10
|
JAVASCRIPT_IDENTIFIERS = 'javaScriptIdentifiers',
|
11
|
+
JAVASCRIPT_SUBSTITUTE = 'javaScriptSubstitute',
|
11
12
|
EVALUATE_JAVASCRIPT_SUBSTRING = 'evaluatableJavaScriptSubstring',
|
12
13
|
ARGUMENTS_LIST = 'argumentsList',
|
13
14
|
}
|
@@ -42,7 +42,7 @@ import {HTMLFormatter} from './HTMLFormatter.js';
|
|
42
42
|
import {IdentityFormatter} from './IdentityFormatter.js';
|
43
43
|
import {JavaScriptFormatter} from './JavaScriptFormatter.js';
|
44
44
|
import {JSONFormatter} from './JSONFormatter.js';
|
45
|
-
import {
|
45
|
+
import {substituteExpression} from './Substitute.js';
|
46
46
|
|
47
47
|
export interface Chunk {
|
48
48
|
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
|
@@ -306,4 +306,4 @@ export function argumentsList(content: string): string[] {
|
|
306
306
|
}
|
307
307
|
})();
|
308
308
|
|
309
|
-
export {
|
309
|
+
export {substituteExpression};
|
@@ -6,7 +6,12 @@ import * as Acorn from '../../third_party/acorn/acorn.js';
|
|
6
6
|
|
7
7
|
import {ECMA_VERSION} from './AcornTokenizer.js';
|
8
8
|
|
9
|
-
export
|
9
|
+
export function substituteExpression(expression: string, nameMap: Map<string, string>): string {
|
10
|
+
const replacements = computeSubstitution(expression, nameMap);
|
11
|
+
return applySubstitution(expression, replacements);
|
12
|
+
}
|
13
|
+
|
14
|
+
interface Replacement {
|
10
15
|
from: string;
|
11
16
|
to: string;
|
12
17
|
offset: number;
|
@@ -17,7 +22,7 @@ export interface Replacement {
|
|
17
22
|
// function returns a list of replacements sorted by the offset. The function throws if
|
18
23
|
// it cannot parse the expression or the substitution is impossible to perform (for example
|
19
24
|
// if the substitution target is 'this' within a function, it would become bound there).
|
20
|
-
|
25
|
+
function computeSubstitution(expression: string, nameMap: Map<string, string>): Replacement[] {
|
21
26
|
// Parse the expression and find variables and scopes.
|
22
27
|
const root = Acorn.parse(expression, {ecmaVersion: ECMA_VERSION, allowAwaitOutsideFunction: true, ranges: false}) as
|
23
28
|
Acorn.ESTree.Node;
|
@@ -80,7 +85,7 @@ export function computeSubstitution(expression: string, nameMap: Map<string, str
|
|
80
85
|
return result;
|
81
86
|
}
|
82
87
|
|
83
|
-
|
88
|
+
function applySubstitution(expression: string, replacements: Replacement[]): string {
|
84
89
|
const accumulator = [];
|
85
90
|
let last = 0;
|
86
91
|
for (const r of replacements) {
|
@@ -10,7 +10,8 @@ import {FormatterActions} from './FormatterActions.js';
|
|
10
10
|
|
11
11
|
self.onmessage = function(event: MessageEvent): void {
|
12
12
|
const method: FormatterActions = event.data.method;
|
13
|
-
const params: {indentString: string, content: string, mimeType: string} =
|
13
|
+
const params: {indentString: string, content: string, mimeType: string, mapping: [string, string][]} =
|
14
|
+
event.data.params;
|
14
15
|
if (!method) {
|
15
16
|
return;
|
16
17
|
}
|
@@ -31,6 +32,11 @@ self.onmessage = function(event: MessageEvent): void {
|
|
31
32
|
case FormatterActions.JAVASCRIPT_IDENTIFIERS:
|
32
33
|
self.postMessage(FormatterWorker.FormatterWorker.javaScriptIdentifiers(params.content));
|
33
34
|
break;
|
35
|
+
case FormatterActions.JAVASCRIPT_SUBSTITUTE: {
|
36
|
+
const mapping = new Map<string, string>(params.mapping);
|
37
|
+
self.postMessage(FormatterWorker.Substitute.substituteExpression(params.content, mapping));
|
38
|
+
break;
|
39
|
+
}
|
34
40
|
case FormatterActions.EVALUATE_JAVASCRIPT_SUBSTRING:
|
35
41
|
self.postMessage(FormatterWorker.FormatterWorker.evaluatableJavaScriptSubstring(params.content));
|
36
42
|
break;
|
@@ -108,7 +108,7 @@ export class FormatterWorkerPool {
|
|
108
108
|
}
|
109
109
|
|
110
110
|
private runTask(methodName: FormatterActions.FormatterActions, params: {
|
111
|
-
[x: string]: string,
|
111
|
+
[x: string]: string|string[][],
|
112
112
|
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
|
113
113
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
114
114
|
}): Promise<any> {
|
@@ -132,6 +132,14 @@ export class FormatterWorkerPool {
|
|
132
132
|
.then(ids => ids || []);
|
133
133
|
}
|
134
134
|
|
135
|
+
javaScriptSubstitute(expression: string, mapping: Map<string, string>): Promise<string> {
|
136
|
+
return this
|
137
|
+
.runTask(
|
138
|
+
FormatterActions.FormatterActions.JAVASCRIPT_SUBSTITUTE,
|
139
|
+
{content: expression, mapping: Array.from(mapping.entries())})
|
140
|
+
.then(result => result || '');
|
141
|
+
}
|
142
|
+
|
135
143
|
evaluatableJavaScriptSubstring(content: string): Promise<string> {
|
136
144
|
return this.runTask(FormatterActions.FormatterActions.EVALUATE_JAVASCRIPT_SUBSTRING, {content: content})
|
137
145
|
.then(text => text || '');
|
@@ -179,13 +187,13 @@ export class FormatterWorkerPool {
|
|
179
187
|
class Task {
|
180
188
|
method: string;
|
181
189
|
params: {
|
182
|
-
[x: string]: string,
|
190
|
+
[x: string]: string|string[][],
|
183
191
|
};
|
184
192
|
callback: (arg0: MessageEvent|null) => void;
|
185
193
|
isChunked: boolean|undefined;
|
186
194
|
constructor(
|
187
195
|
method: string, params: {
|
188
|
-
[x: string]: string,
|
196
|
+
[x: string]: string|string[][],
|
189
197
|
},
|
190
198
|
callback: (arg0: MessageEvent|null) => void, isChunked?: boolean) {
|
191
199
|
this.method = method;
|
@@ -7,6 +7,7 @@ import * as Host from '../../core/host/host.js';
|
|
7
7
|
import * as i18n from '../../core/i18n/i18n.js';
|
8
8
|
import * as Root from '../../core/root/root.js';
|
9
9
|
import * as SDK from '../../core/sdk/sdk.js';
|
10
|
+
import * as Formatter from '../../models/formatter/formatter.js';
|
10
11
|
import * as SourceMapScopes from '../../models/source_map_scopes/source_map_scopes.js';
|
11
12
|
import * as CodeMirror from '../../third_party/codemirror.next/codemirror.next.js';
|
12
13
|
import * as TextEditor from '../../ui/components/text_editor/text_editor.js';
|
@@ -309,7 +310,7 @@ export class ConsolePrompt extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
309
310
|
const callFrame = executionContext.debuggerModel.selectedCallFrame();
|
310
311
|
if (callFrame) {
|
311
312
|
const nameMap = await SourceMapScopes.NamesResolver.allVariablesInCallFrame(callFrame);
|
312
|
-
expression = this.substituteNames(expression, nameMap);
|
313
|
+
expression = await this.substituteNames(expression, nameMap);
|
313
314
|
}
|
314
315
|
}
|
315
316
|
|
@@ -317,11 +318,12 @@ export class ConsolePrompt extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
317
318
|
executionContext, message, expression, useCommandLineAPI);
|
318
319
|
}
|
319
320
|
|
320
|
-
private substituteNames(expression: string, mapping: Map<string, string>): string {
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
321
|
+
private async substituteNames(expression: string, mapping: Map<string, string>): Promise<string> {
|
322
|
+
try {
|
323
|
+
return await Formatter.FormatterWorkerPool.formatterWorkerPool().javaScriptSubstitute(expression, mapping);
|
324
|
+
} catch {
|
325
|
+
return expression;
|
326
|
+
}
|
325
327
|
}
|
326
328
|
|
327
329
|
private editorUpdate(update: CodeMirror.ViewUpdate): void {
|
@@ -219,9 +219,7 @@ const HIGHLIGHTABLE_PROPERTIES = [
|
|
219
219
|
{mode: 'flexibility', properties: ['flex', 'flex-basis', 'flex-grow', 'flex-shrink']},
|
220
220
|
];
|
221
221
|
|
222
|
-
|
223
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
224
|
-
let _stylesSidebarPaneInstance: StylesSidebarPane;
|
222
|
+
let stylesSidebarPaneInstance: StylesSidebarPane;
|
225
223
|
|
226
224
|
// TODO(crbug.com/1172300) This workaround is needed to keep the linter happy.
|
227
225
|
// Otherwise it complains about: Unknown word CssSyntaxError
|
@@ -258,10 +256,10 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
258
256
|
#urlToChangeTracker: Map<string, ChangeTracker> = new Map();
|
259
257
|
|
260
258
|
static instance(): StylesSidebarPane {
|
261
|
-
if (!
|
262
|
-
|
259
|
+
if (!stylesSidebarPaneInstance) {
|
260
|
+
stylesSidebarPaneInstance = new StylesSidebarPane();
|
263
261
|
}
|
264
|
-
return
|
262
|
+
return stylesSidebarPaneInstance;
|
265
263
|
}
|
266
264
|
|
267
265
|
private constructor() {
|
@@ -292,7 +290,7 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
292
290
|
this.swatchPopoverHelperInternal = new InlineEditor.SwatchPopoverHelper.SwatchPopoverHelper();
|
293
291
|
this.swatchPopoverHelperInternal.addEventListener(
|
294
292
|
InlineEditor.SwatchPopoverHelper.Events.WillShowPopover, this.hideAllPopovers, this);
|
295
|
-
this.linkifier = new Components.Linkifier.Linkifier(
|
293
|
+
this.linkifier = new Components.Linkifier.Linkifier(MAX_LINK_LENGTH, /* useLinkDecorator */ true);
|
296
294
|
this.decorator = new StylePropertyHighlighter(this);
|
297
295
|
this.lastRevealedProperty = null;
|
298
296
|
this.userOperation = false;
|
@@ -307,7 +305,7 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
307
305
|
this.sectionBlocks = [];
|
308
306
|
this.idleCallbackManager = null;
|
309
307
|
this.needsForceUpdate = false;
|
310
|
-
|
308
|
+
stylesSidebarPaneInstance = this;
|
311
309
|
UI.Context.Context.instance().addFlavorChangeListener(SDK.DOMModel.DOMNode, this.forceUpdate, this);
|
312
310
|
this.contentElement.addEventListener('copy', this.clipboardCopy.bind(this));
|
313
311
|
this.resizeThrottler = new Common.Throttler.Throttler(100);
|
@@ -1392,9 +1390,7 @@ async function buildPropertyRuleMaps(content: string):
|
|
1392
1390
|
return {propertyToSelector, ruleToSelector};
|
1393
1391
|
}
|
1394
1392
|
|
1395
|
-
|
1396
|
-
// eslint-disable-next-line @typescript-eslint/naming-convention
|
1397
|
-
export const _maxLinkLength = 23;
|
1393
|
+
const MAX_LINK_LENGTH = 23;
|
1398
1394
|
|
1399
1395
|
export class SectionBlock {
|
1400
1396
|
private readonly titleElementInternal: Element|null;
|
@@ -75,14 +75,18 @@
|
|
75
75
|
margin: auto;
|
76
76
|
}
|
77
77
|
|
78
|
+
.authenticator-section-header {
|
79
|
+
display: flex;
|
80
|
+
justify-content: space-between;
|
81
|
+
align-items: flex-end;
|
82
|
+
}
|
83
|
+
|
78
84
|
.authenticator-section-title {
|
79
85
|
line-height: 24px;
|
80
|
-
width: 260px;
|
81
86
|
display: inline-block;
|
82
87
|
}
|
83
88
|
|
84
89
|
.authenticator-section-title .authenticator-name-field {
|
85
|
-
width: 220px;
|
86
90
|
display: inline-block;
|
87
91
|
font-weight: bold;
|
88
92
|
border: none;
|
package/package.json
CHANGED
@@ -0,0 +1,61 @@
|
|
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
|
+
'use strict';
|
5
|
+
|
6
|
+
/**
|
7
|
+
* @fileoverview Prevent importing SVG urls from the `src` directory, and
|
8
|
+
* ensure they are read from `Images/foo.svg`.
|
9
|
+
* Images in the `src/` directory are minified and put into `Images/` as part
|
10
|
+
* of the build process, so we should never import from 'src'.
|
11
|
+
*/
|
12
|
+
|
13
|
+
// ------------------------------------------------------------------------------
|
14
|
+
// Rule Definition
|
15
|
+
// ------------------------------------------------------------------------------
|
16
|
+
|
17
|
+
const SRC_DIRECTORY_PATH_TO_MATCH = 'Images/src/';
|
18
|
+
|
19
|
+
module.exports = {
|
20
|
+
meta: {
|
21
|
+
type: 'problem',
|
22
|
+
|
23
|
+
docs: {
|
24
|
+
description: 'ensure image imports do not include the src/ directory',
|
25
|
+
category: 'Possible Errors',
|
26
|
+
},
|
27
|
+
fixable: 'code',
|
28
|
+
schema: [],
|
29
|
+
messages: {
|
30
|
+
imageImportUsingSrc:
|
31
|
+
'Found an image import containing the `src/` directory. You should always import `Images/foo.svg`.',
|
32
|
+
},
|
33
|
+
},
|
34
|
+
create: function(context) {
|
35
|
+
return {
|
36
|
+
// Matches new URL(...)
|
37
|
+
'NewExpression[callee.name=\'URL\']'(node) {
|
38
|
+
if (!node.arguments || node.arguments.length < 1) {
|
39
|
+
// Invalid code: user is probably mid-way through typing! Just leave
|
40
|
+
// it; TypeScript will error if it ends up being invalid.
|
41
|
+
return;
|
42
|
+
}
|
43
|
+
/** @type {String} */
|
44
|
+
const filePath = node.arguments[0].value;
|
45
|
+
if (!filePath) {
|
46
|
+
return;
|
47
|
+
}
|
48
|
+
if (filePath.includes(SRC_DIRECTORY_PATH_TO_MATCH)) {
|
49
|
+
context.report({
|
50
|
+
node: node.arguments[0],
|
51
|
+
messageId: 'imageImportUsingSrc',
|
52
|
+
fix(fixer) {
|
53
|
+
return fixer.replaceText(
|
54
|
+
node.arguments[0], `'${filePath.replace(SRC_DIRECTORY_PATH_TO_MATCH, 'Images/')}'`);
|
55
|
+
}
|
56
|
+
});
|
57
|
+
}
|
58
|
+
}
|
59
|
+
};
|
60
|
+
}
|
61
|
+
};
|
@@ -0,0 +1,40 @@
|
|
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
|
+
'use strict';
|
5
|
+
|
6
|
+
const rule = require('../lib/no_importing_images_from_src.js');
|
7
|
+
const ruleTester = new (require('eslint').RuleTester)({
|
8
|
+
parser: require.resolve('@typescript-eslint/parser'),
|
9
|
+
parserOptions: {ecmaVersion: 9, sourceType: 'module'},
|
10
|
+
});
|
11
|
+
|
12
|
+
ruleTester.run('no_importing_images_from_src', rule, {
|
13
|
+
valid: [
|
14
|
+
{
|
15
|
+
code: 'const someIcon = new URL(\'../../../Images/test_icon.svg\', import.meta.url).toString()',
|
16
|
+
filename: 'front_end/ui/components/component/file.ts',
|
17
|
+
},
|
18
|
+
],
|
19
|
+
|
20
|
+
invalid: [
|
21
|
+
{
|
22
|
+
code: 'const someIcon = new URL(\'../../../Images/src/test_icon.svg\', import.meta.url).toString()',
|
23
|
+
filename: 'front_end/ui/components/component/file.ts',
|
24
|
+
output: 'const someIcon = new URL(\'../../../Images/test_icon.svg\', import.meta.url).toString()',
|
25
|
+
errors: [{
|
26
|
+
messageId: 'imageImportUsingSrc',
|
27
|
+
}],
|
28
|
+
},
|
29
|
+
{
|
30
|
+
code:
|
31
|
+
'const someIcon = new URL(\'../../../devtools-frontend/front_end/Images/src/test_icon.svg\', import.meta.url).toString()',
|
32
|
+
filename: 'front_end/ui/components/component/file.ts',
|
33
|
+
output:
|
34
|
+
'const someIcon = new URL(\'../../../devtools-frontend/front_end/Images/test_icon.svg\', import.meta.url).toString()',
|
35
|
+
errors: [{
|
36
|
+
messageId: 'imageImportUsingSrc',
|
37
|
+
}],
|
38
|
+
},
|
39
|
+
],
|
40
|
+
});
|