chrome-devtools-frontend 1.0.996044 → 1.0.996595

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/AUTHORS CHANGED
@@ -26,6 +26,7 @@ Edward Trist <edwardtrist@gmail.com>
26
26
  Ergün Erdoğmuş <erdogmusergun@gmail.com>
27
27
  Eric Rannaud <eric.rannaud@gmail.com>
28
28
  Feng Lu <lufengd3@gmail.com>
29
+ Feng Yu <f3n67u@gmail.com>
29
30
  Gabriel Luong <gabriel.luong@gmail.com>
30
31
  Gautham Banasandra <gautham.bangalore@gmail.com>
31
32
  Jacky Hu <flameddd@gmail.com>
@@ -12563,6 +12563,9 @@
12563
12563
  "ui/legacy/TargetCrashedScreen.ts | oncePageIsReloadedDevtoolsWill": {
12564
12564
  "message": "Once page is reloaded, DevTools will automatically reconnect."
12565
12565
  },
12566
+ "ui/legacy/Toolbar.ts | clearInput": {
12567
+ "message": "Clear input"
12568
+ },
12566
12569
  "ui/legacy/Toolbar.ts | notPressed": {
12567
12570
  "message": "not pressed"
12568
12571
  },
@@ -12563,6 +12563,9 @@
12563
12563
  "ui/legacy/TargetCrashedScreen.ts | oncePageIsReloadedDevtoolsWill": {
12564
12564
  "message": "Ôńĉé p̂áĝé îś r̂él̂óâd́êd́, D̂év̂T́ôól̂ś ŵíl̂ĺ âút̂óm̂át̂íĉál̂ĺŷ ŕêćôńn̂éĉt́."
12565
12565
  },
12566
+ "ui/legacy/Toolbar.ts | clearInput": {
12567
+ "message": "Ĉĺêár̂ ín̂ṕût́"
12568
+ },
12566
12569
  "ui/legacy/Toolbar.ts | notPressed": {
12567
12570
  "message": "n̂ót̂ ṕr̂éŝśêd́"
12568
12571
  },
@@ -2,10 +2,11 @@
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 Protocol from '../../generated/protocol.js';
5
6
  import * as TextUtils from '../../models/text_utils/text_utils.js';
6
- import type * as Protocol from '../../generated/protocol.js';
7
7
 
8
8
  import {cssMetadata, CustomVariableRegex, VariableRegex} from './CSSMetadata.js';
9
+
9
10
  import type {CSSModel} from './CSSModel.js';
10
11
  import type {CSSProperty} from './CSSProperty.js';
11
12
  import {CSSKeyframesRule, CSSStyleRule} from './CSSRule.js';
