[PM-8207] Implementing a way of handling capturing the element at the center point of the inline menu

This commit is contained in:
Cesar Gonzalez 2024-06-13 09:53:49 -05:00
parent 5a19140619
commit 113db7a1c0
No known key found for this signature in database
GPG Key ID: 3381A5457F8CCECF
3 changed files with 105 additions and 12 deletions

View File

@ -38,6 +38,18 @@ export type FocusedFieldData = {
frameId?: number;
};
export type InlineMenuElementPosition = {
top: number;
left: number;
width: number;
height: number;
};
export type InlineMenuPosition = {
button?: InlineMenuElementPosition;
list?: InlineMenuElementPosition;
};
export type OverlayAddNewItemMessage = {
login?: {
uri?: string;
@ -114,6 +126,7 @@ export type OverlayBackgroundExtensionMessageHandlers = {
message,
sender,
}: BackgroundOnMessageHandlerParams) => Promise<void>;
getAutofillInlineMenuPosition: () => InlineMenuPosition;
toggleAutofillInlineMenuHidden: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
checkIsAutofillInlineMenuButtonVisible: ({ sender }: BackgroundSenderParam) => void;
checkIsAutofillInlineMenuListVisible: ({ sender }: BackgroundSenderParam) => void;

View File

@ -48,6 +48,7 @@ import {
SubFrameOffsetData,
SubFrameOffsetsForTab,
CloseInlineMenuMessage,
InlineMenuPosition,
} from "./abstractions/overlay.background";
export class OverlayBackground implements OverlayBackgroundInterface {
@ -63,6 +64,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
private inlineMenuCiphers: Map<string, CipherView> = new Map();
private inlineMenuPageTranslations: Record<string, string>;
private inlineMenuFadeInTimeout: number | NodeJS.Timeout;
private inlineMenuPosition: InlineMenuPosition = {};
private updateInlineMenuPositionTimeout: number | NodeJS.Timeout;
private delayedCloseTimeout: number | NodeJS.Timeout;
private focusedFieldData: FocusedFieldData;
@ -87,6 +89,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
focusAutofillInlineMenuList: () => this.focusInlineMenuList(),
updateAutofillInlineMenuPosition: ({ message, sender }) =>
this.updateInlineMenuPosition(message, sender),
getAutofillInlineMenuPosition: () => this.getInlineMenuPosition(),
toggleAutofillInlineMenuHidden: ({ message, sender }) =>
this.updateInlineMenuHidden(message, sender),
checkIsAutofillInlineMenuButtonVisible: ({ sender }) =>
@ -641,6 +644,13 @@ export class OverlayBackground implements OverlayBackgroundInterface {
});
}
/**
* Returns the position of the currently open inline menu.
*/
private getInlineMenuPosition(): InlineMenuPosition {
return this.inlineMenuPosition;
}
/**
* Handles updating the opacity of both the inline menu button and list.
* This is used to simultaneously fade in the inline menu elements.
@ -682,11 +692,18 @@ export class OverlayBackground implements OverlayBackgroundInterface {
? subFrameLeftOffset + left + width - height - (fieldPaddingRight - elementOffset + 2)
: subFrameLeftOffset + left + width - height + elementOffset / 2;
this.inlineMenuPosition.button = {
top: Math.round(elementTopPosition),
left: Math.round(elementLeftPosition),
height: Math.round(elementHeight),
width: Math.round(elementHeight),
};
return {
top: `${Math.round(elementTopPosition)}px`,
left: `${Math.round(elementLeftPosition)}px`,
height: `${Math.round(elementHeight)}px`,
width: `${Math.round(elementHeight)}px`,
top: `${this.inlineMenuPosition.button.top}px`,
left: `${this.inlineMenuPosition.button.left}px`,
height: `${this.inlineMenuPosition.button.height}px`,
width: `${this.inlineMenuPosition.button.width}px`,
};
}
@ -699,10 +716,18 @@ export class OverlayBackground implements OverlayBackgroundInterface {
const subFrameLeftOffset = subFrameOffsets?.left || 0;
const { top, left, width, height } = this.focusedFieldData.focusedFieldRects;
this.inlineMenuPosition.list = {
top: Math.round(top + height + subFrameTopOffset),
left: Math.round(left + subFrameLeftOffset),
height: 0,
width: Math.round(width),
};
return {
width: `${Math.round(width)}px`,
top: `${Math.round(top + height + subFrameTopOffset)}px`,
left: `${Math.round(left + subFrameLeftOffset)}px`,
width: `${this.inlineMenuPosition.list.width}px`,
top: `${this.inlineMenuPosition.list.top}px`,
left: `${this.inlineMenuPosition.list.left}px`,
};
}
@ -1110,6 +1135,11 @@ export class OverlayBackground implements OverlayBackgroundInterface {
* @param message - Contains the dimensions of the inline menu list
*/
private updateInlineMenuListHeight(message: OverlayBackgroundExtensionMessage) {
const parsedHeight = parseInt(message.styles?.height);
if (this.inlineMenuPosition.list && parsedHeight > 0) {
this.inlineMenuPosition.list.height = parsedHeight;
}
this.inlineMenuListPort?.postMessage({
command: "updateAutofillInlineMenuPosition",
styles: message.styles,
@ -1276,10 +1306,12 @@ export class OverlayBackground implements OverlayBackgroundInterface {
private handlePortOnDisconnect = (port: chrome.runtime.Port) => {
if (port.name === AutofillOverlayPort.List) {
this.inlineMenuListPort = null;
this.inlineMenuPosition.list = null;
}
if (port.name === AutofillOverlayPort.Button) {
this.inlineMenuButtonPort = null;
this.inlineMenuPosition.button = null;
}
};
}

View File

@ -1,3 +1,7 @@
import {
InlineMenuElementPosition,
InlineMenuPosition,
} from "../../../background/abstractions/overlay.background";
import { AutofillExtensionMessage } from "../../../content/abstractions/autofill-init";
import { AutofillOverlayElement } from "../../../enums/autofill-overlay.enum";
import {
@ -27,6 +31,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
private bodyElementMutationObserver: MutationObserver;
private mutationObserverIterations = 0;
private mutationObserverIterationsResetTimeout: number | NodeJS.Timeout;
private lastElementOverrides: WeakMap<Element, number> = new WeakMap();
private readonly customElementDefaultStyles: Partial<CSSStyleDeclaration> = {
all: "initial",
position: "fixed",
@ -386,11 +391,14 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
return;
}
if (!lastChildIsInlineMenuList && !lastChildIsInlineMenuButton) {
const lastChildZIndex = parseInt((lastChild as HTMLElement).style.zIndex);
if (lastChildZIndex >= 2147483647) {
(lastChild as HTMLElement).style.zIndex = "2147483646";
}
const lastChildEncounterCount = this.lastElementOverrides.get(lastChild) || 0;
if (!lastChildIsInlineMenuList && !lastChildIsInlineMenuButton && lastChildEncounterCount < 3) {
this.lastElementOverrides.set(lastChild, lastChildEncounterCount + 1);
}
if (this.lastElementOverrides.get(lastChild) >= 3) {
await this.handlePersistentLastChildOverride(lastChild);
return;
}
@ -412,6 +420,46 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
globalThis.document.body.insertBefore(lastChild, this.buttonElement);
};
/**
* Verifies if the last child of the body element is overlaying the inline menu elements.
* This is triggered when the last child of the body is being forced by some script to
* be an element other than the inline menu elements.
*
* @param lastChild - The last child of the body element.
*/
private async handlePersistentLastChildOverride(lastChild: Element) {
const lastChildZIndex = parseInt((lastChild as HTMLElement).style.zIndex);
if (lastChildZIndex >= 2147483647) {
(lastChild as HTMLElement).style.zIndex = "2147483646";
}
const inlineMenuPosition: InlineMenuPosition = await this.sendExtensionMessage(
"getAutofillInlineMenuPosition",
);
const { button, list } = inlineMenuPosition;
if (!!button && this.elementAtCenterOfInlineMenuPosition(button) === lastChild) {
this.closeInlineMenu();
return;
}
if (!!list && this.elementAtCenterOfInlineMenuPosition(list) === lastChild) {
this.closeInlineMenu();
}
}
/**
* Returns the element present at the center of the inline menu position.
*
* @param position - The position of the inline menu element.
*/
private elementAtCenterOfInlineMenuPosition(position: InlineMenuElementPosition): Element | null {
return globalThis.document.elementFromPoint(
position.left + position.width / 2,
position.top + position.height / 2,
);
}
/**
* Identifies if the mutation observer is triggering excessive iterations.
* Will trigger a blur of the most recently focused field and remove the