Merge "ui/extension: Record using Chrome's browser DevTools target"
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',