@@ -22,6 +23,7 @@ export class CSSMatchedStyles {
22
23
  readonly #inheritedStyles: Set<CSSStyleDeclaration>;
23
24
  readonly #mainDOMCascade: DOMInheritanceCascade;
24
25
  readonly #pseudoDOMCascades: Map<Protocol.DOM.PseudoType, DOMInheritanceCascade>;
26
+ readonly #customHighlightPseudoDOMCascades: Map<string, DOMInheritanceCascade>;
25
27
  readonly #styleToDOMCascade: Map<CSSStyleDeclaration, DOMInheritanceCascade>;
26
28
 
27
29
  constructor(
@@ -48,10 +50,13 @@ export class CSSMatchedStyles {
48
50
  }
49
51
 
50
52
  this.#mainDOMCascade = this.buildMainCascade(inlinePayload, attributesPayload, matchedPayload, inheritedPayload);
51
- this.#pseudoDOMCascades = this.buildPseudoCascades(pseudoPayload, inheritedPseudoPayload);
53
+ [this.#pseudoDOMCascades, this.#customHighlightPseudoDOMCascades] =
54
+ this.buildPseudoCascades(pseudoPayload, inheritedPseudoPayload);
52
55
 
53
56
  this.#styleToDOMCascade = new Map();
54
- for (const domCascade of Array.from(this.#pseudoDOMCascades.values()).concat(this.#mainDOMCascade)) {
57
+ for (const domCascade of Array.from(this.#customHighlightPseudoDOMCascades.values())
58
+ .concat(Array.from(this.#pseudoDOMCascades.values()))
59
+ .concat(this.#mainDOMCascade)) {
55
60
  for (const style of domCascade.styles()) {
56
61
  this.#styleToDOMCascade.set(style, domCascade);
57
62
  }
@@ -206,36 +211,133 @@ export class CSSMatchedStyles {
206
211
  }
207
212
  }
208
213
 
214
+ /**
215
+ * Pseudo rule matches received via the inspector protocol are grouped by pseudo type.
216
+ * For custom highlight pseudos, we need to instead group the rule matches by highlight
217
+ * name in order to produce separate cascades for each highlight name. This is necessary
218
+ * so that styles of ::highlight(foo) are not shown as overriding styles of ::highlight(bar).
219
+ *
220
+ * This helper function takes a list of rule matches and generates separate NodeCascades
221
+ * for each custom highlight name that was matched.
222
+ */
223
+ private buildSplitCustomHighlightCascades(
224
+ rules: Protocol.CSS.RuleMatch[], node: DOMNode, isInherited: boolean,
225
+ pseudoCascades: Map<string, NodeCascade[]>): void {
226
+ const splitHighlightRules = new Map<string, CSSStyleDeclaration[]>();
227
+
228
+ for (let j = rules.length - 1; j >= 0; --j) {
229
+ const highlightNamesToMatchingSelectorIndices = this.customHighlightNamesToMatchingSelectorIndices(rules[j]);
230
+
231
+ for (const [highlightName, matchingSelectors] of highlightNamesToMatchingSelectorIndices) {
232
+ const pseudoRule = new CSSStyleRule(this.#cssModelInternal, rules[j].rule);
233
+ this.#nodeForStyleInternal.set(pseudoRule.style, node);
234
+ if (isInherited) {
235
+ this.#inheritedStyles.add(pseudoRule.style);
236
+ }
237
+ this.addMatchingSelectors(node, pseudoRule, matchingSelectors);
238
+
239
+ const ruleListForHighlightName = splitHighlightRules.get(highlightName);
240
+ if (ruleListForHighlightName) {
241
+ ruleListForHighlightName.push(pseudoRule.style);
242
+ } else {
243
+ splitHighlightRules.set(highlightName, [pseudoRule.style]);
244
+ }
245
+ }
246
+ }
247
+
248
+ for (const [highlightName, highlightStyles] of splitHighlightRules) {
249
+ const nodeCascade = new NodeCascade(this, highlightStyles, isInherited, true /* #isHighlightPseudoCascade*/);
250
+ const cascadeListForHighlightName = pseudoCascades.get(highlightName);
251
+ if (cascadeListForHighlightName) {
252
+ cascadeListForHighlightName.push(nodeCascade);
253
+ } else {
254
+ pseudoCascades.set(highlightName, [nodeCascade]);
255
+ }
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Return a mapping of the highlight names in the specified RuleMatch to
261
+ * the indices of selectors in that selector list with that highlight name.
262
+ *
263
+ * For example, consider the following ruleset:
264
+ * span::highlight(foo), div, #mySpan::highlight(bar), .highlighted::highlight(foo) {
265
+ * color: blue;
266
+ * }
267
+ *
268
+ * For a <span id="mySpan" class="highlighted"></span>, a RuleMatch for that span
269
+ * would have matchingSelectors [0, 2, 3] indicating that the span
270
+ * matches all of the highlight selectors.
271
+ *
272
+ * For that RuleMatch, this function would produce the following map:
273
+ * {
274
+ * "foo": [0, 3],
275
+ * "bar": [2]
276
+ * }
277
+ *
278
+ * @param ruleMatch
279
+ * @returns A mapping of highlight names to lists of indices into the selector
280
+ * list associated with ruleMatch. The indices correspond to the selectors in the rule
281
+ * associated with the key's highlight name.
282
+ */
283
+ private customHighlightNamesToMatchingSelectorIndices(ruleMatch: Protocol.CSS.RuleMatch): Map<string, number[]> {
284
+ const highlightNamesToMatchingSelectors = new Map<string, number[]>();
285
+
286
+ for (let i = 0; i < ruleMatch.matchingSelectors.length; i++) {
287
+ const matchingSelectorIndex = ruleMatch.matchingSelectors[i];
288
+ const selectorText = ruleMatch.rule.selectorList.selectors[matchingSelectorIndex].text;
289
+ const highlightNameMatch = selectorText.match(/::highlight\((.*)\)/);
290
+ if (highlightNameMatch) {
291
+ const highlightName = highlightNameMatch[1];
292
+ const selectorsForName = highlightNamesToMatchingSelectors.get(highlightName);
293
+ if (selectorsForName) {
294
+ selectorsForName.push(matchingSelectorIndex);
295
+ } else {
296
+ highlightNamesToMatchingSelectors.set(highlightName, [matchingSelectorIndex]);
297
+ }
298
+ }
299
+ }
300
+ return highlightNamesToMatchingSelectors;
301
+ }
302
+
209
303
  private buildPseudoCascades(
210
304
  pseudoPayload: Protocol.CSS.PseudoElementMatches[],
211
305
  inheritedPseudoPayload: Protocol.CSS.InheritedPseudoElementMatches[]):
212
- Map<Protocol.DOM.PseudoType, DOMInheritanceCascade> {
306
+ [Map<Protocol.DOM.PseudoType, DOMInheritanceCascade>, Map<string, DOMInheritanceCascade>] {
213
307
  const pseudoInheritanceCascades = new Map<Protocol.DOM.PseudoType, DOMInheritanceCascade>();
308
+ const customHighlightPseudoInheritanceCascades = new Map<string, DOMInheritanceCascade>();
214
309
  if (!pseudoPayload) {
215
- return pseudoInheritanceCascades;
310
+ return [pseudoInheritanceCascades, customHighlightPseudoInheritanceCascades];
216
311
  }
217
312
 
218
313
  const pseudoCascades = new Map<Protocol.DOM.PseudoType, NodeCascade[]>();
314
+ const customHighlightPseudoCascades = new Map<string, NodeCascade[]>();
219
315
  for (let i = 0; i < pseudoPayload.length; ++i) {
220
316
  const entryPayload = pseudoPayload[i];
221
317
  // PseudoElement nodes are not created unless "content" css property is set.
222
318
  const pseudoElement = this.#nodeInternal.pseudoElements().get(entryPayload.pseudoType) || null;
223
319
  const pseudoStyles = [];
224
320
  const rules = entryPayload.matches || [];
225
- for (let j = rules.length - 1; j >= 0; --j) {
226
- const pseudoRule = new CSSStyleRule(this.#cssModelInternal, rules[j].rule);
227
- pseudoStyles.push(pseudoRule.style);
228
- const nodeForStyle =
229
- cssMetadata().isHighlightPseudoType(entryPayload.pseudoType) ? this.#nodeInternal : pseudoElement;
230
- this.#nodeForStyleInternal.set(pseudoRule.style, nodeForStyle);
231
- if (nodeForStyle) {
232
- this.addMatchingSelectors(nodeForStyle, pseudoRule, rules[j].matchingSelectors);
321
+
322
+ if (entryPayload.pseudoType === Protocol.DOM.PseudoType.Highlight) {
323
+ this.buildSplitCustomHighlightCascades(
324
+ rules, this.#nodeInternal, false /* #isInherited */, customHighlightPseudoCascades);
325
+ } else {
326
+ for (let j = rules.length - 1; j >= 0; --j) {
327
+ const pseudoRule = new CSSStyleRule(this.#cssModelInternal, rules[j].rule);
328
+ pseudoStyles.push(pseudoRule.style);
329
+ const nodeForStyle =
330
+ cssMetadata().isHighlightPseudoType(entryPayload.pseudoType) ? this.#nodeInternal : pseudoElement;
331
+ this.#nodeForStyleInternal.set(pseudoRule.style, nodeForStyle);
332
+ if (nodeForStyle) {
333
+ this.addMatchingSelectors(nodeForStyle, pseudoRule, rules[j].matchingSelectors);
334
+ }
233
335
  }
336
+ const isHighlightPseudoCascade = cssMetadata().isHighlightPseudoType(entryPayload.pseudoType);
337
+ const nodeCascade = new NodeCascade(
338
+ this, pseudoStyles, false /* #isInherited */, isHighlightPseudoCascade /* #isHighlightPseudoCascade*/);
339
+ pseudoCascades.set(entryPayload.pseudoType, [nodeCascade]);
234
340
  }
235
- const isHighlightPseudoCascade = cssMetadata().isHighlightPseudoType(entryPayload.pseudoType);
236
- const nodeCascade = new NodeCascade(
237
- this, pseudoStyles, false /* #isInherited */, isHighlightPseudoCascade /* #isHighlightPseudoCascade*/);
238
- pseudoCascades.set(entryPayload.pseudoType, [nodeCascade]);
239
341
  }
240
342
 
241
343
  if (inheritedPseudoPayload) {
@@ -244,24 +346,30 @@ export class CSSMatchedStyles {
244
346
  const inheritedPseudoMatches = inheritedPseudoPayload[i].pseudoElements;
245
347
  for (let j = 0; j < inheritedPseudoMatches.length; ++j) {
246
348
  const inheritedEntryPayload = inheritedPseudoMatches[j];
247
- const pseudoStyles = [];
248
349
  const rules = inheritedEntryPayload.matches || [];
249
- for (let k = rules.length - 1; k >= 0; --k) {
250
- const pseudoRule = new CSSStyleRule(this.#cssModelInternal, rules[k].rule);
251
- pseudoStyles.push(pseudoRule.style);
252
- this.#nodeForStyleInternal.set(pseudoRule.style, parentNode);
253
- this.#inheritedStyles.add(pseudoRule.style);
254
- this.addMatchingSelectors(parentNode, pseudoRule, rules[k].matchingSelectors);
255
- }
256
350
 
257
- const isHighlightPseudoCascade = cssMetadata().isHighlightPseudoType(inheritedEntryPayload.pseudoType);
258
- const nodeCascade = new NodeCascade(
259
- this, pseudoStyles, true /* #isInherited */, isHighlightPseudoCascade /* #isHighlightPseudoCascade*/);
260
- const cascadeListForPseudoType = pseudoCascades.get(inheritedEntryPayload.pseudoType);
261
- if (cascadeListForPseudoType) {
262
- cascadeListForPseudoType.push(nodeCascade);
351
+ if (inheritedEntryPayload.pseudoType === Protocol.DOM.PseudoType.Highlight) {
352
+ this.buildSplitCustomHighlightCascades(
353
+ rules, parentNode, true /* #isInherited */, customHighlightPseudoCascades);
263
354
  } else {
264
- pseudoCascades.set(inheritedEntryPayload.pseudoType, [nodeCascade]);
355
+ const pseudoStyles = [];
356
+ for (let k = rules.length - 1; k >= 0; --k) {
357
+ const pseudoRule = new CSSStyleRule(this.#cssModelInternal, rules[k].rule);
358
+ pseudoStyles.push(pseudoRule.style);
359
+ this.#nodeForStyleInternal.set(pseudoRule.style, parentNode);
360
+ this.#inheritedStyles.add(pseudoRule.style);
361
+ this.addMatchingSelectors(parentNode, pseudoRule, rules[k].matchingSelectors);
362
+ }
363
+
364
+ const isHighlightPseudoCascade = cssMetadata().isHighlightPseudoType(inheritedEntryPayload.pseudoType);
365
+ const nodeCascade = new NodeCascade(
366
+ this, pseudoStyles, true /* #isInherited */, isHighlightPseudoCascade /* #isHighlightPseudoCascade*/);
367
+ const cascadeListForPseudoType = pseudoCascades.get(inheritedEntryPayload.pseudoType);
368
+ if (cascadeListForPseudoType) {
369
+ cascadeListForPseudoType.push(nodeCascade);
370
+ } else {
371
+ pseudoCascades.set(inheritedEntryPayload.pseudoType, [nodeCascade]);
372
+ }
265
373
  }
266
374
  }
267
375
 
@@ -275,7 +383,11 @@ export class CSSMatchedStyles {
275
383
  pseudoInheritanceCascades.set(pseudoType, new DOMInheritanceCascade(nodeCascade));
276
384
  }
277
385
 
278
- return pseudoInheritanceCascades;
386
+ for (const [highlightName, nodeCascade] of customHighlightPseudoCascades.entries()) {
387
+ customHighlightPseudoInheritanceCascades.set(highlightName, new DOMInheritanceCascade(nodeCascade));
388
+ }
389
+
390
+ return [pseudoInheritanceCascades, customHighlightPseudoInheritanceCascades];
279
391
  }
280
392
 
281
393
  private addMatchingSelectors(
@@ -405,6 +517,15 @@ export class CSSMatchedStyles {
405
517
  return new Set(this.#pseudoDOMCascades.keys());
406
518
  }
407
519
 
520
+ customHighlightPseudoStyles(highlightName: string): CSSStyleDeclaration[] {
521
+ const domCascade = this.#customHighlightPseudoDOMCascades.get(highlightName);
522
+ return domCascade ? domCascade.styles() : [];
523
+ }
524
+
525
+ customHighlightPseudoNames(): Set<string> {
526
+ return new Set(this.#customHighlightPseudoDOMCascades.keys());
527
+ }
528
+
408
529
  private containsInherited(style: CSSStyleDeclaration): boolean {
409
530
  const properties = style.allProperties();
410
531
  for (let i = 0; i < properties.length; ++i) {
@@ -462,6 +583,10 @@ export class CSSMatchedStyles {
462
583
  for (const domCascade of this.#pseudoDOMCascades.values()) {
463
584
  domCascade.reset();
464
585
  }
586
+
587
+ for (const domCascade of this.#customHighlightPseudoDOMCascades.values()) {
588
+ domCascade.reset();
589
+ }
465
590
  }
466
591
  }
467
592
 
@@ -313,15 +313,8 @@ export class CSSMetadata {
313
313
  }
314
314
 
315
315
  isHighlightPseudoType(pseudoType: Protocol.DOM.PseudoType): boolean {
316
- // TODO(crbug.com/1164461) Currently devtools-frontend groups all custom highlight
317
- // pseudos together in the same pseudo cascade, regardless of highlight name. This means that
318
- // the result of displaying "overloaded" highlight styles as crossed-out can produce
319
- // misleading results, because properties from highlights with one name can be shown as overloaded by
320
- // properties from highlights with another name.
321
- // So until that is fixed, don't include custom highlights among the highlight pseudos
322
- // for which we apply overloaded property annotations.
323
316
  return (
324
- /* pseudoType === Protocol.DOM.PseudoType.Highlight || */ pseudoType === Protocol.DOM.PseudoType.Selection ||
317
+ pseudoType === Protocol.DOM.PseudoType.Highlight || pseudoType === Protocol.DOM.PseudoType.Selection ||
325
318
  pseudoType === Protocol.DOM.PseudoType.TargetText || pseudoType === Protocol.DOM.PseudoType.GrammarError ||
326
319
  pseudoType === Protocol.DOM.PseudoType.SpellingError);
327
320
  }
@@ -273,38 +273,69 @@ export function registerCommands(inspectorBackend) {
273
273
  'Audits.GenericIssueErrorType', {CrossOriginPortalPostMessageError: 'CrossOriginPortalPostMessageError'});
274
274
  inspectorBackend.registerEnum('Audits.DeprecationIssueType', {
275
275
  AuthorizationCoveredByWildcard: 'AuthorizationCoveredByWildcard',
276
+ BatteryStatusInsecureOrigin: 'BatteryStatusInsecureOrigin',
277
+ CanRequestURLHTTPContainingNewline: 'CanRequestURLHTTPContainingNewline',
278
+ ChromeLoadTimesConnectionInfo: 'ChromeLoadTimesConnectionInfo',
279
+ ChromeLoadTimesFirstPaintAfterLoadTime: 'ChromeLoadTimesFirstPaintAfterLoadTime',
280
+ ChromeLoadTimesWasAlternateProtocolAvailable: 'ChromeLoadTimesWasAlternateProtocolAvailable',
276
281
  CookieWithTruncatingChar: 'CookieWithTruncatingChar',
277
282
  CrossOriginAccessBasedOnDocumentDomain: 'CrossOriginAccessBasedOnDocumentDomain',
278
283
  CrossOriginWindowAlert: 'CrossOriginWindowAlert',
279
284
  CrossOriginWindowConfirm: 'CrossOriginWindowConfirm',
285
+ CSSSelectorInternalMediaControlsOverlayCastButton: 'CSSSelectorInternalMediaControlsOverlayCastButton',
286
+ CustomCursorIntersectsViewport: 'CustomCursorIntersectsViewport',
280
287
  DeprecationExample: 'DeprecationExample',
281
288
  DocumentDomainSettingWithoutOriginAgentClusterHeader: 'DocumentDomainSettingWithoutOriginAgentClusterHeader',
289
+ EventPath: 'EventPath',
282
290
  GeolocationInsecureOrigin: 'GeolocationInsecureOrigin',
283
291
  GeolocationInsecureOriginDeprecatedNotRemoved: 'GeolocationInsecureOriginDeprecatedNotRemoved',
284
292
  GetUserMediaInsecureOrigin: 'GetUserMediaInsecureOrigin',
293
+ HostCandidateAttributeGetter: 'HostCandidateAttributeGetter',
294
+ InsecurePrivateNetworkSubresourceRequest: 'InsecurePrivateNetworkSubresourceRequest',
285
295
  LegacyConstraintGoogCpuOveruseDetection: 'LegacyConstraintGoogCpuOveruseDetection',
286
296
  LegacyConstraintGoogIPv6: 'LegacyConstraintGoogIPv6',
287
297
  LegacyConstraintGoogScreencastMinBitrate: 'LegacyConstraintGoogScreencastMinBitrate',
288
298
  LegacyConstraintGoogSuspendBelowMinBitrate: 'LegacyConstraintGoogSuspendBelowMinBitrate',
289
299
  LocalCSSFileExtensionRejected: 'LocalCSSFileExtensionRejected',
300
+ MediaElementAudioSourceNode: 'MediaElementAudioSourceNode',
301
+ MediaSourceAbortRemove: 'MediaSourceAbortRemove',
302
+ MediaSourceDurationTruncatingBuffered: 'MediaSourceDurationTruncatingBuffered',
303
+ NoSysexWebMIDIWithoutPermission: 'NoSysexWebMIDIWithoutPermission',
290
304
  NotificationInsecureOrigin: 'NotificationInsecureOrigin',
305
+ NotificationPermissionRequestedIframe: 'NotificationPermissionRequestedIframe',
291
306
  ObsoleteWebRtcCipherSuite: 'ObsoleteWebRtcCipherSuite',
307
+ PaymentRequestBasicCard: 'PaymentRequestBasicCard',
308
+ PaymentRequestShowWithoutGesture: 'PaymentRequestShowWithoutGesture',
292
309
  PictureSourceSrc: 'PictureSourceSrc',
293
310
  PrefixedCancelAnimationFrame: 'PrefixedCancelAnimationFrame',
294
311
  PrefixedRequestAnimationFrame: 'PrefixedRequestAnimationFrame',
312
+ PrefixedStorageInfo: 'PrefixedStorageInfo',
313
+ PrefixedVideoDisplayingFullscreen: 'PrefixedVideoDisplayingFullscreen',
314
+ PrefixedVideoEnterFullscreen: 'PrefixedVideoEnterFullscreen',
315
+ PrefixedVideoEnterFullScreen: 'PrefixedVideoEnterFullScreen',
316
+ PrefixedVideoExitFullscreen: 'PrefixedVideoExitFullscreen',
317
+ PrefixedVideoExitFullScreen: 'PrefixedVideoExitFullScreen',
318
+ PrefixedVideoSupportsFullscreen: 'PrefixedVideoSupportsFullscreen',
319
+ RangeExpand: 'RangeExpand',
320
+ RequestedSubresourceWithEmbeddedCredentials: 'RequestedSubresourceWithEmbeddedCredentials',
295
321
  RTCConstraintEnableDtlsSrtpFalse: 'RTCConstraintEnableDtlsSrtpFalse',
296
322
  RTCConstraintEnableDtlsSrtpTrue: 'RTCConstraintEnableDtlsSrtpTrue',
297
323
  RTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics:
298
324
  'RTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics',
299
325
  RTCPeerConnectionLegacyCreateWithMediaConstraints: 'RTCPeerConnectionLegacyCreateWithMediaConstraints',
326
+ RTCPeerConnectionSdpSemanticsPlanB: 'RTCPeerConnectionSdpSemanticsPlanB',
327
+ RtcpMuxPolicyNegotiate: 'RtcpMuxPolicyNegotiate',
300
328
  RTPDataChannel: 'RTPDataChannel',
329
+ SelectionAddRangeIntersect: 'SelectionAddRangeIntersect',
301
330
  SharedArrayBufferConstructedWithoutIsolation: 'SharedArrayBufferConstructedWithoutIsolation',
331
+ TextToSpeech_DisallowedByAutoplay: 'TextToSpeech_DisallowedByAutoplay',
302
332
  Untranslated: 'Untranslated',
303
333
  V8SharedArrayBufferConstructedInExtensionWithoutIsolation:
304
334
  'V8SharedArrayBufferConstructedInExtensionWithoutIsolation',
305
335
  WebCodecsVideoFrameDefaultTimestamp: 'WebCodecsVideoFrameDefaultTimestamp',
306
336
  XHRJSONEncodingDetection: 'XHRJSONEncodingDetection',
307
- XMLHttpRequestSynchronousInNonWorkerOutsideBeforeUnload: 'XMLHttpRequestSynchronousInNonWorkerOutsideBeforeUnload'
337
+ XMLHttpRequestSynchronousInNonWorkerOutsideBeforeUnload: 'XMLHttpRequestSynchronousInNonWorkerOutsideBeforeUnload',
338
+ XRSupportsSession: 'XRSupportsSession'
308
339
  });
309
340
  inspectorBackend.registerEnum(
310
341
  'Audits.ClientHintIssueReason',
@@ -2103,6 +2134,7 @@ export function registerCommands(inspectorBackend) {
2103
2134
  InterestCohort: 'interest-cohort',
2104
2135
  JoinAdInterestGroup: 'join-ad-interest-group',
2105
2136
  KeyboardMap: 'keyboard-map',
2137
+ LocalFonts: 'local-fonts',
2106
2138
  Magnetometer: 'magnetometer',
2107
2139
  Microphone: 'microphone',
2108
2140
  Midi: 'midi',
@@ -2450,7 +2482,6 @@ export function registerCommands(inspectorBackend) {
2450
2482
  {'name': 'marginLeft', 'type': 'number', 'optional': true},
2451
2483
  {'name': 'marginRight', 'type': 'number', 'optional': true},
2452
2484
  {'name': 'pageRanges', 'type': 'string', 'optional': true},
2453
- {'name': 'ignoreInvalidPageRanges', 'type': 'boolean', 'optional': true},
2454
2485
  {'name': 'headerTemplate', 'type': 'string', 'optional': true},
2455
2486
  {'name': 'footerTemplate', 'type': 'string', 'optional': true},
2456
2487
  {'name': 'preferCSSPageSize', 'type': 'boolean', 'optional': true},
@@ -1020,38 +1020,69 @@ export namespace Audits {
1020
1020
 
1021
1021
  export const enum DeprecationIssueType {
1022
1022
  AuthorizationCoveredByWildcard = 'AuthorizationCoveredByWildcard',
1023
+ BatteryStatusInsecureOrigin = 'BatteryStatusInsecureOrigin',
1024
+ CanRequestURLHTTPContainingNewline = 'CanRequestURLHTTPContainingNewline',
1025
+ ChromeLoadTimesConnectionInfo = 'ChromeLoadTimesConnectionInfo',
1026
+ ChromeLoadTimesFirstPaintAfterLoadTime = 'ChromeLoadTimesFirstPaintAfterLoadTime',
1027
+ ChromeLoadTimesWasAlternateProtocolAvailable = 'ChromeLoadTimesWasAlternateProtocolAvailable',
1023
1028
  CookieWithTruncatingChar = 'CookieWithTruncatingChar',
1024
1029
  CrossOriginAccessBasedOnDocumentDomain = 'CrossOriginAccessBasedOnDocumentDomain',
1025
1030
  CrossOriginWindowAlert = 'CrossOriginWindowAlert',
1026
1031
  CrossOriginWindowConfirm = 'CrossOriginWindowConfirm',
1032
+ CSSSelectorInternalMediaControlsOverlayCastButton = 'CSSSelectorInternalMediaControlsOverlayCastButton',
1033
+ CustomCursorIntersectsViewport = 'CustomCursorIntersectsViewport',
1027
1034
  DeprecationExample = 'DeprecationExample',
1028
1035
  DocumentDomainSettingWithoutOriginAgentClusterHeader = 'DocumentDomainSettingWithoutOriginAgentClusterHeader',
1036
+ EventPath = 'EventPath',
1029
1037
  GeolocationInsecureOrigin = 'GeolocationInsecureOrigin',
1030
1038
  GeolocationInsecureOriginDeprecatedNotRemoved = 'GeolocationInsecureOriginDeprecatedNotRemoved',
1031
1039
  GetUserMediaInsecureOrigin = 'GetUserMediaInsecureOrigin',
1040
+ HostCandidateAttributeGetter = 'HostCandidateAttributeGetter',
1041
+ InsecurePrivateNetworkSubresourceRequest = 'InsecurePrivateNetworkSubresourceRequest',
1032
1042
  LegacyConstraintGoogCpuOveruseDetection = 'LegacyConstraintGoogCpuOveruseDetection',
1033
1043
  LegacyConstraintGoogIPv6 = 'LegacyConstraintGoogIPv6',
1034
1044
  LegacyConstraintGoogScreencastMinBitrate = 'LegacyConstraintGoogScreencastMinBitrate',
1035
1045
  LegacyConstraintGoogSuspendBelowMinBitrate = 'LegacyConstraintGoogSuspendBelowMinBitrate',
1036
1046
  LocalCSSFileExtensionRejected = 'LocalCSSFileExtensionRejected',
1047
+ MediaElementAudioSourceNode = 'MediaElementAudioSourceNode',
1048
+ MediaSourceAbortRemove = 'MediaSourceAbortRemove',
1049
+ MediaSourceDurationTruncatingBuffered = 'MediaSourceDurationTruncatingBuffered',
1050
+ NoSysexWebMIDIWithoutPermission = 'NoSysexWebMIDIWithoutPermission',
1037
1051
  NotificationInsecureOrigin = 'NotificationInsecureOrigin',
1052
+ NotificationPermissionRequestedIframe = 'NotificationPermissionRequestedIframe',
1038
1053
  ObsoleteWebRtcCipherSuite = 'ObsoleteWebRtcCipherSuite',
1054
+ PaymentRequestBasicCard = 'PaymentRequestBasicCard',
1055
+ PaymentRequestShowWithoutGesture = 'PaymentRequestShowWithoutGesture',
1039
1056
  PictureSourceSrc = 'PictureSourceSrc',
1040
1057
  PrefixedCancelAnimationFrame = 'PrefixedCancelAnimationFrame',
1041
1058
  PrefixedRequestAnimationFrame = 'PrefixedRequestAnimationFrame',
1059
+ PrefixedStorageInfo = 'PrefixedStorageInfo',
1060
+ PrefixedVideoDisplayingFullscreen = 'PrefixedVideoDisplayingFullscreen',
1061
+ PrefixedVideoEnterFullscreen = 'PrefixedVideoEnterFullscreen',
1062
+ PrefixedVideoEnterFullScreen = 'PrefixedVideoEnterFullScreen',
1063
+ PrefixedVideoExitFullscreen = 'PrefixedVideoExitFullscreen',
1064
+ PrefixedVideoExitFullScreen = 'PrefixedVideoExitFullScreen',
1065
+ PrefixedVideoSupportsFullscreen = 'PrefixedVideoSupportsFullscreen',
1066
+ RangeExpand = 'RangeExpand',
1067
+ RequestedSubresourceWithEmbeddedCredentials = 'RequestedSubresourceWithEmbeddedCredentials',
1042
1068
  RTCConstraintEnableDtlsSrtpFalse = 'RTCConstraintEnableDtlsSrtpFalse',
1043
1069
  RTCConstraintEnableDtlsSrtpTrue = 'RTCConstraintEnableDtlsSrtpTrue',
1044
1070
  RTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics =
1045
1071
  'RTCPeerConnectionComplexPlanBSdpUsingDefaultSdpSemantics',
1046
1072
  RTCPeerConnectionLegacyCreateWithMediaConstraints = 'RTCPeerConnectionLegacyCreateWithMediaConstraints',
1073
+ RTCPeerConnectionSdpSemanticsPlanB = 'RTCPeerConnectionSdpSemanticsPlanB',
1074
+ RtcpMuxPolicyNegotiate = 'RtcpMuxPolicyNegotiate',
1047
1075
  RTPDataChannel = 'RTPDataChannel',
1076
+ SelectionAddRangeIntersect = 'SelectionAddRangeIntersect',
1048
1077
  SharedArrayBufferConstructedWithoutIsolation = 'SharedArrayBufferConstructedWithoutIsolation',
1078
+ TextToSpeech_DisallowedByAutoplay = 'TextToSpeech_DisallowedByAutoplay',
1049
1079
  Untranslated = 'Untranslated',
1050
1080
  V8SharedArrayBufferConstructedInExtensionWithoutIsolation =
1051
1081
  'V8SharedArrayBufferConstructedInExtensionWithoutIsolation',
1052
1082
  WebCodecsVideoFrameDefaultTimestamp = 'WebCodecsVideoFrameDefaultTimestamp',
1053
1083
  XHRJSONEncodingDetection = 'XHRJSONEncodingDetection',
1054
1084
  XMLHttpRequestSynchronousInNonWorkerOutsideBeforeUnload = 'XMLHttpRequestSynchronousInNonWorkerOutsideBeforeUnload',
1085
+ XRSupportsSession = 'XRSupportsSession',
1055
1086
  }
1056
1087
 
1057
1088
  /**
@@ -5162,6 +5193,8 @@ export namespace Emulation {
5162
5193
  architecture: string;
5163
5194
  model: string;
5164
5195
  mobile: boolean;
5196
+ bitness?: string;
5197
+ wow64?: boolean;
5165
5198
  }
5166
5199
 
5167
5200
  /**
@@ -10200,6 +10233,7 @@ export namespace Page {
10200
10233
  InterestCohort = 'interest-cohort',
10201
10234
  JoinAdInterestGroup = 'join-ad-interest-group',
10202
10235
  KeyboardMap = 'keyboard-map',
10236
+ LocalFonts = 'local-fonts',
10203
10237
  Magnetometer = 'magnetometer',
10204
10238
  Microphone = 'microphone',
10205
10239
  Midi = 'midi',
@@ -11300,15 +11334,16 @@ export namespace Page {
11300
11334
  */
11301
11335
  marginRight?: number;
11302
11336
  /**
11303
- * Paper ranges to print, e.g., '1-5, 8, 11-13'. Defaults to the empty string, which means
11304
- * print all pages.
11337
+ * Paper ranges to print, one based, e.g., '1-5, 8, 11-13'. Pages are
11338
+ * printed in the document order, not in the order specified, and no
11339
+ * more than once.
11340
+ * Defaults to empty string, which implies the entire document is printed.
11341
+ * The page numbers are quietly capped to actual page count of the
11342
+ * document, and ranges beyond the end of the document are ignored.
11343
+ * If this results in no pages to print, an error is reported.
11344
+ * It is an error to specify a range with start greater than end.
11305
11345
  */
11306
11346
  pageRanges?: string;
11307
- /**
11308
- * Whether to silently ignore invalid but successfully parsed page ranges, such as '3-2'.
11309
- * Defaults to false.
11310
- */
11311
- ignoreInvalidPageRanges?: boolean;
11312
11347
  /**
11313
11348
  * HTML template for the print header. Should be valid HTML markup with following
11314
11349
  * classes used to inject printing values into them:
@@ -61,12 +61,12 @@ const UIStrings = {
61
61
  documentDomainSettingWithoutOriginAgentClusterHeader:
62
62
  'Relaxing the same-origin policy by setting `document.domain` is deprecated, and will be disabled by default. To continue using this feature, please opt-out of origin-keyed agent clusters by sending an `Origin-Agent-Cluster: ?0` header along with the HTTP response for the document and frames. See https://developer.chrome.com/blog/immutable-document-domain/ for more details.',
63
63
  /**
64
- *@description TODO(crbug.com/1318853): Description needed for translation
64
+ *@description Warning displayed to developers when the Geolocation API is used from an insecure origin (one that isn't localhost or doesn't use HTTPS) to notify them that this use is no longer supported.
65
65
  */
66
66
  geolocationInsecureOrigin:
67
67
  '`getCurrentPosition()` and `watchPosition()` no longer work on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. See https://goo.gl/rStTGz for more details.',
68
68
  /**
69
- *@description TODO(crbug.com/1318855): Description needed for translation
69
+ *@description Warning displayed to developers when the Geolocation API is used from an insecure origin (one that isn't localhost or doesn't use HTTPS) to notify them that this use is deprecated.
70
70
  */
71
71
  geolocationInsecureOriginDeprecatedNotRemoved:
72
72
  '`getCurrentPosition()` and `watchPosition()` are deprecated on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. See https://goo.gl/rStTGz for more details.',
@@ -6,6 +6,7 @@ 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
8
  import * as Root from '../../core/root/root.js';
9
+ import type * as Formatter from '../../models/formatter/formatter.js';
9
10
  import {formatCSSChangesFromDiff} from '../../panels/utils/utils.js';
10
11
  import * as Diff from '../../third_party/diff/diff.js';
11
12
  import * as DiffView from '../../ui/components/diff_view/diff_view.js';
@@ -72,6 +73,7 @@ export class ChangesView extends UI.Widget.VBox {
72
73
  private readonly workspaceDiff: WorkspaceDiff.WorkspaceDiff.WorkspaceDiffImpl;
73
74
  readonly changesSidebar: ChangesSidebar;
74
75
  private selectedUISourceCode: Workspace.UISourceCode.UISourceCode|null;
76
+ #selectedSourceCodeFormattedMapping?: Formatter.ScriptFormatter.FormatterSourceMapping;
75
77
  private readonly diffContainer: HTMLElement;
76
78
  private readonly toolbar: UI.Toolbar.Toolbar;
77
79
  private readonly diffStats: UI.Toolbar.ToolbarText;
@@ -167,16 +169,27 @@ export class ChangesView extends UI.Widget.VBox {
167
169
  if (!this.selectedUISourceCode) {
168
170
  return;
169
171
  }
170
- for (let target: HTMLElement|null = event.target as HTMLElement; target; target = target.parentElement) {
171
- if (target.classList.contains('diff-line-content')) {
172
- const number = target.getAttribute('data-line-number');
173
- if (number) {
174
- // Unfortunately, caretRangeFromPoint is broken in shadow
175
- // roots, which makes determining the character offset more
176
- // work than justified here.
177
- void Common.Revealer.reveal(this.selectedUISourceCode.uiLocation(Number(number) - 1, 0), false);
178
- event.consume(true);
172
+
173
+ for (const target of event.composedPath()) {
174
+ if (!(target instanceof HTMLElement)) {
175
+ continue;
176
+ }
177
+ const selection = target.ownerDocument.getSelection();
178
+ if (selection?.toString()) {
179
+ // We abort source revelation when user has text selection.
180
+ break;
181
+ }
182
+ if (target.classList.contains('diff-line-content') && target.hasAttribute('data-line-number')) {
183
+ let lineNumber = Number(target.dataset.lineNumber) - 1;
184
+ // Unfortunately, caretRangeFromPoint is broken in shadow
185
+ // roots, which makes determining the character offset more
186
+ // work than justified here.
187
+ if (Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.PRECISE_CHANGES) &&
188
+ this.#selectedSourceCodeFormattedMapping) {
189
+ lineNumber = this.#selectedSourceCodeFormattedMapping.formattedToOriginal(lineNumber, 0)[0];
179
190
  }
191
+ void Common.Revealer.reveal(this.selectedUISourceCode.uiLocation(lineNumber, 0), false);
192
+ event.consume(true);
180
193
  break;
181
194
  } else if (target.classList.contains('diff-listing')) {
182
195
  break;
@@ -220,10 +233,12 @@ export class ChangesView extends UI.Widget.VBox {
220
233
  return;
221
234
  }
222
235
  const diffResponse = await this.workspaceDiff.requestDiff(
223
- uiSourceCode, {shouldFormatDiff: Root.Runtime.experiments.isEnabled('preciseChanges')});
236
+ uiSourceCode,
237
+ {shouldFormatDiff: Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.PRECISE_CHANGES)});
224
238
  if (this.selectedUISourceCode !== uiSourceCode) {
225
239
  return;
226
240
  }
241
+ this.#selectedSourceCodeFormattedMapping = diffResponse?.formattedCurrentMapping;
227
242
  this.renderDiffRows(diffResponse?.diff);
228
243
  }
229
244
 
@@ -878,18 +878,19 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
878
878
  return node;
879
879
  }
880
880
 
881
- async revealAndSelectNode(node: SDK.DOMModel.DOMNode, focus: boolean, omitHighlight?: boolean): Promise<void> {
881
+ async revealAndSelectNode(nodeToReveal: SDK.DOMModel.DOMNode, focus: boolean, omitHighlight?: boolean):
882
+ Promise<void> {
882
883
  this.omitDefaultSelection = true;
883
884
 
884
- node = Common.Settings.Settings.instance().moduleSetting('showUAShadowDOM').get() ?
885
- node :
886
- this.leaveUserAgentShadowDOM(node);
885
+ const node = Common.Settings.Settings.instance().moduleSetting('showUAShadowDOM').get() ?
886
+ nodeToReveal :
887
+ this.leaveUserAgentShadowDOM(nodeToReveal);
887
888
  if (!omitHighlight) {
888
889
  node.highlightForTwoSeconds();
889
890
  }
890
891
 
891
892
  if (this.accessibilityTreeView) {
892
- void this.accessibilityTreeView.revealAndSelectNode(node);
893
+ void this.accessibilityTreeView.revealAndSelectNode(nodeToReveal);
893
894
  }
894
895
 
895
896
  await UI.ViewManager.ViewManager.instance().showView('elements', false, !focus);
@@ -1029,7 +1030,7 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
1029
1030
  };
1030
1031
 
1031
1032
  this.sidebarPaneView = UI.ViewManager.ViewManager.instance().createTabbedLocation(
1032
- () => UI.ViewManager.ViewManager.instance().showView('elements'));
1033
+ () => UI.ViewManager.ViewManager.instance().showView('elements'), 'Styles-pane-sidebar', false, true);
1033
1034
  const tabbedPane = this.sidebarPaneView.tabbedPane();
1034
1035
  if (this.splitMode !== _splitMode.Vertical) {
1035
1036
  this.splitWidget.installResizer(tabbedPane.headerElement());
@@ -859,17 +859,38 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
859
859
  }
860
860
  }
861
861
 
862
+ const customHighlightPseudoRulesets: {
863
+ highlightName: string|null,
864
+ pseudoType: Protocol.DOM.PseudoType,
865
+ pseudoStyles: SDK.CSSStyleDeclaration.CSSStyleDeclaration[],
866
+ }[] = Array.from(matchedStyles.customHighlightPseudoNames()).map(highlightName => {
867
+ return {
868
+ 'highlightName': highlightName,
869
+ 'pseudoType': Protocol.DOM.PseudoType.Highlight,
870
+ 'pseudoStyles': matchedStyles.customHighlightPseudoStyles(highlightName),
871
+ };
872
+ });
873
+
862
874
  let pseudoTypes: Protocol.DOM.PseudoType[] = [];
863
875
  const keys = matchedStyles.pseudoTypes();
864
876
  if (keys.delete(Protocol.DOM.PseudoType.Before)) {
865
877
  pseudoTypes.push(Protocol.DOM.PseudoType.Before);
866
878
  }
867
879
  pseudoTypes = pseudoTypes.concat([...keys].sort());
868
- for (const pseudoType of pseudoTypes) {
880
+
881
+ const otherPseudoRulesets: {
882
+ highlightName: string|null,
883
+ pseudoType: Protocol.DOM.PseudoType,
884
+ pseudoStyles: SDK.CSSStyleDeclaration.CSSStyleDeclaration[],
885
+ }[] = pseudoTypes.map(pseudoType => {
886
+ return {'highlightName': null, 'pseudoType': pseudoType, 'pseudoStyles': matchedStyles.pseudoStyles(pseudoType)};
887
+ });
888
+
889
+ const pseudoRulesets = customHighlightPseudoRulesets.concat(otherPseudoRulesets);
890
+ for (const pseudo of pseudoRulesets) {
869
891
  lastParentNode = null;
870
- const pseudoStyles = matchedStyles.pseudoStyles(pseudoType);
871
- for (let i = 0; i < pseudoStyles.length; ++i) {
872
- const style = pseudoStyles[i];
892
+ for (let i = 0; i < pseudo.pseudoStyles.length; ++i) {
893
+ const style = pseudo.pseudoStyles[i];
873
894
  const parentNode = matchedStyles.isInherited(style) ? matchedStyles.nodeForStyle(style) : null;
874
895
 
875
896
  // Start a new SectionBlock if this is the first rule for this pseudo type, or if this
@@ -877,10 +898,11 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
877
898
  if (i === 0 || parentNode !== lastParentNode) {
878
899
  lastLayers = null;
879
900
  if (parentNode) {
880
- const block = await SectionBlock.createInheritedPseudoTypeBlock(pseudoType, parentNode);
901
+ const block =
902
+ await SectionBlock.createInheritedPseudoTypeBlock(pseudo.pseudoType, pseudo.highlightName, parentNode);
881
903
  blocks.push(block);
882
904
  } else {
883
- const block = SectionBlock.createPseudoTypeBlock(pseudoType);
905
+ const block = SectionBlock.createPseudoTypeBlock(pseudo.pseudoType, pseudo.highlightName);
884
906
  blocks.push(block);
885
907
  }
886
908
  }
@@ -889,9 +911,7 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
889
911
  addLayerSeparator(style);
890
912
  const lastBlock = blocks[blocks.length - 1];
891
913
  this.idleCallbackManager.schedule(() => {
892
- const section = SDK.CSSMetadata.cssMetadata().isHighlightPseudoType(pseudoType) ?
893
- new HighlightPseudoStylePropertiesSection(this, matchedStyles, style, sectionIdx) :
894
- new StylePropertiesSection(this, matchedStyles, style, sectionIdx);
914
+ const section = new HighlightPseudoStylePropertiesSection(this, matchedStyles, style, sectionIdx);
895
915
  sectionIdx++;
896
916
  lastBlock.sections.push(section);
897
917
  });
@@ -1287,19 +1307,23 @@ export class SectionBlock {
1287
1307
  this.sections = [];
1288
1308
  }
1289
1309
 
1290
- static createPseudoTypeBlock(pseudoType: Protocol.DOM.PseudoType): SectionBlock {
1310
+ static createPseudoTypeBlock(pseudoType: Protocol.DOM.PseudoType, pseudoArgument: string|null): SectionBlock {
1291
1311
  const separatorElement = document.createElement('div');
1292
1312
  separatorElement.className = 'sidebar-separator';
1293
- separatorElement.textContent = i18nString(UIStrings.pseudoSElement, {PH1: pseudoType});
1313
+ const pseudoArgumentString = pseudoArgument ? `(${pseudoArgument})` : '';
1314
+ const pseudoTypeString = `${pseudoType}${pseudoArgumentString}`;
1315
+ separatorElement.textContent = i18nString(UIStrings.pseudoSElement, {PH1: pseudoTypeString});
1294
1316
  return new SectionBlock(separatorElement);
1295
1317
  }
1296
1318
 
1297
- static async createInheritedPseudoTypeBlock(pseudoType: Protocol.DOM.PseudoType, node: SDK.DOMModel.DOMNode):
1298
- Promise<SectionBlock> {
1319
+ static async createInheritedPseudoTypeBlock(
1320
+ pseudoType: Protocol.DOM.PseudoType, pseudoArgument: string|null,
1321
+ node: SDK.DOMModel.DOMNode): Promise<SectionBlock> {
1299
1322
  const separatorElement = document.createElement('div');
1300
1323
  separatorElement.className = 'sidebar-separator';
1301
-
1302
- UI.UIUtils.createTextChild(separatorElement, i18nString(UIStrings.inheritedFromSPseudoOf, {PH1: pseudoType}));
1324
+ const pseudoArgumentString = pseudoArgument ? `(${pseudoArgument})` : '';
1325
+ const pseudoTypeString = `${pseudoType}${pseudoArgumentString}`;
1326
+ UI.UIUtils.createTextChild(separatorElement, i18nString(UIStrings.inheritedFromSPseudoOf, {PH1: pseudoTypeString}));
1303
1327
  const link = await Common.Linkifier.Linkifier.linkify(node, {
1304
1328
  preventKeyboardFocus: true,
1305
1329
  tooltip: undefined,
@@ -130,6 +130,7 @@ ol.expanded {
130
130
  content: "";
131
131
  position: absolute;
132
132
  left: -4px;
133
+ top: 0;
133
134
  width: 2px;
134
135
  height: 100%;
135
136
  background-color: var(--color-accent-green);
@@ -226,7 +226,7 @@
226
226
  .sidebar-pane-section-toolbar {
227
227
  position: absolute;
228
228
  right: 0;
229
- bottom: 0;
229
+ bottom: -5px;
230
230
  z-index: 0;
231
231
  }
232
232
 
@@ -572,6 +572,7 @@ export abstract class HeapSnapshotGenericObjectNode extends HeapSnapshotGridNode
572
572
  valueStyle = 'number';
573
573
  break;
574
574
  case 'hidden':
575
+ case 'object shape':
575
576
  valueStyle = 'null';
576
577
  break;
577
578
  case 'array':
@@ -11,12 +11,14 @@
11
11
  font-size: var(--source-code-font-size);
12
12
  white-space: pre;
13
13
  line-height: 1.2em;
14
+ user-select: text;
14
15
  }
15
16
 
16
17
  .diff-line-number {
17
18
  color: var(--color-line-number);
18
19
  padding: 0 3px 0 9px;
19
20
  text-align: right;
21
+ user-select: none;
20
22
  }
21
23
 
22
24
  .diff-line-marker {
@@ -255,25 +255,36 @@ export class TreeOutline<TreeNodeDataType> extends HTMLElement {
255
255
  return this.#selectedTreeNode;
256
256
  }
257
257
 
258
- async #fetchNodeChildren(node: TreeNodeWithChildren<TreeNodeDataType>): Promise<TreeNode<TreeNodeDataType>[]> {
258
+ async #flattenSubtree(node: TreeNodeWithChildren<TreeNodeDataType>, filter: (node: TreeNodeDataType) => FilterOption):
259
+ Promise<TreeNode<TreeNodeDataType>[]> {
259
260
  const children = await getNodeChildren(node);
260
- if (!this.#nodeFilter) {
261
- return children;
262
- }
263
261
  const filteredChildren = [];
264
262
  for (const child of children) {
265
- const filtering = this.#nodeFilter(child.treeNodeData);
263
+ const filtering = filter(child.treeNodeData);
266
264
  // We always include the selected node in the tree, regardless of its filtering status.
267
- if (filtering === FilterOption.SHOW || this.#isSelectedNode(child) || child.id === this.#nodeIdPendingFocus) {
265
+ const toBeSelected = this.#isSelectedNode(child) || child.id === this.#nodeIdPendingFocus;
266
+ // If a node is already expanded we should not flatten it away.
267
+ const expanded = this.#nodeExpandedMap.get(child.id);
268
+ if (filtering === FilterOption.SHOW || toBeSelected || expanded) {
268
269
  filteredChildren.push(child);
269
270
  } else if (filtering === FilterOption.FLATTEN && isExpandableNode(child)) {
270
- const grandChildren = await this.#fetchNodeChildren(child);
271
+ const grandChildren = await this.#flattenSubtree(child, filter);
271
272
  filteredChildren.push(...grandChildren);
272
273
  }
273
274
  }
274
275
  return filteredChildren;
275
276
  }
276
277
 
278
+ async #fetchNodeChildren(node: TreeNodeWithChildren<TreeNodeDataType>): Promise<TreeNode<TreeNodeDataType>[]> {
279
+ const children = await getNodeChildren(node);
280
+ const filter = this.#nodeFilter;
281
+ if (!filter) {
282
+ return children;
283
+ }
284
+ const filteredDescendants = await this.#flattenSubtree(node, filter);
285
+ return filteredDescendants.length ? filteredDescendants : children;
286
+ }
287
+
277
288
  #setNodeExpandedState(node: TreeNode<TreeNodeDataType>, newExpandedState: boolean): void {
278
289
  this.#nodeExpandedMap.set(node.id, newExpandedState);
279
290
  }
@@ -57,6 +57,10 @@ const UIStrings = {
57
57
  *@description Announced screen reader message for ToolbarSettingToggle when the setting is toggled off.
58
58
  */
59
59
  notPressed: 'not pressed',
60
+ /**
61
+ *@description Tooltip shown when the user hovers over the clear icon to empty the text input.
62
+ */
63
+ clearInput: 'Clear input',
60
64
  };
61
65
  const str_ = i18n.i18n.registerUIStrings('ui/legacy/Toolbar.ts', UIStrings);
62
66
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -701,6 +705,7 @@ export class ToolbarInput extends ToolbarItem<ToolbarInput.EventTypes> {
701
705
  }
702
706
 
703
707
  const clearButton = this.element.createChild('div', 'toolbar-input-clear-button');
708
+ clearButton.title = UIStrings.clearInput;
704
709
  clearButton.appendChild(Icon.create('mediumicon-gray-cross-active', 'search-cancel-button'));
705
710
  clearButton.addEventListener('click', () => {
706
711
  this.setValue('', true);
@@ -23,6 +23,10 @@ button.soft-dropdown > .title {
23
23
  text-overflow: ellipsis;
24
24
  }
25
25
 
26
+ button.soft-dropdown:hover:not(:active) > .title {
27
+ color: var(--color-text-primary);
28
+ }
29
+
26
30
  @media (forced-colors: active) {
27
31
  button.soft-dropdown {
28
32
  border: 1px solid ButtonText;
package/package.json CHANGED
@@ -55,5 +55,5 @@
55
55
  "unittest": "scripts/test/run_unittests.py --no-text-coverage",
56
56
  "watch": "vpython third_party/node/node.py --output scripts/watch_build.js"
57
57
  },
58
- "version": "1.0.996044"
58
+ "version": "1.0.996595"
59
59
  }
@@ -0,0 +1,158 @@
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
+ module.exports = {
7
+ meta: {
8
+ type: 'problem',
9
+
10
+ docs: {
11
+ description: 'Inline type imports.',
12
+ category: 'Possible Errors',
13
+ },
14
+ fixable: 'code',
15
+ messages: {
16
+ inlineTypeImport: 'Type imports must be imported in the same import statement as values, using the type keyword',
17
+ convertTypeImport: 'Type imports must use the type modifier on each item, not on the overall import statement',
18
+ },
19
+ schema: [] // no options
20
+ },
21
+ create: function(context) {
22
+ // Stores any type imports (import type {} from ...).
23
+ // The key is the literal import path ("../foo.js");
24
+ const typeImports = new Map();
25
+ // Stores any value imports (import {} from ...).
26
+ // The key is the literal import path ("../foo.js");
27
+ const valueImports = new Map();
28
+
29
+ // Takes the node that represents an import ("Foo", "Foo as Bar") and
30
+ // return the literal text.
31
+ function getTextForImportSpecifier(specifier) {
32
+ // import {Foo as Bar} from 'foo';
33
+ // Foo = imported name
34
+ // Bar = local name
35
+ const localName = specifier.local.name;
36
+ const importedName = specifier.imported.name;
37
+ if (localName === importedName) {
38
+ // No `X as Y`, so just use either name.
39
+ return localName;
40
+ }
41
+ return `${importedName} as ${localName}`;
42
+ }
43
+
44
+ function mergeImports(fixer, typeImportNode, valueImportNode) {
45
+ // Get all the references from the type import node that we need to add to the value import node.
46
+ const typeImportSpecifiers = typeImportNode.specifiers.map(spec => {
47
+ return getTextForImportSpecifier(spec);
48
+ });
49
+
50
+ // Find the last value specifier, which we will then insert the type imports to.
51
+ const lastValueSpecifier = valueImportNode.specifiers[valueImportNode.specifiers.length - 1];
52
+
53
+ // Remember that we don't need to concern ourselves with indentation: in
54
+ // PRESUBMIT clang-format runs _after_ ESLint, so we can let Clang tidy
55
+ // up any rough edges.
56
+ const textToImport = ', ' +
57
+ typeImportSpecifiers
58
+ .map(spec => `type ${spec}`)
59
+ .join(', ');
60
+
61
+ return [
62
+ // Remove the type import
63
+ fixer.remove(typeImportNode),
64
+ // Add the type imports to the existing import
65
+ fixer.insertTextAfter(lastValueSpecifier, textToImport)
66
+ ];
67
+ }
68
+
69
+ function inlineTypeImportKeyword(fixer, typeImportNode) {
70
+ // We need to remove the " type" text after "import".
71
+ const importStart = typeImportNode.range[0];
72
+ const typeImportStart = importStart + 6; // 6 here = length of "import"
73
+ const typeImportEnd = typeImportStart + 5; // 5 here = length of "type" + 1 to remove the space after it.
74
+
75
+ const addTypeToSpecifiersFixers = typeImportNode.specifiers.map(spec => {
76
+ const newText = getTextForImportSpecifier(spec);
77
+
78
+ return fixer.replaceText(spec, `type ${newText}`);
79
+ });
80
+
81
+ return [
82
+ ...addTypeToSpecifiersFixers,
83
+ fixer.removeRange([typeImportStart, typeImportEnd]),
84
+ ];
85
+ }
86
+
87
+ return {
88
+ ImportDeclaration(node) {
89
+ // Note that we only care about named imports: import {} from 'foo.js'.
90
+ // This is because:
91
+ // 1: if we have `import type * as SDK from '../` that means we know we
92
+ // aren't using `SDK` for any values, otherwise we wouldn't have the
93
+ // `type` modifier.
94
+ // 2: similarly, `import type Foo from './foo'` follows (1). We also
95
+ // don't use this pattern in DevTools, but even if we did we don't have
96
+ // to worry about it.
97
+ // 3: Any side-effect imports (import './foo.js') are irrelevant.
98
+
99
+ if (!node.specifiers || node.specifiers.length < 1) {
100
+ // import './foo.js';
101
+ return;
102
+ }
103
+
104
+ if (node.specifiers[0].type === 'ImportDefaultSpecifier') {
105
+ // import Foo from './foo.js';
106
+ return;
107
+ }
108
+
109
+ if (node.specifiers[0].type === 'ImportNamespaceSpecifier') {
110
+ // import * as Foo from './foo.js';
111
+ return;
112
+ }
113
+
114
+ // Store the import
115
+ const importFilePath = node.source.value;
116
+ if (node.importKind === 'type') {
117
+ typeImports.set(importFilePath, node);
118
+ } else if (node.importKind === 'value') {
119
+ valueImports.set(importFilePath, node);
120
+ }
121
+ },
122
+ 'Program:exit'() {
123
+ // Loop over the type imports and see if there are any matching value
124
+ // imports.
125
+ // Looping this way means if there are any value imports without a
126
+ // matching type import, we leave them alone.
127
+ for (const [typeImportFilePath, typeImportNode] of typeImports) {
128
+ const valueImportNodeForFilePath = valueImports.get(typeImportFilePath);
129
+ if (valueImportNodeForFilePath) {
130
+ // If we've got here, we have two imports for the same file-path, one
131
+ // for types, and one for values, so let's merge them.
132
+ context.report({
133
+ node: typeImportNode,
134
+ messageId: 'inlineTypeImport',
135
+ fix(fixer) {
136
+ return mergeImports(fixer, typeImportNode, valueImportNodeForFilePath);
137
+ }
138
+ });
139
+ continue;
140
+ }
141
+
142
+ // At this point we have just a type import and no matching file
143
+ // import, but we still want to convert the import so that each
144
+ // imported reference uses the type modifier:
145
+ // BEFORE: import type {A, B} from '...';
146
+ // AFTER: import {type A, type B} from '...';
147
+ context.report({
148
+ node: typeImportNode,
149
+ messageId: 'convertTypeImport',
150
+ fix(fixer) {
151
+ return inlineTypeImportKeyword(fixer, typeImportNode);
152
+ }
153
+ });
154
+ }
155
+ },
156
+ };
157
+ }
158
+ };
@@ -0,0 +1,106 @@
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/inline_type_imports.js');
7
+ const ruleTester = new (require('eslint').RuleTester)({
8
+ parserOptions: {ecmaVersion: 9, sourceType: 'module'},
9
+ parser: require.resolve('@typescript-eslint/parser'),
10
+ });
11
+
12
+ ruleTester.run('inline_type_imports', rule, {
13
+ valid: [
14
+ {
15
+ code: 'import \'./foo.js\'',
16
+ },
17
+ {
18
+ code: 'import type * as Foo from \'./foo.js\'',
19
+ },
20
+ {
21
+ code: 'import * as Foo from \'./foo.js\'',
22
+ },
23
+ {
24
+ code: 'import Foo from \'./foo.js\'',
25
+ },
26
+ {
27
+ code: 'import type Foo from \'./foo.js\'',
28
+ },
29
+ {
30
+ code: 'import {type Foo} from \'./foo.js\'',
31
+ },
32
+ {
33
+ code: 'import {type Foo as Foo2} from \'./foo.js\'',
34
+ },
35
+ {
36
+ code: 'import {SomeValue, type Foo as Foo2} from \'./foo.js\'',
37
+ },
38
+ {
39
+ code: 'import {type Bar, type Foo as Foo2} from \'./foo.js\'',
40
+ },
41
+ ],
42
+ invalid: [
43
+ {
44
+ code: `import type {AType} from './foo.js';
45
+ import {AValue} from './foo.js';`,
46
+ output: `
47
+ import {AValue, type AType} from './foo.js';`,
48
+ filename: 'front_end/components/test.ts',
49
+ errors: [{messageId: 'inlineTypeImport'}],
50
+ },
51
+ {
52
+ code: `import type {AType} from './foo.js';
53
+ import {AValue} from './foo.js';
54
+ import type {Foo} from './blah.js'`,
55
+ output: `
56
+ import {AValue, type AType} from './foo.js';
57
+ import {type Foo} from './blah.js'`,
58
+ filename: 'front_end/components/test.ts',
59
+ errors: [{messageId: 'inlineTypeImport'}, {messageId: 'convertTypeImport'}],
60
+ },
61
+ {
62
+ code: `import type {AType} from './foo.js';
63
+ import {AValue} from './foo.js';
64
+ import {Foo} from './blah.js'`,
65
+ output: `
66
+ import {AValue, type AType} from './foo.js';
67
+ import {Foo} from './blah.js'`,
68
+ filename: 'front_end/components/test.ts',
69
+ errors: [{messageId: 'inlineTypeImport'}],
70
+ },
71
+ {
72
+ code: 'import type {AType} from \'./foo.js\';',
73
+ output: 'import {type AType} from \'./foo.js\';',
74
+ filename: 'front_end/components/test.ts',
75
+ errors: [{messageId: 'convertTypeImport'}],
76
+ },
77
+ {
78
+ code: 'import type {Foo as Bar} from \'./foo.js\';',
79
+ output: 'import {type Foo as Bar} from \'./foo.js\';',
80
+ filename: 'front_end/components/test.ts',
81
+ errors: [{messageId: 'convertTypeImport'}],
82
+ },
83
+ {
84
+ code: `import {SomeValue} from './foo.js';
85
+ import type {Foo as Bar} from './foo.js';`,
86
+ output: 'import {SomeValue, type Foo as Bar} from \'./foo.js\';\n',
87
+ filename: 'front_end/components/test.ts',
88
+ errors: [{messageId: 'inlineTypeImport'}],
89
+ },
90
+ {
91
+ code: `import {SomeValue} from './foo.js';
92
+ import type {Foo as Bar, Baz} from './foo.js';`,
93
+ output: 'import {SomeValue, type Foo as Bar, type Baz} from \'./foo.js\';\n',
94
+ filename: 'front_end/components/test.ts',
95
+ errors: [{messageId: 'inlineTypeImport'}],
96
+ },
97
+ {
98
+ code: `import type {SomeValue} from './baz.js';
99
+ import type {Foo as Bar, Baz} from './foo.js';`,
100
+ output: `import {type SomeValue} from './baz.js';
101
+ import {type Foo as Bar, type Baz} from './foo.js';`,
102
+ filename: 'front_end/components/test.ts',
103
+ errors: [{messageId: 'convertTypeImport'}, {messageId: 'convertTypeImport'}],
104
+ },
105
+ ]
106
+ });
@@ -4,7 +4,6 @@
4
4
 
5
5
  import * as fs from 'fs';
6
6
  import glob from 'glob';
7
- import * as path from 'path';
8
7
  import ts from 'typescript';
9
8
  import * as WebIDL2 from 'webidl2';
10
9
 
@@ -45,7 +44,7 @@ for (const file of files) {
45
44
  if (file.includes('testing')) {
46
45
  continue;
47
46
  }
48
- const data = fs.readFileSync(path.join(process.env.PWD, file), 'utf8');
47
+ const data = fs.readFileSync(file, 'utf8');
49
48
  const lines = data.split('\n');
50
49
  const newLines = [];
51
50
  for (const line of lines) {