mirror of
https://github.com/facebook/react.git
synced 2026-02-25 13:13:03 +00:00
Inject early on when reloading-and-profiling
This commit is contained in:
@@ -27,7 +27,12 @@
|
||||
"devtools_page": "main.html",
|
||||
|
||||
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
|
||||
"web_accessible_resources": ["main.html", "panel.html", "build/backend.js"],
|
||||
"web_accessible_resources": [
|
||||
"main.html",
|
||||
"panel.html",
|
||||
"build/backend.js",
|
||||
"build/renderer.js"
|
||||
],
|
||||
|
||||
"background": {
|
||||
"scripts": ["build/background.js"],
|
||||
|
||||
@@ -33,7 +33,12 @@
|
||||
"devtools_page": "main.html",
|
||||
|
||||
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
|
||||
"web_accessible_resources": ["main.html", "panel.html", "build/backend.js"],
|
||||
"web_accessible_resources": [
|
||||
"main.html",
|
||||
"panel.html",
|
||||
"build/backend.js",
|
||||
"build/renderer.js"
|
||||
],
|
||||
|
||||
"background": {
|
||||
"scripts": ["build/background.js"],
|
||||
|
||||
@@ -2,13 +2,24 @@
|
||||
|
||||
import nullthrows from 'nullthrows';
|
||||
import { installHook } from 'src/hook';
|
||||
import { RELOAD_AND_PROFILE_KEY } from 'src/constants';
|
||||
|
||||
function injectCode(code) {
|
||||
const script = document.createElement('script');
|
||||
script.textContent = code;
|
||||
|
||||
// This script runs before the <head> element is created,
|
||||
// so we add the script to <html> instead.
|
||||
nullthrows(document.documentElement).appendChild(script);
|
||||
nullthrows(script.parentNode).removeChild(script);
|
||||
}
|
||||
|
||||
let lastDetectionResult;
|
||||
|
||||
// We want to detect when a renderer attaches, and notify the "background
|
||||
// page" (which is shared between tabs and can highlight the React icon).
|
||||
// Currently we are in "content script" context, so we can't listen
|
||||
// to the hook directly (it will be injected directly into the page).
|
||||
// We want to detect when a renderer attaches, and notify the "background page"
|
||||
// (which is shared between tabs and can highlight the React icon).
|
||||
// Currently we are in "content script" context, so we can't listen to the hook directly
|
||||
// (it will be injected directly into the page).
|
||||
// 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) {
|
||||
@@ -51,14 +62,27 @@ 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 (localStorage.getItem(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);
|
||||
}
|
||||
|
||||
// Inject a `__REACT_DEVTOOLS_GLOBAL_HOOK__` global so that React can detect that the
|
||||
// devtools are installed (and skip its suggestion to install the devtools).
|
||||
const js =
|
||||
';(' + installHook.toString() + '(window))' + saveNativeValues + detectReact;
|
||||
|
||||
// This script runs before the <head> element is created, so we add the script
|
||||
// to <html> instead.
|
||||
const script = document.createElement('script');
|
||||
script.textContent = js;
|
||||
nullthrows(document.documentElement).appendChild(script);
|
||||
nullthrows(script.parentNode).removeChild(script);
|
||||
injectCode(
|
||||
';(' + installHook.toString() + '(window))' + saveNativeValues + detectReact
|
||||
);
|
||||
|
||||
21
shells/browser/shared/src/renderer.js
Normal file
21
shells/browser/shared/src/renderer.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Install the hook on window, which is an event emitter.
|
||||
* Note because Chrome content scripts cannot directly modify the window object,
|
||||
* we are evaling this function by inserting a script tag.
|
||||
* That's why we have to inline the whole event emitter implementation here.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import { attach } from 'src/backend/renderer';
|
||||
|
||||
Object.defineProperty(
|
||||
window,
|
||||
'__REACT_DEVTOOLS_ATTACH__',
|
||||
({
|
||||
enumerable: false,
|
||||
get() {
|
||||
return attach;
|
||||
},
|
||||
}: Object)
|
||||
);
|
||||
@@ -18,6 +18,7 @@ module.exports = {
|
||||
inject: './src/GlobalHook.js',
|
||||
main: './src/main.js',
|
||||
panel: './src/panel.js',
|
||||
renderer: './src/renderer.js',
|
||||
},
|
||||
output: {
|
||||
path: __dirname + '/build',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
|
||||
import EventEmitter from 'events';
|
||||
import { __DEBUG__ } from '../constants';
|
||||
import { RELOAD_AND_PROFILE_KEY, __DEBUG__ } from '../constants';
|
||||
import { hideOverlay, showOverlay } from './views/Highlighter';
|
||||
|
||||
import type { RendererID, RendererInterface } from './types';
|
||||
@@ -38,8 +38,6 @@ type SetInParams = {|
|
||||
value: any,
|
||||
|};
|
||||
|
||||
const RELOAD_AND_PROFILE_KEY = 'React::DevTools::reloadAndProfile';
|
||||
|
||||
export default class Agent extends EventEmitter {
|
||||
_bridge: Bridge = ((null: any): Bridge);
|
||||
_isProfiling: boolean = false;
|
||||
|
||||
@@ -33,8 +33,16 @@ export function initBackend(
|
||||
];
|
||||
|
||||
const attachRenderer = (id: number, renderer: ReactRenderer) => {
|
||||
const rendererInterface = attach(hook, id, renderer, global);
|
||||
hook.rendererInterfaces.set(id, rendererInterface);
|
||||
let rendererInterface = hook.rendererInterfaces.get(id);
|
||||
|
||||
// Inject any not-yet-injected renderers (if we didn't reload-and-profile)
|
||||
if (!rendererInterface) {
|
||||
rendererInterface = attach(hook, id, renderer, global);
|
||||
|
||||
hook.rendererInterfaces.set(id, rendererInterface);
|
||||
}
|
||||
|
||||
// Notify the DevTools frontend about any renderers that were attached early.
|
||||
hook.emit('renderer-attached', {
|
||||
id,
|
||||
renderer,
|
||||
|
||||
@@ -1595,6 +1595,10 @@ export function attach(
|
||||
}
|
||||
|
||||
function startProfiling() {
|
||||
if (isProfiling) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Capture initial values as of the time profiling starts.
|
||||
// It's important we snapshot both the durations and the id-to-root map,
|
||||
// since either of these may change during the profiling session
|
||||
@@ -1611,6 +1615,11 @@ export function attach(
|
||||
isProfiling = false;
|
||||
}
|
||||
|
||||
// Automatically start profiling so that we don't miss timing info from initial "mount".
|
||||
if (localStorage.getItem('React::DevTools::reloadAndProfile') === 'true') {
|
||||
startProfiling();
|
||||
}
|
||||
|
||||
return {
|
||||
cleanup,
|
||||
getCommitDetails,
|
||||
|
||||
@@ -5,4 +5,6 @@ export const TREE_OPERATION_REMOVE = 2;
|
||||
export const TREE_OPERATION_RESET_CHILDREN = 3;
|
||||
export const TREE_OPERATION_UPDATE_TREE_BASE_DURATION = 4;
|
||||
|
||||
export const RELOAD_AND_PROFILE_KEY = 'React::DevTools::reloadAndProfile';
|
||||
|
||||
export const __DEBUG__ = false;
|
||||
|
||||
14
src/hook.js
14
src/hook.js
@@ -79,6 +79,20 @@ export function installHook(target: any): DevToolsHook | null {
|
||||
|
||||
hook.emit('renderer', { id, renderer, reactBuildType });
|
||||
|
||||
// If we have just reloaded to profile, we need to inject the renderer interface before the app loads.
|
||||
// Otherwise the renderer won't yet exist and we can skip this step.
|
||||
const attach = target.__REACT_DEVTOOLS_ATTACH__;
|
||||
if (typeof attach === 'function') {
|
||||
const rendererInterface = attach(hook, id, renderer, target);
|
||||
hook.rendererInterfaces.set(id, rendererInterface);
|
||||
|
||||
/*hook.emit('renderer-attached', {
|
||||
id,
|
||||
renderer,
|
||||
rendererInterface,
|
||||
});*/
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user