[PM-6426] Implementing foreground and background task scheduler services to avoid duplication of task scheudlers and to have the background setup as a fallback to the poopup tasks

This commit is contained in:
Cesar Gonzalez 2024-05-11 20:31:21 -05:00
parent 72cc3a3296
commit d9b5353cfd
No known key found for this signature in database
GPG Key ID: 3381A5457F8CCECF
8 changed files with 224 additions and 18 deletions

View File

@ -221,12 +221,13 @@ import BrowserLocalStorageService from "../platform/services/browser-local-stora
import BrowserMemoryStorageService from "../platform/services/browser-memory-storage.service";
import { BrowserMultithreadEncryptServiceImplementation } from "../platform/services/browser-multithread-encrypt.service.implementation";
import { BrowserScriptInjectorService } from "../platform/services/browser-script-injector.service";
import { BrowserTaskSchedulerServiceImplementation } from "../platform/services/browser-task-scheduler.service";
import { DefaultBrowserStateService } from "../platform/services/default-browser-state.service";
import I18nService from "../platform/services/i18n.service";
import { LocalBackedSessionStorageService } from "../platform/services/local-backed-session-storage.service";
import { BackgroundPlatformUtilsService } from "../platform/services/platform-utils/background-platform-utils.service";
import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service";
import { BackgroundTaskSchedulerService } from "../platform/services/task-scheduler/background-task-scheduler.service";
import { ForegroundTaskSchedulerService } from "../platform/services/task-scheduler/foreground-task-scheduler.service";
import { BackgroundDerivedStateProvider } from "../platform/state/background-derived-state.provider";
import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service";
import { BrowserStorageServiceProvider } from "../platform/storage/browser-storage-service.provider";
@ -507,10 +508,9 @@ export default class MainBackground {
this.derivedStateProvider,
);
this.taskSchedulerService = new BrowserTaskSchedulerServiceImplementation(
this.logService,
this.stateProvider,
);
this.taskSchedulerService = this.popupOnlyContext
? new ForegroundTaskSchedulerService(this.logService, this.stateProvider)
: new BackgroundTaskSchedulerService(this.logService, this.stateProvider);
this.taskSchedulerService.registerTaskHandler(ScheduledTaskNames.scheduleNextSyncInterval, () =>
this.fullSync(),
);

View File

@ -1,4 +1,4 @@
import { BrowserTaskSchedulerServiceImplementation } from "../../services/browser-task-scheduler.service";
import { BrowserTaskSchedulerServiceImplementation } from "../../services/task-scheduler/browser-task-scheduler.service";
import { CachedServices, factory, FactoryOptions } from "./factory-options";
import { logServiceFactory, LogServiceInitOptions } from "./log-service.factory";

View File

