mirror of
https://github.com/bitwarden/browser
synced 2025-12-17 08:43:33 +00:00
Autofill/pm 25597 plex password generation (#16997)
* Correctly fill generated passwords and current password on plex.tv * Correctly fill generated passwords and current password on plex.tv * Leave existing forEach * Add tests for changes
This commit is contained in:
@@ -3286,6 +3286,9 @@ describe("OverlayBackground", () => {
|
|||||||
pageDetails: [pageDetailsForTab],
|
pageDetails: [pageDetailsForTab],
|
||||||
fillNewPassword: true,
|
fillNewPassword: true,
|
||||||
allowTotpAutofill: true,
|
allowTotpAutofill: true,
|
||||||
|
focusedFieldForm: undefined,
|
||||||
|
focusedFieldOpid: undefined,
|
||||||
|
inlineMenuFillType: undefined,
|
||||||
});
|
});
|
||||||
expect(overlayBackground["inlineMenuCiphers"].entries()).toStrictEqual(
|
expect(overlayBackground["inlineMenuCiphers"].entries()).toStrictEqual(
|
||||||
new Map([
|
new Map([
|
||||||
@@ -3680,6 +3683,9 @@ describe("OverlayBackground", () => {
|
|||||||
pageDetails: [overlayBackground["pageDetailsForTab"][sender.tab.id].get(sender.frameId)],
|
pageDetails: [overlayBackground["pageDetailsForTab"][sender.tab.id].get(sender.frameId)],
|
||||||
fillNewPassword: true,
|
fillNewPassword: true,
|
||||||
allowTotpAutofill: false,
|
allowTotpAutofill: false,
|
||||||
|
focusedFieldForm: undefined,
|
||||||
|
focusedFieldOpid: undefined,
|
||||||
|
inlineMenuFillType: InlineMenuFillTypes.PasswordGeneration,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1177,6 +1177,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
allowTotpAutofill: true,
|
allowTotpAutofill: true,
|
||||||
focusedFieldForm: this.focusedFieldData?.focusedFieldForm,
|
focusedFieldForm: this.focusedFieldData?.focusedFieldForm,
|
||||||
focusedFieldOpid: this.focusedFieldData?.focusedFieldOpid,
|
focusedFieldOpid: this.focusedFieldData?.focusedFieldOpid,
|
||||||
|
inlineMenuFillType: this.focusedFieldData?.inlineMenuFillType,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (totpCode) {
|
if (totpCode) {
|
||||||
@@ -1863,6 +1864,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
allowTotpAutofill: false,
|
allowTotpAutofill: false,
|
||||||
focusedFieldForm: this.focusedFieldData?.focusedFieldForm,
|
focusedFieldForm: this.focusedFieldData?.focusedFieldForm,
|
||||||
focusedFieldOpid: this.focusedFieldData?.focusedFieldOpid,
|
focusedFieldOpid: this.focusedFieldData?.focusedFieldOpid,
|
||||||
|
inlineMenuFillType: InlineMenuFillTypes.PasswordGeneration,
|
||||||
});
|
});
|
||||||
|
|
||||||
globalThis.setTimeout(async () => {
|
globalThis.setTimeout(async () => {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { CipherType } from "@bitwarden/common/vault/enums";
|
|||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
|
|
||||||
import { AutofillMessageCommand } from "../../enums/autofill-message.enums";
|
import { AutofillMessageCommand } from "../../enums/autofill-message.enums";
|
||||||
|
import { InlineMenuFillType } from "../../enums/autofill-overlay.enum";
|
||||||
import AutofillField from "../../models/autofill-field";
|
import AutofillField from "../../models/autofill-field";
|
||||||
import AutofillForm from "../../models/autofill-form";
|
import AutofillForm from "../../models/autofill-form";
|
||||||
import AutofillPageDetails from "../../models/autofill-page-details";
|
import AutofillPageDetails from "../../models/autofill-page-details";
|
||||||
@@ -30,6 +31,7 @@ export interface AutoFillOptions {
|
|||||||
autoSubmitLogin?: boolean;
|
autoSubmitLogin?: boolean;
|
||||||
focusedFieldForm?: string;
|
focusedFieldForm?: string;
|
||||||
focusedFieldOpid?: string;
|
focusedFieldOpid?: string;
|
||||||
|
inlineMenuFillType?: InlineMenuFillType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FormData {
|
export interface FormData {
|
||||||
@@ -49,6 +51,7 @@ export interface GenerateFillScriptOptions {
|
|||||||
tabUrl: string;
|
tabUrl: string;
|
||||||
defaultUriMatch: UriMatchStrategySetting;
|
defaultUriMatch: UriMatchStrategySetting;
|
||||||
focusedFieldOpid?: string;
|
focusedFieldOpid?: string;
|
||||||
|
inlineMenuFillType?: InlineMenuFillType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CollectPageDetailsResponseMessage = {
|
export type CollectPageDetailsResponseMessage = {
|
||||||
|
|||||||
@@ -1118,6 +1118,12 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
|||||||
* @param autofillFieldData - Autofill field data captured from the form field element.
|
* @param autofillFieldData - Autofill field data captured from the form field element.
|
||||||
*/
|
*/
|
||||||
private async setQualifiedLoginFillType(autofillFieldData: AutofillField) {
|
private async setQualifiedLoginFillType(autofillFieldData: AutofillField) {
|
||||||
|
// Check if this is a current password field in a password change form
|
||||||
|
if (this.inlineMenuFieldQualificationService.isUpdateCurrentPasswordField(autofillFieldData)) {
|
||||||
|
autofillFieldData.inlineMenuFillType = InlineMenuFillTypes.CurrentPasswordUpdate;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
autofillFieldData.inlineMenuFillType = CipherType.Login;
|
autofillFieldData.inlineMenuFillType = CipherType.Login;
|
||||||
autofillFieldData.showPasskeys = autofillFieldData.autoCompleteType.includes("webauthn");
|
autofillFieldData.showPasskeys = autofillFieldData.autoCompleteType.includes("webauthn");
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import { TotpService } from "@bitwarden/common/vault/services/totp.service";
|
|||||||
import { BrowserApi } from "../../platform/browser/browser-api";
|
import { BrowserApi } from "../../platform/browser/browser-api";
|
||||||
import { BrowserScriptInjectorService } from "../../platform/services/browser-script-injector.service";
|
import { BrowserScriptInjectorService } from "../../platform/services/browser-script-injector.service";
|
||||||
import { AutofillMessageCommand, AutofillMessageSender } from "../enums/autofill-message.enums";
|
import { AutofillMessageCommand, AutofillMessageSender } from "../enums/autofill-message.enums";
|
||||||
|
import { InlineMenuFillTypes } from "../enums/autofill-overlay.enum";
|
||||||
import { AutofillPort } from "../enums/autofill-port.enum";
|
import { AutofillPort } from "../enums/autofill-port.enum";
|
||||||
import AutofillField from "../models/autofill-field";
|
import AutofillField from "../models/autofill-field";
|
||||||
import AutofillPageDetails from "../models/autofill-page-details";
|
import AutofillPageDetails from "../models/autofill-page-details";
|
||||||
@@ -103,6 +104,15 @@ describe("AutofillService", () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
configService = mock<ConfigService>();
|
configService = mock<ConfigService>();
|
||||||
configService.getFeatureFlag$.mockImplementation(() => of(false));
|
configService.getFeatureFlag$.mockImplementation(() => of(false));
|
||||||
|
|
||||||
|
// Initialize domainSettingsService BEFORE it's used
|
||||||
|
domainSettingsService = new DefaultDomainSettingsService(
|
||||||
|
fakeStateProvider,
|
||||||
|
policyService,
|
||||||
|
accountService,
|
||||||
|
);
|
||||||
|
domainSettingsService.equivalentDomains$ = of(mockEquivalentDomains);
|
||||||
|
|
||||||
scriptInjectorService = new BrowserScriptInjectorService(
|
scriptInjectorService = new BrowserScriptInjectorService(
|
||||||
domainSettingsService,
|
domainSettingsService,
|
||||||
platformUtilsService,
|
platformUtilsService,
|
||||||
@@ -141,12 +151,6 @@ describe("AutofillService", () => {
|
|||||||
userNotificationsSettings,
|
userNotificationsSettings,
|
||||||
messageListener,
|
messageListener,
|
||||||
);
|
);
|
||||||
domainSettingsService = new DefaultDomainSettingsService(
|
|
||||||
fakeStateProvider,
|
|
||||||
policyService,
|
|
||||||
accountService,
|
|
||||||
);
|
|
||||||
domainSettingsService.equivalentDomains$ = of(mockEquivalentDomains);
|
|
||||||
jest.spyOn(BrowserApi, "tabSendMessage");
|
jest.spyOn(BrowserApi, "tabSendMessage");
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2077,6 +2081,193 @@ describe("AutofillService", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("given password generation with inlineMenuFillType", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
pageDetails.forms = undefined;
|
||||||
|
pageDetails.fields = []; // Clear fields to start fresh
|
||||||
|
options.inlineMenuFillType = InlineMenuFillTypes.PasswordGeneration;
|
||||||
|
options.cipher.login.totp = null; // Disable TOTP for these tests
|
||||||
|
});
|
||||||
|
|
||||||
|
it("includes all password fields from the same form when filling with password generation", async () => {
|
||||||
|
const newPasswordField = createAutofillFieldMock({
|
||||||
|
opid: "new-password",
|
||||||
|
type: "password",
|
||||||
|
form: "validFormId",
|
||||||
|
elementNumber: 2,
|
||||||
|
});
|
||||||
|
const confirmPasswordField = createAutofillFieldMock({
|
||||||
|
opid: "confirm-password",
|
||||||
|
type: "password",
|
||||||
|
form: "validFormId",
|
||||||
|
elementNumber: 3,
|
||||||
|
});
|
||||||
|
pageDetails.fields.push(newPasswordField, confirmPasswordField);
|
||||||
|
options.focusedFieldOpid = newPasswordField.opid;
|
||||||
|
|
||||||
|
await autofillService["generateLoginFillScript"](
|
||||||
|
fillScript,
|
||||||
|
pageDetails,
|
||||||
|
filledFields,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(filledFields[newPasswordField.opid]).toBeDefined();
|
||||||
|
expect(filledFields[confirmPasswordField.opid]).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("finds username field for the first password field when generating passwords", async () => {
|
||||||
|
const newPasswordField = createAutofillFieldMock({
|
||||||
|
opid: "new-password",
|
||||||
|
type: "password",
|
||||||
|
form: "validFormId",
|
||||||
|
elementNumber: 2,
|
||||||
|
});
|
||||||
|
pageDetails.fields.push(newPasswordField);
|
||||||
|
options.focusedFieldOpid = newPasswordField.opid;
|
||||||
|
jest.spyOn(autofillService as any, "findUsernameField");
|
||||||
|
|
||||||
|
await autofillService["generateLoginFillScript"](
|
||||||
|
fillScript,
|
||||||
|
pageDetails,
|
||||||
|
filledFields,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(autofillService["findUsernameField"]).toHaveBeenCalledWith(
|
||||||
|
pageDetails,
|
||||||
|
expect.objectContaining({ opid: newPasswordField.opid }),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not include password fields from different forms", async () => {
|
||||||
|
const formAPasswordField = createAutofillFieldMock({
|
||||||
|
opid: "form-a-password",
|
||||||
|
type: "password",
|
||||||
|
form: "formA",
|
||||||
|
elementNumber: 1,
|
||||||
|
});
|
||||||
|
const formBPasswordField = createAutofillFieldMock({
|
||||||
|
opid: "form-b-password",
|
||||||
|
type: "password",
|
||||||
|
form: "formB",
|
||||||
|
elementNumber: 2,
|
||||||
|
});
|
||||||
|
pageDetails.fields = [formAPasswordField, formBPasswordField];
|
||||||
|
options.focusedFieldOpid = formAPasswordField.opid;
|
||||||
|
|
||||||
|
await autofillService["generateLoginFillScript"](
|
||||||
|
fillScript,
|
||||||
|
pageDetails,
|
||||||
|
filledFields,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(filledFields[formAPasswordField.opid]).toBeDefined();
|
||||||
|
expect(filledFields[formBPasswordField.opid]).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("given current password update with inlineMenuFillType", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
pageDetails.forms = undefined;
|
||||||
|
pageDetails.fields = []; // Clear fields to start fresh
|
||||||
|
options.inlineMenuFillType = InlineMenuFillTypes.CurrentPasswordUpdate;
|
||||||
|
options.cipher.login.totp = null; // Disable TOTP for these tests
|
||||||
|
});
|
||||||
|
|
||||||
|
it("includes all password fields from the same form when updating current password", async () => {
|
||||||
|
const currentPasswordField = createAutofillFieldMock({
|
||||||
|
opid: "current-password",
|
||||||
|
type: "password",
|
||||||
|
form: "validFormId",
|
||||||
|
elementNumber: 1,
|
||||||
|
});
|
||||||
|
const newPasswordField = createAutofillFieldMock({
|
||||||
|
opid: "new-password",
|
||||||
|
type: "password",
|
||||||
|
form: "validFormId",
|
||||||
|
elementNumber: 2,
|
||||||
|
});
|
||||||
|
const confirmPasswordField = createAutofillFieldMock({
|
||||||
|
opid: "confirm-password",
|
||||||
|
type: "password",
|
||||||
|
form: "validFormId",
|
||||||
|
elementNumber: 3,
|
||||||
|
});
|
||||||
|
pageDetails.fields.push(currentPasswordField, newPasswordField, confirmPasswordField);
|
||||||
|
options.focusedFieldOpid = currentPasswordField.opid;
|
||||||
|
|
||||||
|
await autofillService["generateLoginFillScript"](
|
||||||
|
fillScript,
|
||||||
|
pageDetails,
|
||||||
|
filledFields,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(filledFields[currentPasswordField.opid]).toBeDefined();
|
||||||
|
expect(filledFields[newPasswordField.opid]).toBeDefined();
|
||||||
|
expect(filledFields[confirmPasswordField.opid]).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("includes all password fields from the same form without TOTP", async () => {
|
||||||
|
const currentPasswordField = createAutofillFieldMock({
|
||||||
|
opid: "current-password",
|
||||||
|
type: "password",
|
||||||
|
form: "validFormId",
|
||||||
|
elementNumber: 1,
|
||||||
|
});
|
||||||
|
const newPasswordField = createAutofillFieldMock({
|
||||||
|
opid: "new-password",
|
||||||
|
type: "password",
|
||||||
|
form: "validFormId",
|
||||||
|
elementNumber: 2,
|
||||||
|
});
|
||||||
|
pageDetails.fields.push(currentPasswordField, newPasswordField);
|
||||||
|
options.focusedFieldOpid = currentPasswordField.opid;
|
||||||
|
|
||||||
|
await autofillService["generateLoginFillScript"](
|
||||||
|
fillScript,
|
||||||
|
pageDetails,
|
||||||
|
filledFields,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(filledFields[currentPasswordField.opid]).toBeDefined();
|
||||||
|
expect(filledFields[newPasswordField.opid]).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not include password fields from different forms during password update", async () => {
|
||||||
|
const formAPasswordField = createAutofillFieldMock({
|
||||||
|
opid: "form-a-password",
|
||||||
|
type: "password",
|
||||||
|
form: "formA",
|
||||||
|
elementNumber: 1,
|
||||||
|
});
|
||||||
|
const formBPasswordField = createAutofillFieldMock({
|
||||||
|
opid: "form-b-password",
|
||||||
|
type: "password",
|
||||||
|
form: "formB",
|
||||||
|
elementNumber: 2,
|
||||||
|
});
|
||||||
|
pageDetails.fields = [formAPasswordField, formBPasswordField];
|
||||||
|
options.focusedFieldOpid = formAPasswordField.opid;
|
||||||
|
|
||||||
|
await autofillService["generateLoginFillScript"](
|
||||||
|
fillScript,
|
||||||
|
pageDetails,
|
||||||
|
filledFields,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(filledFields[formAPasswordField.opid]).toBeDefined();
|
||||||
|
expect(filledFields[formBPasswordField.opid]).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("given a set of page details that does not contain a password field", () => {
|
describe("given a set of page details that does not contain a password field", () => {
|
||||||
let emailField: AutofillField;
|
let emailField: AutofillField;
|
||||||
let emailFieldView: FieldView;
|
let emailFieldView: FieldView;
|
||||||
@@ -3140,12 +3331,16 @@ describe("AutofillService", () => {
|
|||||||
"example.com",
|
"example.com",
|
||||||
"exampleapp.com",
|
"exampleapp.com",
|
||||||
]);
|
]);
|
||||||
domainSettingsService.equivalentDomains$ = of([["not-example.com"]]);
|
|
||||||
const pageUrl = "https://subdomain.example.com";
|
const pageUrl = "https://subdomain.example.com";
|
||||||
const tabUrl = "https://www.not-example.com";
|
const tabUrl = "https://www.not-example.com";
|
||||||
const generateFillScriptOptions = createGenerateFillScriptOptionsMock({ tabUrl });
|
const generateFillScriptOptions = createGenerateFillScriptOptionsMock({ tabUrl });
|
||||||
generateFillScriptOptions.cipher.login.matchesUri = jest.fn().mockReturnValueOnce(false);
|
generateFillScriptOptions.cipher.login.matchesUri = jest.fn().mockReturnValueOnce(false);
|
||||||
|
|
||||||
|
// Mock getUrlEquivalentDomains to return the expected domains
|
||||||
|
jest
|
||||||
|
.spyOn(domainSettingsService, "getUrlEquivalentDomains")
|
||||||
|
.mockReturnValue(of(equivalentDomains));
|
||||||
|
|
||||||
const result = await autofillService["inUntrustedIframe"](pageUrl, generateFillScriptOptions);
|
const result = await autofillService["inUntrustedIframe"](pageUrl, generateFillScriptOptions);
|
||||||
|
|
||||||
expect(generateFillScriptOptions.cipher.login.matchesUri).toHaveBeenCalledWith(
|
expect(generateFillScriptOptions.cipher.login.matchesUri).toHaveBeenCalledWith(
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import { ScriptInjectorService } from "../../platform/services/abstractions/scri
|
|||||||
// eslint-disable-next-line no-restricted-imports
|
// eslint-disable-next-line no-restricted-imports
|
||||||
import { openVaultItemPasswordRepromptPopout } from "../../vault/popup/utils/vault-popout-window";
|
import { openVaultItemPasswordRepromptPopout } from "../../vault/popup/utils/vault-popout-window";
|
||||||
import { AutofillMessageCommand, AutofillMessageSender } from "../enums/autofill-message.enums";
|
import { AutofillMessageCommand, AutofillMessageSender } from "../enums/autofill-message.enums";
|
||||||
|
import { InlineMenuFillTypes } from "../enums/autofill-overlay.enum";
|
||||||
import { AutofillPort } from "../enums/autofill-port.enum";
|
import { AutofillPort } from "../enums/autofill-port.enum";
|
||||||
import AutofillField from "../models/autofill-field";
|
import AutofillField from "../models/autofill-field";
|
||||||
import AutofillPageDetails from "../models/autofill-page-details";
|
import AutofillPageDetails from "../models/autofill-page-details";
|
||||||
@@ -452,6 +453,7 @@ export default class AutofillService implements AutofillServiceInterface {
|
|||||||
tabUrl: tab.url,
|
tabUrl: tab.url,
|
||||||
defaultUriMatch: defaultUriMatch,
|
defaultUriMatch: defaultUriMatch,
|
||||||
focusedFieldOpid: options.focusedFieldOpid,
|
focusedFieldOpid: options.focusedFieldOpid,
|
||||||
|
inlineMenuFillType: options.inlineMenuFillType,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!fillScript || !fillScript.script || !fillScript.script.length) {
|
if (!fillScript || !fillScript.script || !fillScript.script.length) {
|
||||||
@@ -971,26 +973,53 @@ export default class AutofillService implements AutofillServiceInterface {
|
|||||||
|
|
||||||
if (passwordFields.length && !passwords.length) {
|
if (passwordFields.length && !passwords.length) {
|
||||||
// in the event that password fields exist but weren't processed within form elements.
|
// in the event that password fields exist but weren't processed within form elements.
|
||||||
// select matching password if focused, otherwise first in prioritized list. for username, use focused field if it matches, otherwise find field before password.
|
const isPasswordGeneration =
|
||||||
const passwordFieldToUse = focusedField
|
options.inlineMenuFillType === InlineMenuFillTypes.PasswordGeneration;
|
||||||
? prioritizedPasswordFields.find(passwordMatchesFocused) || prioritizedPasswordFields[0]
|
const isCurrentPasswordUpdate =
|
||||||
: prioritizedPasswordFields[0];
|
options.inlineMenuFillType === InlineMenuFillTypes.CurrentPasswordUpdate;
|
||||||
|
|
||||||
if (passwordFieldToUse) {
|
// For password generation or current password update, include all password fields from the same form
|
||||||
passwords.push(passwordFieldToUse);
|
// This ensures we have access to all fields regardless of their login/registration classification
|
||||||
|
if ((isPasswordGeneration || isCurrentPasswordUpdate) && focusedField) {
|
||||||
|
// Add all password fields from the same form as the focused field
|
||||||
|
const focusedFieldForm = focusedField.form;
|
||||||
|
|
||||||
if (login.username && passwordFieldToUse.elementNumber > 0) {
|
// Check both login and registration fields to ensure we get all password fields
|
||||||
username = getUsernameForPassword(passwordFieldToUse, true);
|
const allPasswordFields = [...loginPasswordFields, ...registrationPasswordFields];
|
||||||
|
allPasswordFields.forEach((passField) => {
|
||||||
|
if (passField.form === focusedFieldForm) {
|
||||||
|
passwords.push(passField);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we didn't add any passwords above (either not password generation/update or no matching fields),
|
||||||
|
// select matching password if focused, otherwise first in prioritized list.
|
||||||
|
if (!passwords.length) {
|
||||||
|
const passwordFieldToUse = focusedField
|
||||||
|
? prioritizedPasswordFields.find(passwordMatchesFocused) || prioritizedPasswordFields[0]
|
||||||
|
: prioritizedPasswordFields[0];
|
||||||
|
|
||||||
|
if (passwordFieldToUse) {
|
||||||
|
passwords.push(passwordFieldToUse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle username and TOTP for the first password field
|
||||||
|
const firstPasswordField = passwords[0];
|
||||||
|
if (firstPasswordField) {
|
||||||
|
if (login.username && firstPasswordField.elementNumber > 0) {
|
||||||
|
username = getUsernameForPassword(firstPasswordField, true);
|
||||||
if (username) {
|
if (username) {
|
||||||
usernames.set(username.opid, username);
|
usernames.set(username.opid, username);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.allowTotpAutofill && login.totp && passwordFieldToUse.elementNumber > 0) {
|
if (options.allowTotpAutofill && login.totp && firstPasswordField.elementNumber > 0) {
|
||||||
totp =
|
totp =
|
||||||
isFocusedTotpField && passwordMatchesFocused(passwordFieldToUse)
|
isFocusedTotpField && passwordMatchesFocused(firstPasswordField)
|
||||||
? focusedField
|
? focusedField
|
||||||
: this.findTotpField(pageDetails, passwordFieldToUse, false, false, true);
|
: this.findTotpField(pageDetails, firstPasswordField, false, false, true);
|
||||||
if (totp) {
|
if (totp) {
|
||||||
totps.push(totp);
|
totps.push(totp);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user