chrome-devtools-frontend 1.0.1029692 → 1.0.1030457

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.
@@ -4,6 +4,14 @@
4
4
 
5
5
  import * as i18n from '../../core/i18n/i18n.js';
6
6
 
7
+ import {
8
+ buildStyledPropertyText,
9
+ buildStyledRuleText,
10
+ isFlexContainer,
11
+ isGridContainer,
12
+ isMulticolContainer,
13
+ } from './CSSRuleValidatorHelper.js';
14
+
7
15
  const UIStrings = {
8
16
  /**
9
17
  *@description Hint prefix for deprecated properties.
@@ -18,25 +26,36 @@ const UIStrings = {
18
26
  *@example {flex-wrap: nowrap} REASON_RULE_CODE
19
27
  *@example {align-content} AFFECTED_RULE_CODE
20
28
  */
21
- ruleViolatedBySameElementRuleReason: 'This element has {REASON_RULE_CODE} rule, therefore {AFFECTED_RULE_CODE} has no effect.',
29
+ ruleViolatedBySameElementRuleReason:
30
+ 'This element has {REASON_RULE_CODE} rule, therefore {AFFECTED_RULE_CODE} has no effect.',
22
31
  /**
23
32
  *@description Possible fix for rules that was violated because of same elements rule.
24
33
  *@example {flex-wrap: nowrap} REASON_RULE_CODE
25
34
  */
26
- ruleViolatedBySameElementRuleFix: 'For this property to work, please remove or change the value of {REASON_RULE_CODE}',
35
+ ruleViolatedBySameElementRuleFix:
36
+ 'For this property to work, please remove or change the value of {REASON_RULE_CODE}',
37
+ /**
38
+ *@description Possible fix for rules that was violated because of same elements rule.
39
+ *@example {display: block} EXISTING_RULE
40
+ *@example {display: flex} TARGET_RULE
41
+ */
42
+ ruleViolatedBySameElementRuleChangeSuggestion:
43
+ 'For this property to work, please change the {EXISTING_RULE} rule to {TARGET_RULE}',
27
44
  /**
28
45
  *@description Hint for rules that was violated because of parent element rule.
29
46
  *@example {display: block} REASON_RULE_CODE
30
47
  *@example {flex} AFFECTED_RULE_CODE
31
48
  */
32
- ruleViolatedByParentElementRuleReason: 'Parent element has {REASON_RULE_CODE} rule, therefore this elements {AFFECTED_RULE_CODE} has no effect',
49
+ ruleViolatedByParentElementRuleReason:
50
+ 'Parent element has {REASON_RULE_CODE} rule, therefore this elements {AFFECTED_RULE_CODE} has no effect',
33
51
  /**
34
52
  *@description Posible fix for rules that was violated because of parent element rule.
35
53
  *@example {display: block} EXISTING_PARENT_ELEMENT_RULE
36
54
  *@example {display: flex} TARGET_PARENT_ELEMENT_RULE
37
55
  */
38
- ruleViolatedByParentElementRuleFix: 'Please change parent elements {EXISTING_PARENT_ELEMENT_RULE} to {TARGET_PARENT_ELEMENT_RULE} to fix this issue.',
39
- };
56
+ ruleViolatedByParentElementRuleFix:
57
+ 'Please change parent elements {EXISTING_PARENT_ELEMENT_RULE} to {TARGET_PARENT_ELEMENT_RULE} to fix this issue.',
58
+ };
40
59
  const str_ = i18n.i18n.registerUIStrings('panels/elements/CSSRuleValidator.ts', UIStrings);
41
60
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
42
61
 
@@ -94,7 +113,9 @@ export abstract class CSSRuleValidator {
94
113
  return this.#affectedProperties;
95
114
  }
96
115
 
97
- abstract getAuthoringHint(propertyName: string, parentComputedStyles: Map<String, String>|null): AuthoringHint;
116
+ abstract getAuthoringHint(
117
+ propertyName: string, computedStyles: Map<String, String>|null,
118
+ parentComputedStyles: Map<String, String>|null): AuthoringHint;
98
119
  }
99
120
 
