mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-18 14:32:43 +00:00
fix(panes): adopt into the real pane document, not about:blank
Both pop-outs opened blank. window.open() returns immediately, and the window it hands back already has a document — an `about:blank` one, whose readyState is 'complete'. So the host cheerfully adopted the panel into THAT, it worked for a few milliseconds, and then /pane finished loading, replaced the document, and took the panel with it. Blank window, vanished element. Waiting for 'load' is no better: it may already have fired for about:blank before we could listen. So don't trust readyState and don't trust 'load' — wait for the one thing that exists only in the document we actually want: pane.html's #fb-pane-root. Poll for it (guarding the cross-document window while it is mid-swap), give up after 10s, and on failure bring the element home rather than stranding it in a window that never loaded. Also drop the popup's 'beforeunload' listener: it was registered on the about:blank window and discarded along with it, so it never fired. The `closed` poll is what notices a user shutting a pane window — as it must be anyway, since a crashed renderer never says goodbye either. Signed-off-by: topkoa <topkoa@gmail.com>
This commit is contained in:
@@ -68,11 +68,33 @@
|
|||||||
} catch (e) { /* non-fatal */ }
|
} catch (e) { /* non-fatal */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
function _adopt(w, spec, el) {
|
// Wait for the REAL pane document.
|
||||||
|
//
|
||||||
|
// window.open() returns immediately, with an `about:blank` document that is
|
||||||
|
// already readyState 'complete'. Adopt into that and it works for a few
|
||||||
|
// milliseconds — and then /pane finishes loading, replaces the document, and
|
||||||
|
// takes the panel with it. The window is left blank and the element is gone.
|
||||||
|
//
|
||||||
|
// So we do not trust readyState, and we do not trust 'load' (which may have
|
||||||
|
// fired for about:blank before we could listen). We wait for the one thing that
|
||||||
|
// only exists in the document we actually want: pane.html's #fb-pane-root.
|
||||||
|
function _whenReady(w, onReady, onFail) {
|
||||||
|
const deadline = performance.now() + 10000;
|
||||||
|
const tick = () => {
|
||||||
|
if (w.closed) return;
|
||||||
|
let root = null;
|
||||||
|
try { root = w.document && w.document.getElementById('fb-pane-root'); }
|
||||||
|
catch (e) { root = null; } // mid-navigation: the document is being swapped
|
||||||
|
if (root) { onReady(root); return; }
|
||||||
|
if (performance.now() > deadline) { onFail(new Error('the pane window never loaded')); return; }
|
||||||
|
setTimeout(tick, 25);
|
||||||
|
};
|
||||||
|
tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _adopt(w, root, spec, el) {
|
||||||
const doc = w.document;
|
const doc = w.document;
|
||||||
_copyStyles(doc);
|
_copyStyles(doc);
|
||||||
|
|
||||||
const root = doc.getElementById('fb-pane-root') || doc.body;
|
|
||||||
// The panel was almost certainly a fixed/absolute overlay pinned to a
|
// The panel was almost certainly a fixed/absolute overlay pinned to a
|
||||||
// corner of the app. In a window of its own that positioning is nonsense —
|
// corner of the app. In a window of its own that positioning is nonsense —
|
||||||
// it would sit 72px from the top of a 380px window, still 288px wide, still
|
// it would sit 72px from the top of a 380px window, still 288px wide, still
|
||||||
@@ -111,30 +133,22 @@
|
|||||||
wins.set(spec.id, w);
|
wins.set(spec.id, w);
|
||||||
_startReaper();
|
_startReaper();
|
||||||
|
|
||||||
// The document may or may not have parsed yet. Both paths must work, and
|
_whenReady(w, (root) => {
|
||||||
// must not run twice — a double adopt would move the element into the
|
try { _adopt(w, root, spec, el); }
|
||||||
// window and then move it in again, firing the plugin's own observers for
|
|
||||||
// no reason.
|
|
||||||
let done = false;
|
|
||||||
const go = () => {
|
|
||||||
if (done || w.closed) return;
|
|
||||||
done = true;
|
|
||||||
try { _adopt(w, spec, el); }
|
|
||||||
catch (e) {
|
catch (e) {
|
||||||
console.error('[panes] failed to move', spec.id, 'into its window', e);
|
console.error('[panes] failed to move', spec.id, 'into its window', e);
|
||||||
panes.close(spec.id); // returns the element home
|
panes.close(spec.id); // brings the element home
|
||||||
}
|
}
|
||||||
};
|
}, (err) => {
|
||||||
if (w.document && w.document.readyState === 'complete') go();
|
console.error('[panes]', spec.id, err);
|
||||||
else w.addEventListener('load', go, { once: true });
|
panes.close(spec.id); // never strand the element in a dead window
|
||||||
|
|
||||||
// The window is ours and must not outlive the document that owns the
|
|
||||||
// element inside it.
|
|
||||||
w.addEventListener('beforeunload', () => {
|
|
||||||
// Only react to the user closing the window — not to us closing it
|
|
||||||
// during a dock, which has already taken the element back.
|
|
||||||
if (wins.get(spec.id) === w && panes.isOpen(spec.id)) setTimeout(() => panes.close(spec.id), 0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Note there is no 'beforeunload' listener on the popup. A listener added
|
||||||
|
// now would be attached to its throwaway about:blank window and thrown away
|
||||||
|
// with it when /pane loads. The `closed` poll above is what notices the user
|
||||||
|
// shutting a pane window — and it has to be, since a crashed renderer never
|
||||||
|
// gets to say goodbye either.
|
||||||
}
|
}
|
||||||
|
|
||||||
function unplace(id, el) {
|
function unplace(id, el) {
|
||||||
|
|||||||
Reference in New Issue
Block a user