chrome-devtools-frontend 1.0.927127 → 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 (144) hide show
  1. package/AUTHORS +1 -0
  2. package/config/gni/all_devtools_files.gni +0 -1
  3. package/config/gni/devtools_grd_files.gni +30 -4
  4. package/config/gni/devtools_image_files.gni +1 -0
  5. package/front_end/Images/src/ic_preview_feature.svg +3 -0
  6. package/front_end/Tests.js +2 -3
  7. package/front_end/core/common/Settings.ts +26 -45
  8. package/front_end/core/host/UserMetrics.ts +2 -2
  9. package/front_end/core/i18n/locales/en-US.json +60 -15
  10. package/front_end/core/i18n/locales/en-XL.json +60 -15
  11. package/front_end/core/platform/keyboard-utilities.ts +1 -0
  12. package/front_end/core/root/Runtime.ts +62 -61
  13. package/front_end/core/sdk/AccessibilityModel.ts +73 -73
  14. package/front_end/core/sdk/CPUProfileDataModel.ts +14 -14
  15. package/front_end/core/sdk/CPUProfilerModel.ts +33 -33
  16. package/front_end/core/sdk/CPUThrottlingManager.ts +8 -8
  17. package/front_end/core/sdk/CSSFontFace.ts +10 -10
  18. package/front_end/core/sdk/CSSMatchedStyles.ts +114 -114
  19. package/front_end/core/sdk/CSSMedia.ts +22 -22
  20. package/front_end/core/sdk/CSSMetadata.ts +53 -49
  21. package/front_end/core/sdk/CSSModel.ts +139 -135
  22. package/front_end/core/sdk/CSSProperty.ts +18 -18
  23. package/front_end/core/sdk/CSSRule.ts +15 -15
  24. package/front_end/core/sdk/CSSStyleDeclaration.ts +49 -47
  25. package/front_end/core/sdk/CSSStyleSheetHeader.ts +12 -12
  26. package/front_end/core/sdk/ChildTargetManager.ts +41 -40
  27. package/front_end/core/sdk/CompilerSourceMappingContentProvider.ts +10 -10
  28. package/front_end/core/sdk/Connections.ts +81 -81
  29. package/front_end/core/sdk/ConsoleModel.ts +68 -68
  30. package/front_end/core/sdk/Cookie.ts +48 -48
  31. package/front_end/core/sdk/CookieModel.ts +13 -13
  32. package/front_end/core/sdk/CookieParser.ts +45 -45
  33. package/front_end/core/sdk/DOMDebuggerModel.ts +131 -131
  34. package/front_end/core/sdk/DOMModel.ts +264 -252
  35. package/front_end/core/sdk/DebuggerModel.ts +209 -205
  36. package/front_end/core/sdk/EmulationModel.ts +76 -76
  37. package/front_end/core/sdk/FilmStripModel.ts +29 -29
  38. package/front_end/core/sdk/FrameManager.ts +43 -42
  39. package/front_end/core/sdk/HeapProfilerModel.ts +36 -36
  40. package/front_end/core/sdk/IsolateManager.ts +82 -82
  41. package/front_end/core/sdk/IssuesModel.ts +6 -6
  42. package/front_end/core/sdk/LayerTreeBase.ts +37 -37
  43. package/front_end/core/sdk/LogModel.ts +5 -5
  44. package/front_end/core/sdk/NetworkManager.ts +229 -225
  45. package/front_end/core/sdk/NetworkRequest.ts +368 -360
  46. package/front_end/core/sdk/OverlayColorGenerator.ts +9 -9
  47. package/front_end/core/sdk/OverlayModel.ts +155 -153
  48. package/front_end/core/sdk/OverlayPersistentHighlighter.ts +100 -101
  49. package/front_end/core/sdk/PageResourceLoader.ts +30 -30
  50. package/front_end/core/sdk/PaintProfiler.ts +16 -16
  51. package/front_end/core/sdk/PerformanceMetricsModel.ts +12 -12
  52. package/front_end/core/sdk/ProfileTreeModel.ts +3 -3
  53. package/front_end/core/sdk/RemoteObject.ts +108 -104
  54. package/front_end/core/sdk/Resource.ts +85 -84
  55. package/front_end/core/sdk/ResourceTreeModel.ts +150 -145
  56. package/front_end/core/sdk/RuntimeModel.ts +38 -34
  57. package/front_end/core/sdk/SDKModel.ts +3 -3
  58. package/front_end/core/sdk/ScreenCaptureModel.ts +19 -19
  59. package/front_end/core/sdk/Script.ts +29 -29
  60. package/front_end/core/sdk/SecurityOriginManager.ts +19 -19
  61. package/front_end/core/sdk/ServerTiming.ts +2 -2
  62. package/front_end/core/sdk/ServiceWorkerCacheModel.ts +43 -43
  63. package/front_end/core/sdk/ServiceWorkerManager.ts +72 -68
  64. package/front_end/core/sdk/SourceMap.ts +40 -36
  65. package/front_end/core/sdk/SourceMapManager.ts +57 -57
  66. package/front_end/core/sdk/Target.ts +64 -63
  67. package/front_end/core/sdk/TargetManager.ts +60 -56
  68. package/front_end/core/sdk/TracingManager.ts +39 -39
  69. package/front_end/core/sdk/TracingModel.ts +125 -125
  70. package/front_end/core/sdk/WebAuthnModel.ts +9 -9
  71. package/front_end/entrypoints/lighthouse_worker/{LighthouseService.js → LighthouseService.ts} +20 -45
  72. package/front_end/entrypoints/lighthouse_worker/{lighthouse_worker.js → lighthouse_worker.ts} +0 -0
  73. package/front_end/entrypoints/main/MainImpl.ts +7 -2
  74. package/front_end/legacy_test_runner/elements_test_runner/ElementsTestRunner.js +4 -4
  75. package/front_end/legacy_test_runner/sdk_test_runner/sdk_test_runner.js +1 -1
  76. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +0 -6
  77. package/front_end/models/issues_manager/GenericIssue.ts +86 -0
  78. package/front_end/models/issues_manager/Issue.ts +24 -0
  79. package/front_end/models/issues_manager/IssuesManager.ts +18 -6
  80. package/front_end/models/issues_manager/descriptions/genericCrossOriginPortalPostMessageError.md +3 -0
  81. package/front_end/models/issues_manager/issues_manager.ts +2 -0
  82. package/front_end/models/javascript_metadata/NativeFunctions.js +5422 -1
  83. package/front_end/panels/console/ConsoleSidebar.ts +0 -3
  84. package/front_end/panels/elements/ElementsTreeElement.ts +53 -61
  85. package/front_end/panels/elements/ElementsTreeOutline.ts +0 -1
  86. package/front_end/panels/elements/components/LayoutPane.ts +5 -1
  87. package/front_end/panels/issues/GenericIssueDetailsView.ts +68 -0
  88. package/front_end/panels/issues/IssueAggregator.ts +16 -0
  89. package/front_end/panels/issues/IssueKindView.ts +95 -0
  90. package/front_end/panels/issues/IssueView.ts +6 -0
  91. package/front_end/panels/issues/IssuesPane.ts +81 -18
  92. package/front_end/panels/issues/issuesTree.css +8 -3
  93. package/front_end/panels/lighthouse/LighthouseController.ts +3 -1
  94. package/front_end/panels/network/NetworkItemView.ts +1 -1
  95. package/front_end/panels/network/networkLogView.css +5 -0
  96. package/front_end/panels/sensors/LocationsSettingsTab.ts +1 -1
  97. package/front_end/panels/settings/SettingsScreen.ts +1 -0
  98. package/front_end/panels/settings/settingsScreen.css +24 -0
  99. package/front_end/panels/snippets/SnippetsQuickOpen.ts +8 -3
  100. package/front_end/panels/sources/TabbedEditorContainer.ts +1 -1
  101. package/front_end/panels/sources/sources-meta.ts +22 -7
  102. package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
  103. package/front_end/third_party/codemirror.next/package.json +4 -4
  104. package/front_end/ui/components/code_highlighter/CodeHighlighter.ts +137 -0
  105. package/front_end/ui/components/code_highlighter/codeHighlighter.css +51 -0
  106. package/front_end/ui/components/code_highlighter/code_highlighter.ts +11 -0
  107. package/front_end/ui/components/docs/text_editor/basic.html +28 -0
  108. package/front_end/ui/components/docs/text_editor/basic.ts +14 -0
  109. package/front_end/ui/components/docs/text_prompt/basic.html +35 -0
  110. package/front_end/ui/components/docs/text_prompt/basic.ts +19 -0
  111. package/front_end/ui/components/issue_counter/IssueLinkIcon.ts +1 -0
  112. package/front_end/ui/components/render_coordinator/RenderCoordinator.ts +17 -0
  113. package/front_end/ui/components/request_link_icon/RequestLinkIcon.ts +1 -0
  114. package/front_end/ui/components/text_editor/TextEditor.ts +161 -0
  115. package/front_end/ui/components/text_editor/config.ts +264 -0
  116. package/front_end/{panels/console/components/components.ts → ui/components/text_editor/text_editor.ts} +2 -5
  117. package/front_end/ui/components/text_editor/theme.ts +113 -0
  118. package/front_end/ui/components/text_prompt/TextPrompt.ts +144 -0
  119. package/front_end/ui/components/text_prompt/textPrompt.css +33 -0
  120. package/front_end/ui/components/text_prompt/text_prompt.ts +9 -0
  121. package/front_end/ui/legacy/ARIAUtils.ts +14 -11
  122. package/front_end/ui/legacy/TabbedPane.ts +32 -3
  123. package/front_end/ui/legacy/UIUtils.ts +3 -1
  124. package/front_end/ui/legacy/View.ts +6 -0
  125. package/front_end/ui/legacy/ViewManager.ts +5 -1
  126. package/front_end/ui/legacy/ViewRegistration.ts +5 -0
  127. package/front_end/ui/legacy/XLink.ts +1 -1
  128. package/front_end/ui/legacy/closeButton.css +6 -0
  129. package/front_end/ui/legacy/components/quick_open/CommandMenu.ts +8 -3
  130. package/front_end/ui/legacy/components/quick_open/FilteredListWidget.ts +38 -38
  131. package/front_end/ui/legacy/components/quick_open/HelpQuickOpen.ts +10 -4
  132. package/front_end/ui/legacy/components/quick_open/QuickOpen.ts +23 -6
  133. package/front_end/ui/legacy/components/quick_open/filteredListWidget.css +14 -16
  134. package/front_end/ui/legacy/filter.css +1 -0
  135. package/front_end/ui/legacy/tabbedPane.css +24 -0
  136. package/front_end/ui/legacy/toolbar.css +5 -0
  137. package/inspector_overlay/main.ts +2 -1
  138. package/inspector_overlay/tool_screenshot.ts +8 -1
  139. package/package.json +1 -1
  140. package/scripts/build/rollup.config.js +9 -0
  141. package/scripts/migration/class-fields/migrate.js +56 -0
  142. package/scripts/migration/class-fields/package.json +5 -0
  143. package/front_end/panels/console/components/SidebarDeprecation.ts +0 -58
  144. package/front_end/panels/console/components/sidebarDeprecation.css +0 -17
