/
githubmirror
/
angular
Обзор
Документация
Войти
/
githubmirror
/
angular
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
devtools/projects/shell-browser/src/app/same-page-message-bus.ts
120 строк
4 KB
Matthieu Riegler
refactor(devtools): prevent spamming the message bus
09 мар 2026, 20:45
09 мар 2026, 20:45
263b819
Код
Авторство
О чём код?
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ import {Events, MessageBus, Parameters} from '../../../protocol'; type AnyEventCallback<Ev> = <E extends keyof Ev>(topic: E, args: Parameters<Ev[E]>) => void; type ListenerFn = (e: MessageEvent) => void; // We use a bus status to know if we should send the message between the context // eg: We don't want to send events until the devtools tab has been focused to present spaming the message bus // init: initial state, until the backend is ready // waiting: blocking message, until we see the 'enableFrameConnection' event // ready: we send every message through the bus export type BusStatus = 'init' | 'waiting' | 'ready'; /** * Note: This Bus implementation implements a filter based of topic. It might not be suitable for others messages than the ones currently used. */ export class SamePageMessageBus extends MessageBus<Events> { private status: BusStatus = 'init'; private _listeners: ListenerFn[] = []; constructor( private debugName: string, private _source: string, private _destination: string, ) { super(); } onAny(cb: AnyEventCallback<Events>): () => void { const listener: ListenerFn = (e) => { this.updateStatus(e.data?.topic); if (e.source !== window || !e.data || !e.data.topic || e.data.source !== this._destination) { return; } cb(e.data.topic, e.data.args); }; window.addEventListener('message', listener); this._listeners.push(listener); return () => { this._listeners.splice(this._listeners.indexOf(listener), 1); window.removeEventListener('message', listener); }; } override on<E extends keyof Events>(topic: E, cb: Events[E]): () => void { const listener: ListenerFn = (e) => { this.updateStatus(e.data?.topic); if (e.source !== window || !e.data || e.data.source !== this._destination || !e.data.topic) { return; } if (e.data.topic === topic) { (cb as any).apply(null, e.data.args); } }; window.addEventListener('message', listener); this._listeners.push(listener); return () => { this._listeners.splice(this._listeners.indexOf(listener), 1); window.removeEventListener('message', listener); }; } override once<E extends keyof Events>(topic: E, cb: Events[E]): void { const listener: ListenerFn = (e) => { if (e.source !== window || !e.data || e.data.source !== this._destination || !e.data.topic) { return; } if (e.data.topic === topic) { (cb as any).apply(null, e.data.args); } window.removeEventListener('message', listener); }; window.addEventListener('message', listener); } override emit<E extends keyof Events>(topic: E, args?: Parameters<Events[E]>): boolean { if (this.shouldSkipMessage(topic)) { return false; } window.postMessage( { source: this._source, topic, args, __ignore_ng_zone__: true, __NG_DEVTOOLS_EVENT__: true, }, '*', ); return true; } override destroy(): void { this._listeners.forEach((l) => window.removeEventListener('message', l)); this._listeners = []; } private shouldSkipMessage(topic: string): boolean { return this.status === 'waiting' && topic !== 'enableFrameConnection'; } private updateStatus(topic: string): void { if (topic === 'enableFrameConnection') { this.status = 'ready'; } else if (topic === 'backendReady') { this.status = 'waiting'; } } }