Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PM-14954] implement multi input totp styling #12449

Merged
merged 18 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
942c55a
update menu and button position for multi-input totp
Dec 11, 2024
6280fc7
update test to better handle breaking changes
Dec 12, 2024
9a2d7f0
merge main
Dec 17, 2024
7c40eb8
Merge branch 'main' into autofill/PM-14954-implement-multi-input-totp
evan-livefront Dec 17, 2024
f9a0391
Merge branch 'main' into autofill/PM-14954-implement-multi-input-totp
evan-livefront Dec 20, 2024
5580fc8
Merge branch 'main' into autofill/PM-14954-implement-multi-input-totp
evan-livefront Jan 2, 2025
8c0e00b
Merge branch 'main' into autofill/PM-14954-implement-multi-input-totp
evan-livefront Jan 2, 2025
8cbdc1e
Merge branch 'main' into autofill/PM-14954-implement-multi-input-totp
evan-livefront Jan 3, 2025
48712f2
fix sizing bug by filtering duplicate opid fields
Jan 6, 2025
44fd4e2
Merge branch 'main' into autofill/PM-14954-implement-multi-input-totp
evan-livefront Jan 6, 2025
0e4ecb8
update getTotpFields usage
Jan 6, 2025
7a14e6b
Merge branch 'main' into autofill/PM-14954-implement-multi-input-totp
evan-livefront Jan 6, 2025
89d286d
revert private changes per feedback
Jan 7, 2025
aeecab6
Merge branch 'main' into autofill/PM-14954-implement-multi-input-totp
evan-livefront Jan 7, 2025
82938eb
Merge branch 'main' into autofill/PM-14954-implement-multi-input-totp
evan-livefront Jan 13, 2025
e878578
Update apps/browser/src/autofill/utils/index.ts with positive fucntion
evan-livefront Jan 13, 2025
a3ce34c
fix type and update rectNotZero function
Jan 13, 2025
f5f15f9
Merge branch 'main' into autofill/PM-14954-implement-multi-input-totp
evan-livefront Jan 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@ export type InlineMenuElementPosition = {
height: number;
};

export type FieldRect = {
bottom: number;
height: number;
left: number;
right: number;
top: number;
width: number;
x: number;
y: number;
};

