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