chrome-devtools-frontend 1.0.928081 → 1.0.928589

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.
Files changed (29) hide show
  1. package/AUTHORS +1 -0
  2. package/config/gni/devtools_grd_files.gni +22 -0
  3. package/front_end/core/common/Settings.ts +26 -45
  4. package/front_end/core/host/UserMetrics.ts +2 -1
  5. package/front_end/core/i18n/locales/en-US.json +18 -0
  6. package/front_end/core/i18n/locales/en-XL.json +18 -0
  7. package/front_end/core/sdk/CookieParser.ts +2 -2
  8. package/front_end/entrypoints/main/MainImpl.ts +7 -1
  9. package/front_end/models/issues_manager/Issue.ts +23 -0
  10. package/front_end/models/issues_manager/IssuesManager.ts +13 -6
  11. package/front_end/panels/elements/ElementsTreeElement.ts +53 -61
  12. package/front_end/panels/elements/ElementsTreeOutline.ts +0 -1
  13. package/front_end/panels/elements/components/LayoutPane.ts +5 -1
  14. package/front_end/panels/issues/IssueAggregator.ts +8 -0
  15. package/front_end/panels/issues/IssueKindView.ts +95 -0
  16. package/front_end/panels/issues/IssueView.ts +4 -0
  17. package/front_end/panels/issues/IssuesPane.ts +75 -18
  18. package/front_end/panels/issues/issuesTree.css +8 -3
  19. package/front_end/panels/lighthouse/LighthouseController.ts +3 -1
  20. package/front_end/panels/network/networkLogView.css +5 -0
  21. package/front_end/panels/sensors/LocationsSettingsTab.ts +1 -1
  22. package/front_end/panels/settings/SettingsScreen.ts +1 -0
  23. package/front_end/panels/settings/settingsScreen.css +24 -0
  24. package/front_end/ui/components/issue_counter/IssueLinkIcon.ts +1 -0
  25. package/front_end/ui/components/request_link_icon/RequestLinkIcon.ts +1 -0
  26. package/front_end/ui/legacy/ARIAUtils.ts +14 -11
  27. package/front_end/ui/legacy/UIUtils.ts +3 -1
  28. package/front_end/ui/legacy/closeButton.css +6 -0
  29. package/package.json +1 -1
package/AUTHORS CHANGED
@@ -29,6 +29,7 @@ Gautham Banasandra <gautham.bangalore@gmail.com>
29
29
  Jake Mulhern <mulherje@gmail.com>
30
30
  Jeffrey Chen <jeffreyca16@gmail.com>
31
31
  Jesper van den Ende <jespertheend@gmail.com>
32
+ Juba Borgohain <chromiumjuba@gmail.com>
32
33
  Julian Geppert <spctstr@gmail.com>
33
34
  Karntino Areros <karntino.c.areros@gmail.com>
34
35
  Krishnal Ciccolella <ciccolella.krishnal@gmail.com>
