From 9c7565969239552e4ab4c72b63108423686b9b43 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Tue, 11 Jun 2024 08:54:40 -0400 Subject: [PATCH 01/23] Update Chrome manifest.json --- extension/chrome/manifest.json | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/extension/chrome/manifest.json b/extension/chrome/manifest.json index 69b3f14a3f..5931e9f3b1 100644 --- a/extension/chrome/manifest.json +++ b/extension/chrome/manifest.json @@ -3,8 +3,8 @@ "name": "Redux DevTools", "description": "Redux DevTools for debugging application's state changes.", "homepage_url": "https://github.com/reduxjs/redux-devtools", - "manifest_version": 2, - "page_action": { + "manifest_version": 3, + "action": { "default_icon": "img/logo/gray.png", "default_title": "Redux DevTools", "default_popup": "window.html#popup" @@ -34,12 +34,10 @@ "128": "img/logo/128x128.png" }, "options_ui": { - "page": "options.html", - "chrome_style": true + "page": "options.html" }, "background": { - "scripts": ["background.bundle.js"], - "persistent": false + "service_worker": "background.bundle.js" }, "content_scripts": [ { @@ -51,19 +49,21 @@ } ], "devtools_page": "devtools.html", - "web_accessible_resources": ["page.bundle.js"], + "web_accessible_resources": [ + { + "resources": ["page.bundle.js"], + "matches": [""], + "extension_ids": [] + } + ], "externally_connectable": { "ids": ["*"] }, - "permissions": [ - "notifications", - "contextMenus", - "storage", - "file:///*", - "http://*/*", - "https://*/*" - ], - "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;", + "permissions": ["notifications", "contextMenus", "storage"], + "host_permissions": ["file:///*", "http://*/*", "https://*/*"], + "content_security_policy": { + "extension_pages": "script-src 'self'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;" + }, "update_url": "https://clients2.google.com/service/update2/crx", "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsdJEPwY92xUACA9CcDBDBmbdbp8Ap3cKQ0DJTUuVQvqb4FQAv8RtKY3iUjGvdwuAcSJQIZwHXcP2aNDH3TiFik/NhRK2GRW8X3OZyTdkuDueABGP2KEX8q1WQDgjX/rPIinGYztUrvoICw/UerMPwNW62jwGoVU3YhAGf+15CgX2Y6a4tppnf/+1mPedKPidh0RsM+aJY98rX+r1SPAHPcGzMjocLkqcT75DZBXer8VQN14tOOzRCd6T6oy7qm7eWru8lJwcY66qMQvhk0osqEod2G3nA7aTWpmqPFS66VEiecP9PgZlp8gQdgZ3dFhA62exydlD55JuRhiMIR63yQIDAQAB" } From 21c9dcff47bf463249679116ecac1f337cdbc6ad Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Sat, 10 Aug 2024 11:50:34 -0400 Subject: [PATCH 02/23] Remove use of window in background --- extension/src/background/index.ts | 13 +----- extension/src/background/logging.ts | 3 +- .../src/background/store/apiMiddleware.ts | 44 ++++++++----------- 3 files changed, 23 insertions(+), 37 deletions(-) diff --git a/extension/src/background/index.ts b/extension/src/background/index.ts index 0fc24ea7d4..8b81adf3c3 100644 --- a/extension/src/background/index.ts +++ b/extension/src/background/index.ts @@ -1,20 +1,11 @@ -import '../chromeApiMock'; -import { Store } from 'redux'; -import configureStore, { BackgroundAction } from './store/backgroundStore'; +import configureStore from './store/backgroundStore'; import openDevToolsWindow, { DevToolsPosition } from './openWindow'; import { createMenu, removeMenu } from './contextMenus'; import syncOptions from '../options/syncOptions'; -import { BackgroundState } from './store/backgroundReducer'; - -declare global { - interface Window { - store: Store; - } -} // Expose the extension's store globally to access it from the windows // via chrome.runtime.getBackgroundPage -window.store = configureStore(); +export const store = configureStore(); // Listen for keyboard shortcuts chrome.commands.onCommand.addListener((shortcut) => { diff --git a/extension/src/background/logging.ts b/extension/src/background/logging.ts index 22cdd00279..89d9376eb9 100644 --- a/extension/src/background/logging.ts +++ b/extension/src/background/logging.ts @@ -1,4 +1,5 @@ import { LIFTED_ACTION } from '@redux-devtools/app'; +import { store } from './index'; export function getReport( reportId: string, @@ -24,7 +25,7 @@ export function getReport( .then((json) => { const { payload, preloadedState } = json; if (!payload) return; - window.store.dispatch({ + store.dispatch({ type: LIFTED_ACTION, message: 'IMPORT', state: JSON.stringify({ payload, preloadedState }), diff --git a/extension/src/background/store/apiMiddleware.ts b/extension/src/background/store/apiMiddleware.ts index e88c841b42..ffedb28665 100644 --- a/extension/src/background/store/apiMiddleware.ts +++ b/extension/src/background/store/apiMiddleware.ts @@ -14,7 +14,6 @@ import { import syncOptions, { Options, OptionsMessage, - SyncOptions, } from '../../options/syncOptions'; import openDevToolsWindow, { DevToolsPosition } from '../openWindow'; import { getReport } from '../logging'; @@ -32,6 +31,7 @@ import { LiftedState } from '@redux-devtools/instrument'; import type { BackgroundAction, LiftedActionAction } from './backgroundStore'; import type { Position } from '../../pageScript/api/openWindow'; import type { BackgroundState } from './backgroundReducer'; +import { store } from '../index'; interface TabMessageBase { readonly type: string; @@ -346,7 +346,7 @@ function toContentScript(messageBody: ToContentScriptMessage) { type: message, action, state: nonReduxDispatch( - window.store, + store, message, instanceId, action as AppDispatchAction, @@ -360,7 +360,7 @@ function toContentScript(messageBody: ToContentScriptMessage) { type: message, action, state: nonReduxDispatch( - window.store, + store, message, instanceId, action as unknown as AppDispatchAction, @@ -374,7 +374,7 @@ function toContentScript(messageBody: ToContentScriptMessage) { type: message, action, state: nonReduxDispatch( - window.store, + store, message, instanceId, action as unknown as AppDispatchAction, @@ -388,7 +388,7 @@ function toContentScript(messageBody: ToContentScriptMessage) { type: message, action, state: nonReduxDispatch( - window.store, + store, message, instanceId, action as unknown as AppDispatchAction, @@ -402,7 +402,7 @@ function toContentScript(messageBody: ToContentScriptMessage) { type: message, action, state: nonReduxDispatch( - window.store, + store, message, instanceId, action as AppDispatchAction, @@ -434,7 +434,7 @@ function monitorInstances(shouldMonitor: boolean, id?: string) { } function getReducerError() { - const instancesState = window.store.getState().instances; + const instancesState = store.getState().instances; const payload = instancesState.states[instancesState.current]; const computedState = payload.computedStates[payload.currentStateIndex]; if (!computedState) return false; @@ -442,11 +442,11 @@ function getReducerError() { } function togglePersist() { - const state = window.store.getState(); + const state = store.getState(); if (state.instances.persisted) { Object.keys(state.instances.connections).forEach((id) => { if (connections.tab[id]) return; - window.store.dispatch({ type: REMOVE_INSTANCE, id }); + store.dispatch({ type: REMOVE_INSTANCE, id }); toMonitors({ type: 'NA', id }); }); } @@ -487,8 +487,8 @@ function messaging>( if (sender.frameId) tabId = `${tabId}-${sender.frameId}`; if (request.type === 'STOP') { - if (!Object.keys(window.store.getState().instances.connections).length) { - window.store.dispatch({ type: DISCONNECTED }); + if (!Object.keys(store.getState().instances.connections).length) { + store.dispatch({ type: DISCONNECTED }); } return; } @@ -497,7 +497,7 @@ function messaging>( return; } if (request.type === 'GET_OPTIONS') { - window.syncOptions.get((options) => { + syncOptionsToAllTabs.get((options) => { sendResponse!({ options }); }); return; @@ -560,7 +560,7 @@ function messaging>( if (request.instanceId) { action.request.instanceId = instanceId; } - window.store.dispatch(action); + store.dispatch(action); if (request.type === 'EXPORT') { toMonitors(action, tabId, true); @@ -580,8 +580,8 @@ function disconnect( if (p) p.onDisconnect.removeListener(disconnectListener); delete connections[type][id]; if (type === 'tab') { - if (!window.store.getState().instances.persisted) { - window.store.dispatch({ type: REMOVE_INSTANCE, id }); + if (!store.getState().instances.persisted) { + store.dispatch({ type: REMOVE_INSTANCE, id }); toMonitors({ type: 'NA', id }); } } else { @@ -595,7 +595,7 @@ function onConnect>(port: chrome.runtime.Port) { let id: number | string; let listener; - window.store.dispatch({ type: CONNECTED, port }); + store.dispatch({ type: CONNECTED, port }); if (port.name === 'tab') { id = getId(port.sender!); @@ -609,7 +609,7 @@ function onConnect>(port: chrome.runtime.Port) { } if (isMonitored) port.postMessage({ type: 'START' }); - const state = window.store.getState(); + const state = store.getState(); if (state.instances.persisted) { const instanceId = `${id}/${msg.instanceId}`; const persistedState = state.instances.states[instanceId]; @@ -645,7 +645,7 @@ function onConnect>(port: chrome.runtime.Port) { monitorInstances(true, port.name); monitors++; listener = (msg: BackgroundAction) => { - window.store.dispatch(msg); + store.dispatch(msg); }; port.onMessage.addListener(listener); port.onDisconnect.addListener(disconnect('panel', id, listener)); @@ -662,13 +662,7 @@ chrome.notifications.onClicked.addListener((id) => { openDevToolsWindow('devtools-right'); }); -declare global { - interface Window { - syncOptions: SyncOptions; - } -} - -window.syncOptions = syncOptions(toAllTabs); // Expose to the options page +const syncOptionsToAllTabs = syncOptions(toAllTabs); // Expose to the options page const api: Middleware<{}, BackgroundState, Dispatch> = (store) => (next) => (untypedAction) => { From 965f2ab8a6002805e746a36ba7625d2cce4d64da Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Sat, 3 Aug 2024 18:29:30 -0400 Subject: [PATCH 03/23] Test devpanel --- extension/src/devtools/index.ts | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/extension/src/devtools/index.ts b/extension/src/devtools/index.ts index 202e6c9725..9ef741d273 100644 --- a/extension/src/devtools/index.ts +++ b/extension/src/devtools/index.ts @@ -1,17 +1,6 @@ -function createPanel(url: string) { - chrome.devtools.panels.create( - 'Redux', - 'img/logo/scalable.png', - url, - function () {}, - ); -} - -if (chrome.runtime.getBackgroundPage) { - // Check if the background page's object is accessible (not in incognito) - chrome.runtime.getBackgroundPage((background) => { - createPanel(background ? 'window.html' : 'devpanel.html'); - }); -} else { - createPanel('devpanel.html'); -} +chrome.devtools.panels.create( + 'Redux', + 'img/logo/scalable.png', + 'devpanel.html', + () => {}, +); From b164f492df49be954b75fbc1d5652c7b018b049c Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Sat, 10 Aug 2024 14:43:42 -0400 Subject: [PATCH 04/23] Inject pageScript using new API --- extension/chrome/manifest.json | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/extension/chrome/manifest.json b/extension/chrome/manifest.json index 5931e9f3b1..715866040f 100644 --- a/extension/chrome/manifest.json +++ b/extension/chrome/manifest.json @@ -43,19 +43,20 @@ { "matches": [""], "exclude_globs": ["https://www.google*"], - "js": ["content.bundle.js", "pagewrap.bundle.js"], + "js": ["content.bundle.js"], "run_at": "document_start", "all_frames": true - } - ], - "devtools_page": "devtools.html", - "web_accessible_resources": [ + }, { - "resources": ["page.bundle.js"], "matches": [""], - "extension_ids": [] + "exclude_globs": ["https://www.google*"], + "js": ["page.bundle.js"], + "run_at": "document_start", + "all_frames": true, + "world": "MAIN" } ], + "devtools_page": "devtools.html", "externally_connectable": { "ids": ["*"] }, From 8a332528a9a0cfcf31e8e955be7f7bd8852e0c8e Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Sat, 10 Aug 2024 15:50:14 -0400 Subject: [PATCH 05/23] Keep connection from devpanel to background alive --- extension/src/background/store/apiMiddleware.ts | 3 ++- extension/src/devpanel/index.tsx | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/extension/src/background/store/apiMiddleware.ts b/extension/src/background/store/apiMiddleware.ts index ffedb28665..ac072f0e97 100644 --- a/extension/src/background/store/apiMiddleware.ts +++ b/extension/src/background/store/apiMiddleware.ts @@ -644,7 +644,8 @@ function onConnect>(port: chrome.runtime.Port) { connections.panel[id] = port; monitorInstances(true, port.name); monitors++; - listener = (msg: BackgroundAction) => { + listener = (msg: BackgroundAction | 'heartbeat') => { + if (msg === 'heartbeat') return; store.dispatch(msg); }; port.onMessage.addListener(listener); diff --git a/extension/src/devpanel/index.tsx b/extension/src/devpanel/index.tsx index 34271f84de..75a6ae0c45 100644 --- a/extension/src/devpanel/index.tsx +++ b/extension/src/devpanel/index.tsx @@ -103,9 +103,15 @@ let splitMessage: SplitUpdateStateRequest>; function init(id: number) { renderNA(); + bgConnection = chrome.runtime.connect({ name: id ? id.toString() : undefined, }); + + setInterval(() => { + bgConnection.postMessage('heartbeat'); + }, 15000); + bgConnection.onMessage.addListener( >( message: PanelMessageWithSplitAction, From 35b98d82f75161103691de37c3bfc1aa6e1011c0 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Sat, 10 Aug 2024 17:23:29 -0400 Subject: [PATCH 06/23] Keep connection from content script to background alive --- extension/src/background/store/apiMiddleware.ts | 3 ++- extension/src/contentScript/index.ts | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/extension/src/background/store/apiMiddleware.ts b/extension/src/background/store/apiMiddleware.ts index ac072f0e97..54c52ae60b 100644 --- a/extension/src/background/store/apiMiddleware.ts +++ b/extension/src/background/store/apiMiddleware.ts @@ -601,7 +601,8 @@ function onConnect>(port: chrome.runtime.Port) { id = getId(port.sender!); if (port.sender!.frameId) id = `${id}-${port.sender!.frameId}`; connections.tab[id] = port; - listener = (msg: ContentScriptToBackgroundMessage) => { + listener = (msg: ContentScriptToBackgroundMessage | 'heartbeat') => { + if (msg === 'heartbeat') return; if (msg.name === 'INIT_INSTANCE') { if (typeof id === 'number') { chrome.pageAction.show(id); diff --git a/extension/src/contentScript/index.ts b/extension/src/contentScript/index.ts index 123bd3a395..dd0e96ff92 100644 --- a/extension/src/contentScript/index.ts +++ b/extension/src/contentScript/index.ts @@ -318,3 +318,7 @@ function handleMessages>( } window.addEventListener('message', handleMessages, false); + +setInterval(() => { + bg?.postMessage('heartbeat'); +}, 15000); From fae02a6d27436a6270cb9bece69659c965e1ab39 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Sat, 10 Aug 2024 18:14:29 -0400 Subject: [PATCH 07/23] Replace page action with action --- extension/chrome/manifest.json | 4 ++-- extension/src/background/index.ts | 4 +++- extension/src/background/store/apiMiddleware.ts | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/extension/chrome/manifest.json b/extension/chrome/manifest.json index 715866040f..ca2faa9215 100644 --- a/extension/chrome/manifest.json +++ b/extension/chrome/manifest.json @@ -22,9 +22,9 @@ "devtools-remote": { "description": "Remote DevTools" }, - "_execute_page_action": { + "_execute_action": { "suggested_key": { - "default": "Ctrl+Shift+E" + "default": "Ctrl+Shift+F" } } }, diff --git a/extension/src/background/index.ts b/extension/src/background/index.ts index 8b81adf3c3..417e246ad0 100644 --- a/extension/src/background/index.ts +++ b/extension/src/background/index.ts @@ -12,8 +12,10 @@ chrome.commands.onCommand.addListener((shortcut) => { openDevToolsWindow(shortcut as DevToolsPosition); }); -// Create the context menu when installed +// Disable the action by default and create the context menu when installed chrome.runtime.onInstalled.addListener(() => { + chrome.action.disable(); + syncOptions().get((option) => { if (option.showContextMenus) createMenu(); }); diff --git a/extension/src/background/store/apiMiddleware.ts b/extension/src/background/store/apiMiddleware.ts index 54c52ae60b..52ac84d8e5 100644 --- a/extension/src/background/store/apiMiddleware.ts +++ b/extension/src/background/store/apiMiddleware.ts @@ -605,8 +605,8 @@ function onConnect>(port: chrome.runtime.Port) { if (msg === 'heartbeat') return; if (msg.name === 'INIT_INSTANCE') { if (typeof id === 'number') { - chrome.pageAction.show(id); - chrome.pageAction.setIcon({ tabId: id, path: 'img/logo/38x38.png' }); + chrome.action.enable(id); + chrome.action.setIcon({ tabId: id, path: 'img/logo/38x38.png' }); } if (isMonitored) port.postMessage({ type: 'START' }); From a7f6dbc0a2989594ecf274418207ed2d1307df83 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Sun, 11 Aug 2024 09:43:29 -0400 Subject: [PATCH 08/23] Cleanup syncOptions --- extension/src/background/index.ts | 4 ++-- extension/src/background/store/apiMiddleware.ts | 8 ++++---- extension/src/options/syncOptions.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/extension/src/background/index.ts b/extension/src/background/index.ts index 417e246ad0..c34b4a547c 100644 --- a/extension/src/background/index.ts +++ b/extension/src/background/index.ts @@ -1,7 +1,7 @@ import configureStore from './store/backgroundStore'; import openDevToolsWindow, { DevToolsPosition } from './openWindow'; import { createMenu, removeMenu } from './contextMenus'; -import syncOptions from '../options/syncOptions'; +import createSyncOptions 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(); - syncOptions().get((option) => { + createSyncOptions().get((option) => { if (option.showContextMenus) createMenu(); }); }); diff --git a/extension/src/background/store/apiMiddleware.ts b/extension/src/background/store/apiMiddleware.ts index 52ac84d8e5..12764e6ede 100644 --- a/extension/src/background/store/apiMiddleware.ts +++ b/extension/src/background/store/apiMiddleware.ts @@ -11,7 +11,7 @@ import { TOGGLE_PERSIST, UPDATE_STATE, } from '@redux-devtools/app'; -import syncOptions, { +import createSyncOptions, { Options, OptionsMessage, } from '../../options/syncOptions'; @@ -420,6 +420,8 @@ function toAllTabs(msg: TabMessage) { }); } +const syncOptions = createSyncOptions(toAllTabs); + function monitorInstances(shouldMonitor: boolean, id?: string) { if (!id && isMonitored === shouldMonitor) return; const action = { @@ -497,7 +499,7 @@ function messaging>( return; } if (request.type === 'GET_OPTIONS') { - syncOptionsToAllTabs.get((options) => { + syncOptions.get((options) => { sendResponse!({ options }); }); return; @@ -664,8 +666,6 @@ chrome.notifications.onClicked.addListener((id) => { openDevToolsWindow('devtools-right'); }); -const syncOptionsToAllTabs = syncOptions(toAllTabs); // Expose to the options page - const api: Middleware<{}, BackgroundState, Dispatch> = (store) => (next) => (untypedAction) => { const action = untypedAction as BackgroundAction; diff --git a/extension/src/options/syncOptions.ts b/extension/src/options/syncOptions.ts index 4b51685fb3..17324ffcfe 100644 --- a/extension/src/options/syncOptions.ts +++ b/extension/src/options/syncOptions.ts @@ -154,7 +154,7 @@ export interface SyncOptions { readonly subscribe: (callback: (options: Options) => void) => void; } -export default function syncOptions(toAllTabs?: ToAllTabs): SyncOptions { +export default function createSyncOptions(toAllTabs?: ToAllTabs): SyncOptions { if (toAllTabs && !options) get(() => {}); // Initialize return { save: save(toAllTabs), From 4b43b513d39723f8624f09a58c8651f6a98bc27a Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Sun, 11 Aug 2024 13:53:38 -0400 Subject: [PATCH 09/23] Update options to not rely on background page access --- extension/src/background/index.ts | 4 +- .../src/background/store/apiMiddleware.ts | 38 +++----- extension/src/contentScript/index.ts | 37 ++++++-- extension/src/options/index.tsx | 33 ++++--- extension/src/options/syncOptions.ts | 93 ++++++------------- extension/src/pageScript/index.ts | 7 ++ 6 files changed, 99 insertions(+), 113 deletions(-) diff --git a/extension/src/background/index.ts b/extension/src/background/index.ts index c34b4a547c..8573236710 100644 --- a/extension/src/background/index.ts +++ b/extension/src/background/index.ts @@ -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(); }); }); diff --git a/extension/src/background/store/apiMiddleware.ts b/extension/src/background/store/apiMiddleware.ts index 12764e6ede..930efc4efd 100644 --- a/extension/src/background/store/apiMiddleware.ts +++ b/extension/src/background/store/apiMiddleware.ts @@ -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> { 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> = | PageScriptToContentScriptMessageWithoutDisconnectOrInitInstance | SplitMessage | SingleMessage; -type BackgroundStoreResponse = { readonly options: Options }; // Receive messages from content scripts function messaging>( request: BackgroundStoreMessage, sender: chrome.runtime.MessageSender, - sendResponse?: (response?: BackgroundStoreResponse) => void, ) { let tabId = getId(sender); if (!tabId) return; @@ -498,10 +488,8 @@ function messaging>( 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') { diff --git a/extension/src/contentScript/index.ts b/extension/src/contentScript/index.ts index dd0e96ff92..9da069b44f 100644 --- a/extension/src/contentScript/index.ts +++ b/extension/src/contentScript/index.ts @@ -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> { readonly type: 'IMPORT_STATE'; @@ -112,6 +122,7 @@ export type ListenerMessage> = | ActionAction | ExportAction | UpdateAction + | OptionsAction | ImportStateDispatchAction; 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>( ) { 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>( tryCatch(send, message); } +prefetchOptions(); + window.addEventListener('message', handleMessages, false); setInterval(() => { diff --git a/extension/src/options/index.tsx b/extension/src/options/index.tsx index 2e0fd7a42b..29f9efb0e1 100644 --- a/extension/src/options/index.tsx +++ b/extension/src/options/index.tsx @@ -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 = (name: K, value: Options[K]) => { - syncOptions.save(name, value); - }; +subscribeToOptions((options) => { + const message: OptionsMessage = { type: 'OPTIONS', options }; + chrome.runtime.sendMessage(message); +}); - const renderOptions = (options: Options) => { - const root = createRoot(document.getElementById('root')!); - root.render(); - }; +const renderOptions = (options: Options) => { + const root = createRoot(document.getElementById('root')!); + root.render(); +}; - syncOptions.subscribe(renderOptions); - syncOptions.get((options) => { - renderOptions(options); - }); +subscribeToOptions(renderOptions); +getOptions((options) => { + renderOptions(options); }); diff --git a/extension/src/options/syncOptions.ts b/extension/src/options/syncOptions.ts index 17324ffcfe..fe1cae6f39 100644 --- a/extension/src/options/syncOptions.ts +++ b/extension/src/options/syncOptions.ts @@ -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) => - (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 = ( + 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: (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, - }; -} diff --git a/extension/src/pageScript/index.ts b/extension/src/pageScript/index.ts index 313a09ef5e..9888039138 100644 --- a/extension/src/pageScript/index.ts +++ b/extension/src/pageScript/index.ts @@ -432,6 +432,13 @@ function __REDUX_DEVTOOLS_EXTENSION__>( serializeAction, ); } + return; + case 'OPTIONS': + window.devToolsOptions = Object.assign( + window.devToolsOptions || {}, + message.options, + ); + return; } } From 9580a467de74f643dbe36a46cc93ca23a43ccccc Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Sun, 11 Aug 2024 14:47:07 -0400 Subject: [PATCH 10/23] Start work on updating popup --- extension/chrome/manifest.json | 2 +- .../src/background/store/apiMiddleware.ts | 22 +++++----- extension/src/devpanel/devpanel.pug | 6 +++ extension/src/devpanel/index.tsx | 28 +++++++++---- .../src/devpanel/store/panelSyncMiddleware.ts | 40 ++++++++++++++++--- extension/src/window/window.pug | 2 +- 6 files changed, 71 insertions(+), 29 deletions(-) diff --git a/extension/chrome/manifest.json b/extension/chrome/manifest.json index ca2faa9215..a03057fbef 100644 --- a/extension/chrome/manifest.json +++ b/extension/chrome/manifest.json @@ -7,7 +7,7 @@ "action": { "default_icon": "img/logo/gray.png", "default_title": "Redux DevTools", - "default_popup": "window.html#popup" + "default_popup": "devpanel.html#popup" }, "commands": { "devtools-left": { diff --git a/extension/src/background/store/apiMiddleware.ts b/extension/src/background/store/apiMiddleware.ts index 930efc4efd..72e5a3f27f 100644 --- a/extension/src/background/store/apiMiddleware.ts +++ b/extension/src/background/store/apiMiddleware.ts @@ -263,22 +263,18 @@ type MonitorAction> = // Chrome message limit is 64 MB, but we're using 32 MB to include other object's parts const maxChromeMsgSize = 32 * 1024 * 1024; +// TODO Clean up args function toMonitors>( action: MonitorAction, tabId?: string | number, verbose?: boolean, ) { - for (const monitorPort of Object.values(connections.monitor)) { - monitorPort.postMessage( - verbose || action.type === 'ERROR' || action.type === SET_PERSIST - ? action - : { type: UPDATE_STATE }, - ); - } - - for (const panelPort of Object.values(connections.panel)) { + for (const port of [ + ...Object.values(connections.monitor), + ...Object.values(connections.panel), + ]) { try { - panelPort.postMessage(action); + port.postMessage(action); } catch (err) { if ( action.type !== UPDATE_STATE || @@ -309,11 +305,11 @@ function toMonitors>( value; } - panelPort.postMessage({ ...action, request: splitMessageStart }); + port.postMessage({ ...action, request: splitMessageStart }); for (let i = 0; i < toSplit.length; i++) { for (let j = 0; j < toSplit[i][1].length; j += maxChromeMsgSize) { - panelPort.postMessage({ + port.postMessage({ ...action, request: { split: 'chunk', @@ -326,7 +322,7 @@ function toMonitors>( } } - panelPort.postMessage({ ...action, request: { split: 'end' } }); + port.postMessage({ ...action, request: { split: 'end' } }); } } } diff --git a/extension/src/devpanel/devpanel.pug b/extension/src/devpanel/devpanel.pug index bf5105d33f..a4e1a7411a 100644 --- a/extension/src/devpanel/devpanel.pug +++ b/extension/src/devpanel/devpanel.pug @@ -12,5 +12,11 @@ html body #root + div(style='position: relative') + img( + src='/img/loading.svg', + height=300, width=350, + style='position: absolute; top: 50%; left: 50%; margin-top: -150px; margin-left: -175px;' + ) link(href='/devpanel.bundle.css', rel='stylesheet') script(src='/devpanel.bundle.js') diff --git a/extension/src/devpanel/index.tsx b/extension/src/devpanel/index.tsx index 75a6ae0c45..9a331f3b1e 100644 --- a/extension/src/devpanel/index.tsx +++ b/extension/src/devpanel/index.tsx @@ -23,9 +23,10 @@ import { PersistGate } from 'redux-persist/integration/react'; const position = location.hash; const messageStyle: CSSProperties = { - padding: '20px', + paddingTop: '20px', width: '100%', textAlign: 'center', + boxSizing: 'border-box', }; let rendered: boolean | undefined; @@ -72,7 +73,12 @@ function renderNA() { . ); - if (isChrome) { + if ( + isChrome && + chrome && + chrome.devtools && + chrome.devtools.inspectedWindow + ) { chrome.devtools.inspectedWindow.getResources((resources) => { if (resources[0].url.substr(0, 4) === 'file') { message = ( @@ -101,12 +107,14 @@ function renderNA() { let splitMessage: SplitUpdateStateRequest>; -function init(id: number) { +function init() { renderNA(); - bgConnection = chrome.runtime.connect({ - name: id ? id.toString() : undefined, - }); + let name = 'monitor'; + if (chrome && chrome.devtools && chrome.devtools.inspectedWindow) { + name += chrome.devtools.inspectedWindow.tabId; + } + bgConnection = chrome.runtime.connect({ name }); setInterval(() => { bgConnection.postMessage('heartbeat'); @@ -117,7 +125,8 @@ function init(id: number) { message: PanelMessageWithSplitAction, ) => { if (message.type === 'NA') { - if (message.id === id) renderNA(); + // TODO Double-check this now that the name is different + if (message.id === name) renderNA(); else store!.dispatch({ type: REMOVE_INSTANCE, id: message.id }); } else { if (!rendered) renderDevTools(); @@ -163,4 +172,7 @@ function init(id: number) { ); } -init(chrome.devtools.inspectedWindow.tabId); +if (position === '#popup') document.body.style.minWidth = '760px'; +if (position !== '#popup') document.body.style.minHeight = '100%'; + +init(); diff --git a/extension/src/devpanel/store/panelSyncMiddleware.ts b/extension/src/devpanel/store/panelSyncMiddleware.ts index 278aef75ed..cccb479dd2 100644 --- a/extension/src/devpanel/store/panelSyncMiddleware.ts +++ b/extension/src/devpanel/store/panelSyncMiddleware.ts @@ -7,23 +7,51 @@ import { TOGGLE_PERSIST, UPDATE_STATE, } from '@redux-devtools/app'; -import { Dispatch, Middleware } from 'redux'; +import { Dispatch, Middleware, MiddlewareAPI } from 'redux'; + +function selectInstance( + tabId: number, + store: MiddlewareAPI, StoreState>, + next: (action: unknown) => unknown, +) { + const instances = store.getState().instances; + if (instances.current === 'default') return; + const connections = instances.connections[tabId]; + if (connections && connections.length === 1) { + next({ type: SELECT_INSTANCE, selected: connections[0] }); + } +} + +function getCurrentTabId(next: (tabId: number) => void) { + chrome.tabs.query( + { + active: true, + lastFocusedWindow: true, + }, + (tabs) => { + const tab = tabs[0]; + if (!tab) return; + next(tab.id!); + }, + ); +} function panelDispatcher( bgConnection: chrome.runtime.Port, ): Middleware<{}, StoreState, Dispatch> { let autoselected = false; - const tabId = chrome.devtools.inspectedWindow.tabId; return (store) => (next) => (untypedAction) => { const action = untypedAction as StoreAction; const result = next(action); - if (!autoselected && action.type === UPDATE_STATE && tabId) { + if (!autoselected && action.type === UPDATE_STATE) { autoselected = true; - const connections = store.getState().instances.connections[tabId]; - if (connections && connections.length === 1) { - next({ type: SELECT_INSTANCE, selected: connections[0] }); + + if (chrome.devtools && chrome.devtools.inspectedWindow) { + selectInstance(chrome.devtools.inspectedWindow.tabId, store, next); + } else { + getCurrentTabId((tabId) => selectInstance(tabId, store, next)); } } if (action.type === LIFTED_ACTION || action.type === TOGGLE_PERSIST) { diff --git a/extension/src/window/window.pug b/extension/src/window/window.pug index efda47dbf5..10f1009ccd 100644 --- a/extension/src/window/window.pug +++ b/extension/src/window/window.pug @@ -12,7 +12,7 @@ html img( src='/img/loading.svg', height=300, width=350, - style='position: absolute; top: 50%; left: 50%; margin-top: -175px; margin-left: -175px;' + style='position: absolute; top: 50%; left: 50%; margin-top: -150px; margin-left: -175px;' ) link(href='/window.bundle.css', rel='stylesheet') script(src='/window.bundle.js') From 458d8750b4b4a87336eaa68a02c1a9b3558f8535 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Mon, 12 Aug 2024 21:49:11 -0400 Subject: [PATCH 11/23] Updates --- extension/src/background/store/apiMiddleware.ts | 10 ++++++---- extension/src/devpanel/devpanel.pug | 4 ---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/extension/src/background/store/apiMiddleware.ts b/extension/src/background/store/apiMiddleware.ts index 72e5a3f27f..a8271f6f7d 100644 --- a/extension/src/background/store/apiMiddleware.ts +++ b/extension/src/background/store/apiMiddleware.ts @@ -249,7 +249,6 @@ const chunks: { >; } = {}; let monitors = 0; -let isMonitored = false; const getId = (sender: chrome.runtime.MessageSender, name?: string) => sender.tab ? sender.tab.id! : name || sender.id!; @@ -418,7 +417,6 @@ function toAllTabs(msg: TabMessage) { } function monitorInstances(shouldMonitor: boolean, id?: string) { - if (!id && isMonitored === shouldMonitor) return; const action = { type: shouldMonitor ? ('START' as const) : ('STOP' as const), }; @@ -427,7 +425,6 @@ function monitorInstances(shouldMonitor: boolean, id?: string) { } else { toAllTabs(action); } - isMonitored = shouldMonitor; } function getReducerError() { @@ -594,7 +591,7 @@ function onConnect>(port: chrome.runtime.Port) { chrome.action.enable(id); chrome.action.setIcon({ tabId: id, path: 'img/logo/38x38.png' }); } - if (isMonitored) port.postMessage({ type: 'START' }); + port.postMessage({ type: 'START' }); const state = store.getState(); if (state.instances.persisted) { @@ -623,6 +620,11 @@ function onConnect>(port: chrome.runtime.Port) { id = getId(port.sender!, port.name); connections.monitor[id] = port; monitorInstances(true); + listener = (msg: BackgroundAction | 'heartbeat') => { + if (msg === 'heartbeat') return; + store.dispatch(msg); + }; + port.onMessage.addListener(listener); monitors++; port.onDisconnect.addListener(disconnect('monitor', id)); } else { diff --git a/extension/src/devpanel/devpanel.pug b/extension/src/devpanel/devpanel.pug index a4e1a7411a..517f062267 100644 --- a/extension/src/devpanel/devpanel.pug +++ b/extension/src/devpanel/devpanel.pug @@ -5,10 +5,6 @@ html meta(charset='UTF-8') title Redux DevTools include ../style.pug - style. - body { - min-height: 100px; - } body #root From fb0c92df8786b6d1e7fc7dfcb8b50b9406f1973a Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Mon, 12 Aug 2024 21:56:23 -0400 Subject: [PATCH 12/23] Remove window --- extension/build.mjs | 3 +- extension/src/background/openWindow.ts | 2 +- extension/src/window/index.tsx | 37 --------- .../store/instanceSelectorMiddleware.ts | 51 ------------ .../src/window/store/instancesReducer.ts | 34 -------- extension/src/window/store/windowReducer.ts | 32 -------- extension/src/window/store/windowStore.ts | 81 ------------------- .../src/window/store/windowSyncMiddleware.ts | 37 --------- extension/src/window/window.pug | 18 ----- 9 files changed, 2 insertions(+), 293 deletions(-) delete mode 100644 extension/src/window/index.tsx delete mode 100644 extension/src/window/store/instanceSelectorMiddleware.ts delete mode 100644 extension/src/window/store/instancesReducer.ts delete mode 100644 extension/src/window/store/windowReducer.ts delete mode 100644 extension/src/window/store/windowStore.ts delete mode 100644 extension/src/window/store/windowSyncMiddleware.ts delete mode 100644 extension/src/window/window.pug diff --git a/extension/build.mjs b/extension/build.mjs index 86971a68e7..fa4faeec74 100644 --- a/extension/build.mjs +++ b/extension/build.mjs @@ -22,7 +22,6 @@ await esbuild.build({ entryPoints: [ { out: 'background.bundle', in: 'src/background/index.ts' }, { out: 'options.bundle', in: 'src/options/index.tsx' }, - { out: 'window.bundle', in: 'src/window/index.tsx' }, { out: 'remote.bundle', in: 'src/remote/index.tsx' }, { out: 'devpanel.bundle', in: 'src/devpanel/index.tsx' }, { out: 'devtools.bundle', in: 'src/devtools/index.ts' }, @@ -48,7 +47,7 @@ if (prod) { console.log(); console.log('Creating HTML files...'); -const htmlFiles = ['devpanel', 'devtools', 'options', 'remote', 'window']; +const htmlFiles = ['devpanel', 'devtools', 'options', 'remote']; for (const htmlFile of htmlFiles) { fs.writeFileSync( `dist/${htmlFile}.html`, diff --git a/extension/src/background/openWindow.ts b/extension/src/background/openWindow.ts index 2c3f1db0bb..f9622f245e 100644 --- a/extension/src/background/openWindow.ts +++ b/extension/src/background/openWindow.ts @@ -58,7 +58,7 @@ export default function openDevToolsWindow(position: DevToolsPosition) { width: 380, height: window.screen.availHeight, }; - let url = 'window.html'; + let url = 'devpanel.html'; switch (position) { case 'devtools-right': params.left = diff --git a/extension/src/window/index.tsx b/extension/src/window/index.tsx deleted file mode 100644 index 6f40e05539..0000000000 --- a/extension/src/window/index.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import { createRoot } from 'react-dom/client'; -import { Provider } from 'react-redux'; -import { PersistGate } from 'redux-persist/integration/react'; -import { UPDATE_STATE } from '@redux-devtools/app'; -import App from '../app/App'; -import configureStore from './store/windowStore'; -import type { MonitorMessage } from '../background/store/apiMiddleware'; - -const position = location.hash; - -chrome.runtime.getBackgroundPage((window) => { - const { store } = window!; - const { store: localStore, persistor } = configureStore(store, position); - let name = 'monitor'; - if (chrome && chrome.devtools && chrome.devtools.inspectedWindow) { - name += chrome.devtools.inspectedWindow.tabId; - } - const bg = chrome.runtime.connect({ name }); - const update = (action?: MonitorMessage) => { - localStore.dispatch(action || { type: UPDATE_STATE }); - }; - bg.onMessage.addListener(update); - update(); - - const root = createRoot(document.getElementById('root')!); - root.render( - - - - - , - ); -}); - -if (position === '#popup') document.body.style.minWidth = '760px'; -if (position !== '#popup') document.body.style.minHeight = '100%'; diff --git a/extension/src/window/store/instanceSelectorMiddleware.ts b/extension/src/window/store/instanceSelectorMiddleware.ts deleted file mode 100644 index 7587cb8dab..0000000000 --- a/extension/src/window/store/instanceSelectorMiddleware.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Dispatch, Middleware, MiddlewareAPI } from 'redux'; -import { - SELECT_INSTANCE, - StoreAction, - StoreState, - UPDATE_STATE, -} from '@redux-devtools/app'; - -function selectInstance( - tabId: number, - store: MiddlewareAPI, StoreState>, - next: (action: unknown) => unknown, -) { - const instances = store.getState().instances; - if (instances.current === 'default') return; - const connections = instances.connections[tabId]; - if (connections && connections.length === 1) { - next({ type: SELECT_INSTANCE, selected: connections[0] }); - } -} - -function getCurrentTabId(next: (tabId: number) => void) { - chrome.tabs.query( - { - active: true, - lastFocusedWindow: true, - }, - (tabs) => { - const tab = tabs[0]; - if (!tab) return; - next(tab.id!); - }, - ); -} - -const popupSelector: Middleware<{}, StoreState, Dispatch> = - (store) => (next) => (untypedAction) => { - const action = untypedAction as StoreAction; - - const result = next(action); - if (action.type === UPDATE_STATE) { - if (chrome.devtools && chrome.devtools.inspectedWindow) { - selectInstance(chrome.devtools.inspectedWindow.tabId, store, next); - } else { - getCurrentTabId((tabId) => selectInstance(tabId, store, next)); - } - } - return result; - }; - -export default popupSelector; diff --git a/extension/src/window/store/instancesReducer.ts b/extension/src/window/store/instancesReducer.ts deleted file mode 100644 index 29497656c3..0000000000 --- a/extension/src/window/store/instancesReducer.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - instancesInitialState, - dispatchAction, - UPDATE_STATE, - SELECT_INSTANCE, - LIFTED_ACTION, - SET_PERSIST, -} from '@redux-devtools/app'; -import type { - ExpandedUpdateStateAction, - WindowStoreAction, -} from './windowStore'; - -export default function instances( - state = instancesInitialState, - action: WindowStoreAction, -) { - switch (action.type) { - case UPDATE_STATE: - return { - ...(action as ExpandedUpdateStateAction).instances, - selected: state.selected, - }; - case LIFTED_ACTION: - if (action.message === 'DISPATCH') return dispatchAction(state, action); - return state; - case SELECT_INSTANCE: - return { ...state, selected: action.selected }; - case SET_PERSIST: - return { ...state, persisted: action.payload }; - default: - return state; - } -} diff --git a/extension/src/window/store/windowReducer.ts b/extension/src/window/store/windowReducer.ts deleted file mode 100644 index dab281c94f..0000000000 --- a/extension/src/window/store/windowReducer.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { combineReducers, Reducer } from 'redux'; -import { - connection, - monitor, - notification, - reports, - section, - socket, - theme, - stateTreeSettings, - StoreState, -} from '@redux-devtools/app'; -import instances from './instancesReducer'; -import type { WindowStoreAction } from './windowStore'; - -const rootReducer: Reducer< - StoreState, - WindowStoreAction, - Partial -> = combineReducers({ - instances, - monitor, - socket, - reports, - notification, - section, - theme, - connection, - stateTreeSettings, -}) as any; - -export default rootReducer; diff --git a/extension/src/window/store/windowStore.ts b/extension/src/window/store/windowStore.ts deleted file mode 100644 index ce746a929f..0000000000 --- a/extension/src/window/store/windowStore.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { - createStore, - compose, - applyMiddleware, - Store, - StoreEnhancer, - Reducer, -} from 'redux'; -import localForage from 'localforage'; -import { persistReducer, persistStore } from 'redux-persist'; -import { - api, - CONNECT_REQUEST, - exportStateMiddleware, - InstancesState, - StoreActionWithoutUpdateState, - StoreState, - UpdateStateAction, -} from '@redux-devtools/app'; -import syncStores from './windowSyncMiddleware'; -import instanceSelector from './instanceSelectorMiddleware'; -import rootReducer from './windowReducer'; -import type { BackgroundState } from '../../background/store/backgroundReducer'; -import type { BackgroundAction } from '../../background/store/backgroundStore'; -import type { - EmptyUpdateStateAction, - NAAction, -} from '../../background/store/apiMiddleware'; - -export interface ExpandedUpdateStateAction extends UpdateStateAction { - readonly instances: InstancesState; -} - -export type WindowStoreAction = - | StoreActionWithoutUpdateState - | ExpandedUpdateStateAction - | NAAction - | EmptyUpdateStateAction; - -const persistConfig = { - key: 'redux-devtools', - blacklist: ['instances', 'socket'], - storage: localForage, -}; - -const persistedReducer: Reducer = persistReducer( - persistConfig, - rootReducer, -) as any; - -export default function configureStore( - baseStore: Store, - position: string, -) { - let enhancer: StoreEnhancer; - const middlewares = [exportStateMiddleware, api, syncStores(baseStore)]; - if (!position || position === '#popup') { - // select current tab instance for devPanel and pageAction - middlewares.push(instanceSelector); - } - if (process.env.NODE_ENV === 'production') { - enhancer = applyMiddleware(...middlewares); - } else { - enhancer = compose( - applyMiddleware(...middlewares), - window.__REDUX_DEVTOOLS_EXTENSION__ - ? window.__REDUX_DEVTOOLS_EXTENSION__() - : (noop: unknown) => noop, - ); - } - const store = createStore(persistedReducer, enhancer); - const persistor = persistStore(store as Store, null, () => { - if (store.getState().connection.type !== 'disabled') { - store.dispatch({ - type: CONNECT_REQUEST, - }); - } - }); - - return { store, persistor }; -} diff --git a/extension/src/window/store/windowSyncMiddleware.ts b/extension/src/window/store/windowSyncMiddleware.ts deleted file mode 100644 index 9cdcdfa805..0000000000 --- a/extension/src/window/store/windowSyncMiddleware.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { - getActiveInstance, - LIFTED_ACTION, - StoreAction, - StoreState, - TOGGLE_PERSIST, - UPDATE_STATE, -} from '@redux-devtools/app'; -import { Dispatch, Middleware, Store } from 'redux'; -import type { BackgroundState } from '../../background/store/backgroundReducer'; -import type { BackgroundAction } from '../../background/store/backgroundStore'; - -const syncStores = - ( - baseStore: Store, - ): Middleware<{}, StoreState, Dispatch> => - (store) => - (next) => - (untypedAction) => { - const action = untypedAction as StoreAction; - - if (action.type === UPDATE_STATE) { - return next({ - ...action, - instances: baseStore.getState().instances, - }); - } - if (action.type === LIFTED_ACTION || action.type === TOGGLE_PERSIST) { - const instances = store.getState().instances; - const instanceId = getActiveInstance(instances); - const id = instances.options[instanceId].connectionId; - baseStore.dispatch({ ...action, instanceId, id } as any); - } - return next(action); - }; - -export default syncStores; diff --git a/extension/src/window/window.pug b/extension/src/window/window.pug deleted file mode 100644 index 10f1009ccd..0000000000 --- a/extension/src/window/window.pug +++ /dev/null @@ -1,18 +0,0 @@ -doctype html - -html - head - meta(charset='UTF-8') - title Redux DevTools - include ../style.pug - - body - #root - div(style='position: relative') - img( - src='/img/loading.svg', - height=300, width=350, - style='position: absolute; top: 50%; left: 50%; margin-top: -150px; margin-left: -175px;' - ) - link(href='/window.bundle.css', rel='stylesheet') - script(src='/window.bundle.js') From 08eeb5ad962e4ac468d8c7453ada05f75ddad98d Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Mon, 12 Aug 2024 22:53:16 -0400 Subject: [PATCH 13/23] Get opening in a separate window working --- extension/chrome/manifest.json | 10 +- extension/src/app/Actions.tsx | 26 +---- extension/src/background/contextMenus.ts | 16 +--- extension/src/background/openWindow.ts | 95 +++++-------------- .../src/background/store/apiMiddleware.ts | 10 +- extension/src/pageScript/api/openWindow.ts | 4 +- 6 files changed, 39 insertions(+), 122 deletions(-) diff --git a/extension/chrome/manifest.json b/extension/chrome/manifest.json index a03057fbef..4799521a27 100644 --- a/extension/chrome/manifest.json +++ b/extension/chrome/manifest.json @@ -10,14 +10,8 @@ "default_popup": "devpanel.html#popup" }, "commands": { - "devtools-left": { - "description": "DevTools window to left" - }, - "devtools-right": { - "description": "DevTools window to right" - }, - "devtools-bottom": { - "description": "DevTools window to bottom" + "devtools-window": { + "description": "DevTools window" }, "devtools-remote": { "description": "Remote DevTools" diff --git a/extension/src/app/Actions.tsx b/extension/src/app/Actions.tsx index 00abd2b906..216e59afab 100644 --- a/extension/src/app/Actions.tsx +++ b/extension/src/app/Actions.tsx @@ -18,7 +18,7 @@ import { TopButtons, } from '@redux-devtools/app'; import { GoBroadcast } from 'react-icons/go'; -import { MdBorderBottom, MdBorderLeft, MdBorderRight } from 'react-icons/md'; +import { MdOutlineWindow } from 'react-icons/md'; import type { Position } from '../pageScript/api/openWindow'; import type { SingleMessage } from '../background/store/apiMiddleware'; @@ -98,31 +98,13 @@ class Actions extends Component { )} - {!window.isElectron && position !== '#left' && ( - - )} - {!window.isElectron && position !== '#right' && ( - - )} - {!window.isElectron && position !== '#bottom' && ( + {!window.isElectron && ( )} {!window.isElectron && ( diff --git a/extension/src/background/contextMenus.ts b/extension/src/background/contextMenus.ts index d8a301454d..3aaed4ed18 100644 --- a/extension/src/background/contextMenus.ts +++ b/extension/src/background/contextMenus.ts @@ -2,29 +2,23 @@ import openDevToolsWindow, { DevToolsPosition } from './openWindow'; export function createMenu() { const menus = [ - { id: 'devtools-left', title: 'To left' }, - { id: 'devtools-right', title: 'To right' }, - { id: 'devtools-bottom', title: 'To bottom' }, - { - id: 'devtools-panel', - title: 'Open in a panel (enable in browser settings)', - }, + { id: 'devtools-window', title: 'Open in a window' }, { id: 'devtools-remote', title: 'Open Remote DevTools' }, ]; let shortcuts: { [commandName: string]: string | undefined } = {}; chrome.commands.getAll((commands) => { - commands.forEach(({ name, shortcut }) => { + for (const { name, shortcut } of commands) { shortcuts[name!] = shortcut; - }); + } - menus.forEach(({ id, title }) => { + for (const { id, title } of menus) { chrome.contextMenus.create({ id: id, title: title + (shortcuts[id] ? ' (' + shortcuts[id] + ')' : ''), contexts: ['all'], }); - }); + } }); } diff --git a/extension/src/background/openWindow.ts b/extension/src/background/openWindow.ts index f9622f245e..d4524a2d04 100644 --- a/extension/src/background/openWindow.ts +++ b/extension/src/background/openWindow.ts @@ -1,83 +1,34 @@ -export type DevToolsPosition = - | 'devtools-left' - | 'devtools-right' - | 'devtools-bottom' - | 'devtools-panel' - | 'devtools-remote'; +export type DevToolsPosition = 'devtools-window' | 'devtools-remote'; let windows: { [K in DevToolsPosition]?: number } = {}; -let lastPosition: DevToolsPosition | null = null; export default function openDevToolsWindow(position: DevToolsPosition) { - function popWindow( - action: string, - url: string, - customOptions: chrome.windows.CreateData & chrome.windows.UpdateInfo, - ) { - function focusIfExist(callback: () => void) { - if (!windows[position]) { - callback(); - lastPosition = position; - } else { - let params = { focused: true }; - if (lastPosition !== position && position !== 'devtools-panel') { - params = { ...params, ...customOptions }; - } - chrome.windows.update(windows[position]!, params, () => { - lastPosition = null; - if (chrome.runtime.lastError) callback(); - }); - } - } - - focusIfExist(() => { - let options: chrome.windows.CreateData = { - type: 'popup', - ...customOptions, - }; - if (action === 'open') { - options.url = chrome.extension.getURL( - url + '#' + position.substr(position.indexOf('-') + 1), - ); - chrome.windows.create(options, (win) => { - windows[position] = win!.id; - if (navigator.userAgent.indexOf('Firefox') !== -1) { - chrome.windows.update(win!.id!, { - focused: true, - ...customOptions, - }); - } - }); - } + if (!windows[position]) { + createWindow(position); + } else { + chrome.windows.update(windows[position]!, { focused: true }, () => { + if (chrome.runtime.lastError) createWindow(position); }); } +} + +function createWindow(position: DevToolsPosition) { + const url = chrome.runtime.getURL(getPath(position)); + chrome.windows.create({ type: 'popup', url }, (win) => { + windows[position] = win!.id; + if (navigator.userAgent.indexOf('Firefox') !== -1) { + chrome.windows.update(win!.id!, { focused: true }); + } + }); +} - let params: chrome.windows.CreateData & chrome.windows.UpdateInfo = { - left: 0, - top: 0, - width: 380, - height: window.screen.availHeight, - }; - let url = 'devpanel.html'; +function getPath(position: DevToolsPosition) { switch (position) { - case 'devtools-right': - params.left = - (window.screen as unknown as { availLeft: number }).availLeft + - window.screen.availWidth - - params.width!; - break; - case 'devtools-bottom': - params.height = 420; - params.top = window.screen.height - params.height; - params.width = window.screen.availWidth; - break; - case 'devtools-panel': - params.type = 'panel'; - break; + case 'devtools-window': + return 'devpanel.html'; case 'devtools-remote': - params = { width: 850, height: 600 }; - url = 'remote.html'; - break; + return 'remote.html'; + default: + throw new Error(`Unrecognized position: ${position}`); } - popWindow('open', url, params); } diff --git a/extension/src/background/store/apiMiddleware.ts b/extension/src/background/store/apiMiddleware.ts index a8271f6f7d..7f24e0aff8 100644 --- a/extension/src/background/store/apiMiddleware.ts +++ b/extension/src/background/store/apiMiddleware.ts @@ -490,12 +490,8 @@ function messaging>( return; } if (request.type === 'OPEN') { - let position: DevToolsPosition = 'devtools-left'; - if ( - ['remote', 'panel', 'left', 'right', 'bottom'].indexOf( - request.position, - ) !== -1 - ) { + let position: DevToolsPosition = 'devtools-window'; + if (['remote', 'window'].includes(request.position)) { position = ('devtools-' + request.position) as DevToolsPosition; } openDevToolsWindow(position); @@ -649,7 +645,7 @@ chrome.runtime.onMessageExternal.addListener(messaging); chrome.notifications.onClicked.addListener((id) => { chrome.notifications.clear(id); - openDevToolsWindow('devtools-right'); + openDevToolsWindow('devtools-window'); }); const api: Middleware<{}, BackgroundState, Dispatch> = diff --git a/extension/src/pageScript/api/openWindow.ts b/extension/src/pageScript/api/openWindow.ts index f48c3b6b5b..de2947c948 100644 --- a/extension/src/pageScript/api/openWindow.ts +++ b/extension/src/pageScript/api/openWindow.ts @@ -1,7 +1,7 @@ import { Action } from 'redux'; import type { PageScriptToContentScriptMessage } from './index'; -export type Position = 'left' | 'right' | 'bottom' | 'panel' | 'remote'; +export type Position = 'window' | 'remote'; function post>( message: PageScriptToContentScriptMessage, @@ -13,6 +13,6 @@ export default function openWindow(position?: Position) { post({ source: '@devtools-page', type: 'OPEN', - position: position || 'right', + position: position ?? 'window', }); } From 20b575a0b1872437242c6eb245b6ae10a6b1ac01 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Mon, 12 Aug 2024 23:02:00 -0400 Subject: [PATCH 14/23] Remove pageScriptWrap --- extension/build.mjs | 17 +---------------- extension/src/pageScriptWrap.ts | 19 ------------------- 2 files changed, 1 insertion(+), 35 deletions(-) delete mode 100644 extension/src/pageScriptWrap.ts diff --git a/extension/build.mjs b/extension/build.mjs index fa4faeec74..9c378cf4c7 100644 --- a/extension/build.mjs +++ b/extension/build.mjs @@ -5,7 +5,7 @@ import pug from 'pug'; const args = process.argv.slice(2); const prod = !args.includes('--dev'); -const commonEsbuildOptions = { +await esbuild.build({ bundle: true, logLevel: 'info', outdir: 'dist', @@ -15,10 +15,6 @@ const commonEsbuildOptions = { 'process.env.NODE_ENV': prod ? '"production"' : '"development"', 'process.env.BABEL_ENV': prod ? '"production"' : '"development"', }, -}; - -await esbuild.build({ - ...commonEsbuildOptions, entryPoints: [ { out: 'background.bundle', in: 'src/background/index.ts' }, { out: 'options.bundle', in: 'src/options/index.tsx' }, @@ -27,23 +23,12 @@ await esbuild.build({ { out: 'devtools.bundle', in: 'src/devtools/index.ts' }, { out: 'content.bundle', in: 'src/contentScript/index.ts' }, { out: 'page.bundle', in: 'src/pageScript/index.ts' }, - ...(prod ? [] : [{ out: 'pagewrap.bundle', in: 'src/pageScriptWrap.ts' }]), ], loader: { '.woff2': 'file', }, }); -if (prod) { - await esbuild.build({ - ...commonEsbuildOptions, - entryPoints: [{ out: 'pagewrap.bundle', in: 'src/pageScriptWrap.ts' }], - loader: { - '.js': 'text', - }, - }); -} - console.log(); console.log('Creating HTML files...'); diff --git a/extension/src/pageScriptWrap.ts b/extension/src/pageScriptWrap.ts deleted file mode 100644 index 5b02a9ce37..0000000000 --- a/extension/src/pageScriptWrap.ts +++ /dev/null @@ -1,19 +0,0 @@ -// @ts-ignore -import script from '../dist/page.bundle.js'; - -let s = document.createElement('script'); -s.type = 'text/javascript'; - -if (process.env.NODE_ENV === 'production') { - s.appendChild(document.createTextNode(script)); - (document.head || document.documentElement).appendChild(s); - s.parentNode!.removeChild(s); -} else { - s.src = chrome.extension.getURL('page.bundle.js'); - s.onload = function () { - (this as HTMLScriptElement).parentNode!.removeChild( - this as HTMLScriptElement, - ); - }; - (document.head || document.documentElement).appendChild(s); -} From 26f38527eb50bc117bd56808b0b23ff3a617ef8e Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Tue, 13 Aug 2024 21:33:22 -0400 Subject: [PATCH 15/23] Add socket to panelStore --- extension/src/devpanel/index.tsx | 4 +-- extension/src/devpanel/store/panelReducer.ts | 26 ++++---------------- extension/src/devpanel/store/panelStore.ts | 14 ++++++++--- 3 files changed, 17 insertions(+), 27 deletions(-) diff --git a/extension/src/devpanel/index.tsx b/extension/src/devpanel/index.tsx index 9a331f3b1e..de4f6e4a19 100644 --- a/extension/src/devpanel/index.tsx +++ b/extension/src/devpanel/index.tsx @@ -6,6 +6,7 @@ import { Persistor } from 'redux-persist'; import { REMOVE_INSTANCE, StoreAction, + StoreState, UPDATE_STATE, } from '@redux-devtools/app'; import App from '../app/App'; @@ -18,7 +19,6 @@ import { SplitUpdateStateRequest, UpdateStateRequest, } from '../background/store/apiMiddleware'; -import type { StoreStateWithoutSocket } from './store/panelReducer'; import { PersistGate } from 'redux-persist/integration/react'; const position = location.hash; @@ -31,7 +31,7 @@ const messageStyle: CSSProperties = { let rendered: boolean | undefined; let currentRoot: Root | undefined; -let store: Store | undefined; +let store: Store | undefined; let persistor: Persistor | undefined; let bgConnection: chrome.runtime.Port; let naTimeout: NodeJS.Timeout; diff --git a/extension/src/devpanel/store/panelReducer.ts b/extension/src/devpanel/store/panelReducer.ts index a6b75a1a40..a97e963623 100644 --- a/extension/src/devpanel/store/panelReducer.ts +++ b/extension/src/devpanel/store/panelReducer.ts @@ -1,45 +1,29 @@ import { combineReducers, Reducer } from 'redux'; import { connection, - ConnectionState, instances, - InstancesState, monitor, - MonitorState, notification, - NotificationState, reports, - ReportsState, section, - SectionState, - StateTreeSettings, + socket, stateTreeSettings, StoreAction, + StoreState, theme, - ThemeState, } from '@redux-devtools/app'; -export interface StoreStateWithoutSocket { - readonly section: SectionState; - readonly theme: ThemeState; - readonly connection: ConnectionState; - readonly monitor: MonitorState; - readonly instances: InstancesState; - readonly reports: ReportsState; - readonly notification: NotificationState; - readonly stateTreeSettings: StateTreeSettings; -} - const rootReducer: Reducer< - StoreStateWithoutSocket, + StoreState, StoreAction, - Partial + Partial > = combineReducers({ instances, monitor, reports, notification, section, + socket, theme, connection, stateTreeSettings, diff --git a/extension/src/devpanel/store/panelStore.ts b/extension/src/devpanel/store/panelStore.ts index 018b770443..b17802e0ca 100644 --- a/extension/src/devpanel/store/panelStore.ts +++ b/extension/src/devpanel/store/panelStore.ts @@ -1,9 +1,13 @@ import { createStore, applyMiddleware, Reducer, Store } from 'redux'; import localForage from 'localforage'; import { persistReducer, persistStore } from 'redux-persist'; -import { exportStateMiddleware, StoreAction } from '@redux-devtools/app'; +import { + exportStateMiddleware, + StoreAction, + StoreState, +} from '@redux-devtools/app'; import panelDispatcher from './panelSyncMiddleware'; -import rootReducer, { StoreStateWithoutSocket } from './panelReducer'; +import rootReducer from './panelReducer'; const persistConfig = { key: 'redux-devtools', @@ -11,8 +15,10 @@ const persistConfig = { storage: localForage, }; -const persistedReducer: Reducer = - persistReducer(persistConfig, rootReducer) as any; +const persistedReducer: Reducer = persistReducer( + persistConfig, + rootReducer, +) as any; export default function configureStore( position: string, From 7e19e81c04bdb5f94843f69ac9c5b41591b838f8 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Tue, 13 Aug 2024 21:36:07 -0400 Subject: [PATCH 16/23] Fix tests --- extension/test/app/containers/App.spec.js | 2 +- extension/test/app/inject/api.spec.js | 11 +---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/extension/test/app/containers/App.spec.js b/extension/test/app/containers/App.spec.js index 051f5b30ec..a38e3bed50 100644 --- a/extension/test/app/containers/App.spec.js +++ b/extension/test/app/containers/App.spec.js @@ -1,7 +1,7 @@ import React from 'react'; import { render, screen, within } from '@testing-library/react'; import { Provider } from 'react-redux'; -import configureStore from '../../../src/window/store/windowStore'; +import configureStore from '../../../src/devpanel/store/panelStore'; import App from '../../../src/app/App'; Object.defineProperty(window, 'matchMedia', { diff --git a/extension/test/app/inject/api.spec.js b/extension/test/app/inject/api.spec.js index 54a059fe3f..b5a3bfe086 100644 --- a/extension/test/app/inject/api.spec.js +++ b/extension/test/app/inject/api.spec.js @@ -20,16 +20,7 @@ describe('API', () => { expect(message).toEqual({ source: '@devtools-page', type: 'OPEN', - position: 'right', - }); - - message = await listenMessage(() => { - window.__REDUX_DEVTOOLS_EXTENSION__.open('left'); - }); - expect(message).toEqual({ - source: '@devtools-page', - type: 'OPEN', - position: 'left', + position: 'window', }); }); From e8b7783b293b56983ee270f81ccb57c84081ff77 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Mon, 12 Aug 2024 23:12:18 -0400 Subject: [PATCH 17/23] Try to use MV3 for Firefox --- extension/firefox/manifest.json | 43 +++++++++++++++------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/extension/firefox/manifest.json b/extension/firefox/manifest.json index d4292d1885..56a608898f 100644 --- a/extension/firefox/manifest.json +++ b/extension/firefox/manifest.json @@ -1,28 +1,22 @@ { "version": "3.1.10", "name": "Redux DevTools", - "manifest_version": 2, + "manifest_version": 3, "description": "Redux Developer Tools for debugging application state changes.", "homepage_url": "https://github.com/reduxjs/redux-devtools", - "applications": { + "browser_specific_settings": { "gecko": { "id": "extension@redux.devtools" } }, - "page_action": { + "action": { "default_icon": "img/logo/38x38.png", "default_title": "Redux DevTools", - "default_popup": "window.html#popup" + "default_popup": "devpanel.html#popup" }, "commands": { - "devtools-left": { - "description": "DevTools window to left" - }, - "devtools-right": { - "description": "DevTools window to right" - }, - "devtools-bottom": { - "description": "DevTools window to bottom" + "devtools-window": { + "description": "DevTools window" }, "devtools-remote": { "description": "Remote DevTools" @@ -42,21 +36,22 @@ "content_scripts": [ { "matches": [""], - "js": ["content.bundle.js", "pagewrap.bundle.js"], + "js": ["content.bundle.js"], "run_at": "document_start", "all_frames": true + }, + { + "matches": [""], + "js": ["page.bundle.js"], + "run_at": "document_start", + "all_frames": true, + "world": "MAIN" } ], "devtools_page": "devtools.html", - "web_accessible_resources": ["page.bundle.js"], - "permissions": [ - "notifications", - "contextMenus", - "tabs", - "storage", - "file:///*", - "http://*/*", - "https://*/*" - ], - "content_security_policy": "script-src 'self'; object-src 'self'; img-src 'self' data:;" + "permissions": ["notifications", "contextMenus", "tabs", "storage"], + "host_permissions": ["file:///*", "http://*/*", "https://*/*"], + "content_security_policy": { + "extension_pages": "script-src 'self'; object-src 'self'; img-src 'self' data:;" + } } From 3773787eab42218d03ddbe3eaaa2565d610bbeaa Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Thu, 15 Aug 2024 22:30:13 -0400 Subject: [PATCH 18/23] Fix path --- extension/test/chrome/extension.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extension/test/chrome/extension.spec.js b/extension/test/chrome/extension.spec.js index 7c52fe466b..c56046ca27 100644 --- a/extension/test/chrome/extension.spec.js +++ b/extension/test/chrome/extension.spec.js @@ -27,9 +27,9 @@ describe('Chrome extension', function () { }); it("should open extension's window", async () => { - await driver.get(`chrome-extension://${extensionId}/window.html#left`); + await driver.get(`chrome-extension://${extensionId}/devpanel.html`); const url = await driver.getCurrentUrl(); - expect(url).toBe(`chrome-extension://${extensionId}/window.html#left`); + expect(url).toBe(`chrome-extension://${extensionId}/devpanel.html`); }); it('should match document title', async () => { From 0bdb38e8685e47a697fde41eafb3f7b5adc39630 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Thu, 15 Aug 2024 22:44:06 -0400 Subject: [PATCH 19/23] Fix Chrome E2E tests --- extension/test/chrome/extension.spec.js | 31 ++++++++++--------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/extension/test/chrome/extension.spec.js b/extension/test/chrome/extension.spec.js index c56046ca27..35520bf1a8 100644 --- a/extension/test/chrome/extension.spec.js +++ b/extension/test/chrome/extension.spec.js @@ -37,25 +37,6 @@ describe('Chrome extension', function () { expect(title).toBe('Redux DevTools'); }); - it("should contain inspector monitor's component", async () => { - await delay(1000); - const val = await driver - .findElement(webdriver.By.xpath('//div[@data-testid="inspector"]')) - .getText(); - expect(val).toBeDefined(); - }); - - it('should contain an empty actions list', async () => { - const val = await driver - .findElement(webdriver.By.xpath('//div[@data-testid="actionListRows"]')) - .getText(); - expect(val).toBe(''); - }); - - Object.keys(switchMonitorTests).forEach((description) => - it(description, () => switchMonitorTests[description](driver)), - ); - it('should get actions list', async () => { const url = 'https://zalmoxisus.github.io/examples/router/'; await driver.executeScript(`window.open('${url}')`); @@ -68,6 +49,7 @@ describe('Chrome extension', function () { await driver.switchTo().window(tabs[0]); + await delay(1000); const result = await driver.wait( driver .findElement(webdriver.By.xpath('//div[@data-testid="actionListRows"]')) @@ -80,4 +62,15 @@ describe('Chrome extension', function () { ); expect(result).toBeTruthy(); }); + + it("should contain inspector monitor's component", async () => { + const val = await driver + .findElement(webdriver.By.xpath('//div[@data-testid="inspector"]')) + .getText(); + expect(val).toBeDefined(); + }); + + Object.keys(switchMonitorTests).forEach((description) => + it(description, () => switchMonitorTests[description](driver)), + ); }); From 4e8672a52534df639128e4da5860ddf898bfe1b3 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Thu, 15 Aug 2024 22:59:47 -0400 Subject: [PATCH 20/23] Revert unintentional change --- extension/chrome/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/chrome/manifest.json b/extension/chrome/manifest.json index 4799521a27..f72393c844 100644 --- a/extension/chrome/manifest.json +++ b/extension/chrome/manifest.json @@ -18,7 +18,7 @@ }, "_execute_action": { "suggested_key": { - "default": "Ctrl+Shift+F" + "default": "Ctrl+Shift+E" } } }, From 0b19ee28e0991918cdc3201325cf9067b3bd43bb Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Thu, 15 Aug 2024 23:01:31 -0400 Subject: [PATCH 21/23] Skip Electron tests for now Looks like they're still working through stuff in https://github.com/electron/electron/issues/41613 --- extension/test/electron/devpanel.spec.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extension/test/electron/devpanel.spec.js b/extension/test/electron/devpanel.spec.js index 286dc52732..5c37daf5f0 100644 --- a/extension/test/electron/devpanel.spec.js +++ b/extension/test/electron/devpanel.spec.js @@ -76,7 +76,7 @@ describe('DevTools panel for Electron', function () { expect(className).not.toMatch(/hidden/); // not hidden }); - it('should have Redux DevTools UI on current tab', async () => { + it.skip('should have Redux DevTools UI on current tab', async () => { await driver .switchTo() .frame( @@ -87,7 +87,7 @@ describe('DevTools panel for Electron', function () { await delay(1000); }); - it('should contain INIT action', async () => { + it.skip('should contain INIT action', async () => { const element = await driver.wait( webdriver.until.elementLocated( webdriver.By.xpath('//div[@data-testid="actionListRows"]'), @@ -99,7 +99,7 @@ describe('DevTools panel for Electron', function () { expect(val).toMatch(/@@INIT/); }); - it("should contain Inspector monitor's component", async () => { + it.skip("should contain Inspector monitor's component", async () => { const val = await driver .findElement(webdriver.By.xpath('//div[@data-testid="inspector"]')) .getText(); @@ -107,7 +107,7 @@ describe('DevTools panel for Electron', function () { }); Object.keys(switchMonitorTests).forEach((description) => - it(description, () => switchMonitorTests[description](driver)), + it.skip(description, () => switchMonitorTests[description](driver)), ); /* it('should be no logs in console of main window', async () => { From f348e277fcdaab6913b5cd0cb0af16fdb39e084c Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Sat, 17 Aug 2024 14:59:25 -0400 Subject: [PATCH 22/23] Better image centering The Firefox popup did not like the old CSS. This is still not perfect, but it's better than it was. --- extension/src/devpanel/devpanel.pug | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extension/src/devpanel/devpanel.pug b/extension/src/devpanel/devpanel.pug index 517f062267..9b4e97e959 100644 --- a/extension/src/devpanel/devpanel.pug +++ b/extension/src/devpanel/devpanel.pug @@ -8,11 +8,10 @@ html body #root - div(style='position: relative') + div(style='display: flex; justify-content: center; align-items: center') img( src='/img/loading.svg', height=300, width=350, - style='position: absolute; top: 50%; left: 50%; margin-top: -150px; margin-left: -175px;' ) link(href='/devpanel.bundle.css', rel='stylesheet') script(src='/devpanel.bundle.js') From 3941d4fd5b900552621c0fb43e48b546e0f3fa64 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Sat, 17 Aug 2024 15:01:06 -0400 Subject: [PATCH 23/23] Create shaggy-taxis-cross.md --- .changeset/shaggy-taxis-cross.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/shaggy-taxis-cross.md diff --git a/.changeset/shaggy-taxis-cross.md b/.changeset/shaggy-taxis-cross.md new file mode 100644 index 0000000000..cf6cf2ad57 --- /dev/null +++ b/.changeset/shaggy-taxis-cross.md @@ -0,0 +1,5 @@ +--- +'remotedev-redux-devtools-extension': minor +--- + +Upgrade to Manifest V3