1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. import type {Lane, Lanes} from './ReactFiberLane';
    
  11. import type {Fiber, FiberRoot} from './ReactInternalTypes';
    
  12. import type {ReactNodeList, Wakeable} from 'shared/ReactTypes';
    
  13. import type {EventPriority} from './ReactEventPriorities';
    
  14. // import type {DevToolsProfilingHooks} from 'react-devtools-shared/src/backend/types';
    
  15. // TODO: This import doesn't work because the DevTools depend on the DOM version of React
    
  16. // and to properly type check against DOM React we can't also type check again non-DOM
    
  17. // React which this hook might be in.
    
  18. type DevToolsProfilingHooks = any;
    
  19. 
    
  20. import {getLabelForLane, TotalLanes} from 'react-reconciler/src/ReactFiberLane';
    
  21. import {DidCapture} from './ReactFiberFlags';
    
  22. import {
    
  23.   consoleManagedByDevToolsDuringStrictMode,
    
  24.   enableProfilerTimer,
    
  25.   enableSchedulingProfiler,
    
  26. } from 'shared/ReactFeatureFlags';
    
  27. import {
    
  28.   DiscreteEventPriority,
    
  29.   ContinuousEventPriority,
    
  30.   DefaultEventPriority,
    
  31.   IdleEventPriority,
    
  32. } from './ReactEventPriorities';
    
  33. import {
    
  34.   ImmediatePriority as ImmediateSchedulerPriority,
    
  35.   UserBlockingPriority as UserBlockingSchedulerPriority,
    
  36.   NormalPriority as NormalSchedulerPriority,
    
  37.   IdlePriority as IdleSchedulerPriority,
    
  38.   log,
    
  39.   unstable_setDisableYieldValue,
    
  40. } from './Scheduler';
    
  41. import {setSuppressWarning} from 'shared/consoleWithStackDev';
    
  42. import {disableLogs, reenableLogs} from 'shared/ConsolePatchingDev';
    
  43. 
    
  44. declare var __REACT_DEVTOOLS_GLOBAL_HOOK__: Object | void;
    
  45. 
    
  46. let rendererID = null;
    
  47. let injectedHook = null;
    
  48. let injectedProfilingHooks: DevToolsProfilingHooks | null = null;
    
  49. let hasLoggedError = false;
    
  50. 
    
  51. export const isDevToolsPresent =
    
  52.   typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined';
    
  53. 
    
  54. export function injectInternals(internals: Object): boolean {
    
  55.   if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') {
    
  56.     // No DevTools
    
  57.     return false;
    
  58.   }
    
  59.   const hook = __REACT_DEVTOOLS_GLOBAL_HOOK__;
    
  60.   if (hook.isDisabled) {
    
  61.     // This isn't a real property on the hook, but it can be set to opt out
    
  62.     // of DevTools integration and associated warnings and logs.
    
  63.     // https://github.com/facebook/react/issues/3877
    
  64.     return true;
    
  65.   }
    
  66.   if (!hook.supportsFiber) {
    
  67.     if (__DEV__) {
    
  68.       console.error(
    
  69.         'The installed version of React DevTools is too old and will not work ' +
    
  70.           'with the current version of React. Please update React DevTools. ' +
    
  71.           'https://reactjs.org/link/react-devtools',
    
  72.       );
    
  73.     }
    
  74.     // DevTools exists, even though it doesn't support Fiber.
    
  75.     return true;
    
  76.   }
    
  77.   try {
    
  78.     if (enableSchedulingProfiler) {
    
  79.       // Conditionally inject these hooks only if Timeline profiler is supported by this build.
    
  80.       // This gives DevTools a way to feature detect that isn't tied to version number
    
  81.       // (since profiling and timeline are controlled by different feature flags).
    
  82.       internals = {
    
  83.         ...internals,
    
  84.         getLaneLabelMap,
    
  85.         injectProfilingHooks,
    
  86.       };
    
  87.     }
    
  88. 
    
  89.     rendererID = hook.inject(internals);
    
  90. 
    
  91.     // We have successfully injected, so now it is safe to set up hooks.
    
  92.     injectedHook = hook;
    
  93.   } catch (err) {
    
  94.     // Catch all errors because it is unsafe to throw during initialization.
    
  95.     if (__DEV__) {
    
  96.       console.error('React instrumentation encountered an error: %s.', err);
    
  97.     }
    
  98.   }
    
  99.   if (hook.checkDCE) {
    
  100.     // This is the real DevTools.
    
  101.     return true;
    
  102.   } else {
    
  103.     // This is likely a hook installed by Fast Refresh runtime.
    
  104.     return false;
    
  105.   }
    
  106. }
    
  107. 
    
  108. export function onScheduleRoot(root: FiberRoot, children: ReactNodeList) {
    
  109.   if (__DEV__) {
    
  110.     if (
    
  111.       injectedHook &&
    
  112.       typeof injectedHook.onScheduleFiberRoot === 'function'
    
  113.     ) {
    
  114.       try {
    
  115.         injectedHook.onScheduleFiberRoot(rendererID, root, children);
    
  116.       } catch (err) {
    
  117.         if (__DEV__ && !hasLoggedError) {
    
  118.           hasLoggedError = true;
    
  119.           console.error('React instrumentation encountered an error: %s', err);
    
  120.         }
    
  121.       }
    
  122.     }
    
  123.   }
    
  124. }
    
  125. 
    
  126. export function onCommitRoot(root: FiberRoot, eventPriority: EventPriority) {
    
  127.   if (injectedHook && typeof injectedHook.onCommitFiberRoot === 'function') {
    
  128.     try {
    
  129.       const didError = (root.current.flags & DidCapture) === DidCapture;
    
  130.       if (enableProfilerTimer) {
    
  131.         let schedulerPriority;
    
  132.         switch (eventPriority) {
    
  133.           case DiscreteEventPriority:
    
  134.             schedulerPriority = ImmediateSchedulerPriority;
    
  135.             break;
    
  136.           case ContinuousEventPriority:
    
  137.             schedulerPriority = UserBlockingSchedulerPriority;
    
  138.             break;
    
  139.           case DefaultEventPriority:
    
  140.             schedulerPriority = NormalSchedulerPriority;
    
  141.             break;
    
  142.           case IdleEventPriority:
    
  143.             schedulerPriority = IdleSchedulerPriority;
    
  144.             break;
    
  145.           default:
    
  146.             schedulerPriority = NormalSchedulerPriority;
    
  147.             break;
    
  148.         }
    
  149.         injectedHook.onCommitFiberRoot(
    
  150.           rendererID,
    
  151.           root,
    
  152.           schedulerPriority,
    
  153.           didError,
    
  154.         );
    
  155.       } else {
    
  156.         injectedHook.onCommitFiberRoot(rendererID, root, undefined, didError);
    
  157.       }
    
  158.     } catch (err) {
    
  159.       if (__DEV__) {
    
  160.         if (!hasLoggedError) {
    
  161.           hasLoggedError = true;
    
  162.           console.error('React instrumentation encountered an error: %s', err);
    
  163.         }
    
  164.       }
    
  165.     }
    
  166.   }
    
  167. }
    
  168. 
    
  169. export function onPostCommitRoot(root: FiberRoot) {
    
  170.   if (
    
  171.     injectedHook &&
    
  172.     typeof injectedHook.onPostCommitFiberRoot === 'function'
    
  173.   ) {
    
  174.     try {
    
  175.       injectedHook.onPostCommitFiberRoot(rendererID, root);
    
  176.     } catch (err) {
    
  177.       if (__DEV__) {
    
  178.         if (!hasLoggedError) {
    
  179.           hasLoggedError = true;
    
  180.           console.error('React instrumentation encountered an error: %s', err);
    
  181.         }
    
  182.       }
    
  183.     }
    
  184.   }
    
  185. }
    
  186. 
    
  187. export function onCommitUnmount(fiber: Fiber) {
    
  188.   if (injectedHook && typeof injectedHook.onCommitFiberUnmount === 'function') {
    
  189.     try {
    
  190.       injectedHook.onCommitFiberUnmount(rendererID, fiber);
    
  191.     } catch (err) {
    
  192.       if (__DEV__) {
    
  193.         if (!hasLoggedError) {
    
  194.           hasLoggedError = true;
    
  195.           console.error('React instrumentation encountered an error: %s', err);
    
  196.         }
    
  197.       }
    
  198.     }
    
  199.   }
    
  200. }
    
  201. 
    
  202. export function setIsStrictModeForDevtools(newIsStrictMode: boolean) {
    
  203.   if (consoleManagedByDevToolsDuringStrictMode) {
    
  204.     if (typeof log === 'function') {
    
  205.       // We're in a test because Scheduler.log only exists
    
  206.       // in SchedulerMock. To reduce the noise in strict mode tests,
    
  207.       // suppress warnings and disable scheduler yielding during the double render
    
  208.       unstable_setDisableYieldValue(newIsStrictMode);
    
  209.       setSuppressWarning(newIsStrictMode);
    
  210.     }
    
  211. 
    
  212.     if (injectedHook && typeof injectedHook.setStrictMode === 'function') {
    
  213.       try {
    
  214.         injectedHook.setStrictMode(rendererID, newIsStrictMode);
    
  215.       } catch (err) {
    
  216.         if (__DEV__) {
    
  217.           if (!hasLoggedError) {
    
  218.             hasLoggedError = true;
    
  219.             console.error(
    
  220.               'React instrumentation encountered an error: %s',
    
  221.               err,
    
  222.             );
    
  223.           }
    
  224.         }
    
  225.       }
    
  226.     }
    
  227.   } else {
    
  228.     if (newIsStrictMode) {
    
  229.       disableLogs();
    
  230.     } else {
    
  231.       reenableLogs();
    
  232.     }
    
  233.   }
    
  234. }
    
  235. 
    
  236. // Profiler API hooks
    
  237. 
    
  238. function injectProfilingHooks(profilingHooks: DevToolsProfilingHooks): void {
    
  239.   injectedProfilingHooks = profilingHooks;
    
  240. }
    
  241. 
    
  242. function getLaneLabelMap(): Map<Lane, string> | null {
    
  243.   if (enableSchedulingProfiler) {
    
  244.     const map: Map<Lane, string> = new Map();
    
  245. 
    
  246.     let lane = 1;
    
  247.     for (let index = 0; index < TotalLanes; index++) {
    
  248.       const label = ((getLabelForLane(lane): any): string);
    
  249.       map.set(lane, label);
    
  250.       lane *= 2;
    
  251.     }
    
  252. 
    
  253.     return map;
    
  254.   } else {
    
  255.     return null;
    
  256.   }
    
  257. }
    
  258. 
    
  259. export function markCommitStarted(lanes: Lanes): void {
    
  260.   if (enableSchedulingProfiler) {
    
  261.     if (
    
  262.       injectedProfilingHooks !== null &&
    
  263.       typeof injectedProfilingHooks.markCommitStarted === 'function'
    
  264.     ) {
    
  265.       injectedProfilingHooks.markCommitStarted(lanes);
    
  266.     }
    
  267.   }
    
  268. }
    
  269. 
    
  270. export function markCommitStopped(): void {
    
  271.   if (enableSchedulingProfiler) {
    
  272.     if (
    
  273.       injectedProfilingHooks !== null &&
    
  274.       typeof injectedProfilingHooks.markCommitStopped === 'function'
    
  275.     ) {
    
  276.       injectedProfilingHooks.markCommitStopped();
    
  277.     }
    
  278.   }
    
  279. }
    
  280. 
    
  281. export function markComponentRenderStarted(fiber: Fiber): void {
    
  282.   if (enableSchedulingProfiler) {
    
  283.     if (
    
  284.       injectedProfilingHooks !== null &&
    
  285.       typeof injectedProfilingHooks.markComponentRenderStarted === 'function'
    
  286.     ) {
    
  287.       injectedProfilingHooks.markComponentRenderStarted(fiber);
    
  288.     }
    
  289.   }
    
  290. }
    
  291. 
    
  292. export function markComponentRenderStopped(): void {
    
  293.   if (enableSchedulingProfiler) {
    
  294.     if (
    
  295.       injectedProfilingHooks !== null &&
    
  296.       typeof injectedProfilingHooks.markComponentRenderStopped === 'function'
    
  297.     ) {
    
  298.       injectedProfilingHooks.markComponentRenderStopped();
    
  299.     }
    
  300.   }
    
  301. }
    
  302. 
    
  303. export function markComponentPassiveEffectMountStarted(fiber: Fiber): void {
    
  304.   if (enableSchedulingProfiler) {
    
  305.     if (
    
  306.       injectedProfilingHooks !== null &&
    
  307.       typeof injectedProfilingHooks.markComponentPassiveEffectMountStarted ===
    
  308.         'function'
    
  309.     ) {
    
  310.       injectedProfilingHooks.markComponentPassiveEffectMountStarted(fiber);
    
  311.     }
    
  312.   }
    
  313. }
    
  314. 
    
  315. export function markComponentPassiveEffectMountStopped(): void {
    
  316.   if (enableSchedulingProfiler) {
    
  317.     if (
    
  318.       injectedProfilingHooks !== null &&
    
  319.       typeof injectedProfilingHooks.markComponentPassiveEffectMountStopped ===
    
  320.         'function'
    
  321.     ) {
    
  322.       injectedProfilingHooks.markComponentPassiveEffectMountStopped();
    
  323.     }
    
  324.   }
    
  325. }
    
  326. 
    
  327. export function markComponentPassiveEffectUnmountStarted(fiber: Fiber): void {
    
  328.   if (enableSchedulingProfiler) {
    
  329.     if (
    
  330.       injectedProfilingHooks !== null &&
    
  331.       typeof injectedProfilingHooks.markComponentPassiveEffectUnmountStarted ===
    
  332.         'function'
    
  333.     ) {
    
  334.       injectedProfilingHooks.markComponentPassiveEffectUnmountStarted(fiber);
    
  335.     }
    
  336.   }
    
  337. }
    
  338. 
    
  339. export function markComponentPassiveEffectUnmountStopped(): void {
    
  340.   if (enableSchedulingProfiler) {
    
  341.     if (
    
  342.       injectedProfilingHooks !== null &&
    
  343.       typeof injectedProfilingHooks.markComponentPassiveEffectUnmountStopped ===
    
  344.         'function'
    
  345.     ) {
    
  346.       injectedProfilingHooks.markComponentPassiveEffectUnmountStopped();
    
  347.     }
    
  348.   }
    
  349. }
    
  350. 
    
  351. export function markComponentLayoutEffectMountStarted(fiber: Fiber): void {
    
  352.   if (enableSchedulingProfiler) {
    
  353.     if (
    
  354.       injectedProfilingHooks !== null &&
    
  355.       typeof injectedProfilingHooks.markComponentLayoutEffectMountStarted ===
    
  356.         'function'
    
  357.     ) {
    
  358.       injectedProfilingHooks.markComponentLayoutEffectMountStarted(fiber);
    
  359.     }
    
  360.   }
    
  361. }
    
  362. 
    
  363. export function markComponentLayoutEffectMountStopped(): void {
    
  364.   if (enableSchedulingProfiler) {
    
  365.     if (
    
  366.       injectedProfilingHooks !== null &&
    
  367.       typeof injectedProfilingHooks.markComponentLayoutEffectMountStopped ===
    
  368.         'function'
    
  369.     ) {
    
  370.       injectedProfilingHooks.markComponentLayoutEffectMountStopped();
    
  371.     }
    
  372.   }
    
  373. }
    
  374. 
    
  375. export function markComponentLayoutEffectUnmountStarted(fiber: Fiber): void {
    
  376.   if (enableSchedulingProfiler) {
    
  377.     if (
    
  378.       injectedProfilingHooks !== null &&
    
  379.       typeof injectedProfilingHooks.markComponentLayoutEffectUnmountStarted ===
    
  380.         'function'
    
  381.     ) {
    
  382.       injectedProfilingHooks.markComponentLayoutEffectUnmountStarted(fiber);
    
  383.     }
    
  384.   }
    
  385. }
    
  386. 
    
  387. export function markComponentLayoutEffectUnmountStopped(): void {
    
  388.   if (enableSchedulingProfiler) {
    
  389.     if (
    
  390.       injectedProfilingHooks !== null &&
    
  391.       typeof injectedProfilingHooks.markComponentLayoutEffectUnmountStopped ===
    
  392.         'function'
    
  393.     ) {
    
  394.       injectedProfilingHooks.markComponentLayoutEffectUnmountStopped();
    
  395.     }
    
  396.   }
    
  397. }
    
  398. 
    
  399. export function markComponentErrored(
    
  400.   fiber: Fiber,
    
  401.   thrownValue: mixed,
    
  402.   lanes: Lanes,
    
  403. ): void {
    
  404.   if (enableSchedulingProfiler) {
    
  405.     if (
    
  406.       injectedProfilingHooks !== null &&
    
  407.       typeof injectedProfilingHooks.markComponentErrored === 'function'
    
  408.     ) {
    
  409.       injectedProfilingHooks.markComponentErrored(fiber, thrownValue, lanes);
    
  410.     }
    
  411.   }
    
  412. }
    
  413. 
    
  414. export function markComponentSuspended(
    
  415.   fiber: Fiber,
    
  416.   wakeable: Wakeable,
    
  417.   lanes: Lanes,
    
  418. ): void {
    
  419.   if (enableSchedulingProfiler) {
    
  420.     if (
    
  421.       injectedProfilingHooks !== null &&
    
  422.       typeof injectedProfilingHooks.markComponentSuspended === 'function'
    
  423.     ) {
    
  424.       injectedProfilingHooks.markComponentSuspended(fiber, wakeable, lanes);
    
  425.     }
    
  426.   }
    
  427. }
    
  428. 
    
  429. export function markLayoutEffectsStarted(lanes: Lanes): void {
    
  430.   if (enableSchedulingProfiler) {
    
  431.     if (
    
  432.       injectedProfilingHooks !== null &&
    
  433.       typeof injectedProfilingHooks.markLayoutEffectsStarted === 'function'
    
  434.     ) {
    
  435.       injectedProfilingHooks.markLayoutEffectsStarted(lanes);
    
  436.     }
    
  437.   }
    
  438. }
    
  439. 
    
  440. export function markLayoutEffectsStopped(): void {
    
  441.   if (enableSchedulingProfiler) {
    
  442.     if (
    
  443.       injectedProfilingHooks !== null &&
    
  444.       typeof injectedProfilingHooks.markLayoutEffectsStopped === 'function'
    
  445.     ) {
    
  446.       injectedProfilingHooks.markLayoutEffectsStopped();
    
  447.     }
    
  448.   }
    
  449. }
    
  450. 
    
  451. export function markPassiveEffectsStarted(lanes: Lanes): void {
    
  452.   if (enableSchedulingProfiler) {
    
  453.     if (
    
  454.       injectedProfilingHooks !== null &&
    
  455.       typeof injectedProfilingHooks.markPassiveEffectsStarted === 'function'
    
  456.     ) {
    
  457.       injectedProfilingHooks.markPassiveEffectsStarted(lanes);
    
  458.     }
    
  459.   }
    
  460. }
    
  461. 
    
  462. export function markPassiveEffectsStopped(): void {
    
  463.   if (enableSchedulingProfiler) {
    
  464.     if (
    
  465.       injectedProfilingHooks !== null &&
    
  466.       typeof injectedProfilingHooks.markPassiveEffectsStopped === 'function'
    
  467.     ) {
    
  468.       injectedProfilingHooks.markPassiveEffectsStopped();
    
  469.     }
    
  470.   }
    
  471. }
    
  472. 
    
  473. export function markRenderStarted(lanes: Lanes): void {
    
  474.   if (enableSchedulingProfiler) {
    
  475.     if (
    
  476.       injectedProfilingHooks !== null &&
    
  477.       typeof injectedProfilingHooks.markRenderStarted === 'function'
    
  478.     ) {
    
  479.       injectedProfilingHooks.markRenderStarted(lanes);
    
  480.     }
    
  481.   }
    
  482. }
    
  483. 
    
  484. export function markRenderYielded(): void {
    
  485.   if (enableSchedulingProfiler) {
    
  486.     if (
    
  487.       injectedProfilingHooks !== null &&
    
  488.       typeof injectedProfilingHooks.markRenderYielded === 'function'
    
  489.     ) {
    
  490.       injectedProfilingHooks.markRenderYielded();
    
  491.     }
    
  492.   }
    
  493. }
    
  494. 
    
  495. export function markRenderStopped(): void {
    
  496.   if (enableSchedulingProfiler) {
    
  497.     if (
    
  498.       injectedProfilingHooks !== null &&
    
  499.       typeof injectedProfilingHooks.markRenderStopped === 'function'
    
  500.     ) {
    
  501.       injectedProfilingHooks.markRenderStopped();
    
  502.     }
    
  503.   }
    
  504. }
    
  505. 
    
  506. export function markRenderScheduled(lane: Lane): void {
    
  507.   if (enableSchedulingProfiler) {
    
  508.     if (
    
  509.       injectedProfilingHooks !== null &&
    
  510.       typeof injectedProfilingHooks.markRenderScheduled === 'function'
    
  511.     ) {
    
  512.       injectedProfilingHooks.markRenderScheduled(lane);
    
  513.     }
    
  514.   }
    
  515. }
    
  516. 
    
  517. export function markForceUpdateScheduled(fiber: Fiber, lane: Lane): void {
    
  518.   if (enableSchedulingProfiler) {
    
  519.     if (
    
  520.       injectedProfilingHooks !== null &&
    
  521.       typeof injectedProfilingHooks.markForceUpdateScheduled === 'function'
    
  522.     ) {
    
  523.       injectedProfilingHooks.markForceUpdateScheduled(fiber, lane);
    
  524.     }
    
  525.   }
    
  526. }
    
  527. 
    
  528. export function markStateUpdateScheduled(fiber: Fiber, lane: Lane): void {
    
  529.   if (enableSchedulingProfiler) {
    
  530.     if (
    
  531.       injectedProfilingHooks !== null &&
    
  532.       typeof injectedProfilingHooks.markStateUpdateScheduled === 'function'
    
  533.     ) {
    
  534.       injectedProfilingHooks.markStateUpdateScheduled(fiber, lane);
    
  535.     }
    
  536.   }
    
  537. }