Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(228)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/accessibility/AXTreePane.js

Issue 2390783006: [DevTools] Accessibility: Show siblings and children of selected node (Closed)
Patch Set: UI feedback from Chris Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 /** 4 /**
5 * @unrestricted 5 * @unrestricted
6 */ 6 */
7 WebInspector.AXTreePane = class extends WebInspector.AccessibilitySubPane { 7 WebInspector.AXTreePane = class extends WebInspector.AccessibilitySubPane {
8 constructor() { 8 constructor() {
9 super(WebInspector.UIString('Accessibility Tree')); 9 super(WebInspector.UIString('Accessibility Tree'));
10 10
11 this._treeOutline = this.createTreeOutline(); 11 this._treeOutline = this.createTreeOutline();
12 12
13 this.element.classList.add('accessibility-computed'); 13 this.element.classList.add('accessibility-computed');
14
15 this._expandedNodes = new Set();
14 } 16 }
15 17
16 /** 18 /**
17 * @param {!Array<!WebInspector.AccessibilityNode>} nodes 19 * @param {?WebInspector.AccessibilityNode} axNode
20 * @override
18 */ 21 */
19 setAXNodeAndAncestors(nodes) { 22 setAXNode(axNode) {
20 this._nodes = nodes; 23 this._axNode = axNode;
21 24
22 var target = this.node().target();
23 var treeOutline = this._treeOutline; 25 var treeOutline = this._treeOutline;
24 treeOutline.removeChildren(); 26 treeOutline.removeChildren();
27
28 // TODO(aboxhall): show no node UI
29 if (!axNode)
30 return;
31
25 treeOutline.element.classList.remove('hidden'); 32 treeOutline.element.classList.remove('hidden');
26 var previous = treeOutline.rootElement(); 33 var previousTreeElement = treeOutline.rootElement();
27 while (nodes.length) { 34 var inspectedNodeTreeElement = new WebInspector.AXNodeTreeElement(axNode, th is);
28 var ancestor = nodes.pop(); 35 inspectedNodeTreeElement.setInspected(true);
29 var ancestorTreeElement = new WebInspector.AXNodeTreeElement(ancestor, tar get); 36
30 previous.appendChild(ancestorTreeElement); 37 var parent = axNode.parentNode();
31 previous.expand(); 38 if (parent) {
32 previous = ancestorTreeElement; 39 this.setExpanded(parent.domNode(), false);
40
41 var chain = [];
42 var ancestor = parent.parentNode();
43 while (ancestor) {
44 chain.unshift(ancestor);
45 ancestor = ancestor.parentNode();
46 }
47 for (var ancestorNode of chain) {
48 var ancestorTreeElement = new WebInspector.AXNodeTreeElement(ancestorNod e, this);
49 previousTreeElement.appendChild(ancestorTreeElement);
50 previousTreeElement.expand();
51 previousTreeElement = ancestorTreeElement;
52 }
53 var parentTreeElement = new WebInspector.AXNodeTreeParentElement(parent, i nspectedNodeTreeElement, this);
54 if (this.isExpanded(parent.domNode()))
55 parentTreeElement.appendSiblings();
56 else
57 parentTreeElement.appendChild(inspectedNodeTreeElement);
58 previousTreeElement.appendChild(parentTreeElement);
59 previousTreeElement.expand();
60 previousTreeElement = parentTreeElement;
61 } else {
62 previousTreeElement.appendChild(inspectedNodeTreeElement);
33 } 63 }
34 previous.selectable = true; 64
35 previous.select(true /* omitFocus */); 65 previousTreeElement.expand();
66
67 for (var child of axNode.children()) {
68 var childTreeElement = new WebInspector.AXNodeTreeElement(child, this);
69 inspectedNodeTreeElement.appendChild(childTreeElement);
70 }
71
72 inspectedNodeTreeElement.selectable = true;
73 inspectedNodeTreeElement.select(!this._selectedByUser /* omitFocus */, false );
74 if (this.isExpanded(axNode.domNode()))
75 inspectedNodeTreeElement.expand();
76 this.clearSelectedByUser();
77 }
78
79 /**
80 * @param {boolean} selectedByUser
81 */
82 setSelectedByUser(selectedByUser) {
83 this._selectedByUser = true;
84 }
85
86 clearSelectedByUser() {
87 delete this._selectedByUser;
88 }
89
90 /**
91 * @return {!WebInspector.Target}
92 */
93 target() {
94 return this.node().target();
95 }
96
97 /**
98 * @param {?WebInspector.DOMNode} domNode
99 * @param {boolean} expanded
100 */
101 setExpanded(domNode, expanded) {
102 if (!domNode)
103 return;
104 if (expanded)
105 this._expandedNodes.add(domNode.id);
106 else
107 this._expandedNodes.delete(domNode.id);
108 }
109
110 /**
111 * @param {?WebInspector.DOMNode} domNode
112 * @return {boolean}
113 */
114 isExpanded(domNode) {
115 if (!domNode)
116 return false;
117
118 return this._expandedNodes.has(domNode.id);
36 } 119 }
37 }; 120 };
38 121
122 WebInspector.InspectNodeButton = class {
123 /**
124 * @param {!WebInspector.AccessibilityNode} axNode
125 * @param {!WebInspector.AXTreePane} treePane
126 */
127 constructor(axNode, treePane) {
128 this._axNode = axNode;
129 this._treePane = treePane;
130
131 this.element = createElementWithClass('button', 'inspect-dom-node');
132 this.element.addEventListener('mousedown', this._handleMouseDown.bind(this)) ;
133 }
134
135 /**
136 * @param {!Event} event
137 */
138 _handleMouseDown(event) {
139 this._treePane.setSelectedByUser(true);
140 WebInspector.Revealer.reveal(this._axNode.domNode());
141 }
142 };
143
39 /** 144 /**
40 * @unrestricted 145 * @unrestricted
41 */ 146 */
42 WebInspector.AXNodeTreeElement = class extends TreeElement { 147 WebInspector.AXNodeTreeElement = class extends TreeElement {
43 /** 148 /**
44 * @param {!WebInspector.AccessibilityNode} axNode 149 * @param {!WebInspector.AccessibilityNode} axNode
45 * @param {!WebInspector.Target} target 150 * @param {!WebInspector.AXTreePane} treePane
46 */ 151 */
47 constructor(axNode, target) { 152 constructor(axNode, treePane) {
48 // Pass an empty title, the title gets made later in onattach. 153 // Pass an empty title, the title gets made later in onattach.
49 super(''); 154 super('');
50 155
51 /** @type {!WebInspector.AccessibilityNode} */ 156 /** @type {!WebInspector.AccessibilityNode} */
52 this._axNode = axNode; 157 this._axNode = axNode;
53 158
54 /** @type {!WebInspector.Target} */ 159 /** @type {!WebInspector.AXTreePane} */
55 this._target = target; 160 this._treePane = treePane;
56 161
57 this.selectable = false; 162 this.selectable = true;
163
164 this._inspectNodeButton =
165 new WebInspector.InspectNodeButton(axNode, treePane);
58 } 166 }
59 167
60 /** 168 /**
169 * @return {!WebInspector.AccessibilityNode}
170 */
171 axNode() {
172 return this._axNode;
173 }
174
175 /**
176 * @param {boolean} inspected
177 */
178 setInspected(inspected) {
179 if (inspected) {
180 this._inspected = true;
181 this.listItemElement.classList.add('inspected');
dgozman 2016/11/03 22:07:29 this._inspected = inspected. this.listItemElement.
aboxhall 2016/11/04 22:31:17 Done.
182 } else {
183 this._inspected = false;
184 this.listItemElement.classList.remove('inspected');
185 }
186 }
187
188 /**
189 * @override
190 * @return {boolean}
191 */
192 onenter() {
193 this.inspectDOMNode();
194 return true;
195 }
196
197 /**
198 * @override
199 * @param {!Event} event
200 * @return {boolean}
201 */
202 ondblclick(event) {
203 this.inspectDOMNode();
204 return true;
205 }
206
207 inspectDOMNode() {
208 this._treePane.setSelectedByUser(true);
209 WebInspector.Revealer.reveal(this._axNode.domNode());
dgozman 2016/11/03 22:07:29 Note that we can reveal DeferredDOMNode. And for c
aboxhall 2016/11/04 22:31:17 Done.
210 }
211
212 /**
61 * @override 213 * @override
62 */ 214 */
63 onattach() { 215 onattach() {
64 this._update(); 216 this._update();
65 } 217 }
66 218
67 _update() { 219 _update() {
68 this.listItemElement.removeChildren(); 220 this.listItemElement.removeChildren();
69 221
70 if (this._axNode.ignored()) { 222 if (this._axNode.ignored()) {
71 this._appendIgnoredNodeElement(); 223 this._appendIgnoredNodeElement();
72 } else { 224 } else {
73 this._appendRoleElement(this._axNode.role()); 225 this._appendRoleElement(this._axNode.role());
74 if ('name' in this._axNode && this._axNode.name().value) { 226 if (this._axNode && this._axNode.name().value) {
75 this.listItemElement.createChild('span', 'separator').textContent = '\u0 0A0'; 227 this.listItemElement.createChild('span', 'separator').textContent = '\u0 0A0';
76 this._appendNameElement(/** @type {string} */ (this._axNode.name().value )); 228 this._appendNameElement(/** @type {string} */ (this._axNode.name().value ));
77 } 229 }
78 } 230 }
231
232 if (this._axNode.hasOnlyUnloadedChildren()) {
233 this._hasOnlyUnloadedChildren = true;
234 this.listItemElement.classList.add('children-unloaded');
235 this.setExpandable(true);
236 } else {
237 this.setExpandable(!!this._axNode.numChildren());
238 }
239
240 if (!this._axNode.domNode())
241 this.listItemElement.classList.add('no-dom-node');
242 this.listItemElement.appendChild(this._inspectNodeButton.element);
79 } 243 }
80 244
81 /** 245 /**
246 * @override
247 */
248 expand() {
249 if (this._hasOnlyUnloadedChildren)
250 return;
251
252 this._treePane.setExpanded(this._axNode.domNode(), true);
253 super.expand();
254 }
255
256 /**
257 * @override
258 */
259 collapse() {
260 if (this._hasOnlyUnloadedChildren)
261 return;
262
263 if (this._treePane)
264 this._treePane.setExpanded(this._axNode.domNode(), false);
265 super.collapse();
266 }
267
268 /**
82 * @param {string} name 269 * @param {string} name
83 */ 270 */
84 _appendNameElement(name) { 271 _appendNameElement(name) {
85 var nameElement = createElement('span'); 272 var nameElement = createElement('span');
86 nameElement.textContent = '"' + name + '"'; 273 nameElement.textContent = '"' + name + '"';
87 nameElement.classList.add('ax-readable-string'); 274 nameElement.classList.add('ax-readable-string');
88 this.listItemElement.appendChild(nameElement); 275 this.listItemElement.appendChild(nameElement);
89 } 276 }
90 277
91 /** 278 /**
92 * @param {?AccessibilityAgent.AXValue} role 279 * @param {?AccessibilityAgent.AXValue} role
93 */ 280 */
94 _appendRoleElement(role) { 281 _appendRoleElement(role) {
95 if (!role) 282 if (!role)
96 return; 283 return;
97 284
98 var roleElement = createElementWithClass('span', 'monospace'); 285 var roleElement = createElementWithClass('span', 'monospace');
99 roleElement.classList.add(WebInspector.AXNodeTreeElement.RoleStyles[role.typ e]); 286 roleElement.classList.add(WebInspector.AXNodeTreeElement.RoleStyles[role.typ e]);
100 roleElement.setTextContentTruncatedIfNeeded(role.value || ''); 287 roleElement.setTextContentTruncatedIfNeeded(role.value || '');
101 288
102 this.listItemElement.appendChild(roleElement); 289 this.listItemElement.appendChild(roleElement);
103 } 290 }
104 291
105 _appendIgnoredNodeElement() { 292 _appendIgnoredNodeElement() {
106 var ignoredNodeElement = createElementWithClass('span', 'monospace'); 293 var ignoredNodeElement = createElementWithClass('span', 'monospace');
107 ignoredNodeElement.textContent = WebInspector.UIString('Ignored'); 294 ignoredNodeElement.textContent = WebInspector.UIString('Ignored');
108 ignoredNodeElement.classList.add('ax-tree-ignored-node'); 295 ignoredNodeElement.classList.add('ax-tree-ignored-node');
109 this.listItemElement.appendChild(ignoredNodeElement); 296 this.listItemElement.appendChild(ignoredNodeElement);
110 } 297 }
298
299 /**
300 * @param {boolean=} omitFocus
301 * @param {boolean=} selectedByUser
302 * @return {boolean}
303 * @override
304 */
305 select(omitFocus, selectedByUser) {
306 this._ensureSelection();
307
308 this._treePane.setSelectedByUser(!!selectedByUser);
309
310 return super.select(omitFocus, selectedByUser);
311 }
111 }; 312 };
112 313
113 /** @type {!Object<string, string>} */ 314 /** @type {!Object<string, string>} */
114 WebInspector.AXNodeTreeElement.RoleStyles = { 315 WebInspector.AXNodeTreeElement.RoleStyles = {
115 internalRole: 'ax-internal-role', 316 internalRole: 'ax-internal-role',
116 role: 'ax-role', 317 role: 'ax-role',
117 }; 318 };
319
320 /**
321 * @unrestricted
322 */
323 WebInspector.ExpandSiblingsButton = class {
324 /**
325 * @param {!WebInspector.AXNodeTreeParentElement} treeElement
326 * @param {number} numSiblings
327 */
328 constructor(treeElement, numSiblings) {
329 this._treeElement = treeElement;
330
331 this.element = createElementWithClass('button', 'expand-siblings');
332 this.element.textContent = WebInspector.UIString(
333 '+ %d %s', numSiblings, numSiblings === 1 ? 'node' : 'nodes');
334 this.element.addEventListener('mousedown', this._handleMouseDown.bind(this)) ;
335 }
336
337 /**
338 * @param {!Event} event
339 */
340 _handleMouseDown(event) {
341 this._treeElement.expandSiblings();
342 event.consume();
343 }
344 };
345
346 /**
347 * @unrestricted
348 */
349 WebInspector.AXNodeTreeParentElement = class extends WebInspector.AXNodeTreeElem ent {
350 /**
351 * @param {!WebInspector.AccessibilityNode} axNode
352 * @param {!WebInspector.AXNodeTreeElement} inspectedNodeTreeElement
353 * @param {!WebInspector.AXTreePane} treePane
354 */
355 constructor(axNode, inspectedNodeTreeElement, treePane) {
356 super(axNode, treePane);
357
358 this._inspectedNodeTreeElement = inspectedNodeTreeElement;
359 var numSiblings = axNode.children().length - 1;
360 this._expandSibingsButton = new WebInspector.ExpandSiblingsButton(this, numS iblings);
dgozman 2016/11/03 22:07:29 typo: Sibings
aboxhall 2016/11/04 22:31:17 Done.
361 this._partiallyExpanded = false;
362 }
363
364 /**
365 * @override
366 */
367 onattach() {
368 super.onattach();
369 if (this._treePane.isExpanded(this._axNode.domNode()))
370 this._listItemNode.classList.add('siblings-expanded');
371 if (this._axNode.numChildren() > 1) {
372 this._listItemNode.insertBefore(this._expandSibingsButton.element,
373 this._inspectNodeButton.element);
374 }
375 }
376
377 /**
378 * @param {boolean} altKey
379 * @return {boolean}
380 * @override
381 */
382 descendOrExpand(altKey) {
383 if (!this.expanded || !this._partiallyExpanded)
384 return TreeElement.prototype.descendOrExpand.call(this, altKey);
385
386 this.expandSiblings();
387 if (altKey)
388 this.expandRecursively();
389 return true;
390 }
391
392 /**
393 * @override
394 */
395 expand() {
396 TreeElement.prototype.expand.call(this);
397 this._partiallyExpanded = true;
398 }
399
400 expandSiblings() {
401 this._listItemNode.classList.add('siblings-expanded');
402 this.appendSiblings();
403 this.expanded = true;
404 this._partiallyExpanded = false;
405 this._treePane.setExpanded(this._axNode.domNode(), true);
406 }
407
408 appendSiblings() {
409 var inspectedAXNode = this._inspectedNodeTreeElement.axNode();
410 var nextIndex = 0;
411 var foundInspectedNode = false;
412 for (var sibling of this._axNode.children()) {
413 var siblingTreeElement = null;
414 if (sibling === inspectedAXNode) {
415 foundInspectedNode = true;
416 continue;
417 }
418 siblingTreeElement = new WebInspector.AXNodeTreeElement(sibling, this._tre ePane);
419 if (foundInspectedNode)
420 this.appendChild(siblingTreeElement);
421 else
422 this.insertChild(siblingTreeElement, nextIndex++);
423 }
424 }
425 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698