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.
- package/AUTHORS +1 -0
- package/config/gni/devtools_grd_files.gni +22 -0
- package/front_end/core/common/Settings.ts +26 -45
- package/front_end/core/host/UserMetrics.ts +2 -1
- package/front_end/core/i18n/locales/en-US.json +18 -0
- package/front_end/core/i18n/locales/en-XL.json +18 -0
- package/front_end/core/sdk/CookieParser.ts +2 -2
- package/front_end/entrypoints/main/MainImpl.ts +7 -1
- package/front_end/models/issues_manager/Issue.ts +23 -0
- package/front_end/models/issues_manager/IssuesManager.ts +13 -6
- package/front_end/panels/elements/ElementsTreeElement.ts +53 -61
- package/front_end/panels/elements/ElementsTreeOutline.ts +0 -1
- package/front_end/panels/elements/components/LayoutPane.ts +5 -1
- package/front_end/panels/issues/IssueAggregator.ts +8 -0
- package/front_end/panels/issues/IssueKindView.ts +95 -0
- package/front_end/panels/issues/IssueView.ts +4 -0
- package/front_end/panels/issues/IssuesPane.ts +75 -18
- package/front_end/panels/issues/issuesTree.css +8 -3
- package/front_end/panels/lighthouse/LighthouseController.ts +3 -1
- package/front_end/panels/network/networkLogView.css +5 -0
- package/front_end/panels/sensors/LocationsSettingsTab.ts +1 -1
- package/front_end/panels/settings/SettingsScreen.ts +1 -0
- package/front_end/panels/settings/settingsScreen.css +24 -0
- package/front_end/ui/components/issue_counter/IssueLinkIcon.ts +1 -0
- package/front_end/ui/components/request_link_icon/RequestLinkIcon.ts +1 -0
- package/front_end/ui/legacy/ARIAUtils.ts +14 -11
- package/front_end/ui/legacy/UIUtils.ts +3 -1
- package/front_end/ui/legacy/closeButton.css +6 -0
- 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(
|
|
54
|
-
|
|
55
|
-
|
|
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.
|
|
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(
|
|
291
|
-
|
|
292
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
348
|
-
if (this.storage.has(this.
|
|
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.
|
|
338
|
+
this.value = this.serializer.parse(this.storage.get(this.name));
|
|
351
339
|
} catch (e) {
|
|
352
|
-
this.storage.remove(this.
|
|
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.
|
|
352
|
+
this.storage.set(this.name, settingString);
|
|
365
353
|
} catch (e) {
|
|
366
|
-
this.printSettingsSavingError(e.message, this.
|
|
354
|
+
this.printSettingsSavingError(e.message, this.name, settingString);
|
|
367
355
|
}
|
|
368
356
|
} catch (e) {
|
|
369
|
-
Console.instance().error('Cannot stringify setting with name: ' + this.
|
|
357
|
+
Console.instance().error('Cannot stringify setting with name: ' + this.name + ', error: ' + e.message);
|
|
370
358
|
}
|
|
371
|
-
this.eventSupport.dispatchEventToListeners(this.
|
|
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 =
|
|
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
|
-
'
|
|
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]*([
|
|
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
|
|
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
|
-
|
|
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
|
|
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/
|
|
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
|
|
1017
|
-
const
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
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
|
-
|
|
1033
|
-
editor.
|
|
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(
|
|
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.
|
|
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
|
|
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
|
|
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?:
|
|
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;
|
|
@@ -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
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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
|
-
|
|
332
|
-
|
|
333
|
-
if (
|
|
334
|
-
return
|
|
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
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
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
|
-
|
|
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
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
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
|
-
|
|
478
|
-
|
|
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
|
-
|
|
483
|
-
|
|
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 {
|
package/package.json
CHANGED