mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-27 08:30:02 +03:00
Define page script to content script messages
This commit is contained in:
parent
425c78b8fc
commit
eb35441e17
|
@ -139,6 +139,7 @@ export interface PartialLiftedState<S, A extends Action<unknown>> {
|
||||||
readonly stagedActionIds: readonly number[];
|
readonly stagedActionIds: readonly number[];
|
||||||
readonly currentStateIndex: number;
|
readonly currentStateIndex: number;
|
||||||
readonly nextActionId: number;
|
readonly nextActionId: number;
|
||||||
|
readonly committedState?: S;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function startingFrom<S, A extends Action<unknown>>(
|
export function startingFrom<S, A extends Action<unknown>>(
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { getActionsArray } from '@redux-devtools/utils';
|
||||||
import { getLocalFilter, isFiltered, PartialLiftedState } from './filters';
|
import { getLocalFilter, isFiltered, PartialLiftedState } from './filters';
|
||||||
import importState from './importState';
|
import importState from './importState';
|
||||||
import generateId from './generateInstanceId';
|
import generateId from './generateInstanceId';
|
||||||
import { PageScriptToContentScriptMessage } from '../../browser/extension/inject/contentScript';
|
|
||||||
import { Config } from '../../browser/extension/inject/pageScript';
|
import { Config } from '../../browser/extension/inject/pageScript';
|
||||||
import { Action } from 'redux';
|
import { Action } from 'redux';
|
||||||
import { LiftedState, PerformAction } from '@redux-devtools/instrument';
|
import { LiftedState, PerformAction } from '@redux-devtools/instrument';
|
||||||
|
@ -107,7 +106,100 @@ export function getSerializeParameter(
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function post(message: PageScriptToContentScriptMessage) {
|
interface InitInstancePageScriptToContentScriptMessage {
|
||||||
|
readonly type: 'INIT_INSTANCE';
|
||||||
|
readonly instanceId: number;
|
||||||
|
readonly source: typeof source;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DisconnectMessage {
|
||||||
|
readonly type: 'DISCONNECT';
|
||||||
|
readonly source: typeof source;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InitMessage<S, A extends Action<unknown>> {
|
||||||
|
readonly type: 'INIT';
|
||||||
|
readonly payload: string;
|
||||||
|
readonly instanceId: number;
|
||||||
|
readonly source: typeof source;
|
||||||
|
action?: string;
|
||||||
|
name?: string | undefined;
|
||||||
|
liftedState?: LiftedState<S, A, unknown>;
|
||||||
|
libConfig?: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SerializedPartialLiftedState<S, A extends Action<unknown>> {
|
||||||
|
readonly stagedActionIds: readonly number[];
|
||||||
|
readonly currentStateIndex: number;
|
||||||
|
readonly nextActionId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SerializedPartialStateMessage<S, A extends Action<unknown>> {
|
||||||
|
readonly type: 'PARTIAL_STATE';
|
||||||
|
readonly payload: SerializedPartialLiftedState<S, A>;
|
||||||
|
readonly source: typeof source;
|
||||||
|
readonly instanceId: number;
|
||||||
|
readonly maxAge: number;
|
||||||
|
readonly actionsById: string;
|
||||||
|
readonly computedStates: string;
|
||||||
|
readonly committedState: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SerializedExportMessage {
|
||||||
|
readonly type: 'EXPORT';
|
||||||
|
readonly payload: string;
|
||||||
|
readonly committedState: string | undefined;
|
||||||
|
readonly source: typeof source;
|
||||||
|
readonly instanceId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SerializedActionMessage {
|
||||||
|
readonly type: 'ACTION';
|
||||||
|
readonly payload: string;
|
||||||
|
readonly source: typeof source;
|
||||||
|
readonly instanceId: number;
|
||||||
|
readonly action: string;
|
||||||
|
readonly maxAge: number;
|
||||||
|
readonly nextActionId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SerializedStateMessage<S, A extends Action<unknown>> {
|
||||||
|
readonly type: 'STATE';
|
||||||
|
readonly payload: Omit<
|
||||||
|
LiftedState<S, A, unknown>,
|
||||||
|
'actionsById' | 'computedStates' | 'committedState'
|
||||||
|
>;
|
||||||
|
readonly source: typeof source;
|
||||||
|
readonly instanceId: number;
|
||||||
|
readonly libConfig?: unknown;
|
||||||
|
readonly actionsById: string;
|
||||||
|
readonly computedStates: string;
|
||||||
|
readonly committedState: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PageScriptToContentScriptMessageWithoutDisconnect<
|
||||||
|
S,
|
||||||
|
A extends Action<unknown>
|
||||||
|
> =
|
||||||
|
| InitInstancePageScriptToContentScriptMessage
|
||||||
|
| InitMessage<S, A>
|
||||||
|
| LiftedMessage
|
||||||
|
| SerializedPartialStateMessage<S, A>
|
||||||
|
| SerializedExportMessage
|
||||||
|
| SerializedActionMessage
|
||||||
|
| SerializedStateMessage<S, A>
|
||||||
|
| ErrorMessage
|
||||||
|
| InitInstanceMessage
|
||||||
|
| GetReportMessage
|
||||||
|
| StopMessage;
|
||||||
|
|
||||||
|
export type PageScriptToContentScriptMessage<S, A extends Action<unknown>> =
|
||||||
|
| PageScriptToContentScriptMessageWithoutDisconnect<S, A>
|
||||||
|
| DisconnectMessage;
|
||||||
|
|
||||||
|
function post<S, A extends Action<unknown>>(
|
||||||
|
message: PageScriptToContentScriptMessage<S, A>
|
||||||
|
) {
|
||||||
window.postMessage(message, '*');
|
window.postMessage(message, '*');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +256,7 @@ function amendActionType(
|
||||||
return { action, timestamp, stack };
|
return { action, timestamp, stack };
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LiftedMessage {
|
export interface LiftedMessage {
|
||||||
readonly type: 'LIFTED';
|
readonly type: 'LIFTED';
|
||||||
readonly liftedState: { readonly isPaused: boolean };
|
readonly liftedState: { readonly isPaused: boolean };
|
||||||
readonly instanceId: number;
|
readonly instanceId: number;
|
||||||
|
@ -250,28 +342,52 @@ export function toContentScript<S, A extends Action<unknown>>(
|
||||||
serializeAction?: Serialize | undefined
|
serializeAction?: Serialize | undefined
|
||||||
) {
|
) {
|
||||||
if (message.type === 'ACTION') {
|
if (message.type === 'ACTION') {
|
||||||
message.action = stringify(message.action, serializeAction);
|
post({
|
||||||
message.payload = stringify(message.payload, serializeState);
|
...message,
|
||||||
} else if (message.type === 'STATE' || message.type === 'PARTIAL_STATE') {
|
action: stringify(message.action, serializeAction),
|
||||||
|
payload: stringify(message.payload, serializeState),
|
||||||
|
});
|
||||||
|
} else if (message.type === 'STATE') {
|
||||||
const { actionsById, computedStates, committedState, ...rest } =
|
const { actionsById, computedStates, committedState, ...rest } =
|
||||||
message.payload;
|
message.payload;
|
||||||
message.payload = rest;
|
post({
|
||||||
message.actionsById = stringify(actionsById, serializeAction);
|
...message,
|
||||||
message.computedStates = stringify(computedStates, serializeState);
|
payload: rest,
|
||||||
message.committedState = typeof committedState !== 'undefined';
|
actionsById: stringify(actionsById, serializeAction),
|
||||||
|
computedStates: stringify(computedStates, serializeState),
|
||||||
|
committedState: typeof committedState !== 'undefined',
|
||||||
|
});
|
||||||
|
} else if (message.type === 'PARTIAL_STATE') {
|
||||||
|
const { actionsById, computedStates, committedState, ...rest } =
|
||||||
|
message.payload;
|
||||||
|
post({
|
||||||
|
...message,
|
||||||
|
payload: rest,
|
||||||
|
actionsById: stringify(actionsById, serializeAction),
|
||||||
|
computedStates: stringify(computedStates, serializeState),
|
||||||
|
committedState: typeof committedState !== 'undefined',
|
||||||
|
});
|
||||||
} else if (message.type === 'EXPORT') {
|
} else if (message.type === 'EXPORT') {
|
||||||
message.payload = stringify(message.payload, serializeAction);
|
post({
|
||||||
if (typeof message.committedState !== 'undefined') {
|
...message,
|
||||||
message.committedState = stringify(
|
payload: stringify(message.payload, serializeAction),
|
||||||
message.committedState,
|
committedState:
|
||||||
serializeState
|
typeof message.committedState !== 'undefined'
|
||||||
);
|
? stringify(message.committedState, serializeState)
|
||||||
}
|
: (message.committedState as undefined),
|
||||||
}
|
});
|
||||||
|
} else {
|
||||||
post(message);
|
post(message);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function sendMessage(action, state, config, instanceId, name) {
|
export function sendMessage(
|
||||||
|
action,
|
||||||
|
state,
|
||||||
|
config: Config,
|
||||||
|
instanceId?: number,
|
||||||
|
name?: string
|
||||||
|
) {
|
||||||
let amendedAction = action;
|
let amendedAction = action;
|
||||||
if (typeof config !== 'object') {
|
if (typeof config !== 'object') {
|
||||||
// Legacy: sending actions not from connected part
|
// Legacy: sending actions not from connected part
|
||||||
|
@ -427,8 +543,11 @@ export function connect(preConfig) {
|
||||||
sendMessage(amendedAction, amendedState, config);
|
sendMessage(amendedAction, amendedState, config);
|
||||||
};
|
};
|
||||||
|
|
||||||
const init = (state, liftedData) => {
|
const init = <S, A extends Action<unknown>>(
|
||||||
const message = {
|
state: S,
|
||||||
|
liftedData: LiftedState<S, A, unknown>
|
||||||
|
) => {
|
||||||
|
const message: InitMessage<S, A> = {
|
||||||
type: 'INIT',
|
type: 'INIT',
|
||||||
payload: stringify(state, config.serialize),
|
payload: stringify(state, config.serialize),
|
||||||
instanceId: id,
|
instanceId: id,
|
||||||
|
@ -454,8 +573,8 @@ export function connect(preConfig) {
|
||||||
post(message);
|
post(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
const error = (payload) => {
|
const error = (payload: unknown) => {
|
||||||
post({ type: 'ERROR', payload, id, source });
|
post({ type: 'ERROR', payload, instanceId: id, source });
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('message', handleMessages, false);
|
window.addEventListener('message', handleMessages, false);
|
||||||
|
|
|
@ -4,6 +4,11 @@ import {
|
||||||
isAllowed,
|
isAllowed,
|
||||||
} from '../options/syncOptions';
|
} from '../options/syncOptions';
|
||||||
import { TabMessage } from '../../../app/middlewares/api';
|
import { TabMessage } from '../../../app/middlewares/api';
|
||||||
|
import {
|
||||||
|
PageScriptToContentScriptMessage,
|
||||||
|
PageScriptToContentScriptMessageWithoutDisconnect,
|
||||||
|
} from '../../../app/api';
|
||||||
|
import { Action } from 'redux';
|
||||||
const source = '@devtools-extension';
|
const source = '@devtools-extension';
|
||||||
const pageSource = '@devtools-page';
|
const pageSource = '@devtools-page';
|
||||||
// Chrome message limit is 64 MB, but we're using 32 MB to include other object's parts
|
// Chrome message limit is 64 MB, but we're using 32 MB to include other object's parts
|
||||||
|
@ -64,21 +69,46 @@ function handleDisconnect() {
|
||||||
bg = undefined;
|
bg = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function tryCatch<A>(
|
interface SplitMessageBase {
|
||||||
fn: (args: PageScriptToContentScriptMessage) => void,
|
readonly type?: never;
|
||||||
args: PageScriptToContentScriptMessage
|
}
|
||||||
|
|
||||||
|
interface SplitMessageStart extends SplitMessageBase {
|
||||||
|
readonly split: 'start';
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SplitMessageChunk extends SplitMessageBase {
|
||||||
|
readonly instanceId: number;
|
||||||
|
readonly source: typeof pageSource;
|
||||||
|
readonly split: 'chunk';
|
||||||
|
readonly chunk: [string, string];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SplitMessageEnd extends SplitMessageBase {
|
||||||
|
readonly instanceId: number;
|
||||||
|
readonly source: typeof pageSource;
|
||||||
|
readonly split: 'end';
|
||||||
|
}
|
||||||
|
|
||||||
|
type SplitMessage = SplitMessageStart | SplitMessageChunk | SplitMessageEnd;
|
||||||
|
|
||||||
|
function tryCatch<S, A extends Action<unknown>>(
|
||||||
|
fn: (args: PageScriptToContentScriptMessage<S, A> | SplitMessage) => void,
|
||||||
|
args: PageScriptToContentScriptMessageWithoutDisconnect<S, A>
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
return fn(args);
|
return fn(args);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.message === 'Message length exceeded maximum allowed length.') {
|
if (err.message === 'Message length exceeded maximum allowed length.') {
|
||||||
const instanceId = args.instanceId;
|
const instanceId = args.instanceId;
|
||||||
const newArgs = { split: 'start' };
|
const newArgs: SplitMessageStart = {
|
||||||
const toSplit = [];
|
split: 'start',
|
||||||
|
};
|
||||||
|
const toSplit: [string, string][] = [];
|
||||||
let size = 0;
|
let size = 0;
|
||||||
let arg;
|
let arg;
|
||||||
Object.keys(args).map((key) => {
|
Object.keys(args).map((key) => {
|
||||||
arg = args[key];
|
arg = args[key as keyof typeof args];
|
||||||
if (typeof arg === 'string') {
|
if (typeof arg === 'string') {
|
||||||
size += arg.length;
|
size += arg.length;
|
||||||
if (size > maxChromeMsgSize) {
|
if (size > maxChromeMsgSize) {
|
||||||
|
@ -86,7 +116,7 @@ function tryCatch<A>(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
newArgs[key] = arg;
|
newArgs[key as keyof typeof newArgs] = arg;
|
||||||
});
|
});
|
||||||
fn(newArgs);
|
fn(newArgs);
|
||||||
for (let i = 0; i < toSplit.length; i++) {
|
for (let i = 0; i < toSplit.length; i++) {
|
||||||
|
@ -110,21 +140,6 @@ function tryCatch<A>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InitInstancePageScriptToContentScriptMessage {
|
|
||||||
readonly type: 'INIT_INSTANCE';
|
|
||||||
readonly instanceId: number;
|
|
||||||
readonly source: typeof pageSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DisconnectMessage {
|
|
||||||
readonly type: 'DISCONNECT';
|
|
||||||
readonly source: typeof pageSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PageScriptToContentScriptMessage =
|
|
||||||
| InitInstancePageScriptToContentScriptMessage
|
|
||||||
| DisconnectMessage;
|
|
||||||
|
|
||||||
interface InitInstanceContentScriptToBackgroundMessage {
|
interface InitInstanceContentScriptToBackgroundMessage {
|
||||||
readonly name: 'INIT_INSTANCE';
|
readonly name: 'INIT_INSTANCE';
|
||||||
readonly instanceId: number;
|
readonly instanceId: number;
|
||||||
|
@ -143,7 +158,9 @@ function postToBackground(message: ContentScriptToBackgroundMessage) {
|
||||||
bg!.postMessage(message);
|
bg!.postMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
function send(message: never) {
|
function send<S, A extends Action<unknown>>(
|
||||||
|
message: PageScriptToContentScriptMessage<S, A> | SplitMessage
|
||||||
|
) {
|
||||||
if (!connected) connect();
|
if (!connected) connect();
|
||||||
if (message.type === 'INIT_INSTANCE') {
|
if (message.type === 'INIT_INSTANCE') {
|
||||||
getOptionsFromBg();
|
getOptionsFromBg();
|
||||||
|
@ -154,7 +171,9 @@ function send(message: never) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resend messages from the page to the background script
|
// Resend messages from the page to the background script
|
||||||
function handleMessages(event: MessageEvent<PageScriptToContentScriptMessage>) {
|
function handleMessages<S, A extends Action<unknown>>(
|
||||||
|
event: MessageEvent<PageScriptToContentScriptMessage<S, A>>
|
||||||
|
) {
|
||||||
if (!isAllowed()) return;
|
if (!isAllowed()) return;
|
||||||
if (!event || event.source !== window || typeof event.data !== 'object') {
|
if (!event || event.source !== window || typeof event.data !== 'object') {
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user