diff --git a/src/background.js b/src/background.js index 3370384..53321c8 100644 --- a/src/background.js +++ b/src/background.js @@ -81,6 +81,7 @@ function pathToDomain(path) { * @since 3.0.0 * * @param int tabId Tab id + * @return void */ async function updateMatchingPasswordsCount(tabId) { try { @@ -179,7 +180,7 @@ function saveRecent(settings, login, remove = false) { } /** - * Call injected form-fill code + * Call injected code to fill the form * * @param object settings Settings object * @param object fillRequest Fill request details @@ -188,13 +189,7 @@ function saveRecent(settings, login, remove = false) { * @param boolean allowNoSecret Allow forms that don't contain a password field * @return array list of filled fields */ -async function dispatchFill( - settings, - fillRequest, - allFrames = false, - allowForeign = false, - allowNoSecret = false -) { +async function dispatchFill(settings, fillRequest, allFrames, allowForeign, allowNoSecret) { fillRequest = Object.assign(deepCopy(fillRequest), { allowForeign: allowForeign, allowNoSecret: allowNoSecret, @@ -230,6 +225,27 @@ async function dispatchFill( return filledFields; } +/** + * Call injected code to focus or submit the form + * + * @param object settings Settings object + * @param object focusOrSubmitRequest Focus or submit request details + * @param boolean allFrames Dispatch to all frames + * @param boolean allowForeign Allow foreign-origin iframes + * @return void + */ +async function dispatchFocusOrSubmit(settings, focusOrSubmitRequest, allFrames, allowForeign) { + focusOrSubmitRequest = Object.assign(deepCopy(focusOrSubmitRequest), { + allowForeign: allowForeign, + foreignFills: settings.foreignFills[settings.host] || {} + }); + + await chrome.tabs.executeScript(settings.tab.id, { + allFrames: allFrames, + code: `window.browserpass.focusOrSubmit(${JSON.stringify(focusOrSubmitRequest)});` + }); +} + /** * Fill form fields * @@ -256,34 +272,51 @@ async function fillFields(settings, login, fields) { var fillRequest = { origin: new URL(settings.tab.url).origin, login: login, - fields: fields, - autoSubmit: getSetting("autoSubmit", login, settings) + fields: fields }; // fill form via injected script - var filledFields = await dispatchFill(settings, fillRequest); + let allFrames = false; + let allowForeign = false; + let allowNoSecret = false; + let filledFields = await dispatchFill( + settings, + fillRequest, + allFrames, + allowForeign, + allowNoSecret + ); // try again using same-origin frames if we couldn't fill a password field if (!filledFields.includes("secret")) { - filledFields = filledFields.concat(await dispatchFill(settings, fillRequest, true)); + allFrames = true; + filledFields = filledFields.concat( + await dispatchFill(settings, fillRequest, allFrames, allowForeign, allowNoSecret) + ); } // try again using all available frames if we couldn't fill a password field if (!filledFields.includes("secret") && settings.foreignFills[settings.host] !== false) { - filledFields = filledFields.concat(await dispatchFill(settings, fillRequest, true, true)); + allowForeign = true; + filledFields = filledFields.concat( + await dispatchFill(settings, fillRequest, allFrames, allowForeign, allowNoSecret) + ); } // try again using same-origin frames, and don't require a password field if (!filledFields.length) { + allowForeign = false; + allowNoSecret = true; filledFields = filledFields.concat( - await dispatchFill(settings, fillRequest, true, false, true) + await dispatchFill(settings, fillRequest, allFrames, allowForeign, allowNoSecret) ); } // try again using all available frames, and don't require a password field if (!filledFields.length && settings.foreignFills[settings.host] !== false) { + allowForeign = true; filledFields = filledFields.concat( - await dispatchFill(settings, fillRequest, true, true, true) + await dispatchFill(settings, fillRequest, allFrames, allowForeign, allowNoSecret) ); } @@ -291,6 +324,15 @@ async function fillFields(settings, login, fields) { throw new Error(`No fillable forms available for fields: ${fields.join(", ")}`); } + // build focus or submit request + let focusOrSubmitRequest = { + origin: new URL(settings.tab.url).origin, + autoSubmit: getSetting("autoSubmit", login, settings) + }; + + // try to focus or submit form with the settings that were used to fill it + await dispatchFocusOrSubmit(settings, focusOrSubmitRequest, allFrames, allowForeign); + return filledFields; } diff --git a/src/inject.js b/src/inject.js index 1b56335..dc642d4 100644 --- a/src/inject.js +++ b/src/inject.js @@ -126,6 +126,29 @@ result.filledFields.push("secret"); } + // finished filling things successfully + return result; + } + + /** + * Focus submit button, and maybe click on it (based on user settings) + * + * @since 3.0.0 + * + * @param object request Form fill request + * @return void + */ + function focusOrSubmit(request) { + // get the login form + var loginForm = form(); + + // ensure the origin is the same or allowed + if (window.location.origin !== request.origin) { + if (!request.allowForeign || request.foreignFills[window.location.origin] === false) { + return; + } + } + // check for multiple password fields in the login form var password_inputs = queryAllVisible(document, PASSWORD_FIELDS, loginForm); if (password_inputs.length > 1) { @@ -156,9 +179,6 @@ } } } - - // finished filling things successfully - return result; } /** @@ -333,6 +353,7 @@ // set window object window.browserpass = { - fillLogin: fillLogin + fillLogin: fillLogin, + focusOrSubmit: focusOrSubmit }; })();