Chromium Code Reviews| Index: third_party/WebKit/Source/devtools/front_end/accessibility/AXTreePane.js |
| diff --git a/third_party/WebKit/Source/devtools/front_end/accessibility/AXTreePane.js b/third_party/WebKit/Source/devtools/front_end/accessibility/AXTreePane.js |
| index fcda56cce962916bc2ab52c00a60f3f6355cc201..4eda9e0fe7c83868957597d0cfd0b94cd8f71ed3 100644 |
| --- a/third_party/WebKit/Source/devtools/front_end/accessibility/AXTreePane.js |
| +++ b/third_party/WebKit/Source/devtools/front_end/accessibility/AXTreePane.js |
| @@ -11,28 +11,133 @@ WebInspector.AXTreePane = class extends WebInspector.AccessibilitySubPane { |
| this._treeOutline = this.createTreeOutline(); |
| this.element.classList.add('accessibility-computed'); |
| + |
| + this._expandedNodes = new Set(); |
| } |
| /** |
| - * @param {!Array<!WebInspector.AccessibilityNode>} nodes |
| + * @param {?WebInspector.AccessibilityNode} axNode |
| + * @override |
| */ |
| - setAXNodeAndAncestors(nodes) { |
| - this._nodes = nodes; |
| + setAXNode(axNode) { |
| + this._axNode = axNode; |
| - var target = this.node().target(); |
| var treeOutline = this._treeOutline; |
| treeOutline.removeChildren(); |
| + |
| + // TODO(aboxhall): show no node UI |
| + if (!axNode) |
| + return; |
| + |
| treeOutline.element.classList.remove('hidden'); |
| - var previous = treeOutline.rootElement(); |
| - while (nodes.length) { |
| - var ancestor = nodes.pop(); |
| - var ancestorTreeElement = new WebInspector.AXNodeTreeElement(ancestor, target); |
| - previous.appendChild(ancestorTreeElement); |
| - previous.expand(); |
| - previous = ancestorTreeElement; |
| + var previousTreeElement = treeOutline.rootElement(); |
| + var inspectedNodeTreeElement = new WebInspector.AXNodeTreeElement(axNode, this); |
| + inspectedNodeTreeElement.setInspected(true); |
| + |
| + var parent = axNode.parentNode(); |
| + if (parent) { |
| + this.setExpanded(parent.domNode(), false); |
| + |
| + var chain = []; |
| + var ancestor = parent.parentNode(); |
| + while (ancestor) { |
| + chain.unshift(ancestor); |
| + ancestor = ancestor.parentNode(); |
| + } |
| + for (var ancestorNode of chain) { |
| + var ancestorTreeElement = new WebInspector.AXNodeTreeElement(ancestorNode, this); |
| + previousTreeElement.appendChild(ancestorTreeElement); |
| + previousTreeElement.expand(); |
| + previousTreeElement = ancestorTreeElement; |
| + } |
| + var parentTreeElement = new WebInspector.AXNodeTreeParentElement(parent, inspectedNodeTreeElement, this); |
| + if (this.isExpanded(parent.domNode())) |
| + parentTreeElement.appendSiblings(); |
| + else |
| + parentTreeElement.appendChild(inspectedNodeTreeElement); |
| + previousTreeElement.appendChild(parentTreeElement); |
| + previousTreeElement.expand(); |
| + previousTreeElement = parentTreeElement; |
| + } else { |
| + previousTreeElement.appendChild(inspectedNodeTreeElement); |
| + } |
| + |
| + previousTreeElement.expand(); |
| + |
| + for (var child of axNode.children()) { |
| + var childTreeElement = new WebInspector.AXNodeTreeElement(child, this); |
| + inspectedNodeTreeElement.appendChild(childTreeElement); |
| } |
| - previous.selectable = true; |
| - previous.select(true /* omitFocus */); |
| + |
| + inspectedNodeTreeElement.selectable = true; |
| + inspectedNodeTreeElement.select(!this._selectedByUser /* omitFocus */, false); |
| + if (this.isExpanded(axNode.domNode())) |
| + inspectedNodeTreeElement.expand(); |
| + this.clearSelectedByUser(); |
| + } |
| + |
| + /** |
| + * @param {boolean} selectedByUser |
| + */ |
| + setSelectedByUser(selectedByUser) { |
| + this._selectedByUser = true; |
| + } |
| + |
| + clearSelectedByUser() { |
| + delete this._selectedByUser; |
| + } |
| + |
| + /** |
| + * @return {!WebInspector.Target} |
| + */ |
| + target() { |
| + return this.node().target(); |
| + } |
| + |
| + /** |
| + * @param {?WebInspector.DOMNode} domNode |
| + * @param {boolean} expanded |
| + */ |
| + setExpanded(domNode, expanded) { |
| + if (!domNode) |
| + return; |
| + if (expanded) |
| + this._expandedNodes.add(domNode.id); |
| + else |
| + this._expandedNodes.delete(domNode.id); |
| + } |
| + |
| + /** |
| + * @param {?WebInspector.DOMNode} domNode |
| + * @return {boolean} |
| + */ |
| + isExpanded(domNode) { |
| + if (!domNode) |
| + return false; |
| + |
| + return this._expandedNodes.has(domNode.id); |
| + } |
| +}; |
| + |
| +WebInspector.InspectNodeButton = class { |
| + /** |
| + * @param {!WebInspector.AccessibilityNode} axNode |
| + * @param {!WebInspector.AXTreePane} treePane |
| + */ |
| + constructor(axNode, treePane) { |
| + this._axNode = axNode; |
| + this._treePane = treePane; |
| + |
| + this.element = createElementWithClass('button', 'inspect-dom-node'); |
| + this.element.addEventListener('mousedown', this._handleMouseDown.bind(this)); |
| + } |
| + |
| + /** |
| + * @param {!Event} event |
| + */ |
| + _handleMouseDown(event) { |
| + this._treePane.setSelectedByUser(true); |
| + WebInspector.Revealer.reveal(this._axNode.domNode()); |
| } |
| }; |
| @@ -42,19 +147,66 @@ WebInspector.AXTreePane = class extends WebInspector.AccessibilitySubPane { |
| WebInspector.AXNodeTreeElement = class extends TreeElement { |
| /** |
| * @param {!WebInspector.AccessibilityNode} axNode |
| - * @param {!WebInspector.Target} target |
| + * @param {!WebInspector.AXTreePane} treePane |
| */ |
| - constructor(axNode, target) { |
| + constructor(axNode, treePane) { |
| // Pass an empty title, the title gets made later in onattach. |
| super(''); |
| /** @type {!WebInspector.AccessibilityNode} */ |
| this._axNode = axNode; |
| - /** @type {!WebInspector.Target} */ |
| - this._target = target; |
| + /** @type {!WebInspector.AXTreePane} */ |
| + this._treePane = treePane; |
| + |
| + this.selectable = true; |
| - this.selectable = false; |
| + this._inspectNodeButton = |
| + new WebInspector.InspectNodeButton(axNode, treePane); |
| + } |
| + |
| + /** |
| + * @return {!WebInspector.AccessibilityNode} |
| + */ |
| + axNode() { |
| + return this._axNode; |
| + } |
| + |
| + /** |
| + * @param {boolean} inspected |
| + */ |
| + setInspected(inspected) { |
| + if (inspected) { |
| + this._inspected = true; |
| + 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.
|
| + } else { |
| + this._inspected = false; |
| + this.listItemElement.classList.remove('inspected'); |
| + } |
| + } |
| + |
| + /** |
| + * @override |
| + * @return {boolean} |
| + */ |
| + onenter() { |
| + this.inspectDOMNode(); |
| + return true; |
| + } |
| + |
| + /** |
| + * @override |
| + * @param {!Event} event |
| + * @return {boolean} |
| + */ |
| + ondblclick(event) { |
| + this.inspectDOMNode(); |
| + return true; |
| + } |
| + |
| + inspectDOMNode() { |
| + this._treePane.setSelectedByUser(true); |
| + 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.
|
| } |
| /** |
| @@ -71,11 +223,46 @@ WebInspector.AXNodeTreeElement = class extends TreeElement { |
| this._appendIgnoredNodeElement(); |
| } else { |
| this._appendRoleElement(this._axNode.role()); |
| - if ('name' in this._axNode && this._axNode.name().value) { |
| + if (this._axNode && this._axNode.name().value) { |
| this.listItemElement.createChild('span', 'separator').textContent = '\u00A0'; |
| this._appendNameElement(/** @type {string} */ (this._axNode.name().value)); |
| } |
| } |
| + |
| + if (this._axNode.hasOnlyUnloadedChildren()) { |
| + this._hasOnlyUnloadedChildren = true; |
| + this.listItemElement.classList.add('children-unloaded'); |
| + this.setExpandable(true); |
| + } else { |
| + this.setExpandable(!!this._axNode.numChildren()); |
| + } |
| + |
| + if (!this._axNode.domNode()) |
| + this.listItemElement.classList.add('no-dom-node'); |
| + this.listItemElement.appendChild(this._inspectNodeButton.element); |
| + } |
| + |
| + /** |
| + * @override |
| + */ |
| + expand() { |
| + if (this._hasOnlyUnloadedChildren) |
| + return; |
| + |
| + this._treePane.setExpanded(this._axNode.domNode(), true); |
| + super.expand(); |
| + } |
| + |
| + /** |
| + * @override |
| + */ |
| + collapse() { |
| + if (this._hasOnlyUnloadedChildren) |
| + return; |
| + |
| + if (this._treePane) |
| + this._treePane.setExpanded(this._axNode.domNode(), false); |
| + super.collapse(); |
| } |
| /** |
| @@ -108,6 +295,20 @@ WebInspector.AXNodeTreeElement = class extends TreeElement { |
| ignoredNodeElement.classList.add('ax-tree-ignored-node'); |
| this.listItemElement.appendChild(ignoredNodeElement); |
| } |
| + |
| + /** |
| + * @param {boolean=} omitFocus |
| + * @param {boolean=} selectedByUser |
| + * @return {boolean} |
| + * @override |
| + */ |
| + select(omitFocus, selectedByUser) { |
| + this._ensureSelection(); |
| + |
| + this._treePane.setSelectedByUser(!!selectedByUser); |
| + |
| + return super.select(omitFocus, selectedByUser); |
| + } |
| }; |
| /** @type {!Object<string, string>} */ |
| @@ -115,3 +316,110 @@ WebInspector.AXNodeTreeElement.RoleStyles = { |
| internalRole: 'ax-internal-role', |
| role: 'ax-role', |
| }; |
| + |
| +/** |
| + * @unrestricted |
| + */ |
| +WebInspector.ExpandSiblingsButton = class { |
| + /** |
| + * @param {!WebInspector.AXNodeTreeParentElement} treeElement |
| + * @param {number} numSiblings |
| + */ |
| + constructor(treeElement, numSiblings) { |
| + this._treeElement = treeElement; |
| + |
| + this.element = createElementWithClass('button', 'expand-siblings'); |
| + this.element.textContent = WebInspector.UIString( |
| + '+ %d %s', numSiblings, numSiblings === 1 ? 'node' : 'nodes'); |
| + this.element.addEventListener('mousedown', this._handleMouseDown.bind(this)); |
| + } |
| + |
| + /** |
| + * @param {!Event} event |
| + */ |
| + _handleMouseDown(event) { |
| + this._treeElement.expandSiblings(); |
| + event.consume(); |
| + } |
| +}; |
| + |
| +/** |
| + * @unrestricted |
| + */ |
| +WebInspector.AXNodeTreeParentElement = class extends WebInspector.AXNodeTreeElement { |
| + /** |
| + * @param {!WebInspector.AccessibilityNode} axNode |
| + * @param {!WebInspector.AXNodeTreeElement} inspectedNodeTreeElement |
| + * @param {!WebInspector.AXTreePane} treePane |
| + */ |
| + constructor(axNode, inspectedNodeTreeElement, treePane) { |
| + super(axNode, treePane); |
| + |
| + this._inspectedNodeTreeElement = inspectedNodeTreeElement; |
| + var numSiblings = axNode.children().length - 1; |
| + this._expandSibingsButton = new WebInspector.ExpandSiblingsButton(this, numSiblings); |
|
dgozman
2016/11/03 22:07:29
typo: Sibings
aboxhall
2016/11/04 22:31:17
Done.
|
| + this._partiallyExpanded = false; |
| + } |
| + |
| + /** |
| + * @override |
| + */ |
| + onattach() { |
| + super.onattach(); |
| + if (this._treePane.isExpanded(this._axNode.domNode())) |
| + this._listItemNode.classList.add('siblings-expanded'); |
| + if (this._axNode.numChildren() > 1) { |
| + this._listItemNode.insertBefore(this._expandSibingsButton.element, |
| + this._inspectNodeButton.element); |
| + } |
| + } |
| + |
| + /** |
| + * @param {boolean} altKey |
| + * @return {boolean} |
| + * @override |
| + */ |
| + descendOrExpand(altKey) { |
| + if (!this.expanded || !this._partiallyExpanded) |
| + return TreeElement.prototype.descendOrExpand.call(this, altKey); |
| + |
| + this.expandSiblings(); |
| + if (altKey) |
| + this.expandRecursively(); |
| + return true; |
| + } |
| + |
| + /** |
| + * @override |
| + */ |
| + expand() { |
| + TreeElement.prototype.expand.call(this); |
| + this._partiallyExpanded = true; |
| + } |
| + |
| + expandSiblings() { |
| + this._listItemNode.classList.add('siblings-expanded'); |
| + this.appendSiblings(); |
| + this.expanded = true; |
| + this._partiallyExpanded = false; |
| + this._treePane.setExpanded(this._axNode.domNode(), true); |
| + } |
| + |
| + appendSiblings() { |
| + var inspectedAXNode = this._inspectedNodeTreeElement.axNode(); |
| + var nextIndex = 0; |
| + var foundInspectedNode = false; |
| + for (var sibling of this._axNode.children()) { |
| + var siblingTreeElement = null; |
| + if (sibling === inspectedAXNode) { |
| + foundInspectedNode = true; |
| + continue; |
| + } |
| + siblingTreeElement = new WebInspector.AXNodeTreeElement(sibling, this._treePane); |
| + if (foundInspectedNode) |
| + this.appendChild(siblingTreeElement); |
| + else |
| + this.insertChild(siblingTreeElement, nextIndex++); |
| + } |
| + } |
| +}; |