chrome-devtools-frontend 1.0.1009515 → 1.0.1009983

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.
@@ -3293,6 +3293,9 @@
3293
3293
  "panels/application/DOMStorageItemsView.ts | domStorageItems": {
3294
3294
  "message": "DOM Storage Items"
3295
3295
  },
3296
+ "panels/application/DOMStorageItemsView.ts | domStorageItemsCleared": {
3297
+ "message": "{PH1} cleared"
3298
+ },
3296
3299
  "panels/application/DOMStorageItemsView.ts | domStorageNumberEntries": {
3297
3300
  "message": "Number of entries shown in table: {PH1}"
3298
3301
  },
@@ -3293,6 +3293,9 @@
3293
3293
  "panels/application/DOMStorageItemsView.ts | domStorageItems": {
3294
3294
  "message": "D̂ÓM̂ Śt̂ór̂áĝé Ît́êḿŝ"
3295
3295
  },
3296
+ "panels/application/DOMStorageItemsView.ts | domStorageItemsCleared": {
3297
+ "message": "{PH1} ĉĺêár̂éd̂"
3298
+ },
3296
3299
  "panels/application/DOMStorageItemsView.ts | domStorageNumberEntries": {
3297
3300
  "message": "N̂úm̂b́êŕ ôf́ êńt̂ŕîéŝ śĥóŵń îń t̂áb̂ĺê: {PH1}"
3298
3301
  },
