AXBreadcrumbsPane.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. export default class AXBreadcrumbsPane extends Accessibility.AccessibilitySubPane{constructor(axSidebarView){super(ls`Accessibility Tree`);this.element.classList.add('ax-subpane');UI.ARIAUtils.markAsTree(this.element);this.element.tabIndex=-1;this._axSidebarView=axSidebarView;this._preselectedBreadcrumb=null;this._inspectedNodeBreadcrumb=null;this._hoveredBreadcrumb=null;this._rootElement=this.element.createChild('div','ax-breadcrumbs');this._rootElement.addEventListener('keydown',this._onKeyDown.bind(this),true);this._rootElement.addEventListener('mousemove',this._onMouseMove.bind(this),false);this._rootElement.addEventListener('mouseleave',this._onMouseLeave.bind(this),false);this._rootElement.addEventListener('click',this._onClick.bind(this),false);this._rootElement.addEventListener('contextmenu',this._contextMenuEventFired.bind(this),false);this._rootElement.addEventListener('focusout',this._onFocusOut.bind(this),false);this.registerRequiredCSS('accessibility/axBreadcrumbs.css');}
  2. focus(){if(this._inspectedNodeBreadcrumb){this._inspectedNodeBreadcrumb.nodeElement().focus();}else{this.element.focus();}}
  3. setAXNode(axNode){const hadFocus=this.element.hasFocus();super.setAXNode(axNode);this._rootElement.removeChildren();if(!axNode){return;}
  4. const ancestorChain=[];let ancestor=axNode;while(ancestor){ancestorChain.push(ancestor);ancestor=ancestor.parentNode();}
  5. ancestorChain.reverse();let depth=0;let breadcrumb=null;let parent=null;for(ancestor of ancestorChain){breadcrumb=new Accessibility.AXBreadcrumb(ancestor,depth,(ancestor===axNode));if(parent){parent.appendChild(breadcrumb);}else{this._rootElement.appendChild(breadcrumb.element());}
  6. parent=breadcrumb;depth++;}
  7. this._inspectedNodeBreadcrumb=breadcrumb;this._inspectedNodeBreadcrumb.setPreselected(true,hadFocus);this._setPreselectedBreadcrumb(this._inspectedNodeBreadcrumb);function append(parentBreadcrumb,axNode,localDepth){const childBreadcrumb=new Accessibility.AXBreadcrumb(axNode,localDepth,false);parentBreadcrumb.appendChild(childBreadcrumb);for(const child of axNode.children()){append(childBreadcrumb,child,localDepth+1);}}
  8. for(const child of axNode.children()){append(this._inspectedNodeBreadcrumb,child,depth);}}
  9. willHide(){this._setPreselectedBreadcrumb(null);}
  10. _onKeyDown(event){if(!this._preselectedBreadcrumb){return;}
  11. if(!event.composedPath().some(element=>element===this._preselectedBreadcrumb.element())){return;}
  12. if(event.shiftKey||event.metaKey||event.ctrlKey){return;}
  13. let handled=false;if((event.key==='ArrowUp'||event.key==='ArrowLeft')&&!event.altKey){handled=this._preselectPrevious();}else if((event.key==='ArrowDown'||event.key==='ArrowRight')&&!event.altKey){handled=this._preselectNext();}else if(isEnterKey(event)){handled=this._inspectDOMNode(this._preselectedBreadcrumb.axNode());}
  14. if(handled){event.consume(true);}}
  15. _preselectPrevious(){const previousBreadcrumb=this._preselectedBreadcrumb.previousBreadcrumb();if(!previousBreadcrumb){return false;}
  16. this._setPreselectedBreadcrumb(previousBreadcrumb);return true;}
  17. _preselectNext(){const nextBreadcrumb=this._preselectedBreadcrumb.nextBreadcrumb();if(!nextBreadcrumb){return false;}
  18. this._setPreselectedBreadcrumb(nextBreadcrumb);return true;}
  19. _setPreselectedBreadcrumb(breadcrumb){if(breadcrumb===this._preselectedBreadcrumb){return;}
  20. const hadFocus=this.element.hasFocus();if(this._preselectedBreadcrumb){this._preselectedBreadcrumb.setPreselected(false,hadFocus);}
  21. if(breadcrumb){this._preselectedBreadcrumb=breadcrumb;}else{this._preselectedBreadcrumb=this._inspectedNodeBreadcrumb;}
  22. this._preselectedBreadcrumb.setPreselected(true,hadFocus);if(!breadcrumb&&hadFocus){SDK.OverlayModel.hideDOMNodeHighlight();}}
  23. _onMouseLeave(event){this._setHoveredBreadcrumb(null);}
  24. _onMouseMove(event){const breadcrumbElement=event.target.enclosingNodeOrSelfWithClass('ax-breadcrumb');if(!breadcrumbElement){this._setHoveredBreadcrumb(null);return;}
  25. const breadcrumb=breadcrumbElement.breadcrumb;if(!breadcrumb.isDOMNode()){return;}
  26. this._setHoveredBreadcrumb(breadcrumb);}
  27. _onFocusOut(event){if(!this._preselectedBreadcrumb||event.target!==this._preselectedBreadcrumb.nodeElement()){return;}
  28. this._setPreselectedBreadcrumb(null);}
  29. _onClick(event){const breadcrumbElement=event.target.enclosingNodeOrSelfWithClass('ax-breadcrumb');if(!breadcrumbElement){this._setHoveredBreadcrumb(null);return;}
  30. const breadcrumb=breadcrumbElement.breadcrumb;if(breadcrumb.inspected()){breadcrumb.nodeElement().focus();return;}
  31. if(!breadcrumb.isDOMNode()){return;}
  32. this._inspectDOMNode(breadcrumb.axNode());}
  33. _setHoveredBreadcrumb(breadcrumb){if(breadcrumb===this._hoveredBreadcrumb){return;}
  34. if(this._hoveredBreadcrumb){this._hoveredBreadcrumb.setHovered(false);}
  35. if(breadcrumb){breadcrumb.setHovered(true);}else if(this.node()){this.node().domModel().overlayModel().nodeHighlightRequested(this.node().id);}
  36. this._hoveredBreadcrumb=breadcrumb;}
  37. _inspectDOMNode(axNode){if(!axNode.isDOMNode()){return false;}
  38. axNode.deferredDOMNode().resolve(domNode=>{this._axSidebarView.setNode(domNode,true);Common.Revealer.reveal(domNode,true);});return true;}
  39. _contextMenuEventFired(event){const breadcrumbElement=event.target.enclosingNodeOrSelfWithClass('ax-breadcrumb');if(!breadcrumbElement){return;}
  40. const axNode=breadcrumbElement.breadcrumb.axNode();if(!axNode.isDOMNode()||!axNode.deferredDOMNode()){return;}
  41. const contextMenu=new UI.ContextMenu(event);contextMenu.viewSection().appendItem(ls`Scroll into view`,()=>{axNode.deferredDOMNode().resolvePromise().then(domNode=>{if(!domNode){return;}
  42. domNode.scrollIntoView();});});contextMenu.appendApplicableItems(axNode.deferredDOMNode());contextMenu.show();}}
  43. export class AXBreadcrumb{constructor(axNode,depth,inspected){this._axNode=axNode;this._element=createElementWithClass('div','ax-breadcrumb');this._element.breadcrumb=this;this._nodeElement=createElementWithClass('div','ax-node');UI.ARIAUtils.markAsTreeitem(this._nodeElement);this._nodeElement.tabIndex=-1;this._element.appendChild(this._nodeElement);this._nodeWrapper=createElementWithClass('div','wrapper');this._nodeElement.appendChild(this._nodeWrapper);this._selectionElement=createElementWithClass('div','selection fill');this._nodeElement.appendChild(this._selectionElement);this._childrenGroupElement=createElementWithClass('div','children');UI.ARIAUtils.markAsGroup(this._childrenGroupElement);this._element.appendChild(this._childrenGroupElement);this._children=[];this._hovered=false;this._preselected=false;this._parent=null;this._inspected=inspected;this._nodeElement.classList.toggle('inspected',inspected);this._nodeElement.style.paddingLeft=(16*depth+4)+'px';if(this._axNode.ignored()){this._appendIgnoredNodeElement();}else{this._appendRoleElement(this._axNode.role());if(this._axNode.name()&&this._axNode.name().value){this._nodeWrapper.createChild('span','separator').textContent='\xA0';this._appendNameElement((this._axNode.name().value));}}
  44. if(this._axNode.hasOnlyUnloadedChildren()){this._nodeElement.classList.add('children-unloaded');}
  45. if(!this._axNode.isDOMNode()){this._nodeElement.classList.add('no-dom-node');}}
  46. element(){return this._element;}
  47. nodeElement(){return this._nodeElement;}
  48. appendChild(breadcrumb){this._children.push(breadcrumb);breadcrumb.setParent(this);this._nodeElement.classList.add('parent');UI.ARIAUtils.setExpanded(this._nodeElement,true);this._childrenGroupElement.appendChild(breadcrumb.element());}
  49. setParent(breadcrumb){this._parent=breadcrumb;}
  50. preselected(){return this._preselected;}
  51. setPreselected(preselected,selectedByUser){if(this._preselected===preselected){return;}
  52. this._preselected=preselected;this._nodeElement.classList.toggle('preselected',preselected);if(preselected){this._nodeElement.setAttribute('tabIndex',0);}else{this._nodeElement.setAttribute('tabIndex',-1);}
  53. if(this._preselected){if(selectedByUser){this._nodeElement.focus();}
  54. if(!this._inspected){this._axNode.highlightDOMNode();}else{SDK.OverlayModel.hideDOMNodeHighlight();}}}
  55. setHovered(hovered){if(this._hovered===hovered){return;}
  56. this._hovered=hovered;this._nodeElement.classList.toggle('hovered',hovered);if(this._hovered){this._nodeElement.classList.toggle('hovered',true);this._axNode.highlightDOMNode();}}
  57. axNode(){return this._axNode;}
  58. inspected(){return this._inspected;}
  59. isDOMNode(){return this._axNode.isDOMNode();}
  60. nextBreadcrumb(){if(this._children.length){return this._children[0];}
  61. const nextSibling=this.element().nextSibling;if(nextSibling){return nextSibling.breadcrumb;}
  62. return null;}
  63. previousBreadcrumb(){const previousSibling=this.element().previousSibling;if(previousSibling){return previousSibling.breadcrumb;}
  64. return this._parent;}
  65. _appendNameElement(name){const nameElement=createElement('span');nameElement.textContent='"'+name+'"';nameElement.classList.add('ax-readable-string');this._nodeWrapper.appendChild(nameElement);}
  66. _appendRoleElement(role){if(!role){return;}
  67. const roleElement=createElementWithClass('span','monospace');roleElement.classList.add(Accessibility.AXBreadcrumb.RoleStyles[role.type]);roleElement.setTextContentTruncatedIfNeeded(role.value||'');this._nodeWrapper.appendChild(roleElement);}
  68. _appendIgnoredNodeElement(){const ignoredNodeElement=createElementWithClass('span','monospace');ignoredNodeElement.textContent=ls`Ignored`;ignoredNodeElement.classList.add('ax-breadcrumbs-ignored-node');this._nodeWrapper.appendChild(ignoredNodeElement);}}
  69. export const RoleStyles={internalRole:'ax-internal-role',role:'ax-role',};self.Accessibility=self.Accessibility||{};Accessibility=Accessibility||{};Accessibility.AXBreadcrumbsPane=AXBreadcrumbsPane;Accessibility.AXBreadcrumb=AXBreadcrumb;Accessibility.AXBreadcrumb.RoleStyles=RoleStyles;