mirror of
https://github.com/bitwarden/browser
synced 2025-12-14 07:13:32 +00:00
[PM-10552] Values input between separate iframes are not populated when adding a cipher from the inline menu (#10401)
* [PM-10554] Inline menu glitches on single form fields that are wrapped within inline menu * [PM-10554] Adding jest tests to validate expected behavior * [PM-10554] Adding jest tests to validate expected behavior * [PM-10552] Vaules input between separate iframes are not populated in add-edit cipher popout * [PM-10552] Working through issues found when attempting to add ciphers within iframes that trigger a blur event * [PM-10552] Working through issues found when attempting to add ciphers within iframes that trigger a blur event * [PM-10552] Fixing broken jest tests due to implementation changes * [PM-10552] Implementing jest tests to validate behavior within OverlayBackground
This commit is contained in:
@@ -95,6 +95,10 @@ export type OverlayAddNewItemMessage = {
|
|||||||
identity?: NewIdentityCipherData;
|
identity?: NewIdentityCipherData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CurrentAddNewItemData = OverlayAddNewItemMessage & {
|
||||||
|
sender: chrome.runtime.MessageSender;
|
||||||
|
};
|
||||||
|
|
||||||
export type CloseInlineMenuMessage = {
|
export type CloseInlineMenuMessage = {
|
||||||
forceCloseInlineMenu?: boolean;
|
forceCloseInlineMenu?: boolean;
|
||||||
overlayElement?: string;
|
overlayElement?: string;
|
||||||
|
|||||||
@@ -176,8 +176,12 @@ describe("OverlayBackground", () => {
|
|||||||
parentFrameId: getFrameCounter,
|
parentFrameId: getFrameCounter,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
tabsSendMessageSpy = jest.spyOn(BrowserApi, "tabSendMessage");
|
tabsSendMessageSpy = jest
|
||||||
tabSendMessageDataSpy = jest.spyOn(BrowserApi, "tabSendMessageData");
|
.spyOn(BrowserApi, "tabSendMessage")
|
||||||
|
.mockImplementation(() => Promise.resolve());
|
||||||
|
tabSendMessageDataSpy = jest
|
||||||
|
.spyOn(BrowserApi, "tabSendMessageData")
|
||||||
|
.mockImplementation(() => Promise.resolve());
|
||||||
sendMessageSpy = jest.spyOn(BrowserApi, "sendMessage");
|
sendMessageSpy = jest.spyOn(BrowserApi, "sendMessage");
|
||||||
getTabFromCurrentWindowIdSpy = jest.spyOn(BrowserApi, "getTabFromCurrentWindowId");
|
getTabFromCurrentWindowIdSpy = jest.spyOn(BrowserApi, "getTabFromCurrentWindowId");
|
||||||
getTabSpy = jest.spyOn(BrowserApi, "getTab");
|
getTabSpy = jest.spyOn(BrowserApi, "getTab");
|
||||||
@@ -838,7 +842,7 @@ describe("OverlayBackground", () => {
|
|||||||
|
|
||||||
it("posts an `updateOverlayListCiphers` message to the overlay list port, and send a `updateAutofillInlineMenuListCiphers` message to the tab indicating that the list of ciphers is populated", async () => {
|
it("posts an `updateOverlayListCiphers` message to the overlay list port, and send a `updateAutofillInlineMenuListCiphers` message to the tab indicating that the list of ciphers is populated", async () => {
|
||||||
overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ tabId: tab.id });
|
overlayBackground["focusedFieldData"] = createFocusedFieldDataMock({ tabId: tab.id });
|
||||||
cipherService.getAllDecryptedForUrl.mockResolvedValue([cipher1, cipher2]);
|
cipherService.getAllDecryptedForUrl.mockResolvedValue([cipher1]);
|
||||||
cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1);
|
cipherService.sortCiphersByLastUsedThenName.mockReturnValue(-1);
|
||||||
getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab);
|
getTabFromCurrentWindowIdSpy.mockResolvedValueOnce(tab);
|
||||||
|
|
||||||
@@ -857,7 +861,7 @@ describe("OverlayBackground", () => {
|
|||||||
image: "https://icons.bitwarden.com//jest-testing-website.com/icon.png",
|
image: "https://icons.bitwarden.com//jest-testing-website.com/icon.png",
|
||||||
imageEnabled: true,
|
imageEnabled: true,
|
||||||
},
|
},
|
||||||
id: "inline-menu-cipher-1",
|
id: "inline-menu-cipher-0",
|
||||||
login: {
|
login: {
|
||||||
username: "username-1",
|
username: "username-1",
|
||||||
},
|
},
|
||||||
@@ -1119,10 +1123,12 @@ describe("OverlayBackground", () => {
|
|||||||
let openAddEditVaultItemPopoutSpy: jest.SpyInstance;
|
let openAddEditVaultItemPopoutSpy: jest.SpyInstance;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
jest.useFakeTimers();
|
||||||
sender = mock<chrome.runtime.MessageSender>({ tab: { id: 1 } });
|
sender = mock<chrome.runtime.MessageSender>({ tab: { id: 1 } });
|
||||||
openAddEditVaultItemPopoutSpy = jest
|
openAddEditVaultItemPopoutSpy = jest
|
||||||
.spyOn(overlayBackground as any, "openAddEditVaultItemPopout")
|
.spyOn(overlayBackground as any, "openAddEditVaultItemPopout")
|
||||||
.mockImplementation();
|
.mockImplementation();
|
||||||
|
overlayBackground["currentAddNewItemData"] = { sender, addNewCipherType: CipherType.Login };
|
||||||
});
|
});
|
||||||
|
|
||||||
it("will not open the add edit popout window if the message does not have a login cipher provided", () => {
|
it("will not open the add edit popout window if the message does not have a login cipher provided", () => {
|
||||||
@@ -1132,6 +1138,28 @@ describe("OverlayBackground", () => {
|
|||||||
expect(openAddEditVaultItemPopoutSpy).not.toHaveBeenCalled();
|
expect(openAddEditVaultItemPopoutSpy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("resets the currentAddNewItemData to null when a cipher view is not successfully created", async () => {
|
||||||
|
jest.spyOn(overlayBackground as any, "buildLoginCipherView").mockReturnValue(null);
|
||||||
|
|
||||||
|
sendMockExtensionMessage(
|
||||||
|
{
|
||||||
|
command: "autofillOverlayAddNewVaultItem",
|
||||||
|
addNewCipherType: CipherType.Login,
|
||||||
|
login: {
|
||||||
|
uri: "https://tacos.com",
|
||||||
|
hostname: "",
|
||||||
|
username: "username",
|
||||||
|
password: "password",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sender,
|
||||||
|
);
|
||||||
|
jest.advanceTimersByTime(100);
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
|
expect(overlayBackground["currentAddNewItemData"]).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
it("will open the add edit popout window after creating a new cipher", async () => {
|
it("will open the add edit popout window after creating a new cipher", async () => {
|
||||||
sendMockExtensionMessage(
|
sendMockExtensionMessage(
|
||||||
{
|
{
|
||||||
@@ -1146,6 +1174,7 @@ describe("OverlayBackground", () => {
|
|||||||
},
|
},
|
||||||
sender,
|
sender,
|
||||||
);
|
);
|
||||||
|
jest.advanceTimersByTime(100);
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
|
||||||
expect(cipherService.setAddEditCipherInfo).toHaveBeenCalled();
|
expect(cipherService.setAddEditCipherInfo).toHaveBeenCalled();
|
||||||
@@ -1154,6 +1183,8 @@ describe("OverlayBackground", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("creates a new card cipher", async () => {
|
it("creates a new card cipher", async () => {
|
||||||
|
overlayBackground["currentAddNewItemData"].addNewCipherType = CipherType.Card;
|
||||||
|
|
||||||
sendMockExtensionMessage(
|
sendMockExtensionMessage(
|
||||||
{
|
{
|
||||||
command: "autofillOverlayAddNewVaultItem",
|
command: "autofillOverlayAddNewVaultItem",
|
||||||
@@ -1169,6 +1200,7 @@ describe("OverlayBackground", () => {
|
|||||||
},
|
},
|
||||||
sender,
|
sender,
|
||||||
);
|
);
|
||||||
|
jest.advanceTimersByTime(100);
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
|
||||||
expect(cipherService.setAddEditCipherInfo).toHaveBeenCalled();
|
expect(cipherService.setAddEditCipherInfo).toHaveBeenCalled();
|
||||||
@@ -1177,6 +1209,10 @@ describe("OverlayBackground", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("creating a new identity cipher", () => {
|
describe("creating a new identity cipher", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
overlayBackground["currentAddNewItemData"].addNewCipherType = CipherType.Identity;
|
||||||
|
});
|
||||||
|
|
||||||
it("populates an identity cipher view and creates it", async () => {
|
it("populates an identity cipher view and creates it", async () => {
|
||||||
sendMockExtensionMessage(
|
sendMockExtensionMessage(
|
||||||
{
|
{
|
||||||
@@ -1203,6 +1239,7 @@ describe("OverlayBackground", () => {
|
|||||||
},
|
},
|
||||||
sender,
|
sender,
|
||||||
);
|
);
|
||||||
|
jest.advanceTimersByTime(100);
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
|
||||||
expect(cipherService.setAddEditCipherInfo).toHaveBeenCalled();
|
expect(cipherService.setAddEditCipherInfo).toHaveBeenCalled();
|
||||||
@@ -1223,6 +1260,7 @@ describe("OverlayBackground", () => {
|
|||||||
},
|
},
|
||||||
sender,
|
sender,
|
||||||
);
|
);
|
||||||
|
jest.advanceTimersByTime(100);
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
|
||||||
expect(cipherService.setAddEditCipherInfo).toHaveBeenCalled();
|
expect(cipherService.setAddEditCipherInfo).toHaveBeenCalled();
|
||||||
@@ -1241,6 +1279,7 @@ describe("OverlayBackground", () => {
|
|||||||
},
|
},
|
||||||
sender,
|
sender,
|
||||||
);
|
);
|
||||||
|
jest.advanceTimersByTime(100);
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
|
||||||
expect(cipherService.setAddEditCipherInfo).toHaveBeenCalled();
|
expect(cipherService.setAddEditCipherInfo).toHaveBeenCalled();
|
||||||
@@ -1259,11 +1298,173 @@ describe("OverlayBackground", () => {
|
|||||||
},
|
},
|
||||||
sender,
|
sender,
|
||||||
);
|
);
|
||||||
|
jest.advanceTimersByTime(100);
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
|
||||||
expect(cipherService.setAddEditCipherInfo).toHaveBeenCalled();
|
expect(cipherService.setAddEditCipherInfo).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("pulling cipher data from multiple frames of a tab", () => {
|
||||||
|
let subFrameSender: MockProxy<chrome.runtime.MessageSender>;
|
||||||
|
const command = "autofillOverlayAddNewVaultItem";
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
subFrameSender = mock<chrome.runtime.MessageSender>({ tab: sender.tab, frameId: 2 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("combines the login cipher data from all frames", async () => {
|
||||||
|
const buildLoginCipherViewSpy = jest.spyOn(
|
||||||
|
overlayBackground as any,
|
||||||
|
"buildLoginCipherView",
|
||||||
|
);
|
||||||
|
const addNewCipherType = CipherType.Login;
|
||||||
|
const loginCipherData = {
|
||||||
|
uri: "https://tacos.com",
|
||||||
|
hostname: "",
|
||||||
|
username: "username",
|
||||||
|
password: "",
|
||||||
|
};
|
||||||
|
const subFrameLoginCipherData = {
|
||||||
|
uri: "https://tacos.com",
|
||||||
|
hostname: "tacos.com",
|
||||||
|
username: "",
|
||||||
|
password: "password",
|
||||||
|
};
|
||||||
|
|
||||||
|
sendMockExtensionMessage({ command, addNewCipherType, login: loginCipherData }, sender);
|
||||||
|
sendMockExtensionMessage(
|
||||||
|
{ command, addNewCipherType, login: subFrameLoginCipherData },
|
||||||
|
subFrameSender,
|
||||||
|
);
|
||||||
|
jest.advanceTimersByTime(100);
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
|
expect(buildLoginCipherViewSpy).toHaveBeenCalledWith({
|
||||||
|
uri: "https://tacos.com",
|
||||||
|
hostname: "tacos.com",
|
||||||
|
username: "username",
|
||||||
|
password: "password",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("combines the card cipher data from all frames", async () => {
|
||||||
|
const buildCardCipherViewSpy = jest.spyOn(
|
||||||
|
overlayBackground as any,
|
||||||
|
"buildCardCipherView",
|
||||||
|
);
|
||||||
|
overlayBackground["currentAddNewItemData"].addNewCipherType = CipherType.Card;
|
||||||
|
const addNewCipherType = CipherType.Card;
|
||||||
|
const cardCipherData = {
|
||||||
|
cardholderName: "cardholderName",
|
||||||
|
number: "",
|
||||||
|
expirationMonth: "",
|
||||||
|
expirationYear: "",
|
||||||
|
expirationDate: "12/25",
|
||||||
|
cvv: "123",
|
||||||
|
};
|
||||||
|
const subFrameCardCipherData = {
|
||||||
|
cardholderName: "",
|
||||||
|
number: "4242424242424242",
|
||||||
|
expirationMonth: "12",
|
||||||
|
expirationYear: "2025",
|
||||||
|
expirationDate: "",
|
||||||
|
cvv: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
sendMockExtensionMessage({ command, addNewCipherType, card: cardCipherData }, sender);
|
||||||
|
sendMockExtensionMessage(
|
||||||
|
{ command, addNewCipherType, card: subFrameCardCipherData },
|
||||||
|
subFrameSender,
|
||||||
|
);
|
||||||
|
jest.advanceTimersByTime(100);
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
|
expect(buildCardCipherViewSpy).toHaveBeenCalledWith({
|
||||||
|
cardholderName: "cardholderName",
|
||||||
|
number: "4242424242424242",
|
||||||
|
expirationMonth: "12",
|
||||||
|
expirationYear: "2025",
|
||||||
|
expirationDate: "12/25",
|
||||||
|
cvv: "123",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("combines the identity cipher data from all frames", async () => {
|
||||||
|
const buildIdentityCipherViewSpy = jest.spyOn(
|
||||||
|
overlayBackground as any,
|
||||||
|
"buildIdentityCipherView",
|
||||||
|
);
|
||||||
|
overlayBackground["currentAddNewItemData"].addNewCipherType = CipherType.Identity;
|
||||||
|
const addNewCipherType = CipherType.Identity;
|
||||||
|
const identityCipherData = {
|
||||||
|
title: "title",
|
||||||
|
firstName: "firstName",
|
||||||
|
middleName: "middleName",
|
||||||
|
lastName: "",
|
||||||
|
fullName: "",
|
||||||
|
address1: "address1",
|
||||||
|
address2: "address2",
|
||||||
|
address3: "address3",
|
||||||
|
city: "city",
|
||||||
|
state: "state",
|
||||||
|
postalCode: "postalCode",
|
||||||
|
country: "country",
|
||||||
|
company: "company",
|
||||||
|
phone: "phone",
|
||||||
|
email: "email",
|
||||||
|
username: "username",
|
||||||
|
};
|
||||||
|
const subFrameIdentityCipherData = {
|
||||||
|
title: "",
|
||||||
|
firstName: "",
|
||||||
|
middleName: "",
|
||||||
|
lastName: "lastName",
|
||||||
|
fullName: "fullName",
|
||||||
|
address1: "",
|
||||||
|
address2: "",
|
||||||
|
address3: "",
|
||||||
|
city: "",
|
||||||
|
state: "",
|
||||||
|
postalCode: "",
|
||||||
|
country: "",
|
||||||
|
company: "",
|
||||||
|
phone: "",
|
||||||
|
email: "",
|
||||||
|
username: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
sendMockExtensionMessage(
|
||||||
|
{ command, addNewCipherType, identity: identityCipherData },
|
||||||
|
sender,
|
||||||
|
);
|
||||||
|
sendMockExtensionMessage(
|
||||||
|
{ command, addNewCipherType, identity: subFrameIdentityCipherData },
|
||||||
|
subFrameSender,
|
||||||
|
);
|
||||||
|
jest.advanceTimersByTime(100);
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
|
expect(buildIdentityCipherViewSpy).toHaveBeenCalledWith({
|
||||||
|
title: "title",
|
||||||
|
firstName: "firstName",
|
||||||
|
middleName: "middleName",
|
||||||
|
lastName: "lastName",
|
||||||
|
fullName: "fullName",
|
||||||
|
address1: "address1",
|
||||||
|
address2: "address2",
|
||||||
|
address3: "address3",
|
||||||
|
city: "city",
|
||||||
|
state: "state",
|
||||||
|
postalCode: "postalCode",
|
||||||
|
country: "country",
|
||||||
|
company: "company",
|
||||||
|
phone: "phone",
|
||||||
|
email: "email",
|
||||||
|
username: "username",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("checkIsInlineMenuCiphersPopulated message handler", () => {
|
describe("checkIsInlineMenuCiphersPopulated message handler", () => {
|
||||||
@@ -1363,6 +1564,51 @@ describe("OverlayBackground", () => {
|
|||||||
showInlineMenuAccountCreation: true,
|
showInlineMenuAccountCreation: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("triggers an update of the inline menu ciphers when the new focused field's cipher type does not equal the previous focused field's cipher type", async () => {
|
||||||
|
const updateOverlayCiphersSpy = jest.spyOn(overlayBackground, "updateOverlayCiphers");
|
||||||
|
const tab = createChromeTabMock({ id: 2 });
|
||||||
|
const sender = mock<chrome.runtime.MessageSender>({ tab, frameId: 100 });
|
||||||
|
const focusedFieldData = createFocusedFieldDataMock({
|
||||||
|
tabId: tab.id,
|
||||||
|
frameId: sender.frameId,
|
||||||
|
filledByCipherType: CipherType.Login,
|
||||||
|
});
|
||||||
|
sendMockExtensionMessage({ command: "updateFocusedFieldData", focusedFieldData }, sender);
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
|
const newFocusedFieldData = createFocusedFieldDataMock({
|
||||||
|
tabId: tab.id,
|
||||||
|
frameId: sender.frameId,
|
||||||
|
filledByCipherType: CipherType.Card,
|
||||||
|
});
|
||||||
|
sendMockExtensionMessage(
|
||||||
|
{ command: "updateFocusedFieldData", focusedFieldData: newFocusedFieldData },
|
||||||
|
sender,
|
||||||
|
);
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
|
expect(updateOverlayCiphersSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("updateIsFieldCurrentlyFocused message handler", () => {
|
||||||
|
it("skips updating the isFiledCurrentlyFocused value when the focused field data is populated and the sender frame id does not equal the focused field's frame id", async () => {
|
||||||
|
const focusedFieldData = createFocusedFieldDataMock();
|
||||||
|
sendMockExtensionMessage(
|
||||||
|
{ command: "updateFocusedFieldData", focusedFieldData },
|
||||||
|
mock<chrome.runtime.MessageSender>({ tab: { id: 1 }, frameId: 10 }),
|
||||||
|
);
|
||||||
|
overlayBackground["isFieldCurrentlyFocused"] = true;
|
||||||
|
|
||||||
|
sendMockExtensionMessage(
|
||||||
|
{ command: "updateIsFieldCurrentlyFocused", isFieldCurrentlyFocused: false },
|
||||||
|
mock<chrome.runtime.MessageSender>({ tab: { id: 1 }, frameId: 20 }),
|
||||||
|
);
|
||||||
|
await flushPromises();
|
||||||
|
|
||||||
|
expect(overlayBackground["isFieldCurrentlyFocused"]).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("updateIsFieldCurrentlyFocused message handler", () => {
|
describe("updateIsFieldCurrentlyFocused message handler", () => {
|
||||||
@@ -1841,7 +2087,6 @@ describe("OverlayBackground", () => {
|
|||||||
overlayBackground["subFrameOffsetsForTab"][focusedFieldData.tabId] = new Map([
|
overlayBackground["subFrameOffsetsForTab"][focusedFieldData.tabId] = new Map([
|
||||||
[focusedFieldData.frameId, null],
|
[focusedFieldData.frameId, null],
|
||||||
]);
|
]);
|
||||||
tabsSendMessageSpy.mockImplementation();
|
|
||||||
jest.spyOn(overlayBackground as any, "updateInlineMenuPositionAfterRepositionEvent");
|
jest.spyOn(overlayBackground as any, "updateInlineMenuPositionAfterRepositionEvent");
|
||||||
|
|
||||||
sendMockExtensionMessage(
|
sendMockExtensionMessage(
|
||||||
@@ -2090,7 +2335,6 @@ describe("OverlayBackground", () => {
|
|||||||
describe("autofillInlineMenuButtonClicked message handler", () => {
|
describe("autofillInlineMenuButtonClicked message handler", () => {
|
||||||
it("opens the unlock vault popout if the user auth status is not unlocked", async () => {
|
it("opens the unlock vault popout if the user auth status is not unlocked", async () => {
|
||||||
activeAccountStatusMock$.next(AuthenticationStatus.Locked);
|
activeAccountStatusMock$.next(AuthenticationStatus.Locked);
|
||||||
tabsSendMessageSpy.mockImplementation();
|
|
||||||
|
|
||||||
sendPortMessage(buttonMessageConnectorSpy, {
|
sendPortMessage(buttonMessageConnectorSpy, {
|
||||||
command: "autofillInlineMenuButtonClicked",
|
command: "autofillInlineMenuButtonClicked",
|
||||||
@@ -2291,7 +2535,6 @@ describe("OverlayBackground", () => {
|
|||||||
describe("unlockVault message handler", () => {
|
describe("unlockVault message handler", () => {
|
||||||
it("opens the unlock vault popout", async () => {
|
it("opens the unlock vault popout", async () => {
|
||||||
activeAccountStatusMock$.next(AuthenticationStatus.Locked);
|
activeAccountStatusMock$.next(AuthenticationStatus.Locked);
|
||||||
tabsSendMessageSpy.mockImplementation();
|
|
||||||
|
|
||||||
sendPortMessage(listMessageConnectorSpy, { command: "unlockVault", portKey });
|
sendPortMessage(listMessageConnectorSpy, { command: "unlockVault", portKey });
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
@@ -2443,11 +2686,10 @@ describe("OverlayBackground", () => {
|
|||||||
});
|
});
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
|
||||||
expect(tabsSendMessageSpy).toHaveBeenCalledWith(
|
expect(tabsSendMessageSpy).toHaveBeenCalledWith(sender.tab, {
|
||||||
sender.tab,
|
command: "addNewVaultItemFromOverlay",
|
||||||
{ command: "addNewVaultItemFromOverlay", addNewCipherType: CipherType.Login },
|
addNewCipherType: CipherType.Login,
|
||||||
{ frameId: sender.frameId },
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import { generateRandomChars } from "../utils";
|
|||||||
import { LockedVaultPendingNotificationsData } from "./abstractions/notification.background";
|
import { LockedVaultPendingNotificationsData } from "./abstractions/notification.background";
|
||||||
import {
|
import {
|
||||||
CloseInlineMenuMessage,
|
CloseInlineMenuMessage,
|
||||||
|
CurrentAddNewItemData,
|
||||||
FocusedFieldData,
|
FocusedFieldData,
|
||||||
InlineMenuButtonPortMessageHandlers,
|
InlineMenuButtonPortMessageHandlers,
|
||||||
InlineMenuCipherData,
|
InlineMenuCipherData,
|
||||||
@@ -83,6 +84,8 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
private cancelUpdateInlineMenuPositionSubject = new Subject<void>();
|
private cancelUpdateInlineMenuPositionSubject = new Subject<void>();
|
||||||
private repositionInlineMenuSubject = new Subject<chrome.runtime.MessageSender>();
|
private repositionInlineMenuSubject = new Subject<chrome.runtime.MessageSender>();
|
||||||
private rebuildSubFrameOffsetsSubject = new Subject<chrome.runtime.MessageSender>();
|
private rebuildSubFrameOffsetsSubject = new Subject<chrome.runtime.MessageSender>();
|
||||||
|
private addNewVaultItemSubject = new Subject<CurrentAddNewItemData>();
|
||||||
|
private currentAddNewItemData: CurrentAddNewItemData;
|
||||||
private focusedFieldData: FocusedFieldData;
|
private focusedFieldData: FocusedFieldData;
|
||||||
private isFieldCurrentlyFocused: boolean = false;
|
private isFieldCurrentlyFocused: boolean = false;
|
||||||
private isFieldCurrentlyFilling: boolean = false;
|
private isFieldCurrentlyFilling: boolean = false;
|
||||||
@@ -187,6 +190,14 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
switchMap((sender) => this.rebuildSubFrameOffsets(sender)),
|
switchMap((sender) => this.rebuildSubFrameOffsets(sender)),
|
||||||
)
|
)
|
||||||
.subscribe();
|
.subscribe();
|
||||||
|
this.addNewVaultItemSubject
|
||||||
|
.pipe(
|
||||||
|
debounceTime(100),
|
||||||
|
switchMap((addNewItemData) =>
|
||||||
|
this.buildCipherAndOpenAddEditVaultItemPopout(addNewItemData),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
|
||||||
// Debounce used to update inline menu position
|
// Debounce used to update inline menu position
|
||||||
merge(
|
merge(
|
||||||
@@ -231,14 +242,14 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
const authStatus = await firstValueFrom(this.authService.activeAccountStatus$);
|
const authStatus = await firstValueFrom(this.authService.activeAccountStatus$);
|
||||||
if (authStatus !== AuthenticationStatus.Unlocked) {
|
if (authStatus !== AuthenticationStatus.Unlocked) {
|
||||||
if (this.focusedFieldData) {
|
if (this.focusedFieldData) {
|
||||||
void this.closeInlineMenuAfterCiphersUpdate();
|
this.closeInlineMenuAfterCiphersUpdate().catch((error) => this.logService.error(error));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentTab = await BrowserApi.getTabFromCurrentWindowId();
|
const currentTab = await BrowserApi.getTabFromCurrentWindowId();
|
||||||
if (this.focusedFieldData && currentTab?.id !== this.focusedFieldData.tabId) {
|
if (this.focusedFieldData && currentTab?.id !== this.focusedFieldData.tabId) {
|
||||||
void this.closeInlineMenuAfterCiphersUpdate();
|
this.closeInlineMenuAfterCiphersUpdate().catch((error) => this.logService.error(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.inlineMenuCiphers = new Map();
|
this.inlineMenuCiphers = new Map();
|
||||||
@@ -319,7 +330,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
private async getInlineMenuCipherData(): Promise<InlineMenuCipherData[]> {
|
private async getInlineMenuCipherData(): Promise<InlineMenuCipherData[]> {
|
||||||
const showFavicons = await firstValueFrom(this.domainSettingsService.showFavicons$);
|
const showFavicons = await firstValueFrom(this.domainSettingsService.showFavicons$);
|
||||||
const inlineMenuCiphersArray = Array.from(this.inlineMenuCiphers);
|
const inlineMenuCiphersArray = Array.from(this.inlineMenuCiphers);
|
||||||
let inlineMenuCipherData: InlineMenuCipherData[] = [];
|
let inlineMenuCipherData: InlineMenuCipherData[];
|
||||||
|
|
||||||
if (this.showInlineMenuAccountCreation()) {
|
if (this.showInlineMenuAccountCreation()) {
|
||||||
inlineMenuCipherData = this.buildInlineMenuAccountCreationCiphers(
|
inlineMenuCipherData = this.buildInlineMenuAccountCreationCiphers(
|
||||||
@@ -527,10 +538,14 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (pageDetails.frameId !== 0 && pageDetails.details.fields.length) {
|
if (pageDetails.frameId !== 0 && pageDetails.details.fields.length) {
|
||||||
void this.buildSubFrameOffsets(pageDetails.tab, pageDetails.frameId, pageDetails.details.url);
|
this.buildSubFrameOffsets(
|
||||||
void BrowserApi.tabSendMessage(pageDetails.tab, {
|
pageDetails.tab,
|
||||||
|
pageDetails.frameId,
|
||||||
|
pageDetails.details.url,
|
||||||
|
).catch((error) => this.logService.error(error));
|
||||||
|
BrowserApi.tabSendMessage(pageDetails.tab, {
|
||||||
command: "setupRebuildSubFrameOffsetsListeners",
|
command: "setupRebuildSubFrameOffsetsListeners",
|
||||||
});
|
}).catch((error) => this.logService.error(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
const pageDetailsMap = this.pageDetailsForTab[sender.tab.id];
|
const pageDetailsMap = this.pageDetailsForTab[sender.tab.id];
|
||||||
@@ -620,11 +635,11 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
|
|
||||||
if (!subFrameOffset) {
|
if (!subFrameOffset) {
|
||||||
subFrameOffsetsForTab.set(frameId, null);
|
subFrameOffsetsForTab.set(frameId, null);
|
||||||
void BrowserApi.tabSendMessage(
|
BrowserApi.tabSendMessage(
|
||||||
tab,
|
tab,
|
||||||
{ command: "getSubFrameOffsetsFromWindowMessage", subFrameId: frameId },
|
{ command: "getSubFrameOffsetsFromWindowMessage", subFrameId: frameId },
|
||||||
{ frameId },
|
{ frameId },
|
||||||
);
|
).catch((error) => this.logService.error(error));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,11 +671,11 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
frameId,
|
frameId,
|
||||||
);
|
);
|
||||||
|
|
||||||
void BrowserApi.tabSendMessage(
|
BrowserApi.tabSendMessage(
|
||||||
tab,
|
tab,
|
||||||
{ command: "destroyAutofillInlineMenuListeners" },
|
{ command: "destroyAutofillInlineMenuListeners" },
|
||||||
{ frameId },
|
{ frameId },
|
||||||
);
|
).catch((error) => this.logService.error(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -696,13 +711,15 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.checkIsInlineMenuButtonVisible()) {
|
if (!this.checkIsInlineMenuButtonVisible()) {
|
||||||
void this.toggleInlineMenuHidden(
|
this.toggleInlineMenuHidden(
|
||||||
{ isInlineMenuHidden: false, setTransparentInlineMenu: true },
|
{ isInlineMenuHidden: false, setTransparentInlineMenu: true },
|
||||||
sender,
|
sender,
|
||||||
);
|
).catch((error) => this.logService.error(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
void this.updateInlineMenuPosition({ overlayElement: AutofillOverlayElement.Button }, sender);
|
this.updateInlineMenuPosition({ overlayElement: AutofillOverlayElement.Button }, sender).catch(
|
||||||
|
(error) => this.logService.error(error),
|
||||||
|
);
|
||||||
|
|
||||||
const mostRecentlyFocusedFieldHasValue = await BrowserApi.tabSendMessage(
|
const mostRecentlyFocusedFieldHasValue = await BrowserApi.tabSendMessage(
|
||||||
sender.tab,
|
sender.tab,
|
||||||
@@ -722,7 +739,9 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void this.updateInlineMenuPosition({ overlayElement: AutofillOverlayElement.List }, sender);
|
this.updateInlineMenuPosition({ overlayElement: AutofillOverlayElement.List }, sender).catch(
|
||||||
|
(error) => this.logService.error(error),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -807,7 +826,9 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
const command = "closeAutofillInlineMenu";
|
const command = "closeAutofillInlineMenu";
|
||||||
const sendOptions = { frameId: 0 };
|
const sendOptions = { frameId: 0 };
|
||||||
if (forceCloseInlineMenu) {
|
if (forceCloseInlineMenu) {
|
||||||
void BrowserApi.tabSendMessage(sender.tab, { command, overlayElement }, sendOptions);
|
BrowserApi.tabSendMessage(sender.tab, { command, overlayElement }, sendOptions).catch(
|
||||||
|
(error) => this.logService.error(error),
|
||||||
|
);
|
||||||
this.isInlineMenuButtonVisible = false;
|
this.isInlineMenuButtonVisible = false;
|
||||||
this.isInlineMenuListVisible = false;
|
this.isInlineMenuListVisible = false;
|
||||||
return;
|
return;
|
||||||
@@ -818,11 +839,11 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.isFieldCurrentlyFilling) {
|
if (this.isFieldCurrentlyFilling) {
|
||||||
void BrowserApi.tabSendMessage(
|
BrowserApi.tabSendMessage(
|
||||||
sender.tab,
|
sender.tab,
|
||||||
{ command, overlayElement: AutofillOverlayElement.List },
|
{ command, overlayElement: AutofillOverlayElement.List },
|
||||||
sendOptions,
|
sendOptions,
|
||||||
);
|
).catch((error) => this.logService.error(error));
|
||||||
this.isInlineMenuListVisible = false;
|
this.isInlineMenuListVisible = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -840,7 +861,9 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
this.isInlineMenuListVisible = false;
|
this.isInlineMenuListVisible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserApi.tabSendMessage(sender.tab, { command, overlayElement }, sendOptions);
|
BrowserApi.tabSendMessage(sender.tab, { command, overlayElement }, sendOptions).catch((error) =>
|
||||||
|
this.logService.error(error),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1092,11 +1115,11 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
sender: chrome.runtime.MessageSender,
|
sender: chrome.runtime.MessageSender,
|
||||||
) {
|
) {
|
||||||
if (this.focusedFieldData && !this.senderFrameHasFocusedField(sender)) {
|
if (this.focusedFieldData && !this.senderFrameHasFocusedField(sender)) {
|
||||||
void BrowserApi.tabSendMessage(
|
BrowserApi.tabSendMessage(
|
||||||
sender.tab,
|
sender.tab,
|
||||||
{ command: "unsetMostRecentlyFocusedField" },
|
{ command: "unsetMostRecentlyFocusedField" },
|
||||||
{ frameId: this.focusedFieldData.frameId },
|
{ frameId: this.focusedFieldData.frameId },
|
||||||
);
|
).catch((error) => this.logService.error(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
const previousFocusedFieldData = this.focusedFieldData;
|
const previousFocusedFieldData = this.focusedFieldData;
|
||||||
@@ -1108,7 +1131,17 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
!this.focusedFieldData.showInlineMenuAccountCreation;
|
!this.focusedFieldData.showInlineMenuAccountCreation;
|
||||||
|
|
||||||
if (accountCreationFieldBlurred || this.showInlineMenuAccountCreation()) {
|
if (accountCreationFieldBlurred || this.showInlineMenuAccountCreation()) {
|
||||||
void this.updateIdentityCiphersOnLoginField(previousFocusedFieldData);
|
this.updateIdentityCiphersOnLoginField(previousFocusedFieldData).catch((error) =>
|
||||||
|
this.logService.error(error),
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previousFocusedFieldData?.filledByCipherType !== focusedFieldData?.filledByCipherType) {
|
||||||
|
const updateAllCipherTypes = focusedFieldData.filledByCipherType !== CipherType.Login;
|
||||||
|
this.updateOverlayCiphers(updateAllCipherTypes).catch((error) =>
|
||||||
|
this.logService.error(error),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1355,9 +1388,9 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserApi.tabSendMessageData(sender.tab, "redirectAutofillInlineMenuFocusOut", {
|
BrowserApi.tabSendMessageData(sender.tab, "redirectAutofillInlineMenuFocusOut", {
|
||||||
direction,
|
direction,
|
||||||
});
|
}).catch((error) => this.logService.error(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1375,13 +1408,11 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserApi.tabSendMessage(
|
this.currentAddNewItemData = { addNewCipherType, sender };
|
||||||
sender.tab,
|
BrowserApi.tabSendMessage(sender.tab, {
|
||||||
{ command: "addNewVaultItemFromOverlay", addNewCipherType },
|
command: "addNewVaultItemFromOverlay",
|
||||||
{
|
addNewCipherType,
|
||||||
frameId: this.focusedFieldData.frameId || 0,
|
}).catch((error) => this.logService.error(error));
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1398,18 +1429,154 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
{ addNewCipherType, login, card, identity }: OverlayAddNewItemMessage,
|
{ addNewCipherType, login, card, identity }: OverlayAddNewItemMessage,
|
||||||
sender: chrome.runtime.MessageSender,
|
sender: chrome.runtime.MessageSender,
|
||||||
) {
|
) {
|
||||||
if (!addNewCipherType) {
|
if (
|
||||||
|
!this.currentAddNewItemData ||
|
||||||
|
sender.tab.id !== this.currentAddNewItemData.sender.tab.id ||
|
||||||
|
!addNewCipherType ||
|
||||||
|
this.currentAddNewItemData.addNewCipherType !== addNewCipherType
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (login && this.isAddingNewLogin()) {
|
||||||
|
this.updateCurrentAddNewItemLogin(login);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (card && this.isAddingNewCard()) {
|
||||||
|
this.updateCurrentAddNewItemCard(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (identity && this.isAddingNewIdentity()) {
|
||||||
|
this.updateCurrentAddNewItemIdentity(identity);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addNewVaultItemSubject.next(this.currentAddNewItemData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifies if the current add new item data is for adding a new login.
|
||||||
|
*/
|
||||||
|
private isAddingNewLogin() {
|
||||||
|
return this.currentAddNewItemData.addNewCipherType === CipherType.Login;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifies if the current add new item data is for adding a new card.
|
||||||
|
*/
|
||||||
|
private isAddingNewCard() {
|
||||||
|
return this.currentAddNewItemData.addNewCipherType === CipherType.Card;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifies if the current add new item data is for adding a new identity.
|
||||||
|
*/
|
||||||
|
private isAddingNewIdentity() {
|
||||||
|
return this.currentAddNewItemData.addNewCipherType === CipherType.Identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the current add new item data with the provided login data. If the
|
||||||
|
* login data is already present, the data will be merged with the existing data.
|
||||||
|
*
|
||||||
|
* @param login - The login data captured from the extension message
|
||||||
|
*/
|
||||||
|
private updateCurrentAddNewItemLogin(login: NewLoginCipherData) {
|
||||||
|
if (!this.currentAddNewItemData.login) {
|
||||||
|
this.currentAddNewItemData.login = login;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentLoginData = this.currentAddNewItemData.login;
|
||||||
|
this.currentAddNewItemData.login = {
|
||||||
|
uri: login.uri || currentLoginData.uri,
|
||||||
|
hostname: login.hostname || currentLoginData.hostname,
|
||||||
|
username: login.username || currentLoginData.username,
|
||||||
|
password: login.password || currentLoginData.password,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the current add new item data with the provided card data. If the
|
||||||
|
* card data is already present, the data will be merged with the existing data.
|
||||||
|
*
|
||||||
|
* @param card - The card data captured from the extension message
|
||||||
|
*/
|
||||||
|
private updateCurrentAddNewItemCard(card: NewCardCipherData) {
|
||||||
|
if (!this.currentAddNewItemData.card) {
|
||||||
|
this.currentAddNewItemData.card = card;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentCardData = this.currentAddNewItemData.card;
|
||||||
|
this.currentAddNewItemData.card = {
|
||||||
|
cardholderName: card.cardholderName || currentCardData.cardholderName,
|
||||||
|
number: card.number || currentCardData.number,
|
||||||
|
expirationMonth: card.expirationMonth || currentCardData.expirationMonth,
|
||||||
|
expirationYear: card.expirationYear || currentCardData.expirationYear,
|
||||||
|
expirationDate: card.expirationDate || currentCardData.expirationDate,
|
||||||
|
cvv: card.cvv || currentCardData.cvv,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the current add new item data with the provided identity data. If the
|
||||||
|
* identity data is already present, the data will be merged with the existing data.
|
||||||
|
*
|
||||||
|
* @param identity - The identity data captured from the extension message
|
||||||
|
*/
|
||||||
|
private updateCurrentAddNewItemIdentity(identity: NewIdentityCipherData) {
|
||||||
|
if (!this.currentAddNewItemData.identity) {
|
||||||
|
this.currentAddNewItemData.identity = identity;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentIdentityData = this.currentAddNewItemData.identity;
|
||||||
|
this.currentAddNewItemData.identity = {
|
||||||
|
title: identity.title || currentIdentityData.title,
|
||||||
|
firstName: identity.firstName || currentIdentityData.firstName,
|
||||||
|
middleName: identity.middleName || currentIdentityData.middleName,
|
||||||
|
lastName: identity.lastName || currentIdentityData.lastName,
|
||||||
|
fullName: identity.fullName || currentIdentityData.fullName,
|
||||||
|
address1: identity.address1 || currentIdentityData.address1,
|
||||||
|
address2: identity.address2 || currentIdentityData.address2,
|
||||||
|
address3: identity.address3 || currentIdentityData.address3,
|
||||||
|
city: identity.city || currentIdentityData.city,
|
||||||
|
state: identity.state || currentIdentityData.state,
|
||||||
|
postalCode: identity.postalCode || currentIdentityData.postalCode,
|
||||||
|
country: identity.country || currentIdentityData.country,
|
||||||
|
company: identity.company || currentIdentityData.company,
|
||||||
|
phone: identity.phone || currentIdentityData.phone,
|
||||||
|
email: identity.email || currentIdentityData.email,
|
||||||
|
username: identity.username || currentIdentityData.username,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles building a new cipher and opening the add/edit vault item popout.
|
||||||
|
*
|
||||||
|
* @param login - The login data captured from the extension message
|
||||||
|
* @param card - The card data captured from the extension message
|
||||||
|
* @param identity - The identity data captured from the extension message
|
||||||
|
* @param sender - The sender of the extension message
|
||||||
|
*/
|
||||||
|
private async buildCipherAndOpenAddEditVaultItemPopout({
|
||||||
|
login,
|
||||||
|
card,
|
||||||
|
identity,
|
||||||
|
sender,
|
||||||
|
}: CurrentAddNewItemData) {
|
||||||
const cipherView: CipherView = this.buildNewVaultItemCipherView({
|
const cipherView: CipherView = this.buildNewVaultItemCipherView({
|
||||||
addNewCipherType,
|
|
||||||
login,
|
login,
|
||||||
card,
|
card,
|
||||||
identity,
|
identity,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (cipherView) {
|
if (!cipherView) {
|
||||||
|
this.currentAddNewItemData = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
this.closeInlineMenu(sender);
|
this.closeInlineMenu(sender);
|
||||||
await this.cipherService.setAddEditCipherInfo({
|
await this.cipherService.setAddEditCipherInfo({
|
||||||
cipher: cipherView,
|
cipher: cipherView,
|
||||||
@@ -1418,32 +1585,30 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
|
|
||||||
await this.openAddEditVaultItemPopout(sender.tab, { cipherId: cipherView.id });
|
await this.openAddEditVaultItemPopout(sender.tab, { cipherId: cipherView.id });
|
||||||
await BrowserApi.sendMessage("inlineAutofillMenuRefreshAddEditCipher");
|
await BrowserApi.sendMessage("inlineAutofillMenuRefreshAddEditCipher");
|
||||||
|
} catch (error) {
|
||||||
|
this.logService.error("Error building cipher and opening add/edit vault item popout", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.currentAddNewItemData = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds and returns a new cipher view with the provided vault item data.
|
* Builds and returns a new cipher view with the provided vault item data.
|
||||||
*
|
*
|
||||||
* @param addNewCipherType - The type of cipher to add
|
|
||||||
* @param login - The login data captured from the extension message
|
* @param login - The login data captured from the extension message
|
||||||
* @param card - The card data captured from the extension message
|
* @param card - The card data captured from the extension message
|
||||||
* @param identity - The identity data captured from the extension message
|
* @param identity - The identity data captured from the extension message
|
||||||
*/
|
*/
|
||||||
private buildNewVaultItemCipherView({
|
private buildNewVaultItemCipherView({ login, card, identity }: OverlayAddNewItemMessage) {
|
||||||
addNewCipherType,
|
if (login && this.isAddingNewLogin()) {
|
||||||
login,
|
|
||||||
card,
|
|
||||||
identity,
|
|
||||||
}: OverlayAddNewItemMessage) {
|
|
||||||
if (login && addNewCipherType === CipherType.Login) {
|
|
||||||
return this.buildLoginCipherView(login);
|
return this.buildLoginCipherView(login);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (card && addNewCipherType === CipherType.Card) {
|
if (card && this.isAddingNewCard()) {
|
||||||
return this.buildCardCipherView(card);
|
return this.buildCardCipherView(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (identity && addNewCipherType === CipherType.Identity) {
|
if (identity && this.isAddingNewIdentity()) {
|
||||||
return this.buildIdentityCipherView(identity);
|
return this.buildIdentityCipherView(identity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1708,7 +1873,9 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
|
|
||||||
this.resetFocusedFieldSubFrameOffsets(sender);
|
this.resetFocusedFieldSubFrameOffsets(sender);
|
||||||
this.cancelInlineMenuFadeInAndPositionUpdate();
|
this.cancelInlineMenuFadeInAndPositionUpdate();
|
||||||
void this.toggleInlineMenuHidden({ isInlineMenuHidden: true }, sender);
|
this.toggleInlineMenuHidden({ isInlineMenuHidden: true }, sender).catch((error) =>
|
||||||
|
this.logService.error(error),
|
||||||
|
);
|
||||||
this.repositionInlineMenuSubject.next(sender);
|
this.repositionInlineMenuSubject.next(sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1898,14 +2065,14 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
filledByCipherType: this.focusedFieldData?.filledByCipherType,
|
filledByCipherType: this.focusedFieldData?.filledByCipherType,
|
||||||
showInlineMenuAccountCreation: this.showInlineMenuAccountCreation(),
|
showInlineMenuAccountCreation: this.showInlineMenuAccountCreation(),
|
||||||
});
|
});
|
||||||
void this.updateInlineMenuPosition(
|
this.updateInlineMenuPosition(
|
||||||
{
|
{
|
||||||
overlayElement: isInlineMenuListPort
|
overlayElement: isInlineMenuListPort
|
||||||
? AutofillOverlayElement.List
|
? AutofillOverlayElement.List
|
||||||
: AutofillOverlayElement.Button,
|
: AutofillOverlayElement.Button,
|
||||||
},
|
},
|
||||||
port.sender,
|
port.sender,
|
||||||
);
|
).catch((error) => this.logService.error(error));
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1099,7 +1099,9 @@ describe("AutofillOverlayContentService", () => {
|
|||||||
selectFieldElement.dispatchEvent(new Event("focus"));
|
selectFieldElement.dispatchEvent(new Event("focus"));
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
|
||||||
expect(sendExtensionMessageSpy).toHaveBeenCalledWith("closeAutofillInlineMenu");
|
expect(sendExtensionMessageSpy).toHaveBeenCalledWith("closeAutofillInlineMenu", {
|
||||||
|
forceCloseInlineMenu: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("updates the most recently focused field", async () => {
|
it("updates the most recently focused field", async () => {
|
||||||
|
|||||||
@@ -249,10 +249,6 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
|||||||
* to the background script to add a new cipher.
|
* to the background script to add a new cipher.
|
||||||
*/
|
*/
|
||||||
async addNewVaultItem({ addNewCipherType }: AutofillExtensionMessage) {
|
async addNewVaultItem({ addNewCipherType }: AutofillExtensionMessage) {
|
||||||
if (!(await this.isInlineMenuListVisible())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const command = "autofillOverlayAddNewVaultItem";
|
const command = "autofillOverlayAddNewVaultItem";
|
||||||
|
|
||||||
if (addNewCipherType === CipherType.Login) {
|
if (addNewCipherType === CipherType.Login) {
|
||||||
@@ -680,7 +676,9 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (elementIsSelectElement(formFieldElement)) {
|
if (elementIsSelectElement(formFieldElement)) {
|
||||||
await this.sendExtensionMessage("closeAutofillInlineMenu");
|
await this.sendExtensionMessage("closeAutofillInlineMenu", {
|
||||||
|
forceCloseInlineMenu: true,
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -763,7 +761,11 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
|||||||
private async updateMostRecentlyFocusedField(
|
private async updateMostRecentlyFocusedField(
|
||||||
formFieldElement: ElementWithOpId<FormFieldElement>,
|
formFieldElement: ElementWithOpId<FormFieldElement>,
|
||||||
) {
|
) {
|
||||||
if (!formFieldElement || !elementIsFillableFormField(formFieldElement)) {
|
if (
|
||||||
|
!formFieldElement ||
|
||||||
|
!elementIsFillableFormField(formFieldElement) ||
|
||||||
|
elementIsSelectElement(formFieldElement)
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user