DevTools: navigable ElementsTreeOutline in Console

Introduces a common interface between ElementsTreeOutlines and
ObjectPropertiesSections. This allows both to be keyboard navigable
in Console.

Bug: 865674
Change-Id: I405d161692147374827918cce2369d129620a9fd
Reviewed-on: https://chromium-review.googlesource.com/c/1275225
Commit-Queue: Erik Luo <[email protected]>
Reviewed-by: Joel Einbinder <[email protected]>
Cr-Original-Commit-Position: refs/heads/master@{#604855}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: de8a9c26f6a4e28bc14e613e0d97cae69aef749b
diff --git a/front_end/console/ConsoleViewMessage.js b/front_end/console/ConsoleViewMessage.js
index b4f863a..29cd4cd 100644
--- a/front_end/console/ConsoleViewMessage.js
+++ b/front_end/console/ConsoleViewMessage.js
@@ -45,8 +45,8 @@
     this._repeatCount = 1;
     this._closeGroupDecorationCount = 0;
     this._nestingLevel = nestingLevel;
-    /** @type {!Array<!ObjectUI.ObjectPropertiesSection>} */
-    this._focusableChildren = [];
+    /** @type {!Array<!UI.TreeOutline>} */
+    this._treeOutlines = [];
 
     /** @type {?DataGrid.DataGrid} */
     this._dataGrid = null;
@@ -613,7 +613,7 @@
     const section = new ObjectUI.ObjectPropertiesSection(obj, titleElement, this._linkifier);
     section.element.classList.add('console-view-object-properties-section');
     section.enableContextMenu();
-    this._focusableChildren.push(section);
+    this._treeOutlines.push(section);
     return section.element;
   }
 
@@ -682,8 +682,13 @@
         return;
       }
       const renderResult = await UI.Renderer.render(/** @type {!Object} */ (node));
-      const renderedNode = renderResult ? renderResult.node : null;
-      result.appendChild(renderedNode || this._formatParameterAsObject(remoteObject, false));
+      if (renderResult) {
+        if (renderResult.tree)
+          this._treeOutlines.push(renderResult.tree);
+        result.appendChild(renderResult.node);
+      } else {
+        result.appendChild(this._formatParameterAsObject(remoteObject, false));
+      }
       this._formattedParameterAsNodeForTest();
     });
 
@@ -1041,9 +1046,9 @@
    * @return {number}
    */
   _focusedChildIndex() {
-    if (!this._focusableChildren.length)
+    if (!this._treeOutlines.length)
       return -1;
-    return this._focusableChildren.findIndex(child => child.element.hasFocus());
+    return this._treeOutlines.findIndex(child => child.element.hasFocus());
   }
 
   /**
@@ -1070,7 +1075,7 @@
         return true;
       }
     }
-    if (!this._focusableChildren.length)
+    if (!this._treeOutlines.length)
       return false;
 
     if (event.key === 'ArrowLeft') {
@@ -1079,7 +1084,7 @@
     }
     if (event.key === 'ArrowRight') {
       if (isWrapperFocused) {
-        this._focusChild(0);
+        this._treeOutlines[0].selectFirst();
         return true;
       }
     }
@@ -1088,35 +1093,25 @@
         this._element.focus();
         return true;
       } else if (focusedChildIndex > 0) {
-        this._focusChild(focusedChildIndex - 1);
+        this._treeOutlines[focusedChildIndex - 1].selectFirst();
         return true;
       }
     }
     if (event.key === 'ArrowDown') {
       if (isWrapperFocused) {
-        this._focusChild(0);
+        this._treeOutlines[0].selectFirst();
         return true;
-      } else if (focusedChildIndex < this._focusableChildren.length - 1) {
-        this._focusChild(focusedChildIndex + 1);
+      } else if (focusedChildIndex < this._treeOutlines.length - 1) {
+        this._treeOutlines[focusedChildIndex + 1].selectFirst();
         return true;
       }
     }
     return false;
   }
 
-  /**
-   * @param {number} index
-   */
-  _focusChild(index) {
-    const section = this._focusableChildren[index];
-    if (!section.objectTreeElement().selected)
-      section.objectTreeElement().select();
-    section.focus();
-  }
-
   focusLastChildOrSelf() {
-    if (this._focusableChildren.length)
-      this._focusChild(this._focusableChildren.length - 1);
+    if (this._treeOutlines.length)
+      this._treeOutlines[this._treeOutlines.length - 1].selectFirst();
     else if (this._element)
       this._element.focus();
   }