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

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

Issue 2967443002: [DevTools] Add ARIA semantics for AXBreadcrumbs (Closed)
Patch Set: Created 3 years, 5 months 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 Accessibility.AXBreadcrumbsPane = class extends Accessibility.AccessibilitySubPa ne { 5 Accessibility.AXBreadcrumbsPane = class extends Accessibility.AccessibilitySubPa ne {
6 /** 6 /**
7 * @param {!Accessibility.AccessibilitySidebarView} axSidebarView 7 * @param {!Accessibility.AccessibilitySidebarView} axSidebarView
8 */ 8 */
9 constructor(axSidebarView) { 9 constructor(axSidebarView) {
10 super(Common.UIString('Accessibility Tree')); 10 super(Common.UIString('Accessibility Tree'));
11 11
12 this.element.classList.add('ax-subpane'); 12 this.element.classList.add('ax-subpane');
13 UI.ARIAUtils.markAsTree(this.element);
13 14
14 this._axSidebarView = axSidebarView; 15 this._axSidebarView = axSidebarView;
15 16
16 /** @type {?Accessibility.AXBreadcrumb} */ 17 /** @type {?Accessibility.AXBreadcrumb} */
17 this._preselectedBreadcrumb = null; 18 this._preselectedBreadcrumb = null;
18 19
19 this._selectedByUser = true; 20 this._selectedByUser = true;
20 21
21 this._hoveredBreadcrumb = null; 22 this._hoveredBreadcrumb = null;
22 this._rootElement = this.element.createChild('div', 'ax-breadcrumbs'); 23 this._rootElement = this.element.createChild('div', 'ax-breadcrumbs');
(...skipping 23 matching lines...) Expand all
46 var ancestorChain = []; 47 var ancestorChain = [];
47 var ancestor = axNode; 48 var ancestor = axNode;
48 while (ancestor) { 49 while (ancestor) {
49 ancestorChain.push(ancestor); 50 ancestorChain.push(ancestor);
50 ancestor = ancestor.parentNode(); 51 ancestor = ancestor.parentNode();
51 } 52 }
52 ancestorChain.reverse(); 53 ancestorChain.reverse();
53 54
54 var depth = 0; 55 var depth = 0;
55 var breadcrumb = null; 56 var breadcrumb = null;
57 var parent = null;
56 for (ancestor of ancestorChain) { 58 for (ancestor of ancestorChain) {
57 breadcrumb = new Accessibility.AXBreadcrumb(ancestor, depth, (ancestor === axNode)); 59 breadcrumb = new Accessibility.AXBreadcrumb(ancestor, depth, (ancestor === axNode));
58 if (ancestor.children().length) 60 if (parent)
59 breadcrumb.element().classList.add('parent'); 61 parent.appendChild(breadcrumb);
60 this._rootElement.appendChild(breadcrumb.element()); 62 else
63 this._rootElement.appendChild(breadcrumb.element());
64 parent = breadcrumb;
61 depth++; 65 depth++;
62 } 66 }
63 67
64 var inspectedNodeBreadcrumb = breadcrumb; 68 var inspectedNodeBreadcrumb = breadcrumb;
65 inspectedNodeBreadcrumb.setPreselected(true, this._selectedByUser); 69 inspectedNodeBreadcrumb.setPreselected(true, this._selectedByUser);
66 70
67 this._setPreselectedBreadcrumb(inspectedNodeBreadcrumb); 71 this._setPreselectedBreadcrumb(inspectedNodeBreadcrumb);
68 72
69 for (var child of axNode.children()) { 73 for (var child of axNode.children()) {
70 var childBreadcrumb = new Accessibility.AXBreadcrumb(child, depth, false); 74 var childBreadcrumb = new Accessibility.AXBreadcrumb(child, depth, false);
71 this._rootElement.appendChild(childBreadcrumb.element()); 75 inspectedNodeBreadcrumb.appendChild(childBreadcrumb);
72 } 76 }
73 77
74 this._selectedByUser = false; 78 this._selectedByUser = false;
75 } 79 }
76 80
77 /** 81 /**
78 * @override 82 * @override
79 */ 83 */
80 wasShown() { 84 wasShown() {
81 this._selectedByUser = true; 85 this._selectedByUser = true;
(...skipping 26 matching lines...) Expand all
108 handled = this._inspectDOMNode(this._preselectedBreadcrumb.axNode()); 112 handled = this._inspectDOMNode(this._preselectedBreadcrumb.axNode());
109 113
110 if (handled) 114 if (handled)
111 event.consume(true); 115 event.consume(true);
112 } 116 }
113 117
114 /** 118 /**
115 * @return {boolean} 119 * @return {boolean}
116 */ 120 */
117 _preselectPrevious() { 121 _preselectPrevious() {
118 var previousElement = this._preselectedBreadcrumb.element().previousSibling; 122 var previousBreadcrumb = this._preselectedBreadcrumb.previousBreadcrumb();
119 if (!previousElement) 123 if (!previousBreadcrumb)
120 return false; 124 return false;
121 this._selectedByUser = true; 125 this._selectedByUser = true;
122 this._setPreselectedBreadcrumb(previousElement.breadcrumb); 126 this._setPreselectedBreadcrumb(previousBreadcrumb);
123 return true; 127 return true;
124 } 128 }
125 129
126 /** 130 /**
127 * @return {boolean} 131 * @return {boolean}
128 */ 132 */
129 _preselectNext() { 133 _preselectNext() {
130 var nextElement = this._preselectedBreadcrumb.element().nextSibling; 134 var nextBreadcrumb = this._preselectedBreadcrumb.nextBreadcrumb();
131 if (!nextElement) 135 if (!nextBreadcrumb)
132 return false; 136 return false;
133 this._selectedByUser = true; 137 this._selectedByUser = true;
134 this._setPreselectedBreadcrumb(nextElement.breadcrumb); 138 this._setPreselectedBreadcrumb(nextBreadcrumb);
135 return true; 139 return true;
136 } 140 }
137 141
138 /** 142 /**
139 * @param {?Accessibility.AXBreadcrumb} breadcrumb 143 * @param {?Accessibility.AXBreadcrumb} breadcrumb
140 */ 144 */
141 _setPreselectedBreadcrumb(breadcrumb) { 145 _setPreselectedBreadcrumb(breadcrumb) {
142 if (breadcrumb === this._preselectedBreadcrumb) 146 if (breadcrumb === this._preselectedBreadcrumb)
143 return; 147 return;
144 if (this._preselectedBreadcrumb) 148 if (this._preselectedBreadcrumb)
145 this._preselectedBreadcrumb.setPreselected(false, this._selectedByUser); 149 this._preselectedBreadcrumb.setPreselected(false, this._selectedByUser);
146 this._preselectedBreadcrumb = breadcrumb; 150 this._preselectedBreadcrumb = breadcrumb;
147 if (this._preselectedBreadcrumb) 151 if (this._preselectedBreadcrumb)
148 this._preselectedBreadcrumb.setPreselected(true, this._selectedByUser); 152 this._preselectedBreadcrumb.setPreselected(true, this._selectedByUser);
149 else if (this._selectedByUser) 153 else if (this._selectedByUser)
150 SDK.OverlayModel.hideDOMNodeHighlight(); 154 SDK.OverlayModel.hideDOMNodeHighlight();
151 } 155 }
152 156
153 /** 157 /**
154 * @param {!Event} event 158 * @param {!Event} event
155 */ 159 */
156 _onMouseLeave(event) { 160 _onMouseLeave(event) {
157 this._setHoveredBreadcrumb(null); 161 this._setHoveredBreadcrumb(null);
158 } 162 }
159 163
160 /** 164 /**
161 * @param {!Event} event 165 * @param {!Event} event
162 */ 166 */
163 _onMouseMove(event) { 167 _onMouseMove(event) {
164 var breadcrumbElement = event.target.enclosingNodeOrSelfWithClass('ax-node') ; 168 var breadcrumbElement = event.target.enclosingNodeOrSelfWithClass('ax-breadc rumb');
165 if (!breadcrumbElement) { 169 if (!breadcrumbElement) {
166 this._setHoveredBreadcrumb(null); 170 this._setHoveredBreadcrumb(null);
167 return; 171 return;
168 } 172 }
169 var breadcrumb = breadcrumbElement.breadcrumb; 173 var breadcrumb = breadcrumbElement.breadcrumb;
170 if (breadcrumb.preselected() || breadcrumb.inspected() || !breadcrumb.isDOMN ode()) 174 if (breadcrumb.preselected() || breadcrumb.inspected() || !breadcrumb.isDOMN ode())
171 return; 175 return;
172 this._setHoveredBreadcrumb(breadcrumb); 176 this._setHoveredBreadcrumb(breadcrumb);
173 } 177 }
174 178
175 /** 179 /**
176 * @param {!Event} event 180 * @param {!Event} event
177 */ 181 */
178 _onClick(event) { 182 _onClick(event) {
179 var breadcrumbElement = event.target.enclosingNodeOrSelfWithClass('ax-node') ; 183 var breadcrumbElement = event.target.enclosingNodeOrSelfWithClass('ax-breadc rumb');
180 if (!breadcrumbElement) { 184 if (!breadcrumbElement) {
181 this._setHoveredBreadcrumb(null); 185 this._setHoveredBreadcrumb(null);
182 return; 186 return;
183 } 187 }
184 var breadcrumb = breadcrumbElement.breadcrumb; 188 var breadcrumb = breadcrumbElement.breadcrumb;
185 if (breadcrumb.inspected()) { 189 if (breadcrumb.inspected()) {
186 // If the user is clicking the inspected breadcrumb, they probably want to 190 // If the user is clicking the inspected breadcrumb, they probably want to
187 // focus it. 191 // focus it.
188 breadcrumb.element().focus(); 192 breadcrumb.element().focus();
189 return; 193 return;
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
242 Accessibility.AXBreadcrumb = class { 246 Accessibility.AXBreadcrumb = class {
243 /** 247 /**
244 * @param {!Accessibility.AccessibilityNode} axNode 248 * @param {!Accessibility.AccessibilityNode} axNode
245 * @param {number} depth 249 * @param {number} depth
246 * @param {boolean} inspected 250 * @param {boolean} inspected
247 */ 251 */
248 constructor(axNode, depth, inspected) { 252 constructor(axNode, depth, inspected) {
249 /** @type {!Accessibility.AccessibilityNode} */ 253 /** @type {!Accessibility.AccessibilityNode} */
250 this._axNode = axNode; 254 this._axNode = axNode;
251 255
252 this._element = createElementWithClass('div', 'ax-node'); 256 this._element = createElementWithClass('div', 'ax-breadcrumb');
257 UI.ARIAUtils.markAsTreeitem(this._element);
253 this._element.breadcrumb = this; 258 this._element.breadcrumb = this;
254 259
260 this._nodeElement = createElementWithClass('div', 'ax-node');
261 this._element.appendChild(this._nodeElement);
262 this._nodeWrapper = createElementWithClass('div', 'wrapper');
263 this._nodeElement.appendChild(this._nodeWrapper);
264
255 this._selectionElement = createElementWithClass('div', 'selection fill'); 265 this._selectionElement = createElementWithClass('div', 'selection fill');
256 this._element.appendChild(this._selectionElement); 266 this._nodeElement.appendChild(this._selectionElement);
257 267
258 this._nodeWrapper = createElementWithClass('span', 'wrapper'); 268 this._childrenGroupElement = createElementWithClass('div', 'children');
259 this._element.appendChild(this._nodeWrapper); 269 UI.ARIAUtils.markAsGroup(this._childrenGroupElement);
270 this._element.appendChild(this._childrenGroupElement);
260 271
261 this._hovered = false; 272 this._hovered = false;
262 this._preselected = false; 273 this._preselected = false;
274 this._children = [];
lushnikov 2017/07/06 15:49:09 let's add jsdoc for this
aboxhall 2017/07/06 22:14:14 Done.
275 this._parent = null;
263 276
264 this._inspected = inspected; 277 this._inspected = inspected;
265 this.element().classList.toggle('inspected', inspected); 278 this._nodeElement.classList.toggle('inspected', inspected);
266 279
267 this._element.style.paddingLeft = (16 * depth + 4) + 'px'; 280 this._nodeElement.style.paddingLeft = (16 * depth + 4) + 'px';
268 281
269 if (this._axNode.ignored()) { 282 if (this._axNode.ignored()) {
270 this._appendIgnoredNodeElement(); 283 this._appendIgnoredNodeElement();
271 } else { 284 } else {
272 this._appendRoleElement(this._axNode.role()); 285 this._appendRoleElement(this._axNode.role());
273 if (this._axNode.name() && this._axNode.name().value) { 286 if (this._axNode.name() && this._axNode.name().value) {
274 this._nodeWrapper.createChild('span', 'separator').textContent = '\u00A0 '; 287 this._nodeWrapper.createChild('span', 'separator').textContent = '\u00A0 ';
275 this._appendNameElement(/** @type {string} */ (this._axNode.name().value )); 288 this._appendNameElement(/** @type {string} */ (this._axNode.name().value ));
276 } 289 }
277 } 290 }
278 291
279 if (this._axNode.hasOnlyUnloadedChildren()) 292 if (this._axNode.hasOnlyUnloadedChildren())
280 this._element.classList.add('children-unloaded'); 293 this._nodeElement.classList.add('children-unloaded');
281 294
282 if (!this._axNode.isDOMNode()) 295 if (!this._axNode.isDOMNode())
283 this._element.classList.add('no-dom-node'); 296 this._nodeElement.classList.add('no-dom-node');
284 } 297 }
285 298
286 /** 299 /**
287 * @return {!Element} 300 * @return {!Element}
288 */ 301 */
289 element() { 302 element() {
290 return this._element; 303 return this._element;
291 } 304 }
292 305
293 /** 306 /**
307 * @param {!Accessibility.AXBreadcrumb} breadcrumb
308 */
309 appendChild(breadcrumb) {
310 this._children.push(breadcrumb);
311 breadcrumb.setParent(this);
312 this._nodeElement.classList.add('parent');
313 UI.ARIAUtils.setExpanded(this._element, true);
314 this._childrenGroupElement.appendChild(breadcrumb.element());
315 }
316
317 /**
318 * @param {!Accessibility.AXBreadcrumb} breadcrumb
319 */
320 setParent(breadcrumb) {
321 this._parent = breadcrumb;
322 }
323
324 /**
294 * @return {boolean} 325 * @return {boolean}
295 */ 326 */
296 preselected() { 327 preselected() {
297 return this._preselected; 328 return this._preselected;
298 } 329 }
299 330
300 /** 331 /**
301 * @param {boolean} preselected 332 * @param {boolean} preselected
302 * @param {boolean} selectedByUser 333 * @param {boolean} selectedByUser
303 */ 334 */
304 setPreselected(preselected, selectedByUser) { 335 setPreselected(preselected, selectedByUser) {
305 if (this._preselected === preselected) 336 if (this._preselected === preselected)
306 return; 337 return;
307 this._preselected = preselected; 338 this._preselected = preselected;
308 this.element().classList.toggle('preselected', preselected); 339 this._nodeElement.classList.toggle('preselected', preselected);
309 if (preselected) 340 if (preselected)
310 this.element().setAttribute('tabIndex', 0); 341 this._nodeElement.setAttribute('tabIndex', 0);
311 else 342 else
312 this.element().removeAttribute('tabIndex'); 343 this._nodeElement.removeAttribute('tabIndex');
313 if (this._preselected) { 344 if (this._preselected) {
314 if (selectedByUser) 345 if (selectedByUser)
315 this.element().focus(); 346 this._nodeElement.focus();
316 if (!this._inspected) 347 if (!this._inspected)
317 this._axNode.highlightDOMNode(); 348 this._axNode.highlightDOMNode();
318 else 349 else
319 SDK.OverlayModel.hideDOMNodeHighlight(); 350 SDK.OverlayModel.hideDOMNodeHighlight();
320 } 351 }
321 } 352 }
322 353
323 /** 354 /**
324 * @param {boolean} hovered 355 * @param {boolean} hovered
325 */ 356 */
326 setHovered(hovered) { 357 setHovered(hovered) {
327 if (this._hovered === hovered) 358 if (this._hovered === hovered)
328 return; 359 return;
329 this._hovered = hovered; 360 this._hovered = hovered;
330 this.element().classList.toggle('hovered', hovered); 361 this._nodeElement.classList.toggle('hovered', hovered);
331 if (this._hovered) { 362 if (this._hovered) {
332 this.element().classList.toggle('hovered', true); 363 this._nodeElement.classList.toggle('hovered', true);
333 this._axNode.highlightDOMNode(); 364 this._axNode.highlightDOMNode();
334 } 365 }
335 } 366 }
336 367
337 /** 368 /**
338 * @return {!Accessibility.AccessibilityNode} 369 * @return {!Accessibility.AccessibilityNode}
339 */ 370 */
340 axNode() { 371 axNode() {
341 return this._axNode; 372 return this._axNode;
342 } 373 }
343 374
344 /** 375 /**
345 * @return {boolean} 376 * @return {boolean}
346 */ 377 */
347 inspected() { 378 inspected() {
348 return this._inspected; 379 return this._inspected;
349 } 380 }
350 381
351 /** 382 /**
352 * @return {boolean} 383 * @return {boolean}
353 */ 384 */
354 isDOMNode() { 385 isDOMNode() {
355 return this._axNode.isDOMNode(); 386 return this._axNode.isDOMNode();
356 } 387 }
357 388
358 /** 389 /**
390 * @return {?Accessibility.AXBreadcrumb}
391 */
392 nextBreadcrumb() {
393 if (this._children.length)
394 return this._children[0];
395 var nextSibling = this.element().nextSibling;
396 if (nextSibling)
397 return nextSibling.breadcrumb;
398 return null;
399 }
400
401 /**
402 * @return {?Accessibility.AXBreadcrumb}
403 */
404 previousBreadcrumb() {
405 var previousSibling = this.element().previousSibling;
406 if (previousSibling)
407 return previousSibling.breadcrumb;
408
409 return this._parent;
410 }
411
412 /**
359 * @param {string} name 413 * @param {string} name
360 */ 414 */
361 _appendNameElement(name) { 415 _appendNameElement(name) {
362 var nameElement = createElement('span'); 416 var nameElement = createElement('span');
363 nameElement.textContent = '"' + name + '"'; 417 nameElement.textContent = '"' + name + '"';
364 nameElement.classList.add('ax-readable-string'); 418 nameElement.classList.add('ax-readable-string');
365 this._nodeWrapper.appendChild(nameElement); 419 this._nodeWrapper.appendChild(nameElement);
366 } 420 }
367 421
368 /** 422 /**
(...skipping 16 matching lines...) Expand all
385 ignoredNodeElement.classList.add('ax-breadcrumbs-ignored-node'); 439 ignoredNodeElement.classList.add('ax-breadcrumbs-ignored-node');
386 this._nodeWrapper.appendChild(ignoredNodeElement); 440 this._nodeWrapper.appendChild(ignoredNodeElement);
387 } 441 }
388 }; 442 };
389 443
390 /** @type {!Object<string, string>} */ 444 /** @type {!Object<string, string>} */
391 Accessibility.AXBreadcrumb.RoleStyles = { 445 Accessibility.AXBreadcrumb.RoleStyles = {
392 internalRole: 'ax-internal-role', 446 internalRole: 'ax-internal-role',
393 role: 'ax-role', 447 role: 'ax-role',
394 }; 448 };
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698