diff --git a/src/firefox-extension/manifest.json b/src/firefox-extension/manifest.json index 184d59e..3db37bc 100644 --- a/src/firefox-extension/manifest.json +++ b/src/firefox-extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "SoftGlaze Smart Autofill", - "version": "1.0.1", + "version": "1.0.2", "description": "Fills signup/registration forms inside SoftGlaze profiles from your Identity Data Vault. Talks only to the local SoftGlaze app over loopback.", "browser_specific_settings": { "gecko": { diff --git a/src/firefox-extension/sg-background.js b/src/firefox-extension/sg-background.js index 71d3e3c..5f0d44c 100644 --- a/src/firefox-extension/sg-background.js +++ b/src/firefox-extension/sg-background.js @@ -139,8 +139,13 @@ browser.runtime.onMessage.addListener(function (msg, sender) { } var e = activeIds.get(key); if (!e || Date.now() - e.at > ACTIVE_TTL_MS) { activeIds.delete(key); return Promise.resolve(null); } + // 'finish': a submit that looked final but had no password; the next page decides. + if (msg.op === 'finish') { + if (String(msg.id || '') !== e.id) return Promise.resolve(null); + e.pending = true; + } e.at = Date.now(); - return Promise.resolve({ id: e.id }); + return Promise.resolve({ id: e.id, pending: !!e.pending }); } if (msg.type === 'list') { return call('/sg-autofill/list?url=' + encodeURIComponent(url), { method: 'GET' }) diff --git a/src/firefox-extension/sg-widget.js b/src/firefox-extension/sg-widget.js index a088cd0..5781f22 100644 --- a/src/firefox-extension/sg-widget.js +++ b/src/firefox-extension/sg-widget.js @@ -744,15 +744,52 @@ for (var i = 0; i < btns.length; i++) { if (NEXT_RX.test(controlText(btns[i]))) return false; } return true; } - var finalizing = false; - function finalSubmit() { + // A label alone cannot tell the last step: Buildium's STEP 1 button reads "Start + // My Free Trial". A submit that carries a filled password is an account being + // created, so it is final. Anything else is only "finishing": the identity stays + // active (pending) and the page that follows decides - more fields to fill means + // it was a step, nothing left means it was the end and the identity is used. + function hasFilledPassword(scope) { + var pws = (scope && scope.querySelectorAll ? scope : document).querySelectorAll('input[type="password"]'); + for (var i = 0; i < pws.length; i++) { if (pws[i].value) return true; } + return false; + } + function stepContinues() { + if (newEmptyFieldExists()) return true; + var pws = document.querySelectorAll('input[type="password"]'); + for (var i = 0; i < pws.length; i++) { if (fillable(pws[i]) && !pws[i].value) return true; } + return false; + } + // Watch the current page for up to `ms`: a further step continues the signup + // (pending cleared), no step at all marks the identity used. + function settlePending(ms, onContinue) { + var t0 = Date.now(); + (function check() { + if (!active || leaving) return; + if (stepContinues()) { setActive(active); if (onContinue) onContinue(); return; } + if (Date.now() - t0 >= ms) { markSelectedUsed(true); return; } + setTimeout(check, 500); + })(); + } + var finalizing = false, leaving = false; + window.addEventListener('beforeunload', function () { leaving = true; }, true); + window.addEventListener('pagehide', function () { leaving = true; }, true); + function finalSubmit(scope) { if (!active || finalizing) return; finalizing = true; - markSelectedUsed(true).then(function () { finalizing = false; }, function () { finalizing = false; }); + var done = function () { finalizing = false; }; + if (!canTrackActive() || hasFilledPassword(scope)) { markSelectedUsed(true).then(done, done); return; } + var id = active.id; + sgCall('__sgPersonaActive', 'finish', id).then(function (r) { + // A backend without 'finish' (older Firefox extension) keeps the old rule. + if (!r || r.id !== id || !r.pending) return markSelectedUsed(true); + // The page may stay (SPA wizard, or a validation error): decide here too. + setTimeout(function () { settlePending(8000, function () { armMultiStep(active); }); }, 1500); + }).then(done, done); } document.addEventListener('submit', function (e) { if (!e.isTrusted || !active) return; - if (isFinalSubmit(e.submitter || null, e.target)) finalSubmit(); + if (isFinalSubmit(e.submitter || null, e.target)) finalSubmit(e.target); }, true); // SPA signups often have no