diff --git a/packages/react-devtools-extensions/src/inject.js b/packages/react-devtools-extensions/src/inject.js deleted file mode 100644 index 938e2cf7eb22..000000000000 --- a/packages/react-devtools-extensions/src/inject.js +++ /dev/null @@ -1,24 +0,0 @@ -/* global chrome */ - -export default function inject(scriptName: string, done: ?Function) { - const source = ` - // the prototype stuff is in case document.createElement has been modified - (function () { - var script = document.constructor.prototype.createElement.call(document, 'script'); - script.src = "${scriptName}"; - script.charset = "utf-8"; - document.documentElement.appendChild(script); - script.parentNode.removeChild(script); - })() - `; - - chrome.devtools.inspectedWindow.eval(source, function(response, error) { - if (error) { - console.log(error); - } - - if (typeof done === 'function') { - done(); - } - }); -} diff --git a/packages/react-devtools-extensions/src/injectGlobalHook.js b/packages/react-devtools-extensions/src/injectGlobalHook.js index b56f9a299b37..2347671d24a5 100644 --- a/packages/react-devtools-extensions/src/injectGlobalHook.js +++ b/packages/react-devtools-extensions/src/injectGlobalHook.js @@ -24,16 +24,26 @@ let lastDetectionResult; // So instead, the hook will use postMessage() to pass message to us here. // And when this happens, we'll send a message to the "background page". window.addEventListener('message', function(evt) { - if ( - evt.source === window && - evt.data && - evt.data.source === 'react-devtools-detector' - ) { - lastDetectionResult = { - hasDetectedReact: true, - reactBuildType: evt.data.reactBuildType, - }; - chrome.runtime.sendMessage(lastDetectionResult); + if (evt.source === window && evt.data) { + if (evt.data.source === 'react-devtools-detector') { + lastDetectionResult = { + hasDetectedReact: true, + reactBuildType: evt.data.reactBuildType, + }; + chrome.runtime.sendMessage(lastDetectionResult); + } else if (evt.data.source === 'react-devtools-inject-backend') { + // The backend is injected by the content script to avoid CSP and Trusted Types violations, + // since content scripts can modify the DOM and are not subject to the page's policies. + // The prototype stuff is in case document.createElement has been modified. + const script = document.constructor.prototype.createElement.call( + document, + 'script', + ); + script.src = chrome.runtime.getURL('build/backend.js'); + script.charset = 'utf-8'; + document.documentElement.appendChild(script); + script.parentNode.removeChild(script); + } } }); diff --git a/packages/react-devtools-extensions/src/main.js b/packages/react-devtools-extensions/src/main.js index 7d786e032191..fd81dc711d85 100644 --- a/packages/react-devtools-extensions/src/main.js +++ b/packages/react-devtools-extensions/src/main.js @@ -4,7 +4,6 @@ import {createElement} from 'react'; import {unstable_createRoot as createRoot, flushSync} from 'react-dom'; import Bridge from 'react-devtools-shared/src/bridge'; import Store from 'react-devtools-shared/src/devtools/store'; -import inject from './inject'; import { createViewElementSource, getBrowserName, @@ -135,7 +134,14 @@ function createPanelIfReactLoaded() { // Initialize the backend only once the Store has been initialized. // Otherwise the Store may miss important initial tree op codes. - inject(chrome.runtime.getURL('build/backend.js')); + chrome.devtools.inspectedWindow.eval( + `window.postMessage({ source: 'react-devtools-inject-backend' });`, + function(response, evalError) { + if (evalError) { + console.log(evalError); + } + }, + ); const viewElementSourceFunction = createViewElementSource( bridge,