xref: /aosp_15_r20/external/perfetto/ui/src/bigtrace/index.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1// Copyright (C) 2023 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Keep this import first.
16import '../base/static_initializers';
17import m from 'mithril';
18import {defer} from '../base/deferred';
19import {reportError, addErrorHandler, ErrorDetails} from '../base/logging';
20import {initLiveReloadIfLocalhost} from '../core/live_reload';
21import {raf} from '../core/raf_scheduler';
22import {setScheduleFullRedraw} from '../widgets/raf';
23
24function getRoot() {
25  // Works out the root directory where the content should be served from
26  // e.g. `http://origin/v1.2.3/`.
27  const script = document.currentScript as HTMLScriptElement;
28
29  // Needed for DOM tests, that do not have script element.
30  if (script === null) {
31    return '';
32  }
33
34  let root = script.src;
35  root = root.substr(0, root.lastIndexOf('/') + 1);
36  return root;
37}
38
39function setupContentSecurityPolicy() {
40  // Note: self and sha-xxx must be quoted, urls data: and blob: must not.
41  const policy = {
42    'default-src': [
43      `'self'`,
44    ],
45    'script-src': [
46      `'self'`,
47    ],
48    'object-src': ['none'],
49    'connect-src': [
50      `'self'`,
51    ],
52    'img-src': [
53      `'self'`,
54      'data:',
55      'blob:',
56    ],
57    'style-src': [
58      `'self'`,
59    ],
60    'navigate-to': ['https://*.perfetto.dev', 'self'],
61  };
62  const meta = document.createElement('meta');
63  meta.httpEquiv = 'Content-Security-Policy';
64  let policyStr = '';
65  for (const [key, list] of Object.entries(policy)) {
66    policyStr += `${key} ${list.join(' ')}; `;
67  }
68  meta.content = policyStr;
69  document.head.appendChild(meta);
70}
71
72function main() {
73  // Wire up raf for widgets.
74  setScheduleFullRedraw(() => raf.scheduleFullRedraw());
75
76  setupContentSecurityPolicy();
77
78  // Load the css. The load is asynchronous and the CSS is not ready by the time
79  // appendChild returns.
80  const root = getRoot();
81  const cssLoadPromise = defer<void>();
82  const css = document.createElement('link');
83  css.rel = 'stylesheet';
84  css.href = root + 'perfetto.css';
85  css.onload = () => cssLoadPromise.resolve();
86  css.onerror = (err) => cssLoadPromise.reject(err);
87  const favicon = document.head.querySelector('#favicon') as HTMLLinkElement;
88  if (favicon) favicon.href = root + 'assets/favicon.png';
89
90  document.head.append(css);
91
92  // Add Error handlers for JS error and for uncaught exceptions in promises.
93  addErrorHandler((err: ErrorDetails) => console.log(err.message, err.stack));
94  window.addEventListener('error', (e) => reportError(e));
95  window.addEventListener('unhandledrejection', (e) => reportError(e));
96
97  // Prevent pinch zoom.
98  document.body.addEventListener('wheel', (e: MouseEvent) => {
99    if (e.ctrlKey) e.preventDefault();
100  }, {passive: false});
101
102  cssLoadPromise.then(() => onCssLoaded());
103}
104
105function onCssLoaded() {
106  // Clear all the contents of the initial page (e.g. the <pre> error message)
107  // And replace it with the root <main> element which will be used by mithril.
108  document.body.innerHTML = '';
109
110  raf.domRedraw = () => {
111    m.render(document.body, m('div'));
112  };
113
114  initLiveReloadIfLocalhost(false);
115}
116
117main();
118