chrome-devtools-frontend 1.0.943709 → 1.0.943986
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/front_end/core/sdk/Script.ts +0 -4
- package/front_end/models/bindings/DefaultScriptMapping.ts +1 -9
- package/front_end/models/bindings/ResourceScriptMapping.ts +3 -11
- package/front_end/models/formatter/SourceFormatter.ts +0 -15
- package/front_end/panels/elements/AccessibilityTreeUtils.ts +96 -66
- package/front_end/panels/elements/AccessibilityTreeView.ts +76 -126
- package/front_end/panels/elements/ElementsPanel.ts +6 -7
- package/package.json +1 -1
|
@@ -313,10 +313,6 @@ export class Script implements TextUtils.ContentProvider.ContentProvider, FrameA
|
|
|
313
313
|
return !this.sourceURL;
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
-
isInlineScriptWithSourceURL(): boolean {
|
|
317
|
-
return Boolean(this.hasSourceURL) && this.isInlineScript();
|
|
318
|
-
}
|
|
319
|
-
|
|
320
316
|
async setBlackboxedRanges(positions: Protocol.Debugger.ScriptPosition[]): Promise<boolean> {
|
|
321
317
|
const response = await this.debuggerModel.target().debuggerAgent().invoke_setBlackboxedRanges(
|
|
322
318
|
{scriptId: this.scriptId, positions});
|
|
@@ -75,11 +75,7 @@ export class DefaultScriptMapping implements DebuggerSourceMapping {
|
|
|
75
75
|
if (!uiSourceCode) {
|
|
76
76
|
return null;
|
|
77
77
|
}
|
|
78
|
-
const lineNumber =
|
|
79
|
-
let columnNumber = rawLocation.columnNumber || 0;
|
|
80
|
-
if (script.isInlineScriptWithSourceURL() && !lineNumber && columnNumber) {
|
|
81
|
-
columnNumber -= script.columnOffset;
|
|
82
|
-
}
|
|
78
|
+
const {lineNumber, columnNumber = 0} = rawLocation;
|
|
83
79
|
return uiSourceCode.uiLocation(lineNumber, columnNumber);
|
|
84
80
|
}
|
|
85
81
|
|
|
@@ -89,10 +85,6 @@ export class DefaultScriptMapping implements DebuggerSourceMapping {
|
|
|
89
85
|
if (!script) {
|
|
90
86
|
return [];
|
|
91
87
|
}
|
|
92
|
-
if (script.isInlineScriptWithSourceURL()) {
|
|
93
|
-
return [this.#debuggerModel.createRawLocation(
|
|
94
|
-
script, lineNumber + script.lineOffset, lineNumber ? columnNumber : columnNumber + script.columnOffset)];
|
|
95
|
-
}
|
|
96
88
|
return [this.#debuggerModel.createRawLocation(script, lineNumber, columnNumber)];
|
|
97
89
|
}
|
|
98
90
|
|
|
@@ -127,30 +127,22 @@ export class ResourceScriptMapping implements DebuggerSourceMapping {
|
|
|
127
127
|
if (!scriptFile.hasScripts([script])) {
|
|
128
128
|
return null;
|
|
129
129
|
}
|
|
130
|
-
const lineNumber =
|
|
131
|
-
let columnNumber = rawLocation.columnNumber || 0;
|
|
132
|
-
if (script.isInlineScriptWithSourceURL() && !lineNumber && columnNumber) {
|
|
133
|
-
columnNumber -= script.columnOffset;
|
|
134
|
-
}
|
|
130
|
+
const {lineNumber, columnNumber = 0} = rawLocation;
|
|
135
131
|
return uiSourceCode.uiLocation(lineNumber, columnNumber);
|
|
136
132
|
}
|
|
137
133
|
|
|
138
134
|
uiLocationToRawLocations(uiSourceCode: Workspace.UISourceCode.UISourceCode, lineNumber: number, columnNumber: number):
|
|
139
135
|
SDK.DebuggerModel.Location[] {
|
|
140
136
|
const scriptFile = this.#uiSourceCodeToScriptFile.get(uiSourceCode);
|
|
141
|
-
if (!scriptFile
|
|
137
|
+
if (!scriptFile) {
|
|
142
138
|
return [];
|
|
143
139
|
}
|
|
144
140
|
|
|
145
|
-
const script = scriptFile
|
|
141
|
+
const {script} = scriptFile;
|
|
146
142
|
if (!script) {
|
|
147
143
|
return [];
|
|
148
144
|
}
|
|
149
145
|
|
|
150
|
-
if (script.isInlineScriptWithSourceURL()) {
|
|
151
|
-
return [this.debuggerModel.createRawLocation(
|
|
152
|
-
script, lineNumber + script.lineOffset, lineNumber ? columnNumber : columnNumber + script.columnOffset)];
|
|
153
|
-
}
|
|
154
146
|
return [this.debuggerModel.createRawLocation(script, lineNumber, columnNumber)];
|
|
155
147
|
}
|
|
156
148
|
|
|
@@ -165,21 +165,6 @@ class ScriptMapping implements Bindings.DebuggerWorkspaceBinding.DebuggerSourceM
|
|
|
165
165
|
if (!formatData || !script) {
|
|
166
166
|
return null;
|
|
167
167
|
}
|
|
168
|
-
if (script.isInlineScriptWithSourceURL()) {
|
|
169
|
-
// Inline scripts with #sourceURL= have lineEndings wrt. the inline script (and not wrt. the containing document),
|
|
170
|
-
// but `rawLocation` will always use locations wrt. the containing document, because that is what the back-end is
|
|
171
|
-
// sending. This is a hack, because what we are really doing here is deciding the location based on /how/ the
|
|
172
|
-
// script is displayed, which is really something this layer cannot and should not have to decide: The
|
|
173
|
-
// SourceFormatter should not have to know whether a script is displayed inline (in its containing document) or
|
|
174
|
-
// stand-alone.
|
|
175
|
-
const [relativeLineNumber, relativeColumnNumber] = script.toRelativeLocation(rawLocation);
|
|
176
|
-
const [formattedLineNumber, formattedColumnNumber] =
|
|
177
|
-
formatData.mapping.originalToFormatted(relativeLineNumber, relativeColumnNumber);
|
|
178
|
-
return formatData.formattedSourceCode.uiLocation(formattedLineNumber, formattedColumnNumber);
|
|
179
|
-
}
|
|
180
|
-
// Here we either have an inline script without a #sourceURL= or a stand-alone script. For stand-alone scripts, no
|
|
181
|
-
// translation must be applied. For inline scripts, also no translation must be applied, because the line-endings
|
|
182
|
-
// tables in the mapping are the same as in the containing document.
|
|
183
168
|
const [lineNumber, columnNumber] =
|
|
184
169
|
formatData.mapping.originalToFormatted(rawLocation.lineNumber, rawLocation.columnNumber || 0);
|
|
185
170
|
return formatData.formattedSourceCode.uiLocation(lineNumber, columnNumber);
|
|
@@ -6,87 +6,113 @@ import * as SDK from '../../core/sdk/sdk.js';
|
|
|
6
6
|
import type * as TreeOutline from '../../ui/components/tree_outline/tree_outline.js';
|
|
7
7
|
import * as ElementsComponents from './components/components.js';
|
|
8
8
|
import * as LitHtml from '../../ui/lit-html/lit-html.js';
|
|
9
|
+
import type * as Protocol from '../../generated/protocol.js';
|
|
9
10
|
|
|
10
11
|
export type AXTreeNodeData = SDK.AccessibilityModel.AccessibilityNode;
|
|
11
12
|
export type AXTreeNode = TreeOutline.TreeOutlineUtils.TreeNode<AXTreeNodeData>;
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (role === 'Iframe') {
|
|
18
|
-
return {
|
|
19
|
-
treeNodeData,
|
|
20
|
-
children: async(): Promise<AXTreeNode[]> => {
|
|
21
|
-
const domNode = await sdkNode.deferredDOMNode()?.resolvePromise();
|
|
22
|
-
if (!domNode) {
|
|
23
|
-
throw new Error('Could not find corresponding DOMNode');
|
|
24
|
-
}
|
|
25
|
-
const frameId = domNode.frameOwnerFrameId();
|
|
14
|
+
function isLeafNode(node: SDK.AccessibilityModel.AccessibilityNode): boolean {
|
|
15
|
+
return node.numChildren() === 0 && node.role()?.value !== 'Iframe';
|
|
16
|
+
}
|
|
26
17
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (!document) {
|
|
36
|
-
return [];
|
|
37
|
-
}
|
|
18
|
+
function getModel(frameId: Protocol.Page.FrameId): SDK.AccessibilityModel.AccessibilityModel {
|
|
19
|
+
const frame = SDK.FrameManager.FrameManager.instance().getFrame(frameId);
|
|
20
|
+
const model = frame?.resourceTreeModel().target().model(SDK.AccessibilityModel.AccessibilityModel);
|
|
21
|
+
if (!model) {
|
|
22
|
+
throw Error('Could not instantiate model for frameId');
|
|
23
|
+
}
|
|
24
|
+
return model;
|
|
25
|
+
}
|
|
38
26
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
let localRoot = axmodel.axNodeForDOMNode(document);
|
|
45
|
-
if (!localRoot && frameId) {
|
|
46
|
-
// Request the root node of the iframe document:
|
|
47
|
-
localRoot = await axmodel.requestRootNode(frameId) || null;
|
|
48
|
-
}
|
|
49
|
-
if (!localRoot) {
|
|
50
|
-
throw new Error('Could not find root node');
|
|
51
|
-
}
|
|
52
|
-
return [sdkNodeToAXTreeNode(localRoot)];
|
|
53
|
-
},
|
|
54
|
-
id: sdkNode.id(),
|
|
55
|
-
};
|
|
27
|
+
export async function getRootNode(frameId: Protocol.Page.FrameId): Promise<SDK.AccessibilityModel.AccessibilityNode> {
|
|
28
|
+
const model = getModel(frameId);
|
|
29
|
+
const root = await model.requestRootNode(frameId);
|
|
30
|
+
if (!root) {
|
|
31
|
+
throw Error('No accessibility root for frame');
|
|
56
32
|
}
|
|
33
|
+
return root;
|
|
34
|
+
}
|
|
57
35
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
36
|
+
function getFrameIdForNodeOrDocument(node: SDK.DOMModel.DOMNode): Protocol.Page.FrameId {
|
|
37
|
+
let frameId;
|
|
38
|
+
if (node instanceof SDK.DOMModel.DOMDocument) {
|
|
39
|
+
frameId = node.body?.frameId();
|
|
40
|
+
} else {
|
|
41
|
+
frameId = node.frameId();
|
|
42
|
+
}
|
|
43
|
+
if (!frameId) {
|
|
44
|
+
throw Error('No frameId for DOM node');
|
|
63
45
|
}
|
|
46
|
+
return frameId;
|
|
47
|
+
}
|
|
64
48
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
// there are backend children that need to be loaded into the model, so request them now.
|
|
74
|
-
await sdkNode.accessibilityModel().requestAXChildren(sdkNode.id(), sdkNode.getFrameId() || undefined);
|
|
49
|
+
export async function getNodeAndAncestorsFromDOMNode(domNode: SDK.DOMModel.DOMNode):
|
|
50
|
+
Promise<SDK.AccessibilityModel.AccessibilityNode[]> {
|
|
51
|
+
let frameId = getFrameIdForNodeOrDocument(domNode);
|
|
52
|
+
const model = getModel(frameId);
|
|
53
|
+
const result = await model.requestAndLoadSubTreeToNode(domNode);
|
|
54
|
+
if (!result) {
|
|
55
|
+
throw Error('Could not retrieve accessibility node for inspected DOM node');
|
|
56
|
+
}
|
|
75
57
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
58
|
+
const topFrameId = SDK.FrameManager.FrameManager.instance().getTopFrame()?.id;
|
|
59
|
+
if (!topFrameId) {
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
while (frameId !== topFrameId) {
|
|
63
|
+
const node = await SDK.FrameManager.FrameManager.instance().getFrame(frameId)?.getOwnerDOMNodeOrDocument();
|
|
64
|
+
if (!node) {
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
frameId = getFrameIdForNodeOrDocument(node);
|
|
68
|
+
const model = getModel(frameId);
|
|
69
|
+
const ancestors = await model.requestAndLoadSubTreeToNode(node);
|
|
70
|
+
result.push(...ancestors || []);
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
79
74
|
|
|
80
|
-
|
|
75
|
+
async function getChildren(node: SDK.AccessibilityModel.AccessibilityNode):
|
|
76
|
+
Promise<SDK.AccessibilityModel.AccessibilityNode[]> {
|
|
77
|
+
if (node.role()?.value === 'Iframe') {
|
|
78
|
+
const domNode = await node.deferredDOMNode()?.resolvePromise();
|
|
79
|
+
if (!domNode) {
|
|
80
|
+
throw new Error('Could not find corresponding DOMNode');
|
|
81
|
+
}
|
|
82
|
+
const frameId = domNode.frameOwnerFrameId();
|
|
83
|
+
if (!frameId) {
|
|
84
|
+
throw Error('No owner frameId on iframe node');
|
|
85
|
+
}
|
|
86
|
+
const localRoot = await getRootNode(frameId);
|
|
87
|
+
return [localRoot];
|
|
88
|
+
}
|
|
89
|
+
if (node.hasUnloadedChildren()) {
|
|
90
|
+
await node.accessibilityModel().requestAXChildren(node.id(), node.getFrameId() || undefined);
|
|
91
|
+
if (node.numChildren() !== node.children().length) {
|
|
92
|
+
throw new Error('Once loaded, number of children and length of children must match.');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return node.children();
|
|
96
|
+
}
|
|
81
97
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
98
|
+
export async function sdkNodeToAXTreeNodes(sdkNode: SDK.AccessibilityModel.AccessibilityNode): Promise<AXTreeNode[]> {
|
|
99
|
+
const treeNodeData = sdkNode;
|
|
100
|
+
if (isLeafNode(sdkNode)) {
|
|
101
|
+
return [{
|
|
102
|
+
treeNodeData,
|
|
103
|
+
id: getNodeId(sdkNode),
|
|
104
|
+
}];
|
|
105
|
+
}
|
|
85
106
|
|
|
86
|
-
|
|
107
|
+
return [{
|
|
108
|
+
treeNodeData,
|
|
109
|
+
children: async(): Promise<AXTreeNode[]> => {
|
|
110
|
+
const childNodes = await getChildren(sdkNode);
|
|
111
|
+
const childTreeNodes = await Promise.all(childNodes.map(childNode => sdkNodeToAXTreeNodes(childNode)));
|
|
112
|
+
return childTreeNodes.flat(1);
|
|
87
113
|
},
|
|
88
|
-
id: sdkNode
|
|
89
|
-
};
|
|
114
|
+
id: getNodeId(sdkNode),
|
|
115
|
+
}];
|
|
90
116
|
}
|
|
91
117
|
|
|
92
118
|
type Data = ElementsComponents.AccessibilityTreeNode.AccessibilityTreeNodeData;
|
|
@@ -99,3 +125,7 @@ export function accessibilityNodeRenderer(node: AXTreeNode): LitHtml.TemplateRes
|
|
|
99
125
|
const ignored = sdkNode.ignored();
|
|
100
126
|
return LitHtml.html`<${tag} .data=${{name, role, ignored} as Data}></${tag}>`;
|
|
101
127
|
}
|
|
128
|
+
|
|
129
|
+
export function getNodeId(node: SDK.AccessibilityModel.AccessibilityNode): string {
|
|
130
|
+
return node.getFrameId() + '#' + node.id();
|
|
131
|
+
}
|
|
@@ -9,15 +9,12 @@ import * as UI from '../../ui/legacy/legacy.js';
|
|
|
9
9
|
import * as AccessibilityTreeUtils from './AccessibilityTreeUtils.js';
|
|
10
10
|
import {ElementsPanel} from './ElementsPanel.js';
|
|
11
11
|
|
|
12
|
-
export class AccessibilityTreeView extends UI.Widget.VBox
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
private treeData: AccessibilityTreeUtils.AXTreeNode[] = [];
|
|
12
|
+
export class AccessibilityTreeView extends UI.Widget.VBox implements
|
|
13
|
+
SDK.TargetManager.SDKModelObserver<SDK.AccessibilityModel.AccessibilityModel> {
|
|
14
|
+
private accessibilityTreeComponent = new TreeOutline.TreeOutline.TreeOutline<AccessibilityTreeUtils.AXTreeNodeData>();
|
|
16
15
|
private readonly toggleButton: HTMLButtonElement;
|
|
17
|
-
private accessibilityModel: SDK.AccessibilityModel.AccessibilityModel|null = null;
|
|
18
|
-
private rootAXNode: SDK.AccessibilityModel.AccessibilityNode|null = null;
|
|
19
|
-
private selectedTreeNode: AccessibilityTreeUtils.AXTreeNode|null = null;
|
|
20
16
|
private inspectedDOMNode: SDK.DOMModel.DOMNode|null = null;
|
|
17
|
+
private root: SDK.AccessibilityModel.AccessibilityNode|null = null;
|
|
21
18
|
|
|
22
19
|
constructor(toggleButton: HTMLButtonElement) {
|
|
23
20
|
super();
|
|
@@ -27,6 +24,8 @@ export class AccessibilityTreeView extends UI.Widget.VBox {
|
|
|
27
24
|
this.contentElement.appendChild(this.toggleButton);
|
|
28
25
|
this.contentElement.appendChild(this.accessibilityTreeComponent);
|
|
29
26
|
|
|
27
|
+
SDK.TargetManager.TargetManager.instance().observeModels(SDK.AccessibilityModel.AccessibilityModel, this);
|
|
28
|
+
|
|
30
29
|
// The DOM tree and accessibility are kept in sync as much as possible, so
|
|
31
30
|
// on node selection, update the currently inspected node and reveal in the
|
|
32
31
|
// DOM tree.
|
|
@@ -57,154 +56,105 @@ export class AccessibilityTreeView extends UI.Widget.VBox {
|
|
|
57
56
|
});
|
|
58
57
|
}
|
|
59
58
|
|
|
60
|
-
wasShown(): void {
|
|
59
|
+
async wasShown(): Promise<void> {
|
|
60
|
+
await this.refreshAccessibilityTree();
|
|
61
61
|
if (this.inspectedDOMNode) {
|
|
62
|
-
this.loadSubTreeIntoAccessibilityModel(this.inspectedDOMNode);
|
|
63
|
-
} else {
|
|
64
|
-
this.accessibilityTreeComponent.expandRecursively(1);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
setAccessibilityModel(model: SDK.AccessibilityModel.AccessibilityModel|null): void {
|
|
69
|
-
this.accessibilityModel = model;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
wireToDOMModel(domModel: SDK.DOMModel.DOMModel): void {
|
|
73
|
-
if (!domModel.parentModel()) {
|
|
74
|
-
this.setAccessibilityModel(domModel.target().model(SDK.AccessibilityModel.AccessibilityModel));
|
|
62
|
+
await this.loadSubTreeIntoAccessibilityModel(this.inspectedDOMNode);
|
|
75
63
|
}
|
|
76
|
-
domModel.addEventListener(SDK.DOMModel.Events.NodeInserted, this.domUpdated, this);
|
|
77
|
-
domModel.addEventListener(SDK.DOMModel.Events.NodeRemoved, this.domUpdatedNode, this);
|
|
78
|
-
domModel.addEventListener(SDK.DOMModel.Events.AttrModified, this.domUpdatedNode, this);
|
|
79
|
-
domModel.addEventListener(SDK.DOMModel.Events.AttrRemoved, this.domUpdatedNode, this);
|
|
80
|
-
domModel.addEventListener(SDK.DOMModel.Events.CharacterDataModified, this.domUpdated, this);
|
|
81
|
-
domModel.addEventListener(SDK.DOMModel.Events.ChildNodeCountUpdated, this.domUpdated, this);
|
|
82
|
-
domModel.addEventListener(SDK.DOMModel.Events.DistributedNodesChanged, this.domUpdated, this);
|
|
83
|
-
domModel.addEventListener(SDK.DOMModel.Events.DocumentUpdated, this.documentUpdated, this);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
unwireFromDOMModel(domModel: SDK.DOMModel.DOMModel): void {
|
|
87
|
-
domModel.removeEventListener(SDK.DOMModel.Events.NodeInserted, this.domUpdated, this);
|
|
88
|
-
domModel.removeEventListener(SDK.DOMModel.Events.NodeRemoved, this.domUpdatedNode, this);
|
|
89
|
-
domModel.removeEventListener(SDK.DOMModel.Events.AttrModified, this.domUpdatedNode, this);
|
|
90
|
-
domModel.removeEventListener(SDK.DOMModel.Events.AttrRemoved, this.domUpdatedNode, this);
|
|
91
|
-
domModel.removeEventListener(SDK.DOMModel.Events.CharacterDataModified, this.domUpdated, this);
|
|
92
|
-
domModel.removeEventListener(SDK.DOMModel.Events.ChildNodeCountUpdated, this.domUpdated, this);
|
|
93
|
-
domModel.removeEventListener(SDK.DOMModel.Events.DistributedNodesChanged, this.domUpdated, this);
|
|
94
|
-
domModel.removeEventListener(SDK.DOMModel.Events.DocumentUpdated, this.documentUpdated, this);
|
|
95
64
|
}
|
|
96
65
|
|
|
97
|
-
|
|
98
|
-
this.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
await axModel?.requestAndLoadSubTreeToNode(node);
|
|
108
|
-
this.renderTree();
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
documentUpdated(event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMModel>): void {
|
|
112
|
-
const domModel = event.data;
|
|
113
|
-
const axModel = domModel.target().model(SDK.AccessibilityModel.AccessibilityModel);
|
|
114
|
-
if (domModel.existingDocument() && !domModel.parentModel() && axModel) {
|
|
115
|
-
this.refreshAccessibilityTree(axModel);
|
|
66
|
+
async refreshAccessibilityTree(): Promise<void> {
|
|
67
|
+
if (!this.root) {
|
|
68
|
+
const frameId = SDK.FrameManager.FrameManager.instance().getTopFrame()?.id;
|
|
69
|
+
if (!frameId) {
|
|
70
|
+
throw Error('No top frame');
|
|
71
|
+
}
|
|
72
|
+
this.root = await AccessibilityTreeUtils.getRootNode(frameId);
|
|
73
|
+
if (!this.root) {
|
|
74
|
+
throw Error('No root');
|
|
75
|
+
}
|
|
116
76
|
}
|
|
77
|
+
await this.renderTree();
|
|
78
|
+
await this.accessibilityTreeComponent.expandRecursively(1);
|
|
117
79
|
}
|
|
118
80
|
|
|
119
|
-
renderTree(): void {
|
|
120
|
-
if (!this.
|
|
121
|
-
|
|
122
|
-
} else {
|
|
123
|
-
this.treeData = [AccessibilityTreeUtils.sdkNodeToAXTreeNode(this.rootAXNode)];
|
|
81
|
+
async renderTree(): Promise<void> {
|
|
82
|
+
if (!this.root) {
|
|
83
|
+
return;
|
|
124
84
|
}
|
|
125
|
-
|
|
85
|
+
const treeData = await AccessibilityTreeUtils.sdkNodeToAXTreeNodes(this.root);
|
|
126
86
|
this.accessibilityTreeComponent.data = {
|
|
127
87
|
defaultRenderer: AccessibilityTreeUtils.accessibilityNodeRenderer,
|
|
128
|
-
tree:
|
|
88
|
+
tree: treeData,
|
|
89
|
+
filter: (node): TreeOutline.TreeOutline.FilterOption => {
|
|
90
|
+
return node.ignored() || (node.role()?.value === 'generic' && !node.name()?.value) ?
|
|
91
|
+
TreeOutline.TreeOutline.FilterOption.FLATTEN :
|
|
92
|
+
TreeOutline.TreeOutline.FilterOption.SHOW;
|
|
93
|
+
},
|
|
129
94
|
};
|
|
130
|
-
|
|
131
|
-
const axModel = this.inspectedDOMNode?.domModel().target().model(SDK.AccessibilityModel.AccessibilityModel);
|
|
132
|
-
const inspectedAXNode = axModel?.axNodeForDOMNode(this.inspectedDOMNode);
|
|
133
|
-
if (inspectedAXNode) {
|
|
134
|
-
this.selectedTreeNode = AccessibilityTreeUtils.sdkNodeToAXTreeNode(inspectedAXNode);
|
|
135
|
-
this.accessibilityTreeComponent.expandToAndSelectTreeNode(this.selectedTreeNode);
|
|
136
|
-
} else {
|
|
137
|
-
this.accessibilityTreeComponent.expandRecursively(1);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
async refreshAccessibilityTree(accessibilityModel: SDK.AccessibilityModel.AccessibilityModel): Promise<void> {
|
|
142
|
-
// We always expand the root node so we might as well fetch one level of children immediately.
|
|
143
|
-
const root = await accessibilityModel.requestRootNode();
|
|
144
|
-
if (!root) {
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
this.rootAXNode = root;
|
|
149
|
-
this.inspectedDOMNode = null;
|
|
150
|
-
this.renderTree();
|
|
151
95
|
}
|
|
152
96
|
|
|
153
97
|
// Given a selected DOM node, asks the model to load the missing subtree from the root to the
|
|
154
98
|
// selected node and then re-renders the tree.
|
|
155
99
|
async loadSubTreeIntoAccessibilityModel(selectedNode: SDK.DOMModel.DOMNode): Promise<void> {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// If this node has been loaded previously, the accessibility tree will return it's cached node.
|
|
161
|
-
// Eventually we'll need some mechanism for forcing it to fetch a new node when we are subscribing
|
|
162
|
-
// for updates, but TBD later.
|
|
163
|
-
// EG for a backend tree like:
|
|
164
|
-
//
|
|
165
|
-
// A*
|
|
166
|
-
// B
|
|
167
|
-
// C
|
|
168
|
-
// D
|
|
169
|
-
// E
|
|
170
|
-
// Where only A is already loaded into the model, calling requestAndLoadSubTreeToNode(C) will
|
|
171
|
-
// load [A, B, D, C] into the model, and return C.
|
|
172
|
-
await this.accessibilityModel.requestAndLoadSubTreeToNode(selectedNode);
|
|
173
|
-
const inspectedAXNode = this.accessibilityModel.axNodeForDOMNode(selectedNode);
|
|
100
|
+
const ancestors = await AccessibilityTreeUtils.getNodeAndAncestorsFromDOMNode(selectedNode);
|
|
101
|
+
const inspectedAXNode = ancestors.find(node => node.backendDOMNodeId() === selectedNode.backendNodeId());
|
|
174
102
|
if (!inspectedAXNode) {
|
|
175
103
|
return;
|
|
176
104
|
}
|
|
105
|
+
await this.accessibilityTreeComponent.expandNodeIds(ancestors.map(node => node.getFrameId() + '#' + node.id()));
|
|
106
|
+
await this.accessibilityTreeComponent.focusNodeId(AccessibilityTreeUtils.getNodeId(inspectedAXNode));
|
|
107
|
+
}
|
|
177
108
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
// These nodes require a special case, as they don't have an unignored node in the
|
|
184
|
-
// accessibility tree. Someone inspecting these in the DOM is probably expecting to
|
|
185
|
-
// be focused on the root WebArea of the accessibility tree.
|
|
186
|
-
// TODO(meredithl): Fix for when the inspected node is ingored in the accessibility
|
|
187
|
-
// tree. Eg, inspecting <head> in the DOM tree.
|
|
188
|
-
if (selectedNode.nodeName() === 'BODY' || selectedNode.nodeName() === 'HTML') {
|
|
189
|
-
this.accessibilityTreeComponent.expandToAndSelectTreeNode(this.treeData[0]);
|
|
190
|
-
this.selectedTreeNode = this.treeData[0];
|
|
109
|
+
// A node was revealed through the elements picker.
|
|
110
|
+
async revealAndSelectNode(inspectedNode: SDK.DOMModel.DOMNode): Promise<void> {
|
|
111
|
+
if (inspectedNode === this.inspectedDOMNode) {
|
|
191
112
|
return;
|
|
192
113
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
this.
|
|
114
|
+
this.inspectedDOMNode = inspectedNode;
|
|
115
|
+
// We only want to load nodes into the model when the AccessibilityTree is visible.
|
|
116
|
+
if (this.isShowing()) {
|
|
117
|
+
await this.loadSubTreeIntoAccessibilityModel(inspectedNode);
|
|
118
|
+
}
|
|
196
119
|
}
|
|
197
120
|
|
|
198
|
-
// Selected node in the DOM has changed
|
|
199
|
-
// unloaded.
|
|
121
|
+
// Selected node in the DOM tree has changed.
|
|
200
122
|
async selectedNodeChanged(inspectedNode: SDK.DOMModel.DOMNode): Promise<void> {
|
|
123
|
+
if (this.isShowing()) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (inspectedNode.ownerDocument && (inspectedNode.nodeName() === 'HTML' || inspectedNode.nodeName() === 'BODY')) {
|
|
127
|
+
inspectedNode = inspectedNode.ownerDocument;
|
|
128
|
+
}
|
|
201
129
|
if (inspectedNode === this.inspectedDOMNode) {
|
|
202
130
|
return;
|
|
203
131
|
}
|
|
204
132
|
this.inspectedDOMNode = inspectedNode;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
treeUpdated({data}: Common.EventTarget
|
|
136
|
+
.EventTargetEvent<SDK.AccessibilityModel.EventTypes[SDK.AccessibilityModel.Events.TreeUpdated]>):
|
|
137
|
+
void {
|
|
138
|
+
if (!data.root) {
|
|
139
|
+
this.renderTree();
|
|
140
|
+
return;
|
|
208
141
|
}
|
|
142
|
+
const topFrameId = SDK.FrameManager.FrameManager.instance().getTopFrame()?.id;
|
|
143
|
+
if (data.root?.getFrameId() !== topFrameId) {
|
|
144
|
+
this.renderTree();
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
this.root = data.root;
|
|
148
|
+
this.accessibilityTreeComponent.collapseAllNodes();
|
|
149
|
+
|
|
150
|
+
this.refreshAccessibilityTree();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
modelAdded(model: SDK.AccessibilityModel.AccessibilityModel): void {
|
|
154
|
+
model.addEventListener(SDK.AccessibilityModel.Events.TreeUpdated, this.treeUpdated, this);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
modelRemoved(model: SDK.AccessibilityModel.AccessibilityModel): void {
|
|
158
|
+
model.removeEventListener(SDK.AccessibilityModel.Events.TreeUpdated, this.treeUpdated, this);
|
|
209
159
|
}
|
|
210
160
|
}
|
|
@@ -328,11 +328,6 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
|
|
|
328
328
|
modelAdded(domModel: SDK.DOMModel.DOMModel): void {
|
|
329
329
|
const parentModel = domModel.parentModel();
|
|
330
330
|
|
|
331
|
-
// Different frames will have different DOMModels, we only want to add the accessibility model
|
|
332
|
-
// for the top level frame, as the accessibility tree does not yet support exploring IFrames.
|
|
333
|
-
if (this.accessibilityTreeView) {
|
|
334
|
-
this.accessibilityTreeView.wireToDOMModel(domModel);
|
|
335
|
-
}
|
|
336
331
|
let treeOutline: ElementsTreeOutline|null = parentModel ? ElementsTreeOutline.forDOMModel(parentModel) : null;
|
|
337
332
|
if (!treeOutline) {
|
|
338
333
|
treeOutline = new ElementsTreeOutline(true, true);
|
|
@@ -507,6 +502,10 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
|
|
|
507
502
|
crumbs,
|
|
508
503
|
selectedNode: ElementsComponents.Helper.legacyNodeToElementsComponentsNode(selectedNode),
|
|
509
504
|
};
|
|
505
|
+
|
|
506
|
+
if (this.accessibilityTreeView) {
|
|
507
|
+
this.accessibilityTreeView.selectedNodeChanged(selectedNode);
|
|
508
|
+
}
|
|
510
509
|
} else {
|
|
511
510
|
this.breadcrumbs.data = {crumbs: [], selectedNode: null};
|
|
512
511
|
}
|
|
@@ -837,7 +836,7 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
|
|
|
837
836
|
if (!treeOutline) {
|
|
838
837
|
return null;
|
|
839
838
|
}
|
|
840
|
-
return
|
|
839
|
+
return treeOutline.findTreeElement(node);
|
|
841
840
|
}
|
|
842
841
|
|
|
843
842
|
private leaveUserAgentShadowDOM(node: SDK.DOMModel.DOMNode): SDK.DOMModel.DOMNode {
|
|
@@ -859,7 +858,7 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
|
|
|
859
858
|
}
|
|
860
859
|
|
|
861
860
|
if (this.accessibilityTreeView) {
|
|
862
|
-
this.accessibilityTreeView.
|
|
861
|
+
this.accessibilityTreeView.revealAndSelectNode(node);
|
|
863
862
|
}
|
|
864
863
|
|
|
865
864
|
await UI.ViewManager.ViewManager.instance().showView('elements', false, !focus);
|
package/package.json
CHANGED