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