mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-06-20 04:53:20 +03:00
Update options to not rely on background page access
This commit is contained in:
parent
a7f6dbc0a2
commit
4b43b513d3
|
@ -1,7 +1,7 @@
|
||||||
import configureStore from './store/backgroundStore';
|
import configureStore from './store/backgroundStore';
|
||||||
import openDevToolsWindow, { DevToolsPosition } from './openWindow';
|
import openDevToolsWindow, { DevToolsPosition } from './openWindow';
|
||||||
import { createMenu, removeMenu } from './contextMenus';
|
import { createMenu, removeMenu } from './contextMenus';
|
||||||
import createSyncOptions from '../options/syncOptions';
|
import { getOptions } from '../options/syncOptions';
|
||||||
|
|
||||||
// Expose the extension's store globally to access it from the windows
|
// Expose the extension's store globally to access it from the windows
|
||||||
// via chrome.runtime.getBackgroundPage
|
// via chrome.runtime.getBackgroundPage
|
||||||
|
@ -16,7 +16,7 @@ chrome.commands.onCommand.addListener((shortcut) => {
|
||||||
chrome.runtime.onInstalled.addListener(() => {
|
chrome.runtime.onInstalled.addListener(() => {
|
||||||
chrome.action.disable();
|
chrome.action.disable();
|
||||||
|
|
||||||
createSyncOptions().get((option) => {
|
getOptions((option) => {
|
||||||
if (option.showContextMenus) createMenu();
|
if (option.showContextMenus) createMenu();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,10 +11,7 @@ import {
|
||||||
TOGGLE_PERSIST,
|
TOGGLE_PERSIST,
|
||||||
UPDATE_STATE,
|
UPDATE_STATE,
|
||||||
} from '@redux-devtools/app';
|
} from '@redux-devtools/app';
|
||||||
import createSyncOptions, {
|
import type { Options, OptionsMessage } from '../../options/syncOptions';
|
||||||
Options,
|
|
||||||
OptionsMessage,
|
|
||||||
} from '../../options/syncOptions';
|
|
||||||
import openDevToolsWindow, { DevToolsPosition } from '../openWindow';
|
import openDevToolsWindow, { DevToolsPosition } from '../openWindow';
|
||||||
import { getReport } from '../logging';
|
import { getReport } from '../logging';
|
||||||
import { Action, Dispatch, Middleware } from 'redux';
|
import { Action, Dispatch, Middleware } from 'redux';
|
||||||
|
@ -51,6 +48,11 @@ interface StopAction extends TabMessageBase {
|
||||||
readonly id?: never;
|
readonly id?: never;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface OptionsAction {
|
||||||
|
readonly type: 'OPTIONS';
|
||||||
|
readonly options: Options;
|
||||||
|
}
|
||||||
|
|
||||||
interface DispatchAction extends TabMessageBase {
|
interface DispatchAction extends TabMessageBase {
|
||||||
readonly type: 'DISPATCH';
|
readonly type: 'DISPATCH';
|
||||||
readonly action: AppDispatchAction;
|
readonly action: AppDispatchAction;
|
||||||
|
@ -196,7 +198,7 @@ interface SplitUpdateStateAction<S, A extends Action<string>> {
|
||||||
export type TabMessage =
|
export type TabMessage =
|
||||||
| StartAction
|
| StartAction
|
||||||
| StopAction
|
| StopAction
|
||||||
| OptionsMessage
|
| OptionsAction
|
||||||
| DispatchAction
|
| DispatchAction
|
||||||
| ImportAction
|
| ImportAction
|
||||||
| ActionAction
|
| ActionAction
|
||||||
|
@ -414,14 +416,11 @@ function toContentScript(messageBody: ToContentScriptMessage) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toAllTabs(msg: TabMessage) {
|
function toAllTabs(msg: TabMessage) {
|
||||||
const tabs = connections.tab;
|
for (const tabPort of Object.values(connections.tab)) {
|
||||||
Object.keys(tabs).forEach((id) => {
|
tabPort.postMessage(msg);
|
||||||
tabs[id].postMessage(msg);
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const syncOptions = createSyncOptions(toAllTabs);
|
|
||||||
|
|
||||||
function monitorInstances(shouldMonitor: boolean, id?: string) {
|
function monitorInstances(shouldMonitor: boolean, id?: string) {
|
||||||
if (!id && isMonitored === shouldMonitor) return;
|
if (!id && isMonitored === shouldMonitor) return;
|
||||||
const action = {
|
const action = {
|
||||||
|
@ -463,26 +462,17 @@ interface OpenOptionsMessage {
|
||||||
readonly type: 'OPEN_OPTIONS';
|
readonly type: 'OPEN_OPTIONS';
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GetOptionsMessage {
|
export type SingleMessage = OpenMessage | OpenOptionsMessage | OptionsMessage;
|
||||||
readonly type: 'GET_OPTIONS';
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SingleMessage =
|
|
||||||
| OpenMessage
|
|
||||||
| OpenOptionsMessage
|
|
||||||
| GetOptionsMessage;
|
|
||||||
|
|
||||||
type BackgroundStoreMessage<S, A extends Action<string>> =
|
type BackgroundStoreMessage<S, A extends Action<string>> =
|
||||||
| PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance<S, A>
|
| PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance<S, A>
|
||||||
| SplitMessage
|
| SplitMessage
|
||||||
| SingleMessage;
|
| SingleMessage;
|
||||||
type BackgroundStoreResponse = { readonly options: Options };
|
|
||||||
|
|
||||||
// Receive messages from content scripts
|
// Receive messages from content scripts
|
||||||
function messaging<S, A extends Action<string>>(
|
function messaging<S, A extends Action<string>>(
|
||||||
request: BackgroundStoreMessage<S, A>,
|
request: BackgroundStoreMessage<S, A>,
|
||||||
sender: chrome.runtime.MessageSender,
|
sender: chrome.runtime.MessageSender,
|
||||||
sendResponse?: (response?: BackgroundStoreResponse) => void,
|
|
||||||
) {
|
) {
|
||||||
let tabId = getId(sender);
|
let tabId = getId(sender);
|
||||||
if (!tabId) return;
|
if (!tabId) return;
|
||||||
|
@ -498,10 +488,8 @@ function messaging<S, A extends Action<string>>(
|
||||||
chrome.runtime.openOptionsPage();
|
chrome.runtime.openOptionsPage();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (request.type === 'GET_OPTIONS') {
|
if (request.type === 'OPTIONS') {
|
||||||
syncOptions.get((options) => {
|
toAllTabs({ type: 'OPTIONS', options: request.options });
|
||||||
sendResponse!({ options });
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (request.type === 'GET_REPORT') {
|
if (request.type === 'GET_REPORT') {
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import '../chromeApiMock';
|
import '../chromeApiMock';
|
||||||
import {
|
import {
|
||||||
injectOptions,
|
getOptions,
|
||||||
getOptionsFromBg,
|
|
||||||
isAllowed,
|
isAllowed,
|
||||||
|
Options,
|
||||||
|
prefetchOptions,
|
||||||
|
prepareOptionsForPage,
|
||||||
} from '../options/syncOptions';
|
} from '../options/syncOptions';
|
||||||
import type { TabMessage } from '../background/store/apiMiddleware';
|
import type { TabMessage } from '../background/store/apiMiddleware';
|
||||||
import type {
|
import type {
|
||||||
|
@ -84,6 +86,13 @@ interface UpdateAction {
|
||||||
readonly source: typeof source;
|
readonly source: typeof source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface OptionsAction {
|
||||||
|
readonly type: 'OPTIONS';
|
||||||
|
readonly options: Options;
|
||||||
|
readonly id: undefined;
|
||||||
|
readonly source: typeof source;
|
||||||
|
}
|
||||||
|
|
||||||
export type ContentScriptToPageScriptMessage =
|
export type ContentScriptToPageScriptMessage =
|
||||||
| StartAction
|
| StartAction
|
||||||
| StopAction
|
| StopAction
|
||||||
|
@ -91,7 +100,8 @@ export type ContentScriptToPageScriptMessage =
|
||||||
| ImportAction
|
| ImportAction
|
||||||
| ActionAction
|
| ActionAction
|
||||||
| ExportAction
|
| ExportAction
|
||||||
| UpdateAction;
|
| UpdateAction
|
||||||
|
| OptionsAction;
|
||||||
|
|
||||||
interface ImportStatePayload<S, A extends Action<string>> {
|
interface ImportStatePayload<S, A extends Action<string>> {
|
||||||
readonly type: 'IMPORT_STATE';
|
readonly type: 'IMPORT_STATE';
|
||||||
|
@ -112,6 +122,7 @@ export type ListenerMessage<S, A extends Action<string>> =
|
||||||
| ActionAction
|
| ActionAction
|
||||||
| ExportAction
|
| ExportAction
|
||||||
| UpdateAction
|
| UpdateAction
|
||||||
|
| OptionsAction
|
||||||
| ImportStateDispatchAction<S, A>;
|
| ImportStateDispatchAction<S, A>;
|
||||||
|
|
||||||
function postToPageScript(message: ContentScriptToPageScriptMessage) {
|
function postToPageScript(message: ContentScriptToPageScriptMessage) {
|
||||||
|
@ -156,8 +167,13 @@ function connect() {
|
||||||
source,
|
source,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if ('options' in message) {
|
} else if (message.type === 'OPTIONS') {
|
||||||
injectOptions(message.options);
|
postToPageScript({
|
||||||
|
type: message.type,
|
||||||
|
options: prepareOptionsForPage(message.options),
|
||||||
|
id: undefined,
|
||||||
|
source,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
postToPageScript({
|
postToPageScript({
|
||||||
type: message.type,
|
type: message.type,
|
||||||
|
@ -289,7 +305,14 @@ function send<S, A extends Action<string>>(
|
||||||
) {
|
) {
|
||||||
if (!connected) connect();
|
if (!connected) connect();
|
||||||
if (message.type === 'INIT_INSTANCE') {
|
if (message.type === 'INIT_INSTANCE') {
|
||||||
getOptionsFromBg();
|
getOptions((options) => {
|
||||||
|
postToPageScript({
|
||||||
|
type: 'OPTIONS',
|
||||||
|
options: prepareOptionsForPage(options),
|
||||||
|
id: undefined,
|
||||||
|
source,
|
||||||
|
});
|
||||||
|
});
|
||||||
postToBackground({ name: 'INIT_INSTANCE', instanceId: message.instanceId });
|
postToBackground({ name: 'INIT_INSTANCE', instanceId: message.instanceId });
|
||||||
} else {
|
} else {
|
||||||
postToBackground({ name: 'RELAY', message });
|
postToBackground({ name: 'RELAY', message });
|
||||||
|
@ -317,6 +340,8 @@ function handleMessages<S, A extends Action<string>>(
|
||||||
tryCatch(send, message);
|
tryCatch(send, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prefetchOptions();
|
||||||
|
|
||||||
window.addEventListener('message', handleMessages, false);
|
window.addEventListener('message', handleMessages, false);
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
|
|
|
@ -2,22 +2,25 @@ import '../chromeApiMock';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
import OptionsComponent from './Options';
|
import OptionsComponent from './Options';
|
||||||
import { Options } from './syncOptions';
|
import {
|
||||||
|
getOptions,
|
||||||
|
Options,
|
||||||
|
OptionsMessage,
|
||||||
|
saveOption,
|
||||||
|
subscribeToOptions,
|
||||||
|
} from './syncOptions';
|
||||||
|
|
||||||
chrome.runtime.getBackgroundPage((background) => {
|
subscribeToOptions((options) => {
|
||||||
const syncOptions = background!.syncOptions;
|
const message: OptionsMessage = { type: 'OPTIONS', options };
|
||||||
|
chrome.runtime.sendMessage(message);
|
||||||
const saveOption = <K extends keyof Options>(name: K, value: Options[K]) => {
|
});
|
||||||
syncOptions.save(name, value);
|
|
||||||
};
|
const renderOptions = (options: Options) => {
|
||||||
|
const root = createRoot(document.getElementById('root')!);
|
||||||
const renderOptions = (options: Options) => {
|
root.render(<OptionsComponent options={options} saveOption={saveOption} />);
|
||||||
const root = createRoot(document.getElementById('root')!);
|
};
|
||||||
root.render(<OptionsComponent options={options} saveOption={saveOption} />);
|
|
||||||
};
|
subscribeToOptions(renderOptions);
|
||||||
|
getOptions((options) => {
|
||||||
syncOptions.subscribe(renderOptions);
|
renderOptions(options);
|
||||||
syncOptions.get((options) => {
|
|
||||||
renderOptions(options);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -38,21 +38,22 @@ let options: Options | undefined;
|
||||||
let subscribers: ((options: Options) => void)[] = [];
|
let subscribers: ((options: Options) => void)[] = [];
|
||||||
|
|
||||||
export interface OptionsMessage {
|
export interface OptionsMessage {
|
||||||
|
readonly type: 'OPTIONS';
|
||||||
readonly options: Options;
|
readonly options: Options;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ToAllTabs = (msg: OptionsMessage) => void;
|
export const saveOption = <K extends keyof Options>(
|
||||||
|
key: K,
|
||||||
const save =
|
value: Options[K],
|
||||||
(toAllTabs: ToAllTabs | undefined) =>
|
) => {
|
||||||
<K extends keyof Options>(key: K, value: Options[K]) => {
|
let obj: { [K1 in keyof Options]?: Options[K1] } = {};
|
||||||
let obj: { [K1 in keyof Options]?: Options[K1] } = {};
|
obj[key] = value;
|
||||||
obj[key] = value;
|
chrome.storage.sync.set(obj);
|
||||||
chrome.storage.sync.set(obj);
|
options![key] = value;
|
||||||
options![key] = value;
|
for (const subscriber of subscribers) {
|
||||||
toAllTabs!({ options: options! });
|
subscriber(options!);
|
||||||
subscribers.forEach((s) => s(options!));
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const migrateOldOptions = (oldOptions: OldOrNewOptions): Options => ({
|
const migrateOldOptions = (oldOptions: OldOrNewOptions): Options => ({
|
||||||
...oldOptions,
|
...oldOptions,
|
||||||
|
@ -71,7 +72,7 @@ const migrateOldOptions = (oldOptions: OldOrNewOptions): Options => ({
|
||||||
: oldOptions.filter,
|
: oldOptions.filter,
|
||||||
});
|
});
|
||||||
|
|
||||||
const get = (callback: (options: Options) => void) => {
|
export const getOptions = (callback: (options: Options) => void) => {
|
||||||
if (options) callback(options);
|
if (options) callback(options);
|
||||||
else {
|
else {
|
||||||
chrome.storage.sync.get(
|
chrome.storage.sync.get(
|
||||||
|
@ -98,67 +99,29 @@ const get = (callback: (options: Options) => void) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const subscribe = (callback: (options: Options) => void) => {
|
export const prefetchOptions = () => getOptions(() => {});
|
||||||
|
|
||||||
|
export const subscribeToOptions = (callback: (options: Options) => void) => {
|
||||||
subscribers = subscribers.concat(callback);
|
subscribers = subscribers.concat(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
const toReg = (str: string) =>
|
const toReg = (str: string) =>
|
||||||
str !== '' ? str.split('\n').filter(Boolean).join('|') : null;
|
str !== '' ? str.split('\n').filter(Boolean).join('|') : null;
|
||||||
|
|
||||||
export const injectOptions = (newOptions: Options) => {
|
export const prepareOptionsForPage = (options: Options): Options => ({
|
||||||
if (!newOptions) return;
|
...options,
|
||||||
|
allowlist:
|
||||||
options = {
|
options.filter !== FilterState.DO_NOT_FILTER
|
||||||
...newOptions,
|
? toReg(options.allowlist)!
|
||||||
allowlist:
|
: options.allowlist,
|
||||||
newOptions.filter !== FilterState.DO_NOT_FILTER
|
denylist:
|
||||||
? toReg(newOptions.allowlist)!
|
options.filter !== FilterState.DO_NOT_FILTER
|
||||||
: newOptions.allowlist,
|
? toReg(options.denylist)!
|
||||||
denylist:
|
: options.denylist,
|
||||||
newOptions.filter !== FilterState.DO_NOT_FILTER
|
});
|
||||||
? toReg(newOptions.denylist)!
|
|
||||||
: newOptions.denylist,
|
|
||||||
};
|
|
||||||
let s = document.createElement('script');
|
|
||||||
s.type = 'text/javascript';
|
|
||||||
s.appendChild(
|
|
||||||
document.createTextNode(
|
|
||||||
'window.devToolsOptions = Object.assign(window.devToolsOptions||{},' +
|
|
||||||
JSON.stringify(options) +
|
|
||||||
');',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
(document.head || document.documentElement).appendChild(s);
|
|
||||||
s.parentNode!.removeChild(s);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getOptionsFromBg = () => {
|
|
||||||
/* chrome.runtime.sendMessage({ type: 'GET_OPTIONS' }, response => {
|
|
||||||
if (response && response.options) injectOptions(response.options);
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
get((newOptions) => {
|
|
||||||
injectOptions(newOptions);
|
|
||||||
}); // Legacy
|
|
||||||
};
|
|
||||||
|
|
||||||
export const isAllowed = (localOptions = options) =>
|
export const isAllowed = (localOptions = options) =>
|
||||||
!localOptions ||
|
!localOptions ||
|
||||||
localOptions.inject ||
|
localOptions.inject ||
|
||||||
!localOptions.urls ||
|
!localOptions.urls ||
|
||||||
location.href.match(toReg(localOptions.urls)!);
|
location.href.match(toReg(localOptions.urls)!);
|
||||||
|
|
||||||
export interface SyncOptions {
|
|
||||||
readonly save: <K extends keyof Options>(key: K, value: Options[K]) => void;
|
|
||||||
readonly get: (callback: (options: Options) => void) => void;
|
|
||||||
readonly subscribe: (callback: (options: Options) => void) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function createSyncOptions(toAllTabs?: ToAllTabs): SyncOptions {
|
|
||||||
if (toAllTabs && !options) get(() => {}); // Initialize
|
|
||||||
return {
|
|
||||||
save: save(toAllTabs),
|
|
||||||
get: get,
|
|
||||||
subscribe: subscribe,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -432,6 +432,13 @@ function __REDUX_DEVTOOLS_EXTENSION__<S, A extends Action<string>>(
|
||||||
serializeAction,
|
serializeAction,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
case 'OPTIONS':
|
||||||
|
window.devToolsOptions = Object.assign(
|
||||||
|
window.devToolsOptions || {},
|
||||||
|
message.options,
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user