@ -1,6 +1,24 @@
import { Observable } from "rxjs";
import { TaskSchedulerService } from "@bitwarden/common/platform/abstractions/task-scheduler.service";
import { ScheduledTaskName } from "@bitwarden/common/platform/enums/scheduled-task-name.enum";
export const BrowserTaskSchedulerPortName = "browser-task-scheduler-port";
export const BrowserTaskSchedulerPortActions = {
setTimeout: "setTimeout",
setInterval: "setInterval",
clearAlarm: "clearAlarm",
} as const;
export type BrowserTaskSchedulerPortAction = keyof typeof BrowserTaskSchedulerPortActions;
export type BrowserTaskSchedulerPortMessage = {
action: BrowserTaskSchedulerPortAction;
taskName: ScheduledTaskName;
alarmName?: string;
delayInMs?: number;
intervalInMs?: number;
};
export type ActiveAlarm = {
alarmName: string;
@ -12,4 +30,5 @@ export abstract class BrowserTaskSchedulerService extends TaskSchedulerService {
activeAlarms$: Observable<ActiveAlarm[]>;
abstract clearAllScheduledTasks(): Promise<void>;
abstract verifyAlarmsState(): Promise<void>;
abstract clearScheduledAlarm(alarmName: string): Promise<void>;
}

View File

@ -0,0 +1,96 @@
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { StateProvider } from "@bitwarden/common/platform/state";
import { BrowserApi } from "../../browser/browser-api";
import {
BrowserTaskSchedulerPortActions,
BrowserTaskSchedulerPortMessage,
BrowserTaskSchedulerPortName,
} from "../abstractions/browser-task-scheduler.service";
import { BrowserTaskSchedulerServiceImplementation } from "./browser-task-scheduler.service";
export class BackgroundTaskSchedulerService extends BrowserTaskSchedulerServiceImplementation {
private ports: Set<chrome.runtime.Port> = new Set();
constructor(logService: LogService, stateProvider: StateProvider) {
super(logService, stateProvider);
BrowserApi.addListener(chrome.runtime.onConnect, this.handlePortOnConnect);
}
/**
* Clears a scheduled alarm and sends a message to all ports to clear the alarm.
*
* @param alarmName - The name of the alarm.
*/
async clearScheduledAlarm(alarmName: string): Promise<void> {
void super.clearScheduledAlarm(alarmName);
const taskName = this.getTaskFromAlarmName(alarmName);
this.sendMessageToPorts({
action: BrowserTaskSchedulerPortActions.clearAlarm,
taskName,
alarmName,
});
}
/**
* Handles a port connection made from the foreground task scheduler.
*
* @param port - The port that was connected.
*/
private handlePortOnConnect = (port: chrome.runtime.Port) => {
this.ports.add(port);
port.onMessage.addListener(this.handlePortMessage);
port.onDisconnect.addListener(this.handlePortOnDisconnect);
};
/**
* Handles a port disconnection.
*
* @param port - The port that was disconnected.
*/
private handlePortOnDisconnect = (port: chrome.runtime.Port) => {
port.onMessage.removeListener(this.handlePortMessage);
this.ports.delete(port);
};
/**
* Handles a message from a port.
*
* @param message - The message that was received.
* @param port - The port that sent the message.
*/
private handlePortMessage = (
message: BrowserTaskSchedulerPortMessage,
port: chrome.runtime.Port,
) => {
if (port.name !== BrowserTaskSchedulerPortName) {
return;
}
if (message.action === BrowserTaskSchedulerPortActions.setTimeout) {
super.setTimeout(message.taskName, message.delayInMs);
return;
}
if (message.action === BrowserTaskSchedulerPortActions.setInterval) {
super.setInterval(message.taskName, message.intervalInMs);
return;
}
if (message.action === BrowserTaskSchedulerPortActions.clearAlarm) {
void super.clearScheduledAlarm(message.alarmName);
return;
}
};
/**
* Sends a message to all ports.
*
* @param message - The message to send.
*/
private sendMessageToPorts(message: BrowserTaskSchedulerPortMessage) {
this.ports.forEach((port) => port.postMessage(message));
}
}

View File

@ -5,12 +5,12 @@ import { ScheduledTaskNames } from "@bitwarden/common/platform/enums/scheduled-t
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
import { GlobalState, StateProvider } from "@bitwarden/common/platform/state";
import { flushPromises, triggerOnAlarmEvent } from "../../autofill/spec/testing-utils";
import { flushPromises, triggerOnAlarmEvent } from "../../../autofill/spec/testing-utils";
import {
ActiveAlarm,
BrowserTaskSchedulerService,
} from "./abstractions/browser-task-scheduler.service";
} from "../abstractions/browser-task-scheduler.service";
import { BrowserTaskSchedulerServiceImplementation } from "./browser-task-scheduler.service";
jest.mock("rxjs", () => {

View File

@ -10,12 +10,11 @@ import {
StateProvider,
} from "@bitwarden/common/platform/state";
import { BrowserApi } from "../browser/browser-api";
import { BrowserApi } from "../../browser/browser-api";
import {
ActiveAlarm,
BrowserTaskSchedulerService,
} from "./abstractions/browser-task-scheduler.service";
} from "../abstractions/browser-task-scheduler.service";
const ACTIVE_ALARMS = new KeyDefinition(TASK_SCHEDULER_DISK, "activeAlarms", {
deserializer: (value: ActiveAlarm[]) => value ?? [],
@ -265,7 +264,7 @@ export class BrowserTaskSchedulerServiceImplementation
*
* @param alarmName - The name of the alarm to clear.
*/
private async clearScheduledAlarm(alarmName: string): Promise<void> {
async clearScheduledAlarm(alarmName: string): Promise<void> {
const wasCleared = await this.clearAlarm(alarmName);
if (wasCleared) {
await this.deleteActiveAlarm(alarmName);
@ -322,7 +321,7 @@ export class BrowserTaskSchedulerServiceImplementation
*
* @param alarmName - The alarm name to parse.
*/
private getTaskFromAlarmName(alarmName: string): ScheduledTaskName {
protected getTaskFromAlarmName(alarmName: string): ScheduledTaskName {
return alarmName.split("__")[0] as ScheduledTaskName;
}

View File

@ -0,0 +1,91 @@
import { Subscription } from "rxjs";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { ScheduledTaskName } from "@bitwarden/common/platform/enums/scheduled-task-name.enum";
import { StateProvider } from "@bitwarden/common/platform/state";
import {
BrowserTaskSchedulerPortActions,
BrowserTaskSchedulerPortMessage,
BrowserTaskSchedulerPortName,
} from "../abstractions/browser-task-scheduler.service";
import { BrowserTaskSchedulerServiceImplementation } from "./browser-task-scheduler.service";
export class ForegroundTaskSchedulerService extends BrowserTaskSchedulerServiceImplementation {
private port: chrome.runtime.Port;
constructor(logService: LogService, stateProvider: StateProvider) {
super(logService, stateProvider);
this.port = chrome.runtime.connect({ name: BrowserTaskSchedulerPortName });
this.port.onMessage.addListener(this.handlePortMessage);
}
/**
* Sends a port message to the background to set up a fallback timeout. Also sets a timeout locally.
* This is done to ensure that the timeout triggers even if the popup is closed.
*
* @param taskName - The name of the task.
* @param delayInMs - The delay in milliseconds.
*/
setTimeout(taskName: ScheduledTaskName, delayInMs: number): Subscription {
this.sendPortMessage({
action: BrowserTaskSchedulerPortActions.setTimeout,
taskName,
delayInMs,
});
return super.setTimeout(taskName, delayInMs);
}
/**
* Sends a port message to the background to set up a fallback interval. Also sets an interval locally.
* This is done to ensure that the interval triggers even if the popup is closed.
*
* @param taskName - The name of the task.
* @param intervalInMs - The interval in milliseconds.
* @param initialDelayInMs - The initial delay in milliseconds.
*/
setInterval(
taskName: ScheduledTaskName,
intervalInMs: number,
initialDelayInMs?: number,
): Subscription {
this.sendPortMessage({
action: BrowserTaskSchedulerPortActions.setInterval,
taskName,
intervalInMs,
});
return super.setInterval(taskName, intervalInMs, initialDelayInMs);
}
/**
* Handles port messages from the background task scheduler.
*
* @param message - The message that indicates we should clear an alarm.
* @param port - The port that the message was received on.
*/
private handlePortMessage = (
message: BrowserTaskSchedulerPortMessage,
port: chrome.runtime.Port,
) => {
if (port.name !== BrowserTaskSchedulerPortName) {
return;
}
if (message.action === BrowserTaskSchedulerPortActions.clearAlarm) {
void super.clearScheduledAlarm(message.alarmName);
}
};
/**
* Sends a message to the background task scheduler.
*
* @param message - The message to send.
*/
private sendPortMessage(message: BrowserTaskSchedulerPortMessage) {
this.port.postMessage(message);
}
}

View File

@ -110,10 +110,10 @@ import { BrowserCryptoService } from "../../platform/services/browser-crypto.ser
import { BrowserEnvironmentService } from "../../platform/services/browser-environment.service";
import BrowserLocalStorageService from "../../platform/services/browser-local-storage.service";
import { BrowserScriptInjectorService } from "../../platform/services/browser-script-injector.service";
import { BrowserTaskSchedulerServiceImplementation } from "../../platform/services/browser-task-scheduler.service";
import { DefaultBrowserStateService } from "../../platform/services/default-browser-state.service";
import I18nService from "../../platform/services/i18n.service";
import { ForegroundPlatformUtilsService } from "../../platform/services/platform-utils/foreground-platform-utils.service";
import { ForegroundTaskSchedulerService } from "../../platform/services/task-scheduler/foreground-task-scheduler.service";
import { ForegroundDerivedStateProvider } from "../../platform/state/foreground-derived-state.provider";
import { BrowserStorageServiceProvider } from "../../platform/storage/browser-storage-service.provider";
import { ForegroundMemoryStorageService } from "../../platform/storage/foreground-memory-storage.service";
@ -611,11 +611,12 @@ const safeProviders: SafeProvider[] = [
}),
safeProvider({
provide: TaskSchedulerService,
useExisting: BrowserTaskSchedulerServiceImplementation,
useExisting: ForegroundTaskSchedulerService,
}),
safeProvider({
provide: BrowserTaskSchedulerServiceImplementation,
deps: [LogService, StateProvider],
provide: ForegroundTaskSchedulerService,
useFactory: getBgService<ForegroundTaskSchedulerService>("taskSchedulerService"),
deps: [],
}),
];