mirror of
https://github.com/HeyPuter/puter.git
synced 2026-08-25 07:27:04 +00:00
Poll for the decision when the popup's opener is severed
crossOriginIsolated was the test for "the popup can't message us back", but being isolated also requires COEP. A site sending COOP: same-origin on its own still has its opener relationship severed when it opens the Puter popup, and took the watch-the-window path instead — where the detached proxy reports closed === true on the first tick, so requestPermission resolved false about a second after the popup opened, while the user was still reading the dialog. Their "Allow" then had nowhere to go. Treat an already-closed popup as severed and poll. Pin the expected event.source before those early returns. popupWindow was assigned after them, so for the whole consent-dialog wait — as long as the user takes to click Continue — the handler accepted a decision from any window on the GUI origin. A forged answer is only advisory since the grant is written server-side, but the check may as well hold. Settle instead of rejecting when the consent dialog can't be appended: document.body is null in a <head> script, and the throw both rejected a promise documented to resolve to a boolean and left the message listener behind.
This commit is contained in:
@@ -1194,15 +1194,23 @@ class UI extends EventListener {
|
|||||||
settle(false);
|
settle(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( window.crossOriginIsolated ) {
|
// Pin the expected event.source before anything can return
|
||||||
// COOP severs the opener relationship: the popup can't
|
// early: until this is set the message handler accepts a
|
||||||
// postMessage back and `popup.closed` is meaningless.
|
// decision from any window on the GUI origin, and the wait for
|
||||||
// Poll the permission check instead (mirrors signIn's
|
// the consent dialog's Continue click is user-paced.
|
||||||
// /login/wait fallback), giving up after a timeout.
|
popupWindow = popup;
|
||||||
|
// A severed opener relationship means the popup can't
|
||||||
|
// postMessage back and `popup.closed` tells us nothing about
|
||||||
|
// the window the user is looking at — it reads `true` for a
|
||||||
|
// detached proxy. Poll the permission check instead (mirrors
|
||||||
|
// signIn's /login/wait fallback), giving up after a timeout.
|
||||||
|
// `crossOriginIsolated` alone misses this: COOP severs the
|
||||||
|
// relationship on its own, while being isolated also requires
|
||||||
|
// COEP.
|
||||||
|
if ( window.crossOriginIsolated || popup.closed ) {
|
||||||
pollDecision();
|
pollDecision();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
popupWindow = popup;
|
|
||||||
checkClosed = setInterval(() => {
|
checkClosed = setInterval(() => {
|
||||||
if ( ! popup.closed ) return;
|
if ( ! popup.closed ) return;
|
||||||
// The GUI posts the decision and then closes the popup,
|
// The GUI posts the decision and then closes the popup,
|
||||||
@@ -1264,8 +1272,16 @@ class UI extends EventListener {
|
|||||||
onCancel: () => settle(false),
|
onCancel: () => settle(false),
|
||||||
});
|
});
|
||||||
consentDialog = dialog;
|
consentDialog = dialog;
|
||||||
document.body.appendChild(dialog);
|
try {
|
||||||
dialog.open();
|
document.body.appendChild(dialog);
|
||||||
|
dialog.open();
|
||||||
|
} catch (e) {
|
||||||
|
// Nothing to show the user (e.g. called from a <head>
|
||||||
|
// script, so there is no body yet). This resolves to a
|
||||||
|
// boolean for every other caller, so deny rather than
|
||||||
|
// reject — and let cleanup drop the message listener.
|
||||||
|
settle(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user