@@ -188,7 +188,7 @@ export class CSSProperty {
188
188
  if (insideProperty) {
189
189
  result += propertyText;
190
190
  }
191
- result = result.substring(2, result.length - 1).trimRight();
191
+ result = result.substring(2, result.length - 1).trimEnd();
192
192
  return result + (indentation ? '\n' + endIndentation : '');
193
193
 
194
194
  function processToken(token: string, tokenType: string|null): void {
@@ -230,8 +230,8 @@ export class CSSProperty {
230
230
  }
231
231
  if (cssMetadata().isGridAreaDefiningProperty(propertyName)) {
232
232
  const rowResult = GridAreaRowRegex.exec(token);
233
- if (rowResult && rowResult.index === 0 && !propertyText.trimRight().endsWith(']')) {
234
- propertyText = propertyText.trimRight() + '\n' + doubleIndent;
233
+ if (rowResult && rowResult.index === 0 && !propertyText.trimEnd().endsWith(']')) {
234
+ propertyText = propertyText.trimEnd() + '\n' + doubleIndent;
235
235
  }
236
236
  }
237
237
  if (!propertyName && token === ':') {
@@ -192,7 +192,6 @@ export class Script implements TextUtils.ContentProvider.ContentProvider, FrameA
192
192
 
193
193
  originalContentProvider(): TextUtils.ContentProvider.ContentProvider {
194
194
  if (!this.#originalContentProviderInternal) {
195
- /* } */
196
195
  let lazyContentPromise: Promise<TextUtils.ContentProvider.DeferredContent>|null;
197
196
  this.#originalContentProviderInternal =
198
197
  new TextUtils.StaticContentProvider.StaticContentProvider(this.contentURL(), this.contentType(), () => {
@@ -220,7 +219,9 @@ export class Script implements TextUtils.ContentProvider.ContentProvider, FrameA
220
219
  return {content: bytecode, isEncoded: true};
221
220
  }
222
221
  let content: string = scriptSource || '';
223
- if (this.hasSourceURL) {
222
+ if (this.hasSourceURL && this.sourceURL.startsWith('snippet://')) {
223
+ // TODO(crbug.com/1330846): Find a better way to establish the snippet automapping binding then adding
224
+ // a sourceURL comment before evaluation and removing it here.
224
225
  content = Script.trimSourceURLComment(content);
225
226
  }
226
227
  return {content, isEncoded: false};
@@ -51,6 +51,7 @@ export class BreakpointManager extends Common.ObjectWrapper.ObjectWrapper<EventT
51
51
  readonly debuggerWorkspaceBinding: DebuggerWorkspaceBinding;
52
52
  readonly #breakpointsForUISourceCode: Map<Workspace.UISourceCode.UISourceCode, Map<string, BreakpointLocation>>;
53
53
  readonly #breakpointByStorageId: Map<string, Breakpoint>;
54
+ #updateBindingsCallbacks: ((uiSourceCode: Workspace.UISourceCode.UISourceCode) => Promise<void>)[];
54
55
 
55
56
  private constructor(
56
57
  targetManager: SDK.TargetManager.TargetManager, workspace: Workspace.Workspace.WorkspaceImpl,
@@ -69,6 +70,7 @@ export class BreakpointManager extends Common.ObjectWrapper.ObjectWrapper<EventT
69
70
  this.#workspace.addEventListener(Workspace.Workspace.Events.ProjectRemoved, this.projectRemoved, this);
70
71
 
71
72
  this.targetManager.observeModels(SDK.DebuggerModel.DebuggerModel, this);
73
+ this.#updateBindingsCallbacks = [];
72
74
  }
73
75
 
74
76
  static instance(opts: {
@@ -108,6 +110,10 @@ export class BreakpointManager extends Common.ObjectWrapper.ObjectWrapper<EventT
108
110
  debuggerModel.setSynchronizeBreakpointsCallback(null);
109
111
  }
110
112
 
113
+ addUpdateBindingsCallback(callback: ((uiSourceCode: Workspace.UISourceCode.UISourceCode) => Promise<void>)): void {
114
+ this.#updateBindingsCallbacks.push(callback);
115
+ }
116
+
111
117
  async copyBreakpoints(fromURL: Platform.DevToolsPath.UrlString, toSourceCode: Workspace.UISourceCode.UISourceCode):
112
118
  Promise<void> {
113
119
  const breakpointItems = this.storage.breakpointItems(fromURL);
@@ -122,15 +128,13 @@ export class BreakpointManager extends Common.ObjectWrapper.ObjectWrapper<EventT
122
128
  if (!Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.INSTRUMENTATION_BREAKPOINTS)) {
123
129
  return;
124
130
  }
131
+ if (!script.sourceURL) {
132
+ return;
133
+ }
134
+
125
135
  const debuggerModel = script.debuggerModel;
136
+ const uiSourceCode = await this.getUpdatedUISourceCode(script);
126
137
  if (this.#hasBreakpointsForUrl(script.sourceURL)) {
127
- // Handle inline scripts without sourceURL comment separately:
128
- // The UISourceCode of inline scripts without sourceURLs will not be availabe
129
- // until a later point. Use the v8 script for setting the breakpoint.
130
- const isInlineScriptWithoutSourceURL = script.isInlineScript() && !script.hasSourceURL;
131
- const sourceURL =
132
- isInlineScriptWithoutSourceURL ? DefaultScriptMapping.createV8ScriptURL(script) : script.sourceURL;
133
- const uiSourceCode = await Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURLPromise(sourceURL);
134
138
  await this.#restoreBreakpointsForUrl(uiSourceCode);
135
139
  }
136
140
 
@@ -161,6 +165,34 @@ export class BreakpointManager extends Common.ObjectWrapper.ObjectWrapper<EventT
161
165
  }
162
166
  }
163
167
 
168
+ async getUpdatedUISourceCode(script: SDK.Script.Script): Promise<Workspace.UISourceCode.UISourceCode> {
169
+ const isSnippet = script.sourceURL.startsWith('snippet://');
170
+ const projectType = isSnippet ? Workspace.Workspace.projectTypes.Network : undefined;
171
+
172
+ // Handle inline scripts without sourceURL comment separately:
173
+ // The UISourceCode of inline scripts without sourceURLs will not be availabe
174
+ // until a later point. Use the v8 script for setting the breakpoint.
175
+ const isInlineScriptWithoutSourceURL = script.isInlineScript() && !script.hasSourceURL;
176
+ const sourceURL =
177
+ isInlineScriptWithoutSourceURL ? DefaultScriptMapping.createV8ScriptURL(script) : script.sourceURL;
178
+ const uiSourceCode =
179
+ await Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURLPromise(sourceURL, projectType);
180
+
181
+ if (this.#updateBindingsCallbacks.length > 0) {
182
+ // It's possible to set breakpoints on files on the file system, and to have them
183
+ // hit whenever we navigate to a page that serves that file.
184
+ // To make sure that we have all breakpoint information moved from the file system
185
+ // to the served file, we need to update the bindings and await it. This will
186
+ // move the breakpoints from the FileSystem UISourceCode to the Network UiSourceCode.
187
+ const promises = [];
188
+ for (const callback of this.#updateBindingsCallbacks) {
189
+ promises.push(callback(uiSourceCode));
190
+ }
191
+ await Promise.all(promises);
192
+ }
193
+ return uiSourceCode;
194
+ }
195
+
164
196
  async #restoreBreakpointsForUrl(uiSourceCode: Workspace.UISourceCode.UISourceCode): Promise<void> {
165
197
  this.restoreBreakpoints(uiSourceCode);
166
198
  const breakpoints = this.#breakpointByStorageId.values();
@@ -713,20 +745,24 @@ export class ModelBreakpoint {
713
745
  };
714
746
  });
715
747
  newState = new Breakpoint.State(positions, condition);
716
- } else if (this.#breakpoint.currentState) {
717
- newState = new Breakpoint.State(this.#breakpoint.currentState.positions, condition);
718
- } else {
719
- // TODO(bmeurer): This fallback doesn't make a whole lot of sense, we should
720
- // at least signal a warning to the developer that this #breakpoint wasn't
721
- // really resolved.
722
- const position = {
723
- url: this.#breakpoint.url(),
724
- scriptId: '' as Protocol.Runtime.ScriptId,
725
- scriptHash: '',
726
- lineNumber,
727
- columnNumber,
728
- };
729
- newState = new Breakpoint.State([position], condition);
748
+ } else if (!Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.INSTRUMENTATION_BREAKPOINTS)) {
749
+ // Use this fallback if we do not have instrumentation breakpoints enabled yet. This currently makes
750
+ // sure that v8 knows about the breakpoint and is able to restore it whenever the script is parsed.
751
+ if (this.#breakpoint.currentState) {
752
+ newState = new Breakpoint.State(this.#breakpoint.currentState.positions, condition);
753
+ } else {
754
+ // TODO(bmeurer): This fallback doesn't make a whole lot of sense, we should
755
+ // at least signal a warning to the developer that this #breakpoint wasn't
756
+ // really resolved.
757
+ const position = {
758
+ url: this.#breakpoint.url(),
759
+ scriptId: '' as Protocol.Runtime.ScriptId,
760
+ scriptHash: '',
761
+ lineNumber,
762
+ columnNumber,
763
+ };
764
+ newState = new Breakpoint.State([position], condition);
765
+ }
730
766
  }
731
767
  }
732
768
 
@@ -298,7 +298,7 @@ export class ResourceScriptFile extends Common.ObjectWrapper.ObjectWrapper<Resou
298
298
  }
299
299
 
300
300
  // Match ignoring sourceURL.
301
- if (!workingCopy.startsWith(this.#scriptSource.trimRight())) {
301
+ if (!workingCopy.startsWith(this.#scriptSource.trimEnd())) {
302
302
  return true;
303
303
  }
304
304
  const suffix = this.#uiSourceCodeInternal.workingCopy().substr(this.#scriptSource.length);
@@ -111,6 +111,7 @@ export namespace PrivateAPI {
111
111
 
112
112
  export const enum RecorderExtensionPluginCommands {
113
113
  Stringify = 'stringify',
114
+ StringifyStep = 'stringifyStep',
114
115
  }
115
116
 
116
117
  export const enum RecorderExtensionPluginEvents {
@@ -277,7 +278,12 @@ export namespace PrivateAPI {
277
278
  parameters: {recording: Record<string, unknown>},
278
279
  };
279
280
 
280
- export type RecorderExtensionRequests = StringifyRequest;
281
+ type StringifyStepRequest = {
282
+ method: RecorderExtensionPluginCommands.StringifyStep,
283
+ parameters: {step: Record<string, unknown>},
284
+ };
285
+
286
+ export type RecorderExtensionRequests = StringifyRequest|StringifyStepRequest;
281
287
  }
282
288
 
283
289
  declare global {
@@ -721,11 +727,14 @@ self.injectedExtensionAPI = function(
721
727
  .catch(error => port.postMessage({requestId, error: {message: error.message}}));
722
728
  };
723
729
 
724
- function dispatchMethodCall(request: PrivateAPI.RecorderExtensionRequests): Promise<unknown> {
730
+ async function dispatchMethodCall(request: PrivateAPI.RecorderExtensionRequests): Promise<unknown> {
725
731
  switch (request.method) {
726
732
  case PrivateAPI.RecorderExtensionPluginCommands.Stringify:
727
733
  return plugin.stringify(request.parameters.recording);
734
+ case PrivateAPI.RecorderExtensionPluginCommands.StringifyStep:
735
+ return plugin.stringifyStep(request.parameters.step);
728
736
  default:
737
+ // @ts-expect-error
729
738
  throw new Error(`'${request.method}' is not recognized`);
730
739
  }
731
740
  }
@@ -46,4 +46,15 @@ export class RecorderExtensionEndpoint extends ExtensionEndpoint {
46
46
  stringify(recording: Object): Promise<string> {
47
47
  return this.sendRequest(PrivateAPI.RecorderExtensionPluginCommands.Stringify, {recording});
48
48
  }
49
+
50
+ /**
51
+ * In practice, `step` is a Step[1], but we avoid defining this type on the
52
+ * API in order to prevent dependencies between Chrome and puppeteer. Extensions
53
+ * are responsible for working out compatibility issues.
54
+ *
55
+ * [1]: https://github.com/puppeteer/replay/blob/main/src/Schema.ts#L243
56
+ */
57
+ stringifyStep(step: Object): Promise<string> {
58
+ return this.sendRequest(PrivateAPI.RecorderExtensionPluginCommands.StringifyStep, {step});
59
+ }
49
60
  }
@@ -88,7 +88,7 @@ export class Automapping {
88
88
  const networkProjects = this.workspace.projectsForType(Workspace.Workspace.projectTypes.Network);
89
89
  for (const networkProject of networkProjects) {
90
90
  for (const uiSourceCode of networkProject.uiSourceCodes()) {
91
- this.computeNetworkStatus(uiSourceCode);
91
+ void this.computeNetworkStatus(uiSourceCode);
92
92
  }
93
93
  }
94
94
  this.onSweepHappenedForTest();
@@ -137,7 +137,7 @@ export class Automapping {
137
137
  this.fileSystemUISourceCodes.add(uiSourceCode);
138
138
  this.scheduleSweep();
139
139
  } else if (project.type() === Workspace.Workspace.projectTypes.Network) {
140
- this.computeNetworkStatus(uiSourceCode);
140
+ void this.computeNetworkStatus(uiSourceCode);
141
141
  }
142
142
  }
143
143
 
@@ -173,20 +173,24 @@ export class Automapping {
173
173
  this.scheduleSweep();
174
174
  }
175
175
 
176
- private computeNetworkStatus(networkSourceCode: Workspace.UISourceCode.UISourceCode): void {
177
- if (this.sourceCodeToProcessingPromiseMap.has(networkSourceCode) ||
178
- this.sourceCodeToAutoMappingStatusMap.has(networkSourceCode)) {
179
- return;
176
+ computeNetworkStatus(networkSourceCode: Workspace.UISourceCode.UISourceCode): Promise<void> {
177
+ const processingPromise = this.sourceCodeToProcessingPromiseMap.get(networkSourceCode);
178
+ if (processingPromise) {
179
+ return processingPromise;
180
+ }
181
+ if (this.sourceCodeToAutoMappingStatusMap.has(networkSourceCode)) {
182
+ return Promise.resolve();
180
183
  }
181
184
  if (this.interceptors.some(interceptor => interceptor(networkSourceCode))) {
182
- return;
185
+ return Promise.resolve();
183
186
  }
184
187
  if (networkSourceCode.url().startsWith('wasm://')) {
185
- return;
188
+ return Promise.resolve();
186
189
  }
187
190
  const createBindingPromise =
188
191
  this.createBinding(networkSourceCode).then(validateStatus.bind(this)).then(onStatus.bind(this));
189
192
  this.sourceCodeToProcessingPromiseMap.set(networkSourceCode, createBindingPromise);
193
+ return createBindingPromise;
190
194
 
191
195
  async function validateStatus(this: Automapping, status: AutomappingStatus|null): Promise<AutomappingStatus|null> {
192
196
  if (!status) {
@@ -242,7 +246,7 @@ export class Automapping {
242
246
  } else {
243
247
  if (networkContent.content) {
244
248
  // Trim trailing whitespaces because V8 adds trailing newline.
245
- isValid = fileContent.trimRight() === networkContent.content.trimRight();
249
+ isValid = fileContent.trimEnd() === networkContent.content.trimEnd();
246
250
  }
247
251
  }
248
252
  if (!isValid) {
@@ -252,18 +256,19 @@ export class Automapping {
252
256
  return status;
253
257
  }
254
258
 
255
- function onStatus(this: Automapping, status: AutomappingStatus|null): void {
259
+ async function onStatus(this: Automapping, status: AutomappingStatus|null): Promise<void> {
256
260
  if (this.sourceCodeToProcessingPromiseMap.get(networkSourceCode) !== createBindingPromise) {
257
261
  return;
258
262
  }
259
- this.sourceCodeToProcessingPromiseMap.delete(networkSourceCode);
260
263
  if (!status) {
261
264
  this.onBindingFailedForTest();
265
+ this.sourceCodeToProcessingPromiseMap.delete(networkSourceCode);
262
266
  return;
263
267
  }
264
268
  // TODO(lushnikov): remove this check once there's a single uiSourceCode per url. @see crbug.com/670180
265
269
  if (this.sourceCodeToAutoMappingStatusMap.has(status.network) ||
266
270
  this.sourceCodeToAutoMappingStatusMap.has(status.fileSystem)) {
271
+ this.sourceCodeToProcessingPromiseMap.delete(networkSourceCode);
267
272
  return;
268
273
  }
269
274
 
@@ -277,7 +282,8 @@ export class Automapping {
277
282
  this.scheduleSweep();
278
283
  }
279
284
  }
280
- void this.onStatusAdded.call(null, status);
285
+ await this.onStatusAdded.call(null, status);
286
+ this.sourceCodeToProcessingPromiseMap.delete(networkSourceCode);
281
287
  }
282
288
  }
283
289
 
@@ -29,6 +29,7 @@ export class PersistenceImpl extends Common.ObjectWrapper.ObjectWrapper<EventTyp
29
29
  super();
30
30
  this.workspace = workspace;
31
31
  this.breakpointManager = breakpointManager;
32
+ this.breakpointManager.addUpdateBindingsCallback(this.#setupBindings.bind(this));
32
33
  this.filePathPrefixesToBindingCount = new FilePathPrefixesBindingCounts();
33
34
 
34
35
  this.subscribedBindingEventListeners = new Platform.MapUtilities.Multimap();
@@ -79,6 +80,13 @@ export class PersistenceImpl extends Common.ObjectWrapper.ObjectWrapper<EventTyp
79
80
  await this.innerRemoveBinding(binding);
80
81
  }
81
82
 
83
+ #setupBindings(networkUISourceCode: Workspace.UISourceCode.UISourceCode): Promise<void> {
84
+ if (networkUISourceCode.project().type() !== Workspace.Workspace.projectTypes.Network) {
85
+ return Promise.resolve();
86
+ }
87
+ return this.mapping.computeNetworkStatus(networkUISourceCode);
88
+ }
89
+
82
90
  private async innerAddBinding(binding: PersistenceBinding): Promise<void> {
83
91
  bindings.set(binding.network, binding);
84
92
  bindings.set(binding.fileSystem, binding);
@@ -140,10 +148,10 @@ export class PersistenceImpl extends Common.ObjectWrapper.ObjectWrapper<EventTyp
140
148
  this.dispatchEventToListeners(Events.BindingRemoved, binding);
141
149
  }
142
150
 
143
- private async onStatusAdded(status: AutomappingStatus): Promise<void> {
151
+ private onStatusAdded(status: AutomappingStatus): Promise<void> {
144
152
  const binding = new PersistenceBinding(status.network, status.fileSystem);
145
153
  statusBindings.set(status, binding);
146
- await this.innerAddBinding(binding);
154
+ return this.innerAddBinding(binding);
147
155
  }
148
156
 
149
157
  private async onStatusRemoved(status: AutomappingStatus): Promise<void> {
@@ -261,8 +261,8 @@ export class WorkspaceImpl extends Common.ObjectWrapper.ObjectWrapper<EventTypes
261
261
 
262
262
  // This method explicitly awaits the UISourceCode if not yet
263
263
  // available.
264
- uiSourceCodeForURLPromise(url: Platform.DevToolsPath.UrlString): Promise<UISourceCode> {
265
- const uiSourceCode = this.uiSourceCodeForURL(url);
264
+ uiSourceCodeForURLPromise(url: Platform.DevToolsPath.UrlString, type?: projectTypes): Promise<UISourceCode> {
265
+ const uiSourceCode = this.uiSourceCodeForURL(url, type);
266
266
  if (uiSourceCode) {
267
267
  return Promise.resolve(uiSourceCode);
268
268
  }
@@ -270,24 +270,31 @@ export class WorkspaceImpl extends Common.ObjectWrapper.ObjectWrapper<EventTypes
270
270
  const descriptor = this.addEventListener(Events.UISourceCodeAdded, event => {
271
271
  const uiSourceCode = event.data;
272
272
  if (uiSourceCode.url() === url) {
273
- this.removeEventListener(Events.UISourceCodeAdded, descriptor.listener);
274
- resolve(uiSourceCode);
273
+ if (!type || type === uiSourceCode.project().type()) {
274
+ this.removeEventListener(Events.UISourceCodeAdded, descriptor.listener);
275
+ resolve(uiSourceCode);
276
+ }
275
277
  }
276
278
  });
277
279
  });
278
280
  }
279
281
 
280
- uiSourceCodeForURL(url: Platform.DevToolsPath.UrlString): UISourceCode|null {
282
+ uiSourceCodeForURL(url: Platform.DevToolsPath.UrlString, type?: projectTypes): UISourceCode|null {
281
283
  for (const project of this.projectsInternal.values()) {
282
- const uiSourceCode = project.uiSourceCodeForURL(url);
283
- if (uiSourceCode) {
284
- return uiSourceCode;
284
+ // For snippets, we may get two different UISourceCodes for the same url (one belonging to
285
+ // the file system project, one belonging to the network project). Allow selecting the UISourceCode
286
+ // for a specific project type.
287
+ if (!type || project.type() === type) {
288
+ const uiSourceCode = project.uiSourceCodeForURL(url);
289
+ if (uiSourceCode) {
290
+ return uiSourceCode;
291
+ }
285
292
  }
286
293
  }
287
294
  return null;
288
295
  }
289
296
 
290
- uiSourceCodesForProjectType(type: string): UISourceCode[] {
297
+ uiSourceCodesForProjectType(type: projectTypes): UISourceCode[] {
291
298
  const result: UISourceCode[] = [];
292
299
  for (const project of this.projectsInternal.values()) {
293
300
  if (project.type() === type) {
@@ -56,6 +56,11 @@ const UIStrings = {
56
56
  *@description Data grid name for DOM Storage Items data grids
57
57
  */
58
58
  domStorageItems: 'DOM Storage Items',
59
+ /**
60
+ *@description Text for announcing number of entries after filtering
61
+ *@example {'DOM Storage Items'} PH1
62
+ */
63
+ domStorageItemsCleared: '{PH1} cleared',
59
64
  /**
60
65
  *@description Text in DOMStorage Items View of the Application panel
61
66
  */
@@ -149,6 +154,7 @@ export class DOMStorageItemsView extends StorageItemsView {
149
154
 
150
155
  this.dataGrid.rootNode().removeChildren();
151
156
  this.dataGrid.addCreationNode(false);
157
+ UI.ARIAUtils.alert(i18nString(UIStrings.domStorageItemsCleared, {PH1: this.dataGrid.displayName}));
152
158
  this.setCanDeleteSelected(false);
153
159
  }
154
160
 
@@ -53,6 +53,7 @@ export interface DataGridData {
53
53
  rows: Row[];
54
54
  activeSort: SortState|null;
55
55
  contextMenus?: DataGridContextMenusConfiguration;
56
+ label?: string;
56
57
  }
57
58
 
58
59
  const enum UserScrollState {
@@ -76,6 +77,7 @@ export class DataGrid extends HTMLElement {
76
77
  #isRendering = false;
77
78
  #userScrollState: UserScrollState = UserScrollState.NOT_SCROLLED;
78
79
  #contextMenus?: DataGridContextMenusConfiguration = undefined;
80
+ #label?: string = undefined;
79
81
  #currentResize: {
80
82
  rightCellCol: HTMLTableColElement,
81
83
  leftCellCol: HTMLTableColElement,
@@ -132,6 +134,7 @@ export class DataGrid extends HTMLElement {
132
134
  rows: this.#rows as Row[],
133
135
  activeSort: this.#sortState,
134
136
  contextMenus: this.#contextMenus,
137
+ label: this.#label,
135
138
  };
136
139
  }
137
140
 
@@ -143,6 +146,7 @@ export class DataGrid extends HTMLElement {
143
146
  });
144
147
  this.#sortState = data.activeSort;
145
148
  this.#contextMenus = data.contextMenus;
149
+ this.#label = data.label;
146
150
 
147
151
  /**
148
152
  * On first render, now we have data, we can figure out which cell is the
@@ -711,6 +715,7 @@ export class DataGrid extends HTMLElement {
711
715
  })}
712
716
  <div class="wrapping-container" @scroll=${this.#onScroll} @focusout=${this.#onFocusOut}>
713
717
  <table
718
+ aria-label=${LitHtml.Directives.ifDefined(this.#label)}
714
719
  aria-rowcount=${this.#rows.length}
715
720
  aria-colcount=${this.#columns.length}
716
721
  @keydown=${this.#onTableKeyDown}
@@ -23,6 +23,7 @@ export interface DataGridControllerData {
23
23
  */
24
24
  initialSort?: SortState;
25
25
  contextMenus?: DataGridContextMenusConfiguration;
26
+ label?: string;
26
27
  }
27
28
 
28
29
  export class DataGridController extends HTMLElement {
@@ -33,6 +34,7 @@ export class DataGridController extends HTMLElement {
33
34
  #columns: readonly Column[] = [];
34
35
  #rows: Row[] = [];
35
36
  #contextMenus?: DataGridContextMenusConfiguration = undefined;
37
+ #label?: string = undefined;
36
38
 
37
39
  /**
38
40
  * Because the controller will sort data in place (e.g. mutate it) when we get
@@ -56,6 +58,7 @@ export class DataGridController extends HTMLElement {
56
58
  rows: this.#originalRows as Row[],
57
59
  filters: this.#filters,
58
60
  contextMenus: this.#contextMenus,
61
+ label: this.#label,
59
62
  };
60
63
  }
61
64
 
@@ -65,6 +68,7 @@ export class DataGridController extends HTMLElement {
65
68
  this.#contextMenus = data.contextMenus;
66
69
  this.#filters = data.filters || [];
67
70
  this.#contextMenus = data.contextMenus;
71
+ this.#label = data.label;
68
72
 
69
73
  this.#columns = [...this.#originalColumns];
70
74
  this.#rows = this.#cloneAndFilterRows(data.rows, this.#filters);
@@ -210,6 +214,7 @@ export class DataGridController extends HTMLElement {
210
214
  rows: this.#rows,
211
215
  activeSort: this.#sortState,
212
216
  contextMenus: this.#contextMenus,
217
+ label: this.#label,
213
218
  } as DataGridData}
214
219
  @columnheaderclick=${this.#onColumnHeaderClick}
215
220
  @contextmenucolumnsortclick=${this.#onContextMenuColumnSortClick}
@@ -122,7 +122,7 @@ const elementToIndexMap = new WeakMap<Element, number>();
122
122
 
123
123
  export class DataGridImpl<T> extends Common.ObjectWrapper.ObjectWrapper<EventTypes<T>> {
124
124
  element: HTMLDivElement;
125
- private displayName: string;
125
+ displayName: string;
126
126
  private editCallback: ((arg0: any, arg1: string, arg2: any, arg3: any) => void)|undefined;
127
127
  private readonly deleteCallback: ((arg0: any) => void)|undefined;
128
128
  private readonly refreshCallback: (() => void)|undefined;
package/package.json CHANGED
@@ -55,5 +55,5 @@
55
55
  "unittest": "scripts/test/run_unittests.py --no-text-coverage",
56
56
  "watch": "vpython third_party/node/node.py --output scripts/watch_build.js"
57
57
  },
58
- "version": "1.0.1009515"
58
+ "version": "1.0.1009983"
59
59
  }