Skip to content

Commit

Permalink
Merge branch 'main' into km/fix-badge-mv2
Browse files Browse the repository at this point in the history
  • Loading branch information
quexten committed Jan 15, 2025
2 parents a39bd41 + 8e95029 commit 71a7f05
Show file tree
Hide file tree
Showing 112 changed files with 835 additions and 293 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,9 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}

- name: Scan with SonarCloud
uses: sonarsource/sonarcloud-github-action@02ef91109b2d589e757aefcfb2854c2783fd7b19 # v4.0.0
uses: sonarsource/sonarqube-scan-action@bfd4e558cda28cda6b5defafb9232d191be8c203 # v4.2.1
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: >
-Dsonar.organization=${{ github.repository_owner }}
Expand Down
5 changes: 3 additions & 2 deletions apps/browser/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bitwarden/browser",
"version": "2025.1.0",
"version": "2025.1.1",
"scripts": {
"build": "npm run build:chrome",
"build:chrome": "cross-env BROWSER=chrome MANIFEST_VERSION=3 webpack",
Expand Down Expand Up @@ -30,6 +30,7 @@
"dist:safari:mv3": "cross-env MANIFEST_VERSION=3 npm run dist:safari",
"test": "jest",
"test:watch": "jest --watch",
"test:watch:all": "jest --watchAll"
"test:watch:all": "jest --watchAll",
"test:clearCache": "jest --clear-cache"
}
}
12 changes: 6 additions & 6 deletions apps/browser/src/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2339,11 +2339,11 @@
"blockedDomainsDesc": {
"message": "Autofill and other related features will not be offered for these websites. You must refresh the page for changes to take effect."
},
"autofillBlockedNotice": {
"message": "Autofill is blocked for this website. Review or change this in settings."
"autofillBlockedNoticeV2": {
"message": "Autofill is blocked for this website."
},
"autofillBlockedTooltip": {
"message": "Autofill is blocked on this website. Review in settings."
"autofillBlockedNoticeGuidance": {
"message": "Change this in settings"
},
"websiteItemLabel": {
"message": "Website $number$ (URI)",
Expand Down Expand Up @@ -4007,8 +4007,8 @@
"passkeyRemoved": {
"message": "Passkey removed"
},
"autofillSuggestions": {
"message": "Autofill suggestions"
"itemSuggestions": {
"message": "Suggested items"
},
"autofillSuggestionsTip": {
"message": "Save a login item for this site to autofill"
Expand Down
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 @@ -2913,6 +2913,124 @@ describe("OverlayBackground", () => {
);
});
});
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
92 changes: 89 additions & 3 deletions apps/browser/src/autofill/background/overlay.background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ import {
generateDomainMatchPatterns,
generateRandomChars,
isInvalidResponseStatusCode,
rectHasSize,
specialCharacterToKeyMap,
} from "../utils";

Expand Down Expand Up @@ -130,6 +131,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
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 @@ -1367,6 +1369,71 @@ export class OverlayBackground implements OverlayBackgroundInterface {
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;
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) => rectHasSize(obj.rect)).map((obj) => obj.rect.left),
);
const largestRight = Math.max(
...filteredObjects.filter((obj) => rectHasSize(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 @@ -1472,8 +1539,17 @@ export class OverlayBackground implements OverlayBackgroundInterface {
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;

if (this.isTotpFieldForCurrentField()) {
const totpFields = this.getTotpFields();
if (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 @@ -1512,7 +1588,16 @@ export class OverlayBackground implements OverlayBackgroundInterface {
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;

if (this.isTotpFieldForCurrentField()) {
const totpFields = this.getTotpFields();

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

this.inlineMenuPosition.list = {
top: Math.round(top + height + subFrameTopOffset),
Expand All @@ -1535,7 +1620,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
* @param sender - The sender of the extension message
*/
private setFocusedFieldData(
{ focusedFieldData }: OverlayBackgroundExtensionMessage,
{ focusedFieldData, allFieldsRect }: OverlayBackgroundExtensionMessage,
sender: chrome.runtime.MessageSender,
) {
if (
Expand All @@ -1552,6 +1637,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {

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,8 +957,17 @@ 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,
});
}

Expand Down
Loading

0 comments on commit 71a7f05

Please sign in to comment.