143 lines
5.7 KiB
Markdown
143 lines
5.7 KiB
Markdown
# Deep linking to the Perfetto UI
|
|
|
|
This document describes how to open traces hosted on external servers with the
|
|
Perfetto UI. This can help integrating the Perfetto UI with custom dashboards
|
|
and implement _'Open with Perfetto UI'_-like features.
|
|
|
|
## Using window.open and postMessage
|
|
|
|
The supported way of doing this is to _inject_ the trace as an ArrayBuffer
|
|
via `window.open('https://ui.perfetto.dev')` and `postMessage()`.
|
|
In order to do this you need some minimal JavaScript code running on some
|
|
hosting infrastructure you control which can access the trace file. In most
|
|
cases this is some dashboard which you want to deep-link to the Perfetto UI.
|
|
|
|
#### Open ui.perfetto.dev via window.open
|
|
|
|
The source dashboard, the one that knows how to locate a trace and deal with
|
|
ACL checking / oauth authentication and the like, creates a new tab by doing
|
|
|
|
```js
|
|
var handle = window.open('https://ui.perfetto.dev');
|
|
```
|
|
|
|
The window handle allows bidirectional communication using `postMessage()`
|
|
between the source dashboard and the Perfetto UI.
|
|
|
|
#### Wait for the UI to be ready via PING/PONG
|
|
|
|
Wait for the UI to be ready. The `window.open()` message channel is not
|
|
buffered. If you send a message before the opened page has registered an
|
|
`onmessage` listener the messagge will be dropped on the floor.
|
|
In order to avoid this race, you can use a very basic PING/PONG protocol: keep
|
|
sending a 'PING' message until the opened window replies with a 'PONG'.
|
|
When this happens, that is the signal that the Perfetto UI is ready to open
|
|
traces.
|
|
|
|
#### Post a message the following JavaScript object
|
|
|
|
```js
|
|
{
|
|
'perfetto': {
|
|
buffer: ArrayBuffer;
|
|
title: string;
|
|
fileName?: string; // Optional
|
|
url?: string; // Optional
|
|
}
|
|
}
|
|
```
|
|
|
|
`buffer` is the ArrayBuffer with the actual trace file content. This is
|
|
typically something that you obtain by doing a `fetch()` on your backend
|
|
storage.
|
|
|
|
`title` is the human friendly trace title that will be shown in the
|
|
sidebar. This can help people to disambiguate traces from several tabs.
|
|
|
|
`fileName` will be used if the user clicks on "Download". A generic name will
|
|
be used if omitted.
|
|
|
|
`url` is used if the user clicks on the "Share" link in the sidebar. This should
|
|
print to a URL owned by you that would cause your dashboard to re-open the
|
|
current trace, by re-kicking-off the window.open() process herein described.
|
|
If omitted traces won't be shareable.
|
|
|
|
### Code samples
|
|
|
|
See [this example caller](https://gistcdn.rawgit.org/primiano/e164868b617844ef8fa4770eb3b323b9/1d9aa2bf52cf903709ea7dd4d583fd2d07d7a255/open_with_perfetto_ui.html),
|
|
for which the code is in
|
|
[this GitHub gist](https://gist.github.com/primiano/e164868b617844ef8fa4770eb3b323b9).
|
|
|
|
Googlers: take a look at the
|
|
[existing examples in the internal codesearch](http://go/perfetto-ui-deeplink-cs)
|
|
|
|
### Common pitfalls
|
|
|
|
Many browsers sometimes block window.open() requests prompting the user to allow
|
|
popups for the site. This usually happens if:
|
|
|
|
- The window.open() is NOT initiated by a user gesture.
|
|
- Too much time is passed from the user gesture to the window.open()
|
|
|
|
If the trace file is big enough, the fetch() might take long time and pass the
|
|
user gesture threshold. This can be detected by observing that the window.open()
|
|
returned `null`. When this happens the best option is to show another clickable
|
|
element and bind the fetched trace ArrayBuffer to the new onclick handler, like
|
|
the code in the example above does.
|
|
|
|
Some browser can have a variable time threshold for the user gesture timeout
|
|
which depends on the website engagement score (how much the user has visited
|
|
the page that does the window.open() before). It's quite common when testing
|
|
this code to see a popup blocker the first time the new feature is used and
|
|
then not see it again.
|
|
|
|
### Where does the posted trace go?
|
|
|
|
The Perfetto UI is client-only and doesn't require any server-side interaction.
|
|
Traces pushed via postMessage() are kept only in the browser memory/cache and
|
|
are not sent to any server.
|
|
|
|
## Why can't I just pass a URL?
|
|
|
|
_"Why you don't let me just pass a URL to the Perfetto UI (e.g. ui.perfetto.dev?url=...) and you deal with all this?"_
|
|
|
|
The answer to this is manifold and boils down to security.
|
|
|
|
#### Cross origin requests blocking
|
|
|
|
If ui.perfetto.dev had to do a `fetch('https://yourwebsite.com/trace')` that
|
|
would be a cross-origin request. Browsers disallow by default cross-origin
|
|
fetch requests.
|
|
In order for this to work, the web server that hosts yourwebsite.com would have
|
|
to expose a custom HTTP response header
|
|
(`Access-Control-Allow-Origin: https://ui.perfetto.dev`) to allow the fetch.
|
|
In most cases customizing the HTTP response headers is outside of dashboard's
|
|
owners control.
|
|
|
|
You can learn more about CORS at
|
|
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
|
|
|
#### Content Security Policy
|
|
|
|
Perfetto UI uses a strict Content Security Policy which disallows foreign
|
|
fetches and subresources, as a security mitigation about common attacks.
|
|
Even assuming that CORS headers are properly set and your trace files are
|
|
publicly accessible, fetching the trace from the Perfetto UI would require
|
|
allow-listing your origin in our CSP policy. This is not scalable.
|
|
|
|
You can learn more about CSP at
|
|
https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
|
|
|
|
#### Dealing with OAuth2 or other authentication mechanisms
|
|
|
|
Even ignoring CORS, the Perfetto UI would have to deal with OAuth2 or other
|
|
authentication mechanisms to fetch the trace file. Even if all the dashboards
|
|
out there used OAuth2, that would still mean that Perfetto UI would have to know
|
|
about all the possible OAuth2 scopes, one for each dashboard. This is not
|
|
scalable.
|
|
|
|
## Source links
|
|
|
|
The source code that deals with the postMessage() in the Perfetto UI is
|
|
[`post_message_handler.ts`](/ui/src/frontend/post_message_handler.ts)
|