|  | // Copyright (C) 2023 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. | 
|  |  | 
|  | // Keep this import first. | 
|  | import '../base/static_initializers'; | 
|  |  | 
|  | import m from 'mithril'; | 
|  |  | 
|  | import {defer} from '../base/deferred'; | 
|  | import {reportError, setErrorHandler} from '../base/logging'; | 
|  | import {initLiveReloadIfLocalhost} from '../core/live_reload'; | 
|  | import {raf} from '../core/raf_scheduler'; | 
|  | import {setScheduleFullRedraw} from '../widgets/raf'; | 
|  |  | 
|  | function getRoot() { | 
|  | // Works out the root directory where the content should be served from | 
|  | // e.g. `http://origin/v1.2.3/`. | 
|  | const script = document.currentScript as HTMLScriptElement; | 
|  |  | 
|  | // Needed for DOM tests, that do not have script element. | 
|  | if (script === null) { | 
|  | return ''; | 
|  | } | 
|  |  | 
|  | let root = script.src; | 
|  | root = root.substr(0, root.lastIndexOf('/') + 1); | 
|  | return root; | 
|  | } | 
|  |  | 
|  | function setupContentSecurityPolicy() { | 
|  | // Note: self and sha-xxx must be quoted, urls data: and blob: must not. | 
|  | const policy = { | 
|  | 'default-src': [ | 
|  | `'self'`, | 
|  | ], | 
|  | 'script-src': [ | 
|  | `'self'`, | 
|  | ], | 
|  | 'object-src': ['none'], | 
|  | 'connect-src': [ | 
|  | `'self'`, | 
|  | ], | 
|  | 'img-src': [ | 
|  | `'self'`, | 
|  | 'data:', | 
|  | 'blob:', | 
|  | ], | 
|  | 'style-src': [ | 
|  | `'self'`, | 
|  | ], | 
|  | 'navigate-to': ['https://*.perfetto.dev', 'self'], | 
|  | }; | 
|  | const meta = document.createElement('meta'); | 
|  | meta.httpEquiv = 'Content-Security-Policy'; | 
|  | let policyStr = ''; | 
|  | for (const [key, list] of Object.entries(policy)) { | 
|  | policyStr += `${key} ${list.join(' ')}; `; | 
|  | } | 
|  | meta.content = policyStr; | 
|  | document.head.appendChild(meta); | 
|  | } | 
|  |  | 
|  | function main() { | 
|  | // Wire up raf for widgets. | 
|  | setScheduleFullRedraw(() => raf.scheduleFullRedraw()); | 
|  |  | 
|  | setupContentSecurityPolicy(); | 
|  |  | 
|  | // Load the css. The load is asynchronous and the CSS is not ready by the time | 
|  | // appendChild returns. | 
|  | const root = getRoot(); | 
|  | const cssLoadPromise = defer<void>(); | 
|  | const css = document.createElement('link'); | 
|  | css.rel = 'stylesheet'; | 
|  | css.href = root + 'perfetto.css'; | 
|  | css.onload = () => cssLoadPromise.resolve(); | 
|  | css.onerror = (err) => cssLoadPromise.reject(err); | 
|  | const favicon = document.head.querySelector('#favicon') as HTMLLinkElement; | 
|  | if (favicon) favicon.href = root + 'assets/favicon.png'; | 
|  |  | 
|  | document.head.append(css); | 
|  |  | 
|  | // Add Error handlers for JS error and for uncaught exceptions in promises. | 
|  | setErrorHandler((err: string) => console.log(err)); | 
|  | window.addEventListener('error', (e) => reportError(e)); | 
|  | window.addEventListener('unhandledrejection', (e) => reportError(e)); | 
|  |  | 
|  | // Prevent pinch zoom. | 
|  | document.body.addEventListener('wheel', (e: MouseEvent) => { | 
|  | if (e.ctrlKey) e.preventDefault(); | 
|  | }, {passive: false}); | 
|  |  | 
|  | cssLoadPromise.then(() => onCssLoaded()); | 
|  | } | 
|  |  | 
|  | function onCssLoaded() { | 
|  | // Clear all the contents of the initial page (e.g. the <pre> error message) | 
|  | // And replace it with the root <main> element which will be used by mithril. | 
|  | document.body.innerHTML = ''; | 
|  |  | 
|  | raf.domRedraw = () => { | 
|  | m.render(document.body, m('div')); | 
|  | }; | 
|  |  | 
|  | initLiveReloadIfLocalhost(false); | 
|  | } | 
|  |  | 
|  | main(); |