@@ -0,0 +1,264 @@
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 i18n from '../../../core/i18n/i18n.js';
7
+ import * as CM from '../../../third_party/codemirror.next/codemirror.next.js';
8
+ import * as CodeHighlighter from '../code_highlighter/code_highlighter.js';
9
+
10
+ import {editorTheme} from './theme.js';
11
+
12
+ const LINES_TO_SCAN_FOR_INDENTATION_GUESSING = 1000;
13
+
14
+ const UIStrings = {
15
+ /**
16
+ *@description Label text for the editor
17
+ */
18
+ codeEditor: 'Code editor',
19
+ };
20
+ const str_ = i18n.i18n.registerUIStrings('ui/components/text_editor/config.ts', UIStrings);
21
+ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
22
+
23
+ const empty: CM.Extension = [];
24
+
25
+ export const dynamicSetting = CM.Facet.define<DynamicSetting<unknown>>();
26
+
27
+ // The code below is used to wire up dynamic settings to editors. When
28
+ // you include one of these objects in an editor configuration, the
29
+ // TextEditor class will take care of listening to changes in the
30
+ // setting, and updating the configuration as appropriate.
31
+
32
+ export class DynamicSetting<T> {
33
+ compartment = new CM.Compartment();
34
+ extension: CM.Extension;
35
+
36
+ constructor(
37
+ readonly settingName: string,
38
+ private readonly getExtension: (value: T, state: CM.EditorState) => CM.Extension,
39
+ ) {
40
+ this.extension = [this.compartment.of(empty), dynamicSetting.of(this as DynamicSetting<unknown>)];
41
+ }
42
+
43
+ sync(state: CM.EditorState, value: T): CM.StateEffect<unknown>|null {
44
+ const cur = this.compartment.get(state);
45
+ const needed = this.getExtension(value, state);
46
+ return cur === needed ? null : this.compartment.reconfigure(needed);
47
+ }
48
+
49
+ static bool(name: string, enabled: CM.Extension, disabled: CM.Extension = empty): DynamicSetting<boolean> {
50
+ return new DynamicSetting<boolean>(name, val => val ? enabled : disabled);
51
+ }
52
+ }
53
+
54
+ export const tabMovesFocus = DynamicSetting.bool('textEditorTabMovesFocus', CM.keymap.of([{
55
+ key: 'Tab',
56
+ run: (view: CM.EditorView): boolean => view.state.doc.length ? CM.indentMore(view) : false,
57
+ shift: (view: CM.EditorView): boolean => view.state.doc.length ? CM.indentLess(view) : false,
58
+ }]));
59
+
60
+ export const bracketMatching = DynamicSetting.bool('textEditorBracketMatching', CM.bracketMatching());
61
+
62
+ export function guessIndent(doc: CM.Text): string {
63
+ const values: {[indent: string]: number} = Object.create(null);
64
+ let scanned = 0;
65
+ for (let cur = doc.iterLines(1, Math.min(doc.lines + 1, LINES_TO_SCAN_FOR_INDENTATION_GUESSING)); !cur.next().done;) {
66
+ let space = (/^\s*/.exec(cur.value) as string[])[0];
67
+ if (space.length === cur.value.length || !space.length) {
68
+ continue;
69
+ }
70
+ if (space[0] === '\t') {
71
+ space = '\t';
72
+ } else if (/[^ ]/.test(space)) {
73
+ continue;
74
+ }
75
+ scanned++;
76
+ values[space] = (values[space] || 0) + 1;
77
+ }
78
+ const minOccurrence = scanned * 0.05;
79
+ const sorted = Object.entries(values).filter(e => e[1] > minOccurrence).sort((a, b) => a[1] - b[1]);
80
+ return sorted.length ? sorted[0][0] : Common.Settings.Settings.instance().moduleSetting('textEditorIndent').get();
81
+ }
82
+
83
+ const cachedIndentUnit: {[indent: string]: CM.Extension} = Object.create(null);
84
+
85
+ function getIndentUnit(indent: string): CM.Extension {
86
+ let value = cachedIndentUnit[indent];
87
+ if (!value) {
88
+ value = cachedIndentUnit[indent] = CM.indentUnit.of(indent);
89
+ }
90
+ return value;
91
+ }
92
+
93
+ export const autoDetectIndent = new DynamicSetting<boolean>('textEditorAutoDetectIndent', (on, state) => {
94
+ return on ? CM.Prec.override(getIndentUnit(guessIndent(state.doc))) : empty;
95
+ });
96
+
97
+ function matcher(decorator: CM.MatchDecorator): CM.Extension {
98
+ return CM.ViewPlugin.define(
99
+ view => ({
100
+ decorations: decorator.createDeco(view),
101
+ update(u): void {
102
+ this.decorations = decorator.updateDeco(u, this.decorations);
103
+ },
104
+ }),
105
+ {
106
+ decorations: v => v.decorations,
107
+ });
108
+ }
109
+
110
+ const WhitespaceDeco = new Map<string, CM.Decoration>();
111
+
112
+ function getWhitespaceDeco(space: string): CM.Decoration {
113
+ const cached = WhitespaceDeco.get(space);
114
+ if (cached) {
115
+ return cached;
116
+ }
117
+ const result = CM.Decoration.mark({
118
+ attributes: space === '\t' ? {
119
+ class: 'cm-highlightedTab',
120
+ } :
121
+ {class: 'cm-highlightedSpaces', 'data-display': '·'.repeat(space.length)},
122
+ });
123
+ WhitespaceDeco.set(space, result);
124
+ return result;
125
+ }
126
+
127
+ const showAllWhitespace = matcher(new CM.MatchDecorator({
128
+ regexp: /\t| +/g,
129
+ decoration: (match: RegExpExecArray): CM.Decoration => getWhitespaceDeco(match[0]),
130
+ boundary: /\S/,
131
+ }));
132
+
133
+ const showTrailingWhitespace = matcher(new CM.MatchDecorator({
134
+ regexp: /\s+$/g,
135
+ decoration: CM.Decoration.mark({class: 'cm-trailingWhitespace'}),
136
+ boundary: /\S/,
137
+ }));
138
+
139
+ export const showWhitespace = new DynamicSetting<string>('showWhitespacesInEditor', value => {
140
+ if (value === 'all') {
141
+ return showAllWhitespace;
142
+ }
143
+ if (value === 'trailing') {
144
+ return showTrailingWhitespace;
145
+ }
146
+ return empty;
147
+ });
148
+
149
+ export const allowScrollPastEof = DynamicSetting.bool('allowScrollPastEof', CM.scrollPastEnd());
150
+
151
+ export const indentUnit = new DynamicSetting<string>('textEditorIndent', getIndentUnit);
152
+
153
+ export const domWordWrap = DynamicSetting.bool('domWordWrap', CM.EditorView.lineWrapping);
154
+
155
+ function detectLineSeparator(text: string): CM.Extension {
156
+ if (/\r\n/.test(text) && !/(^|[^\r])\n/.test(text)) {
157
+ return CM.EditorState.lineSeparator.of('\r\n');
158
+ }
159
+ return [];
160
+ }
161
+
162
+ const baseKeymap = CM.keymap.of([
163
+ {key: 'Ctrl-m', run: CM.cursorMatchingBracket, shift: CM.selectMatchingBracket},
164
+ {key: 'Mod-/', run: CM.toggleComment},
165
+ {key: 'Mod-d', run: CM.selectNextOccurrence},
166
+ {key: 'Alt-ArrowLeft', mac: 'Ctrl-ArrowLeft', run: CM.cursorSubwordBackward, shift: CM.selectSubwordBackward},
167
+ {key: 'Alt-ArrowRight', mac: 'Ctrl-ArrowRight', run: CM.cursorSubwordForward, shift: CM.selectSubwordForward},
168
+ ...CM.closeBracketsKeymap,
169
+ ...CM.standardKeymap,
170
+ ...CM.historyKeymap,
171
+ ]);
172
+
173
+ function themeIsDark(): boolean {
174
+ const setting = Common.Settings.Settings.instance().moduleSetting('uiTheme').get();
175
+ return setting === 'systemPreferred' ? window.matchMedia('(prefers-color-scheme: dark)').matches : setting === 'dark';
176
+ }
177
+
178
+ const dummyDarkTheme = CM.EditorView.theme({}, {dark: true});
179
+
180
+ export function baseConfiguration(text: string): CM.Extension {
181
+ return [
182
+ editorTheme,
183
+ themeIsDark() ? dummyDarkTheme : [],
184
+ CM.highlightSpecialChars(),
185
+ CM.history(),
186
+ CM.drawSelection(),
187
+ CM.EditorState.allowMultipleSelections.of(true),
188
+ CM.indentOnInput(),
189
+ CodeHighlighter.CodeHighlighter.getHighlightStyle(CM),
190
+ CM.closeBrackets(),
191
+ baseKeymap,
192
+ tabMovesFocus,
193
+ bracketMatching,
194
+ indentUnit,
195
+ CM.Prec.fallback(CM.EditorView.contentAttributes.of({'aria-label': i18nString(UIStrings.codeEditor)})),
196
+ detectLineSeparator(text),
197
+ CM.tooltips({position: 'absolute'}),
198
+ ];
199
+ }
200
+
201
+ class CompletionHint extends CM.WidgetType {
202
+ constructor(readonly text: string) {
203
+ super();
204
+ }
205
+
206
+ eq(other: CompletionHint): boolean {
207
+ return this.text === other.text;
208
+ }
209
+
210
+ toDOM(): HTMLElement {
211
+ const span = document.createElement('span');
212
+ span.className = 'cm-completionHint';
213
+ span.textContent = this.text;
214
+ return span;
215
+ }
216
+ }
217
+
218
+ export const showCompletionHint = CM.ViewPlugin.fromClass(class {
219
+ decorations: CM.DecorationSet = CM.Decoration.none;
220
+ currentHint: string|null = null;
221
+
222
+ update(update: CM.ViewUpdate): void {
223
+ const top = this.currentHint = this.topCompletion(update.state);
224
+ if (!top) {
225
+ this.decorations = CM.Decoration.none;
226
+ } else {
227
+ this.decorations = CM.Decoration.set(
228
+ [CM.Decoration.widget({widget: new CompletionHint(top), side: 1}).range(update.state.selection.main.head)]);
229
+ }
230
+ }
231
+
232
+ topCompletion(state: CM.EditorState): string|null {
233
+ const completions = CM.currentCompletions(state);
234
+ if (!completions.length) {
235
+ return null;
236
+ }
237
+ const {label} = completions[0];
238
+ if (label.length > 100 || label.indexOf('\n') > -1) {
239
+ return null;
240
+ }
241
+ const pos = state.selection.main.head;
242
+ const lineBefore = state.doc.lineAt(pos);
243
+ if (pos !== lineBefore.to) {
244
+ return null;
245
+ }
246
+ const textBefore = lineBefore.text.slice(0, pos - lineBefore.from);
247
+ for (let i = label.length - 1; i > 0; i--) {
248
+ if (textBefore.endsWith(label.slice(0, i)) && !/\w/.test(textBefore.charAt(textBefore.length - i - 1))) {
249
+ return label.slice(i);
250
+ }
251
+ }
252
+ return null;
253
+ }
254
+ }, {decorations: p => p.decorations});
255
+
256
+ export function contentIncludingHint(view: CM.EditorView): string {
257
+ const plugin = view.plugin(showCompletionHint);
258
+ let content = view.state.doc.toString();
259
+ if (plugin && plugin.currentHint) {
260
+ const {head} = view.state.selection.main;
261
+ content = content.slice(0, head) + plugin.currentHint + content.slice(head);
262
+ }
263
+ return content;
264
+ }
@@ -2,8 +2,5 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
- import * as SidebarDeprecation from './SidebarDeprecation.js';
6
-
7
- export {
8
- SidebarDeprecation,
9
- };
5
+ export * as Config from './config.js';
6
+ export * as TextEditor from './TextEditor.js';
@@ -0,0 +1,113 @@
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 CM from '../../../third_party/codemirror.next/codemirror.next.js';
6
+
7
+ export const editorTheme = CM.EditorView.theme({
8
+ '&.cm-editor': {
9
+ color: 'color: var(--color-text-primary)',
10
+ },
11
+
12
+ '.cm-scroller': {
13
+ lineHeight: '1.2em',
14
+ fontFamily: 'var(--source-code-font-family)',
15
+ fontSize: 'var(--source-code-font-size)',
16
+ },
17
+
18
+ '.cm-panels, .cm-tooltip': {
19
+ backgroundColor: 'var(--color-background-elevation-1)',
20
+ },
21
+
22
+ '.cm-selectionMatch': {
23
+ backgroundColor: 'var(--color-selection-highlight)',
24
+ },
25
+
26
+ '.cm-cursor': {
27
+ borderLeft: '1px solid var(--color-background-inverted)',
28
+ },
29
+
30
+ '&.cm-readonly .cm-cursor': {
31
+ display: 'none',
32
+ },
33
+
34
+ '.cm-cursor-secondary': {
35
+ borderLeft: '1px solid var(--color-secondary-cursor)',
36
+ },
37
+
38
+ '.cm-selectionBackground': {
39
+ background: 'var(--color-editor-selection-selection)',
40
+ },
41
+
42
+ '&.cm-focused .cm-selectionBackground': {
43
+ background: 'var(--color-editor-selection)',
44
+ },
45
+
46
+ '.cm-gutters': {
47
+ borderRight: '1px solid var(--color-details-hairline)',
48
+ whiteSpace: 'nowrap',
49
+ backgroundColor: 'var(--color-background)',
50
+ },
51
+
52
+ '.cm-lineNumbers .cm-gutterElement': {
53
+ color: 'var(--color-line-number)',
54
+ padding: '0 3px 0 9px',
55
+ },
56
+
57
+ '&:focus-within .cm-matchingBracket': {
58
+ color: 'inherit',
59
+ backgroundColor: 'var(--color-matching-bracket-background)',
60
+ borderBottom: '1px solid var(--color-matching-bracket-underline)',
61
+ },
62
+
63
+ '&:focus-within .cm-nonmatchingBracket': {
64
+ backgroundColor: 'var(--color-nonmatching-bracket-background)',
65
+ borderBottom: '1px solid var(--color-nonmatching-bracket-underline)',
66
+ },
67
+
68
+ '.cm-trailingWhitespace': {
69
+ backgroundColor: 'var(--color-error-text)',
70
+ },
71
+
72
+ '.cm-highlightedTab': {
73
+ display: 'inline-block',
74
+ position: 'relative',
75
+ '&:before': {
76
+ content: '""',
77
+ borderBottom: '1px solid var(--color-text-secondary)',
78
+ position: 'absolute',
79
+ left: '5%',
80
+ bottom: '50%',
81
+ width: '90%',
82
+ pointerEvents: 'none',
83
+ },
84
+ },
85
+
86
+ '.cm-highlightedSpaces:before': {
87
+ color: 'var(--color-text-secondary)',
88
+ content: 'attr(data-display)',
89
+ position: 'absolute',
90
+ pointerEvents: 'none',
91
+ },
92
+
93
+ '.cm-placeholder': {
94
+ color: 'var(--color-text-secondary)',
95
+ },
96
+
97
+ '.cm-completionHint': {
98
+ color: 'var(--color-text-secondary)',
99
+ },
100
+
101
+ '.cm-highlightedLine': {
102
+ animation: 'cm-fading-highlight 2s 0s',
103
+ },
104
+
105
+ '@keyframes cm-fading-highlight': {
106
+ from: {
107
+ backgroundColor: 'var(--color-highlighted-line)',
108
+ },
109
+ to: {
110
+ backgroundColor: 'transparent',
111
+ },
112
+ },
113
+ });
@@ -0,0 +1,144 @@
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 Platform from '../../../core/platform/platform.js';
6
+ import * as ComponentHelpers from '../../components/helpers/helpers.js';
7
+ import * as LitHtml from '../../lit-html/lit-html.js';
8
+
9
+ import textPromptStyles from './textPrompt.css.js';
10
+
11
+ export interface TextPromptData {
12
+ ariaLabel: string;
13
+ prefix: string;
14
+ suggestion: string;
15
+ }
16
+
17
+ export class PromptInputEvent extends Event {
18
+ static readonly eventName = 'promptinputchanged';
19
+ data: string;
20
+
21
+ constructor(value: string) {
22
+ super(PromptInputEvent.eventName);
23
+ this.data = value;
24
+ }
25
+ }
26
+
27
+ export class TextPrompt extends HTMLElement {
28
+ static readonly litTagName = LitHtml.literal`devtools-text-prompt`;
29
+ private readonly shadow = this.attachShadow({mode: 'open'});
30
+ private ariaLabelText = '';
31
+ private prefixText = '';
32
+ private suggestionText = '';
33
+
34
+ connectedCallback(): void {
35
+ this.shadow.adoptedStyleSheets = [textPromptStyles];
36
+ }
37
+
38
+ set data(data: TextPromptData) {
39
+ this.ariaLabelText = data.ariaLabel;
40
+ this.prefixText = data.prefix;
41
+ this.suggestionText = data.suggestion;
42
+ this.render();
43
+ }
44
+
45
+ get data(): TextPromptData {
46
+ return {
47
+ ariaLabel: this.ariaLabelText,
48
+ prefix: this.prefixText,
49
+ suggestion: this.suggestionText,
50
+ };
51
+ }
52
+
53
+ focus(): void {
54
+ this.input().focus();
55
+ }
56
+
57
+ private input(): HTMLElement {
58
+ const inputElement = this.shadow.querySelector('.text-prompt-input');
59
+ if (!inputElement) {
60
+ throw new Error('Expected an input element!');
61
+ }
62
+ return /** @type {!HTMLElement} */ inputElement as HTMLElement;
63
+ }
64
+
65
+ moveCaretToEndOfInput(): void {
66
+ this.setSelectedRange(this.text().length, this.text().length);
67
+ }
68
+
69
+ onInput(): void {
70
+ this.dispatchEvent(new PromptInputEvent(this.text().trim()));
71
+ }
72
+
73
+ onKeyDown(event: KeyboardEvent): void {
74
+ if (event.key === Platform.KeyboardUtilities.ENTER_KEY) {
75
+ event.preventDefault();
76
+ }
77
+ }
78
+
79
+ setSelectedRange(startIndex: number, endIndex: number): void {
80
+ if (startIndex < 0) {
81
+ throw new RangeError('Selected range start must be a nonnegative integer');
82
+ }
83
+ const textContentLength = this.text().length;
84
+ if (endIndex > textContentLength) {
85
+ endIndex = textContentLength;
86
+ }
87
+ if (endIndex < startIndex) {
88
+ endIndex = startIndex;
89
+ }
90
+ const inputBox = this.input();
91
+ const range = document.createRange();
92
+ range.setStart(inputBox, startIndex);
93
+ range.setEnd(inputBox, endIndex);
94
+ const selection = window.getSelection();
95
+ if (selection) {
96
+ selection.removeAllRanges();
97
+ selection.addRange(range);
98
+ }
99
+ }
100
+
101
+ setPrefix(prefix: string): void {
102
+ this.prefixText = prefix;
103
+ this.render();
104
+ }
105
+
106
+ setSuggestion(suggestion: string): void {
107
+ this.suggestionText = suggestion;
108
+ this.render();
109
+ }
110
+
111
+ setText(text: string): void {
112
+ this.input().textContent = text;
113
+ if (this.input().hasFocus()) {
114
+ this.moveCaretToEndOfInput();
115
+ this.input().scrollIntoView();
116
+ }
117
+ }
118
+
119
+ private text(): string {
120
+ return this.input().textContent || '';
121
+ }
122
+
123
+ private render(): void {
124
+ const output = LitHtml.html`
125
+ <span class="prefix">${this.prefixText}</span>
126
+ <input aria-label=${this.ariaLabelText}>
127
+ <div class="text-prompt-input" spellcheck="false" contenteditable="plaintext-only" @keydown=${
128
+ this.onKeyDown} @input=${this.onInput} suggestion=" ${this.suggestionText}"></div>
129
+ </input>`;
130
+ LitHtml.render(output, this.shadow, {host: this});
131
+ }
132
+ }
133
+
134
+ ComponentHelpers.CustomElements.defineComponent('devtools-text-prompt', TextPrompt);
135
+
136
+ declare global {
137
+ interface HTMLElementTagNameMap {
138
+ 'devtools-text-prompt': TextPrompt;
139
+ }
140
+
141
+ interface HTMLElementEventMap {
142
+ 'promptinputchanged': PromptInputEvent;
143
+ }
144
+ }
@@ -0,0 +1,33 @@
1
+ /*
2
+ * Copyright 2021 The Chromium Authors. All rights reserved.
3
+ * Use of this source code is governed by a BSD-style license that can be
4
+ * found in the LICENSE file.
5
+ */
6
+
7
+ :host {
8
+ white-space: pre;
9
+ overflow: hidden;
10
+ display: inline-flex;
11
+ }
12
+
13
+ .prefix {
14
+ color: var(--color-primary);
15
+ }
16
+
17
+ input {
18
+ width: 0;
19
+ border: none;
20
+ outline: none;
21
+ }
22
+
23
+ .text-prompt-input {
24
+ border: none;
25
+ outline: none;
26
+ display: inline;
27
+ flex: 1 0 auto;
28
+ }
29
+
30
+ .text-prompt-input::after {
31
+ content: attr(suggestion);
32
+ color: var(--color-background-highlight);
33
+ }
@@ -0,0 +1,9 @@
1
+ // Copyright (c) 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 TextPrompt from './TextPrompt.js';
6
+
7
+ export {
8
+ TextPrompt,
9
+ };
@@ -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
  }
