Merge "Speculatively fix windows build and unblock autoroller"
diff --git a/ui/src/assets/perfetto.scss b/ui/src/assets/perfetto.scss
index cac3d75..8ef1161 100644
--- a/ui/src/assets/perfetto.scss
+++ b/ui/src/assets/perfetto.scss
@@ -312,6 +312,19 @@
   height: 25px;
 }
 
+header.overview {
+  display: flex;
+  justify-content: space-between;
+}
+
+.query-error {
+  user-select: text;
+}
+
+span.code {
+  user-select: text;
+}
+
 .text-column {
   font-size: 115%;
   // 2-3 alphabets per line is comfortable for reading.
diff --git a/ui/src/frontend/clipboard.ts b/ui/src/frontend/clipboard.ts
new file mode 100644
index 0000000..28d70be
--- /dev/null
+++ b/ui/src/frontend/clipboard.ts
@@ -0,0 +1,23 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+export async function copyToClipboard(text: string): Promise<void> {
+  try {
+    // TODO(hjd): Fix typescript type for navigator.
+    // tslint:disable-next-line no-any
+    await(navigator as any).clipboard.writeText(text);
+  } catch (err) {
+    console.error(`Failed to copy "${text}" to clipboard: ${err}`);
+  }
+}
diff --git a/ui/src/frontend/record_page.ts b/ui/src/frontend/record_page.ts
index 79d8417..21e295e 100644
--- a/ui/src/frontend/record_page.ts
+++ b/ui/src/frontend/record_page.ts
@@ -14,21 +14,12 @@
 
 import * as m from 'mithril';
 
+import {copyToClipboard} from './clipboard';
 import {createPage} from './pages';
 
 const RECORD_COMMAND_LINE =
     'echo CgYIgKAGIAESIwohCgxsaW51eC5mdHJhY2UQAKIGDhIFc2NoZWQSBWlucHV0GJBOMh0KFnBlcmZldHRvLnRyYWNlZF9wcm9iZXMQgCAYBEAASAA= | base64 --decode | adb shell "perfetto -c - -o /data/misc/perfetto-traces/trace" && adb pull /data/misc/perfetto-traces/trace /tmp/trace';
 
-async function copyToClipboard(text: string): Promise<void> {
-  try {
-    // TODO(hjd): Fix typescript type for navigator.
-    // tslint:disable-next-line no-any
-    await(navigator as any).clipboard.writeText(text);
-  } catch (err) {
-    console.error(`Failed to copy "${text}" to clipboard: ${err}`);
-  }
-}
-
 interface CodeSampleAttrs {
   text: string;
 }
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index adca962..d89bd5d 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -17,6 +17,7 @@
 import {QueryResponse} from '../common/queries';
 import {TimeSpan} from '../common/time';
 
+import {copyToClipboard} from './clipboard';
 import {FlameGraphPanel} from './flame_graph_panel';
 import {globals} from './globals';
 import {HeaderPanel} from './header_panel';
@@ -54,8 +55,27 @@
     return m(
         'div',
         m('header.overview',
-          `Query result - ${Math.round(resp.durationMs)} ms`,
-          m('span.code', resp.query)),
+          m('span',
+            `Query result - ${Math.round(resp.durationMs)} ms`,
+            m('span.code', resp.query)),
+          resp.error ? null :
+                       m('button',
+                         {
+                           onclick: () => {
+                             const lines: string[][] = [];
+                             lines.push(resp.columns);
+                             for (const row of resp.rows) {
+                               const line = [];
+                               for (const col of resp.columns) {
+                                 line.push(row[col].toString());
+                               }
+                               lines.push(line);
+                             }
+                             copyToClipboard(
+                                 lines.map(line => line.join('\t')).join('\n'));
+                           },
+                         },
+                         'Copy as .tsv')),
         resp.error ?
             m('.query-error', `SQL error: ${resp.error}`) :
             m('table.query-table', m('thead', header), m('tbody', rows)));