Add toggle for block client.

Screenshots: https://imgur.com/a/w0UT3wQ

Change-Id: I986b73999969c4bf8f4a471d6fda85ecba1aaf39
diff --git a/ui/src/assets/record.scss b/ui/src/assets/record.scss
index 02dab44..5714119 100644
--- a/ui/src/assets/record.scss
+++ b/ui/src/assets/record.scss
@@ -506,6 +506,81 @@
     }
   }  // probe
 
+  .toggle {
+    transition: color 0.2s ease;
+    padding-top: var(--record-section-padding);
+    padding-bottom: var(--record-section-padding);
+
+    &:hover {
+      >img { opacity: 1; }
+      >label {
+        color: #333;
+        input[type=checkbox]::after {
+          background: hsl(207, 60%, 60%);
+        }
+      }
+    }  // :hover
+
+    >label {
+      cursor: pointer;
+      font-size: 14px;
+      color: var(--record-text-color);
+
+      // The per-probe on-off switch.
+      input[type=checkbox] {
+        -moz-appearance: none;
+        -webkit-appearance: none;
+        cursor: pointer;
+        margin: 0 12px 0 2px;
+        position: relative;
+        display: inline-block;
+        height: 10px;
+        width: 22px;
+        background: #89898966;
+        border-radius: 100px;
+        transition: all 0.3s ease;
+        overflow: visible;
+        vertical-align: middle;
+
+        &:focus {
+          outline: none;
+        }
+
+        &::after {
+          position: absolute;
+          left: -5px;
+          top: -5px;
+          display: block;
+          width: 20px;
+          height: 20px;
+          border-radius: 100px;
+          background: #f5f5f5;
+          box-shadow: 0px 3px 3px rgba(0,0,0,0.15);
+          content: '';
+          transition: all 0.3s ease;
+        }
+        &:checked {
+          background: #8398b7;
+        }
+        &:focus::after {
+          background: hsl(207, 60%, 60%);
+        }
+        &:checked::after {
+          left: 12px;
+          background: #27303d;
+        }
+      }  // checkbox
+    }  // label
+
+    // The content of the toggle section.
+    >div.descr {
+      font-size: 12px;
+      min-height: 50px;
+      color: #666;
+      line-height: 20px;
+    }
+  }  // toggle
+
   // The three "Stop when full", "Ring buffer", "Long trace" buttons.
   .record-mode {
     display: grid;
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index 97ee845..6d4ba72 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -416,6 +416,7 @@
   hpContinuousDumpsPhase: number;
   hpContinuousDumpsInterval: number;
   hpSharedMemoryBuffer: number;
+  hpBlockClient: boolean;
 
   javaHeapDump: boolean;
   jpProcesses: string;
@@ -479,6 +480,7 @@
     hpContinuousDumpsPhase: 0,
     hpContinuousDumpsInterval: 0,
     hpSharedMemoryBuffer: 8 * 1048576,
+    hpBlockClient: true,
 
     javaHeapDump: false,
     jpProcesses: '',
diff --git a/ui/src/controller/record_controller.ts b/ui/src/controller/record_controller.ts
index d6aa111..5525778 100644
--- a/ui/src/controller/record_controller.ts
+++ b/ui/src/controller/record_controller.ts
@@ -269,8 +269,7 @@
         cdc.dumpPhaseMs = uiCfg.hpContinuousDumpsPhase;
       }
     }
-    // TODO(fmayer): Add a toggle for this to the UI?
-    cfg.blockClient = true;
+    cfg.blockClient = uiCfg.hpBlockClient;
     heapprofd = cfg;
   }
 
diff --git a/ui/src/frontend/record_page.ts b/ui/src/frontend/record_page.ts
index 05b46a1..6769e65 100644
--- a/ui/src/frontend/record_page.ts
+++ b/ui/src/frontend/record_page.ts
@@ -44,7 +44,9 @@
   Slider,
   SliderAttrs,
   Textarea,
-  TextareaAttrs
+  TextareaAttrs,
+  Toggle,
+  ToggleAttrs
 } from './record_widgets';
 import {Router} from './router';
 
@@ -388,7 +390,14 @@
         min: 0,
         set: (cfg, val) => cfg.hpSharedMemoryBuffer = val,
         get: (cfg) => cfg.hpSharedMemoryBuffer
-      } as SliderAttrs)
+      } as SliderAttrs),
+      m(Toggle, {
+        title: 'Block client',
+        cssClass: '.thin',
+        descr: `Slow down target application if profiler cannot keep up.`,
+        setEnabled: (cfg, val) => cfg.hpBlockClient = val,
+        isEnabled: (cfg) => cfg.hpBlockClient
+      } as ToggleAttrs)
       // TODO(taylori): Add advanced options.
   );
 }
diff --git a/ui/src/frontend/record_widgets.ts b/ui/src/frontend/record_widgets.ts
index c31545e..2e6db1f 100644
--- a/ui/src/frontend/record_widgets.ts
+++ b/ui/src/frontend/record_widgets.ts
@@ -85,6 +85,43 @@
   }
 }
 
+// +-------------------------------------------------------------+
+// | Toggle: an on/off switch.
+// +-------------------------------------------------------------+
+
+export interface ToggleAttrs {
+  title: string;
+  descr: string;
+  cssClass?: string;
+  isEnabled: Getter<boolean>;
+  setEnabled: Setter<boolean>;
+}
+
+export class Toggle implements m.ClassComponent<ToggleAttrs> {
+  view({attrs}: m.CVnode<ToggleAttrs>) {
+    const onToggle = (enabled: boolean) => {
+      const traceCfg = produce(globals.state.recordConfig, draft => {
+        attrs.setEnabled(draft, enabled);
+      });
+      globals.dispatch(Actions.setRecordConfig({config: traceCfg}));
+    };
+
+    const enabled = attrs.isEnabled(globals.state.recordConfig);
+
+    return m(
+        `.toggle${enabled ? '.enabled' : ''}${attrs.cssClass || ''}`,
+        m('label',
+          m(`input[type=checkbox]`, {
+            checked: enabled,
+            oninput: (e: InputEvent) => {
+              onToggle((e.target as HTMLInputElement).checked);
+            },
+          }),
+          m('span', attrs.title)),
+        m('.descr', attrs.descr));
+  }
+}
+
 // +---------------------------------------------------------------------------+
 // | Slider: draggable horizontal slider with numeric spinner.                 |
 // +---------------------------------------------------------------------------+