Add ui for choosing chrome categories

If the categories have been fetched from the extension, at the bottom
of the chrome tab a list of categories is shown. After the selection,
they are inserted in the configuration.

Bug: 140224909
Change-Id: I402133d088113afe8914d27ee49a3e94aa7115b8
diff --git a/ui/src/assets/record.scss b/ui/src/assets/record.scss
index 7b2b8ff..40c3751 100644
--- a/ui/src/assets/record.scss
+++ b/ui/src/assets/record.scss
@@ -563,6 +563,24 @@
         }
         margin: 0;
       }
+
+      &.two-columns {
+        height: 400px;
+        width: auto;
+        margin: var(--record-section-padding);
+        optgroup {
+          display: grid;
+          padding: 0;
+          grid-template-columns: 1fr 1fr;
+        }
+        option {
+          &:nth-of-type(2n + 1) {
+            border-left: 1px solid #eee;
+            border-right: 1px solid #eee;
+          }
+          margin: 0;
+        }
+      }
     }
   }
 
@@ -574,6 +592,10 @@
     height: 152px;
   }
 
+  .chrome-categories {
+    height: 152px;
+  }
+
   textarea.extra-input {
     width: 100%;
     height: 60px;
@@ -595,7 +617,6 @@
     user-select: text;
     box-shadow: 0 0 12px #999;
 
-
     @keyframes ripple{
       0% { transform: scale(1.00); }
       30% { transform: scale(1.20); }
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index c427a44..21ac4be 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -250,6 +250,8 @@
 
   procStats: boolean;
   procStatsPeriodMs: number;
+
+  chromeCategoriesSelected: string[];
 }
 
 export function createEmptyRecordConfig(): RecordConfig {
@@ -302,6 +304,8 @@
     memLmk: false,
     procStats: false,
     procStatsPeriodMs: 1000,
+
+    chromeCategoriesSelected: [],
   };
 }
 
diff --git a/ui/src/controller/record_controller.ts b/ui/src/controller/record_controller.ts
index e073cb7..47a4e80 100644
--- a/ui/src/controller/record_controller.ts
+++ b/ui/src/controller/record_controller.ts
@@ -101,6 +101,7 @@
   const atraceCats = new Set<string>(uiCfg.atrace ? uiCfg.atraceCats : []);
   const atraceApps = new Set<string>();
   const chromeCategories = new Set<string>();
+  uiCfg.chromeCategoriesSelected.forEach(it => chromeCategories.add(it));
 
   let procThreadAssociationPolling = false;
   let procThreadAssociationFtrace = false;
diff --git a/ui/src/frontend/record_page.ts b/ui/src/frontend/record_page.ts
index 39afc3c..33cda2d 100644
--- a/ui/src/frontend/record_page.ts
+++ b/ui/src/frontend/record_page.ts
@@ -473,7 +473,25 @@
         descr: `Records network events for navigations and resources.`,
         setEnabled: (cfg, val) => cfg.navigationAndLoading = val,
         isEnabled: (cfg) => cfg.navigationAndLoading
-      } as ProbeAttrs));
+      } as ProbeAttrs),
+      ChromeCategoriesSelection());
+}
+
+function ChromeCategoriesSelection() {
+  // The categories are displayed only if the extension is installed, because
+  // they come from the chrome.debugging API, not available from normal web
+  // pages.
+  const categories = globals.state.chromeCategories;
+  if (!categories) return [];
+  const categoriesMap = new Map<string, string>();
+  categories.forEach(cat => categoriesMap.set(cat, cat));
+  return m(Dropdown, {
+    title: 'Additional Chrome categories',
+    cssClass: '.multicolumn.two-columns.chrome-categories',
+    options: categoriesMap,
+    set: (cfg, val) => cfg.chromeCategoriesSelected = val,
+    get: (cfg) => cfg.chromeCategoriesSelected
+  } as DropdownAttrs);
 }
 
 function AdvancedSettings(cssClass: string) {