text: /**
   * Copyright (c) 2013-present, Facebook, Inc.
   *
   * This source code is licensed under the MIT license found in the
   * LICENSE file in the root directory of this source tree.
   *
   * @flow
   */
  
  // This is a built-in polyfill for requestIdleCallback. It works by scheduling
  // a requestAnimationFrame, storing the time for the start of the frame, then
  // scheduling a postMessage which gets scheduled after paint. Within the
  // postMessage handler do as much work as possible until time + frame rate.
  // By separating the idle call into a separate event tick we ensure that
  // layout, paint and other browser work is counted against the available time.
  // The frame rate is dynamically adjusted.
  
  import type {Deadline} from 'react-reconciler';
  
  import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
  import warning from 'fbjs/lib/warning';
  
  if (__DEV__) {
    if (
      ExecutionEnvironment.canUseDOM &&
      typeof requestAnimationFrame !== 'function'
    ) {
      warning(
        false,
        'React depends on requestAnimationFrame. Make sure that you load a ' +
          'polyfill in older browsers. http://fb.me/react-polyfills',
      );
    }
  }
  
  const hasNativePerformanceNow =
    typeof performance === 'object' && typeof performance.now === 'function';
  
  let now;
  if (hasNativePerformanceNow) {
    now = function() {
      return performance.now();
    };
  } else {
    now = function() {
      return Date.now();
    };
  }
  
  // TODO: There's no way to cancel, because Fiber doesn't atm.
  let rIC: (
    callback: (deadline: Deadline, options?: {timeout: number}) => void,
  ) => number;
  let cIC: (callbackID: number) => void;
  
  if (!ExecutionEnvironment.canUseDOM) {
    rIC = function(
      frameCallback: (deadline: Deadline, options?: {timeout: number}) => void,
    ): number {
      return setTimeout(() => {
        frameCallback({
          timeRemaining() {
            return Infinity;
          },
        });
      });
    };
    cIC = function(timeoutID: number) {
      clearTimeout(timeoutID);
    };
  } else if (
    typeof requestIdleCallback !== 'function' ||
    typeof cancelIdleCallback !== 'function'
  ) {
    // Polyfill requestIdleCallback and cancelIdleCallback
  
    var scheduledRICCallback = null;
    var isIdleScheduled = false;
    var timeoutTime = -1;
  
    var isAnimationFrameScheduled = false;
  
    var frameDeadline = 0;
    // We start out assuming that we run at 30fps but then the heuristic tracking
    // will adjust this value to a faster fps if we get more frequent animation
    // frames.
    var previousFrameTime = 33;
    var activeFrameTime = 33;
  
    var frameDeadlineObject;
    if (hasNativePerformanceNow) {
      frameDeadlineObject = {
        didTimeout: false,
        timeRemaining() {
          // We assume that if we have a performance timer that the rAF callback
          // gets a performance timer value. Not sure if this is always true.
          const remaining = frameDeadline - performance.now();
          return remaining > 0 ? remaining : 0;
        },
      };
    } else {
      frameDeadlineObject = {
        didTimeout: false,
        timeRemaining() {
          // Fallback to Date.now()
          const remaining = frameDeadline - Date.now();
          return remaining > 0 ? remaining : 0;
        },
      };
    }
  
    // We use the postMessage trick to defer idle work until after the repaint.
    var messageKey =
      '__reactIdleCallback$' +
      Math.random()
        .toString(36)
        .slice(2);
    var idleTick = function(event) {
      if (event.source !== window || event.data !== messageKey) {
        return;
      }
  
      isIdleScheduled = false;
  
      const currentTime = now();
      if (frameDeadline - currentTime <= 0) {
        // There's no time left in this idle period. Check if the callback has
        // a timeout and whether it's been exceeded.
        if (timeoutTime !== -1 && timeoutTime <= currentTime) {
          // Exceeded the timeout. Invoke the callback even though there's no
          // time left.
          frameDeadlineObject.didTimeout = true;
        } else {
          // No timeout.
          if (!isAnimationFrameScheduled) {
            // Schedule another animation callback so we retry later.
            isAnimationFrameScheduled = true;
            requestAnimationFrame(animationTick);
          }
          // Exit without invoking the callback.
          return;
        }
      } else {
        // There's still time left in this idle period.
        frameDeadlineObject.didTimeout = false;
      }
  
      timeoutTime = -1;
      var callback = scheduledRICCallback;
      scheduledRICCallback = null;
      if (callback !== null) {
        callback(frameDeadlineObject);
      }
    };
    // Assumes that we have addEventListener in this environment. Might need
    // something better for old IE.
    window.addEventListener('message', idleTick, false);
  
    var animationTick = function(rafTime) {
      isAnimationFrameScheduled = false;
      var nextFrameTime = rafTime - frameDeadline + activeFrameTime;
      if (
        nextFrameTime < activeFrameTime &&
        previousFrameTime < activeFrameTime
      ) {
        if (nextFrameTime < 8) {
          // Defensive coding. We don't support higher frame rates than 120hz.
          // If we get lower than that, it is probably a bug.
          nextFrameTime = 8;
        }
        // If one frame goes long, then the next one can be short to catch up.
        // If two frames are short in a row, then that's an indication that we
        // actually have a higher frame rate than what we're currently optimizing.
        // We adjust our heuristic dynamically accordingly. For example, if we're
        // running on 120hz display or 90hz VR display.
        // Take the max of the two in case one of them was an anomaly due to
        // missed frame deadlines.
        activeFrameTime =
          nextFrameTime < previousFrameTime ? previousFrameTime : nextFrameTime;
      } else {
        previousFrameTime = nextFrameTime;
      }
      frameDeadline = rafTime + activeFrameTime;
      if (!isIdleScheduled) {
        isIdleScheduled = true;
        window.postMessage(messageKey, '*');
      }
    };
  
    rIC = function(
      callback: (deadline: Deadline) => void,
      options?: {timeout: number},
    ): number {
      // This assumes that we only schedule one callback at a time because that's
      // how Fiber uses it.
      scheduledRICCallback = callback;
      if (options != null && typeof options.timeout === 'number') {
        timeoutTime = now() + options.timeout;
      }
      if (!isAnimationFrameScheduled) {
        // If rAF didn't already schedule one, we need to schedule a frame.
        // TODO: If this rAF doesn't materialize because the browser throttles, we
        // might want to still have setTimeout trigger rIC as a backup to ensure
        // that we keep performing work.
        isAnimationFrameScheduled = true;
        requestAnimationFrame(animationTick);
      }
      return 0;
    };
  
    cIC = function() {
      scheduledRICCallback = null;
      isIdleScheduled = false;
      timeoutTime = -1;
    };
  } else {
    rIC = window.requestIdleCallback;
    cIC = window.cancelIdleCallback;
  }
  
  export {now, rIC, cIC};
  