[PM-4401] Re-add manual ngZone.run execution (#6647)

* [PM-4401] feat: add browser messaging api service

* [PM-4401] feat: use new service

* [PM-4401] chore: rename to `ZonedMessageListenerService`

* [PM-4401] chore: remove polyfill
This commit is contained in:
Andreas Coroiu 2023-10-20 18:41:26 +02:00 committed by GitHub
parent 8dc81b603d
commit c1494b8494
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 65 additions and 51 deletions

View File

@ -0,0 +1,21 @@
import { NgZone } from "@angular/core";
import { MonoTypeOperatorFunction, Observable } from "rxjs";
export function runInsideAngular<T>(ngZone: NgZone): MonoTypeOperatorFunction<T> {
return (source: Observable<T>) =>
new Observable<T>((subscriber) => {
const subscription = source.subscribe({
next(value) {
ngZone.run(() => subscriber.next(value));
},
error(error: unknown) {
ngZone.run(() => subscriber.error(error));
},
complete() {
ngZone.run(() => subscriber.complete());
},
});
return () => subscription.unsubscribe();
});
}

View File

@ -0,0 +1,33 @@
import { Injectable, NgZone } from "@angular/core";
import { Observable } from "rxjs";
import { BrowserApi } from "./browser-api";
import { runInsideAngular } from "./run-inside-angular.operator";
/**
* This service is used for listening to messages from the background script.
* It automatically runs all callbacks inside the Angular zone.
* This should be used instead of `BrowserApi.messageListener` in all popup-components.
* Not needed for services running in the background script.
*/
@Injectable({ providedIn: "root" })
export class ZonedMessageListenerService {
constructor(private ngZone: NgZone) {}
messageListener(
name: string,
callback: (
message: any,
sender: chrome.runtime.MessageSender,
sendResponse: any
) => boolean | void
) {
BrowserApi.messageListener(name, (message, sender, sendResponse) => {
return this.ngZone.run(() => callback(message, sender, sendResponse));
});
}
messageListener$(): Observable<unknown> {
return BrowserApi.messageListener$().pipe(runInsideAngular(this.ngZone));
}
}

View File

@ -1,44 +0,0 @@
/**
* Monkey patch `chrome.runtime.onMessage` event listeners to run in the Angular zone.
*/
Zone.__load_patch("ChromeRuntimeOnMessage", (global: any, Zone: ZoneType, api: _ZonePrivate) => {
if (typeof global?.chrome?.runtime?.onMessage === "undefined") {
return;
}
const onMessage = global.chrome.runtime.onMessage;
// eslint-disable-next-line @typescript-eslint/ban-types
const nativeAddListener = onMessage.addListener as Function;
api.ObjectDefineProperty(chrome.runtime.onMessage, "addListener", {
value: function (...args: any[]) {
const callback = args.length > 0 ? args[0] : null;
if (typeof callback === "function") {
const wrapperedCallback = Zone.current.wrap(callback, "ChromeRuntimeOnMessage");
callback[api.symbol("chromeRuntimeOnMessageCallback")] = wrapperedCallback;
return nativeAddListener.call(onMessage, wrapperedCallback);
} else {
return nativeAddListener.apply(onMessage, args);
}
},
writable: false,
});
// eslint-disable-next-line @typescript-eslint/ban-types
const nativeRemoveListener = onMessage.removeListener as Function;
api.ObjectDefineProperty(chrome.runtime.onMessage, "removeListener", {
value: function (...args: any[]) {
const callback = args.length > 0 ? args[0] : null;
if (typeof callback === "function") {
const wrapperedCallback = callback[api.symbol("chromeRuntimeOnMessageCallback")];
if (wrapperedCallback) {
return nativeRemoveListener.call(onMessage, wrapperedCallback);
} else {
return nativeRemoveListener.apply(onMessage, args);
}
} else {
return nativeRemoveListener.apply(onMessage, args);
}
},
writable: false,
});
});

View File

@ -19,6 +19,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
import { DialogService, SimpleDialogOptions } from "@bitwarden/components";
import { BrowserApi } from "../platform/browser/browser-api";
import { ZonedMessageListenerService } from "../platform/browser/zoned-message-listener.service";
import { BrowserStateService } from "../platform/services/abstractions/browser-state.service";
import { routerTransition } from "./app-routing.animations";
@ -50,7 +51,8 @@ export class AppComponent implements OnInit, OnDestroy {
private ngZone: NgZone,
private sanitizer: DomSanitizer,
private platformUtilsService: PlatformUtilsService,
private dialogService: DialogService
private dialogService: DialogService,
private browserMessagingApi: ZonedMessageListenerService
) {}
async ngOnInit() {
@ -128,7 +130,7 @@ export class AppComponent implements OnInit, OnDestroy {
};
(window as any).bitwardenPopupMainMessageListener = bitwardenPopupMainMessageListener;
BrowserApi.messageListener("app.component", bitwardenPopupMainMessageListener);
this.browserMessagingApi.messageListener("app.component", bitwardenPopupMainMessageListener);
// eslint-disable-next-line rxjs/no-async-subscribe
this.router.events.pipe(takeUntil(this.destroy$)).subscribe(async (event) => {

View File

@ -1,4 +1,2 @@
import "core-js/stable";
import "zone.js";
import "../platform/polyfills/zone-patch-chrome-runtime";

View File

@ -30,7 +30,7 @@ import { SecureNoteView } from "@bitwarden/common/vault/models/view/secure-note.
import { DialogService } from "@bitwarden/components";
import { PasswordRepromptService } from "@bitwarden/vault";
import { BrowserApi } from "../../../../platform/browser/browser-api";
import { ZonedMessageListenerService } from "../../../../platform/browser/zoned-message-listener.service";
import {
BrowserFido2Message,
BrowserFido2UserInterfaceSession,
@ -78,7 +78,8 @@ export class Fido2Component implements OnInit, OnDestroy {
private settingsService: SettingsService,
private searchService: SearchService,
private logService: LogService,
private dialogService: DialogService
private dialogService: DialogService,
private browserMessagingApi: ZonedMessageListenerService
) {}
ngOnInit() {
@ -93,7 +94,10 @@ export class Fido2Component implements OnInit, OnDestroy {
}))
);
combineLatest([queryParams$, BrowserApi.messageListener$() as Observable<BrowserFido2Message>])
combineLatest([
queryParams$,
this.browserMessagingApi.messageListener$() as Observable<BrowserFido2Message>,
])
.pipe(
concatMap(async ([queryParams, message]) => {
this.sessionId = queryParams.sessionId;