mirror of
https://github.com/bitwarden/browser.git
synced 2025-01-31 22:51:28 +01:00
[PM-15065] Vault Loading on empty tabs (#12059)
* catch errors from `tabSendMessage` - Removes the need for a timeout when fetching page details * Add parameter to reject on error - allows each implementation to decide if they want to handle the error or not
This commit is contained in:
parent
da6a0cb8e9
commit
d52da5869b
@ -143,7 +143,7 @@ describe("AutofillService", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("collectPageDetailsFromTab$", () => {
|
describe("collectPageDetailsFromTab$", () => {
|
||||||
const tab = mock<chrome.tabs.Tab>({ id: 1 });
|
const tab = mock<chrome.tabs.Tab>({ id: 1, url: "https://www.example.com" });
|
||||||
const messages = new Subject<CollectPageDetailsResponseMessage>();
|
const messages = new Subject<CollectPageDetailsResponseMessage>();
|
||||||
|
|
||||||
function mockCollectPageDetailsResponseMessage(
|
function mockCollectPageDetailsResponseMessage(
|
||||||
@ -165,11 +165,16 @@ describe("AutofillService", () => {
|
|||||||
it("sends a `collectPageDetails` message to the passed tab", () => {
|
it("sends a `collectPageDetails` message to the passed tab", () => {
|
||||||
autofillService.collectPageDetailsFromTab$(tab);
|
autofillService.collectPageDetailsFromTab$(tab);
|
||||||
|
|
||||||
expect(BrowserApi.tabSendMessage).toHaveBeenCalledWith(tab, {
|
expect(BrowserApi.tabSendMessage).toHaveBeenCalledWith(
|
||||||
command: AutofillMessageCommand.collectPageDetails,
|
|
||||||
sender: AutofillMessageSender.collectPageDetailsFromTabObservable,
|
|
||||||
tab,
|
tab,
|
||||||
});
|
{
|
||||||
|
command: AutofillMessageCommand.collectPageDetails,
|
||||||
|
sender: AutofillMessageSender.collectPageDetailsFromTabObservable,
|
||||||
|
tab,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("builds an array of page details from received `collectPageDetailsResponse` messages", async () => {
|
it("builds an array of page details from received `collectPageDetailsResponse` messages", async () => {
|
||||||
@ -218,6 +223,24 @@ describe("AutofillService", () => {
|
|||||||
|
|
||||||
expect(tracker.emissions[1]).toBeUndefined();
|
expect(tracker.emissions[1]).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("returns an empty array when the tab.url is empty", async () => {
|
||||||
|
const tracker = subscribeTo(autofillService.collectPageDetailsFromTab$({ ...tab, url: "" }));
|
||||||
|
|
||||||
|
await tracker.pauseUntilReceived(1);
|
||||||
|
|
||||||
|
expect(tracker.emissions[0]).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns an empty array when the `BrowserApi.tabSendMessage` throws an error", async () => {
|
||||||
|
(BrowserApi.tabSendMessage as jest.Mock).mockRejectedValueOnce(undefined);
|
||||||
|
|
||||||
|
const tracker = subscribeTo(autofillService.collectPageDetailsFromTab$(tab));
|
||||||
|
|
||||||
|
await tracker.pauseUntilReceived(1);
|
||||||
|
|
||||||
|
expect(tracker.emissions[0]).toEqual([]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("loadAutofillScriptsOnInstall", () => {
|
describe("loadAutofillScriptsOnInstall", () => {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { filter, firstValueFrom, Observable, scan, startWith } from "rxjs";
|
import { filter, firstValueFrom, merge, Observable, ReplaySubject, scan, startWith } from "rxjs";
|
||||||
import { pairwise } from "rxjs/operators";
|
import { pairwise } from "rxjs/operators";
|
||||||
|
|
||||||
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service";
|
||||||
@ -91,6 +91,9 @@ export default class AutofillService implements AutofillServiceInterface {
|
|||||||
* @param tab The tab to collect page details from
|
* @param tab The tab to collect page details from
|
||||||
*/
|
*/
|
||||||
collectPageDetailsFromTab$(tab: chrome.tabs.Tab): Observable<PageDetail[]> {
|
collectPageDetailsFromTab$(tab: chrome.tabs.Tab): Observable<PageDetail[]> {
|
||||||
|
/** Replay Subject that can be utilized when `messages$` may not emit the page details. */
|
||||||
|
const pageDetailsFallback$ = new ReplaySubject<[]>(1);
|
||||||
|
|
||||||
const pageDetailsFromTab$ = this.messageListener
|
const pageDetailsFromTab$ = this.messageListener
|
||||||
.messages$(COLLECT_PAGE_DETAILS_RESPONSE_COMMAND)
|
.messages$(COLLECT_PAGE_DETAILS_RESPONSE_COMMAND)
|
||||||
.pipe(
|
.pipe(
|
||||||
@ -112,13 +115,28 @@ export default class AutofillService implements AutofillServiceInterface {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
void BrowserApi.tabSendMessage(tab, {
|
void BrowserApi.tabSendMessage(
|
||||||
tab: tab,
|
tab,
|
||||||
command: AutofillMessageCommand.collectPageDetails,
|
{
|
||||||
sender: AutofillMessageSender.collectPageDetailsFromTabObservable,
|
tab: tab,
|
||||||
|
command: AutofillMessageCommand.collectPageDetails,
|
||||||
|
sender: AutofillMessageSender.collectPageDetailsFromTabObservable,
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
true,
|
||||||
|
).catch(() => {
|
||||||
|
// When `tabSendMessage` throws an error the `pageDetailsFromTab$` will not emit,
|
||||||
|
// fallback to an empty array
|
||||||
|
pageDetailsFallback$.next([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
return pageDetailsFromTab$;
|
// Empty/New tabs do not have a URL.
|
||||||
|
// In Safari, `tabSendMessage` doesn't throw an error for this case. Fallback to the empty array to handle.
|
||||||
|
if (!tab.url) {
|
||||||
|
pageDetailsFallback$.next([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return merge(pageDetailsFromTab$, pageDetailsFallback$);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -200,15 +200,17 @@ export class BrowserApi {
|
|||||||
tab: chrome.tabs.Tab,
|
tab: chrome.tabs.Tab,
|
||||||
obj: T,
|
obj: T,
|
||||||
options: chrome.tabs.MessageSendOptions = null,
|
options: chrome.tabs.MessageSendOptions = null,
|
||||||
|
rejectOnError = false,
|
||||||
): Promise<TResponse> {
|
): Promise<TResponse> {
|
||||||
if (!tab || !tab.id) {
|
if (!tab || !tab.id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise<TResponse>((resolve) => {
|
return new Promise<TResponse>((resolve, reject) => {
|
||||||
chrome.tabs.sendMessage(tab.id, obj, options, (response) => {
|
chrome.tabs.sendMessage(tab.id, obj, options, (response) => {
|
||||||
if (chrome.runtime.lastError) {
|
if (chrome.runtime.lastError && rejectOnError) {
|
||||||
// Some error happened
|
// Some error happened
|
||||||
|
reject();
|
||||||
}
|
}
|
||||||
resolve(response);
|
resolve(response);
|
||||||
});
|
});
|
||||||
|
@ -10,7 +10,6 @@ import {
|
|||||||
startWith,
|
startWith,
|
||||||
Subject,
|
Subject,
|
||||||
switchMap,
|
switchMap,
|
||||||
timeout,
|
|
||||||
} from "rxjs";
|
} from "rxjs";
|
||||||
|
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
@ -75,9 +74,7 @@ export class VaultPopupAutofillService {
|
|||||||
if (!tab) {
|
if (!tab) {
|
||||||
return of([]);
|
return of([]);
|
||||||
}
|
}
|
||||||
return this.autofillService
|
return this.autofillService.collectPageDetailsFromTab$(tab);
|
||||||
.collectPageDetailsFromTab$(tab)
|
|
||||||
.pipe(timeout({ first: 1500, with: () => of([]) }));
|
|
||||||
}),
|
}),
|
||||||
shareReplay({ refCount: false, bufferSize: 1 }),
|
shareReplay({ refCount: false, bufferSize: 1 }),
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user