chrome-devtools-frontend 1.0.1034366 → 1.0.1034802
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/core/host/InspectorFrontendHostAPI.ts +1 -0
- package/front_end/core/host/UserMetrics.ts +23 -0
- package/front_end/entrypoints/lighthouse_worker/LighthouseWorkerService.ts +18 -7
- package/front_end/panels/elements/CSSRuleValidator.ts +169 -101
- package/front_end/panels/elements/CSSRuleValidatorHelper.ts +13 -7
- package/front_end/panels/elements/StylePropertiesSection.ts +8 -0
- package/front_end/panels/elements/StylePropertyTreeElement.ts +35 -31
- package/front_end/panels/elements/StylesSidebarPane.ts +24 -0
- package/front_end/panels/elements/components/CSSHintDetailsView.ts +5 -5
- package/front_end/panels/elements/components/LayoutPane.ts +1 -1
- package/front_end/panels/lighthouse/LighthouseProtocolService.ts +5 -5
- package/package.json +1 -1
@@ -277,6 +277,11 @@ export class UserMetrics {
|
|
277
277
|
InspectorFrontendHostInstance.recordEnumeratedHistogram(
|
278
278
|
EnumeratedHistogram.ManifestSectionSelected, code, ManifestSectionCodes.MaxValue);
|
279
279
|
}
|
280
|
+
|
281
|
+
cssHintShown(type: CSSHintType): void {
|
282
|
+
InspectorFrontendHostInstance.recordEnumeratedHistogram(
|
283
|
+
EnumeratedHistogram.CSSHintShown, type, CSSHintType.MaxValue);
|
284
|
+
}
|
280
285
|
}
|
281
286
|
|
282
287
|
/**
|
@@ -993,4 +998,22 @@ export enum ManifestSectionCodes {
|
|
993
998
|
MaxValue = 5,
|
994
999
|
}
|
995
1000
|
|
1001
|
+
// The names here match the CSSRuleValidator names in CSSRuleValidator.ts.
|
1002
|
+
// TODO(crbug.com/1167717): Make this a const enum again
|
1003
|
+
// eslint-disable-next-line rulesdir/const_enum
|
1004
|
+
export enum CSSHintType {
|
1005
|
+
Other = 0,
|
1006
|
+
AlignContent = 1,
|
1007
|
+
FlexItem = 2,
|
1008
|
+
FlexContainer = 3,
|
1009
|
+
GridContainer = 4,
|
1010
|
+
GridItem = 5,
|
1011
|
+
FlexGrid = 6,
|
1012
|
+
MulticolFlexGrid = 7,
|
1013
|
+
Padding = 8,
|
1014
|
+
Position = 9,
|
1015
|
+
ZIndex = 10,
|
1016
|
+
MaxValue = 11,
|
1017
|
+
}
|
1018
|
+
|
996
1019
|
/* eslint-enable @typescript-eslint/naming-convention */
|
@@ -222,25 +222,36 @@ async function fetchLocaleData(locales: string[]): Promise<string|void> {
|
|
222
222
|
*/
|
223
223
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
224
224
|
function notifyFrontendViaWorkerMessage(action: string, args: any): void {
|
225
|
-
self.postMessage(
|
225
|
+
self.postMessage({action, args});
|
226
226
|
}
|
227
227
|
|
228
228
|
async function onFrontendMessage(event: MessageEvent): Promise<void> {
|
229
|
-
const messageFromFrontend =
|
229
|
+
const messageFromFrontend = event.data;
|
230
230
|
switch (messageFromFrontend.action) {
|
231
231
|
case 'startTimespan':
|
232
232
|
case 'endTimespan':
|
233
233
|
case 'snapshot':
|
234
234
|
case 'navigation': {
|
235
235
|
const result = await invokeLH(messageFromFrontend.action, messageFromFrontend.args);
|
236
|
-
|
236
|
+
if (result && typeof result === 'object') {
|
237
|
+
// Report isn't used upstream.
|
238
|
+
if ('report' in result) {
|
239
|
+
// @ts-expect-error
|
240
|
+
delete result.report;
|
241
|
+
}
|
242
|
+
|
243
|
+
// Logger PerformanceTiming objects cannot be cloned by this worker's `postMessage` function.
|
244
|
+
if ('artifacts' in result) {
|
245
|
+
// @ts-expect-error
|
246
|
+
result.artifacts.Timing = JSON.parse(JSON.stringify(result.artifacts.Timing));
|
247
|
+
}
|
248
|
+
}
|
249
|
+
self.postMessage({id: messageFromFrontend.id, result});
|
237
250
|
break;
|
238
251
|
}
|
239
252
|
case 'dispatchProtocolMessage': {
|
240
|
-
cdpConnection?.onMessage?.(
|
241
|
-
|
242
|
-
);
|
243
|
-
legacyPort.onMessage?.(messageFromFrontend.args.message);
|
253
|
+
cdpConnection?.onMessage?.(messageFromFrontend.args.message);
|
254
|
+
legacyPort.onMessage?.(JSON.stringify(messageFromFrontend.args.message));
|
244
255
|
break;
|
245
256
|
}
|
246
257
|
default: {
|
@@ -2,11 +2,12 @@
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
3
3
|
// found in the LICENSE file.
|
4
4
|
|
5
|
+
import * as Host from '../../core/host/host.js';
|
5
6
|
import * as i18n from '../../core/i18n/i18n.js';
|
6
7
|
|
7
8
|
import {
|
8
|
-
|
9
|
-
|
9
|
+
buildPropertyDefinitionText,
|
10
|
+
buildPropertyText,
|
10
11
|
isFlexContainer,
|
11
12
|
isGridContainer,
|
12
13
|
isMulticolContainer,
|
@@ -58,35 +59,34 @@ const UIStrings = {
|
|
58
59
|
const str_ = i18n.i18n.registerUIStrings('panels/elements/CSSRuleValidator.ts', UIStrings);
|
59
60
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
60
61
|
|
61
|
-
export const enum
|
62
|
+
export const enum HintType {
|
62
63
|
INACTIVE_PROPERTY = 'ruleValidation',
|
63
64
|
DEPRECATED_PROPERTY = 'deprecatedProperty',
|
64
65
|
}
|
65
66
|
|
66
|
-
export class
|
67
|
-
readonly #hintType:
|
67
|
+
export class Hint {
|
68
|
+
readonly #hintType: HintType;
|
68
69
|
readonly #hintMessage: string;
|
69
70
|
readonly #possibleFixMessage: string|null;
|
70
71
|
readonly #learnMoreLink: string|undefined;
|
71
72
|
|
72
|
-
constructor(
|
73
|
-
hintType: AuthoringHintType, hintMessage: string, possibleFixMessage: string|null, learnMoreLink?: string) {
|
73
|
+
constructor(hintType: HintType, hintMessage: string, possibleFixMessage: string|null, learnMoreLink?: string) {
|
74
74
|
this.#hintType = hintType;
|
75
75
|
this.#hintMessage = hintMessage;
|
76
76
|
this.#possibleFixMessage = possibleFixMessage;
|
77
77
|
this.#learnMoreLink = learnMoreLink;
|
78
78
|
}
|
79
79
|
|
80
|
-
|
80
|
+
getPrefix(): string {
|
81
81
|
switch (this.#hintType) {
|
82
|
-
case
|
82
|
+
case HintType.INACTIVE_PROPERTY:
|
83
83
|
return i18nString(UIStrings.inactivePropertyHintPrefix);
|
84
|
-
case
|
84
|
+
case HintType.DEPRECATED_PROPERTY:
|
85
85
|
return i18nString(UIStrings.deprecatedPropertyHintPrefix);
|
86
86
|
}
|
87
87
|
}
|
88
88
|
|
89
|
-
|
89
|
+
getMessage(): string {
|
90
90
|
return this.#hintMessage;
|
91
91
|
}
|
92
92
|
|
@@ -100,25 +100,23 @@ export class AuthoringHint {
|
|
100
100
|
}
|
101
101
|
|
102
102
|
export abstract class CSSRuleValidator {
|
103
|
+
getMetricType(): Host.UserMetrics.CSSHintType {
|
104
|
+
return Host.UserMetrics.CSSHintType.Other;
|
105
|
+
}
|
106
|
+
|
103
107
|
readonly #affectedProperties: string[];
|
104
108
|
|
105
109
|
constructor(affectedProperties: string[]) {
|
106
110
|
this.#affectedProperties = affectedProperties;
|
107
111
|
}
|
108
112
|
|
109
|
-
|
110
|
-
* If `isRuleValid` returns false, it means there is a hint to be shown. The hint is retrieved by invoking `getAuthoringHint`.
|
111
|
-
*/
|
112
|
-
abstract isRuleValid(computedStyles: Map<String, String>|null, parentsComputedStyles?: Map<String, String>|null):
|
113
|
-
boolean;
|
114
|
-
|
115
|
-
getAffectedProperties(): string[] {
|
113
|
+
getApplicableProperties(): string[] {
|
116
114
|
return this.#affectedProperties;
|
117
115
|
}
|
118
116
|
|
119
|
-
abstract
|
120
|
-
propertyName: string, computedStyles
|
121
|
-
|
117
|
+
abstract getHint(
|
118
|
+
propertyName: string, computedStyles?: Map<string, string>, parentComputedStyles?: Map<string, string>): Hint
|
119
|
+
|undefined;
|
122
120
|
}
|
123
121
|
|
124
122
|
export class AlignContentValidator extends CSSRuleValidator {
|
@@ -126,7 +124,11 @@ export class AlignContentValidator extends CSSRuleValidator {
|
|
126
124
|
super(['align-content']);
|
127
125
|
}
|
128
126
|
|
129
|
-
|
127
|
+
getMetricType(): Host.UserMetrics.CSSHintType {
|
128
|
+
return Host.UserMetrics.CSSHintType.AlignContent;
|
129
|
+
}
|
130
|
+
|
131
|
+
#isRuleValid(computedStyles?: Map<string, string>): boolean {
|
130
132
|
if (computedStyles === null || computedStyles === undefined) {
|
131
133
|
return true;
|
132
134
|
}
|
@@ -136,12 +138,15 @@ export class AlignContentValidator extends CSSRuleValidator {
|
|
136
138
|
return computedStyles.get('flex-wrap') !== 'nowrap';
|
137
139
|
}
|
138
140
|
|
139
|
-
|
140
|
-
|
141
|
-
|
141
|
+
getHint(_propertyName: string, computedStyles?: Map<string, string>): Hint|undefined {
|
142
|
+
if (this.#isRuleValid(computedStyles)) {
|
143
|
+
return;
|
144
|
+
}
|
145
|
+
const reasonPropertyDeclaration = buildPropertyText('flex-wrap');
|
146
|
+
const affectedPropertyDeclarationCode = buildPropertyText('align-content');
|
142
147
|
|
143
|
-
return new
|
144
|
-
|
148
|
+
return new Hint(
|
149
|
+
HintType.INACTIVE_PROPERTY,
|
145
150
|
i18nString(UIStrings.ruleViolatedBySameElementRuleReason, {
|
146
151
|
'REASON_PROPERTY_DECLARATION_CODE': reasonPropertyDeclaration,
|
147
152
|
'AFFECTED_PROPERTY_DECLARATION_CODE': affectedPropertyDeclarationCode,
|
@@ -158,22 +163,28 @@ export class FlexItemValidator extends CSSRuleValidator {
|
|
158
163
|
super(['flex', 'flex-basis', 'flex-grow', 'flex-shrink']);
|
159
164
|
}
|
160
165
|
|
161
|
-
|
162
|
-
|
166
|
+
getMetricType(): Host.UserMetrics.CSSHintType {
|
167
|
+
return Host.UserMetrics.CSSHintType.FlexItem;
|
168
|
+
}
|
169
|
+
|
170
|
+
#isRuleValid(computedStyles?: Map<string, string>, parentComputedStyles?: Map<string, string>): boolean {
|
171
|
+
if (parentComputedStyles === null) {
|
163
172
|
return true;
|
164
173
|
}
|
165
|
-
return isFlexContainer(
|
174
|
+
return isFlexContainer(parentComputedStyles);
|
166
175
|
}
|
167
176
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
const
|
177
|
+
getHint(propertyName: string, computedStyles?: Map<string, string>, parentComputedStyles?: Map<string, string>): Hint
|
178
|
+
|undefined {
|
179
|
+
if (this.#isRuleValid(computedStyles, parentComputedStyles)) {
|
180
|
+
return;
|
181
|
+
}
|
182
|
+
const reasonPropertyDeclaration = buildPropertyDefinitionText('display', parentComputedStyles?.get('display'));
|
183
|
+
const affectedPropertyDeclarationCode = buildPropertyText(propertyName);
|
184
|
+
const targeParentPropertyDeclaration = buildPropertyDefinitionText('display', 'flex');
|
174
185
|
|
175
|
-
return new
|
176
|
-
|
186
|
+
return new Hint(
|
187
|
+
HintType.INACTIVE_PROPERTY,
|
177
188
|
i18nString(UIStrings.ruleViolatedByParentElementRuleReason, {
|
178
189
|
'REASON_PROPERTY_DECLARATION_CODE': reasonPropertyDeclaration,
|
179
190
|
'AFFECTED_PROPERTY_DECLARATION_CODE': affectedPropertyDeclarationCode,
|
@@ -191,20 +202,27 @@ export class FlexContainerValidator extends CSSRuleValidator {
|
|
191
202
|
super(['flex-direction', 'flex-flow', 'flex-wrap', 'justify-content']);
|
192
203
|
}
|
193
204
|
|
194
|
-
|
205
|
+
getMetricType(): Host.UserMetrics.CSSHintType {
|
206
|
+
return Host.UserMetrics.CSSHintType.FlexContainer;
|
207
|
+
}
|
208
|
+
|
209
|
+
#isRuleValid(computedStyles?: Map<string, string>): boolean {
|
195
210
|
if (computedStyles === null) {
|
196
211
|
return true;
|
197
212
|
}
|
198
213
|
return isFlexContainer(computedStyles);
|
199
214
|
}
|
200
215
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
216
|
+
getHint(propertyName: string, computedStyles?: Map<string, string>): Hint|undefined {
|
217
|
+
if (this.#isRuleValid(computedStyles)) {
|
218
|
+
return;
|
219
|
+
}
|
220
|
+
const reasonPropertyDeclaration = buildPropertyDefinitionText('display', computedStyles?.get('display'));
|
221
|
+
const targetRuleCode = buildPropertyDefinitionText('display', 'flex');
|
222
|
+
const affectedPropertyDeclarationCode = buildPropertyText(propertyName);
|
205
223
|
|
206
|
-
return new
|
207
|
-
|
224
|
+
return new Hint(
|
225
|
+
HintType.INACTIVE_PROPERTY,
|
208
226
|
i18nString(UIStrings.ruleViolatedBySameElementRuleReason, {
|
209
227
|
'REASON_PROPERTY_DECLARATION_CODE': reasonPropertyDeclaration,
|
210
228
|
'AFFECTED_PROPERTY_DECLARATION_CODE': affectedPropertyDeclarationCode,
|
@@ -231,20 +249,24 @@ export class GridContainerValidator extends CSSRuleValidator {
|
|
231
249
|
]);
|
232
250
|
}
|
233
251
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
252
|
+
getMetricType(): Host.UserMetrics.CSSHintType {
|
253
|
+
return Host.UserMetrics.CSSHintType.GridContainer;
|
254
|
+
}
|
255
|
+
|
256
|
+
#isRuleValid(computedStyles?: Map<string, string>): boolean {
|
238
257
|
return isGridContainer(computedStyles);
|
239
258
|
}
|
240
259
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
260
|
+
getHint(propertyName: string, computedStyles?: Map<string, string>): Hint|undefined {
|
261
|
+
if (this.#isRuleValid(computedStyles)) {
|
262
|
+
return;
|
263
|
+
}
|
264
|
+
const reasonPropertyDeclaration = buildPropertyDefinitionText('display', computedStyles?.get('display'));
|
265
|
+
const targetRuleCode = buildPropertyDefinitionText('display', 'grid');
|
266
|
+
const affectedPropertyDeclarationCode = buildPropertyText(propertyName);
|
245
267
|
|
246
|
-
return new
|
247
|
-
|
268
|
+
return new Hint(
|
269
|
+
HintType.INACTIVE_PROPERTY,
|
248
270
|
i18nString(UIStrings.ruleViolatedBySameElementRuleReason, {
|
249
271
|
'REASON_PROPERTY_DECLARATION_CODE': reasonPropertyDeclaration,
|
250
272
|
'AFFECTED_PROPERTY_DECLARATION_CODE': affectedPropertyDeclarationCode,
|
@@ -268,22 +290,28 @@ export class GridItemValidator extends CSSRuleValidator {
|
|
268
290
|
]);
|
269
291
|
}
|
270
292
|
|
271
|
-
|
272
|
-
|
293
|
+
getMetricType(): Host.UserMetrics.CSSHintType {
|
294
|
+
return Host.UserMetrics.CSSHintType.GridItem;
|
295
|
+
}
|
296
|
+
|
297
|
+
#isRuleValid(computedStyles?: Map<string, string>, parentComputedStyles?: Map<string, string>): boolean {
|
298
|
+
if (!parentComputedStyles) {
|
273
299
|
return true;
|
274
300
|
}
|
275
301
|
return isGridContainer(parentComputedStyles);
|
276
302
|
}
|
277
303
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
const
|
304
|
+
getHint(propertyName: string, computedStyles?: Map<string, string>, parentComputedStyles?: Map<string, string>): Hint
|
305
|
+
|undefined {
|
306
|
+
if (this.#isRuleValid(computedStyles, parentComputedStyles)) {
|
307
|
+
return;
|
308
|
+
}
|
309
|
+
const reasonPropertyDeclaration = buildPropertyDefinitionText('display', parentComputedStyles?.get('display'));
|
310
|
+
const targeParentPropertyDeclaration = buildPropertyDefinitionText('display', 'grid');
|
311
|
+
const affectedPropertyDeclarationCode = buildPropertyText(propertyName);
|
284
312
|
|
285
|
-
return new
|
286
|
-
|
313
|
+
return new Hint(
|
314
|
+
HintType.INACTIVE_PROPERTY,
|
287
315
|
i18nString(UIStrings.ruleViolatedByParentElementRuleReason, {
|
288
316
|
'REASON_PROPERTY_DECLARATION_CODE': reasonPropertyDeclaration,
|
289
317
|
'AFFECTED_PROPERTY_DECLARATION_CODE': affectedPropertyDeclarationCode,
|
@@ -306,19 +334,27 @@ export class FlexGridValidator extends CSSRuleValidator {
|
|
306
334
|
]);
|
307
335
|
}
|
308
336
|
|
309
|
-
|
337
|
+
getMetricType(): Host.UserMetrics.CSSHintType {
|
338
|
+
return Host.UserMetrics.CSSHintType.FlexGrid;
|
339
|
+
}
|
340
|
+
|
341
|
+
#isRuleValid(computedStyles?: Map<string, string>): boolean {
|
310
342
|
if (computedStyles === null) {
|
311
343
|
return true;
|
312
344
|
}
|
313
345
|
return isFlexContainer(computedStyles) || isGridContainer(computedStyles);
|
314
346
|
}
|
315
347
|
|
316
|
-
|
317
|
-
|
318
|
-
|
348
|
+
getHint(propertyName: string, computedStyles?: Map<string, string>): Hint|undefined {
|
349
|
+
if (this.#isRuleValid(computedStyles)) {
|
350
|
+
return;
|
351
|
+
}
|
352
|
+
|
353
|
+
const reasonPropertyDeclaration = buildPropertyDefinitionText('display', computedStyles?.get('display'));
|
354
|
+
const affectedPropertyDeclarationCode = buildPropertyText(propertyName);
|
319
355
|
|
320
|
-
return new
|
321
|
-
|
356
|
+
return new Hint(
|
357
|
+
HintType.INACTIVE_PROPERTY,
|
322
358
|
i18nString(UIStrings.ruleViolatedBySameElementRuleReason, {
|
323
359
|
'REASON_PROPERTY_DECLARATION_CODE': reasonPropertyDeclaration,
|
324
360
|
'AFFECTED_PROPERTY_DECLARATION_CODE': affectedPropertyDeclarationCode,
|
@@ -343,19 +379,27 @@ export class MulticolFlexGridValidator extends CSSRuleValidator {
|
|
343
379
|
]);
|
344
380
|
}
|
345
381
|
|
346
|
-
|
382
|
+
getMetricType(): Host.UserMetrics.CSSHintType {
|
383
|
+
return Host.UserMetrics.CSSHintType.MulticolFlexGrid;
|
384
|
+
}
|
385
|
+
|
386
|
+
#isRuleValid(computedStyles?: Map<string, string>): boolean {
|
347
387
|
if (computedStyles === null) {
|
348
388
|
return true;
|
349
389
|
}
|
350
390
|
return isMulticolContainer(computedStyles) || isFlexContainer(computedStyles) || isGridContainer(computedStyles);
|
351
391
|
}
|
352
392
|
|
353
|
-
|
354
|
-
|
355
|
-
|
393
|
+
getHint(propertyName: string, computedStyles?: Map<string, string>): Hint|undefined {
|
394
|
+
if (this.#isRuleValid(computedStyles)) {
|
395
|
+
return;
|
396
|
+
}
|
356
397
|
|
357
|
-
|
358
|
-
|
398
|
+
const reasonPropertyDeclaration = buildPropertyDefinitionText('display', computedStyles?.get('display'));
|
399
|
+
const affectedPropertyDeclarationCode = buildPropertyText(propertyName);
|
400
|
+
|
401
|
+
return new Hint(
|
402
|
+
HintType.INACTIVE_PROPERTY,
|
359
403
|
i18nString(UIStrings.ruleViolatedBySameElementRuleReason, {
|
360
404
|
'REASON_PROPERTY_DECLARATION_CODE': reasonPropertyDeclaration,
|
361
405
|
'AFFECTED_PROPERTY_DECLARATION_CODE': affectedPropertyDeclarationCode,
|
@@ -378,7 +422,11 @@ export class PaddingValidator extends CSSRuleValidator {
|
|
378
422
|
]);
|
379
423
|
}
|
380
424
|
|
381
|
-
|
425
|
+
getMetricType(): Host.UserMetrics.CSSHintType {
|
426
|
+
return Host.UserMetrics.CSSHintType.Padding;
|
427
|
+
}
|
428
|
+
|
429
|
+
#isRuleValid(computedStyles?: Map<string, string>): boolean {
|
382
430
|
const display = computedStyles?.get('display');
|
383
431
|
if (display === null || display === undefined) {
|
384
432
|
return true;
|
@@ -388,12 +436,16 @@ export class PaddingValidator extends CSSRuleValidator {
|
|
388
436
|
.includes(display as string);
|
389
437
|
}
|
390
438
|
|
391
|
-
|
392
|
-
|
393
|
-
|
439
|
+
getHint(propertyName: string, computedStyles?: Map<string, string>): Hint|undefined {
|
440
|
+
if (this.#isRuleValid(computedStyles)) {
|
441
|
+
return;
|
442
|
+
}
|
443
|
+
|
444
|
+
const reasonPropertyDeclaration = buildPropertyDefinitionText('display', computedStyles?.get('display'));
|
445
|
+
const affectedPropertyDeclarationCode = buildPropertyText(propertyName);
|
394
446
|
|
395
|
-
return new
|
396
|
-
|
447
|
+
return new Hint(
|
448
|
+
HintType.INACTIVE_PROPERTY,
|
397
449
|
i18nString(UIStrings.ruleViolatedBySameElementRuleReason, {
|
398
450
|
'REASON_PROPERTY_DECLARATION_CODE': reasonPropertyDeclaration,
|
399
451
|
'AFFECTED_PROPERTY_DECLARATION_CODE': affectedPropertyDeclarationCode,
|
@@ -415,7 +467,11 @@ export class PositionValidator extends CSSRuleValidator {
|
|
415
467
|
]);
|
416
468
|
}
|
417
469
|
|
418
|
-
|
470
|
+
getMetricType(): Host.UserMetrics.CSSHintType {
|
471
|
+
return Host.UserMetrics.CSSHintType.Position;
|
472
|
+
}
|
473
|
+
|
474
|
+
#isRuleValid(computedStyles?: Map<string, string>): boolean {
|
419
475
|
const position = computedStyles?.get('position');
|
420
476
|
if (position === null || position === undefined) {
|
421
477
|
return true;
|
@@ -423,12 +479,16 @@ export class PositionValidator extends CSSRuleValidator {
|
|
423
479
|
return position !== 'static';
|
424
480
|
}
|
425
481
|
|
426
|
-
|
427
|
-
|
428
|
-
|
482
|
+
getHint(propertyName: string, computedStyles?: Map<string, string>): Hint|undefined {
|
483
|
+
if (this.#isRuleValid(computedStyles)) {
|
484
|
+
return;
|
485
|
+
}
|
429
486
|
|
430
|
-
|
431
|
-
|
487
|
+
const reasonPropertyDeclaration = buildPropertyDefinitionText('position', computedStyles?.get('position'));
|
488
|
+
const affectedPropertyDeclarationCode = buildPropertyText(propertyName);
|
489
|
+
|
490
|
+
return new Hint(
|
491
|
+
HintType.INACTIVE_PROPERTY,
|
432
492
|
i18nString(UIStrings.ruleViolatedBySameElementRuleReason, {
|
433
493
|
'REASON_PROPERTY_DECLARATION_CODE': reasonPropertyDeclaration,
|
434
494
|
'AFFECTED_PROPERTY_DECLARATION_CODE': affectedPropertyDeclarationCode,
|
@@ -447,21 +507,29 @@ export class ZIndexValidator extends CSSRuleValidator {
|
|
447
507
|
]);
|
448
508
|
}
|
449
509
|
|
450
|
-
|
510
|
+
getMetricType(): Host.UserMetrics.CSSHintType {
|
511
|
+
return Host.UserMetrics.CSSHintType.ZIndex;
|
512
|
+
}
|
513
|
+
|
514
|
+
#isRuleValid(computedStyles?: Map<string, string>, parentComputedStyles?: Map<string, string>): boolean {
|
451
515
|
const position = computedStyles?.get('position');
|
452
516
|
if (position === null || position === undefined) {
|
453
517
|
return true;
|
454
518
|
}
|
455
|
-
return ['absolute', 'relative', 'fixed', 'sticky'].includes(position
|
456
|
-
isFlexContainer(parentComputedStyles);
|
519
|
+
return ['absolute', 'relative', 'fixed', 'sticky'].includes(position) || isFlexContainer(parentComputedStyles);
|
457
520
|
}
|
458
521
|
|
459
|
-
|
460
|
-
|
461
|
-
|
522
|
+
getHint(propertyName: string, computedStyles?: Map<string, string>, parentComputedStyles?: Map<string, string>): Hint
|
523
|
+
|undefined {
|
524
|
+
if (this.#isRuleValid(computedStyles, parentComputedStyles)) {
|
525
|
+
return;
|
526
|
+
}
|
527
|
+
|
528
|
+
const reasonPropertyDeclaration = buildPropertyDefinitionText('position', computedStyles?.get('position'));
|
529
|
+
const affectedPropertyDeclarationCode = buildPropertyText(propertyName);
|
462
530
|
|
463
|
-
return new
|
464
|
-
|
531
|
+
return new Hint(
|
532
|
+
HintType.INACTIVE_PROPERTY,
|
465
533
|
i18nString(UIStrings.ruleViolatedBySameElementRuleReason, {
|
466
534
|
'REASON_PROPERTY_DECLARATION_CODE': reasonPropertyDeclaration,
|
467
535
|
'AFFECTED_PROPERTY_DECLARATION_CODE': affectedPropertyDeclarationCode,
|
@@ -486,11 +554,11 @@ const CSS_RULE_VALIDATORS = [
|
|
486
554
|
ZIndexValidator,
|
487
555
|
];
|
488
556
|
|
489
|
-
const setupCSSRulesValidators = (): Map<
|
490
|
-
const validatorsMap = new Map<
|
557
|
+
const setupCSSRulesValidators = (): Map<string, CSSRuleValidator[]> => {
|
558
|
+
const validatorsMap = new Map<string, CSSRuleValidator[]>();
|
491
559
|
for (const validatorClass of CSS_RULE_VALIDATORS) {
|
492
560
|
const validator = new validatorClass();
|
493
|
-
const affectedProperties = validator.
|
561
|
+
const affectedProperties = validator.getApplicableProperties();
|
494
562
|
|
495
563
|
for (const affectedProperty of affectedProperties) {
|
496
564
|
let propertyValidators = validatorsMap.get(affectedProperty);
|
@@ -505,4 +573,4 @@ const setupCSSRulesValidators = (): Map<String, CSSRuleValidator[]> => {
|
|
505
573
|
return validatorsMap;
|
506
574
|
};
|
507
575
|
|
508
|
-
export const cssRuleValidatorsMap: Map<
|
576
|
+
export const cssRuleValidatorsMap: Map<string, CSSRuleValidator[]> = setupCSSRulesValidators();
|
@@ -2,31 +2,37 @@
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
3
3
|
// found in the LICENSE file.
|
4
4
|
|
5
|
-
export const
|
5
|
+
export const buildPropertyDefinitionText = (property: string, value?: string): string => {
|
6
6
|
if (value === undefined) {
|
7
|
-
return
|
7
|
+
return buildPropertyText(property);
|
8
8
|
}
|
9
9
|
return '<code class="unbreakable-text"><span class="property">' + property + '</span>: ' + value + '</code>';
|
10
10
|
};
|
11
11
|
|
12
|
-
export const
|
12
|
+
export const buildPropertyText = (property: string): string => {
|
13
13
|
return '<code class="unbreakable-text"><span class="property">' + property + '</span></code>';
|
14
14
|
};
|
15
15
|
|
16
|
-
export const isFlexContainer = (computedStyles
|
17
|
-
if (computedStyles
|
16
|
+
export const isFlexContainer = (computedStyles?: Map<string, string>): boolean => {
|
17
|
+
if (!computedStyles) {
|
18
18
|
return false;
|
19
19
|
}
|
20
20
|
const display = computedStyles.get('display');
|
21
21
|
return display === 'flex' || display === 'inline-flex';
|
22
22
|
};
|
23
23
|
|
24
|
-
export const isGridContainer = (computedStyles
|
24
|
+
export const isGridContainer = (computedStyles?: Map<string, string>): boolean => {
|
25
|
+
if (!computedStyles) {
|
26
|
+
return false;
|
27
|
+
}
|
25
28
|
const display = computedStyles.get('display');
|
26
29
|
return display === 'grid' || display === 'inline-grid';
|
27
30
|
};
|
28
31
|
|
29
|
-
export const isMulticolContainer = (computedStyles
|
32
|
+
export const isMulticolContainer = (computedStyles?: Map<string, string>): boolean => {
|
33
|
+
if (!computedStyles) {
|
34
|
+
return false;
|
35
|
+
}
|
30
36
|
const columnWidth = computedStyles.get('column-width');
|
31
37
|
const columnCount = computedStyles.get('column-count');
|
32
38
|
|
@@ -290,6 +290,14 @@ export class StylePropertiesSection {
|
|
290
290
|
this.onpopulate();
|
291
291
|
}
|
292
292
|
|
293
|
+
setComputedStyles(computedStyles: Map<string, string>|null): void {
|
294
|
+
this.computedStyles = computedStyles;
|
295
|
+
}
|
296
|
+
|
297
|
+
setParentsComputedStyles(parentsComputedStyles: Map<string, string>|null): void {
|
298
|
+
this.parentsComputedStyles = parentsComputedStyles;
|
299
|
+
}
|
300
|
+
|
293
301
|
setSectionIdx(sectionIdx: number): void {
|
294
302
|
this.sectionIdx = sectionIdx;
|
295
303
|
this.onpopulate();
|
@@ -21,12 +21,12 @@ import {StyleEditorWidget} from './StyleEditorWidget.js';
|
|
21
21
|
import {type StylePropertiesSection} from './StylePropertiesSection.js';
|
22
22
|
import {CSSPropertyPrompt, StylesSidebarPane, StylesSidebarPropertyRenderer} from './StylesSidebarPane.js';
|
23
23
|
import {getCssDeclarationAsJavascriptProperty} from './StylePropertyUtils.js';
|
24
|
-
import {cssRuleValidatorsMap, type
|
24
|
+
import {cssRuleValidatorsMap, type Hint} from './CSSRuleValidator.js';
|
25
25
|
|
26
26
|
const FlexboxEditor = ElementsComponents.StylePropertyEditor.FlexboxEditor;
|
27
27
|
const GridEditor = ElementsComponents.StylePropertyEditor.GridEditor;
|
28
28
|
|
29
|
-
export const activeHints = new WeakMap<Element,
|
29
|
+
export const activeHints = new WeakMap<Element, Hint>();
|
30
30
|
|
31
31
|
const UIStrings = {
|
32
32
|
/**
|
@@ -717,23 +717,16 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
717
717
|
}
|
718
718
|
}
|
719
719
|
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
activeHints.set(hintIcon, authoringHint);
|
725
|
-
this.listItemElement.append(hintIcon);
|
726
|
-
}
|
727
|
-
|
728
|
-
if (!this.property.parsedOk) {
|
720
|
+
if (this.property.parsedOk) {
|
721
|
+
void this.updateFontVariationSettingsWarning();
|
722
|
+
this.updateAuthoringHint();
|
723
|
+
} else {
|
729
724
|
// Avoid having longhands under an invalid shorthand.
|
730
725
|
this.listItemElement.classList.add('not-parsed-ok');
|
731
726
|
|
732
727
|
// Add a separate exclamation mark IMG element with a tooltip.
|
733
728
|
this.listItemElement.insertBefore(
|
734
729
|
StylesSidebarPane.createExclamationMark(this.property, null), this.listItemElement.firstChild);
|
735
|
-
} else {
|
736
|
-
void this.updateFontVariationSettingsWarning();
|
737
730
|
}
|
738
731
|
|
739
732
|
if (!this.property.activeInStyle()) {
|
@@ -768,6 +761,35 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
768
761
|
}
|
769
762
|
}
|
770
763
|
|
764
|
+
private updateAuthoringHint(): void {
|
765
|
+
if (!Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.CSS_AUTHORING_HINTS)) {
|
766
|
+
return;
|
767
|
+
}
|
768
|
+
|
769
|
+
const existingElement = this.listItemElement.querySelector('.hint');
|
770
|
+
if (existingElement) {
|
771
|
+
activeHints.delete(existingElement);
|
772
|
+
existingElement.parentElement?.removeChild(existingElement);
|
773
|
+
}
|
774
|
+
const propertyName = this.property.name;
|
775
|
+
|
776
|
+
if (!cssRuleValidatorsMap.has(propertyName)) {
|
777
|
+
return;
|
778
|
+
}
|
779
|
+
|
780
|
+
for (const validator of cssRuleValidatorsMap.get(propertyName) || []) {
|
781
|
+
const hint =
|
782
|
+
validator.getHint(propertyName, this.computedStyles || undefined, this.parentsComputedStyles || undefined);
|
783
|
+
if (hint) {
|
784
|
+
Host.userMetrics.cssHintShown(validator.getMetricType());
|
785
|
+
const hintIcon = UI.Icon.Icon.create('mediumicon-info', 'hint');
|
786
|
+
activeHints.set(hintIcon, hint);
|
787
|
+
this.listItemElement.append(hintIcon);
|
788
|
+
break;
|
789
|
+
}
|
790
|
+
}
|
791
|
+
}
|
792
|
+
|
771
793
|
private async updateFontVariationSettingsWarning(): Promise<void> {
|
772
794
|
if (this.property.name !== 'font-variation-settings') {
|
773
795
|
return;
|
@@ -816,24 +838,6 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
816
838
|
StylesSidebarPane.createExclamationMark(this.property, warnings.join(' ')), this.listItemElement.firstChild);
|
817
839
|
}
|
818
840
|
|
819
|
-
private getAuthoringHint(computedStyles: Map<string, string>|null, parentComputedStyles: Map<string, string>|null):
|
820
|
-
AuthoringHint|null {
|
821
|
-
const propertyName = this.property.name;
|
822
|
-
|
823
|
-
if (!Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.CSS_AUTHORING_HINTS) ||
|
824
|
-
!cssRuleValidatorsMap.has(propertyName)) {
|
825
|
-
return null;
|
826
|
-
}
|
827
|
-
|
828
|
-
for (const validator of cssRuleValidatorsMap.get(propertyName) || []) {
|
829
|
-
if (!validator.isRuleValid(computedStyles, parentComputedStyles)) {
|
830
|
-
return validator.getAuthoringHint(propertyName, this.computedStyles, this.parentsComputedStyles);
|
831
|
-
}
|
832
|
-
}
|
833
|
-
|
834
|
-
return null;
|
835
|
-
}
|
836
|
-
|
837
841
|
private mouseUp(event: MouseEvent): void {
|
838
842
|
const activeTreeElement = parentMap.get(this.parentPaneInternal);
|
839
843
|
parentMap.delete(this.parentPaneInternal);
|
@@ -218,6 +218,7 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
218
218
|
#urlToChangeTracker: Map<Platform.DevToolsPath.UrlString, ChangeTracker> = new Map();
|
219
219
|
#copyChangesButton?: UI.Toolbar.ToolbarButton;
|
220
220
|
#updateAbortController?: AbortController;
|
221
|
+
#updateComputedStylesAbortController?: AbortController;
|
221
222
|
|
222
223
|
static instance(): StylesSidebarPane {
|
223
224
|
if (!stylesSidebarPaneInstance) {
|
@@ -753,10 +754,12 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
753
754
|
for (const section of this.allSections()) {
|
754
755
|
section.styleSheetEdited(edit);
|
755
756
|
}
|
757
|
+
void this.refreshComputedStyles();
|
756
758
|
return;
|
757
759
|
}
|
758
760
|
|
759
761
|
if (this.userOperation || this.isEditingStyle) {
|
762
|
+
void this.refreshComputedStyles();
|
760
763
|
return;
|
761
764
|
}
|
762
765
|
|
@@ -764,6 +767,27 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
764
767
|
this.update();
|
765
768
|
}
|
766
769
|
|
770
|
+
async refreshComputedStyles(): Promise<void> {
|
771
|
+
this.#updateComputedStylesAbortController?.abort();
|
772
|
+
this.#updateAbortController = new AbortController();
|
773
|
+
const signal = this.#updateAbortController.signal;
|
774
|
+
const matchedStyles = await this.fetchMatchedCascade();
|
775
|
+
const nodeId = this.node()?.id;
|
776
|
+
const parentNodeId = matchedStyles?.getParentLayoutNodeId();
|
777
|
+
|
778
|
+
const [computedStyles, parentsComputedStyles] =
|
779
|
+
await Promise.all([this.fetchComputedStylesFor(nodeId), this.fetchComputedStylesFor(parentNodeId)]);
|
780
|
+
|
781
|
+
if (signal.aborted) {
|
782
|
+
return;
|
783
|
+
}
|
784
|
+
|
785
|
+
for (const section of this.allSections()) {
|
786
|
+
section.setComputedStyles(computedStyles);
|
787
|
+
section.setParentsComputedStyles(parentsComputedStyles);
|
788
|
+
}
|
789
|
+
}
|
790
|
+
|
767
791
|
focusedSectionIndex(): number {
|
768
792
|
let index = 0;
|
769
793
|
for (const block of this.sectionBlocks) {
|
@@ -20,10 +20,10 @@ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
20
20
|
const {render, html, Directives} = LitHtml;
|
21
21
|
|
22
22
|
interface Hint {
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
getPrefix(): string;
|
24
|
+
getMessage(): string;
|
25
|
+
getPossibleFixMessage(): string|null;
|
26
|
+
getLearnMoreLink(): string|undefined;
|
27
27
|
}
|
28
28
|
|
29
29
|
export class CSSHintDetailsView extends HTMLElement {
|
@@ -44,7 +44,7 @@ export class CSSHintDetailsView extends HTMLElement {
|
|
44
44
|
render(html`
|
45
45
|
<div class="hint-popup-wrapper">
|
46
46
|
<div class="hint-popup-reason">
|
47
|
-
<strong>${this.#authoringHint.
|
47
|
+
<strong>${this.#authoringHint.getPrefix()}:</strong> ${Directives.unsafeHTML(this.#authoringHint.getMessage())}
|
48
48
|
</div>
|
49
49
|
${this.#authoringHint.getPossibleFixMessage() ? html`
|
50
50
|
<div class="hint-popup-possible-fix">
|
@@ -267,7 +267,7 @@ export class LayoutPane extends HTMLElement {
|
|
267
267
|
} as NodeText.NodeText.NodeTextData}></${NodeText.NodeText.NodeText.litTagName}>
|
268
268
|
</span>
|
269
269
|
</label>
|
270
|
-
<label @keyup=${onColorLabelKeyUp} @keydown=${onColorLabelKeyDown} tabindex="0" title=${i18nString(UIStrings.chooseElementOverlayColor)} class="color-picker-label" style="background: ${element.color};">
|
270
|
+
<label @keyup=${onColorLabelKeyUp} @keydown=${onColorLabelKeyDown} tabindex="0" title=${i18nString(UIStrings.chooseElementOverlayColor)} aria-label=${i18nString(UIStrings.chooseElementOverlayColor)} class="color-picker-label" style="background: ${element.color};">
|
271
271
|
<input @change=${onColorChange} @input=${onColorChange} tabindex="-1" class="color-picker" type="color" value=${element.color} />
|
272
272
|
</label>
|
273
273
|
<button tabindex="0" @click=${onElementClick} title=${i18nString(UIStrings.showElementInTheElementsPanel)} class="show-element"></button>
|
@@ -181,7 +181,7 @@ export class ProtocolService {
|
|
181
181
|
method?: string,
|
182
182
|
};
|
183
183
|
if (protocolMessage.sessionId || (protocolMessage.method && protocolMessage.method.startsWith('Target'))) {
|
184
|
-
void this.send('dispatchProtocolMessage', {message
|
184
|
+
void this.send('dispatchProtocolMessage', {message});
|
185
185
|
}
|
186
186
|
}
|
187
187
|
|
@@ -218,7 +218,7 @@ export class ProtocolService {
|
|
218
218
|
}
|
219
219
|
|
220
220
|
private onWorkerMessage(event: MessageEvent): void {
|
221
|
-
const lighthouseMessage =
|
221
|
+
const lighthouseMessage = event.data;
|
222
222
|
|
223
223
|
if (lighthouseMessage.action === 'statusUpdate') {
|
224
224
|
if (this.lighthouseMessageUpdateCallback && lighthouseMessage.args && 'message' in lighthouseMessage.args) {
|
@@ -240,7 +240,7 @@ export class ProtocolService {
|
|
240
240
|
private async send(action: string, args: {[x: string]: string|string[]|Object} = {}): Promise<void> {
|
241
241
|
const worker = await this.ensureWorkerExists();
|
242
242
|
const messageId = lastId++;
|
243
|
-
worker.postMessage(
|
243
|
+
worker.postMessage({id: messageId, action, args: {...args, id: messageId}});
|
244
244
|
}
|
245
245
|
|
246
246
|
/** sendWithResponse currently only handles the original startLighthouse request and LHR-filled response. */
|
@@ -250,7 +250,7 @@ export class ProtocolService {
|
|
250
250
|
const messageId = lastId++;
|
251
251
|
const messageResult = new Promise<ReportRenderer.RunnerResult>(resolve => {
|
252
252
|
const workerListener = (event: MessageEvent): void => {
|
253
|
-
const lighthouseMessage =
|
253
|
+
const lighthouseMessage = event.data;
|
254
254
|
|
255
255
|
if (lighthouseMessage.id === messageId) {
|
256
256
|
worker.removeEventListener('message', workerListener);
|
@@ -259,7 +259,7 @@ export class ProtocolService {
|
|
259
259
|
};
|
260
260
|
worker.addEventListener('message', workerListener);
|
261
261
|
});
|
262
|
-
worker.postMessage(
|
262
|
+
worker.postMessage({id: messageId, action, args: {...args, id: messageId}});
|
263
263
|
|
264
264
|
return messageResult;
|
265
265
|
}
|
package/package.json
CHANGED