@@ -70,6 +70,10 @@ const UIStrings = {
70
70
  *@description Text on a menu option to close all the drawers except Console when right click on a drawer title
71
71
  */
72
72
  closeAll: 'Close all',
73
+ /**
74
+ *@description Indicates that a tab contains a preview feature (i.e., a beta / experimental feature).
75
+ */
76
+ previewFeature: 'Preview feature',
73
77
  };
74
78
  const str_ = i18n.i18n.registerUIStrings('ui/legacy/TabbedPane.ts', UIStrings);
75
79
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -224,9 +228,9 @@ export class TabbedPane extends Common.ObjectWrapper.eventMixin<EventTypes, type
224
228
 
225
229
  appendTab(
226
230
  id: string, tabTitle: string, view: Widget, tabTooltip?: string, userGesture?: boolean, isCloseable?: boolean,
227
- index?: number): void {
231
+ isPreviewFeature?: boolean, index?: number): void {
228
232
  const closeable = typeof isCloseable === 'boolean' ? isCloseable : Boolean(this.closeableTabs);
229
- const tab = new TabbedPaneTab(this, id, tabTitle, closeable, view, tabTooltip);
233
+ const tab = new TabbedPaneTab(this, id, tabTitle, closeable, Boolean(isPreviewFeature), view, tabTooltip);
230
234
  tab.setDelegate((this.delegate as TabbedPaneTabDelegate));
231
235
  console.assert(!this.tabsById.has(id), `Tabbed pane already contains a tab with id '${id}'`);
232
236
  this.tabsById.set(id, tab);
@@ -966,6 +970,7 @@ export type EventTypes = {
966
970
 
967
971
  export class TabbedPaneTab {
968
972
  closeable: boolean;
973
+ previewFeature = false;
969
974
  private readonly tabbedPane: TabbedPane;
970
975
  idInternal: string;
971
976
  private titleInternal: string;
@@ -980,8 +985,11 @@ export class TabbedPaneTab {
980
985
  private delegate?: TabbedPaneTabDelegate;
981
986
  private titleElement?: HTMLElement;
982
987
  private dragStartX?: number;
983
- constructor(tabbedPane: TabbedPane, id: string, title: string, closeable: boolean, view: Widget, tooltip?: string) {
988
+ constructor(
989
+ tabbedPane: TabbedPane, id: string, title: string, closeable: boolean, previewFeature: boolean, view: Widget,
990
+ tooltip?: string) {
984
991
  this.closeable = closeable;
992
+ this.previewFeature = previewFeature;
985
993
  this.tabbedPane = tabbedPane;
986
994
  this.idInternal = id;
987
995
  this.titleInternal = title;
@@ -1107,6 +1115,12 @@ export class TabbedPaneTab {
1107
1115
  this.titleElement = titleElement;
1108
1116
  }
1109
1117
 
1118
+ if (this.previewFeature) {
1119
+ const previewIcon = this.createPreviewIcon();
1120
+ tabElement.appendChild(previewIcon);
1121
+ tabElement.classList.add('preview');
1122
+ }
1123
+
1110
1124
  if (this.closeable) {
1111
1125
  const closeIcon = this.createCloseIconButton();
1112
1126
  tabElement.appendChild(closeIcon);
@@ -1148,6 +1162,21 @@ export class TabbedPaneTab {
1148
1162
  return closeIconContainer;
1149
1163
  }
1150
1164
 
1165
+ private createPreviewIcon(): HTMLDivElement {
1166
+ const previewIcon = document.createElement('div');
1167
+ previewIcon.classList.add('preview-icon');
1168
+ const closeIcon = new IconButton.Icon.Icon();
1169
+ closeIcon.data = {
1170
+ iconName: 'ic_preview_feature',
1171
+ color: 'var(--override-tabbed-pane-preview-icon-color)',
1172
+ width: '14px',
1173
+ };
1174
+ previewIcon.appendChild(closeIcon);
1175
+ previewIcon.setAttribute('title', i18nString(UIStrings.previewFeature));
1176
+ previewIcon.setAttribute('aria-label', i18nString(UIStrings.previewFeature));
1177
+ return previewIcon;
1178
+ }
1179
+
1151
1180
  private isCloseIconClicked(element: HTMLElement): boolean {
1152
1181
  return element?.classList.contains('tabbed-pane-close-button') ||
1153
1182
  element?.parentElement?.classList.contains('tabbed-pane-close-button') || false;