100
121
  export class AlignContentValidator extends CSSRuleValidator {
@@ -106,16 +127,15 @@ export class AlignContentValidator extends CSSRuleValidator {
106
127
  if (computedStyles === null || computedStyles === undefined) {
107
128
  return true;
108
129
  }
109
- const display = computedStyles.get('display');
110
- if (display !== 'flex' && display !== 'inline-flex') {
130
+ if (!isFlexContainer(computedStyles)) {
111
131
  return true;
112
132
  }
113
133
  return computedStyles.get('flex-wrap') !== 'nowrap';
114
134
  }
115
135
 
116
136
  getAuthoringHint(): AuthoringHint {
117
- const reasonRuleCode = '<code class="unbreakable-text"><span class="property">flex-wrap</span>: nowrap</code>';
118
- const affectedRuleCode = '<code class="unbreakable-text"><span class="property">align-content</span></code>';
137
+ const reasonRuleCode = buildStyledPropertyText('flex-wrap');
138
+ const affectedRuleCode = buildStyledPropertyText('align-content');
119
139
 
120
140
  return new AuthoringHint(
121
141
  'align-content',
@@ -138,18 +158,18 @@ export class FlexItemValidator extends CSSRuleValidator {
138
158
  }
139
159
 
140
160
  isRuleValid(computedStyles: Map<String, String>|null, parentsComputedStyles: Map<String, String>|null): boolean {
141
- if (computedStyles === null || computedStyles === undefined || parentsComputedStyles === null ||
142
- parentsComputedStyles === undefined) {
161
+ if (parentsComputedStyles === null) {
143
162
  return true;
144
163
  }
145
- const parentDisplay = parentsComputedStyles.get('display');
146
- return parentDisplay === 'flex' || parentDisplay === 'inline-flex';
164
+ return isFlexContainer(parentsComputedStyles);
147
165
  }
148
166
 
149
- getAuthoringHint(property: string, parentsComputedStyles: Map<String, String>|null): AuthoringHint {
150
- const reasonRuleCode = '<code class="unbreakable-text"><span class="property">display</span>:' + parentsComputedStyles?.get('display') + '</code>';
151
- const affectedRuleCode = '<code class="unbreakable-text"><span class="property">' + property + '</span></code>';
152
- const targetParentRuleCode = '<code class="unbreakable-text"><span class="property">display</span>: flex</code>';
167
+ getAuthoringHint(
168
+ property: string, computedStyles: Map<String, String>|null,
169
+ parentsComputedStyles: Map<String, String>|null): AuthoringHint {
170
+ const reasonRuleCode = buildStyledRuleText('display', parentsComputedStyles?.get('display'));
171
+ const affectedRuleCode = buildStyledPropertyText(property);
172
+ const targetParentRuleCode = buildStyledRuleText('display', 'flex');
153
173
 
154
174
  return new AuthoringHint(
155
175
  property,
@@ -167,11 +187,326 @@ export class FlexItemValidator extends CSSRuleValidator {
167
187
  }
168
188
  }
169
189
 
170
- const setupCSSRulesValidators = (): Map<String, CSSRuleValidator[]> => {
171
- const validators = [new AlignContentValidator(), new FlexItemValidator()];
190
+ export class FlexContainerValidator extends CSSRuleValidator {
191
+ constructor() {
192
+ super(['flex-direction', 'flex-flow', 'flex-wrap', 'justify-content']);
193
+ }
194
+
195
+ isRuleValid(computedStyles: Map<String, String>|null): boolean {
196
+ if (computedStyles === null) {
197
+ return true;
198
+ }
199
+ return isFlexContainer(computedStyles);
200
+ }
201
+
202
+ getAuthoringHint(property: string, computedStyles: Map<String, String>|null): AuthoringHint {
203
+ const reasonRuleCode = buildStyledRuleText('display', computedStyles?.get('display'));
204
+ const targetRuleCode = buildStyledRuleText('display', 'flex');
205
+ const affectedRuleCode = buildStyledPropertyText(property);
206
+
207
+ return new AuthoringHint(
208
+ property,
209
+ AuthoringHintType.RULE_VALIDATION,
210
+ i18nString(UIStrings.ruleViolatedBySameElementRuleReason, {
211
+ 'REASON_RULE_CODE': reasonRuleCode,
212
+ 'AFFECTED_RULE_CODE': affectedRuleCode,
213
+ }),
214
+ i18nString(UIStrings.ruleViolatedBySameElementRuleChangeSuggestion, {
215
+ 'EXISTING_RULE': reasonRuleCode,
216
+ 'TARGET_RULE': targetRuleCode,
217
+ }),
218
+ true,
219
+ );
220
+ }
221
+ }
222
+
223
+ export class GridContainerValidator extends CSSRuleValidator {
224
+ constructor() {
225
+ super([
226
+ 'grid',
227
+ 'grid-auto-columns',
228
+ 'grid-auto-flow',
229
+ 'grid-auto-rows',
230
+ 'grid-template',
231
+ 'grid-template-areas',
232
+ 'grid-template-columns',
233
+ 'grid-template-rows',
234
+ ]);
235
+ }
236
+
237
+ isRuleValid(computedStyles: Map<String, String>|null): boolean {
238
+ if (computedStyles === null) {
239
+ return true;
240
+ }
241
+ return isGridContainer(computedStyles);
242
+ }
243
+
244
+ getAuthoringHint(property: string, computedStyles: Map<String, String>|null): AuthoringHint {
245
+ const reasonRuleCode = buildStyledRuleText('display', computedStyles?.get('display'));
246
+ const targetRuleCode = buildStyledRuleText('display', 'grid');
247
+ const affectedRuleCode = buildStyledPropertyText(property);
248
+
249
+ return new AuthoringHint(
250
+ property,
251
+ AuthoringHintType.RULE_VALIDATION,
252
+ i18nString(UIStrings.ruleViolatedBySameElementRuleReason, {
253
+ 'REASON_RULE_CODE': reasonRuleCode,
254
+ 'AFFECTED_RULE_CODE': affectedRuleCode,
255
+ }),
256
+ i18nString(UIStrings.ruleViolatedBySameElementRuleChangeSuggestion, {
257
+ 'EXISTING_RULE': reasonRuleCode,
258
+ 'TARGET_RULE': targetRuleCode,
259
+ }),
260
+ true,
261
+ );
262
+ }
263
+ }
264
+
265
+ export class GridItemValidator extends CSSRuleValidator {
266
+ constructor() {
267
+ super([
268
+ 'grid-area',
269
+ 'grid-column',
270
+ 'grid-row',
271
+ 'grid-row-end',
272
+ 'grid-row-start',
273
+ ]);
274
+ }
275
+
276
+ isRuleValid(computedStyles: Map<String, String>|null, parentComputedStyles: Map<String, String>|null): boolean {
277
+ if (parentComputedStyles === null) {
278
+ return true;
279
+ }
280
+ return isGridContainer(parentComputedStyles);
281
+ }
282
+
283
+ getAuthoringHint(
284
+ property: string, computedStyles: Map<String, String>|null,
285
+ parentComputedStyles: Map<String, String>|null): AuthoringHint {
286
+ const reasonRuleCode = buildStyledRuleText('display', parentComputedStyles?.get('display'));
287
+ const targetParentRuleCode = buildStyledRuleText('display', 'grid');
288
+ const affectedRuleCode = buildStyledPropertyText(property);
289
+
290
+ return new AuthoringHint(
291
+ property,
292
+ AuthoringHintType.RULE_VALIDATION,
293
+ i18nString(UIStrings.ruleViolatedByParentElementRuleReason, {
294
+ 'REASON_RULE_CODE': reasonRuleCode,
295
+ 'AFFECTED_RULE_CODE': affectedRuleCode,
296
+ }),
297
+ i18nString(UIStrings.ruleViolatedByParentElementRuleFix, {
298
+ 'EXISTING_PARENT_ELEMENT_RULE': reasonRuleCode,
299
+ 'TARGET_PARENT_ELEMENT_RULE': targetParentRuleCode,
300
+ }),
301
+ true,
302
+ );
303
+ }
304
+ }
305
+
306
+ export class FlexGridValidator extends CSSRuleValidator {
307
+ constructor() {
308
+ super([
309
+ 'order',
310
+ 'align-content',
311
+ 'align-items',
312
+ 'align-self',
313
+ ]);
314
+ }
315
+
316
+ isRuleValid(computedStyles: Map<String, String>|null): boolean {
317
+ if (computedStyles === null) {
318
+ return true;
319
+ }
320
+ return isFlexContainer(computedStyles) || isGridContainer(computedStyles);
321
+ }
322
+
323
+ getAuthoringHint(property: string, computedStyles: Map<String, String>|null): AuthoringHint {
324
+ const reasonRuleCode = buildStyledRuleText('display', computedStyles?.get('display'));
325
+ const affectedRuleCode = buildStyledPropertyText(property);
326
+
327
+ return new AuthoringHint(
328
+ property,
329
+ AuthoringHintType.RULE_VALIDATION,
330
+ i18nString(UIStrings.ruleViolatedBySameElementRuleReason, {
331
+ 'REASON_RULE_CODE': reasonRuleCode,
332
+ 'AFFECTED_RULE_CODE': affectedRuleCode,
333
+ }),
334
+ i18nString(UIStrings.ruleViolatedBySameElementRuleFix, {
335
+ 'REASON_RULE_CODE': reasonRuleCode,
336
+ }),
337
+ true,
338
+ );
339
+ }
340
+ }
341
+
342
+ export class MulticolFlexGridValidator extends CSSRuleValidator {
343
+ constructor() {
344
+ super([
345
+ 'gap',
346
+ 'column-gap',
347
+ 'row-gap',
348
+ 'grid-gap',
349
+ 'grid-column-gap',
350
+ 'grid-column-end',
351
+ 'grid-row-gap',
352
+ ]);
353
+ }
354
+
355
+ isRuleValid(computedStyles: Map<String, String>|null): boolean {
356
+ if (computedStyles === null) {
357
+ return true;
358
+ }
359
+ return isMulticolContainer(computedStyles) || isFlexContainer(computedStyles) || isGridContainer(computedStyles);
360
+ }
361
+
362
+ getAuthoringHint(property: string, computedStyles: Map<String, String>|null): AuthoringHint {
363
+ const reasonRuleCode = buildStyledRuleText('display', computedStyles?.get('display'));
364
+ const affectedRuleCode = buildStyledPropertyText(property);
365
+
366
+ return new AuthoringHint(
367
+ property,
368
+ AuthoringHintType.RULE_VALIDATION,
369
+ i18nString(UIStrings.ruleViolatedBySameElementRuleReason, {
370
+ 'REASON_RULE_CODE': reasonRuleCode,
371
+ 'AFFECTED_RULE_CODE': affectedRuleCode,
372
+ }),
373
+ i18nString(UIStrings.ruleViolatedBySameElementRuleFix, {
374
+ 'REASON_RULE_CODE': reasonRuleCode,
375
+ }),
376
+ true,
377
+ );
378
+ }
379
+ }
380
+
381
+ export class PaddingValidator extends CSSRuleValidator {
382
+ constructor() {
383
+ super([
384
+ 'padding',
385
+ 'padding-top',
386
+ 'padding-right',
387
+ 'padding-bottom',
388
+ 'padding-left',
389
+ ]);
390
+ }
391
+
392
+ isRuleValid(computedStyles: Map<String, String>|null): boolean {
393
+ const display = computedStyles?.get('display');
394
+ if (display === null || display === undefined) {
395
+ return true;
396
+ }
397
+ return !['table-row-group', 'table-header-group', 'table-footer-group', 'table-row', 'table-column-group',
398
+ 'table-column']
399
+ .includes(display as string);
400
+ }
401
+
402
+ getAuthoringHint(property: string, computedStyles: Map<String, String>|null): AuthoringHint {
403
+ const reasonRuleCode = buildStyledRuleText('display', computedStyles?.get('display'));
404
+ const affectedRuleCode = buildStyledPropertyText(property);
405
+
406
+ return new AuthoringHint(
407
+ property,
408
+ AuthoringHintType.RULE_VALIDATION,
409
+ i18nString(UIStrings.ruleViolatedBySameElementRuleReason, {
410
+ 'REASON_RULE_CODE': reasonRuleCode,
411
+ 'AFFECTED_RULE_CODE': affectedRuleCode,
412
+ }),
413
+ i18nString(UIStrings.ruleViolatedBySameElementRuleFix, {
414
+ 'REASON_RULE_CODE': reasonRuleCode,
415
+ }),
416
+ true,
417
+ );
418
+ }
419
+ }
172
420
 
421
+ export class PositionValidator extends CSSRuleValidator {
422
+ constructor() {
423
+ super([
424
+ 'top',
425
+ 'right',
426
+ 'bottom',
427
+ 'left',
428
+ ]);
429
+ }
430
+
431
+ isRuleValid(computedStyles: Map<String, String>|null): boolean {
432
+ const position = computedStyles?.get('position');
433
+ if (position === null || position === undefined) {
434
+ return true;
435
+ }
436
+ return position !== 'static';
437
+ }
438
+
439
+ getAuthoringHint(property: string, computedStyles: Map<String, String>|null): AuthoringHint {
440
+ const reasonRuleCode = buildStyledRuleText('position', computedStyles?.get('position'));
441
+ const affectedRuleCode = buildStyledPropertyText(property);
442
+
443
+ return new AuthoringHint(
444
+ property,
445
+ AuthoringHintType.RULE_VALIDATION,
446
+ i18nString(UIStrings.ruleViolatedBySameElementRuleReason, {
447
+ 'REASON_RULE_CODE': reasonRuleCode,
448
+ 'AFFECTED_RULE_CODE': affectedRuleCode,
449
+ }),
450
+ i18nString(UIStrings.ruleViolatedBySameElementRuleFix, {
451
+ 'REASON_RULE_CODE': reasonRuleCode,
452
+ }),
453
+ true,
454
+ );
455
+ }
456
+ }
457
+
458
+ export class ZIndexValidator extends CSSRuleValidator {
459
+ constructor() {
460
+ super([
461
+ 'z-index',
462
+ ]);
463
+ }
464
+
465
+ isRuleValid(computedStyles: Map<String, String>|null, parentComputedStyles: Map<String, String>|null): boolean {
466
+ const position = computedStyles?.get('position');
467
+ if (position === null || position === undefined) {
468
+ return true;
469
+ }
470
+ return ['absolute', 'relative', 'fixed', 'sticky'].includes(position as string) ||
471
+ isFlexContainer(parentComputedStyles);
472
+ }
473
+
474
+ getAuthoringHint(property: string, computedStyles: Map<String, String>|null): AuthoringHint {
475
+ const reasonRuleCode = buildStyledRuleText('position', computedStyles?.get('position'));
476
+ const affectedRuleCode = buildStyledPropertyText(property);
477
+
478
+ return new AuthoringHint(
479
+ property,
480
+ AuthoringHintType.RULE_VALIDATION,
481
+ i18nString(UIStrings.ruleViolatedBySameElementRuleReason, {
482
+ 'REASON_RULE_CODE': reasonRuleCode,
483
+ 'AFFECTED_RULE_CODE': affectedRuleCode,
484
+ }),
485
+ i18nString(UIStrings.ruleViolatedBySameElementRuleFix, {
486
+ 'REASON_RULE_CODE': reasonRuleCode,
487
+ }),
488
+ true,
489
+ );
490
+ }
491
+ }
492
+
493
+ const CSS_RULE_VALIDATORS = [
494
+ AlignContentValidator,
495
+ FlexItemValidator,
496
+ FlexContainerValidator,
497
+ GridContainerValidator,
498
+ GridItemValidator,
499
+ FlexGridValidator,
500
+ MulticolFlexGridValidator,
501
+ PaddingValidator,
502
+ PositionValidator,
503
+ ZIndexValidator,
504
+ ];
505
+
506
+ const setupCSSRulesValidators = (): Map<String, CSSRuleValidator[]> => {
173
507
  const validatorsMap = new Map<String, CSSRuleValidator[]>();
174
- for (const validator of validators) {
508
+ for (const validatorClass of CSS_RULE_VALIDATORS) {
509
+ const validator = new validatorClass();
175
510
  const affectedProperties = validator.getAffectedProperties();
176
511
 
177
512
  for (const affectedProperty of affectedProperties) {
@@ -0,0 +1,34 @@
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
+ export const buildStyledRuleText = (property: String, value: String|undefined): string => {
6
+ if (value === undefined) {
7
+ return buildStyledPropertyText(property);
8
+ }
9
+ return '<code class="unbreakable-text"><span class="property">' + property + '</span>: ' + value + '</code>';
10
+ };
11
+
12
+ export const buildStyledPropertyText = (property: String): string => {
13
+ return '<code class="unbreakable-text"><span class="property">' + property + '</span></code>';
14
+ };
15
+
16
+ export const isFlexContainer = (computedStyles: Map<String, String>|null): boolean => {
17
+ if (computedStyles === null) {
18
+ return false;
19
+ }
20
+ const display = computedStyles.get('display');
21
+ return display === 'flex' || display === 'inline-flex';
22
+ };
23
+
24
+ export const isGridContainer = (computedStyles: Map<String, String>): boolean => {
25
+ const display = computedStyles.get('display');
26
+ return display === 'grid' || display === 'inline-grid';
27
+ };
28
+
29
+ export const isMulticolContainer = (computedStyles: Map<String, String>): boolean => {
30
+ const columnWidth = computedStyles.get('column-width');
31
+ const columnCount = computedStyles.get('column-count');
32
+
33
+ return columnWidth !== 'auto' || columnCount !== 'auto';
34
+ };
@@ -26,6 +26,8 @@ import {cssRuleValidatorsMap, type AuthoringHint} from './CSSRuleValidator.js';
26
26
  const FlexboxEditor = ElementsComponents.StylePropertyEditor.FlexboxEditor;
27
27
  const GridEditor = ElementsComponents.StylePropertyEditor.GridEditor;
28
28
 
29
+ export const activeHints = new WeakMap<Element, AuthoringHint>();
30
+
29
31
  const UIStrings = {
30
32
  /**
31
33
  *@description Text in Color Swatch Popover Icon of the Elements panel
@@ -719,11 +721,7 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
719
721
  const showAuthoringHint = authoringHint !== null && this.property.parsedOk;
720
722
  if (showAuthoringHint) {
721
723
  const hintIcon = UI.Icon.Icon.create('mediumicon-info', 'hint');
722
- const hintPopover =
723
- new UI.PopoverHelper.PopoverHelper(hintIcon, event => this.handleHintPopoverRequest(authoringHint, event));
724
- hintPopover.setHasPadding(true);
725
- hintPopover.setTimeout(0, 100);
726
-
724
+ activeHints.set(hintIcon, authoringHint);
727
725
  this.listItemElement.append(hintIcon);
728
726
  }
729
727
 
@@ -829,31 +827,13 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
829
827
 
830
828
  for (const validator of cssRuleValidatorsMap.get(propertyName) || []) {
831
829
  if (!validator.isRuleValid(computedStyles, parentComputedStyles)) {
832
- return validator.getAuthoringHint(propertyName, this.parentsComputedStyles);
830
+ return validator.getAuthoringHint(propertyName, this.computedStyles, this.parentsComputedStyles);
833
831
  }
834
832
  }
835
833
 
836
834
  return null;
837
835
  }
838
836
 
839
- private handleHintPopoverRequest(authoringHint: AuthoringHint, event: Event): UI.PopoverHelper.PopoverRequest|null {
840
- const link = event.composedPath()[0];
841
- Platform.DCHECK(() => link instanceof Element, 'Link is not an instance of Element');
842
-
843
- return {
844
- box: (link as Element).boxInWindow(),
845
- show: async(popover: UI.GlassPane.GlassPane): Promise<boolean> => {
846
- const node = this.node();
847
- if (!node) {
848
- return false;
849
- }
850
- const popupElement = new ElementsComponents.CSSHintDetailsView.CSSHintDetailsView(authoringHint);
851
- popover.contentElement.insertAdjacentElement('beforeend', popupElement);
852
- return true;
853
- },
854
- };
855
- }
856
-
857
837
  private mouseUp(event: MouseEvent): void {
858
838
  const activeTreeElement = parentMap.get(this.parentPaneInternal);
859
839
  parentMap.delete(this.parentPaneInternal);
@@ -60,7 +60,7 @@ import {StyleEditorWidget} from './StyleEditorWidget.js';
60
60
  import {StylePropertyHighlighter} from './StylePropertyHighlighter.js';
61
61
  import stylesSidebarPaneStyles from './stylesSidebarPane.css.js';
62
62
 
63
- import {type StylePropertyTreeElement} from './StylePropertyTreeElement.js';
63
+ import {activeHints, type StylePropertyTreeElement} from './StylePropertyTreeElement.js';
64
64
  import {
65
65
  StylePropertiesSection,
66
66
  BlankStylePropertiesSection,
@@ -213,6 +213,7 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
213
213
  private needsForceUpdate: boolean;
214
214
  private readonly resizeThrottler: Common.Throttler.Throttler;
215
215
  private readonly imagePreviewPopover: ImagePreviewPopover;
216
+ #hintPopoverHelper: UI.PopoverHelper.PopoverHelper;
216
217
  activeCSSAngle: InlineEditor.CSSAngle.CSSAngle|null;
217
218
  #urlToChangeTracker: Map<Platform.DevToolsPath.UrlString, ChangeTracker> = new Map();
218
219
  #copyChangesButton?: UI.Toolbar.ToolbarButton;
@@ -282,6 +283,35 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
282
283
  }, () => this.node());
283
284
 
284
285
  this.activeCSSAngle = null;
286
+
287
+ this.#hintPopoverHelper = new UI.PopoverHelper.PopoverHelper(this.contentElement, event => {
288
+ const icon = event.composedPath()[0] as Element;
289
+
290
+ if (!icon) {
291
+ return null;
292
+ }
293
+
294
+ if (!icon.matches('.hint')) {
295
+ return null;
296
+ }
297
+
298
+ const hint = activeHints.get(icon);
299
+
300
+ if (!hint) {
301
+ return null;
302
+ }
303
+
304
+ return {
305
+ box: icon.boxInWindow(),
306
+ show: async(popover: UI.GlassPane.GlassPane): Promise<boolean> => {
307
+ const popupElement = new ElementsComponents.CSSHintDetailsView.CSSHintDetailsView(hint);
308
+ popover.contentElement.appendChild(popupElement);
309
+ return true;
310
+ },
311
+ };
312
+ });
313
+ this.#hintPopoverHelper.setTimeout(200);
314
+ this.#hintPopoverHelper.setHasPadding(true);
285
315
  }
286
316
 
287
317
  swatchPopoverHelper(): InlineEditor.SwatchPopoverHelper.SwatchPopoverHelper {
@@ -57,6 +57,7 @@ export class AttributionReportingIssueDetailsView extends AffectedResourcesView
57
57
  switch (issueCode) {
58
58
  case IssuesManager.AttributionReportingIssue.IssueCode.InvalidRegisterSourceHeader:
59
59
  case IssuesManager.AttributionReportingIssue.IssueCode.InvalidRegisterTriggerHeader:
60
+ case IssuesManager.AttributionReportingIssue.IssueCode.InvalidEligibleHeader:
60
61
  this.appendColumnTitle(header, i18nString(UIStrings.request));
61
62
  this.appendColumnTitle(header, i18nString(UIStrings.invalidHeaderValue));
62
63
  break;
@@ -92,6 +93,7 @@ export class AttributionReportingIssueDetailsView extends AffectedResourcesView
92
93
  switch (issueCode) {
93
94
  case IssuesManager.AttributionReportingIssue.IssueCode.InvalidRegisterSourceHeader:
94
95
  case IssuesManager.AttributionReportingIssue.IssueCode.InvalidRegisterTriggerHeader:
96
+ case IssuesManager.AttributionReportingIssue.IssueCode.InvalidEligibleHeader:
95
97
  this.#appendRequestOrEmptyCell(element, details.request);
96
98
  this.appendIssueDetailCell(element, details.invalidParameter || '');
97
99
  break;
@@ -27,17 +27,12 @@ details[open] .header-count {
27
27
  display: none;
28
28
  }
29
29
 
30
- details summary label {
30
+ details .hide-when-closed {
31
31
  display: none;
32
32
  }
33
33
 
34
- details[open] summary label {
35
- display: inline-flex;
36
- gap: 2px;
37
- }
38
-
39
- .raw-checkbox-container {
40
- float: right;
34
+ details[open] .hide-when-closed {
35
+ display: block;
41
36
  }
42
37
 
43
38
  details summary input {
@@ -175,3 +170,40 @@ div.raw-headers-row {
175
170
  font-size: 90%;
176
171
  color: var(--color-text-secondary);
177
172
  }
173
+
174
+ .header-grid-container {
175
+ display: inline-grid;
176
+ grid-template-columns: 156px 50px 1fr;
177
+ grid-gap: 4px;
178
+ /* Make this fit into the same line as the summary marker */
179
+ width: calc(100% - 15px);
180
+ }
181
+
182
+ .header-grid-container div:last-child {
183
+ text-align: right;
184
+ }
185
+
186
+ .header .devtools-link {
187
+ color: var(--color-text-primary);
188
+ }
189
+
190
+ x-link .inline-icon { /* stylelint-disable-line selector-type-no-unknown */
191
+ padding-right: 3px;
192
+ }
193
+
194
+ .purple-dot {
195
+ filter: hue-rotate(160deg);
196
+ }
197
+
198
+ .-theme-with-dark-background .purple-dot,
199
+ :host-context(.-theme-with-dark-background) .purple-dot {
200
+ filter: invert(80%) hue-rotate(350deg);
201
+ }
202
+
203
+ summary label {
204
+ color: var(--color-text-secondary);
205
+ }
206
+
207
+ summary label:hover {
208
+ color: var(--color-text-primary);
209
+ }