1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-01-25 21:51:30 +01:00

[PM-8027] Fixing issue with username fields not qualifyng as a valid login field if a viewable password field is not present

This commit is contained in:
Cesar Gonzalez 2024-06-13 13:36:05 -05:00
parent 71b2c27e01
commit 31999b9e84
No known key found for this signature in database
GPG Key ID: 3381A5457F8CCECF
4 changed files with 55 additions and 41 deletions

View File

@ -65,7 +65,6 @@ export type OverlayBackgroundExtensionMessage = {
subFrameData?: SubFrameOffsetData; subFrameData?: SubFrameOffsetData;
focusedFieldData?: FocusedFieldData; focusedFieldData?: FocusedFieldData;
styles?: Partial<CSSStyleDeclaration>; styles?: Partial<CSSStyleDeclaration>;
triggerInlineMenuPositionUpdate?: boolean;
data?: LockedVaultPendingNotificationsData; data?: LockedVaultPendingNotificationsData;
} & OverlayAddNewItemMessage & } & OverlayAddNewItemMessage &
CloseInlineMenuMessage; CloseInlineMenuMessage;
@ -121,7 +120,8 @@ export type OverlayBackgroundExtensionMessageHandlers = {
checkShouldRepositionInlineMenu: ({ sender }: BackgroundSenderParam) => boolean; checkShouldRepositionInlineMenu: ({ sender }: BackgroundSenderParam) => boolean;
getCurrentTabFrameId: ({ sender }: BackgroundSenderParam) => number; getCurrentTabFrameId: ({ sender }: BackgroundSenderParam) => number;
updateSubFrameData: ({ message, sender }: BackgroundOnMessageHandlerParams) => void; updateSubFrameData: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
rebuildSubFrameOffsets: ({ message, sender }: BackgroundOnMessageHandlerParams) => void; updateSubFrameOffsetsForReflowEvent: ({ sender }: BackgroundSenderParam) => void;
repositionAutofillInlineMenuForSubFrame: ({ sender }: BackgroundSenderParam) => void;
destroyAutofillInlineMenuListeners: ({ destroyAutofillInlineMenuListeners: ({
message, message,
sender, sender,

View File

@ -361,11 +361,11 @@ describe("OverlayBackground", () => {
); );
}); });
describe("rebuildSubFrameOffsets", () => { describe("repositionInlineMenuForSubFrame", () => {
it("skips rebuilding sub frame offsets if the sender contains the currently focused field", () => { it("skips rebuilding sub frame offsets if the sender contains the currently focused field", () => {
const sender = mock<chrome.runtime.MessageSender>({ tab, frameId: bottomFrameId }); const sender = mock<chrome.runtime.MessageSender>({ tab, frameId: bottomFrameId });
sendMockExtensionMessage({ command: "rebuildSubFrameOffsets" }, sender); sendMockExtensionMessage({ command: "repositionInlineMenuForSubFrame" }, sender);
expect(getFrameDetailsSpy).not.toHaveBeenCalled(); expect(getFrameDetailsSpy).not.toHaveBeenCalled();
}); });
@ -376,7 +376,7 @@ describe("OverlayBackground", () => {
frameId: 0, frameId: 0,
}); });
sendMockExtensionMessage({ command: "rebuildSubFrameOffsets" }, sender); sendMockExtensionMessage({ command: "repositionInlineMenuForSubFrame" }, sender);
expect(getFrameDetailsSpy).not.toHaveBeenCalled(); expect(getFrameDetailsSpy).not.toHaveBeenCalled();
}); });
@ -384,7 +384,7 @@ describe("OverlayBackground", () => {
it("rebuilds the sub frame offsets for a given tab", async () => { it("rebuilds the sub frame offsets for a given tab", async () => {
const sender = mock<chrome.runtime.MessageSender>({ tab, frameId: middleFrameId }); const sender = mock<chrome.runtime.MessageSender>({ tab, frameId: middleFrameId });
sendMockExtensionMessage({ command: "rebuildSubFrameOffsets" }, sender); sendMockExtensionMessage({ command: "repositionInlineMenuForSubFrame" }, sender);
await flushPromises(); await flushPromises();
expect(getFrameDetailsSpy).toHaveBeenCalledWith({ tabId, frameId: topFrameId }); expect(getFrameDetailsSpy).toHaveBeenCalledWith({ tabId, frameId: topFrameId });
@ -399,7 +399,7 @@ describe("OverlayBackground", () => {
jest.spyOn(overlayBackground as any, "updateInlineMenuPositionAfterSubFrameRebuild"); jest.spyOn(overlayBackground as any, "updateInlineMenuPositionAfterSubFrameRebuild");
sendMockExtensionMessage( sendMockExtensionMessage(
{ command: "rebuildSubFrameOffsets", triggerInlineMenuPositionUpdate: true }, { command: "repositionInlineMenuForSubFrame", triggerInlineMenuPositionUpdate: true },
sender, sender,
); );
await flushPromises(); await flushPromises();
@ -435,7 +435,7 @@ describe("OverlayBackground", () => {
isFieldCurrentlyFocused: false, isFieldCurrentlyFocused: false,
}); });
sendMockExtensionMessage({ command: "rebuildSubFrameOffsets" }, sender); sendMockExtensionMessage({ command: "repositionInlineMenuForSubFrame" }, sender);
await flushInlineMenuUpdatePromises(); await flushInlineMenuUpdatePromises();
expect(tabsSendMessageSpy).not.toHaveBeenCalledWith( expect(tabsSendMessageSpy).not.toHaveBeenCalledWith(
@ -458,7 +458,7 @@ describe("OverlayBackground", () => {
it("updates the position of the inline menu elements", async () => { it("updates the position of the inline menu elements", async () => {
sendMockExtensionMessage( sendMockExtensionMessage(
{ command: "rebuildSubFrameOffsets", triggerInlineMenuPositionUpdate: true }, { command: "repositionInlineMenuForSubFrame", triggerInlineMenuPositionUpdate: true },
sender, sender,
); );
await flushInlineMenuUpdatePromises(); await flushInlineMenuUpdatePromises();
@ -491,7 +491,7 @@ describe("OverlayBackground", () => {
}); });
sendMockExtensionMessage( sendMockExtensionMessage(
{ command: "rebuildSubFrameOffsets", triggerInlineMenuPositionUpdate: true }, { command: "repositionInlineMenuForSubFrame", triggerInlineMenuPositionUpdate: true },
sender, sender,
); );
await flushInlineMenuUpdatePromises(); await flushInlineMenuUpdatePromises();

View File

@ -64,6 +64,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
private inlineMenuPageTranslations: Record<string, string>; private inlineMenuPageTranslations: Record<string, string>;
private inlineMenuFadeInTimeout: number | NodeJS.Timeout; private inlineMenuFadeInTimeout: number | NodeJS.Timeout;
private updateInlineMenuPositionTimeout: number | NodeJS.Timeout; private updateInlineMenuPositionTimeout: number | NodeJS.Timeout;
private isReflowUpdatingSubFrames: boolean = false;
private delayedCloseTimeout: number | NodeJS.Timeout; private delayedCloseTimeout: number | NodeJS.Timeout;
private focusedFieldData: FocusedFieldData; private focusedFieldData: FocusedFieldData;
private isFieldCurrentlyFocused: boolean = false; private isFieldCurrentlyFocused: boolean = false;
@ -95,7 +96,9 @@ export class OverlayBackground implements OverlayBackgroundInterface {
checkShouldRepositionInlineMenu: ({ sender }) => this.checkShouldRepositionInlineMenu(sender), checkShouldRepositionInlineMenu: ({ sender }) => this.checkShouldRepositionInlineMenu(sender),
getCurrentTabFrameId: ({ sender }) => this.getSenderFrameId(sender), getCurrentTabFrameId: ({ sender }) => this.getSenderFrameId(sender),
updateSubFrameData: ({ message, sender }) => this.updateSubFrameData(message, sender), updateSubFrameData: ({ message, sender }) => this.updateSubFrameData(message, sender),
rebuildSubFrameOffsets: ({ message, sender }) => this.rebuildSubFrameOffsets(message, sender), updateSubFrameOffsetsForReflowEvent: ({ sender }) => this.rebuildSubFrameOffsets(sender),
repositionAutofillInlineMenuForSubFrame: ({ sender }) =>
this.repositionInlineMenuForSubFrame(sender),
destroyAutofillInlineMenuListeners: ({ message, sender }) => destroyAutofillInlineMenuListeners: ({ message, sender }) =>
this.triggerDestroyInlineMenuListeners(sender.tab, message.subFrameData.frameId), this.triggerDestroyInlineMenuListeners(sender.tab, message.subFrameData.frameId),
collectPageDetailsResponse: ({ message, sender }) => this.storePageDetails(message, sender), collectPageDetailsResponse: ({ message, sender }) => this.storePageDetails(message, sender),
@ -376,26 +379,12 @@ export class OverlayBackground implements OverlayBackgroundInterface {
} }
/** /**
* Handles rebuilding the sub frame offsets when the tab is repositioned or scrolled. * Rebuilds the sub frame offsets for the tab associated with the sender.
* Will trigger a re-positioning of the inline menu list and button. Note that we
* do not trigger an update to sub frame data if the sender is the frame that has
* the field currently focused. We trigger a re-calculation of the field's position
* and as a result, the sub frame offsets of that frame will be updated.
* *
* @param message - The message received from the `rebuildSubFrameOffsets` command
* @param sender - The sender of the message * @param sender - The sender of the message
*/ */
private async rebuildSubFrameOffsets( private async rebuildSubFrameOffsets(sender: chrome.runtime.MessageSender) {
message: OverlayBackgroundExtensionMessage, this.clearDelayedInlineMenuClosure();
sender: chrome.runtime.MessageSender,
) {
if (sender.frameId === this.focusedFieldData?.frameId) {
return;
}
if (this.updateInlineMenuPositionTimeout) {
clearTimeout(this.updateInlineMenuPositionTimeout);
}
const subFrameOffsetsForTab = this.subFrameOffsetsForTab[sender.tab.id]; const subFrameOffsetsForTab = this.subFrameOffsetsForTab[sender.tab.id];
if (subFrameOffsetsForTab) { if (subFrameOffsetsForTab) {
@ -409,13 +398,32 @@ export class OverlayBackground implements OverlayBackgroundInterface {
await this.buildSubFrameOffsets(sender.tab, frameId, sender.url); await this.buildSubFrameOffsets(sender.tab, frameId, sender.url);
} }
} }
}
if (message.triggerInlineMenuPositionUpdate) { /**
this.updateInlineMenuPositionTimeout = globalThis.setTimeout( * Handles rebuilding the sub frame offsets when the tab is repositioned or scrolled.
() => this.updateInlineMenuPositionAfterSubFrameRebuild(sender), * Will trigger a re-positioning of the inline menu list and button. Note that we
650, * do not trigger an update to sub frame data if the sender is the frame that has
); * the field currently focused. We trigger a re-calculation of the field's position
* and as a result, the sub frame offsets of that frame will be updated.
*
* @param sender - The sender of the message
*/
private async repositionInlineMenuForSubFrame(sender: chrome.runtime.MessageSender) {
if (sender.frameId === this.focusedFieldData?.frameId) {
return;
} }
if (this.updateInlineMenuPositionTimeout) {
clearTimeout(this.updateInlineMenuPositionTimeout);
}
await this.rebuildSubFrameOffsets(sender);
this.updateInlineMenuPositionTimeout = globalThis.setTimeout(
() => this.updateInlineMenuPositionAfterSubFrameRebuild(sender),
650,
);
} }
/** /**

View File

@ -787,7 +787,16 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
capture: true, capture: true,
}); });
globalThis.addEventListener(EVENTS.RESIZE, this.handleOverlayRepositionEvent); globalThis.addEventListener(EVENTS.RESIZE, this.handleOverlayRepositionEvent);
this.performanceObserver = new PerformanceObserver(() => this.rebuildSubFrameOffsets(0, false)); this.performanceObserver = new PerformanceObserver((list) => {
const entries: any = list.getEntries();
for (let index = 0; index < entries.length; index++) {
const entry = entries[index];
if (entry.sources?.length > 0) {
void this.sendExtensionMessage("updateSubFrameOffsetsForReflowEvent");
return;
}
}
});
this.performanceObserver.observe({ type: "layout-shift", buffered: true }); this.performanceObserver.observe({ type: "layout-shift", buffered: true });
} }
@ -811,7 +820,7 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
return; return;
} }
this.rebuildSubFrameOffsets(); this.repositionInlineMenuForSubFrame();
this.toggleInlineMenuHidden(true); this.toggleInlineMenuHidden(true);
this.clearUserInteractionEventTimeout(); this.clearUserInteractionEventTimeout();
this.userInteractionEventTimeout = globalThis.setTimeout( this.userInteractionEventTimeout = globalThis.setTimeout(
@ -823,14 +832,11 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
/** /**
* Triggers a rebuild of a sub frame's offsets within the tab. * Triggers a rebuild of a sub frame's offsets within the tab.
*/ */
private rebuildSubFrameOffsets(delay: number = 150, triggerInlineMenuPositionUpdate = true) { private repositionInlineMenuForSubFrame() {
this.clearRecalculateSubFrameOffsetsTimeout(); this.clearRecalculateSubFrameOffsetsTimeout();
this.recalculateSubFrameOffsetsTimeout = globalThis.setTimeout( this.recalculateSubFrameOffsetsTimeout = globalThis.setTimeout(
() => () => void this.sendExtensionMessage("repositionAutofillInlineMenuForSubFrame"),
void this.sendExtensionMessage("rebuildSubFrameOffsets", { 150,
triggerInlineMenuPositionUpdate,
}),
delay,
); );
} }