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 {ReactContext} from 'shared/ReactTypes';
    
  11. import type {Fiber, FiberRoot} from './ReactInternalTypes';
    
  12. import type {Lanes} from './ReactFiberLane';
    
  13. import type {SuspenseState} from './ReactFiberSuspenseComponent';
    
  14. import type {Cache} from './ReactFiberCacheComponent';
    
  15. import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent';
    
  16. 
    
  17. import {
    
  18.   ClassComponent,
    
  19.   HostRoot,
    
  20.   HostComponent,
    
  21.   HostHoistable,
    
  22.   HostSingleton,
    
  23.   HostPortal,
    
  24.   ContextProvider,
    
  25.   SuspenseComponent,
    
  26.   SuspenseListComponent,
    
  27.   OffscreenComponent,
    
  28.   LegacyHiddenComponent,
    
  29.   CacheComponent,
    
  30.   TracingMarkerComponent,
    
  31. } from './ReactWorkTags';
    
  32. import {DidCapture, NoFlags, ShouldCapture} from './ReactFiberFlags';
    
  33. import {NoMode, ProfileMode} from './ReactTypeOfMode';
    
  34. import {
    
  35.   enableProfilerTimer,
    
  36.   enableCache,
    
  37.   enableTransitionTracing,
    
  38. } from 'shared/ReactFeatureFlags';
    
  39. 
    
  40. import {popHostContainer, popHostContext} from './ReactFiberHostContext';
    
  41. import {
    
  42.   popSuspenseListContext,
    
  43.   popSuspenseHandler,
    
  44. } from './ReactFiberSuspenseContext';
    
  45. import {popHiddenContext} from './ReactFiberHiddenContext';
    
  46. import {resetHydrationState} from './ReactFiberHydrationContext';
    
  47. import {
    
  48.   isContextProvider as isLegacyContextProvider,
    
  49.   popContext as popLegacyContext,
    
  50.   popTopLevelContextObject as popTopLevelLegacyContextObject,
    
  51. } from './ReactFiberContext';
    
  52. import {popProvider} from './ReactFiberNewContext';
    
  53. import {popCacheProvider} from './ReactFiberCacheComponent';
    
  54. import {transferActualDuration} from './ReactProfilerTimer';
    
  55. import {popTreeContext} from './ReactFiberTreeContext';
    
  56. import {popRootTransition, popTransition} from './ReactFiberTransition';
    
  57. import {
    
  58.   popMarkerInstance,
    
  59.   popRootMarkerInstance,
    
  60. } from './ReactFiberTracingMarkerComponent';
    
  61. 
    
  62. function unwindWork(
    
  63.   current: Fiber | null,
    
  64.   workInProgress: Fiber,
    
  65.   renderLanes: Lanes,
    
  66. ): Fiber | null {
    
  67.   // Note: This intentionally doesn't check if we're hydrating because comparing
    
  68.   // to the current tree provider fiber is just as fast and less error-prone.
    
  69.   // Ideally we would have a special version of the work loop only
    
  70.   // for hydration.
    
  71.   popTreeContext(workInProgress);
    
  72.   switch (workInProgress.tag) {
    
  73.     case ClassComponent: {
    
  74.       const Component = workInProgress.type;
    
  75.       if (isLegacyContextProvider(Component)) {
    
  76.         popLegacyContext(workInProgress);
    
  77.       }
    
  78.       const flags = workInProgress.flags;
    
  79.       if (flags & ShouldCapture) {
    
  80.         workInProgress.flags = (flags & ~ShouldCapture) | DidCapture;
    
  81.         if (
    
  82.           enableProfilerTimer &&
    
  83.           (workInProgress.mode & ProfileMode) !== NoMode
    
  84.         ) {
    
  85.           transferActualDuration(workInProgress);
    
  86.         }
    
  87.         return workInProgress;
    
  88.       }
    
  89.       return null;
    
  90.     }
    
  91.     case HostRoot: {
    
  92.       const root: FiberRoot = workInProgress.stateNode;
    
  93.       if (enableCache) {
    
  94.         const cache: Cache = workInProgress.memoizedState.cache;
    
  95.         popCacheProvider(workInProgress, cache);
    
  96.       }
    
  97. 
    
  98.       if (enableTransitionTracing) {
    
  99.         popRootMarkerInstance(workInProgress);
    
  100.       }
    
  101. 
    
  102.       popRootTransition(workInProgress, root, renderLanes);
    
  103.       popHostContainer(workInProgress);
    
  104.       popTopLevelLegacyContextObject(workInProgress);
    
  105.       const flags = workInProgress.flags;
    
  106.       if (
    
  107.         (flags & ShouldCapture) !== NoFlags &&
    
  108.         (flags & DidCapture) === NoFlags
    
  109.       ) {
    
  110.         // There was an error during render that wasn't captured by a suspense
    
  111.         // boundary. Do a second pass on the root to unmount the children.
    
  112.         workInProgress.flags = (flags & ~ShouldCapture) | DidCapture;
    
  113.         return workInProgress;
    
  114.       }
    
  115.       // We unwound to the root without completing it. Exit.
    
  116.       return null;
    
  117.     }
    
  118.     case HostHoistable:
    
  119.     case HostSingleton:
    
  120.     case HostComponent: {
    
  121.       // TODO: popHydrationState
    
  122.       popHostContext(workInProgress);
    
  123.       return null;
    
  124.     }
    
  125.     case SuspenseComponent: {
    
  126.       popSuspenseHandler(workInProgress);
    
  127.       const suspenseState: null | SuspenseState = workInProgress.memoizedState;
    
  128.       if (suspenseState !== null && suspenseState.dehydrated !== null) {
    
  129.         if (workInProgress.alternate === null) {
    
  130.           throw new Error(
    
  131.             'Threw in newly mounted dehydrated component. This is likely a bug in ' +
    
  132.               'React. Please file an issue.',
    
  133.           );
    
  134.         }
    
  135. 
    
  136.         resetHydrationState();
    
  137.       }
    
  138. 
    
  139.       const flags = workInProgress.flags;
    
  140.       if (flags & ShouldCapture) {
    
  141.         workInProgress.flags = (flags & ~ShouldCapture) | DidCapture;
    
  142.         // Captured a suspense effect. Re-render the boundary.
    
  143.         if (
    
  144.           enableProfilerTimer &&
    
  145.           (workInProgress.mode & ProfileMode) !== NoMode
    
  146.         ) {
    
  147.           transferActualDuration(workInProgress);
    
  148.         }
    
  149.         return workInProgress;
    
  150.       }
    
  151.       return null;
    
  152.     }
    
  153.     case SuspenseListComponent: {
    
  154.       popSuspenseListContext(workInProgress);
    
  155.       // SuspenseList doesn't actually catch anything. It should've been
    
  156.       // caught by a nested boundary. If not, it should bubble through.
    
  157.       return null;
    
  158.     }
    
  159.     case HostPortal:
    
  160.       popHostContainer(workInProgress);
    
  161.       return null;
    
  162.     case ContextProvider:
    
  163.       const context: ReactContext<any> = workInProgress.type._context;
    
  164.       popProvider(context, workInProgress);
    
  165.       return null;
    
  166.     case OffscreenComponent:
    
  167.     case LegacyHiddenComponent: {
    
  168.       popSuspenseHandler(workInProgress);
    
  169.       popHiddenContext(workInProgress);
    
  170.       popTransition(workInProgress, current);
    
  171.       const flags = workInProgress.flags;
    
  172.       if (flags & ShouldCapture) {
    
  173.         workInProgress.flags = (flags & ~ShouldCapture) | DidCapture;
    
  174.         // Captured a suspense effect. Re-render the boundary.
    
  175.         if (
    
  176.           enableProfilerTimer &&
    
  177.           (workInProgress.mode & ProfileMode) !== NoMode
    
  178.         ) {
    
  179.           transferActualDuration(workInProgress);
    
  180.         }
    
  181.         return workInProgress;
    
  182.       }
    
  183.       return null;
    
  184.     }
    
  185.     case CacheComponent:
    
  186.       if (enableCache) {
    
  187.         const cache: Cache = workInProgress.memoizedState.cache;
    
  188.         popCacheProvider(workInProgress, cache);
    
  189.       }
    
  190.       return null;
    
  191.     case TracingMarkerComponent:
    
  192.       if (enableTransitionTracing) {
    
  193.         if (workInProgress.stateNode !== null) {
    
  194.           popMarkerInstance(workInProgress);
    
  195.         }
    
  196.       }
    
  197.       return null;
    
  198.     default:
    
  199.       return null;
    
  200.   }
    
  201. }
    
  202. 
    
  203. function unwindInterruptedWork(
    
  204.   current: Fiber | null,
    
  205.   interruptedWork: Fiber,
    
  206.   renderLanes: Lanes,
    
  207. ) {
    
  208.   // Note: This intentionally doesn't check if we're hydrating because comparing
    
  209.   // to the current tree provider fiber is just as fast and less error-prone.
    
  210.   // Ideally we would have a special version of the work loop only
    
  211.   // for hydration.
    
  212.   popTreeContext(interruptedWork);
    
  213.   switch (interruptedWork.tag) {
    
  214.     case ClassComponent: {
    
  215.       const childContextTypes = interruptedWork.type.childContextTypes;
    
  216.       if (childContextTypes !== null && childContextTypes !== undefined) {
    
  217.         popLegacyContext(interruptedWork);
    
  218.       }
    
  219.       break;
    
  220.     }
    
  221.     case HostRoot: {
    
  222.       const root: FiberRoot = interruptedWork.stateNode;
    
  223.       if (enableCache) {
    
  224.         const cache: Cache = interruptedWork.memoizedState.cache;
    
  225.         popCacheProvider(interruptedWork, cache);
    
  226.       }
    
  227. 
    
  228.       if (enableTransitionTracing) {
    
  229.         popRootMarkerInstance(interruptedWork);
    
  230.       }
    
  231. 
    
  232.       popRootTransition(interruptedWork, root, renderLanes);
    
  233.       popHostContainer(interruptedWork);
    
  234.       popTopLevelLegacyContextObject(interruptedWork);
    
  235.       break;
    
  236.     }
    
  237.     case HostHoistable:
    
  238.     case HostSingleton:
    
  239.     case HostComponent: {
    
  240.       popHostContext(interruptedWork);
    
  241.       break;
    
  242.     }
    
  243.     case HostPortal:
    
  244.       popHostContainer(interruptedWork);
    
  245.       break;
    
  246.     case SuspenseComponent:
    
  247.       popSuspenseHandler(interruptedWork);
    
  248.       break;
    
  249.     case SuspenseListComponent:
    
  250.       popSuspenseListContext(interruptedWork);
    
  251.       break;
    
  252.     case ContextProvider:
    
  253.       const context: ReactContext<any> = interruptedWork.type._context;
    
  254.       popProvider(context, interruptedWork);
    
  255.       break;
    
  256.     case OffscreenComponent:
    
  257.     case LegacyHiddenComponent:
    
  258.       popSuspenseHandler(interruptedWork);
    
  259.       popHiddenContext(interruptedWork);
    
  260.       popTransition(interruptedWork, current);
    
  261.       break;
    
  262.     case CacheComponent:
    
  263.       if (enableCache) {
    
  264.         const cache: Cache = interruptedWork.memoizedState.cache;
    
  265.         popCacheProvider(interruptedWork, cache);
    
  266.       }
    
  267.       break;
    
  268.     case TracingMarkerComponent:
    
  269.       if (enableTransitionTracing) {
    
  270.         const instance: TracingMarkerInstance | null =
    
  271.           interruptedWork.stateNode;
    
  272.         if (instance !== null) {
    
  273.           popMarkerInstance(interruptedWork);
    
  274.         }
    
  275.       }
    
  276.       break;
    
  277.     default:
    
  278.       break;
    
  279.   }
    
  280. }
    
  281. 
    
  282. export {unwindWork, unwindInterruptedWork};