mirror of
https://github.com/sol1/rustguac.git
synced 2026-10-25 13:09:56 +00:00
d1d2cdf859
A browser only lets the tab that holds the Connect gesture go fullscreen;
opening a new tab drops that gesture, so a fullscreen-on-connect entry
always fell back to a manual click. Connect now fullscreens the CURRENT
tab on the click — the one moment a valid gesture exists — and loads the
session in a full-viewport same-origin iframe (rgLaunchFullscreen in
search.js, shared by the connections page and the global search): overlay,
spinner and an Exit button, torn down on Exit, on fullscreen exit or on a
session-end message from the embedded client. The client in ?embedded=1
mode skips all in-client fullscreen logic (the parent tab owns it) and
posts ready/end to the parent. X-Frame-Options becomes SAMEORIGIN (was
DENY) with CSP frame-ancestors 'self', so a same-origin page can host the
client in a frame while cross-origin framing stays blocked.
The final viewport size cannot be predicted (a fullscreen viewport on a
notched Mac is shorter than the screen), so it is measured: the launcher
enters fullscreen, waits until the viewport has held one size for 150ms,
then sends the connect with that width and height; a refused request
connects at the tab's size at once and a 2.5s cap never leaves the user on
a spinner. The embedded client shows its connect cover from load and lifts
it — then runs the post-connect fit sends — only on the launcher's
'viewport' message, so the user sees only the final picture. The ⧉ (open
in a new window) button opens the client explicitly windowed (fs=0) on a
small splash page, static/launch.html, which measures its own viewport
once stable and posts it to the opener (rgOpenSessionWindow); the session
is created at that size, so nothing resizes on load. No screen_* is sent
for a popup. When the browser refuses the load-time request, the next
click or key enters fullscreen, taken in the capture phase and never sent
to the remote, and everything is released once fullscreen lands.
Fixes #257
tests: same-origin framing only, and the zero-click fullscreen launcher
The CSP test pins frame-ancestors to exactly 'self'. The browser spec drives
Connect on a fullscreen-on-connect entry: the launcher overlay opens in the
same tab, the connect request carries the measured viewport and no screen
size, the embedded client loads in the frame, and Exit tears it down.
(cherry picked from commit ea456b696f)
60 lines
3.7 KiB
HTML
60 lines
3.7 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Connecting…</title>
|
|
<style>
|
|
:root { --bg: #1a1a2e; --text: #e0e0e0; --text-dim: #888; --accent: #5bc0be; --border: #333; }
|
|
html, body { margin: 0; height: 100%; background: var(--bg); color: var(--text); font-family: monospace; }
|
|
#box { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; text-align: center; }
|
|
#spinner { width: 30px; height: 30px; margin: 0 auto 1.2em; border: 3px solid var(--border); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.8s linear infinite; }
|
|
@keyframes spin { to { transform: rotate(360deg); } }
|
|
#title { font-size: 1.3em; font-weight: bold; margin-bottom: 0.5em; }
|
|
#status { color: var(--text-dim); font-size: 0.95em; letter-spacing: 0.02em; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="box"><div><div id="spinner"></div><div id="title"></div><div id="status">Connecting…</div></div></div>
|
|
<script>
|
|
// Popup splash for "open in a new window" (⧉). A popup's viewport size
|
|
// cannot be read reliably from the opener right after window.open (it
|
|
// reports the opener's size until the new window is laid out), so the
|
|
// popup measures itself: once loaded and the size has held for 150ms,
|
|
// it tells the opener, which creates the session at exactly that size
|
|
// and navigates this window to it. No resize after the first paint.
|
|
(function () {
|
|
var q = new URLSearchParams(location.search);
|
|
var name = q.get('name') || 'Session';
|
|
document.getElementById('title').textContent = name;
|
|
document.title = name + ' — connecting…';
|
|
// Match the site theme (same source as the client page).
|
|
fetch('/api/auth/status', { credentials: 'same-origin' }).then(function (r) { return r.json(); }).then(function (d) {
|
|
var t = d && d.theme; if (!t) return;
|
|
var u = null; try { u = localStorage.getItem('rustguac_theme'); } catch (e) {}
|
|
var presets = t.presets || {}, admin = t.admin_preset || 'aurora';
|
|
var active = u && presets[u] ? u : admin;
|
|
var colors = (active === admin) ? t.admin_colors : presets[active];
|
|
if (!colors) return;
|
|
for (var k in colors) if (Object.prototype.hasOwnProperty.call(colors, k)) document.documentElement.style.setProperty('--' + String(k).replace(/_/g, '-'), colors[k]);
|
|
}).catch(function () {});
|
|
if (!window.opener) { document.getElementById('status').textContent = 'Opened directly — returning to Connections…'; setTimeout(function () { location.replace('/connections.html'); }, 1500); return; }
|
|
var sent = false, timer = null, lastW = 0, lastH = 0;
|
|
function send() {
|
|
if (sent) return; sent = true;
|
|
window.removeEventListener('resize', arm);
|
|
try { window.opener.postMessage({ rgLaunch: 'size', width: window.innerWidth, height: window.innerHeight }, location.origin); } catch (e) {}
|
|
}
|
|
function arm() {
|
|
lastW = window.innerWidth; lastH = window.innerHeight;
|
|
clearTimeout(timer);
|
|
timer = setTimeout(function () { if (window.innerWidth === lastW && window.innerHeight === lastH) send(); else arm(); }, 150);
|
|
}
|
|
window.addEventListener('resize', arm);
|
|
if (document.readyState === 'complete') arm(); else window.addEventListener('load', arm);
|
|
setTimeout(send, 2000); // never keep the opener waiting
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|