chrome-devtools-frontend 1.0.1524741 → 1.0.1525561

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.
@@ -4,11 +4,11 @@
4
4
  <small>([go/chrome-devtools:slow-close-policy])</small>
5
5
 
6
6
  In November 2024, we instituted a slow close policy for Chrome DevTools to
7
- automatically maintain hygiene of our bug database. We ended up with a list
8
- of over 1650 open bugs and over 750 open feature requests, some of them going
9
- back over 10 years, which was not only challenging to maintain, but also made
10
- it difficult to determine what's relevant and where we should invest our
11
- resources best.
7
+ automatically maintain hygiene of our bug database. We ended up with a list of
8
+ over 1650 open bugs and over 750 open feature requests, some of them going back
9
+ over 10 years, which was not only challenging to maintain, but also made it
10
+ difficult to determine what's relevant and where we should invest our resources
11
+ best.
12
12
 
13
13
  By automatically nudging and closing stale bugs and feature requests, we can
14
14
  reduce this burden, and better communicate to our users what will actually be
@@ -20,28 +20,31 @@ actioned, and hear from them what remains relevant.
20
20
 
21
21
  The criteria for slow close:
22
22
 
23
- - Status: unassigned
24
- - Type: Bug or Feature Request
25
- - Created: over 3 years ago
26
- - Last updated: over 90 days ago
27
- - Popularity: (cc count + vote count) < 10
28
- - No open descendants
23
+ - Status: unassigned
24
+ - Type: Bug or Feature Request
25
+ - Created: over 3 years ago
26
+ - Last updated: over 90 days ago
27
+ - Popularity: (cc count + vote count) < 10
28
+ - No open descendants
29
29
 
30
30
  Googlers can mark issues as exempt from slow close by adding them to the
