ui/extension: Record using Chrome's browser DevTools target

After crrev.com/c/1986333 (M81), we can connect to the browser
target, and thus record from all Chrome processes by avoiding
the process filter other DevTools targets apply.

This means we won't support recording from older Chrome versions
anymore. This should be fine given that it wouldn't be very useful
because of missing data from many processes.

In order to select custom Chrome categories when recording via ADB
from browsers below M81, this patch also adds a snapshot of the
built-in Chrome category list to the UI.

Bug: chromium:1014152
Change-Id: I1254adb6f7f720378858a2651def10eb83eb4bcd
diff --git a/ui/src/chrome_extension/chrome_tracing_controller.ts b/ui/src/chrome_extension/chrome_tracing_controller.ts
index 356bd44..280068a 100644
--- a/ui/src/chrome_extension/chrome_tracing_controller.ts
+++ b/ui/src/chrome_extension/chrome_tracing_controller.ts
@@ -208,8 +208,14 @@
       fetchCategories();
       return;
     }
-    // Otherwise, we find attach to the target temporarily.
-    this.devtoolsSocket.findAndAttachTarget(async _ => {
+    // Otherwise, we attach temporarily.
+    this.devtoolsSocket.attachToBrowser(async (error?: string) => {
+      if (error) {
+        this.sendErrorMessage(
+            `Could not attach to DevTools browser target ` +
+            `(req. Chrome >= M81): ${error}`);
+        return;
+      }
       fetchCategories();
       this.devtoolsSocket.detach();
     });
@@ -230,7 +236,13 @@
   }
 
   handleStartTracing(traceConfig: Protocol.Tracing.TraceConfig) {
-    this.devtoolsSocket.findAndAttachTarget(async _ => {
+    this.devtoolsSocket.attachToBrowser(async (error?: string) => {
+      if (error) {
+        this.sendErrorMessage(
+            `Could not attach to DevTools browser target ` +
+            `(req. Chrome >= M81): ${error}`);
+        return;
+      }
       await this.api.Tracing.start({
         traceConfig,
         streamFormat: 'proto',
diff --git a/ui/src/chrome_extension/devtools_socket.ts b/ui/src/chrome_extension/devtools_socket.ts
index aa23f8f..bb40f5c 100644
--- a/ui/src/chrome_extension/devtools_socket.ts
+++ b/ui/src/chrome_extension/devtools_socket.ts
@@ -64,25 +64,20 @@
     throw new Error('Call unexpected');
   }
 
-  findTarget(then: (target: chrome.debugger.Debuggee) => void) {
-    chrome.debugger.getTargets(targets => {
-      const perfettoTab =
-          targets.find(target => target.title.includes('Perfetto'));
-      if (perfettoTab === undefined) {
-        console.log('No perfetto tab found');
-        return;
-      }
-      this.target = {targetId: perfettoTab.id};
-      then(this.target);
-    });
+  attachToBrowser(then: (error?: string) => void) {
+    this.attachToTarget({targetId: 'browser'}, then);
   }
 
-  findAndAttachTarget(then: (target: chrome.debugger.Debuggee) => void) {
-    this.findTarget(t => {
-      chrome.debugger.attach(t, /*requiredVersion=*/ '1.3', () => {
-        this.openCallback();
-        then(t);
-      });
+  private attachToTarget(
+      target: chrome.debugger.Debuggee, then: (error?: string) => void) {
+    chrome.debugger.attach(target, /*requiredVersion=*/ '1.3', () => {
+      if (chrome.runtime.lastError) {
+        then(chrome.runtime.lastError.message);
+        return;
+      }
+      this.target = target;
+      this.openCallback();
+      then();
     });
   }
 
diff --git a/ui/src/chrome_extension/manifest.json b/ui/src/chrome_extension/manifest.json
index 5abd7bb..96dfe3e 100644
--- a/ui/src/chrome_extension/manifest.json
+++ b/ui/src/chrome_extension/manifest.json
@@ -2,8 +2,9 @@
   "name": "Perfetto UI",
   "key":"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhm3X7qutsrskke84ltokTObnFJakd/d0XFQ6Ox2wQueHTGJM5GUNPTY/x8bdreNtGnfzvt/Sd0vABbR0wsS6lz5yY+g6ksMXJnigFe9N7uz8E3KojDrl3xYjIe+mkiJo8yxxzPydgb7GjQ6jmsX3g+yjj67kXzm9rZFkmoZ5WmqwBZlguPYVRN/W8CIIqBZkC3Qmq6uSG7b/g93YbwqmTmGiL2sAzgvXtqvDOD6503abtQkRC795E4VjJd+ffyeRH38fAEz5ZIrA6GJsfmov1TZTIu1NTwqylSpBYl5as7C6gpmuxDV4SvHvGT2hMQuIufDhZhErjI3B7bcX+XLe1wIDAQAB",
   "description": "Enables the Perfetto trace viewer (https://ui.perfetto.dev) to record Chrome browser traces.",
-  "version": "0.0.0.9",
+  "version": "0.0.0.10",
   "manifest_version": 2,
+  "minimum_chrome_version": "81.0.4022.0",
   "permissions": [
     "declarativeContent",
     "debugger"
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index cf9fc43..2b6d2b2 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -424,6 +424,214 @@
   ];
 }
 
+export function getBuiltinChromeCategoryList(): string[] {
+  // List of static Chrome categories, last updated at Chromium 81.0.4021.0 from
+  // Chromium's //base/trace_event/builtin_categories.h.
+  return [
+    'accessibility',
+    'AccountFetcherService',
+    'android_webview',
+    'audio',
+    'base',
+    'benchmark',
+    'blink',
+    'blink.animations',
+    'blink.console',
+    'blink_gc',
+    'blink.net',
+    'blink_style',
+    'blink.user_timing',
+    'blink.worker',
+    'Blob',
+    'browser',
+    'browsing_data',
+    'CacheStorage',
+    'camera',
+    'cast_perf_test',
+    'cast.stream',
+    'cc',
+    'cc.debug',
+    'cdp.perf',
+    'chromeos',
+    'cma',
+    'compositor',
+    'content',
+    'content_capture',
+    'devtools',
+    'devtools.timeline',
+    'devtools.timeline.async',
+    'download',
+    'download_service',
+    'drm',
+    'drmcursor',
+    'dwrite',
+    'DXVA Decoding',
+    'EarlyJava',
+    'evdev',
+    'event',
+    'exo',
+    'explore_sites',
+    'FileSystem',
+    'file_system_provider',
+    'fonts',
+    'GAMEPAD',
+    'gpu',
+    'gpu.capture',
+    'headless',
+    'hwoverlays',
+    'identity',
+    'IndexedDB',
+    'input',
+    'io',
+    'ipc',
+    'Java',
+    'jni',
+    'jpeg',
+    'latency',
+    'latencyInfo',
+    'leveldb',
+    'loading',
+    'log',
+    'login',
+    'media',
+    'media_router',
+    'memory',
+    'midi',
+    'mojom',
+    'mus',
+    'native',
+    'navigation',
+    'net',
+    'netlog',
+    'offline_pages',
+    'omnibox',
+    'oobe',
+    'ozone',
+    'passwords',
+    'p2p',
+    'page-serialization',
+    'pepper',
+    'ppapi',
+    'ppapi proxy',
+    'rail',
+    'renderer',
+    'renderer_host',
+    'renderer.scheduler',
+    'RLZ',
+    'safe_browsing',
+    'screenlock_monitor',
+    'sequence_manager',
+    'service_manager',
+    'ServiceWorker',
+    'shell',
+    'shortcut_viewer',
+    'shutdown',
+    'SiteEngagement',
+    'skia',
+    'startup',
+    'sync',
+    'sync_lock_contention',
+    'thread_pool',
+    'test_gpu',
+    'test_tracing',
+    'toplevel',
+    'ui',
+    'v8',
+    'v8.execute',
+    'ValueStoreFrontend::Backend',
+    'views',
+    'views.frame',
+    'viz',
+    'vk',
+    'wayland',
+    'webaudio',
+    'weblayer',
+    'WebCore',
+    'webrtc',
+    'xr',
+    'disabled-by-default-animation-worklet',
+    'disabled-by-default-audio-worklet',
+    'disabled-by-default-blink.debug',
+    'disabled-by-default-blink.debug.display_lock',
+    'disabled-by-default-blink.debug.layout',
+    'disabled-by-default-blink.debug.layout.trees',
+    'disabled-by-default-blink.feature_usage',
+    'disabled-by-default-blink_gc',
+    'disabled-by-default-blink.image_decoding',
+    'disabled-by-default-blink.invalidation',
+    'disabled-by-default-cc',
+    'disabled-by-default-cc.debug',
+    'disabled-by-default-cc.debug.cdp-perf',
+    'disabled-by-default-cc.debug.display_items',
+    'disabled-by-default-cc.debug.picture',
+    'disabled-by-default-cc.debug.scheduler',
+    'disabled-by-default-cc.debug.scheduler.frames',
+    'disabled-by-default-cc.debug.scheduler.now',
+    'disabled-by-default-cpu_profiler',
+    'disabled-by-default-cpu_profiler.debug',
+    'disabled-by-default-devtools.screenshot',
+    'disabled-by-default-devtools.timeline',
+    'disabled-by-default-devtools.timeline.frame',
+    'disabled-by-default-devtools.timeline.inputs',
+    'disabled-by-default-devtools.timeline.invalidationTracking',
+    'disabled-by-default-devtools.timeline.layers',
+    'disabled-by-default-devtools.timeline.picture',
+    'disabled-by-default-file',
+    'disabled-by-default-fonts',
+    'disabled-by-default-gpu_cmd_queue',
+    'disabled-by-default-gpu.dawn',
+    'disabled-by-default-gpu.debug',
+    'disabled-by-default-gpu_decoder',
+    'disabled-by-default-gpu.device',
+    'disabled-by-default-gpu.service',
+    'disabled-by-default-histogram_samples',
+    'disabled-by-default-ipc.flow',
+    'disabled-by-default-java-heap-profiler',
+    'disabled-by-default-layer-element',
+    'disabled-by-default-lifecycles',
+    'disabled-by-default-loading',
+    'disabled-by-default-memory-infra',
+    'disabled-by-default-memory-infra.v8.code_stats',
+    'disabled-by-default-net',
+    'disabled-by-default-network',
+    'disabled-by-default-paint-worklet',
+    'disabled-by-default-power',
+    'disabled-by-default-renderer.scheduler',
+    'disabled-by-default-renderer.scheduler.debug',
+    'disabled-by-default-sequence_manager',
+    'disabled-by-default-sequence_manager.debug',
+    'disabled-by-default-sequence_manager.verbose_snapshots',
+    'disabled-by-default-skia',
+    'disabled-by-default-skia.gpu',
+    'disabled-by-default-skia.gpu.cache',
+    'disabled-by-default-SyncFileSystem',
+    'disabled-by-default-system_stats',
+    'disabled-by-default-thread_pool_diagnostics',
+    'disabled-by-default-toplevel.flow',
+    'disabled-by-default-toplevel.ipc',
+    'disabled-by-default-v8.compile',
+    'disabled-by-default-v8.cpu_profiler',
+    'disabled-by-default-v8.cpu_profiler.hires',
+    'disabled-by-default-v8.gc',
+    'disabled-by-default-v8.gc_stats',
+    'disabled-by-default-v8.ic_stats',
+    'disabled-by-default-v8.runtime',
+    'disabled-by-default-v8.runtime_stats',
+    'disabled-by-default-v8.runtime_stats_sampling',
+    'disabled-by-default-v8.turbofan',
+    'disabled-by-default-v8.wasm',
+    'disabled-by-default-video_and_image_capture',
+    'disabled-by-default-viz.debug.overlay_planes',
+    'disabled-by-default-viz.hit_testing_flow',
+    'disabled-by-default-viz.overdraw',
+    'disabled-by-default-viz.quads',
+    'disabled-by-default-viz.surface_id_flow',
+    'disabled-by-default-viz.surface_lifetime',
+    'disabled-by-default-viz.triangles',
+    'disabled-by-default-worker.scheduler',
+  ];
+}
+
 export function createEmptyState(): State {
   return {
     route: null,
diff --git a/ui/src/frontend/record_page.ts b/ui/src/frontend/record_page.ts
index 135c874..65e0dcf 100644
--- a/ui/src/frontend/record_page.ts
+++ b/ui/src/frontend/record_page.ts
@@ -20,6 +20,7 @@
 import {MeminfoCounters, VmstatCounters} from '../common/protos';
 import {
   AdbRecordingTarget,
+  getBuiltinChromeCategoryList,
   getDefaultRecordingTargets,
   isAdbTarget,
   isAndroidTarget,
@@ -592,11 +593,13 @@
 }
 
 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 [];
+  // If we are attempting to record via the Chrome extension, we receive the
+  // list of actually supported categories via DevTools. Otherwise, we fall back
+  // to an integrated list of categories from a recent version of Chrome.
+  let categories = globals.state.chromeCategories;
+  if (!categories || !isChromeTarget(globals.state.recordingTarget)) {
+    categories = getBuiltinChromeCategoryList();
+  }
 
   // Show "disabled-by-default" categories last.
   const categoriesMap = new Map<string, string>();
@@ -613,6 +616,7 @@
     categoriesMap.set(
         cat, `${cat.replace(disabledPrefix, '')} (high overhead)`);
   });
+
   return m(Dropdown, {
     title: 'Additional Chrome categories',
     cssClass: '.multicolumn.two-columns.chrome-categories',