File size: 5,755 Bytes
3864f2f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Perfetto Trace Relay</title>
    <style>
        body { font-family: sans-serif; padding: 2em; }
    </style>
</head>
<body>
<h1>Perfetto Trace Relay</h1>
<p id="status">Initializing...</p>

<script>
    (async () => {
        const statusElement = document.getElementById('status');
        const PERFETTO_UI_URL = 'https://ui.perfetto.dev';

        /**
         * Displays a final message on the relay page itself.
         * @param {string} message The message to display.
         * @param {boolean} isError If true, display as an error.
         */
        const setStatus = (message, isError = false) => {
            console.log(message);
            statusElement.textContent = message;
            if (isError) {
                statusElement.style.color = 'red';
                console.error(message);
            }
        };

        /**
         * Establishes a handshake with the Perfetto UI window to ensure it's ready.
         * The Perfetto UI will respond with 'PONG' when it receives a 'PING'.
         * @param {Window} ui The window handle for the Perfetto UI.
         * @returns {Promise<void>} A promise that resolves when the handshake is complete.
         */
        const establishHandshake = (ui) => {
            return new Promise((resolve, reject) => {
                let intervalId = null;
                let timeoutId = null;

                const cleanup = () => {
                    clearInterval(intervalId);
                    clearTimeout(timeoutId);
                    window.removeEventListener('message', messageHandler);
                };

                const messageHandler = (event) => {
                    if (event.source === ui && event.data === 'PONG') {
                        cleanup();
                        resolve();
                    }
                };

                window.addEventListener('message', messageHandler);

                // 1. Start pinging the UI
                intervalId = setInterval(() => {
                    // The try-catch is necessary because the UI window might be closed by the user.
                    try {
                        ui.postMessage('PING', PERFETTO_UI_URL);
                    } catch (e) {
                        // If postMessage fails, it's likely the window was closed.
                        cleanup();
                        reject(new Error('Perfetto UI window was closed before handshake could complete.'));
                    }
                }, 200);

                // 2. Set a timeout for the handshake
                timeoutId = setTimeout(() => {
                    cleanup();
                    reject(new Error('Handshake with Perfetto UI timed out. The UI might not have loaded correctly.'));
                }, 10000); // 10-second timeout
            });
        };

        /**
         * Fetches the trace file and posts it to the Perfetto UI.
         * @param {Window} ui The window handle for the Perfetto UI.
         * @param {string} srcUrl The URL of the trace file to fetch.
         * @param {string | null} customTitle An optional title for the trace.
         */
        const fetchTraceAndPost = async (ui, srcUrl, customTitle) => {
            // This fetch is executed on your domain, so it can handle auth/CORS/ACLs.
            const response = await fetch(srcUrl, {
                mode: 'cors',
                credentials: 'omit', // Avoids "ACAO: * + credentials" error.
                cache: 'no-store',   // Ensures the latest trace is always fetched.
            });

            if (!response.ok) {
                const errorMsg = `Failed to load trace: ${response.status} ${response.statusText}`;
                ui.postMessage({ perfetto: { title: errorMsg } }, PERFETTO_UI_URL);
                throw new Error(errorMsg);
            }

            const buffer = await response.arrayBuffer();

            // Determine a sensible title and filename for the trace.
            const title = customTitle || srcUrl.split('/').pop() || 'trace';
            const fileName = title.endsWith('.gz') || title.endsWith('.zip') ? title : `${title}.perfetto-trace`;

            // Post the trace buffer to the Perfetto UI.
            ui.postMessage({
                perfetto: {
                    buffer,
                    title,
                    fileName,
                    // Keep the UI open after loading, useful for debugging.
                    keepApiOpen: true,
                }
            }, PERFETTO_UI_URL, [buffer]); // Transfer the buffer for performance.
        };


        // --- Main Execution Logic ---
        try {
            const params = new URLSearchParams(location.search);
            const src = params.get('src');
            const title = params.get('title');

            if (!src) {
                throw new Error('Required "src" URL parameter is missing.');
            }

            setStatus('Opening Perfetto UI...');
            const ui = window.open(PERFETTO_UI_URL);
            if (!ui) {
                throw new Error('Failed to open Perfetto UI. Please disable your popup blocker for this site.');
            }

            setStatus('Waiting for handshake with Perfetto UI...');
            await establishHandshake(ui);

            setStatus(`Fetching trace from ${src}...`);
            await fetchTraceAndPost(ui, src, title);

            setStatus('Trace successfully sent to Perfetto UI. You can now close this tab.');
            // Optionally, close the relay window automatically.
            // window.close();

        } catch (error) {
            setStatus(error.message, true);
        }
    })();
</script>
</body>
</html>