31
31
  [`DevTools-Blintz-Close-Exempt` hotlist](https://issues.chromium.org/hotlists/6459983).
32
+ All issues on the
33
+ [`ChromeTooling-Icebox`](https://issues.chromium.org/hotlists/7363836) are also
34
+ automatically exempt from slow close.
32
35
 
33
36
  ## Process
34
37
 
35
38
  The automation runs on a daily basis, and performs the following steps:
36
39
 
37
- - Issues that meet the criteria outlined above are added to the
38
- [`DevTools-Blintz-Close-Candidate` hotlist](https://issues.chromium.org/hotlists/6459982)
39
- for closure.
40
- - If 14 days have passed and no updates have occurred, the issue will be closed
41
- and moved to the
42
- [`DevTools-Blintz-Close` hotlist](https://issues.chromium.org/hotlists/6460812)
43
- for recording purposes.
44
- - No more than 25 issues will be updated in a single run.
40
+ - Issues that meet the criteria outlined above are added to the
41
+ [`DevTools-Blintz-Close-Candidate` hotlist](https://issues.chromium.org/hotlists/6459982)
42
+ for closure.
43
+ - If 14 days have passed and no updates have occurred, the issue will be
44
+ closed and moved to the
45
+ [`DevTools-Blintz-Close` hotlist](https://issues.chromium.org/hotlists/6460812)
46
+ for recording purposes.
47
+ - No more than 25 issues will be updated in a single run.
45
48
 
46
49
  ## Implementation
47
50
 
@@ -2,24 +2,25 @@
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 type * as SDK from '../../core/sdk/sdk.js';
6
- import * as Formatter from '../formatter/formatter.js';
7
- import * as TextUtils from '../text_utils/text_utils.js';
5
+ import * as Formatter from '../../models/formatter/formatter.js';
6
+ import * as TextUtils from '../../models/text_utils/text_utils.js';
7
+
8
+ import type {Script} from './Script.js';
8
9
 
9
10
  type ScopeTreeNode = Formatter.FormatterWorkerPool.ScopeTreeNode;
10
11
 
11
12
  /** If a script failed to parse, we stash null in order to prevent unnecessary re-parsing */
12
- const scopeTrees = new WeakMap<SDK.Script.Script, Promise<ScopeTreeNode|null>>();
13
+ const scopeTrees = new WeakMap<Script, Promise<ScopeTreeNode|null>>();
13
14
 
14
15
  /**
15
16
  * Computes and caches the scope tree for `script`.
16
17
  *
17
- * We use {@link SDK.Script.Script} as a key to uniquely identify scripts.
18
- * {@link SDK.Script.Script} boils down to "target" + "script ID". This
18
+ * We use {@link Script} as a key to uniquely identify scripts.
19
+ * {@link Script} boils down to "target" + "script ID". This
19
20
  * duplicates work in case of identitical script running on multiple targets
20
21
  * (e.g. workers).
21
22
  */
22
- export function scopeTreeForScript(script: SDK.Script.Script): Promise<ScopeTreeNode|null> {
23
+ export function scopeTreeForScript(script: Script): Promise<ScopeTreeNode|null> {
23
24
  let promise = scopeTrees.get(script);
24
25
  if (promise === undefined) {
25
26
  promise = script.requestContentData().then(content => {
@@ -68,6 +68,7 @@ import * as RemoteObject from './RemoteObject.js';
68
68
  import * as Resource from './Resource.js';
69
69
  import * as ResourceTreeModel from './ResourceTreeModel.js';
70
70
  import * as RuntimeModel from './RuntimeModel.js';
71
+ import * as ScopeTreeCache from './ScopeTreeCache.js';
71
72
  import * as ScreenCaptureModel from './ScreenCaptureModel.js';
72
73
  import * as Script from './Script.js';
73
74
  import * as SDKModel from './SDKModel.js';
@@ -149,6 +150,7 @@ export {
149
150
  Resource,
150
151
  ResourceTreeModel,
151
152
  RuntimeModel,
153
+ ScopeTreeCache,
152
154
  ScreenCaptureModel,
153
155
  Script,
154
156
  SDKModel,
@@ -44,9 +44,16 @@ export const enum DefinitionKind {
44
44
  FIXED = 3,
45
45
  }
46
46
 
47
+ export const enum ScopeKind {
48
+ BLOCK = 1,
49
+ FUNCTION = 2,
50
+ GLOBAL = 3,
51
+ }
52
+
47
53
  export interface ScopeTreeNode {
48
54
  variables: Array<{name: string, kind: DefinitionKind, offsets: number[]}>;
49
55
  start: number;
50
56
  end: number;
57
+ kind: ScopeKind;
51
58
  children: ScopeTreeNode[];
52
59
  }
@@ -5,7 +5,7 @@
5
5
  import * as Acorn from '../../third_party/acorn/acorn.js';
6
6
 
7
7
  import {ECMA_VERSION} from './AcornTokenizer.js';
8
- import {DefinitionKind, type ScopeTreeNode} from './FormatterActions.js';
8
+ import {DefinitionKind, ScopeKind, type ScopeTreeNode} from './FormatterActions.js';
9
9
 
10
10
  export function parseScopes(expression: string, sourceType: 'module'|'script' = 'script'): Scope|null {
11
11
  // Parse the expression and find variables and scopes.
@@ -36,12 +36,14 @@ export class Scope {
36
36
  readonly parent: Scope|null;
37
37
  readonly start: number;
38
38
  readonly end: number;
39
+ readonly kind: ScopeKind;
39
40
  readonly children: Scope[] = [];
40
41
 
41
- constructor(start: number, end: number, parent: Scope|null) {
42
+ constructor(start: number, end: number, parent: Scope|null, kind: ScopeKind) {
42
43
  this.start = start;
43
44
  this.end = end;
44
45
  this.parent = parent;
46
+ this.kind = kind;
45
47
  if (parent) {
46
48
  parent.children.push(this);
47
49
  }
@@ -61,6 +63,7 @@ export class Scope {
61
63
  start: this.start,
62
64
  end: this.end,
63
65
  variables,
66
+ kind: this.kind,
64
67
  children,
65
68
  };
66
69
  }
@@ -137,7 +140,7 @@ export class ScopeVariableAnalysis {
137
140
 
138
141
  constructor(node: Acorn.ESTree.Node) {
139
142
  this.#rootNode = node;
140
- this.#rootScope = new Scope(node.start, node.end, null);
143
+ this.#rootScope = new Scope(node.start, node.end, null, ScopeKind.GLOBAL);
141
144
  this.#currentScope = this.#rootScope;
142
145
  }
143
146
 
@@ -169,7 +172,7 @@ export class ScopeVariableAnalysis {
169
172
  node.elements.forEach(item => this.#processNode(item));
170
173
  break;
171
174
  case 'ArrowFunctionExpression': {
172
- this.#pushScope(node.start, node.end);
175
+ this.#pushScope(node.start, node.end, ScopeKind.FUNCTION);
173
176
  node.params.forEach(this.#processNodeAsDefinition.bind(this, DefinitionKind.VAR, false));
174
177
  if (node.body.type === 'BlockStatement') {
175
178
  // Include the body of the arrow function in the same scope as the arguments.
@@ -188,7 +191,7 @@ export class ScopeVariableAnalysis {
188
191
  this.#processNode(node.right);
189
192
  break;
190
193
  case 'BlockStatement':
191
- this.#pushScope(node.start, node.end);
194
+ this.#pushScope(node.start, node.end, ScopeKind.BLOCK);
192
195
  node.body.forEach(this.#processNode.bind(this));
193
196
  this.#popScope(false);
194
197
  break;
@@ -202,7 +205,7 @@ export class ScopeVariableAnalysis {
202
205
  break;
203
206
  }
204
207
  case 'CatchClause':
205
- this.#pushScope(node.start, node.end);
208
+ this.#pushScope(node.start, node.end, ScopeKind.BLOCK);
206
209
  this.#processNodeAsDefinition(DefinitionKind.LET, false, node.param);
207
210
  this.#processNode(node.body);
208
211
  this.#popScope(false);
@@ -234,14 +237,14 @@ export class ScopeVariableAnalysis {
234
237
  break;
235
238
  case 'ForInStatement':
236
239
  case 'ForOfStatement':
237
- this.#pushScope(node.start, node.end);
240
+ this.#pushScope(node.start, node.end, ScopeKind.BLOCK);
238
241
  this.#processNode(node.left);
239
242
  this.#processNode(node.right);
240
243
  this.#processNode(node.body);
241
244
  this.#popScope(false);
242
245
  break;
243
246
  case 'ForStatement':
244
- this.#pushScope(node.start, node.end);
247
+ this.#pushScope(node.start, node.end, ScopeKind.BLOCK);
245
248
  this.#processNode(node.init ?? null);
246
249
  this.#processNode(node.test ?? null);
247
250
  this.#processNode(node.update ?? null);
@@ -250,7 +253,7 @@ export class ScopeVariableAnalysis {
250
253
  break;
251
254
  case 'FunctionDeclaration':
252
255
  this.#processNodeAsDefinition(DefinitionKind.VAR, false, node.id);
253
- this.#pushScope(node.id?.end ?? node.start, node.end);
256
+ this.#pushScope(node.id?.end ?? node.start, node.end, ScopeKind.FUNCTION);
254
257
  this.#addVariable('this', node.start, DefinitionKind.FIXED);
255
258
  this.#addVariable('arguments', node.start, DefinitionKind.FIXED);
256
259
  node.params.forEach(this.#processNodeAsDefinition.bind(this, DefinitionKind.LET, false));
@@ -259,7 +262,7 @@ export class ScopeVariableAnalysis {
259
262
  this.#popScope(true);
260
263
  break;
261
264
  case 'FunctionExpression':
262
- this.#pushScope(node.id?.end ?? node.start, node.end);
265
+ this.#pushScope(node.id?.end ?? node.start, node.end, ScopeKind.FUNCTION);
263
266
  this.#addVariable('this', node.start, DefinitionKind.FIXED);
264
267
  this.#addVariable('arguments', node.start, DefinitionKind.FIXED);
265
268
  node.params.forEach(this.#processNodeAsDefinition.bind(this, DefinitionKind.LET, false));
@@ -421,8 +424,8 @@ export class ScopeVariableAnalysis {
421
424
  return this.#allNames;
422
425
  }
423
426
 
424
- #pushScope(start: number, end: number): void {
425
- this.#currentScope = new Scope(start, end, this.#currentScope);
427
+ #pushScope(start: number, end: number, kind: ScopeKind): void {
428
+ this.#currentScope = new Scope(start, end, this.#currentScope, kind);
426
429
  }
427
430
 
428
431
  #popScope(isFunctionContext: boolean): void {
@@ -5,7 +5,7 @@
5
5
  import * as Common from '../../core/common/common.js';
6
6
  import * as FormatterActions from '../../entrypoints/formatter_worker/FormatterActions.js'; // eslint-disable-line rulesdir/es-modules-import
7
7
 
8
- export {DefinitionKind, type ScopeTreeNode} from '../../entrypoints/formatter_worker/FormatterActions.js';
8
+ export {DefinitionKind, ScopeKind, type ScopeTreeNode} from '../../entrypoints/formatter_worker/FormatterActions.js';
9
9
 
10
10
  let formatterWorkerPoolInstance: FormatterWorkerPool;
11
11
 
@@ -9,8 +9,6 @@ import * as Bindings from '../bindings/bindings.js';
9
9
  import * as Formatter from '../formatter/formatter.js';
10
10
  import * as TextUtils from '../text_utils/text_utils.js';
11
11
 
12
- import {scopeTreeForScript} from './ScopeTreeCache.js';
13
-
14
12
  interface CachedScopeMap {
15
13
  sourceMap: SDK.SourceMap.SourceMap|undefined;
16
14
  mappingPromise: Promise<{variableMapping: Map<string, string>, thisMapping: string|null}>;
@@ -55,7 +53,7 @@ scopeTree:
55
53
  return null;
56
54
  }
57
55
 
58
- const scopeTree = await scopeTreeForScript(script);
56
+ const scopeTree = await SDK.ScopeTreeCache.scopeTreeForScript(script);
59
57
  if (!scopeTree) {
60
58
  return null;
61
59
  }
@@ -4,10 +4,8 @@
4
4
 
5
5
  import * as NamesResolver from './NamesResolver.js';
6
6
  import * as ScopeChainModel from './ScopeChainModel.js';
7
- import * as ScopeTreeCache from './ScopeTreeCache.js';
8
7
 
9
8
  export {
10
9
  NamesResolver,
11
10
  ScopeChainModel,
12
- ScopeTreeCache,
13
11
  };
@@ -67,14 +67,21 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
67
67
 
68
68
  export class ChangesSidebar extends Common.ObjectWrapper.eventMixin<EventTypes, typeof UI.Widget.Widget>(
69
69
  UI.Widget.Widget) {
70
- readonly #workspaceDiff: WorkspaceDiff.WorkspaceDiff.WorkspaceDiffImpl;
70
+ #workspaceDiff: WorkspaceDiff.WorkspaceDiff.WorkspaceDiffImpl|null = null;
71
71
  readonly #view: View;
72
72
  readonly #sourceCodes = new Set<Workspace.UISourceCode.UISourceCode>();
73
73
  #selectedUISourceCode: Workspace.UISourceCode.UISourceCode|null = null;
74
- constructor(workspaceDiff: WorkspaceDiff.WorkspaceDiff.WorkspaceDiffImpl, target?: HTMLElement, view = DEFAULT_VIEW) {
75
- super({jslog: `${VisualLogging.pane('sidebar').track({resize: true})}`});
74
+ constructor(target?: HTMLElement, view = DEFAULT_VIEW) {
75
+ super(target, {jslog: `${VisualLogging.pane('sidebar').track({resize: true})}`});
76
76
  this.#view = view;
77
+ }
77
78
 
79
+ set workspaceDiff(workspaceDiff: WorkspaceDiff.WorkspaceDiff.WorkspaceDiffImpl) {
80
+ if (this.#workspaceDiff) {
81
+ this.#workspaceDiff.modifiedUISourceCodes().forEach(this.#removeUISourceCode.bind(this));
82
+ this.#workspaceDiff.removeEventListener(
83
+ WorkspaceDiff.WorkspaceDiff.Events.MODIFIED_STATUS_CHANGED, this.uiSourceCodeModifiedStatusChanged, this);
84
+ }
78
85
  this.#workspaceDiff = workspaceDiff;
79
86
  this.#workspaceDiff.modifiedUISourceCodes().forEach(this.#addUISourceCode.bind(this));
80
87
  this.#workspaceDiff.addEventListener(
@@ -1,7 +1,6 @@
1
1
  // Copyright 2017 The Chromium Authors
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
- /* eslint-disable rulesdir/no-imperative-dom-api */
5
4
 
6
5
  import '../../ui/legacy/legacy.js';
7
6
 
@@ -10,6 +9,7 @@ import type * as Platform from '../../core/platform/platform.js';
10
9
  import type * as Workspace from '../../models/workspace/workspace.js';
11
10
  import * as WorkspaceDiff from '../../models/workspace_diff/workspace_diff.js';
12
11
  import * as UI from '../../ui/legacy/legacy.js';
12
+ import * as Lit from '../../ui/lit/lit.js';
13
13
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
14
14
 
15
15
  import {ChangesSidebar, Events} from './ChangesSidebar.js';
@@ -30,93 +30,93 @@ const UIStrings = {
30
30
  } as const;
31
31
  const str_ = i18n.i18n.registerUIStrings('panels/changes/ChangesView.ts', UIStrings);
32
32
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
33
+ const {render, html} = Lit;
34
+ interface ViewInput {
35
+ selectedSourceCode: Workspace.UISourceCode.UISourceCode|null;
36
+ onSelect(sourceCode: Workspace.UISourceCode.UISourceCode|null): void;
37
+ workspaceDiff: WorkspaceDiff.WorkspaceDiff.WorkspaceDiffImpl;
38
+ }
39
+ type View = (input: ViewInput, output: object, target: HTMLElement) => void;
40
+ export const DEFAULT_VIEW: View = (input, output, target) => {
41
+ const onSidebar = (sidebar: ChangesSidebar): void => {
42
+ sidebar.addEventListener(
43
+ Events.SELECTED_UI_SOURCE_CODE_CHANGED, () => input.onSelect(sidebar.selectedUISourceCode()));
44
+ };
45
+ render(
46
+ // clang-format off
47
+ html`
48
+ <style>${changesViewStyles}</style>
49
+ <devtools-split-view direction=column>
50
+ <div class=vbox slot="main">
51
+ <devtools-widget
52
+ ?hidden=${input.workspaceDiff.modifiedUISourceCodes().length > 0}
53
+ .widgetConfig=${UI.Widget.widgetConfig(UI.EmptyWidget.EmptyWidget, {
54
+ header: i18nString(UIStrings.noChanges),
55
+ text: i18nString(UIStrings.changesViewDescription),
56
+ link: CHANGES_VIEW_URL,
57
+ })}>
58
+ </devtools-widget>
59
+ <div class=diff-container role=tabpanel ?hidden=${input.workspaceDiff.modifiedUISourceCodes().length === 0}>
60
+ <devtools-widget .widgetConfig=${UI.Widget.widgetConfig(CombinedDiffView.CombinedDiffView, {
61
+ selectedFileUrl: input.selectedSourceCode?.url(),
62
+ workspaceDiff: input.workspaceDiff
63
+ })}></devtools-widget>
64
+ </div>
65
+ </div>
66
+ <devtools-widget
67
+ slot="sidebar"
68
+ .widgetConfig=${UI.Widget.widgetConfig(ChangesSidebar, {
69
+ workspaceDiff: input.workspaceDiff
70
+ })}
71
+ ${UI.Widget.widgetRef(ChangesSidebar, onSidebar)}>
72
+ </devtools-widget>
73
+ </devtools-split-view>`,
74
+ // clang-format on
75
+ target);
76
+ };
33
77
 
34
78
  export class ChangesView extends UI.Widget.VBox {
35
- private emptyWidget: UI.EmptyWidget.EmptyWidget;
36
- private readonly workspaceDiff: WorkspaceDiff.WorkspaceDiff.WorkspaceDiffImpl;
37
- readonly changesSidebar: ChangesSidebar;
38
- private selectedUISourceCode: Workspace.UISourceCode.UISourceCode|null;
39
- private readonly diffContainer: HTMLElement;
40
- private readonly combinedDiffView: CombinedDiffView.CombinedDiffView;
79
+ readonly #workspaceDiff: WorkspaceDiff.WorkspaceDiff.WorkspaceDiffImpl;
80
+ #selectedUISourceCode: Workspace.UISourceCode.UISourceCode|null = null;
81
+ readonly #view: View;
41
82
 
42
- constructor() {
43
- super({
83
+ constructor(target?: HTMLElement, view = DEFAULT_VIEW) {
84
+ super(target, {
44
85
  jslog: `${VisualLogging.panel('changes').track({resize: true})}`,
45
86
  useShadowDom: true,
46
87
  });
47
- this.registerRequiredCSS(changesViewStyles);
48
-
49
- const splitWidget = new UI.SplitWidget.SplitWidget(true /* vertical */, false /* sidebar on left */);
50
- const mainWidget = new UI.Widget.VBox();
51
- splitWidget.setMainWidget(mainWidget);
52
- splitWidget.show(this.contentElement);
53
-
54
- this.emptyWidget = new UI.EmptyWidget.EmptyWidget('', '');
55
- this.emptyWidget.show(mainWidget.element);
56
-
57
- this.workspaceDiff = WorkspaceDiff.WorkspaceDiff.workspaceDiff();
58
- this.changesSidebar = new ChangesSidebar(this.workspaceDiff);
59
- this.changesSidebar.addEventListener(
60
- Events.SELECTED_UI_SOURCE_CODE_CHANGED, this.selectedUISourceCodeChanged, this);
61
- splitWidget.setSidebarWidget(this.changesSidebar);
62
88
 
63
- this.selectedUISourceCode = null;
89
+ this.#workspaceDiff = WorkspaceDiff.WorkspaceDiff.workspaceDiff();
90
+ this.#view = view;
64
91
 
65
- this.diffContainer = mainWidget.element.createChild('div', 'diff-container');
66
- UI.ARIAUtils.markAsTabpanel(this.diffContainer);
67
- this.combinedDiffView = new CombinedDiffView.CombinedDiffView();
68
- this.combinedDiffView.workspaceDiff = this.workspaceDiff;
69
- this.combinedDiffView.show(this.diffContainer);
70
-
71
- this.hideDiff();
72
- this.selectedUISourceCodeChanged();
92
+ this.requestUpdate();
73
93
  }
74
94
 
75
- private renderDiffOrEmptyState(): void {
76
- // There are modified UI source codes, we should render the combined diff view.
77
- if (this.workspaceDiff.modifiedUISourceCodes().length > 0) {
78
- this.showDiff();
79
- } else {
80
- this.hideDiff();
81
- }
82
- }
83
-
84
- private selectedUISourceCodeChanged(): void {
85
- const selectedUISourceCode = this.changesSidebar.selectedUISourceCode();
86
- if (!selectedUISourceCode || this.selectedUISourceCode === selectedUISourceCode) {
87
- return;
88
- }
89
-
90
- this.selectedUISourceCode = selectedUISourceCode;
91
- this.combinedDiffView.selectedFileUrl = selectedUISourceCode.url();
95
+ override performUpdate(): void {
96
+ this.#view(
97
+ {
98
+ workspaceDiff: this.#workspaceDiff,
99
+ selectedSourceCode: this.#selectedUISourceCode,
100
+ onSelect: sourceCode => {
101
+ this.#selectedUISourceCode = sourceCode;
102
+ this.requestUpdate();
103
+ },
104
+ },
105
+ {}, this.contentElement);
92
106
  }
93
107
 
94
108
  override wasShown(): void {
95
109
  UI.Context.Context.instance().setFlavor(ChangesView, this);
96
110
  super.wasShown();
97
- this.renderDiffOrEmptyState();
98
- this.workspaceDiff.addEventListener(
99
- WorkspaceDiff.WorkspaceDiff.Events.MODIFIED_STATUS_CHANGED, this.renderDiffOrEmptyState, this);
111
+ this.requestUpdate();
112
+ this.#workspaceDiff.addEventListener(
113
+ WorkspaceDiff.WorkspaceDiff.Events.MODIFIED_STATUS_CHANGED, this.requestUpdate, this);
100
114
  }
101
115
 
102
116
  override willHide(): void {
103
117
  super.willHide();
104
118
  UI.Context.Context.instance().setFlavor(ChangesView, null);
105
- this.workspaceDiff.removeEventListener(
106
- WorkspaceDiff.WorkspaceDiff.Events.MODIFIED_STATUS_CHANGED, this.renderDiffOrEmptyState, this);
107
- }
108
-
109
- private hideDiff(): void {
110
- this.diffContainer.style.display = 'none';
111
- this.emptyWidget.header = i18nString(UIStrings.noChanges);
112
- this.emptyWidget.text = i18nString(UIStrings.changesViewDescription);
113
-
114
- this.emptyWidget.link = CHANGES_VIEW_URL;
115
- this.emptyWidget.showWidget();
116
- }
117
-
118
- private showDiff(): void {
119
- this.emptyWidget.hideWidget();
120
- this.diffContainer.style.display = 'block';
119
+ this.#workspaceDiff.removeEventListener(
120
+ WorkspaceDiff.WorkspaceDiff.Events.MODIFIED_STATUS_CHANGED, this.requestUpdate, this);
121
121
  }
122
122
  }
@@ -149,7 +149,7 @@ export class CombinedDiffView extends UI.Widget.Widget {
149
149
  void this.#initializeModifiedUISourceCodes();
150
150
  }
151
151
 
152
- set selectedFileUrl(fileUrl: string) {
152
+ set selectedFileUrl(fileUrl: string|undefined) {
153
153
  this.#selectedFileUrl = fileUrl;
154
154
  this.requestUpdate();
155
155
  void this.updateComplete.then(() => {
@@ -36,3 +36,7 @@
36
36
  background-color: var(--sys-color-cdt-base-container);
37
37
  border-top: 1px solid var(--sys-color-divider);
38
38
  }
39
+
40
+ [hidden] {
41
+ display: none !important; /* stylelint-disable-line declaration-no-important */
42
+ }
@@ -401,8 +401,13 @@ export class LighthouseController extends Common.ObjectWrapper.ObjectWrapper<Eve
401
401
  }
402
402
 
403
403
  getCategoryIDs(): string[] {
404
+ const {mode} = this.getFlags();
404
405
  const categoryIDs = [];
405
406
  for (const preset of Presets) {
407
+ if (mode && !preset.supportedModes.includes(mode)) {
408
+ continue;
409
+ }
410
+
406
411
  if (preset.setting.get()) {
407
412
  categoryIDs.push(preset.configID);
408
413
  }
@@ -122,6 +122,8 @@ export interface EventTypes {
122
122
 
123
123
  export class LinearMemoryInspectorView extends UI.Widget.VBox {
124
124
  #memoryWrapper: LazyUint8Array;
125
+ #memory?: Uint8Array<ArrayBuffer>;
126
+ #offset = 0;
125
127
  #address: number;
126
128
  #tabId: string;
127
129
  #inspector: LinearMemoryInspectorComponents.LinearMemoryInspector.LinearMemoryInspector;
@@ -140,32 +142,52 @@ export class LinearMemoryInspectorView extends UI.Widget.VBox {
140
142
  this.#address = address;
141
143
  this.#tabId = tabId;
142
144
  this.#hideValueInspector = Boolean(hideValueInspector);
145
+ this.firstTimeOpen = true;
146
+
143
147
  this.#inspector = new LinearMemoryInspectorComponents.LinearMemoryInspector.LinearMemoryInspector();
144
- this.#inspector.addEventListener(
148
+ this.#inspector.contentElement.addEventListener(
145
149
  LinearMemoryInspectorComponents.LinearMemoryInspector.MemoryRequestEvent.eventName,
146
- (event: LinearMemoryInspectorComponents.LinearMemoryInspector.MemoryRequestEvent) => {
147
- this.#memoryRequested(event);
148
- });
149
- this.#inspector.addEventListener(
150
+ (event: LinearMemoryInspectorComponents.LinearMemoryInspector.MemoryRequestEvent) =>
151
+ this.#memoryRequested(event));
152
+ this.#inspector.contentElement.addEventListener(
150
153
  LinearMemoryInspectorComponents.LinearMemoryInspector.AddressChangedEvent.eventName,
151
- (event: LinearMemoryInspectorComponents.LinearMemoryInspector.AddressChangedEvent) => {
152
- this.updateAddress(event.data);
153
- });
154
- this.#inspector.addEventListener(
154
+ (event: LinearMemoryInspectorComponents.LinearMemoryInspector.AddressChangedEvent) =>
155
+ this.updateAddress(event.data));
156
+ this.#inspector.contentElement.addEventListener(
155
157
  LinearMemoryInspectorComponents.LinearMemoryInspector.SettingsChangedEvent.eventName,
156
158
  (event: LinearMemoryInspectorComponents.LinearMemoryInspector.SettingsChangedEvent) => {
157
159
  // Stop event from bubbling up, since no element further up needs the event.
158
160
  event.stopPropagation();
159
161
  this.saveSettings(event.data);
160
162
  });
161
- this.#inspector.addEventListener(
163
+ this.#inspector.contentElement.addEventListener(
162
164
  LinearMemoryInspectorComponents.LinearMemoryHighlightChipList.DeleteMemoryHighlightEvent.eventName,
163
165
  (event: LinearMemoryInspectorComponents.LinearMemoryHighlightChipList.DeleteMemoryHighlightEvent) => {
164
166
  LinearMemoryInspectorController.instance().removeHighlight(this.#tabId, event.data);
165
167
  this.refreshData();
166
168
  });
167
- this.contentElement.appendChild(this.#inspector);
168
- this.firstTimeOpen = true;
169
+ this.#inspector.show(this.contentElement);
170
+ }
171
+
172
+ render(): void {
173
+ if (this.firstTimeOpen) {
174
+ const settings = LinearMemoryInspectorController.instance().loadSettings();
175
+ this.#inspector.valueTypes = settings.valueTypes;
176
+ this.#inspector.valueTypeModes = settings.modes;
177
+ this.#inspector.endianness = settings.endianness;
178
+ this.firstTimeOpen = false;
179
+ }
180
+
181
+ if (!this.#memory) {
182
+ return;
183
+ }
184
+
185
+ this.#inspector.memory = this.#memory;
186
+ this.#inspector.memoryOffset = this.#offset;
187
+ this.#inspector.address = this.#address;
188
+ this.#inspector.outerMemoryLength = this.#memoryWrapper.length();
189
+ this.#inspector.highlightInfo = this.#getHighlightInfo();
190
+ this.#inspector.hideValueInspector = this.#hideValueInspector;
169
191
  }
170
192
 
171
193
  override wasShown(): void {
@@ -184,32 +206,12 @@ export class LinearMemoryInspectorView extends UI.Widget.VBox {
184
206
  }
185
207
 
186
208
  refreshData(): void {
187
- void LinearMemoryInspectorController.getMemoryForAddress(this.#memoryWrapper, this.#address).then(({
188
- memory,
189
- offset,
190
- }) => {
191
- let valueTypes;
192
- let valueTypeModes;
193
- let endianness;
194
- if (this.firstTimeOpen) {
195
- const settings = LinearMemoryInspectorController.instance().loadSettings();
196
- valueTypes = settings.valueTypes;
197
- valueTypeModes = settings.modes;
198
- endianness = settings.endianness;
199
- this.firstTimeOpen = false;
200
- }
201
- this.#inspector.data = {
202
- memory,
203
- address: this.#address,
204
- memoryOffset: offset,
205
- outerMemoryLength: this.#memoryWrapper.length(),
206
- valueTypes,
207
- valueTypeModes,
208
- endianness,
209
- highlightInfo: this.#getHighlightInfo(),
210
- hideValueInspector: this.#hideValueInspector,
211
- };
212
- });
209
+ void LinearMemoryInspectorController.getMemoryForAddress(this.#memoryWrapper, this.#address)
210
+ .then(({memory, offset}) => {
211
+ this.#memory = memory;
212
+ this.#offset = offset;
213
+ this.render();
214
+ });
213
215
  }
214
216
 
215
217
  #memoryRequested(event: LinearMemoryInspectorComponents.LinearMemoryInspector.MemoryRequestEvent): void {
@@ -219,14 +221,9 @@ export class LinearMemoryInspectorView extends UI.Widget.VBox {
219
221
  }
220
222
 
221
223
  void LinearMemoryInspectorController.getMemoryRange(this.#memoryWrapper, start, end).then(memory => {
222
- this.#inspector.data = {
223
- memory,
224
- address,
225
- memoryOffset: start,
226
- outerMemoryLength: this.#memoryWrapper.length(),
227
- highlightInfo: this.#getHighlightInfo(),
228
- hideValueInspector: this.#hideValueInspector,
229
- };
224
+ this.#memory = memory;
225
+ this.#offset = start;
226
+ this.render();
230
227
  });
231
228
  }
232
229