Sign-in widgets and embedded checkouts often put the login form in an iframe, and the password hooks only ran in a tab's top frame, so those logins were never offered for saving or filling. - Web tabs now run preloads in iframes (nodeIntegrationInSubFrames; pages still get no Node). Only the password hooks act there: the home-page bridge, window.bcnr, add-on page scripts and window.theseusId return early outside the top frame, exactly as before. - A login in a frame belongs to the frame's own site, taken from that frame's committed URL. The save prompt says "login.example (in a frame on shop.example)", the fill offer names both, and the fill goes into that exact frame only while it is still that tab's and still on that site. - The "did the login go through" check runs against the frame. Verified with a shop page embedding a cross-site login frame: save offer, fill offer and fill all target the frame; the outer page gets nothing; top-frame logins behave as before.
52 lines
3.1 KiB
HTML
52 lines
3.1 KiB
HTML
<!doctype html>
|
|
<html><head><meta charset="utf-8">
|
|
<style>
|
|
:root { color-scheme: light dark; font-family: system-ui, sans-serif; }
|
|
html, body { margin: 0; background: transparent; }
|
|
.menu { background: #1c222c; border: 1px solid #ffffff26; border-radius: 10px; box-shadow: 0 12px 34px #000c; overflow: hidden; color: #e7eaf1; }
|
|
.hdr { font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: #8b98a9; padding: 10px 14px 6px; }
|
|
.item { display: flex; align-items: center; gap: 10px; padding: 9px 14px; cursor: pointer; font-size: 13px; }
|
|
.item:hover { background: #ffffff12; }
|
|
.item .ic { font-size: 14px; }
|
|
.item .txt { flex: 1; overflow: hidden; }
|
|
.item .u { color: #e7eaf1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
.item .d { color: #7f8aa0; font-size: 11.5px; }
|
|
.empty { padding: 14px; color: #7f8aa0; font-size: 12.5px; text-align: center; }
|
|
@media (prefers-color-scheme: light) {
|
|
.menu { background: #ffffff; border-color: rgba(0,0,0,.15); color: #1a1f28; }
|
|
.item:hover { background: rgba(0,0,0,.05); }
|
|
.item .u { color: #1a1f28; } .item .d, .hdr, .empty { color: #7b8494; }
|
|
}
|
|
</style></head>
|
|
<body>
|
|
<div class="menu">
|
|
<div class="hdr">Fill password for this site</div>
|
|
<div id="list"></div>
|
|
</div>
|
|
<script>
|
|
const $ = (id) => document.getElementById(id);
|
|
const esc = (s) => String(s || "").replace(/&/g, "&").replace(/</g, "<").replace(/"/g, """);
|
|
function report() { requestAnimationFrame(() => { try { window.pwfill.resize(document.querySelector(".menu").offsetHeight); } catch (e) {} }); }
|
|
window.pwfill.onMatches((data) => {
|
|
const list = $("list");
|
|
const matches = data.matches || [];
|
|
// A login form inside an iframe belongs to the iframe's site: name it,
|
|
// and the page it sits in, so the user knows where the password goes.
|
|
const framed = data.framed && data.topHost && data.topHost !== data.host;
|
|
const hdr = document.querySelector(".hdr");
|
|
hdr.textContent = framed
|
|
? `Login for ${data.host} · in a frame on ${data.topHost}`
|
|
: data.locked ? "Theseus Vault" : "Fill password for this site";
|
|
hdr.style.textTransform = framed ? "none" : ""; // host names stay as written
|
|
if (data.locked) {
|
|
// The vault is locked, but it has a login for this site.
|
|
list.innerHTML = `<div class="item" data-id="__unlock"><span class="ic">🔒</span><span class="txt"><div class="u">Sign in with a saved login</div><div class="d">Unlock your vault to fill ${esc(data.host)}</div></span></div>`;
|
|
} else if (!matches.length) { list.innerHTML = `<div class="empty">No saved credentials for this site.</div>`; report(); return; }
|
|
else list.innerHTML = matches.map((m) =>
|
|
`<div class="item" data-id="${esc(m.id)}"><span class="ic">🔑</span><span class="txt"><div class="u">${esc(m.username || "(no username)")}</div><div class="d">${esc(m.domain)}${m.confirm ? " · asks for your PIN" : ""}</div></span></div>`
|
|
).join("");
|
|
list.querySelectorAll(".item").forEach((el) => el.onclick = () => window.pwfill.pick(el.dataset.id));
|
|
report();
|
|
});
|
|
</script>
|
|
</body></html>
|