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>
|