export type InlineMenuPosition = {
button?: InlineMenuElementPosition;
list?: InlineMenuElementPosition;
Expand Down Expand Up @@ -134,6 +145,7 @@ export type OverlayBackgroundExtensionMessage = {
isFieldCurrentlyFilling?: boolean;
subFrameData?: SubFrameOffsetData;
focusedFieldData?: FocusedFieldData;
allFieldsRect?: any;
isOpeningFullInlineMenu?: boolean;
styles?: Partial<CSSStyleDeclaration>;
data?: LockedVaultPendingNotificationsData;
Expand Down
118 changes: 118 additions & 0 deletions apps/browser/src/autofill/background/overlay.background.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { BehaviorSubject } from "rxjs";

import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";

Check failure on line 5 in apps/browser/src/autofill/background/overlay.background.spec.ts

View workflow job for this annotation

GitHub Actions / Test Results

OverlayBackground โ–บ OverlayBackground extension message handlers openAutofillInlineMenu message handler updates the inline menu position of both button and list elements if the inline menu is being forced open โ–บ OverlayBackground extension message hand...

Failed test found in: junit.xml Error: Error: expect(jest.fn()).toHaveBeenCalledWith(...expected)
Raw output
Error: expect(jest.fn()).toHaveBeenCalledWith(...expected)

Expected: undefined, {"command": "appendAutofillInlineMenuToDom", "overlayElement": "autofill-inline-menu-list"}, {"frameId": 0}
Received
       1
          undefined,
          Object {
        -   "command": "appendAutofillInlineMenuToDom",
        -   "overlayElement": "autofill-inline-menu-list",
        +   "command": "getInlineMenuFormFieldData",
        +   "ignoreFieldFocus": true,
          },
          Object {
        -   "frameId": 0,
        +   "frameId": [Function mockConstructor],
          },
       2
          undefined,
          Object {
            "command": "appendAutofillInlineMenuToDom",
        -   "overlayElement": "autofill-inline-menu-list",
        +   "overlayElement": "autofill-inline-menu-button",
          },
          {"frameId": 0},

Number of calls: 2
    at /home/runner/work/clients/clients/apps/browser/src/autofill/background/overlay.background.spec.ts:2241:36
    at Generator.next (<anonymous>)
    at fulfilled (/home/runner/work/clients/clients/apps/browser/src/autofill/background/overlay.background.spec.ts:5:58)
    at _ZoneDelegate.invoke (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:416:32)
    at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone-testing.umd.js:2176:43)
    at _ZoneDelegate.invoke (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:415:38)
    at ZoneImpl.run (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:147:47)
    at /home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:2598:42
    at _ZoneDelegate.invokeTask (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:449:37)
    at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvokeTask (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone-testing.umd.js:2207:43)
    at _ZoneDelegate.invokeTask (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:448:42)
    at ZoneImpl.runTask (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:196:51)
    at drainMicroTaskQueue (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:638:39)
    at ZoneTask.invokeTask (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:535:25)
    at ZoneTask.invoke (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:520:52)
    at data.args.<computed> (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:1839:36)
    at Timeout.task [as _onTimeout] (/home/runner/work/clients/clients/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/browser/Window.js:520:19)
    at listOnTimeout (node:internal/timers:581:17)
    at processTimers (node:internal/timers:519:7)

Check failure on line 5 in apps/browser/src/autofill/background/overlay.background.spec.ts

View workflow job for this annotation

GitHub Actions / Test Results

OverlayBackground โ–บ OverlayBackground extension message handlers openAutofillInlineMenu message handler when the focused field does not have a value updates the position of the both button and list elements if the user has the inline menu set to show o...

Failed test found in: junit.xml Error: Error: expect(jest.fn()).toHaveBeenCalledWith(...expected)
Raw output
Error: expect(jest.fn()).toHaveBeenCalledWith(...expected)

Expected: undefined, {"command": "appendAutofillInlineMenuToDom", "overlayElement": "autofill-inline-menu-list"}, {"frameId": 0}
Received
       1
          undefined,
          Object {
        -   "command": "appendAutofillInlineMenuToDom",
        -   "overlayElement": "autofill-inline-menu-list",
        +   "command": "getInlineMenuFormFieldData",
        +   "ignoreFieldFocus": true,
          },
          Object {
        -   "frameId": 0,
        +   "frameId": [Function mockConstructor],
          },
       2
          undefined,
          Object {
            "command": "appendAutofillInlineMenuToDom",
        -   "overlayElement": "autofill-inline-menu-list",
        +   "overlayElement": "autofill-inline-menu-button",
          },
          {"frameId": 0},

Number of calls: 2
    at /home/runner/work/clients/clients/apps/browser/src/autofill/background/overlay.background.spec.ts:2272:38
    at Generator.next (<anonymous>)
    at fulfilled (/home/runner/work/clients/clients/apps/browser/src/autofill/background/overlay.background.spec.ts:5:58)
    at _ZoneDelegate.invoke (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:416:32)
    at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone-testing.umd.js:2176:43)
    at _ZoneDelegate.invoke (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:415:38)
    at ZoneImpl.run (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:147:47)
    at /home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:2598:42
    at _ZoneDelegate.invokeTask (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:449:37)
    at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvokeTask (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone-testing.umd.js:2207:43)
    at _ZoneDelegate.invokeTask (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:448:42)
    at ZoneImpl.runTask (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:196:51)
    at drainMicroTaskQueue (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:638:39)
    at ZoneTask.invokeTask (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:535:25)
    at ZoneTask.invoke (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:520:52)
    at data.args.<computed> (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:1839:36)
    at Timeout.task [as _onTimeout] (/home/runner/work/clients/clients/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/browser/Window.js:520:19)
    at listOnTimeout (node:internal/timers:581:17)
    at processTimers (node:internal/timers:519:7)

Check failure on line 5 in apps/browser/src/autofill/background/overlay.background.spec.ts

View workflow job for this annotation

GitHub Actions / Test Results

OverlayBackground โ–บ OverlayBackground extension message handlers openAutofillInlineMenu message handler when the focused field has a value updates the position of both button and list elements if the inline menu is showing the save login view โ–บ Overlay...

Failed test found in: junit.xml Error: Error: expect(jest.fn()).toHaveBeenCalledWith(...expected)
Raw output
Error: expect(jest.fn()).toHaveBeenCalledWith(...expected)

