1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | 'use strict';
|
11 |
|
12 | var ReactInvalidSetStateWarningHook = require('./ReactInvalidSetStateWarningHook');
|
13 | var ReactHostOperationHistoryHook = require('./ReactHostOperationHistoryHook');
|
14 | var ReactComponentTreeHook = require('react/lib/ReactComponentTreeHook');
|
15 | var ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
|
16 |
|
17 | var performanceNow = require('fbjs/lib/performanceNow');
|
18 | var warning = require('fbjs/lib/warning');
|
19 |
|
20 | var hooks = [];
|
21 | var didHookThrowForEvent = {};
|
22 |
|
23 | function callHook(event, fn, context, arg1, arg2, arg3, arg4, arg5) {
|
24 | try {
|
25 | fn.call(context, arg1, arg2, arg3, arg4, arg5);
|
26 | } catch (e) {
|
27 | process.env.NODE_ENV !== 'production' ? warning(didHookThrowForEvent[event], 'Exception thrown by hook while handling %s: %s', event, e + '\n' + e.stack) : void 0;
|
28 | didHookThrowForEvent[event] = true;
|
29 | }
|
30 | }
|
31 |
|
32 | function emitEvent(event, arg1, arg2, arg3, arg4, arg5) {
|
33 | for (var i = 0; i < hooks.length; i++) {
|
34 | var hook = hooks[i];
|
35 | var fn = hook[event];
|
36 | if (fn) {
|
37 | callHook(event, fn, hook, arg1, arg2, arg3, arg4, arg5);
|
38 | }
|
39 | }
|
40 | }
|
41 |
|
42 | var isProfiling = false;
|
43 | var flushHistory = [];
|
44 | var lifeCycleTimerStack = [];
|
45 | var currentFlushNesting = 0;
|
46 | var currentFlushMeasurements = [];
|
47 | var currentFlushStartTime = 0;
|
48 | var currentTimerDebugID = null;
|
49 | var currentTimerStartTime = 0;
|
50 | var currentTimerNestedFlushDuration = 0;
|
51 | var currentTimerType = null;
|
52 |
|
53 | var lifeCycleTimerHasWarned = false;
|
54 |
|
55 | function clearHistory() {
|
56 | ReactComponentTreeHook.purgeUnmountedComponents();
|
57 | ReactHostOperationHistoryHook.clearHistory();
|
58 | }
|
59 |
|
60 | function getTreeSnapshot(registeredIDs) {
|
61 | return registeredIDs.reduce(function (tree, id) {
|
62 | var ownerID = ReactComponentTreeHook.getOwnerID(id);
|
63 | var parentID = ReactComponentTreeHook.getParentID(id);
|
64 | tree[id] = {
|
65 | displayName: ReactComponentTreeHook.getDisplayName(id),
|
66 | text: ReactComponentTreeHook.getText(id),
|
67 | updateCount: ReactComponentTreeHook.getUpdateCount(id),
|
68 | childIDs: ReactComponentTreeHook.getChildIDs(id),
|
69 |
|
70 | ownerID: ownerID || parentID && ReactComponentTreeHook.getOwnerID(parentID) || 0,
|
71 | parentID: parentID
|
72 | };
|
73 | return tree;
|
74 | }, {});
|
75 | }
|
76 |
|
77 | function resetMeasurements() {
|
78 | var previousStartTime = currentFlushStartTime;
|
79 | var previousMeasurements = currentFlushMeasurements;
|
80 | var previousOperations = ReactHostOperationHistoryHook.getHistory();
|
81 |
|
82 | if (currentFlushNesting === 0) {
|
83 | currentFlushStartTime = 0;
|
84 | currentFlushMeasurements = [];
|
85 | clearHistory();
|
86 | return;
|
87 | }
|
88 |
|
89 | if (previousMeasurements.length || previousOperations.length) {
|
90 | var registeredIDs = ReactComponentTreeHook.getRegisteredIDs();
|
91 | flushHistory.push({
|
92 | duration: performanceNow() - previousStartTime,
|
93 | measurements: previousMeasurements || [],
|
94 | operations: previousOperations || [],
|
95 | treeSnapshot: getTreeSnapshot(registeredIDs)
|
96 | });
|
97 | }
|
98 |
|
99 | clearHistory();
|
100 | currentFlushStartTime = performanceNow();
|
101 | currentFlushMeasurements = [];
|
102 | }
|
103 |
|
104 | function checkDebugID(debugID) {
|
105 | var allowRoot = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
106 |
|
107 | if (allowRoot && debugID === 0) {
|
108 | return;
|
109 | }
|
110 | if (!debugID) {
|
111 | process.env.NODE_ENV !== 'production' ? warning(false, 'ReactDebugTool: debugID may not be empty.') : void 0;
|
112 | }
|
113 | }
|
114 |
|
115 | function beginLifeCycleTimer(debugID, timerType) {
|
116 | if (currentFlushNesting === 0) {
|
117 | return;
|
118 | }
|
119 | if (currentTimerType && !lifeCycleTimerHasWarned) {
|
120 | process.env.NODE_ENV !== 'production' ? warning(false, 'There is an internal error in the React performance measurement code. ' + 'Did not expect %s timer to start while %s timer is still in ' + 'progress for %s instance.', timerType, currentTimerType || 'no', debugID === currentTimerDebugID ? 'the same' : 'another') : void 0;
|
121 | lifeCycleTimerHasWarned = true;
|
122 | }
|
123 | currentTimerStartTime = performanceNow();
|
124 | currentTimerNestedFlushDuration = 0;
|
125 | currentTimerDebugID = debugID;
|
126 | currentTimerType = timerType;
|
127 | }
|
128 |
|
129 | function endLifeCycleTimer(debugID, timerType) {
|
130 | if (currentFlushNesting === 0) {
|
131 | return;
|
132 | }
|
133 | if (currentTimerType !== timerType && !lifeCycleTimerHasWarned) {
|
134 | process.env.NODE_ENV !== 'production' ? warning(false, 'There is an internal error in the React performance measurement code. ' + 'We did not expect %s timer to stop while %s timer is still in ' + 'progress for %s instance. Please report this as a bug in React.', timerType, currentTimerType || 'no', debugID === currentTimerDebugID ? 'the same' : 'another') : void 0;
|
135 | lifeCycleTimerHasWarned = true;
|
136 | }
|
137 | if (isProfiling) {
|
138 | currentFlushMeasurements.push({
|
139 | timerType: timerType,
|
140 | instanceID: debugID,
|
141 | duration: performanceNow() - currentTimerStartTime - currentTimerNestedFlushDuration
|
142 | });
|
143 | }
|
144 | currentTimerStartTime = 0;
|
145 | currentTimerNestedFlushDuration = 0;
|
146 | currentTimerDebugID = null;
|
147 | currentTimerType = null;
|
148 | }
|
149 |
|
150 | function pauseCurrentLifeCycleTimer() {
|
151 | var currentTimer = {
|
152 | startTime: currentTimerStartTime,
|
153 | nestedFlushStartTime: performanceNow(),
|
154 | debugID: currentTimerDebugID,
|
155 | timerType: currentTimerType
|
156 | };
|
157 | lifeCycleTimerStack.push(currentTimer);
|
158 | currentTimerStartTime = 0;
|
159 | currentTimerNestedFlushDuration = 0;
|
160 | currentTimerDebugID = null;
|
161 | currentTimerType = null;
|
162 | }
|
163 |
|
164 | function resumeCurrentLifeCycleTimer() {
|
165 | var _lifeCycleTimerStack$ = lifeCycleTimerStack.pop(),
|
166 | startTime = _lifeCycleTimerStack$.startTime,
|
167 | nestedFlushStartTime = _lifeCycleTimerStack$.nestedFlushStartTime,
|
168 | debugID = _lifeCycleTimerStack$.debugID,
|
169 | timerType = _lifeCycleTimerStack$.timerType;
|
170 |
|
171 | var nestedFlushDuration = performanceNow() - nestedFlushStartTime;
|
172 | currentTimerStartTime = startTime;
|
173 | currentTimerNestedFlushDuration += nestedFlushDuration;
|
174 | currentTimerDebugID = debugID;
|
175 | currentTimerType = timerType;
|
176 | }
|
177 |
|
178 | var lastMarkTimeStamp = 0;
|
179 | var canUsePerformanceMeasure = typeof performance !== 'undefined' && typeof performance.mark === 'function' && typeof performance.clearMarks === 'function' && typeof performance.measure === 'function' && typeof performance.clearMeasures === 'function';
|
180 |
|
181 | function shouldMark(debugID) {
|
182 | if (!isProfiling || !canUsePerformanceMeasure) {
|
183 | return false;
|
184 | }
|
185 | var element = ReactComponentTreeHook.getElement(debugID);
|
186 | if (element == null || typeof element !== 'object') {
|
187 | return false;
|
188 | }
|
189 | var isHostElement = typeof element.type === 'string';
|
190 | if (isHostElement) {
|
191 | return false;
|
192 | }
|
193 | return true;
|
194 | }
|
195 |
|
196 | function markBegin(debugID, markType) {
|
197 | if (!shouldMark(debugID)) {
|
198 | return;
|
199 | }
|
200 |
|
201 | var markName = debugID + '::' + markType;
|
202 | lastMarkTimeStamp = performanceNow();
|
203 | performance.mark(markName);
|
204 | }
|
205 |
|
206 | function markEnd(debugID, markType) {
|
207 | if (!shouldMark(debugID)) {
|
208 | return;
|
209 | }
|
210 |
|
211 | var markName = debugID + '::' + markType;
|
212 | var displayName = ReactComponentTreeHook.getDisplayName(debugID) || 'Unknown';
|
213 |
|
214 |
|
215 |
|
216 |
|
217 |
|
218 |
|
219 |
|
220 | var timeStamp = performanceNow();
|
221 | if (timeStamp - lastMarkTimeStamp > 0.1) {
|
222 | var measurementName = displayName + ' [' + markType + ']';
|
223 | performance.measure(measurementName, markName);
|
224 | }
|
225 |
|
226 | performance.clearMarks(markName);
|
227 | if (measurementName) {
|
228 | performance.clearMeasures(measurementName);
|
229 | }
|
230 | }
|
231 |
|
232 | var ReactDebugTool = {
|
233 | addHook: function (hook) {
|
234 | hooks.push(hook);
|
235 | },
|
236 | removeHook: function (hook) {
|
237 | for (var i = 0; i < hooks.length; i++) {
|
238 | if (hooks[i] === hook) {
|
239 | hooks.splice(i, 1);
|
240 | i--;
|
241 | }
|
242 | }
|
243 | },
|
244 | isProfiling: function () {
|
245 | return isProfiling;
|
246 | },
|
247 | beginProfiling: function () {
|
248 | if (isProfiling) {
|
249 | return;
|
250 | }
|
251 |
|
252 | isProfiling = true;
|
253 | flushHistory.length = 0;
|
254 | resetMeasurements();
|
255 | ReactDebugTool.addHook(ReactHostOperationHistoryHook);
|
256 | },
|
257 | endProfiling: function () {
|
258 | if (!isProfiling) {
|
259 | return;
|
260 | }
|
261 |
|
262 | isProfiling = false;
|
263 | resetMeasurements();
|
264 | ReactDebugTool.removeHook(ReactHostOperationHistoryHook);
|
265 | },
|
266 | getFlushHistory: function () {
|
267 | return flushHistory;
|
268 | },
|
269 | onBeginFlush: function () {
|
270 | currentFlushNesting++;
|
271 | resetMeasurements();
|
272 | pauseCurrentLifeCycleTimer();
|
273 | emitEvent('onBeginFlush');
|
274 | },
|
275 | onEndFlush: function () {
|
276 | resetMeasurements();
|
277 | currentFlushNesting--;
|
278 | resumeCurrentLifeCycleTimer();
|
279 | emitEvent('onEndFlush');
|
280 | },
|
281 | onBeginLifeCycleTimer: function (debugID, timerType) {
|
282 | checkDebugID(debugID);
|
283 | emitEvent('onBeginLifeCycleTimer', debugID, timerType);
|
284 | markBegin(debugID, timerType);
|
285 | beginLifeCycleTimer(debugID, timerType);
|
286 | },
|
287 | onEndLifeCycleTimer: function (debugID, timerType) {
|
288 | checkDebugID(debugID);
|
289 | endLifeCycleTimer(debugID, timerType);
|
290 | markEnd(debugID, timerType);
|
291 | emitEvent('onEndLifeCycleTimer', debugID, timerType);
|
292 | },
|
293 | onBeginProcessingChildContext: function () {
|
294 | emitEvent('onBeginProcessingChildContext');
|
295 | },
|
296 | onEndProcessingChildContext: function () {
|
297 | emitEvent('onEndProcessingChildContext');
|
298 | },
|
299 | onHostOperation: function (operation) {
|
300 | checkDebugID(operation.instanceID);
|
301 | emitEvent('onHostOperation', operation);
|
302 | },
|
303 | onSetState: function () {
|
304 | emitEvent('onSetState');
|
305 | },
|
306 | onSetChildren: function (debugID, childDebugIDs) {
|
307 | checkDebugID(debugID);
|
308 | childDebugIDs.forEach(checkDebugID);
|
309 | emitEvent('onSetChildren', debugID, childDebugIDs);
|
310 | },
|
311 | onBeforeMountComponent: function (debugID, element, parentDebugID) {
|
312 | checkDebugID(debugID);
|
313 | checkDebugID(parentDebugID, true);
|
314 | emitEvent('onBeforeMountComponent', debugID, element, parentDebugID);
|
315 | markBegin(debugID, 'mount');
|
316 | },
|
317 | onMountComponent: function (debugID) {
|
318 | checkDebugID(debugID);
|
319 | markEnd(debugID, 'mount');
|
320 | emitEvent('onMountComponent', debugID);
|
321 | },
|
322 | onBeforeUpdateComponent: function (debugID, element) {
|
323 | checkDebugID(debugID);
|
324 | emitEvent('onBeforeUpdateComponent', debugID, element);
|
325 | markBegin(debugID, 'update');
|
326 | },
|
327 | onUpdateComponent: function (debugID) {
|
328 | checkDebugID(debugID);
|
329 | markEnd(debugID, 'update');
|
330 | emitEvent('onUpdateComponent', debugID);
|
331 | },
|
332 | onBeforeUnmountComponent: function (debugID) {
|
333 | checkDebugID(debugID);
|
334 | emitEvent('onBeforeUnmountComponent', debugID);
|
335 | markBegin(debugID, 'unmount');
|
336 | },
|
337 | onUnmountComponent: function (debugID) {
|
338 | checkDebugID(debugID);
|
339 | markEnd(debugID, 'unmount');
|
340 | emitEvent('onUnmountComponent', debugID);
|
341 | },
|
342 | onTestEvent: function () {
|
343 | emitEvent('onTestEvent');
|
344 | }
|
345 | };
|
346 |
|
347 |
|
348 | ReactDebugTool.addDevtool = ReactDebugTool.addHook;
|
349 | ReactDebugTool.removeDevtool = ReactDebugTool.removeHook;
|
350 |
|
351 | ReactDebugTool.addHook(ReactInvalidSetStateWarningHook);
|
352 | ReactDebugTool.addHook(ReactComponentTreeHook);
|
353 | var url = ExecutionEnvironment.canUseDOM && window.location.href || '';
|
354 | if (/[?&]react_perf\b/.test(url)) {
|
355 | ReactDebugTool.beginProfiling();
|
356 | }
|
357 |
|
358 | module.exports = ReactDebugTool; |
\ | No newline at end of file |