From c93038fabe08f7dc26b1a301ed4518a3bc64102f Mon Sep 17 00:00:00 2001 From: Stephanie Ding Date: Tue, 10 Sep 2019 17:22:04 -0700 Subject: [PATCH 1/8] Moved backend injection logic to content script --- .../react-devtools-extensions/src/inject.js | 11 ++---- .../src/injectGlobalHook.js | 39 ++++++++++++------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/packages/react-devtools-extensions/src/inject.js b/packages/react-devtools-extensions/src/inject.js index 938e2cf7eb22..02750dbb06a8 100644 --- a/packages/react-devtools-extensions/src/inject.js +++ b/packages/react-devtools-extensions/src/inject.js @@ -1,14 +1,9 @@ /* global chrome */ -export default function inject(scriptName: string, done: ?Function) { +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); + window.postMessage({ source: 'react-devtools-inject-backend', type: "FROM_PAGE", text: "Hello from the webpage!" }, "*"); })() `; @@ -21,4 +16,4 @@ export default function inject(scriptName: string, done: ?Function) { done(); } }); -} +} \ 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 b56f9a299b37..a06b9bbf6294 100644 --- a/packages/react-devtools-extensions/src/injectGlobalHook.js +++ b/packages/react-devtools-extensions/src/injectGlobalHook.js @@ -1,9 +1,15 @@ /* 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'; +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) { const script = document.createElement('script'); @@ -24,16 +30,21 @@ 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') { + //Inject backend + var 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); + } } }); @@ -86,4 +97,4 @@ if (sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') { // devtools are installed (and skip its suggestion to install the devtools). injectCode( ';(' + installHook.toString() + '(window))' + saveNativeValues + detectReact, -); +); \ No newline at end of file From 788036c7ed51fa2a7fc021bbe3807762072dee33 Mon Sep 17 00:00:00 2001 From: Stephanie Ding Date: Tue, 10 Sep 2019 17:34:12 -0700 Subject: [PATCH 2/8] Moved backend injection logic to content script --- packages/react-devtools-extensions/src/inject.js | 2 +- packages/react-devtools-extensions/src/injectGlobalHook.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/react-devtools-extensions/src/inject.js b/packages/react-devtools-extensions/src/inject.js index 02750dbb06a8..909a192b53e5 100644 --- a/packages/react-devtools-extensions/src/inject.js +++ b/packages/react-devtools-extensions/src/inject.js @@ -3,7 +3,7 @@ export default function inject(scriptName: string, done: ? Function) { const source = ` (function () { - window.postMessage({ source: 'react-devtools-inject-backend', type: "FROM_PAGE", text: "Hello from the webpage!" }, "*"); + window.postMessage({ source: 'react-devtools-inject-script', scriptName: "${scriptName}" }, "*"); })() `; diff --git a/packages/react-devtools-extensions/src/injectGlobalHook.js b/packages/react-devtools-extensions/src/injectGlobalHook.js index a06b9bbf6294..e35c675e1f29 100644 --- a/packages/react-devtools-extensions/src/injectGlobalHook.js +++ b/packages/react-devtools-extensions/src/injectGlobalHook.js @@ -37,10 +37,10 @@ window.addEventListener('message', function(evt) { reactBuildType: evt.data.reactBuildType, }; chrome.runtime.sendMessage(lastDetectionResult); - } else if (evt.data.source === 'react-devtools-inject-backend') { + } else if (evt.data.source === 'react-devtools-inject-backend' && evt.data.scriptName) { //Inject backend var script = document.constructor.prototype.createElement.call(document, 'script'); - script.src = chrome.runtime.getURL('build/backend.js'); + script.src = evt.data.scriptName; script.charset = "utf-8"; document.documentElement.appendChild(script); script.parentNode.removeChild(script); @@ -97,4 +97,4 @@ if (sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') { // devtools are installed (and skip its suggestion to install the devtools). injectCode( ';(' + installHook.toString() + '(window))' + saveNativeValues + detectReact, -); \ No newline at end of file +); From 85c7211014a1364fef8d0c3da512ddf63fb408e8 Mon Sep 17 00:00:00 2001 From: Stephanie Ding Date: Tue, 10 Sep 2019 17:45:27 -0700 Subject: [PATCH 3/8] Moved injection logic to content script --- packages/react-devtools-extensions/src/inject.js | 2 +- packages/react-devtools-extensions/src/injectGlobalHook.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-devtools-extensions/src/inject.js b/packages/react-devtools-extensions/src/inject.js index 909a192b53e5..b2f4a03e1295 100644 --- a/packages/react-devtools-extensions/src/inject.js +++ b/packages/react-devtools-extensions/src/inject.js @@ -1,6 +1,6 @@ /* global chrome */ -export default function inject(scriptName: string, done: ? Function) { +export default function inject(scriptName: string, done: ?Function) { const source = ` (function () { window.postMessage({ source: 'react-devtools-inject-script', scriptName: "${scriptName}" }, "*"); diff --git a/packages/react-devtools-extensions/src/injectGlobalHook.js b/packages/react-devtools-extensions/src/injectGlobalHook.js index e35c675e1f29..dd8bf9def7b4 100644 --- a/packages/react-devtools-extensions/src/injectGlobalHook.js +++ b/packages/react-devtools-extensions/src/injectGlobalHook.js @@ -37,8 +37,8 @@ window.addEventListener('message', function(evt) { reactBuildType: evt.data.reactBuildType, }; chrome.runtime.sendMessage(lastDetectionResult); - } else if (evt.data.source === 'react-devtools-inject-backend' && evt.data.scriptName) { - //Inject backend + } else if (evt.data.source === 'react-devtools-inject-script' && evt.data.scriptName) { + //Inject the specified script var script = document.constructor.prototype.createElement.call(document, 'script'); script.src = evt.data.scriptName; script.charset = "utf-8"; From d51f062d039b9c3d37ff936574096e456e1cc7ef Mon Sep 17 00:00:00 2001 From: Stephanie Ding Date: Tue, 10 Sep 2019 17:46:29 -0700 Subject: [PATCH 4/8] Formatting changes --- .../src/injectGlobalHook.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/react-devtools-extensions/src/injectGlobalHook.js b/packages/react-devtools-extensions/src/injectGlobalHook.js index dd8bf9def7b4..a087c50dc53f 100644 --- a/packages/react-devtools-extensions/src/injectGlobalHook.js +++ b/packages/react-devtools-extensions/src/injectGlobalHook.js @@ -1,15 +1,9 @@ /* 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'; +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) { const script = document.createElement('script'); From 8a6cd3cd12932e5c6dfea9529c8cbafb9be46445 Mon Sep 17 00:00:00 2001 From: Stephanie Ding Date: Tue, 10 Sep 2019 18:06:23 -0700 Subject: [PATCH 5/8] remove ability to inject arbitrary scripts --- packages/react-devtools-extensions/src/inject.js | 9 +++++++-- .../src/injectGlobalHook.js | 4 ++-- packages/react-devtools-extensions/src/main.js | 13 ++++++++++++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/packages/react-devtools-extensions/src/inject.js b/packages/react-devtools-extensions/src/inject.js index b2f4a03e1295..938e2cf7eb22 100644 --- a/packages/react-devtools-extensions/src/inject.js +++ b/packages/react-devtools-extensions/src/inject.js @@ -2,8 +2,13 @@ export default function inject(scriptName: string, done: ?Function) { const source = ` + // the prototype stuff is in case document.createElement has been modified (function () { - window.postMessage({ source: 'react-devtools-inject-script', scriptName: "${scriptName}" }, "*"); + var script = document.constructor.prototype.createElement.call(document, 'script'); + script.src = "${scriptName}"; + script.charset = "utf-8"; + document.documentElement.appendChild(script); + script.parentNode.removeChild(script); })() `; @@ -16,4 +21,4 @@ export default function inject(scriptName: string, done: ?Function) { done(); } }); -} \ 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 a087c50dc53f..550e2f38512a 100644 --- a/packages/react-devtools-extensions/src/injectGlobalHook.js +++ b/packages/react-devtools-extensions/src/injectGlobalHook.js @@ -31,10 +31,10 @@ window.addEventListener('message', function(evt) { reactBuildType: evt.data.reactBuildType, }; chrome.runtime.sendMessage(lastDetectionResult); - } else if (evt.data.source === 'react-devtools-inject-script' && evt.data.scriptName) { + } else if (evt.data.source === 'react-devtools-inject-backend') { //Inject the specified script var script = document.constructor.prototype.createElement.call(document, 'script'); - script.src = evt.data.scriptName; + 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..d5ff4a40c268 100644 --- a/packages/react-devtools-extensions/src/main.js +++ b/packages/react-devtools-extensions/src/main.js @@ -135,7 +135,18 @@ 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, error) { + if (error) { + console.log(error); + } + + if (typeof done === 'function') { + done(); + } + } + ); const viewElementSourceFunction = createViewElementSource( bridge, From 2e75000f404ec7810fac03420b2e2cf215baa69d Mon Sep 17 00:00:00 2001 From: Stephanie Ding Date: Wed, 11 Sep 2019 08:05:27 -0700 Subject: [PATCH 6/8] Removed done(), added some comments explaining the change --- .../react-devtools-extensions/src/injectGlobalHook.js | 6 +++++- packages/react-devtools-extensions/src/main.js | 10 +++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/react-devtools-extensions/src/injectGlobalHook.js b/packages/react-devtools-extensions/src/injectGlobalHook.js index 550e2f38512a..390084ead20d 100644 --- a/packages/react-devtools-extensions/src/injectGlobalHook.js +++ b/packages/react-devtools-extensions/src/injectGlobalHook.js @@ -31,8 +31,12 @@ window.addEventListener('message', function(evt) { reactBuildType: evt.data.reactBuildType, }; chrome.runtime.sendMessage(lastDetectionResult); + + // Inject the backend. This is done in 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 } else if (evt.data.source === 'react-devtools-inject-backend') { - //Inject the specified script + // the prototype stuff is in case document.createElement has been modified var script = document.constructor.prototype.createElement.call(document, 'script'); script.src = chrome.runtime.getURL('build/backend.js'); script.charset = "utf-8"; diff --git a/packages/react-devtools-extensions/src/main.js b/packages/react-devtools-extensions/src/main.js index d5ff4a40c268..7649d099f73c 100644 --- a/packages/react-devtools-extensions/src/main.js +++ b/packages/react-devtools-extensions/src/main.js @@ -138,14 +138,10 @@ function createPanelIfReactLoaded() { chrome.devtools.inspectedWindow.eval( `window.postMessage({ source: 'react-devtools-inject-backend' });`, function(response, error) { - if (error) { - console.log(error); - } - - if (typeof done === 'function') { - done(); + if (error) { + console.log(error); + } } - } ); const viewElementSourceFunction = createViewElementSource( From 776d1c69b91aafe59027fbf51b8a07d8c7596e78 Mon Sep 17 00:00:00 2001 From: Stephanie Ding Date: Wed, 11 Sep 2019 08:11:14 -0700 Subject: [PATCH 7/8] Lint fixes --- .../src/injectGlobalHook.js | 13 ++++++++----- packages/react-devtools-extensions/src/main.js | 11 +++++------ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/react-devtools-extensions/src/injectGlobalHook.js b/packages/react-devtools-extensions/src/injectGlobalHook.js index 390084ead20d..ca4acfcbd50c 100644 --- a/packages/react-devtools-extensions/src/injectGlobalHook.js +++ b/packages/react-devtools-extensions/src/injectGlobalHook.js @@ -32,14 +32,17 @@ window.addEventListener('message', function(evt) { }; chrome.runtime.sendMessage(lastDetectionResult); - // Inject the backend. This is done in 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 + // Inject the backend. This is done in 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. } else if (evt.data.source === 'react-devtools-inject-backend') { // the prototype stuff is in case document.createElement has been modified - var script = document.constructor.prototype.createElement.call(document, 'script'); + const script = document.constructor.prototype.createElement.call( + document, + 'script', + ); script.src = chrome.runtime.getURL('build/backend.js'); - script.charset = "utf-8"; + 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 7649d099f73c..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, @@ -136,12 +135,12 @@ function createPanelIfReactLoaded() { // Initialize the backend only once the Store has been initialized. // Otherwise the Store may miss important initial tree op codes. chrome.devtools.inspectedWindow.eval( - `window.postMessage({ source: 'react-devtools-inject-backend' });`, - function(response, error) { - if (error) { - console.log(error); + `window.postMessage({ source: 'react-devtools-inject-backend' });`, + function(response, evalError) { + if (evalError) { + console.log(evalError); } - } + }, ); const viewElementSourceFunction = createViewElementSource( From f09854a9e85cb612f26172f6ea1dea0b3de45aec Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Wed, 11 Sep 2019 09:30:57 -0700 Subject: [PATCH 8/8] Moved inline comment. --- .../react-devtools-extensions/src/injectGlobalHook.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/react-devtools-extensions/src/injectGlobalHook.js b/packages/react-devtools-extensions/src/injectGlobalHook.js index ca4acfcbd50c..2347671d24a5 100644 --- a/packages/react-devtools-extensions/src/injectGlobalHook.js +++ b/packages/react-devtools-extensions/src/injectGlobalHook.js @@ -31,12 +31,10 @@ window.addEventListener('message', function(evt) { reactBuildType: evt.data.reactBuildType, }; chrome.runtime.sendMessage(lastDetectionResult); - - // Inject the backend. This is done in 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. } else if (evt.data.source === 'react-devtools-inject-backend') { - // the prototype stuff is in case document.createElement has been modified + // 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',