Expected: undefined, {"command": "appendAutofillInlineMenuToDom", "overlayElement": "autofill-inline-menu-list"}, {"frameId": 0}
Received
       1
          undefined,
          Object {
        -   "command": "appendAutofillInlineMenuToDom",
        -   "overlayElement": "autofill-inline-menu-list",
        +   "command": "getInlineMenuFormFieldData",
        +   "ignoreFieldFocus": true,
          },
          Object {
        -   "frameId": 0,
        +   "frameId": [Function mockConstructor],
          },
       2
          undefined,
          Object {
        -   "command": "appendAutofillInlineMenuToDom",
        -   "overlayElement": "autofill-inline-menu-list",
        +   "command": "getInlineMenuFormFieldData",
        +   "ignoreFieldFocus": true,
          },
          Object {
        -   "frameId": 0,
        +   "frameId": [Function mockConstructor],
          },
       3
          undefined,
          Object {
            "command": "appendAutofillInlineMenuToDom",
        -   "overlayElement": "autofill-inline-menu-list",
        +   "overlayElement": "autofill-inline-menu-button",
          },
          {"frameId": 0},

Number of calls: 3
    at /home/runner/work/clients/clients/apps/browser/src/autofill/background/overlay.background.spec.ts:2356:38
    at Generator.next (<anonymous>)
    at fulfilled (/home/runner/work/clients/clients/apps/browser/src/autofill/background/overlay.background.spec.ts:5:58)
    at _ZoneDelegate.invoke (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:416:32)
    at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone-testing.umd.js:2176:43)
    at _ZoneDelegate.invoke (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:415:38)
    at ZoneImpl.run (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:147:47)
    at /home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:2598:42
    at _ZoneDelegate.invokeTask (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:449:37)
    at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvokeTask (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone-testing.umd.js:2207:43)
    at _ZoneDelegate.invokeTask (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:448:42)
    at ZoneImpl.runTask (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:196:51)
    at drainMicroTaskQueue (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:638:39)
    at ZoneTask.invokeTask (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:535:25)
    at ZoneTask.invoke (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:520:52)
    at data.args.<computed> (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:1839:36)
    at Timeout.task [as _onTimeout] (/home/runner/work/clients/clients/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/browser/Window.js:520:19)
    at listOnTimeout (node:internal/timers:581:17)
    at processTimers (node:internal/timers:519:7)
import {
AutofillOverlayVisibility,
SHOW_AUTOFILL_BUTTON,
Expand Down Expand Up @@ -2899,6 +2899,124 @@
);
});
});
describe("handles menu position when input is focused", () => {
it("sets button and menu width and position when non-multi-input totp field is focused", async () => {
const subframe = {
top: 0,
left: 0,
url: "",
frameId: 0,
};

overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({
focusedFieldRects: {
width: 49.328125,
height: 64,
top: 302.171875,
left: 1270.8125,
},
});

const buttonPostion = overlayBackground.getInlineMenuButtonPosition(subframe);
const menuPostion = overlayBackground.getInlineMenuListPosition(subframe);

expect(menuPostion).toEqual({
width: "49px",
top: "366px",
left: "1271px",
});
expect(buttonPostion).toEqual({
width: "34px",
height: "34px",
top: "317px",
left: "1271px",
});
});
it("sets button and menu width and position when multi-input totp field is focused", async () => {
const subframe = {
top: 0,
left: 0,
url: "",
frameId: 0,
};

const totpFields = [
createAutofillFieldMock({ autoCompleteType: "one-time-code", opid: "__0" }),
createAutofillFieldMock({ autoCompleteType: "one-time-code", opid: "__1" }),
createAutofillFieldMock({ autoCompleteType: "one-time-code", opid: "__2" }),
];
const allFieldData = [
createAutofillFieldMock({
autoCompleteType: "one-time-code",
opid: "__0",
rect: {
x: 1041.5,
y: 302.171875,
width: 49.328125,
height: 64,
top: 302.171875,
right: 1090.828125,
bottom: 366.171875,
left: 1041.5,
},
}),
createAutofillFieldMock({
autoCompleteType: "one-time-code",
opid: "__1",
rect: {
x: 1098.828125,
y: 302.171875,
width: 49.328125,
height: 64,
top: 302.171875,
right: 1148.15625,
bottom: 366.171875,
left: 1098.828125,
},
}),
createAutofillFieldMock({
autoCompleteType: "one-time-code",
opid: "__2",
rect: {
x: 1156.15625,
y: 302.171875,
width: 249.328125,
height: 64,
top: 302.171875,
right: 2205.484375,
bottom: 366.171875,
left: 2156.15625,
},
}),
];
overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({
focusedFieldRects: {
width: 49.328125,
height: 64,
top: 302.171875,
left: 1270.8125,
},
});

overlayBackground["allFieldData"] = allFieldData;
jest.spyOn(overlayBackground as any, "isTotpFieldForCurrentField").mockReturnValue(true);
jest.spyOn(overlayBackground as any, "getTotpFields").mockReturnValue(totpFields);

const buttonPostion = overlayBackground.getInlineMenuButtonPosition(subframe);
const menuPostion = overlayBackground.getInlineMenuListPosition(subframe);
expect(menuPostion).toEqual({
width: "1164px",
top: "366px",
left: "1042px",
});
expect(buttonPostion).toEqual({
width: "34px",
height: "34px",
top: "292px",
left: "2187px",
});
});
});