@@ -460,6 +460,20 @@ grd_files_release_sources = [
460
460
  "front_end/third_party/acorn-loose/acorn-loose.js",
461
461
  "front_end/third_party/acorn/acorn.js",
462
462
  "front_end/third_party/chromium/client-variations/client-variations.js",
463
+ "front_end/third_party/codemirror.next/chunk/codemirror.js",
464
+ "front_end/third_party/codemirror.next/chunk/cpp.js",
465
+ "front_end/third_party/codemirror.next/chunk/css.js",
466
+ "front_end/third_party/codemirror.next/chunk/html.js",
467
+ "front_end/third_party/codemirror.next/chunk/java.js",
468
+ "front_end/third_party/codemirror.next/chunk/javascript.js",
469
+ "front_end/third_party/codemirror.next/chunk/json.js",
470
+ "front_end/third_party/codemirror.next/chunk/legacy.js",
471
+ "front_end/third_party/codemirror.next/chunk/markdown.js",
472
+ "front_end/third_party/codemirror.next/chunk/php.js",
473
+ "front_end/third_party/codemirror.next/chunk/python.js",
474
+ "front_end/third_party/codemirror.next/chunk/wast.js",
475
+ "front_end/third_party/codemirror.next/chunk/xml.js",
476
+ "front_end/third_party/codemirror.next/codemirror.next.js",
463
477
  "front_end/third_party/codemirror/codemirror.js",
464
478
  "front_end/third_party/diff/diff-legacy.js",
465
479
  "front_end/third_party/diff/diff.js",
@@ -471,6 +485,7 @@ grd_files_release_sources = [
471
485
  "front_end/third_party/marked/marked.js",
472
486
  "front_end/third_party/wasmparser/wasmparser.js",
473
487
  "front_end/ui/components/adorners/adorners.js",
488
+ "front_end/ui/components/code_highlighter/code_highlighter.js",
474
489
  "front_end/ui/components/data_grid/data_grid.js",
475
490
  "front_end/ui/components/expandable_list/expandable_list.js",
476
491
  "front_end/ui/components/helpers/helpers.js",
@@ -484,6 +499,7 @@ grd_files_release_sources = [
484
499
  "front_end/ui/components/report_view/report_view.js",
485
500
  "front_end/ui/components/request_link_icon/request_link_icon.js",
486
501
  "front_end/ui/components/survey_link/survey_link.js",
502
+ "front_end/ui/components/text_editor/text_editor.js",
487
503
  "front_end/ui/components/text_prompt/text_prompt.js",
488
504
  "front_end/ui/components/tree_outline/tree_outline.js",
489
505
  "front_end/ui/legacy/components/color_picker/color_picker-legacy.js",
@@ -1003,6 +1019,7 @@ grd_files_debug_sources = [
1003
1019
  "front_end/panels/issues/GenericIssueDetailsView.js",
1004
1020
  "front_end/panels/issues/HiddenIssuesRow.js",
1005
1021
  "front_end/panels/issues/IssueAggregator.js",
1022
+ "front_end/panels/issues/IssueKindView.js",
1006
1023
  "front_end/panels/issues/IssueRevealer.js",
1007
1024
  "front_end/panels/issues/IssueView.js",
1008
1025
  "front_end/panels/issues/IssuesPane.js",
@@ -1333,6 +1350,8 @@ grd_files_debug_sources = [
1333
1350
  "front_end/third_party/wasmparser/package/dist/esm/WasmParser.js",
1334
1351
  "front_end/ui/components/adorners/Adorner.js",
1335
1352
  "front_end/ui/components/adorners/adorner.css.js",
1353
+ "front_end/ui/components/code_highlighter/CodeHighlighter.js",
1354
+ "front_end/ui/components/code_highlighter/codeHighlighter.css.js",
1336
1355
  "front_end/ui/components/data_grid/DataGrid.js",
1337
1356
  "front_end/ui/components/data_grid/DataGridContextMenuUtils.js",
1338
1357
  "front_end/ui/components/data_grid/DataGridController.js",
@@ -1397,6 +1416,9 @@ grd_files_debug_sources = [
1397
1416
  "front_end/ui/components/request_link_icon/requestLinkIcon.css.js",
1398
1417
  "front_end/ui/components/survey_link/SurveyLink.js",
1399
1418
  "front_end/ui/components/survey_link/surveyLink.css.js",
1419
+ "front_end/ui/components/text_editor/TextEditor.js",
1420
+ "front_end/ui/components/text_editor/config.js",
1421
+ "front_end/ui/components/text_editor/theme.js",
1400
1422
  "front_end/ui/components/text_prompt/TextPrompt.js",
1401
1423
  "front_end/ui/components/text_prompt/textPrompt.css.js",
1402
1424
  "front_end/ui/components/tree_outline/TreeOutline.js",
@@ -41,8 +41,6 @@ import {getLocalizedSettingsCategory, getRegisteredSettings, maybeRemoveSettingE
41
41
  let settingsInstance: Settings|undefined;
42
42
 
43
43
  export class Settings {
44
- readonly globalStorage: SettingsStorage;
45
- private readonly localStorage: SettingsStorage;
46
44
  private readonly sessionStorage: SettingsStorage;
47
45
  settingNameSet: Set<string>;
48
46
  orderValuesBySettingCategory: Map<SettingCategory, Set<number>>;
@@ -50,9 +48,9 @@ export class Settings {
50
48
  private registry: Map<string, Setting<unknown>>;
51
49
  readonly moduleSettings: Map<string, Setting<unknown>>;
52
50
 
53
- private constructor(globalStorage: SettingsStorage, localStorage: SettingsStorage) {
54
- this.globalStorage = globalStorage;
55
- this.localStorage = localStorage;
51
+ private constructor(
52
+ private readonly syncedStorage: SettingsStorage, readonly globalStorage: SettingsStorage,
53
+ private readonly localStorage: SettingsStorage) {
56
54
  this.sessionStorage = new SettingsStorage({});
57
55
 
58
56
  this.settingNameSet = new Set();
@@ -91,16 +89,17 @@ export class Settings {
91
89
 
92
90
  static instance(opts: {
93
91
  forceNew: boolean|null,
92
+ syncedStorage: SettingsStorage|null,
94
93
  globalStorage: SettingsStorage|null,
95
94
  localStorage: SettingsStorage|null,
96
- } = {forceNew: null, globalStorage: null, localStorage: null}): Settings {
97
- const {forceNew, globalStorage, localStorage} = opts;
95
+ } = {forceNew: null, syncedStorage: null, globalStorage: null, localStorage: null}): Settings {
96
+ const {forceNew, syncedStorage, globalStorage, localStorage} = opts;
98
97
  if (!settingsInstance || forceNew) {
99
- if (!globalStorage || !localStorage) {
98
+ if (!syncedStorage || !globalStorage || !localStorage) {
100
99
  throw new Error(`Unable to create settings: global and local storage must be provided: ${new Error().stack}`);
101
100
  }
102
101
 
103
- settingsInstance = new Settings(globalStorage, localStorage);
102
+ settingsInstance = new Settings(syncedStorage, globalStorage, localStorage);
104
103
  }
105
104
 
106
105
  return settingsInstance;
@@ -270,29 +269,22 @@ function removeSetting(setting: Setting<unknown>): void {
270
269
  settings.getRegistry().delete(name);
271
270
  settings.moduleSettings.delete(name);
272
271
 
273
- setting.getStorage().remove(name);
272
+ setting.storage.remove(name);
274
273
  }
275
274
 
276
275
  export class Setting<V> {
277
- private nameInternal: string;
278
- private defaultValueInternal: V;
279
- private readonly eventSupport: ObjectWrapper<GenericEvents>;
280
- private storage: SettingsStorage;
281
276
  private titleFunction!: () => Platform.UIString.LocalizedString;
282
277
  private titleInternal!: string;
283
- private registration: SettingRegistration|null;
278
+ private registration: SettingRegistration|null = null;
284
279
  private requiresUserAction?: boolean;
285
280
  private value?: V;
286
281
  // TODO(crbug.com/1172300) Type cannot be inferred without changes to consumers. See above.
287
282
  private serializer: Serializer<unknown, V> = JSON;
288
283
  private hadUserAction?: boolean;
289
284
 
290
- constructor(name: string, defaultValue: V, eventSupport: ObjectWrapper<GenericEvents>, storage: SettingsStorage) {
291
- this.nameInternal = name;
292
- this.defaultValueInternal = defaultValue;
293
- this.eventSupport = eventSupport;
294
- this.storage = storage;
295
- this.registration = null;
285
+ constructor(
286
+ readonly name: string, readonly defaultValue: V, private readonly eventSupport: ObjectWrapper<GenericEvents>,
287
+ readonly storage: SettingsStorage) {
296
288
  }
297
289
 
298
290
  setSerializer(serializer: Serializer<unknown, V>): void {
@@ -300,15 +292,11 @@ export class Setting<V> {
300
292
  }
301
293
 
302
294
  addChangeListener(listener: (arg0: EventTargetEvent<V>) => void, thisObject?: Object): EventDescriptor {
303
- return this.eventSupport.addEventListener(this.nameInternal, listener, thisObject);
295
+ return this.eventSupport.addEventListener(this.name, listener, thisObject);
304
296
  }
305
297
 
306
298
  removeChangeListener(listener: (arg0: EventTargetEvent<V>) => void, thisObject?: Object): void {
307
- this.eventSupport.removeEventListener(this.nameInternal, listener, thisObject);
308
- }
309
-
310
- get name(): string {
311
- return this.nameInternal;
299
+ this.eventSupport.removeEventListener(this.name, listener, thisObject);
312
300
  }
313
301
 
314
302
  title(): string {
@@ -337,19 +325,19 @@ export class Setting<V> {
337
325
 
338
326
  get(): V {
339
327
  if (this.requiresUserAction && !this.hadUserAction) {
340
- return this.defaultValueInternal;
328
+ return this.defaultValue;
341
329
  }
342
330
 
343
331
  if (typeof this.value !== 'undefined') {
344
332
  return this.value;
345
333
  }
346
334
 
347
- this.value = this.defaultValueInternal;
348
- if (this.storage.has(this.nameInternal)) {
335
+ this.value = this.defaultValue;
336
+ if (this.storage.has(this.name)) {
349
337
  try {
350
- this.value = this.serializer.parse(this.storage.get(this.nameInternal));
338
+ this.value = this.serializer.parse(this.storage.get(this.name));
351
339
  } catch (e) {
352
- this.storage.remove(this.nameInternal);
340
+ this.storage.remove(this.name);
353
341
  }
354
342
  }
355
343
  return this.value;
@@ -361,14 +349,14 @@ export class Setting<V> {
361
349
  try {
362
350
  const settingString = this.serializer.stringify(value);
363
351
  try {
364
- this.storage.set(this.nameInternal, settingString);
352
+ this.storage.set(this.name, settingString);
365
353
  } catch (e) {
366
- this.printSettingsSavingError(e.message, this.nameInternal, settingString);
354
+ this.printSettingsSavingError(e.message, this.name, settingString);
367
355
  }
368
356
  } catch (e) {
369
- Console.instance().error('Cannot stringify setting with name: ' + this.nameInternal + ', error: ' + e.message);
357
+ Console.instance().error('Cannot stringify setting with name: ' + this.name + ', error: ' + e.message);
370
358
  }
371
- this.eventSupport.dispatchEventToListeners(this.nameInternal, value);
359
+ this.eventSupport.dispatchEventToListeners(this.name, value);
372
360
  }
373
361
 
374
362
  setRegistration(registration: SettingRegistration): void {
@@ -427,19 +415,12 @@ export class Setting<V> {
427
415
  }
428
416
 
429
417
  private printSettingsSavingError(message: string, name: string, value: string): void {
430
- const errorMessage = 'Error saving setting with name: ' + this.nameInternal + ', value length: ' + value.length +
431
- '. Error: ' + message;
418
+ const errorMessage =
419
+ 'Error saving setting with name: ' + this.name + ', value length: ' + value.length + '. Error: ' + message;
432
420
  console.error(errorMessage);
433
421
  Console.instance().error(errorMessage);
434
422
  this.storage.dumpSizes();
435
423
  }
436
- defaultValue(): V {
437
- return this.defaultValueInternal;
438
- }
439
-
440
- getStorage(): SettingsStorage {
441
- return this.storage;
442
- }
443
424
  }
444
425
 
445
426
  // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
@@ -576,7 +576,8 @@ export const DevtoolsExperiments: {
576
576
  'hideIssuesFeature': 48,
577
577
  'reportingApiDebugging': 49,
578
578
  'syncSettings': 50,
579
- '__lastValidEnumPosition': 50,
579
+ 'groupAndHideIssuesByKind': 51,
580
+ '__lastValidEnumPosition': 51,
580
581
  };
581
582
 
582
583
  export const IssueExpanded: {
@@ -1343,12 +1343,21 @@
1343
1343
  "models/issues_manager/Issue.ts | breakingChangeIssue": {
1344
1344
  "message": "A breaking change issue: the page may stop working in an upcoming version of Chrome"
1345
1345
  },
1346
+ "models/issues_manager/Issue.ts | breakingChanges": {
1347
+ "message": "Breaking Changes"
1348
+ },
1346
1349
  "models/issues_manager/Issue.ts | improvementIssue": {
1347
1350
  "message": "An improvement issue: there is an opportunity to improve the page"
1348
1351
  },
1352
+ "models/issues_manager/Issue.ts | improvements": {
1353
+ "message": "Improvements"
1354
+ },
1349
1355
  "models/issues_manager/Issue.ts | pageErrorIssue": {
1350
1356
  "message": "A page error issue: the page is not working correctly"
1351
1357
  },
1358
+ "models/issues_manager/Issue.ts | pageErrors": {
1359
+ "message": "Page Errors"
1360
+ },
1352
1361
  "models/issues_manager/LowTextContrastIssue.ts | colorAndContrastAccessibility": {
1353
1362
  "message": "Color and contrast accessibility"
1354
1363
  },
@@ -3983,6 +3992,9 @@
3983
3992
  "panels/elements/components/ElementsBreadcrumbsUtils.ts | text": {
3984
3993
  "message": "(text)"
3985
3994
  },
3995
+ "panels/elements/components/LayoutPane.ts | chooseElementOverlayColor": {
3996
+ "message": "Choose the overlay color for this element"
3997
+ },
3986
3998
  "panels/elements/components/LayoutPane.ts | flexbox": {
3987
3999
  "message": "Flexbox"
3988
4000
  },
@@ -5015,9 +5027,15 @@
5015
5027
  "panels/issues/IssuesPane.ts | groupByCategory": {
5016
5028
  "message": "Group by category"
5017
5029
  },
5030
+ "panels/issues/IssuesPane.ts | groupByKind": {
5031
+ "message": "Group by kind"
5032
+ },
5018
5033
  "panels/issues/IssuesPane.ts | groupDisplayedIssuesUnder": {
5019
5034
  "message": "Group displayed issues under associated categories"
5020
5035
  },
5036
+ "panels/issues/IssuesPane.ts | groupDisplayedIssuesUnderKind": {
5037
+ "message": "Group displayed issues as Page errors, Breaking changes and Improvements"
5038
+ },
5021
5039
  "panels/issues/IssuesPane.ts | heavyAds": {
5022
5040
  "message": "Heavy Ads"
5023
5041
  },
@@ -1343,12 +1343,21 @@
1343
1343
  "models/issues_manager/Issue.ts | breakingChangeIssue": {
1344
1344
  "message": "Â b́r̂éâḱîńĝ ćĥán̂ǵê íŝśûé: t̂h́ê ṕâǵê ḿâý ŝt́ôṕ ŵór̂ḱîńĝ ín̂ án̂ úp̂ćôḿîńĝ v́êŕŝíôń ôf́ Ĉh́r̂óm̂é"
1345
1345
  },
1346
+ "models/issues_manager/Issue.ts | breakingChanges": {
1347
+ "message": "B̂ŕêák̂ín̂ǵ Ĉh́âńĝéŝ"
1348
+ },
1346
1349
  "models/issues_manager/Issue.ts | improvementIssue": {
1347
1350
  "message": "Âń îḿp̂ŕôv́êḿêńt̂ íŝśûé: t̂h́êŕê íŝ án̂ óp̂ṕôŕt̂ún̂ít̂ý t̂ó îḿp̂ŕôv́ê t́ĥé p̂áĝé"
1348
1351
  },
1352
+ "models/issues_manager/Issue.ts | improvements": {
1353
+ "message": "Îḿp̂ŕôv́êḿêńt̂ś"
1354
+ },
1349
1355
  "models/issues_manager/Issue.ts | pageErrorIssue": {
1350
1356
  "message": "Â ṕâǵê ér̂ŕôŕ îśŝúê: t́ĥé p̂áĝé îś n̂ót̂ ẃôŕk̂ín̂ǵ ĉór̂ŕêćt̂ĺŷ"
1351
1357
  },
1358
+ "models/issues_manager/Issue.ts | pageErrors": {
1359
+ "message": "P̂áĝé Êŕr̂ór̂ś"
1360
+ },
1352
1361
  "models/issues_manager/LowTextContrastIssue.ts | colorAndContrastAccessibility": {
1353
1362
  "message": "Ĉól̂ór̂ án̂d́ ĉón̂t́r̂áŝt́ âćĉéŝśîb́îĺît́ŷ"
1354
1363
  },
@@ -3983,6 +3992,9 @@
3983
3992
  "panels/elements/components/ElementsBreadcrumbsUtils.ts | text": {
3984
3993
  "message": "(t̂éx̂t́)"
3985
3994
  },
3995
+ "panels/elements/components/LayoutPane.ts | chooseElementOverlayColor": {
3996
+ "message": "Ĉh́ôóŝé t̂h́ê óv̂ér̂ĺâý ĉól̂ór̂ f́ôŕ t̂h́îś êĺêḿêńt̂"
3997
+ },
3986
3998
  "panels/elements/components/LayoutPane.ts | flexbox": {
3987
3999
  "message": "F̂ĺêx́b̂óx̂"
3988
4000
  },
@@ -5015,9 +5027,15 @@
5015
5027
  "panels/issues/IssuesPane.ts | groupByCategory": {
5016
5028
  "message": "Ĝŕôúp̂ b́ŷ ćât́êǵôŕŷ"
5017
5029
  },
5030
+ "panels/issues/IssuesPane.ts | groupByKind": {
5031
+ "message": "Ĝŕôúp̂ b́ŷ ḱîńd̂"
5032
+ },
5018
5033
  "panels/issues/IssuesPane.ts | groupDisplayedIssuesUnder": {
5019
5034
  "message": "Ĝŕôúp̂ d́îśp̂ĺâýêd́ îśŝúêś ûńd̂ér̂ áŝśôćîát̂éd̂ ćât́êǵôŕîéŝ"
5020
5035
  },
5036
+ "panels/issues/IssuesPane.ts | groupDisplayedIssuesUnderKind": {
5037
+ "message": "Ĝŕôúp̂ d́îśp̂ĺâýêd́ îśŝúêś âś P̂áĝé êŕr̂ór̂ś, B̂ŕêák̂ín̂ǵ ĉh́âńĝéŝ án̂d́ Îḿp̂ŕôv́êḿêńt̂ś"
5038
+ },
5021
5039
  "panels/issues/IssuesPane.ts | heavyAds": {
5022
5040
  "message": "Ĥéâv́ŷ Ád̂ś"
5023
5041
  },
@@ -117,14 +117,14 @@ export class CookieParser {
117
117
  // and http://crbug.com/12361). The logic below matches latest versions of IE, Firefox,
118
118
  // Chrome and Safari on some old platforms. The latest version of Safari supports quoted
119
119
  // cookie values, though.
120
- const keyValueMatch = /^[ \t]*([^\s=;]+)[ \t]*(?:=[ \t]*([^;\n]*))?/.exec(this.#input);
120
+ const keyValueMatch = /^[ \t]*([^=;]+)[ \t]*(?:=[ \t]*([^;\n]*))?/.exec(this.#input);
121
121
  if (!keyValueMatch) {
122
122
  console.error('Failed parsing cookie header before: ' + this.#input);
123
123
  return null;
124
124
  }
125
125
 
126
126
  const result = new KeyValue(
127
- keyValueMatch[1], keyValueMatch[2] && keyValueMatch[2].trim(),
127
+ keyValueMatch[1] && keyValueMatch[1].trim(), keyValueMatch[2] && keyValueMatch[2].trim(),
128
128
  (this.#originalInputLength as number) - this.#input.length);
129
129
  this.#lastCookieLine += keyValueMatch[0];
130
130
  this.#input = this.#input.slice(keyValueMatch[0].length);
@@ -215,7 +215,7 @@ export class MainImpl {
215
215
  clear: Host.InspectorFrontendHost.InspectorFrontendHostInstance.clearPreferences,
216
216
  };
217
217
  const globalStorage = new Common.Settings.SettingsStorage(prefs, hostStorage, storagePrefix);
218
- Common.Settings.Settings.instance({forceNew: true, globalStorage, localStorage});
218
+ Common.Settings.Settings.instance({forceNew: true, syncedStorage: globalStorage, globalStorage, localStorage});
219
219
 
220
220
  // @ts-ignore layout test global
221
221
  self.Common.settings = Common.Settings.Settings.instance();
@@ -316,6 +316,9 @@ export class MainImpl {
316
316
  'hideIssuesFeature', 'Enable experimental hide issues menu', undefined,
317
317
  'https://developer.chrome.com/blog/new-in-devtools-94/#hide-issues');
318
318
 
319
+ // Hide Issues Feature.
320
+ Root.Runtime.experiments.register('groupAndHideIssuesByKind', 'Allow grouping and hiding of issues by IssueKind');
321
+
319
322
  // Localized DevTools, hide "locale selector" setting behind an experiment.
320
323
  Root.Runtime.experiments.register(Root.Runtime.ExperimentName.LOCALIZED_DEVTOOLS, 'Enable localized DevTools');
321
324
 
@@ -526,6 +529,9 @@ export class MainImpl {
526
529
  Timeline.TimelinePanel.LoadTimelineHandler.instance().handleQueryParam(value);
527
530
  }
528
531
 
532
+ // Initialize ARIAUtils.alert Element
533
+ UI.ARIAUtils.alertElementInstance();
534
+
529
535
  // Allow UI cycles to repaint prior to creating connection.
530
536
  setTimeout(this.initializeTarget.bind(this), 0);
531
537
  MainImpl.timeEnd('Main._showAppUI');
@@ -11,6 +11,18 @@ import type * as Protocol from '../../generated/protocol.js';
11
11
  import type {MarkdownIssueDescription} from './MarkdownIssueDescription.js';
12
12
 
13
13
  const UIStrings = {
14
+ /**
15
+ *@description The kind of an issue (plural) (Issues are categorized into kinds).
16
+ */
17
+ improvements: 'Improvements',
18
+ /**
19
+ *@description The kind of an issue (plural) (Issues are categorized into kinds).
20
+ */
21
+ pageErrors: 'Page Errors',
22
+ /**
23
+ *@description The kind of an issue (plural) (Issues are categorized into kinds).
24
+ */
25
+ breakingChanges: 'Breaking Changes',
14
26
  /**
15
27
  *@description A description for a kind of issue we display in the issues tab.
16
28
  */
@@ -64,6 +76,17 @@ export enum IssueKind {
64
76
  Improvement = 'Improvement',
65
77
  }
66
78
 
79
+ export function getIssueKindName(issueKind: IssueKind): Common.UIString.LocalizedString {
80
+ switch (issueKind) {
81
+ case IssueKind.BreakingChange:
82
+ return i18nString(UIStrings.breakingChanges);
83
+ case IssueKind.Improvement:
84
+ return i18nString(UIStrings.improvements);
85
+ case IssueKind.PageError:
86
+ return i18nString(UIStrings.pageErrors);
87
+ }
88
+ }
89
+
67
90
  export function getIssueKindDescription(issueKind: IssueKind): Common.UIString.LocalizedString {
68
91
  switch (issueKind) {
69
92
  case IssueKind.PageError:
@@ -159,7 +159,7 @@ export class IssuesManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes
159
159
  private allIssues = new Map<string, Issue>();
160
160
  private filteredIssues = new Map<string, Issue>();
161
161
  private issueCounts = new Map<IssueKind, number>();
162
- private hiddenIssueCount: number = 0;
162
+ private hiddenIssueCount = new Map<IssueKind, number>();
163
163
  private hasSeenTopFrameNavigated = false;
164
164
  private sourceFrameIssuesManager = new SourceFrameIssuesManager(this);
165
165
  private issuesById: Map<string, Issue> = new Map();
@@ -278,7 +278,7 @@ export class IssuesManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes
278
278
  this.updateIssueHiddenStatus(issue, values);
279
279
  }
280
280
  if (issue.isHidden()) {
281
- this.hiddenIssueCount++;
281
+ this.hiddenIssueCount.set(issue.getKind(), 1 + (this.hiddenIssueCount.get(issue.getKind()) || 0));
282
282
  }
283
283
  this.dispatchEventToListeners(Events.IssueAdded, {issuesModel, issue});
284
284
  }
@@ -298,8 +298,15 @@ export class IssuesManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes
298
298
  return this.filteredIssues.size;
299
299
  }
300
300
 
301
- numberOfHiddenIssues(): number {
302
- return this.hiddenIssueCount;
301
+ numberOfHiddenIssues(kind?: IssueKind): number {
302
+ if (kind) {
303
+ return this.hiddenIssueCount.get(kind) ?? 0;
304
+ }
305
+ let count = 0;
306
+ for (const num of this.hiddenIssueCount.values()) {
307
+ count += num;
308
+ }
309
+ return count;
303
310
  }
304
311
 
305
312
  numberOfAllStoredIssues(): number {
@@ -332,7 +339,7 @@ export class IssuesManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes
332
339
  this.filteredIssues.clear();
333
340
  this.issueCounts.clear();
334
341
  this.issuesById.clear();
335
- this.hiddenIssueCount = 0;
342
+ this.hiddenIssueCount.clear();
336
343
  const values = this.hideIssueSetting?.get();
337
344
  const hideIssuesFeature = Root.Runtime.experiments.isEnabled('hideIssuesFeature');
338
345
  for (const [key, issue] of this.allIssues) {
@@ -343,7 +350,7 @@ export class IssuesManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes
343
350
  this.filteredIssues.set(key, issue);
344
351
  this.issueCounts.set(issue.getKind(), 1 + (this.issueCounts.get(issue.getKind()) ?? 0));
345
352
  if (issue.isHidden()) {
346
- this.hiddenIssueCount++;
353
+ this.hiddenIssueCount.set(issue.getKind(), 1 + (this.hiddenIssueCount.get(issue.getKind()) || 0));
347
354
  }
348
355
  const issueId = issue.getIssueId();
349
356
  if (issueId) {
@@ -39,13 +39,16 @@ import * as Platform from '../../core/platform/platform.js';
39
39
  import * as SDK from '../../core/sdk/sdk.js';
40
40
  import * as TextUtils from '../../models/text_utils/text_utils.js';
41
41
  import * as Adorners from '../../ui/components/adorners/adorners.js';
42
- import * as TextEditor from '../../ui/legacy/components/text_editor/text_editor.js';
42
+ import type * as TextEditor from '../../ui/components/text_editor/text_editor.js';
43
+ import * as TextEditorLegacy from '../../ui/legacy/components/text_editor/text_editor.js';
43
44
  import * as Components from '../../ui/legacy/components/utils/utils.js';
44
45
  import * as UI from '../../ui/legacy/legacy.js';
45
46
  import * as Emulation from '../emulation/emulation.js';
47
+
46
48
  import * as ElementsComponents from './components/components.js';
47
49
  import {canGetJSPath, cssPath, jsPath, xPath} from './DOMPath.js';
48
50
  import {ElementsPanel} from './ElementsPanel.js';
51
+
49
52
  import type {ElementsTreeOutline, UpdateRecord} from './ElementsTreeOutline.js';
50
53
  import {MappedCharToEntity} from './ElementsTreeOutline.js';
51
54
  import {ImagePreviewPopover} from './ImagePreviewPopover.js';
@@ -986,9 +989,9 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
986
989
  }
987
990
  }
988
991
 
989
- private startEditingAsHTML(
992
+ private async startEditingAsHTML(
990
993
  commitCallback: (arg0: string, arg1: string) => void, disposeCallback: () => void,
991
- maybeInitialValue: string|null): void {
994
+ maybeInitialValue: string|null): Promise<void> {
992
995
  if (maybeInitialValue === null) {
993
996
  return;
994
997
  }
@@ -1013,56 +1016,62 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1013
1016
  // Append editor.
1014
1017
  this.listItemElement.appendChild(this.htmlEditElement);
1015
1018
 
1016
- const factory = TextEditor.CodeMirrorTextEditor.CodeMirrorTextEditorFactory.instance();
1017
- const editor = factory.createEditor({
1018
- lineNumbers: false,
1019
- lineWrapping: Common.Settings.Settings.instance().moduleSetting('domWordWrap').get(),
1020
- mimeType: 'text/html',
1021
- autoHeight: false,
1022
- padBottom: false,
1023
- bracketMatchingSetting: undefined,
1024
- devtoolsAccessibleName: undefined,
1025
- maxHighlightLength: undefined,
1026
- placeholder: undefined,
1027
- lineWiseCopyCut: undefined,
1028
- inputStyle: undefined,
1029
- });
1019
+ const TextEditor = await import('../../ui/components/text_editor/text_editor.js');
1020
+ const CodeMirror = await import('../../third_party/codemirror.next/codemirror.next.js');
1021
+ const {html} = await CodeMirror.html();
1022
+ const editor = new TextEditor.TextEditor.TextEditor(CodeMirror.EditorState.create({
1023
+ doc: initialValue,
1024
+ extensions: [
1025
+ CodeMirror.keymap.of([
1026
+ {
1027
+ key: 'Mod-Enter',
1028
+ run: (): boolean => {
1029
+ this.editing?.commit();
1030
+ return true;
1031
+ },
1032
+ },
1033
+ {
1034
+ key: 'Escape',
1035
+ run: (): boolean => {
1036
+ this.editing?.cancel();
1037
+ return true;
1038
+ },
1039
+ },
1040
+ ]),
1041
+ TextEditor.Config.baseConfiguration(initialValue),
1042
+ html(),
1043
+ TextEditor.Config.domWordWrap,
1044
+ CodeMirror.EditorView.theme({
1045
+ '.cm-editor': {maxHeight: '300px'},
1046
+ '.cm-scroller': {overflowY: 'auto'},
1047
+ }),
1048
+ CodeMirror.EditorView.domEventHandlers({
1049
+ focusout: event => {
1050
+ // The relatedTarget is null when no element gains focus, e.g. switching windows.
1051
+ const relatedTarget = (event.relatedTarget as Node | null);
1052
+ if (relatedTarget && !relatedTarget.isSelfOrDescendant(editor)) {
1053
+ this.editing && this.editing.commit();
1054
+ }
1055
+ },
1056
+ }),
1057
+ ],
1058
+ }));
1030
1059
  this.editing = {commit: commit.bind(this), cancel: dispose.bind(this), editor, resize: resize.bind(this)};
1031
1060
  resize.call(this);
1032
- editor.widget().show((this.htmlEditElement as HTMLElement));
1033
- editor.setText(initialValue);
1034
- editor.widget().focus();
1035
- editor.widget().element.addEventListener('focusout', event => {
1036
- // The relatedTarget is null when no element gains focus, e.g. switching windows.
1037
- const relatedTarget = (event.relatedTarget as Node | null);
1038
- if (relatedTarget && !relatedTarget.isSelfOrDescendant(editor.widget().element)) {
1039
- this.editing && this.editing.commit();
1040
- }
1041
- }, false);
1042
- editor.widget().element.addEventListener('keydown', keydown.bind(this), true);
1061
+ this.htmlEditElement.appendChild(editor);
1062
+ editor.editor.focus();
1043
1063
 
1044
- this.treeOutline && this.treeOutline.setMultilineEditing((this.editing as {
1045
- commit: () => void,
1046
- cancel: () => void,
1047
- editor: UI.TextEditor.TextEditor,
1048
- // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
1049
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1050
- resize: () => any,
1051
- }));
1064
+ this.treeOutline && this.treeOutline.setMultilineEditing(this.editing);
1052
1065
 
1053
1066
  function resize(this: ElementsTreeElement): void {
1054
1067
  if (this.treeOutline && this.htmlEditElement) {
1055
1068
  this.htmlEditElement.style.width = this.treeOutline.visibleWidth() - this.computeLeftIndent() - 30 + 'px';
1056
1069
  }
1057
-
1058
- if (this.editing && this.editing.editor) {
1059
- (this.editing.editor as TextEditor.CodeMirrorTextEditor.CodeMirrorTextEditor).onResize();
1060
- }
1061
1070
  }
1062
1071
 
1063
1072
  function commit(this: ElementsTreeElement): void {
1064
1073
  if (this.editing && this.editing.editor) {
1065
- commitCallback(initialValue, this.editing.editor.text());
1074
+ commitCallback(initialValue, this.editing.editor.state.doc.toString());
1066
1075
  }
1067
1076
  dispose.call(this);
1068
1077
  }
@@ -1071,8 +1080,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1071
1080
  if (!this.editing || !this.editing.editor) {
1072
1081
  return;
1073
1082
  }
1074
- this.editing.editor.widget().element.removeEventListener('blur', this.editing.commit, true);
1075
- this.editing.editor.widget().detach();
1076
1083
  this.editing = null;
1077
1084
 
1078
1085
  // Remove editor.
@@ -1098,21 +1105,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1098
1105
 
1099
1106
  disposeCallback();
1100
1107
  }
1101
-
1102
- function keydown(this: ElementsTreeElement, event: Event): void {
1103
- const keyboardEvent = (event as KeyboardEvent);
1104
- const isMetaOrCtrl = UI.KeyboardShortcut.KeyboardShortcut.eventHasCtrlEquivalentKey(keyboardEvent) &&
1105
- !keyboardEvent.altKey && !keyboardEvent.shiftKey;
1106
- if (keyboardEvent.key === 'Enter' && (isMetaOrCtrl || keyboardEvent.isMetaOrCtrlForTest)) {
1107
- keyboardEvent.consume(true);
1108
- this.editing && this.editing.commit();
1109
- } else if (
1110
- keyboardEvent.keyCode === UI.KeyboardShortcut.Keys.Esc.code ||
1111
- keyboardEvent.key === Platform.KeyboardUtilities.ESCAPE_KEY) {
1112
- keyboardEvent.consume(true);
1113
- this.editing && this.editing.cancel();
1114
- }
1115
- }
1116
1108
  }
1117
1109
 
1118
1110
  private attributeEditingCommitted(
@@ -1732,14 +1724,14 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1732
1724
  newNode.textContent = text.startsWith('\n') ? text.substring(1) : text;
1733
1725
 
1734
1726
  const javascriptSyntaxHighlighter =
1735
- new TextEditor.SyntaxHighlighter.SyntaxHighlighter('text/javascript', true);
1727
+ new TextEditorLegacy.SyntaxHighlighter.SyntaxHighlighter('text/javascript', true);
1736
1728
  javascriptSyntaxHighlighter.syntaxHighlightNode(newNode).then(updateSearchHighlight);
1737
1729
  } else if (node.parentNode && node.parentNode.nodeName().toLowerCase() === 'style') {
1738
1730
  const newNode = titleDOM.createChild('span', 'webkit-html-text-node webkit-html-css-node');
1739
1731
  const text = node.nodeValue();
1740
1732
  newNode.textContent = text.startsWith('\n') ? text.substring(1) : text;
1741
1733
 
1742
- const cssSyntaxHighlighter = new TextEditor.SyntaxHighlighter.SyntaxHighlighter('text/css', true);
1734
+ const cssSyntaxHighlighter = new TextEditorLegacy.SyntaxHighlighter.SyntaxHighlighter('text/css', true);
1743
1735
  cssSyntaxHighlighter.syntaxHighlightNode(newNode).then(updateSearchHighlight);
1744
1736
  } else {
1745
1737
  UI.UIUtils.createTextChild(titleDOM, '"');
@@ -2224,7 +2216,7 @@ export function adornerComparator(adornerA: Adorners.Adorner.Adorner, adornerB:
2224
2216
  export interface EditorHandles {
2225
2217
  commit: () => void;
2226
2218
  cancel: () => void;
2227
- editor?: UI.TextEditor.TextEditor|TextEditor.CodeMirrorTextEditor.CodeMirrorTextEditor;
2219
+ editor?: TextEditor.TextEditor.TextEditor;
2228
2220
  // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
2229
2221
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2230
2222
  resize: () => any;
@@ -1631,7 +1631,6 @@ export interface MultilineEditorController {
1631
1631
  cancel: () => void;
1632
1632
  commit: () => void;
1633
1633
  resize: () => void;
1634
- editor: UI.TextEditor.TextEditor;
1635
1634
  }
1636
1635
 
1637
1636
  export interface ClipboardData {
@@ -17,6 +17,10 @@ import inspectorCommonStyles from '../../../ui/legacy/inspectorCommon.css.js';
17
17
 
18
18
  import * as i18n from '../../../core/i18n/i18n.js';
19
19
  const UIStrings = {
20
+ /**
21
+ *@description Title of the input to select the overlay color for an element using the color picker
22
+ */
23
+ chooseElementOverlayColor: 'Choose the overlay color for this element',
20
24
  /**
21
25
  *@description Title of the show element button in the Layout pane of the Elements panel
22
26
  */
@@ -257,7 +261,7 @@ export class LayoutPane extends HTMLElement {
257
261
  } as NodeTextData}></${NodeText.litTagName}>
258
262
  </span>
259
263
  </label>
260
- <label @keyup=${onColorLabelKeyUp} @keydown=${onColorLabelKeyDown} tabindex="0" class="color-picker-label" style="background: ${element.color};">
264
+ <label @keyup=${onColorLabelKeyUp} @keydown=${onColorLabelKeyDown} tabindex="0" title=${i18nString(UIStrings.chooseElementOverlayColor)} class="color-picker-label" style="background: ${element.color};">
261
265
  <input @change=${onColorChange} @input=${onColorChange} class="color-picker" type="color" value=${element.color} />
262
266
  </label>
263
267
  <button tabindex="0" @click=${onElementClick} title=${i18nString(UIStrings.showElementInTheElementsPanel)} class="show-element"></button>
@@ -309,6 +309,14 @@ export class IssueAggregator extends Common.ObjectWrapper.ObjectWrapper<EventTyp
309
309
  return result;
310
310
  }
311
311
 
312
+ aggregatedIssueKinds(): Set<IssuesManager.Issue.IssueKind> {
313
+ const result = new Set<IssuesManager.Issue.IssueKind>();
314
+ for (const issue of this.aggregatedIssuesByKey.values()) {
315
+ result.add(issue.getKind());
316
+ }
317
+ return result;
318
+ }
319
+
312
320
  numberOfAggregatedIssues(): number {
313
321
  return this.aggregatedIssuesByKey.size;
314
322
  }
@@ -0,0 +1,95 @@
1
+ // Copyright 2021 The Chromium Authors. All rights reserved.
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as Common from '../../core/common/common.js';
6
+ import * as IssuesManager from '../../models/issues_manager/issues_manager.js';
7
+ import * as Adorners from '../../ui/components/adorners/adorners.js';
8
+ import * as IconButton from '../../ui/components/icon_button/icon_button.js';
9
+ import * as IssueCounter from '../../ui/components/issue_counter/issue_counter.js';
10
+ import * as UI from '../../ui/legacy/legacy.js';
11
+
12
+ export function getGroupIssuesByKindSetting(): Common.Settings.Setting<boolean> {
13
+ return Common.Settings.Settings.instance().createSetting('groupIssuesByKind', false);
14
+ }
15
+
16
+ export function issueKindViewSortPriority(a: IssueKindView, b: IssueKindView): number {
17
+ if (a.getKind() === b.getKind()) {
18
+ return 0;
19
+ }
20
+ if (a.getKind() === IssuesManager.Issue.IssueKind.PageError) {
21
+ return -1;
22
+ }
23
+ if (a.getKind() === IssuesManager.Issue.IssueKind.BreakingChange &&
24
+ b.getKind() === IssuesManager.Issue.IssueKind.Improvement) {
25
+ return -1;
26
+ }
27
+ return 1;
28
+ }
29
+
30
+ export function getClassNameFromKind(kind: IssuesManager.Issue.IssueKind): string {
31
+ switch (kind) {
32
+ case IssuesManager.Issue.IssueKind.BreakingChange:
33
+ return 'breaking-changes';
34
+ case IssuesManager.Issue.IssueKind.Improvement:
35
+ return 'improvements';
36
+ case IssuesManager.Issue.IssueKind.PageError:
37
+ return 'page-errors';
38
+ }
39
+ }
40
+
41
+ export class IssueKindView extends UI.TreeOutline.TreeElement {
42
+ private kind: IssuesManager.Issue.IssueKind;
43
+ private issueCount: HTMLElement;
44
+
45
+ constructor(kind: IssuesManager.Issue.IssueKind) {
46
+ super(undefined, true);
47
+ this.kind = kind;
48
+ this.issueCount = document.createElement('span');
49
+
50
+ this.toggleOnClick = true;
51
+ this.listItemElement.classList.add('issue-kind');
52
+ this.listItemElement.classList.add(getClassNameFromKind(kind));
53
+ this.childrenListElement.classList.add('issue-kind-body');
54
+ }
55
+
56
+ getKind(): IssuesManager.Issue.IssueKind {
57
+ return this.kind;
58
+ }
59
+
60
+ private appendHeader(): void {
61
+ const header = document.createElement('div');
62
+ header.classList.add('header');
63
+
64
+ const issueKindIcon = new IconButton.Icon.Icon();
65
+ issueKindIcon.data = IssueCounter.IssueCounter.getIssueKindIconData(this.kind);
66
+ issueKindIcon.classList.add('leading-issue-icon');
67
+
68
+ const countAdorner = new Adorners.Adorner.Adorner();
69
+ countAdorner.data = {
70
+ name: 'countWrapper',
71
+ content: this.issueCount,
72
+ };
73
+ countAdorner.classList.add('aggregated-issues-count');
74
+ this.issueCount.textContent = '0';
75
+
76
+ const title = document.createElement('div');
77
+ title.classList.add('title');
78
+ title.textContent = IssuesManager.Issue.getIssueKindName(this.kind);
79
+
80
+ header.appendChild(issueKindIcon);
81
+ header.appendChild(countAdorner);
82
+ header.appendChild(title);
83
+
84
+ this.listItemElement.appendChild(header);
85
+ }
86
+
87
+ onattach(): void {
88
+ this.appendHeader();
89
+ this.expand();
90
+ }
91
+
92
+ update(count: number): void {
93
+ this.issueCount.textContent = `${count}`;
94
+ }
95
+ }
@@ -444,6 +444,10 @@ export class IssueView extends UI.TreeOutline.TreeElement {
444
444
  this.throttle.schedule(async () => this.doUpdate());
445
445
  }
446
446
 
447
+ getIssueKind(): IssuesManager.Issue.IssueKind {
448
+ return this.issue.getKind();
449
+ }
450
+
447
451
  isForHiddenIssue(): boolean {
448
452
  return this.issue.isHidden();
449
453
  }
@@ -4,6 +4,7 @@
4
4
 
5
5
  import * as Common from '../../core/common/common.js';
6
6
  import * as i18n from '../../core/i18n/i18n.js';
7
+ import * as Root from '../../core/root/root.js';
7
8
  import * as IssuesManager from '../../models/issues_manager/issues_manager.js';
8
9
  import * as IssueCounter from '../../ui/components/issue_counter/issue_counter.js';
9
10
  import * as UI from '../../ui/legacy/legacy.js';
@@ -15,6 +16,7 @@ import issuesTreeStyles from './issuesTree.css.js';
15
16
  import type {AggregatedIssue, AggregationKey} from './IssueAggregator.js';
16
17
  import {Events as IssueAggregatorEvents, IssueAggregator} from './IssueAggregator.js';
17
18
  import {IssueView} from './IssueView.js';
19
+ import {IssueKindView, getGroupIssuesByKindSetting, issueKindViewSortPriority} from './IssueKindView.js';
18
20
 
19
21
  const UIStrings = {
20
22
  /**
@@ -64,6 +66,14 @@ const UIStrings = {
64
66
  * @description Label for a checkbox which toggles grouping by category in the issues tab
65
67
  */
66
68
  groupByCategory: 'Group by category',
69
+ /**
70
+ * @description Title for a checkbox which toggles grouping by kind in the issues tab
71
+ */
72
+ groupDisplayedIssuesUnderKind: 'Group displayed issues as Page errors, Breaking changes and Improvements',
73
+ /**
74
+ * @description Label for a checkbox which toggles grouping by kind in the issues tab
75
+ */
76
+ groupByKind: 'Group by kind',
67
77
  /**
68
78
  * @description Title for a checkbox. Whether the issues tab should include third-party issues or not.
69
79
  */
@@ -168,6 +178,7 @@ let issuesPaneInstance: IssuesPane;
168
178
  export class IssuesPane extends UI.Widget.VBox {
169
179
  private categoryViews: Map<IssuesManager.Issue.IssueCategory, IssueCategoryView>;
170
180
  private issueViews: Map<AggregationKey, IssueView>;
181
+ private kindViews: Map<IssuesManager.Issue.IssueKind, IssueKindView>;
171
182
  private showThirdPartyCheckbox: UI.Toolbar.ToolbarSettingCheckbox|null;
172
183
  private issuesTree: UI.TreeOutline.TreeOutlineInShadow;
173
184
  private hiddenIssuesRow: HiddenIssuesRow;
@@ -182,6 +193,7 @@ export class IssuesPane extends UI.Widget.VBox {
182
193
  this.contentElement.classList.add('issues-pane');
183
194
 
184
195
  this.categoryViews = new Map();
196
+ this.kindViews = new Map();
185
197
  this.issueViews = new Map();
186
198
  this.showThirdPartyCheckbox = null;
187
199
 
@@ -237,6 +249,15 @@ export class IssuesPane extends UI.Widget.VBox {
237
249
  this.fullUpdate(true);
238
250
  });
239
251
 
252
+ const groupByKindSetting = getGroupIssuesByKindSetting();
253
+ const groupByKindSettingCheckbox = new UI.Toolbar.ToolbarSettingCheckbox(
254
+ groupByKindSetting, i18nString(UIStrings.groupDisplayedIssuesUnderKind), i18nString(UIStrings.groupByKind));
255
+ rightToolbar.appendToolbarItem(groupByKindSettingCheckbox);
256
+ groupByKindSetting.addChangeListener(() => {
257
+ this.fullUpdate(true);
258
+ });
259
+ groupByKindSettingCheckbox.setVisible(Root.Runtime.experiments.isEnabled('groupAndHideIssuesByKind'));
260
+
240
261
  const thirdPartySetting = IssuesManager.Issue.getShowThirdPartyIssuesSetting();
241
262
  this.showThirdPartyCheckbox = new UI.Toolbar.ToolbarSettingCheckbox(
242
263
  thirdPartySetting, i18nString(UIStrings.includeCookieIssuesCausedBy),
@@ -315,28 +336,45 @@ export class IssuesPane extends UI.Widget.VBox {
315
336
  }
316
337
 
317
338
  private getIssueViewParent(issue: AggregatedIssue): UI.TreeOutline.TreeOutline|UI.TreeOutline.TreeElement {
318
- if (!getGroupIssuesByCategorySetting().get()) {
319
- if (issue.isHidden()) {
320
- return this.hiddenIssuesRow;
321
- }
322
- return this.issuesTree;
339
+ const groupByKind = Root.Runtime.experiments.isEnabled('groupAndHideIssuesByKind');
340
+ if (issue.isHidden()) {
341
+ return this.hiddenIssuesRow;
323
342
  }
343
+ if (groupByKind && getGroupIssuesByKindSetting().get()) {
344
+ const kind = issue.getKind();
345
+ const view = this.kindViews.get(kind);
346
+ if (view) {
347
+ return view;
348
+ }
324
349
 
325
- const category = issue.getCategory();
326
- const view = this.categoryViews.get(category);
327
- if (view) {
328
- return view;
350
+ const newView = new IssueKindView(kind);
351
+ this.issuesTree.appendChild(newView, (a, b) => {
352
+ if (a instanceof IssueKindView && b instanceof IssueKindView) {
353
+ return issueKindViewSortPriority(a, b);
354
+ }
355
+ return 0;
356
+ });
357
+ this.kindViews.set(kind, newView);
358
+ return newView;
329
359
  }
330
-
331
- const newView = new IssueCategoryView(category);
332
- this.issuesTree.appendChild(newView, (a, b) => {
333
- if (a instanceof IssueCategoryView && b instanceof IssueCategoryView) {
334
- return a.getCategoryName().localeCompare(b.getCategoryName());
360
+ if (getGroupIssuesByCategorySetting().get()) {
361
+ const category = issue.getCategory();
362
+ const view = this.categoryViews.get(category);
363
+ if (view) {
364
+ return view;
335
365
  }
336
- return 0;
337
- });
338
- this.categoryViews.set(category, newView);
339
- return newView;
366
+
367
+ const newView = new IssueCategoryView(category);
368
+ this.issuesTree.appendChild(newView, (a, b) => {
369
+ if (a instanceof IssueCategoryView && b instanceof IssueCategoryView) {
370
+ return a.getCategoryName().localeCompare(b.getCategoryName());
371
+ }
372
+ return 0;
373
+ });
374
+ this.categoryViews.set(category, newView);
375
+ return newView;
376
+ }
377
+ return this.issuesTree;
340
378
  }
341
379
 
342
380
  private clearViews<T>(views: Map<T, UI.TreeOutline.TreeElement>, preservedSet?: Set<T>): void {
@@ -355,6 +393,7 @@ export class IssuesPane extends UI.Widget.VBox {
355
393
 
356
394
  private fullUpdate(force: boolean): void {
357
395
  this.clearViews(this.categoryViews, force ? undefined : this.aggregator.aggregatedIssueCategories());
396
+ this.clearViews(this.kindViews, force ? undefined : this.aggregator.aggregatedIssueKinds());
358
397
  this.clearViews(this.issueViews, force ? undefined : this.aggregator.aggregatedIssueCodes());
359
398
  if (this.aggregator) {
360
399
  for (const issue of this.aggregator.aggregatedIssues()) {
@@ -364,9 +403,21 @@ export class IssuesPane extends UI.Widget.VBox {
364
403
  this.updateCounts();
365
404
  }
366
405
 
406
+ private updateIssueKindViewsCount(): void {
407
+ for (const view of this.kindViews.values()) {
408
+ const count =
409
+ this.issuesManager.numberOfIssues(view.getKind()) - this.issuesManager.numberOfHiddenIssues(view.getKind());
410
+ view.update(count);
411
+ }
412
+ }
413
+
367
414
  private updateCounts(): void {
415
+ const groupByKind = Root.Runtime.experiments.isEnabled('groupAndHideIssuesByKind');
368
416
  this.showIssuesTreeOrNoIssuesDetectedMessage(
369
417
  this.issuesManager.numberOfIssues(), this.issuesManager.numberOfHiddenIssues());
418
+ if (groupByKind && getGroupIssuesByKindSetting().get()) {
419
+ this.updateIssueKindViewsCount();
420
+ }
370
421
  }
371
422
 
372
423
  private showIssuesTreeOrNoIssuesDetectedMessage(issuesCount: number, hiddenIssueCount: number): void {
@@ -397,11 +448,17 @@ export class IssuesPane extends UI.Widget.VBox {
397
448
  await this.issueViewUpdatePromise;
398
449
  const key = this.aggregator.keyForIssue(issue);
399
450
  const issueView = this.issueViews.get(key);
451
+ const groupByKind = Root.Runtime.experiments.isEnabled('groupAndHideIssuesByKind');
400
452
  if (issueView) {
401
453
  if (issueView.isForHiddenIssue()) {
402
454
  this.hiddenIssuesRow.expand();
403
455
  this.hiddenIssuesRow.reveal();
404
456
  }
457
+ if (groupByKind && getGroupIssuesByKindSetting().get() && !issueView.isForHiddenIssue()) {
458
+ const kindView = this.kindViews.get(issueView.getIssueKind());
459
+ kindView?.expand();
460
+ kindView?.reveal();
461
+ }
405
462
  issueView.expand();
406
463
  issueView.reveal();
407
464
  issueView.select(false, true);
@@ -38,6 +38,7 @@
38
38
  }
39
39
 
40
40
  .issue-category,
41
+ .issue-kind,
41
42
  .issue {
42
43
  padding: 0 8px;
43
44
  padding-left: var(--issue-indent);
@@ -48,7 +49,8 @@
48
49
  border-width: 0 0 1px;
49
50
  }
50
51
 
51
- .issue-category.hidden-issues.parent.expanded {
52
+ .issue-category.hidden-issues.parent.expanded,
53
+ .issue-kind.parent.expanded {
52
54
  border-width: 0 0 1px 0;
53
55
  background-color: var(--color-background-elevation-1);
54
56
  }
@@ -78,6 +80,7 @@ p {
78
80
  /* Override selected tree item styles for issues to avoid changing width. */
79
81
 
80
82
  .tree-outline-disclosure:not(.tree-outline-disclosure-hide-overflow) .tree-outline.hide-selection-when-blurred .issue-category.selected:focus-visible,
83
+ .tree-outline-disclosure:not(.tree-outline-disclosure-hide-overflow) .tree-outline.hide-selection-when-blurred .issue-kind.selected:focus-visible,
81
84
  .tree-outline-disclosure:not(.tree-outline-disclosure-hide-overflow) .tree-outline.hide-selection-when-blurred .issue.selected:focus-visible {
82
85
  width: auto;
83
86
  padding-right: 8px;
@@ -92,7 +95,8 @@ p {
92
95
  width: 100%;
93
96
  }
94
97
 
95
- .issue-category .header {
98
+ .issue-category .header,
99
+ .issue-kind .header {
96
100
  line-height: 24px;
97
101
  padding-left: 2px;
98
102
  }
@@ -118,7 +122,8 @@ p {
118
122
  padding-right: 8px;
119
123
  }
120
124
 
121
- .issue-category + .children {
125
+ .issue-category + .children,
126
+ .issue-kind + .children {
122
127
  --issue-indent: 24px;
123
128
 
124
129
  padding-left: 0;
@@ -288,7 +288,9 @@ export class LighthouseController extends Common.ObjectWrapper.ObjectWrapper<Eve
288
288
  },
289
289
  /* userGesture */ false, /* awaitPromise */ false);
290
290
  if ((!('exceptionDetails' in result) || !result.exceptionDetails) && 'object' in result && result.object) {
291
- inspectedURL = result.object.value;
291
+ if (result.object.value) {
292
+ inspectedURL = result.object.value;
293
+ }
292
294
  result.object.release();
293
295
  }
294
296
  } catch (err) {
@@ -401,6 +401,11 @@
401
401
  .network-status-pane > .recording-hint {
402
402
  color: canvastext;
403
403
  }
404
+
405
+ .initiator-column .devtools-link {
406
+ color: linktext;
407
+ }
408
+
404
409
  /* This is part of the large color block declared above, but should not be
405
410
  extracted out. */
406
411
  /* stylelint-disable no-descending-specificity */
@@ -116,7 +116,7 @@ export class LocationsSettingsTab extends UI.Widget.VBox implements UI.ListWidge
116
116
  this.customSetting =
117
117
  Common.Settings.Settings.instance().moduleSetting<LocationDescription[]>('emulation.locations');
118
118
  const list =
119
- this.customSetting.get().map(location => replaceLocationTitles(location, this.customSetting.defaultValue()));
119
+ this.customSetting.get().map(location => replaceLocationTitles(location, this.customSetting.defaultValue));
120
120
 
121
121
  function replaceLocationTitles(
122
122
  location: LocationDescription, defaultValues: LocationDescription[]): LocationDescription {
@@ -121,6 +121,7 @@ export class SettingsScreen extends UI.Widget.VBox implements UI.View.ViewLocati
121
121
  this.tabbedLocation = UI.ViewManager.ViewManager.instance().createTabbedLocation(
122
122
  () => SettingsScreen.revealSettingsScreen(), 'settings-view');
123
123
  const tabbedPane = this.tabbedLocation.tabbedPane();
124
+ tabbedPane.registerCSSFiles([settingsScreenStyles]);
124
125
  tabbedPane.leftToolbar().appendToolbarItem(new UI.Toolbar.ToolbarItem(settingsLabelElement));
125
126
  tabbedPane.setShrinkableTabs(false);
126
127
  tabbedPane.makeVerticalTabLayout();
@@ -187,3 +187,27 @@ fieldset {
187
187
  .settings-experiment-unstable {
188
188
  color: var(--color-text-secondary);
189
189
  }
190
+
191
+ @media (forced-colors: active) {
192
+ .settings-window-title {
193
+ color: canvastext;
194
+ }
195
+
196
+ .tabbed-pane-header-tab {
197
+ background: ButtonFace;
198
+ }
199
+
200
+ .tabbed-pane-header-tab-title {
201
+ color: canvastext;
202
+ }
203
+ }
204
+
205
+ @media (forced-colors: active) and (prefers-color-scheme: dark) {
206
+ .tabbed-pane-header-tab.selected {
207
+ background: ButtonFace;
208
+ }
209
+
210
+ .tabbed-pane-header-tab.selected .tabbed-pane-header-tab-title {
211
+ color: HighlightText;
212
+ }
213
+ }
@@ -163,6 +163,7 @@ export class IssueLinkIcon extends HTMLElement {
163
163
  // clang-format off
164
164
  return LitHtml.html`
165
165
  <span class=${LitHtml.Directives.classMap({'link': Boolean(this.issue)})}
166
+ tabindex="0"
166
167
  @click=${this.handleClick}>
167
168
  <${IconButton.Icon.Icon.litTagName} .data=${this.iconData() as IconButton.Icon.IconData}
168
169
  title=${this.getTooltip()}></${IconButton.Icon.Icon.litTagName}>
@@ -197,6 +197,7 @@ export class RequestLinkIcon extends HTMLElement {
197
197
  // clang-format off
198
198
  return LitHtml.html`
199
199
  <span class=${LitHtml.Directives.classMap({'link': Boolean(this.request)})}
200
+ tabindex="0"
200
201
  @click=${this.handleClick}>
201
202
  <${IconButton.Icon.Icon.litTagName} .data=${this.iconData() as IconButton.Icon.IconData}
202
203
  title=${this.getTooltip()}></${IconButton.Icon.Icon.litTagName}>
@@ -462,23 +462,26 @@ function hideFromLayout(element: HTMLElement): void {
462
462
 
463
463
  let alertElement: HTMLElement|undefined;
464
464
 
465
- function createAriaAlertElement(): HTMLElement {
466
- const element = document.body.createChild('div') as HTMLElement;
467
- hideFromLayout(element);
468
- element.setAttribute('role', 'alert');
469
- element.setAttribute('aria-atomic', 'true');
470
- return element;
465
+ export function alertElementInstance(): HTMLElement {
466
+ if (!alertElement) {
467
+ const element = document.body.createChild('div') as HTMLElement;
468
+ hideFromLayout(element);
469
+ element.setAttribute('role', 'alert');
470
+ element.setAttribute('aria-atomic', 'true');
471
+ alertElement = element;
472
+ }
473
+ return alertElement;
471
474
  }
475
+
472
476
  /**
473
477
  * This function is used to announce a message with the screen reader.
474
478
  * Setting the textContent would allow the SR to access the offscreen element via browse mode
475
479
  */
476
480
  export function alert(message: string): void {
477
- if (!alertElement) {
478
- alertElement = createAriaAlertElement();
479
- }
481
+ const element = alertElementInstance();
482
+
480
483
  // We first set the textContent to blank so that the string will announce even if it is replaced
481
484
  // with the same string.
482
- alertElement.textContent = '';
483
- alertElement.textContent = Platform.StringUtilities.trimEndWithMaxLength(message, 10000);
485
+ element.textContent = '';
486
+ element.textContent = Platform.StringUtilities.trimEndWithMaxLength(message, 10000);
484
487
  }
@@ -344,7 +344,9 @@ export function isEditing(): boolean {
344
344
  if (!focused) {
345
345
  return false;
346
346
  }
347
- return focused.classList.contains('text-prompt') || focused.nodeName === 'INPUT' || focused.nodeName === 'TEXTAREA';
347
+ return focused.classList.contains('text-prompt') || focused.nodeName === 'INPUT' || focused.nodeName === 'TEXTAREA' ||
348
+ ((focused as HTMLElement).contentEditable === 'true' ||
349
+ (focused as HTMLElement).contentEditable === 'plaintext-only');
348
350
  }
349
351
 
350
352
  export function markBeingEdited(element: Element, value: boolean): boolean {
@@ -43,3 +43,9 @@
43
43
  .close-button:active .active-icon {
44
44
  display: block;
45
45
  }
46
+
47
+ @media (forced-colors: active) and (prefers-color-scheme: dark) {
48
+ .close-button {
49
+ filter: brightness(100);
50
+ }
51
+ }
package/package.json CHANGED
@@ -55,5 +55,5 @@
55
55
  "unittest": "scripts/test/run_unittests.py --no-text-coverage",
56
56
  "watch": "third_party/node/node.py --output scripts/watch_build.js"
57
57
  },
58
- "version": "1.0.928081"
58
+ "version": "1.0.928589"
59
59
  }