From 83e81231668efd2e9e2c98eaaa5819422574e233 Mon Sep 17 00:00:00 2001 From: Mengdi Chen Date: Thu, 25 Aug 2022 16:16:18 -0400 Subject: [PATCH 01/12] upgrade to manifest v3 (chrome) --- .../chrome/manifest.json | 39 +++++++++------ .../src/background.js | 23 +++++---- .../src/hook-exec.js | 17 +++++++ .../src/injectGlobalHook.js | 49 +++---------------- .../webpack.config.js | 1 + 5 files changed, 62 insertions(+), 67 deletions(-) create mode 100644 packages/react-devtools-extensions/src/hook-exec.js diff --git a/packages/react-devtools-extensions/chrome/manifest.json b/packages/react-devtools-extensions/chrome/manifest.json index 5753fbba7ca84..db330925d8042 100644 --- a/packages/react-devtools-extensions/chrome/manifest.json +++ b/packages/react-devtools-extensions/chrome/manifest.json @@ -1,5 +1,5 @@ { - "manifest_version": 2, + "manifest_version": 3, "name": "React Developer Tools", "description": "Adds React debugging tools to the Chrome Developer Tools.", "version": "4.26.1", @@ -11,7 +11,7 @@ "48": "icons/48-production.png", "128": "icons/128-production.png" }, - "browser_action": { + "action": { "default_icon": { "16": "icons/16-disabled.png", "32": "icons/32-disabled.png", @@ -21,23 +21,34 @@ "default_popup": "popups/disabled.html" }, "devtools_page": "main.html", - "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", + + "content_security_policy": { + "extension_pages": "script-src 'self'; object-src 'self'" + }, "web_accessible_resources": [ - "main.html", - "panel.html", - "build/react_devtools_backend.js", - "build/renderer.js" + { + "resources": [ + "main.html", + "panel.html", + "build/react_devtools_backend.js", + "build/renderer.js", + "build/hook-exec.js" + ], + "matches": [ + "" + ], + "extension_ids": [] + } ], "background": { - "scripts": [ - "build/background.js" - ], - "persistent": false + "service_worker": "build/background.js" }, "permissions": [ - "file:///*", - "http://*/*", - "https://*/*" + "storage", + "scripting" + ], + "host_permissions": [ + "" ], "content_scripts": [ { diff --git a/packages/react-devtools-extensions/src/background.js b/packages/react-devtools-extensions/src/background.js index 9e09513b78fb4..a2d9db4a5a08d 100644 --- a/packages/react-devtools-extensions/src/background.js +++ b/packages/react-devtools-extensions/src/background.js @@ -36,11 +36,10 @@ function isNumeric(str: string): boolean { } function installContentScript(tabId: number) { - chrome.tabs.executeScript( - tabId, - {file: '/build/contentScript.js'}, - function() {}, - ); + chrome.scripting.executeScript({ + target: {tabId: tabId}, + files: ['/build/contentScript.js'], + }); } function doublePipe(one, two) { @@ -63,18 +62,18 @@ function doublePipe(one, two) { } function setIconAndPopup(reactBuildType, tabId) { - chrome.browserAction.setIcon({ + chrome.action.setIcon({ tabId: tabId, path: { - '16': 'icons/16-' + reactBuildType + '.png', - '32': 'icons/32-' + reactBuildType + '.png', - '48': 'icons/48-' + reactBuildType + '.png', - '128': 'icons/128-' + reactBuildType + '.png', + '16': chrome.runtime.getURL(`icons/16-${reactBuildType}.png`), + '32': chrome.runtime.getURL(`icons/32-${reactBuildType}.png`), + '48': chrome.runtime.getURL(`icons/48-${reactBuildType}.png`), + '128': chrome.runtime.getURL(`icons/128-${reactBuildType}.png`), }, }); - chrome.browserAction.setPopup({ + chrome.action.setPopup({ tabId: tabId, - popup: 'popups/' + reactBuildType + '.html', + popup: chrome.runtime.getURL(`popups/${reactBuildType}.html`), }); } diff --git a/packages/react-devtools-extensions/src/hook-exec.js b/packages/react-devtools-extensions/src/hook-exec.js new file mode 100644 index 0000000000000..d8b9bde1dfa03 --- /dev/null +++ b/packages/react-devtools-extensions/src/hook-exec.js @@ -0,0 +1,17 @@ +import {installHook} from 'react-devtools-shared/src/hook'; + +installHook(window); + +// detect react +window.__REACT_DEVTOOLS_GLOBAL_HOOK__.on('renderer', function({reactBuildType}) { + window.postMessage({ + source: 'react-devtools-detector', + reactBuildType, + }, '*'); +}); + +// save native values +window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeObjectCreate = Object.create; +window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeMap = Map; +window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeWeakMap = WeakMap; +window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeSet = Set; \ No newline at end of file diff --git a/packages/react-devtools-extensions/src/injectGlobalHook.js b/packages/react-devtools-extensions/src/injectGlobalHook.js index ff9123824e38f..f84d8330b2a69 100644 --- a/packages/react-devtools-extensions/src/injectGlobalHook.js +++ b/packages/react-devtools-extensions/src/injectGlobalHook.js @@ -1,18 +1,20 @@ /* global chrome */ import nullthrows from 'nullthrows'; -import {installHook} from 'react-devtools-shared/src/hook'; import {SESSION_STORAGE_RELOAD_AND_PROFILE_KEY} from 'react-devtools-shared/src/constants'; import {sessionStorageGetItem} from 'react-devtools-shared/src/storage'; -function injectCode(code) { +function injectCode(src) { + console.log('injectCode', src); const script = document.createElement('script'); - script.textContent = code; + script.src = src; + script.onload = () => { + script.remove() + } // This script runs before the element is created, // so we add the script to instead. nullthrows(document.documentElement).appendChild(script); - nullthrows(script.parentNode).removeChild(script); } let lastDetectionResult; @@ -96,38 +98,9 @@ window.addEventListener('pageshow', function({target}) { chrome.runtime.sendMessage(lastDetectionResult); }); -const detectReact = ` -window.__REACT_DEVTOOLS_GLOBAL_HOOK__.on('renderer', function({reactBuildType}) { - window.postMessage({ - source: 'react-devtools-detector', - reactBuildType, - }, '*'); -}); -`; -const saveNativeValues = ` -window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeObjectCreate = Object.create; -window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeMap = Map; -window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeWeakMap = WeakMap; -window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeSet = Set; -`; - // If we have just reloaded to profile, we need to inject the renderer interface before the app loads. if (sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') { - const rendererURL = chrome.runtime.getURL('build/renderer.js'); - let rendererCode; - - // We need to inject in time to catch the initial mount. - // This means we need to synchronously read the renderer code itself, - // and synchronously inject it into the page. - // There are very few ways to actually do this. - // This seems to be the best approach. - const request = new XMLHttpRequest(); - request.addEventListener('load', function() { - rendererCode = this.responseText; - }); - request.open('GET', rendererURL, false); - request.send(); - injectCode(rendererCode); + injectCode(chrome.runtime.getURL('build/renderer.js')); } // Inject a __REACT_DEVTOOLS_GLOBAL_HOOK__ global for React to interact with. @@ -138,13 +111,7 @@ if (sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') { switch (document.contentType) { case 'text/html': case 'application/xhtml+xml': { - injectCode( - ';(' + - installHook.toString() + - '(window))' + - saveNativeValues + - detectReact, - ); + injectCode(chrome.runtime.getURL('build/hook-exec.js')); break; } } diff --git a/packages/react-devtools-extensions/webpack.config.js b/packages/react-devtools-extensions/webpack.config.js index 3a913e0373804..9254c02060650 100644 --- a/packages/react-devtools-extensions/webpack.config.js +++ b/packages/react-devtools-extensions/webpack.config.js @@ -56,6 +56,7 @@ module.exports = { main: './src/main.js', panel: './src/panel.js', renderer: './src/renderer.js', + 'hook-exec': './src/hook-exec.js', }, output: { path: __dirname + '/build', From 2c8b0bca8192a5416cb71f5a5ab12e9f5e9fa36c Mon Sep 17 00:00:00 2001 From: Mengdi Chen Date: Thu, 25 Aug 2022 16:40:56 -0400 Subject: [PATCH 02/12] manifest v3 for edge --- .../edge/manifest.json | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/packages/react-devtools-extensions/edge/manifest.json b/packages/react-devtools-extensions/edge/manifest.json index 0d7bead9d2521..645150f81966e 100644 --- a/packages/react-devtools-extensions/edge/manifest.json +++ b/packages/react-devtools-extensions/edge/manifest.json @@ -1,5 +1,5 @@ { - "manifest_version": 2, + "manifest_version": 3, "name": "React Developer Tools", "description": "Adds React debugging tools to the Microsoft Edge Developer Tools.", "version": "4.26.1", @@ -11,7 +11,7 @@ "48": "icons/48-production.png", "128": "icons/128-production.png" }, - "browser_action": { + "action": { "default_icon": { "16": "icons/16-disabled.png", "32": "icons/32-disabled.png", @@ -21,23 +21,33 @@ "default_popup": "popups/disabled.html" }, "devtools_page": "main.html", - "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", + "content_security_policy": { + "extension_pages": "script-src 'self'; object-src 'self'" + }, "web_accessible_resources": [ - "main.html", - "panel.html", - "build/react_devtools_backend.js", - "build/renderer.js" + { + "resources": [ + "main.html", + "panel.html", + "build/react_devtools_backend.js", + "build/renderer.js", + "build/hook-exec.js" + ], + "matches": [ + "" + ], + "extension_ids": [] + } ], "background": { - "scripts": [ - "build/background.js" - ], - "persistent": false + "service_worker": "build/background.js" }, "permissions": [ - "file:///*", - "http://*/*", - "https://*/*" + "storage", + "scripting" + ], + "host_permissions": [ + "" ], "content_scripts": [ { From 9b238807e9eafb5450b898b263f763f859c5ccc9 Mon Sep 17 00:00:00 2001 From: Mengdi Chen Date: Thu, 25 Aug 2022 16:53:19 -0400 Subject: [PATCH 03/12] fallback logic for firefox --- .../src/background.js | 58 +++++++++++++------ 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/packages/react-devtools-extensions/src/background.js b/packages/react-devtools-extensions/src/background.js index a2d9db4a5a08d..9ca125ccab4b1 100644 --- a/packages/react-devtools-extensions/src/background.js +++ b/packages/react-devtools-extensions/src/background.js @@ -36,10 +36,18 @@ function isNumeric(str: string): boolean { } function installContentScript(tabId: number) { - chrome.scripting.executeScript({ - target: {tabId: tabId}, - files: ['/build/contentScript.js'], - }); + if (IS_FIREFOX) { + chrome.tabs.executeScript( + tabId, + {file: '/build/contentScript.js'}, + function() {}, + ); + } else { + chrome.scripting.executeScript({ + target: {tabId: tabId}, + files: ['/build/contentScript.js'], + }); + } } function doublePipe(one, two) { @@ -62,19 +70,35 @@ function doublePipe(one, two) { } function setIconAndPopup(reactBuildType, tabId) { - chrome.action.setIcon({ - tabId: tabId, - path: { - '16': chrome.runtime.getURL(`icons/16-${reactBuildType}.png`), - '32': chrome.runtime.getURL(`icons/32-${reactBuildType}.png`), - '48': chrome.runtime.getURL(`icons/48-${reactBuildType}.png`), - '128': chrome.runtime.getURL(`icons/128-${reactBuildType}.png`), - }, - }); - chrome.action.setPopup({ - tabId: tabId, - popup: chrome.runtime.getURL(`popups/${reactBuildType}.html`), - }); + if (IS_FIREFOX) { + chrome.browserAction.setIcon({ + tabId: tabId, + path: { + '16': chrome.runtime.getURL(`icons/16-${reactBuildType}.png`), + '32': chrome.runtime.getURL(`icons/32-${reactBuildType}.png`), + '48': chrome.runtime.getURL(`icons/48-${reactBuildType}.png`), + '128': chrome.runtime.getURL(`icons/128-${reactBuildType}.png`), + } + }); + chrome.browserAction.setPopup({ + tabId: tabId, + popup: chrome.runtime.getURL(`popups/${reactBuildType}.html`), + }); + } else { + chrome.action.setIcon({ + tabId: tabId, + path: { + '16': chrome.runtime.getURL(`icons/16-${reactBuildType}.png`), + '32': chrome.runtime.getURL(`icons/32-${reactBuildType}.png`), + '48': chrome.runtime.getURL(`icons/48-${reactBuildType}.png`), + '128': chrome.runtime.getURL(`icons/128-${reactBuildType}.png`), + }, + }); + chrome.action.setPopup({ + tabId: tabId, + popup: chrome.runtime.getURL(`popups/${reactBuildType}.html`), + }); + } } function isRestrictedBrowserPage(url) { From 6ae72f0af024bc8634c98bf5b5e85f7de5b9f075 Mon Sep 17 00:00:00 2001 From: Mengdi Chen Date: Thu, 25 Aug 2022 16:54:39 -0400 Subject: [PATCH 04/12] fix lint --- .../chrome/manifest.json | 1 - .../react-devtools-extensions/src/background.js | 2 +- .../react-devtools-extensions/src/hook-exec.js | 17 +++++++++++------ .../src/injectGlobalHook.js | 5 ++--- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/react-devtools-extensions/chrome/manifest.json b/packages/react-devtools-extensions/chrome/manifest.json index db330925d8042..a0eaf34e65829 100644 --- a/packages/react-devtools-extensions/chrome/manifest.json +++ b/packages/react-devtools-extensions/chrome/manifest.json @@ -21,7 +21,6 @@ "default_popup": "popups/disabled.html" }, "devtools_page": "main.html", - "content_security_policy": { "extension_pages": "script-src 'self'; object-src 'self'" }, diff --git a/packages/react-devtools-extensions/src/background.js b/packages/react-devtools-extensions/src/background.js index 9ca125ccab4b1..f83d7da0939c6 100644 --- a/packages/react-devtools-extensions/src/background.js +++ b/packages/react-devtools-extensions/src/background.js @@ -78,7 +78,7 @@ function setIconAndPopup(reactBuildType, tabId) { '32': chrome.runtime.getURL(`icons/32-${reactBuildType}.png`), '48': chrome.runtime.getURL(`icons/48-${reactBuildType}.png`), '128': chrome.runtime.getURL(`icons/128-${reactBuildType}.png`), - } + }, }); chrome.browserAction.setPopup({ tabId: tabId, diff --git a/packages/react-devtools-extensions/src/hook-exec.js b/packages/react-devtools-extensions/src/hook-exec.js index d8b9bde1dfa03..a7f92cd1c0e6b 100644 --- a/packages/react-devtools-extensions/src/hook-exec.js +++ b/packages/react-devtools-extensions/src/hook-exec.js @@ -3,15 +3,20 @@ import {installHook} from 'react-devtools-shared/src/hook'; installHook(window); // detect react -window.__REACT_DEVTOOLS_GLOBAL_HOOK__.on('renderer', function({reactBuildType}) { - window.postMessage({ - source: 'react-devtools-detector', - reactBuildType, - }, '*'); +window.__REACT_DEVTOOLS_GLOBAL_HOOK__.on('renderer', function({ + reactBuildType, +}) { + window.postMessage( + { + source: 'react-devtools-detector', + reactBuildType, + }, + '*', + ); }); // save native values window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeObjectCreate = Object.create; window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeMap = Map; window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeWeakMap = WeakMap; -window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeSet = Set; \ No newline at end of file +window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeSet = Set; diff --git a/packages/react-devtools-extensions/src/injectGlobalHook.js b/packages/react-devtools-extensions/src/injectGlobalHook.js index f84d8330b2a69..c65e3a3f7a6b8 100644 --- a/packages/react-devtools-extensions/src/injectGlobalHook.js +++ b/packages/react-devtools-extensions/src/injectGlobalHook.js @@ -5,12 +5,11 @@ import {SESSION_STORAGE_RELOAD_AND_PROFILE_KEY} from 'react-devtools-shared/src/ import {sessionStorageGetItem} from 'react-devtools-shared/src/storage'; function injectCode(src) { - console.log('injectCode', src); const script = document.createElement('script'); script.src = src; script.onload = () => { - script.remove() - } + script.remove(); + }; // This script runs before the element is created, // so we add the script to instead. From 12a081cf301db3fc53ce1a1771a5cbce74eed9aa Mon Sep 17 00:00:00 2001 From: Mengdi Chen Date: Thu, 8 Sep 2022 15:21:02 -0400 Subject: [PATCH 05/12] Use new method to inject content scripts + rename files --- .../chrome/manifest.json | 9 ++-- .../edge/manifest.json | 8 +-- .../firefox/manifest.json | 7 ++- .../src/background.js | 41 ++++++++++----- .../installHook.js} | 0 .../prepareInjection.js} | 51 ++++++++++++------- .../proxy.js} | 0 .../src/{ => contentScripts}/renderer.js | 31 ++++++----- .../react-devtools-extensions/src/utils.js | 6 +-- .../webpack.config.js | 8 +-- 10 files changed, 101 insertions(+), 60 deletions(-) rename packages/react-devtools-extensions/src/{hook-exec.js => contentScripts/installHook.js} (100%) rename packages/react-devtools-extensions/src/{injectGlobalHook.js => contentScripts/prepareInjection.js} (72%) rename packages/react-devtools-extensions/src/{contentScript.js => contentScripts/proxy.js} (100%) rename packages/react-devtools-extensions/src/{ => contentScripts}/renderer.js (58%) diff --git a/packages/react-devtools-extensions/chrome/manifest.json b/packages/react-devtools-extensions/chrome/manifest.json index a0eaf34e65829..c92d2663353e8 100644 --- a/packages/react-devtools-extensions/chrome/manifest.json +++ b/packages/react-devtools-extensions/chrome/manifest.json @@ -4,7 +4,7 @@ "description": "Adds React debugging tools to the Chrome Developer Tools.", "version": "4.26.1", "version_name": "4.26.1", - "minimum_chrome_version": "60", + "minimum_chrome_version": "102", "icons": { "16": "icons/16-production.png", "32": "icons/32-production.png", @@ -30,8 +30,10 @@ "main.html", "panel.html", "build/react_devtools_backend.js", + "build/proxy.js", + "build/prepareInjection.js", "build/renderer.js", - "build/hook-exec.js" + "build/installHook.js" ], "matches": [ "" @@ -43,6 +45,7 @@ "service_worker": "build/background.js" }, "permissions": [ + "webNavigation", "storage", "scripting" ], @@ -55,7 +58,7 @@ "" ], "js": [ - "build/injectGlobalHook.js" + "build/prepareInjection.js" ], "run_at": "document_start" } diff --git a/packages/react-devtools-extensions/edge/manifest.json b/packages/react-devtools-extensions/edge/manifest.json index 645150f81966e..1d662e48c609e 100644 --- a/packages/react-devtools-extensions/edge/manifest.json +++ b/packages/react-devtools-extensions/edge/manifest.json @@ -4,7 +4,7 @@ "description": "Adds React debugging tools to the Microsoft Edge Developer Tools.", "version": "4.26.1", "version_name": "4.26.1", - "minimum_chrome_version": "60", + "minimum_chrome_version": "102", "icons": { "16": "icons/16-production.png", "32": "icons/32-production.png", @@ -30,8 +30,10 @@ "main.html", "panel.html", "build/react_devtools_backend.js", + "build/proxy.js", + "build/prepareInjection.js", "build/renderer.js", - "build/hook-exec.js" + "build/installHook.js" ], "matches": [ "" @@ -55,7 +57,7 @@ "" ], "js": [ - "build/injectGlobalHook.js" + "build/prepareInjection.js" ], "run_at": "document_start" } diff --git a/packages/react-devtools-extensions/firefox/manifest.json b/packages/react-devtools-extensions/firefox/manifest.json index 401f0ae1fed82..97326523e9f05 100644 --- a/packages/react-devtools-extensions/firefox/manifest.json +++ b/packages/react-devtools-extensions/firefox/manifest.json @@ -31,7 +31,10 @@ "main.html", "panel.html", "build/react_devtools_backend.js", - "build/renderer.js" + "build/proxy.js", + "build/prepareInjection.js", + "build/renderer.js", + "build/installHook.js" ], "background": { "scripts": [ @@ -50,7 +53,7 @@ "" ], "js": [ - "build/injectGlobalHook.js" + "build/prepareInjection.js" ], "run_at": "document_start" } diff --git a/packages/react-devtools-extensions/src/background.js b/packages/react-devtools-extensions/src/background.js index f83d7da0939c6..b8c7e033d710b 100644 --- a/packages/react-devtools-extensions/src/background.js +++ b/packages/react-devtools-extensions/src/background.js @@ -2,9 +2,33 @@ 'use strict'; +import {IS_FIREFOX} from './utils'; + const ports = {}; -const IS_FIREFOX = navigator.userAgent.indexOf('Firefox') >= 0; +if (!IS_FIREFOX) { + // Manifest V3 method of injecting content scripts (not yet supported in Firefox) + // Note: the "world" option in registerContentScripts is only available in Chrome v102+ + // It's critical since it allows us to directly run scripts on the "main" world on the page + // "document_start" allows it to run before the page's scripts + // so the hook can be detected by react reconciler + chrome.scripting.registerContentScripts([ + { + id: 'hook', + matches: [''], + js: ['build/installHook.js'], + runAt: 'document_start', + world: chrome.scripting.ExecutionWorld.MAIN, + }, + { + id: 'rennderer', + matches: [''], + js: ['build/renderer.js'], + runAt: 'document_start', + world: chrome.scripting.ExecutionWorld.MAIN, + }, + ]); +} chrome.runtime.onConnect.addListener(function(port) { let tab = null; @@ -12,7 +36,7 @@ chrome.runtime.onConnect.addListener(function(port) { if (isNumeric(port.name)) { tab = port.name; name = 'devtools'; - installContentScript(+port.name); + installProxy(+port.name); } else { tab = port.sender.tab.id; name = 'content-script'; @@ -35,17 +59,13 @@ function isNumeric(str: string): boolean { return +str + '' === str; } -function installContentScript(tabId: number) { +function installProxy(tabId: number) { if (IS_FIREFOX) { - chrome.tabs.executeScript( - tabId, - {file: '/build/contentScript.js'}, - function() {}, - ); + chrome.tabs.executeScript(tabId, {file: '/build/proxy.js'}, function() {}); } else { chrome.scripting.executeScript({ target: {tabId: tabId}, - files: ['/build/contentScript.js'], + files: ['/build/proxy.js'], }); } } @@ -146,9 +166,6 @@ chrome.runtime.onMessage.addListener((request, sender) => { // This is sent from the hook content script. // It tells us a renderer has attached. if (request.hasDetectedReact) { - // We use browserAction instead of pageAction because this lets us - // display a custom default popup when React is *not* detected. - // It is specified in the manifest. setIconAndPopup(request.reactBuildType, id); } else { switch (request.payload?.type) { diff --git a/packages/react-devtools-extensions/src/hook-exec.js b/packages/react-devtools-extensions/src/contentScripts/installHook.js similarity index 100% rename from packages/react-devtools-extensions/src/hook-exec.js rename to packages/react-devtools-extensions/src/contentScripts/installHook.js diff --git a/packages/react-devtools-extensions/src/injectGlobalHook.js b/packages/react-devtools-extensions/src/contentScripts/prepareInjection.js similarity index 72% rename from packages/react-devtools-extensions/src/injectGlobalHook.js rename to packages/react-devtools-extensions/src/contentScripts/prepareInjection.js index c65e3a3f7a6b8..0098a61db2ed5 100644 --- a/packages/react-devtools-extensions/src/injectGlobalHook.js +++ b/packages/react-devtools-extensions/src/contentScripts/prepareInjection.js @@ -3,17 +3,24 @@ import nullthrows from 'nullthrows'; import {SESSION_STORAGE_RELOAD_AND_PROFILE_KEY} from 'react-devtools-shared/src/constants'; import {sessionStorageGetItem} from 'react-devtools-shared/src/storage'; +import {IS_FIREFOX} from '../utils'; + +function injectCodeSync(src) { + let code = ''; + const request = new XMLHttpRequest(); + request.addEventListener('load', function() { + code = this.responseText; + }); + request.open('GET', src, false); + request.send(); -function injectCode(src) { const script = document.createElement('script'); - script.src = src; - script.onload = () => { - script.remove(); - }; + script.textContent = code; // This script runs before the element is created, // so we add the script to instead. nullthrows(document.documentElement).appendChild(script); + // nullthrows(script.parentNode).removeChild(script); } let lastDetectionResult; @@ -97,21 +104,27 @@ window.addEventListener('pageshow', function({target}) { chrome.runtime.sendMessage(lastDetectionResult); }); -// If we have just reloaded to profile, we need to inject the renderer interface before the app loads. -if (sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') { - injectCode(chrome.runtime.getURL('build/renderer.js')); -} +// The legacy way to inject the global hook in Manifest V2 extensions. +// In V3, we use chrome.scripting.registerContentScripts instead (see background.js) +if (IS_FIREFOX) { + // If we have just reloaded to profile, we need to inject the renderer interface before the app loads. + if ( + sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true' + ) { + injectCodeSync(chrome.runtime.getURL('build/renderer.js')); + } -// Inject a __REACT_DEVTOOLS_GLOBAL_HOOK__ global for React to interact with. -// Only do this for HTML documents though, to avoid e.g. breaking syntax highlighting for XML docs. -// We need to inject this code because content scripts (ie injectGlobalHook.js) don't have access -// to the webpage's window, so in order to access front end settings -// and communicate with React, we must inject this code into the webpage -switch (document.contentType) { - case 'text/html': - case 'application/xhtml+xml': { - injectCode(chrome.runtime.getURL('build/hook-exec.js')); - break; + // Inject a __REACT_DEVTOOLS_GLOBAL_HOOK__ global for React to interact with. + // Only do this for HTML documents though, to avoid e.g. breaking syntax highlighting for XML docs. + // We need to inject this code because content scripts (ie injectGlobalHook.js) don't have access + // to the webpage's window, so in order to access front end settings + // and communicate with React, we must inject this code into the webpage + switch (document.contentType) { + case 'text/html': + case 'application/xhtml+xml': { + injectCodeSync(chrome.runtime.getURL('build/installHook.js')); + break; + } } } diff --git a/packages/react-devtools-extensions/src/contentScript.js b/packages/react-devtools-extensions/src/contentScripts/proxy.js similarity index 100% rename from packages/react-devtools-extensions/src/contentScript.js rename to packages/react-devtools-extensions/src/contentScripts/proxy.js diff --git a/packages/react-devtools-extensions/src/renderer.js b/packages/react-devtools-extensions/src/contentScripts/renderer.js similarity index 58% rename from packages/react-devtools-extensions/src/renderer.js rename to packages/react-devtools-extensions/src/contentScripts/renderer.js index e9c9639f51f22..559f7ba6ecb43 100644 --- a/packages/react-devtools-extensions/src/renderer.js +++ b/packages/react-devtools-extensions/src/contentScripts/renderer.js @@ -9,18 +9,21 @@ */ import {attach} from 'react-devtools-shared/src/backend/renderer'; +import {IS_FIREFOX} from '../utils'; -Object.defineProperty( - window, - '__REACT_DEVTOOLS_ATTACH__', - ({ - enumerable: false, - // This property needs to be configurable to allow third-party integrations - // to attach their own renderer. Note that using third-party integrations - // is not officially supported. Use at your own risk. - configurable: true, - get() { - return attach; - }, - }: Object), -); +if (IS_FIREFOX) { + Object.defineProperty( + window, + '__REACT_DEVTOOLS_ATTACH__', + ({ + enumerable: false, + // This property needs to be configurable to allow third-party integrations + // to attach their own renderer. Note that using third-party integrations + // is not officially supported. Use at your own risk. + configurable: true, + get() { + return attach; + }, + }: Object), + ); +} diff --git a/packages/react-devtools-extensions/src/utils.js b/packages/react-devtools-extensions/src/utils.js index ea2bd27ee992a..c34c01d21d0a7 100644 --- a/packages/react-devtools-extensions/src/utils.js +++ b/packages/react-devtools-extensions/src/utils.js @@ -2,9 +2,9 @@ import type {BrowserTheme} from 'react-devtools-shared/src/devtools/views/DevTools'; -const IS_EDGE = navigator.userAgent.indexOf('Edg') >= 0; -const IS_FIREFOX = navigator.userAgent.indexOf('Firefox') >= 0; -const IS_CHROME = IS_EDGE === false && IS_FIREFOX === false; +export const IS_EDGE = navigator.userAgent.indexOf('Edg') >= 0; +export const IS_FIREFOX = navigator.userAgent.indexOf('Firefox') >= 0; +export const IS_CHROME = IS_EDGE === false && IS_FIREFOX === false; export type BrowserName = 'Chrome' | 'Firefox' | 'Edge'; diff --git a/packages/react-devtools-extensions/webpack.config.js b/packages/react-devtools-extensions/webpack.config.js index 9254c02060650..ffbf153a8db4a 100644 --- a/packages/react-devtools-extensions/webpack.config.js +++ b/packages/react-devtools-extensions/webpack.config.js @@ -51,12 +51,12 @@ module.exports = { devtool: __DEV__ ? 'cheap-module-eval-source-map' : false, entry: { background: './src/background.js', - contentScript: './src/contentScript.js', - injectGlobalHook: './src/injectGlobalHook.js', main: './src/main.js', panel: './src/panel.js', - renderer: './src/renderer.js', - 'hook-exec': './src/hook-exec.js', + proxy: './src/contentScripts/proxy.js', + prepareInjection: './src/contentScripts/prepareInjection.js', + renderer: './src/contentScripts/renderer.js', + installHook: './src/contentScripts/installHook.js', }, output: { path: __dirname + '/build', From 78f35701e4cb7670fdb1e58284e54fb5a176ce1f Mon Sep 17 00:00:00 2001 From: Mengdi Chen Date: Thu, 8 Sep 2022 15:29:15 -0400 Subject: [PATCH 06/12] cleanup --- packages/react-devtools-extensions/chrome/manifest.json | 1 - .../src/contentScripts/prepareInjection.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/react-devtools-extensions/chrome/manifest.json b/packages/react-devtools-extensions/chrome/manifest.json index c92d2663353e8..d883c0f1b76b2 100644 --- a/packages/react-devtools-extensions/chrome/manifest.json +++ b/packages/react-devtools-extensions/chrome/manifest.json @@ -45,7 +45,6 @@ "service_worker": "build/background.js" }, "permissions": [ - "webNavigation", "storage", "scripting" ], diff --git a/packages/react-devtools-extensions/src/contentScripts/prepareInjection.js b/packages/react-devtools-extensions/src/contentScripts/prepareInjection.js index 0098a61db2ed5..9e7990c9dcaa1 100644 --- a/packages/react-devtools-extensions/src/contentScripts/prepareInjection.js +++ b/packages/react-devtools-extensions/src/contentScripts/prepareInjection.js @@ -20,7 +20,7 @@ function injectCodeSync(src) { // This script runs before the element is created, // so we add the script to instead. nullthrows(document.documentElement).appendChild(script); - // nullthrows(script.parentNode).removeChild(script); + nullthrows(script.parentNode).removeChild(script); } let lastDetectionResult; From e53b5fe73272125322e4adda75253b8d42a65750 Mon Sep 17 00:00:00 2001 From: Mengdi Chen Date: Thu, 8 Sep 2022 16:17:45 -0400 Subject: [PATCH 07/12] fix renderer logic --- .../react-devtools-extensions/src/contentScripts/renderer.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/react-devtools-extensions/src/contentScripts/renderer.js b/packages/react-devtools-extensions/src/contentScripts/renderer.js index 559f7ba6ecb43..238b765587624 100644 --- a/packages/react-devtools-extensions/src/contentScripts/renderer.js +++ b/packages/react-devtools-extensions/src/contentScripts/renderer.js @@ -9,9 +9,10 @@ */ import {attach} from 'react-devtools-shared/src/backend/renderer'; -import {IS_FIREFOX} from '../utils'; +import {SESSION_STORAGE_RELOAD_AND_PROFILE_KEY} from 'react-devtools-shared/src/constants'; +import {sessionStorageGetItem} from 'react-devtools-shared/src/storage'; -if (IS_FIREFOX) { +if (sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') { Object.defineProperty( window, '__REACT_DEVTOOLS_ATTACH__', From 4835a5185e3d187592b77f1ce4f117743c9c35c9 Mon Sep 17 00:00:00 2001 From: Mengdi Chen Date: Mon, 26 Sep 2022 20:17:28 -0400 Subject: [PATCH 08/12] fallback logic when world is not supported --- .../chrome/manifest.json | 2 +- .../edge/manifest.json | 2 +- .../src/background.js | 2 +- .../src/contentScripts/installHook.js | 39 +++++----- .../src/contentScripts/prepareInjection.js | 59 ++++++++------- .../src/contentScripts/renderer.js | 5 +- .../src/devtools/views/Components/Tree.css | 19 +++++ .../src/devtools/views/Components/Tree.js | 72 ++++++++++++------- packages/react-devtools/README.md | 5 ++ 9 files changed, 132 insertions(+), 73 deletions(-) diff --git a/packages/react-devtools-extensions/chrome/manifest.json b/packages/react-devtools-extensions/chrome/manifest.json index d883c0f1b76b2..9faca84dc813d 100644 --- a/packages/react-devtools-extensions/chrome/manifest.json +++ b/packages/react-devtools-extensions/chrome/manifest.json @@ -4,7 +4,7 @@ "description": "Adds React debugging tools to the Chrome Developer Tools.", "version": "4.26.1", "version_name": "4.26.1", - "minimum_chrome_version": "102", + "minimum_chrome_version": "88", "icons": { "16": "icons/16-production.png", "32": "icons/32-production.png", diff --git a/packages/react-devtools-extensions/edge/manifest.json b/packages/react-devtools-extensions/edge/manifest.json index 1d662e48c609e..b932d5a9b395d 100644 --- a/packages/react-devtools-extensions/edge/manifest.json +++ b/packages/react-devtools-extensions/edge/manifest.json @@ -4,7 +4,7 @@ "description": "Adds React debugging tools to the Microsoft Edge Developer Tools.", "version": "4.26.1", "version_name": "4.26.1", - "minimum_chrome_version": "102", + "minimum_chrome_version": "88", "icons": { "16": "icons/16-production.png", "32": "icons/32-production.png", diff --git a/packages/react-devtools-extensions/src/background.js b/packages/react-devtools-extensions/src/background.js index b8c7e033d710b..7120dfa17a1d7 100644 --- a/packages/react-devtools-extensions/src/background.js +++ b/packages/react-devtools-extensions/src/background.js @@ -21,7 +21,7 @@ if (!IS_FIREFOX) { world: chrome.scripting.ExecutionWorld.MAIN, }, { - id: 'rennderer', + id: 'renderer', matches: [''], js: ['build/renderer.js'], runAt: 'document_start', diff --git a/packages/react-devtools-extensions/src/contentScripts/installHook.js b/packages/react-devtools-extensions/src/contentScripts/installHook.js index a7f92cd1c0e6b..8517ff5ff999f 100644 --- a/packages/react-devtools-extensions/src/contentScripts/installHook.js +++ b/packages/react-devtools-extensions/src/contentScripts/installHook.js @@ -1,22 +1,25 @@ import {installHook} from 'react-devtools-shared/src/hook'; -installHook(window); +// avoid double execution +if (!window.hasOwnProperty('__REACT_DEVTOOLS_GLOBAL_HOOK__')) { + installHook(window); -// detect react -window.__REACT_DEVTOOLS_GLOBAL_HOOK__.on('renderer', function({ - reactBuildType, -}) { - window.postMessage( - { - source: 'react-devtools-detector', - reactBuildType, - }, - '*', - ); -}); + // detect react + window.__REACT_DEVTOOLS_GLOBAL_HOOK__.on('renderer', function({ + reactBuildType, + }) { + window.postMessage( + { + source: 'react-devtools-detector', + reactBuildType, + }, + '*', + ); + }); -// save native values -window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeObjectCreate = Object.create; -window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeMap = Map; -window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeWeakMap = WeakMap; -window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeSet = Set; + // save native values + window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeObjectCreate = Object.create; + window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeMap = Map; + window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeWeakMap = WeakMap; + window.__REACT_DEVTOOLS_GLOBAL_HOOK__.nativeSet = Set; +} diff --git a/packages/react-devtools-extensions/src/contentScripts/prepareInjection.js b/packages/react-devtools-extensions/src/contentScripts/prepareInjection.js index 9e7990c9dcaa1..cfe5dabfa7de2 100644 --- a/packages/react-devtools-extensions/src/contentScripts/prepareInjection.js +++ b/packages/react-devtools-extensions/src/contentScripts/prepareInjection.js @@ -5,7 +5,7 @@ import {SESSION_STORAGE_RELOAD_AND_PROFILE_KEY} from 'react-devtools-shared/src/ import {sessionStorageGetItem} from 'react-devtools-shared/src/storage'; import {IS_FIREFOX} from '../utils'; -function injectCodeSync(src) { +function injectScriptSync(src) { let code = ''; const request = new XMLHttpRequest(); request.addEventListener('load', function() { @@ -23,6 +23,15 @@ function injectCodeSync(src) { nullthrows(script.parentNode).removeChild(script); } +function injectScriptAsync(src) { + const script = document.createElement('script'); + script.src = src; + script.onload = function() { + script.remove(); + }; + nullthrows(document.documentElement).appendChild(script); +} + let lastDetectionResult; // We want to detect when a renderer attaches, and notify the "background page" @@ -85,10 +94,7 @@ window.addEventListener('message', function onMessage({data, source}) { } break; case 'react-devtools-inject-backend': - const script = document.createElement('script'); - script.src = chrome.runtime.getURL('build/react_devtools_backend.js'); - document.documentElement.appendChild(script); - script.parentNode.removeChild(script); + injectScriptAsync(chrome.runtime.getURL('build/react_devtools_backend.js')); break; } }); @@ -104,27 +110,30 @@ window.addEventListener('pageshow', function({target}) { chrome.runtime.sendMessage(lastDetectionResult); }); -// The legacy way to inject the global hook in Manifest V2 extensions. -// In V3, we use chrome.scripting.registerContentScripts instead (see background.js) -if (IS_FIREFOX) { - // If we have just reloaded to profile, we need to inject the renderer interface before the app loads. - if ( - sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true' - ) { - injectCodeSync(chrome.runtime.getURL('build/renderer.js')); - } +// We create a "sync" script tag to page to inject the global hook on Manifest V2 extensions. +// To comply with the new security policy in V3, we use chrome.scripting.registerContentScripts instead (see background.js). +// However, the new API only works for Chrome v102+. +// We insert a "async" script tag as a fallback for older versions. +// It has known issues if JS on the page is faster than the extension. +// Users will see a notice in components tab when that happens (see ). +// For Firefox, V3 is not ready, so sync injection is still the best approach. +const injectScript = IS_FIREFOX ? injectScriptSync : injectScriptAsync; - // Inject a __REACT_DEVTOOLS_GLOBAL_HOOK__ global for React to interact with. - // Only do this for HTML documents though, to avoid e.g. breaking syntax highlighting for XML docs. - // We need to inject this code because content scripts (ie injectGlobalHook.js) don't have access - // to the webpage's window, so in order to access front end settings - // and communicate with React, we must inject this code into the webpage - switch (document.contentType) { - case 'text/html': - case 'application/xhtml+xml': { - injectCodeSync(chrome.runtime.getURL('build/installHook.js')); - break; - } +// If we have just reloaded to profile, we need to inject the renderer interface before the app loads. +if (sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') { + injectScript(chrome.runtime.getURL('build/renderer.js')); +} + +// Inject a __REACT_DEVTOOLS_GLOBAL_HOOK__ global for React to interact with. +// Only do this for HTML documents though, to avoid e.g. breaking syntax highlighting for XML docs. +// We need to inject this code because content scripts (ie injectGlobalHook.js) don't have access +// to the webpage's window, so in order to access front end settings +// and communicate with React, we must inject this code into the webpage +switch (document.contentType) { + case 'text/html': + case 'application/xhtml+xml': { + injectScript(chrome.runtime.getURL('build/installHook.js')); + break; } } diff --git a/packages/react-devtools-extensions/src/contentScripts/renderer.js b/packages/react-devtools-extensions/src/contentScripts/renderer.js index 238b765587624..ab02997bbaab8 100644 --- a/packages/react-devtools-extensions/src/contentScripts/renderer.js +++ b/packages/react-devtools-extensions/src/contentScripts/renderer.js @@ -12,7 +12,10 @@ import {attach} from 'react-devtools-shared/src/backend/renderer'; import {SESSION_STORAGE_RELOAD_AND_PROFILE_KEY} from 'react-devtools-shared/src/constants'; import {sessionStorageGetItem} from 'react-devtools-shared/src/storage'; -if (sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') { +if ( + sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true' && + !window.hasOwnProperty('__REACT_DEVTOOLS_ATTACH__') +) { Object.defineProperty( window, '__REACT_DEVTOOLS_ATTACH__', diff --git a/packages/react-devtools-shared/src/devtools/views/Components/Tree.css b/packages/react-devtools-shared/src/devtools/views/Components/Tree.css index 2036695a50e34..bf18f1d2e6019 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/Tree.css +++ b/packages/react-devtools-shared/src/devtools/views/Components/Tree.css @@ -79,3 +79,22 @@ .WarningIcon { color: var(--color-console-warning-icon); } + +.ZeroElementsNotice { + padding-left: 1em; + opacity: 0; + animation: fadeIn 0.5s forwards; + animation-delay: 2s; +} +@keyframes fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +.Link { + color: var(--color-button-active); +} \ No newline at end of file diff --git a/packages/react-devtools-shared/src/devtools/views/Components/Tree.js b/packages/react-devtools-shared/src/devtools/views/Components/Tree.js index a7c4048da2c98..34dfa933c6946 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/Tree.js +++ b/packages/react-devtools-shared/src/devtools/views/Components/Tree.js @@ -344,6 +344,22 @@ export default function Tree(props: Props): React.Node { clearErrorsAndWarningsAPI({bridge, store}); }; + const zeroElementsNotice = ( +
+

Loading elements from the React app...

+

+ If this seems stuck, please follow the{' '} + + troubleshooting instructions + + . +

+
+ ); + return (
@@ -398,32 +414,36 @@ export default function Tree(props: Props): React.Node { )}
-
- - {({height, width}) => ( - - {Element} - - )} - -
+ {numElements === 0 ? ( + zeroElementsNotice + ) : ( +
+ + {({height, width}) => ( + + {Element} + + )} + +
+ )}
); diff --git a/packages/react-devtools/README.md b/packages/react-devtools/README.md index 07d811ec223da..aa96235f2091a 100644 --- a/packages/react-devtools/README.md +++ b/packages/react-devtools/README.md @@ -105,8 +105,13 @@ Or you could develop with a local HTTP server like [`serve`](https://www.npmjs.c **If your app is inside an iframe, a Chrome extension, React Native, or in another unusual environment**, try [the standalone version instead](https://github.com/facebook/react/tree/main/packages/react-devtools). Chrome apps are currently not inspectable. +**If your React DevTools tab is showing an empty tree**, please read the "the issue with Chrome v101 and earlier versions" part below. + **If you still have issues** please [report them](https://github.com/facebook/react/issues/new?labels=Component:%20Developer%20Tools). Don't forget to specify your OS, browser version, extension version, and the exact instructions to reproduce the issue with a screenshot. +### The Issue with Chrome v101 and earlier versions +As we migrate to a Chrome Extension Manifest V3, we start to use a new method to hook the DevTools with the inspected page. This new method is more secure, but relies on a new API that's only supported in Chrome v102+. For Chrome v101 or earlier, we use a fallback method, which can cause malfunctions (e.g. an empty tree in the React component tab) if the JS resources on your page is loaded from cache. Please upgrade to Chrome v102+ to avoid this issue. + ## Local development The standalone DevTools app can be built and tested from source following the instructions below. From 0edb72a419556889e7cf805d0f1a272e73a5ebdf Mon Sep 17 00:00:00 2001 From: Mengdi Chen Date: Mon, 26 Sep 2022 20:25:53 -0400 Subject: [PATCH 09/12] cleaner code --- .../src/background.js | 43 ++++++------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/packages/react-devtools-extensions/src/background.js b/packages/react-devtools-extensions/src/background.js index 7120dfa17a1d7..5b33330994581 100644 --- a/packages/react-devtools-extensions/src/background.js +++ b/packages/react-devtools-extensions/src/background.js @@ -90,35 +90,20 @@ function doublePipe(one, two) { } function setIconAndPopup(reactBuildType, tabId) { - if (IS_FIREFOX) { - chrome.browserAction.setIcon({ - tabId: tabId, - path: { - '16': chrome.runtime.getURL(`icons/16-${reactBuildType}.png`), - '32': chrome.runtime.getURL(`icons/32-${reactBuildType}.png`), - '48': chrome.runtime.getURL(`icons/48-${reactBuildType}.png`), - '128': chrome.runtime.getURL(`icons/128-${reactBuildType}.png`), - }, - }); - chrome.browserAction.setPopup({ - tabId: tabId, - popup: chrome.runtime.getURL(`popups/${reactBuildType}.html`), - }); - } else { - chrome.action.setIcon({ - tabId: tabId, - path: { - '16': chrome.runtime.getURL(`icons/16-${reactBuildType}.png`), - '32': chrome.runtime.getURL(`icons/32-${reactBuildType}.png`), - '48': chrome.runtime.getURL(`icons/48-${reactBuildType}.png`), - '128': chrome.runtime.getURL(`icons/128-${reactBuildType}.png`), - }, - }); - chrome.action.setPopup({ - tabId: tabId, - popup: chrome.runtime.getURL(`popups/${reactBuildType}.html`), - }); - } + const action = IS_FIREFOX ? chrome.browserAction : chrome.action; + action.setIcon({ + tabId: tabId, + path: { + '16': chrome.runtime.getURL(`icons/16-${reactBuildType}.png`), + '32': chrome.runtime.getURL(`icons/32-${reactBuildType}.png`), + '48': chrome.runtime.getURL(`icons/48-${reactBuildType}.png`), + '128': chrome.runtime.getURL(`icons/128-${reactBuildType}.png`), + }, + }); + action.setPopup({ + tabId: tabId, + popup: chrome.runtime.getURL(`popups/${reactBuildType}.html`), + }); } function isRestrictedBrowserPage(url) { From ba9dc3b75a8ac1932b063c65e7a85efdb522ff2f Mon Sep 17 00:00:00 2001 From: Mengdi Chen Date: Tue, 27 Sep 2022 11:07:51 -0400 Subject: [PATCH 10/12] fix lint --- .../src/contentScripts/prepareInjection.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/react-devtools-extensions/src/contentScripts/prepareInjection.js b/packages/react-devtools-extensions/src/contentScripts/prepareInjection.js index cfe5dabfa7de2..c609b58c909d6 100644 --- a/packages/react-devtools-extensions/src/contentScripts/prepareInjection.js +++ b/packages/react-devtools-extensions/src/contentScripts/prepareInjection.js @@ -94,7 +94,9 @@ window.addEventListener('message', function onMessage({data, source}) { } break; case 'react-devtools-inject-backend': - injectScriptAsync(chrome.runtime.getURL('build/react_devtools_backend.js')); + injectScriptAsync( + chrome.runtime.getURL('build/react_devtools_backend.js'), + ); break; } }); From 53572272a6eaa0dd0f330661e44a0ead43bf0dd2 Mon Sep 17 00:00:00 2001 From: Mengdi Chen Date: Fri, 21 Oct 2022 19:14:28 -0400 Subject: [PATCH 11/12] minor improvements per review --- packages/react-devtools-extensions/chrome/manifest.json | 1 - packages/react-devtools-extensions/edge/manifest.json | 1 - .../src/devtools/views/Components/Tree.js | 2 +- packages/react-devtools/README.md | 4 ++-- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/react-devtools-extensions/chrome/manifest.json b/packages/react-devtools-extensions/chrome/manifest.json index 9faca84dc813d..ffb1afc80de90 100644 --- a/packages/react-devtools-extensions/chrome/manifest.json +++ b/packages/react-devtools-extensions/chrome/manifest.json @@ -31,7 +31,6 @@ "panel.html", "build/react_devtools_backend.js", "build/proxy.js", - "build/prepareInjection.js", "build/renderer.js", "build/installHook.js" ], diff --git a/packages/react-devtools-extensions/edge/manifest.json b/packages/react-devtools-extensions/edge/manifest.json index b932d5a9b395d..16e9790f943ab 100644 --- a/packages/react-devtools-extensions/edge/manifest.json +++ b/packages/react-devtools-extensions/edge/manifest.json @@ -31,7 +31,6 @@ "panel.html", "build/react_devtools_backend.js", "build/proxy.js", - "build/prepareInjection.js", "build/renderer.js", "build/installHook.js" ], diff --git a/packages/react-devtools-shared/src/devtools/views/Components/Tree.js b/packages/react-devtools-shared/src/devtools/views/Components/Tree.js index 34dfa933c6946..106bda6d36123 100644 --- a/packages/react-devtools-shared/src/devtools/views/Components/Tree.js +++ b/packages/react-devtools-shared/src/devtools/views/Components/Tree.js @@ -346,7 +346,7 @@ export default function Tree(props: Props): React.Node { const zeroElementsNotice = (
-

Loading elements from the React app...

+

Loading React Element Tree...

If this seems stuck, please follow the{' '} Date: Fri, 21 Oct 2022 21:30:50 -0400 Subject: [PATCH 12/12] more nits --- packages/react-devtools-extensions/firefox/manifest.json | 1 - packages/react-devtools/README.md | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/react-devtools-extensions/firefox/manifest.json b/packages/react-devtools-extensions/firefox/manifest.json index 97326523e9f05..953ad1efff9c2 100644 --- a/packages/react-devtools-extensions/firefox/manifest.json +++ b/packages/react-devtools-extensions/firefox/manifest.json @@ -32,7 +32,6 @@ "panel.html", "build/react_devtools_backend.js", "build/proxy.js", - "build/prepareInjection.js", "build/renderer.js", "build/installHook.js" ], diff --git a/packages/react-devtools/README.md b/packages/react-devtools/README.md index 6f83cefc17cb1..60e31025c01a6 100644 --- a/packages/react-devtools/README.md +++ b/packages/react-devtools/README.md @@ -109,7 +109,7 @@ Or you could develop with a local HTTP server like [`serve`](https://www.npmjs.c **If you still have issues** please [report them](https://github.com/facebook/react/issues/new?labels=Component:%20Developer%20Tools). Don't forget to specify your OS, browser version, extension version, and the exact instructions to reproduce the issue with a screenshot. -### The Issue with Chrome v101 and earlier versions +### The Issue with Chrome v101 and earlier As we migrate to a Chrome Extension Manifest V3, we start to use a new method to hook the DevTools with the inspected page. This new method is more secure, but relies on a new API that's only supported in Chrome v102+. For Chrome v101 or earlier, we use a fallback method, which can cause malfunctions (e.g. empty React component tab) if the JS resources on your page is loaded from cache. Please upgrade to Chrome v102+ to avoid this issue. ## Local development