Type react-devtools-hook-installer and react-devtools-hook-settings-injector messages (#35586)

This commit is contained in:
Sebastian "Sebbie" Silbermann
2026-01-21 19:13:24 +01:00
committed by GitHub
parent b546603bcb
commit cdbd55f440
8 changed files with 106 additions and 44 deletions

View File

@@ -593,6 +593,7 @@ module.exports = {
mixin$Animatable: 'readonly',
MouseEventHandler: 'readonly',
NavigateEvent: 'readonly',
Partial: 'readonly',
PerformanceMeasureOptions: 'readonly',
PropagationPhases: 'readonly',
PropertyDescriptor: 'readonly',

View File

@@ -826,7 +826,7 @@ declare class WebSocket extends EventTarget {
bufferedAmount: number;
extensions: string;
onopen: (ev: any) => mixed;
onmessage: (ev: MessageEvent) => mixed;
onmessage: (ev: MessageEvent<>) => mixed;
onclose: (ev: CloseEvent) => mixed;
onerror: (ev: any) => mixed;
binaryType: 'blob' | 'arraybuffer';
@@ -855,8 +855,8 @@ declare class Worker extends EventTarget {
workerOptions?: WorkerOptions
): void;
onerror: null | ((ev: any) => mixed);
onmessage: null | ((ev: MessageEvent) => mixed);
onmessageerror: null | ((ev: MessageEvent) => mixed);
onmessage: null | ((ev: MessageEvent<>) => mixed);
onmessageerror: null | ((ev: MessageEvent<>) => mixed);
postMessage(message: any, ports?: any): void;
terminate(): void;
}
@@ -888,14 +888,14 @@ declare class WorkerGlobalScope extends EventTarget {
}
declare class DedicatedWorkerGlobalScope extends WorkerGlobalScope {
onmessage: (ev: MessageEvent) => mixed;
onmessageerror: (ev: MessageEvent) => mixed;
onmessage: (ev: MessageEvent<>) => mixed;
onmessageerror: (ev: MessageEvent<>) => mixed;
postMessage(message: any, transfer?: Iterable<any>): void;
}
declare class SharedWorkerGlobalScope extends WorkerGlobalScope {
name: string;
onconnect: (ev: MessageEvent) => mixed;
onconnect: (ev: MessageEvent<>) => mixed;
}
declare class WorkerLocation {
@@ -2056,8 +2056,8 @@ declare class MessagePort extends EventTarget {
start(): void;
close(): void;
onmessage: null | ((ev: MessageEvent) => mixed);
onmessageerror: null | ((ev: MessageEvent) => mixed);
onmessage: null | ((ev: MessageEvent<>) => mixed);
onmessageerror: null | ((ev: MessageEvent<>) => mixed);
}
declare class MessageChannel {

View File

@@ -151,7 +151,7 @@ type TransitionEventHandler = (event: TransitionEvent) => mixed;
type TransitionEventListener =
| {handleEvent: TransitionEventHandler, ...}
| TransitionEventHandler;
type MessageEventHandler = (event: MessageEvent) => mixed;
type MessageEventHandler = (event: MessageEvent<>) => mixed;
type MessageEventListener =
| {handleEvent: MessageEventHandler, ...}
| MessageEventHandler;
@@ -845,8 +845,8 @@ declare class PageTransitionEvent extends Event {
// https://www.w3.org/TR/2008/WD-html5-20080610/comms.html
// and
// https://html.spec.whatwg.org/multipage/comms.html#the-messageevent-interfaces
declare class MessageEvent extends Event {
data: mixed;
declare class MessageEvent<Data = mixed> extends Event {
data: Data;
origin: string;
lastEventId: string;
source: WindowProxy;

View File

@@ -109,8 +109,8 @@ declare class ErrorEvent extends Event {
// https://html.spec.whatwg.org/multipage/web-messaging.html#broadcasting-to-other-browsing-contexts
declare class BroadcastChannel extends EventTarget {
name: string;
onmessage: ?(event: MessageEvent) => void;
onmessageerror: ?(event: MessageEvent) => void;
onmessage: ?(event: MessageEvent<>) => void;
onmessageerror: ?(event: MessageEvent<>) => void;
constructor(name: string): void;
postMessage(msg: mixed): void;

View File

@@ -293,7 +293,7 @@ export function connectToDevTools(options: ?ConnectOptions) {
scheduleRetry();
}
function handleMessage(event: MessageEvent) {
function handleMessage(event: MessageEvent<>) {
let data;
try {
if (typeof event.data === 'string') {

View File

@@ -1,38 +1,50 @@
/* global chrome */
/** @flow */
// We can't use chrome.storage domain from scripts which are injected in ExecutionWorld.MAIN
// This is the only purpose of this script - to send persisted settings to installHook.js content script
async function messageListener(event: MessageEvent) {
import type {UnknownMessageEvent} from './messages';
import type {DevToolsHookSettings} from 'react-devtools-shared/src/backend/types';
import {postMessage} from './messages';
async function messageListener(event: UnknownMessageEvent) {
if (event.source !== window) {
return;
}
if (event.data.source === 'react-devtools-hook-installer') {
if (event.data.payload.handshake) {
const settings = await chrome.storage.local.get();
const settings: Partial<DevToolsHookSettings> =
await chrome.storage.local.get();
// If storage was empty (first installation), define default settings
if (typeof settings.appendComponentStack !== 'boolean') {
settings.appendComponentStack = true;
}
if (typeof settings.breakOnConsoleErrors !== 'boolean') {
settings.breakOnConsoleErrors = false;
}
if (typeof settings.showInlineWarningsAndErrors !== 'boolean') {
settings.showInlineWarningsAndErrors = true;
}
if (typeof settings.hideConsoleLogsInStrictMode !== 'boolean') {
settings.hideConsoleLogsInStrictMode = false;
}
if (
typeof settings.disableSecondConsoleLogDimmingInStrictMode !== 'boolean'
) {
settings.disableSecondConsoleLogDimmingInStrictMode = false;
}
const hookSettings: DevToolsHookSettings = {
appendComponentStack:
typeof settings.appendComponentStack === 'boolean'
? settings.appendComponentStack
: true,
breakOnConsoleErrors:
typeof settings.breakOnConsoleErrors === 'boolean'
? settings.breakOnConsoleErrors
: false,
showInlineWarningsAndErrors:
typeof settings.showInlineWarningsAndErrors === 'boolean'
? settings.showInlineWarningsAndErrors
: true,
hideConsoleLogsInStrictMode:
typeof settings.hideConsoleLogsInStrictMode === 'boolean'
? settings.hideConsoleLogsInStrictMode
: false,
disableSecondConsoleLogDimmingInStrictMode:
typeof settings.disableSecondConsoleLogDimmingInStrictMode ===
'boolean'
? settings.disableSecondConsoleLogDimmingInStrictMode
: false,
};
window.postMessage({
postMessage({
source: 'react-devtools-hook-settings-injector',
payload: {settings},
payload: {settings: hookSettings},
});
window.removeEventListener('message', messageListener);
@@ -41,7 +53,7 @@ async function messageListener(event: MessageEvent) {
}
window.addEventListener('message', messageListener);
window.postMessage({
postMessage({
source: 'react-devtools-hook-settings-injector',
payload: {handshake: true},
});

View File

@@ -1,39 +1,46 @@
/** @flow */
import type {UnknownMessageEvent} from './messages';
import type {DevToolsHookSettings} from 'react-devtools-shared/src/backend/types';
import {installHook} from 'react-devtools-shared/src/hook';
import {
getIfReloadedAndProfiling,
getProfilingSettings,
} from 'react-devtools-shared/src/utils';
import {postMessage} from './messages';
let resolveHookSettingsInjection;
let resolveHookSettingsInjection: (settings: DevToolsHookSettings) => void;
function messageListener(event: MessageEvent) {
function messageListener(event: UnknownMessageEvent) {
if (event.source !== window) {
return;
}
if (event.data.source === 'react-devtools-hook-settings-injector') {
const payload = event.data.payload;
// In case handshake message was sent prior to hookSettingsInjector execution
// We can't guarantee order
if (event.data.payload.handshake) {
window.postMessage({
if (payload.handshake) {
postMessage({
source: 'react-devtools-hook-installer',
payload: {handshake: true},
});
} else if (event.data.payload.settings) {
} else if (payload.settings) {
window.removeEventListener('message', messageListener);
resolveHookSettingsInjection(event.data.payload.settings);
resolveHookSettingsInjection(payload.settings);
}
}
}
// Avoid double execution
if (!window.hasOwnProperty('__REACT_DEVTOOLS_GLOBAL_HOOK__')) {
const hookSettingsPromise = new Promise(resolve => {
const hookSettingsPromise = new Promise<DevToolsHookSettings>(resolve => {
resolveHookSettingsInjection = resolve;
});
window.addEventListener('message', messageListener);
window.postMessage({
postMessage({
source: 'react-devtools-hook-installer',
payload: {handshake: true},
});

View File

@@ -0,0 +1,42 @@
/** @flow */
import type {DevToolsHookSettings} from 'react-devtools-shared/src/backend/types';
export function postMessage(event: UnknownMessageEventData): void {
window.postMessage(event);
}
export interface UnknownMessageEvent
extends MessageEvent<UnknownMessageEventData> {}
export type UnknownMessageEventData =
| HookSettingsInjectorEventData
| HookInstallerEventData;
export type HookInstallerEventData = {
source: 'react-devtools-hook-installer',
payload: HookInstallerEventPayload,
};
export type HookInstallerEventPayload = HookInstallerEventPayloadHandshake;
export type HookInstallerEventPayloadHandshake = {
handshake: true,
};
export type HookSettingsInjectorEventData = {
source: 'react-devtools-hook-settings-injector',
payload: HookSettingsInjectorEventPayload,
};
export type HookSettingsInjectorEventPayload =
| HookSettingsInjectorEventPayloadHandshake
| HookSettingsInjectorEventPayloadSettings;
export type HookSettingsInjectorEventPayloadHandshake = {
handshake: true,
};
export type HookSettingsInjectorEventPayloadSettings = {
settings: DevToolsHookSettings,
};