describe("triggerDelayedAutofillInlineMenuClosure message handler", () => {
it("skips triggering the delayed closure of the inline menu if a field is currently focused", async () => {
Expand Down
91 changes: 86 additions & 5 deletions apps/browser/src/autofill/background/overlay.background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
generateDomainMatchPatterns,
generateRandomChars,
isInvalidResponseStatusCode,
rectNotZero,
specialCharacterToKeyMap,
} from "../utils";

Expand Down Expand Up @@ -129,6 +130,7 @@
private currentInlineMenuCiphersCount: number = 0;
private currentAddNewItemData: CurrentAddNewItemData;
private focusedFieldData: FocusedFieldData;
private allFieldData: AutofillField[];
private isFieldCurrentlyFocused: boolean = false;
private isFieldCurrentlyFilling: boolean = false;
private isInlineMenuButtonVisible: boolean = false;
Expand Down Expand Up @@ -1344,6 +1346,71 @@
this.isInlineMenuListVisible = false;
}

/**
* Get all the totp fields for the tab and frame of the currently focused field
*/
private getTotpFields(): AutofillField[] {
const currentTabId = this.focusedFieldData?.tabId;
const currentFrameId = this.focusedFieldData?.frameId;
const pageDetailsMap = this.pageDetailsForTab[currentTabId];
const pageDetails = pageDetailsMap?.get(currentFrameId);

const fields = pageDetails.details.fields;

Check failure on line 1358 in apps/browser/src/autofill/background/overlay.background.ts

View workflow job for this annotation

GitHub Actions / Test Results

OverlayBackground โ–บ OverlayBackground inline menu button message handlers handles menu position when input is focused sets button and menu width and position when non-multi-input totp field is focused โ–บ OverlayBackground inline menu button message hand...

Failed test found in: junit.xml Error: TypeError: Cannot read properties of undefined (reading 'details')
Raw output
TypeError: Cannot read properties of undefined (reading 'details')
    at OverlayBackground.details [as getTotpFields] (/home/runner/work/clients/clients/apps/browser/src/autofill/background/overlay.background.ts:1358:32)
    at OverlayBackground.getTotpFields [as getInlineMenuButtonPosition] (/home/runner/work/clients/clients/apps/browser/src/autofill/background/overlay.background.ts:1522:29)
    at /home/runner/work/clients/clients/apps/browser/src/autofill/background/overlay.background.spec.ts:2920:49
    at Generator.next (<anonymous>)
    at /home/runner/work/clients/clients/apps/browser/src/autofill/background/overlay.background.spec.ts:8:71
    at new ZoneAwarePromise (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:2623:29)
    at Object.<anonymous>.__awaiter (/home/runner/work/clients/clients/apps/browser/src/autofill/background/overlay.background.spec.ts:4:12)
    at /home/runner/work/clients/clients/apps/browser/src/autofill/background/overlay.background.spec.ts:2903:107
    at _ZoneDelegate.invoke (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:416:32)
    at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone-testing.umd.js:2176:43)
    at _ZoneDelegate.invoke (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:415:38)
    at ZoneImpl.run (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone.umd.js:147:47)
    at Object.wrappedFunc (/home/runner/work/clients/clients/node_modules/zone.js/bundles/zone-testing.umd.js:450:38)
    at Promise.then.completed (/home/runner/work/clients/clients/node_modules/jest-circus/build/utils.js:298:28)
    at new Promise (<anonymous>)
    at callAsyncCircusFn (/home/runner/work/clients/clients/node_modules/jest-circus/build/utils.js:231:10)
    at _callCircusTest (/home/runner/work/clients/clients/node_modules/jest-circus/build/run.js:316:40)
    at _runTest (/home/runner/work/clients/clients/node_modules/jest-circus/build/run.js:252:3)
    at _runTestsForDescribeBlock (/home/runner/work/clients/clients/node_modules/jest-circus/build/run.js:126:9)
    at _runTestsForDescribeBlock (/home/runner/work/clients/clients/node_modules/jest-circus/build/run.js:121:9)
    at _runTestsForDescribeBlock (/home/runner/work/clients/clients/node_modules/jest-circus/build/run.js:121:9)
    at _runTestsForDescribeBlock (/home/runner/work/clients/clients/node_modules/jest-circus/build/run.js:121:9)
    at run (/home/runner/work/clients/clients/node_modules/jest-circus/build/run.js:71:3)
    at runAndTransformResultsToJestFormat (/home/runner/work/clients/clients/node_modules/jest-circus/build/legacy-code-todo-rewrite/jestAdapterInit.js:122:21)
const totpFields = fields.filter((f) =>
this.inlineMenuFieldQualificationService.isTotpField(f),
);

return totpFields;
}

/**
* calculates the postion and width for multi-input totp field inline menu
* @param totpFieldArray - the totp fields used to evaluate the position of the menu
*/
private calculateTotpMultiInputMenuBounds(totpFieldArray: AutofillField[]) {
// Filter the fields based on the provided totpfields
const filteredObjects = this.allFieldData.filter((obj) =>
totpFieldArray.some((o) => o.opid === obj.opid),
);

// Return null if no matching objects are found
if (filteredObjects.length === 0) {
return null;
}
// Calculate the smallest left and largest right values to determine width
const left = Math.min(
...filteredObjects.filter((obj) => rectNotZero(obj.rect)).map((obj) => obj.rect.left),
);
const largestRight = Math.max(
...filteredObjects.filter((obj) => rectNotZero(obj.rect)).map((obj) => obj.rect.right),
);

const width = largestRight - left;

return { left, width };
}

/**
* calculates the postion for multi-input totp field inline button
* @param totpFieldArray - the totp fields used to evaluate the position of the menu
*/
private calculateTotpMultiInputButtonBounds(totpFieldArray: AutofillField[]) {
const filteredObjects = this.allFieldData.filter((obj) =>
totpFieldArray.some((o) => o.opid === obj.opid),
);

if (filteredObjects.length === 0) {
return null;
}

const maxRight = Math.max(...filteredObjects.map((obj) => obj.rect.right));
const maxObject = filteredObjects.find((obj) => obj.rect.right === maxRight);
const top = maxObject.rect.top - maxObject.rect.height * 0.39;
const left = maxRight - maxObject.rect.height * 0.3;

return { left, top };
}

/**
* Updates the position of either the inline menu list or button. The position
* is based on the focused field's position and dimensions.
Expand Down Expand Up @@ -1445,12 +1512,19 @@
* Gets the position of the focused field and calculates the position
* of the inline menu button based on the focused field's position and dimensions.
*/
private getInlineMenuButtonPosition(subFrameOffsets: SubFrameOffsetData) {
getInlineMenuButtonPosition(subFrameOffsets: SubFrameOffsetData) {
const subFrameTopOffset = subFrameOffsets?.top || 0;
const subFrameLeftOffset = subFrameOffsets?.left || 0;

const { top, left, width, height } = this.focusedFieldData.focusedFieldRects;
const { width, height } = this.focusedFieldData.focusedFieldRects;
let { top, left } = this.focusedFieldData.focusedFieldRects;
const { paddingRight, paddingLeft } = this.focusedFieldData.focusedFieldStyles;
const totpFields = this.getTotpFields();

if (this.isTotpFieldForCurrentField() && totpFields.length > 1) {
({ left, top } = this.calculateTotpMultiInputButtonBounds(totpFields));
}

let elementOffset = height * 0.37;
if (height >= 35) {
elementOffset = height >= 50 ? height * 0.47 : height * 0.42;
Expand Down Expand Up @@ -1485,11 +1559,17 @@
* Gets the position of the focused field and calculates the position
* of the inline menu list based on the focused field's position and dimensions.
*/
private getInlineMenuListPosition(subFrameOffsets: SubFrameOffsetData) {
getInlineMenuListPosition(subFrameOffsets: SubFrameOffsetData) {
const subFrameTopOffset = subFrameOffsets?.top || 0;
const subFrameLeftOffset = subFrameOffsets?.left || 0;

const { top, left, width, height } = this.focusedFieldData.focusedFieldRects;
const { top, height } = this.focusedFieldData.focusedFieldRects;
let { left, width } = this.focusedFieldData.focusedFieldRects;
const totpFields = this.getTotpFields();

if (this.isTotpFieldForCurrentField() && totpFields.length > 1) {
({ left, width } = this.calculateTotpMultiInputMenuBounds(totpFields));
}

this.inlineMenuPosition.list = {
top: Math.round(top + height + subFrameTopOffset),
Expand All @@ -1512,7 +1592,7 @@
* @param sender - The sender of the extension message
*/
private setFocusedFieldData(
{ focusedFieldData }: OverlayBackgroundExtensionMessage,
{ focusedFieldData, allFieldsRect }: OverlayBackgroundExtensionMessage,
sender: chrome.runtime.MessageSender,
) {
if (
Expand All @@ -1529,6 +1609,7 @@

const previousFocusedFieldData = this.focusedFieldData;
this.focusedFieldData = { ...focusedFieldData, tabId: sender.tab.id, frameId: sender.frameId };
this.allFieldData = allFieldsRect;
this.isFieldCurrentlyFocused = true;

if (this.shouldUpdatePasswordGeneratorMenuOnFieldFocus()) {
Expand Down
6 changes: 6 additions & 0 deletions apps/browser/src/autofill/models/autofill-field.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { FieldRect } from "../background/abstractions/overlay.background";
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { AutofillFieldQualifierType } from "../enums/autofill-field.enums";
Expand Down Expand Up @@ -124,4 +125,9 @@ export default class AutofillField {
fieldQualifier?: AutofillFieldQualifierType;

accountCreationFieldType?: InlineMenuAccountCreationFieldTypes;

/**
* used for totp multiline calculations
*/
fieldRect?: FieldRect;
}
Original file line number Diff line number Diff line change
Expand Up @@ -957,12 +957,21 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
accountCreationFieldType: autofillFieldData?.accountCreationFieldType,
};

const allFields = this.formFieldElements;
const allFieldsRect = [];

for (const key of allFields.keys()) {
const rect = await this.getMostRecentlyFocusedFieldRects(key);
allFieldsRect.push({ ...allFields.get(key), rect }); // Add the combined result to the array
}

await this.sendExtensionMessage("updateFocusedFieldData", {
focusedFieldData: this.focusedFieldData,
allFieldsRect,
});
}

/**
/**his.formFieldElements
* Gets the bounding client rects for the most recently focused field. This method will
* attempt to use an intersection observer to get the most recently focused field's
* bounding client rects. If the intersection observer is not supported, or the
Expand Down
10 changes: 10 additions & 0 deletions apps/browser/src/autofill/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { FieldRect } from "../background/abstractions/overlay.background";
import { AutofillPort } from "../enums/autofill-port.enum";
import { FillableFormFieldElement, FormElementWithAttribute, FormFieldElement } from "../types";

Expand Down Expand Up @@ -544,3 +545,12 @@ export const specialCharacterToKeyMap: Record<string, string> = {
"?": "questionCharacterDescriptor",
"/": "forwardSlashCharacterDescriptor",
};

/**
* Determines if the current rect values are not all 0.
*/
export function rectNotZero(rect: FieldRect): boolean {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non-blocking nit; a non-negative function name (e.g. rectHasSize or rectIsZero and invert logic) could improve readability

if (rect.right !== 0 && rect.left !== 0 && rect.top !== 0 && rect.bottom !== 0) {
return true;
}
}
Loading