private void calculateLayout()

in litho-core/src/main/java/com/facebook/litho/ComponentTree.java [2255:2444]


  private void calculateLayout(
      @Nullable Size output,
      @CalculateLayoutSource int source,
      @Nullable String extraAttribution,
      @Nullable TreeProps treeProps,
      boolean isCreateLayoutInProgress) {
    final int widthSpec;
    final int heightSpec;
    final Component root;
    final int localLayoutVersion;

    // Cancel any scheduled layout requests we might have in the background queue
    // since we are starting a new layout computation.
    synchronized (mCurrentCalculateLayoutRunnableLock) {
      if (mCurrentCalculateLayoutRunnable != null) {
        mLayoutThreadHandler.remove(mCurrentCalculateLayoutRunnable);
        mCurrentCalculateLayoutRunnable = null;
      }
    }

    synchronized (this) {
      // Can't compute a layout if specs or root are missing
      if (!hasSizeSpec() || mRoot == null) {
        return;
      }

      // Check if we already have a compatible layout.
      if (isCompatibleComponentAndSpec(mCommittedLayoutState)) {
        if (output != null) {
          output.width = mCommittedLayoutState.getWidth();
          output.height = mCommittedLayoutState.getHeight();
        }
        return;
      }

      widthSpec = mWidthSpec;
      heightSpec = mHeightSpec;
      root = mRoot;
      localLayoutVersion = mNextLayoutVersion++;
    }

    final LayoutState localLayoutState =
        calculateLayoutState(
            mContext,
            root,
            widthSpec,
            heightSpec,
            localLayoutVersion,
            mIsLayoutDiffingEnabled,
            treeProps,
            source,
            extraAttribution);

    if (localLayoutState == null) {
      if (!isReleased()
          && isFromSyncLayout(source)
          && !mComponentsConfiguration.getUseCancelableLayoutFutures()) {
        final String errorMessage =
            "LayoutState is null, but only async operations can return a null LayoutState. Source: "
                + layoutSourceToString(source)
                + ", current thread: "
                + Thread.currentThread().getName()
                + ". Root: "
                + (mRoot == null ? "null" : mRoot.getSimpleName())
                + ". Interruptible layouts: "
                + mMoveLayoutsBetweenThreads;

        if (mComponentsConfiguration.getIgnoreNullLayoutStateError()) {
          ComponentsReporter.emitMessage(
              ComponentsReporter.LogLevel.ERROR, "ComponentTree:LayoutStateNull", errorMessage);
        } else {
          throw new IllegalStateException(errorMessage);
        }
      }

      return;
    }

    if (output != null) {
      output.width = localLayoutState.getWidth();
      output.height = localLayoutState.getHeight();
    }

    List<ScopedComponentInfo> scopedComponentInfos = null;

    int rootWidth = 0;
    int rootHeight = 0;
    boolean committedNewLayout = false;
    synchronized (this) {
      // We don't want to compute, layout, or reduce trees while holding a lock. However this means
      // that another thread could compute a layout and commit it before we get to this point. To
      // handle this, we make sure that the committed setRootId is only ever increased, meaning
      // we only go "forward in time" and will eventually get to the latest layout.
      // TODO(t66287929): Remove isCommitted check by only allowing one LayoutStateFuture at a time
      if (localLayoutVersion > mCommittedLayoutVersion
          && !localLayoutState.isCommitted()
          && isCompatibleSpec(localLayoutState, mWidthSpec, mHeightSpec)) {
        mCommittedLayoutVersion = localLayoutVersion;
        mCommittedLayoutState = localLayoutState;
        localLayoutState.markCommitted();
        committedNewLayout = true;
      }

      if (DEBUG_LOGS) {
        logFinishLayout(source, extraAttribution, localLayoutState, committedNewLayout);
      }

      final StateHandler layoutStateStateHandler = localLayoutState.consumeStateHandler();
      if (committedNewLayout) {

        scopedComponentInfos = localLayoutState.consumeScopedComponentInfos();

        if (layoutStateStateHandler != null && scopedComponentInfos != null) {
          final StateHandler stateHandler = mStateHandler;
          if (stateHandler != null) { // we could have been released
            if (ComponentsConfiguration.isTimelineEnabled) {
              ScopedComponentInfo rootScopedComponentInfo = null;
              for (ScopedComponentInfo scopedComponentInfo : scopedComponentInfos) {
                if (scopedComponentInfo.getComponent().equals(root)) {
                  rootScopedComponentInfo = scopedComponentInfo;
                  break;
                }
              }
              final String globalKey =
                  (rootScopedComponentInfo != null)
                      ? rootScopedComponentInfo.getContext().getGlobalKey()
                      : null;
              DebugComponentTimeMachine.saveTimelineSnapshot(
                  this, root, globalKey, stateHandler, treeProps, source, extraAttribution);
            }
            stateHandler.commit(layoutStateStateHandler);
          }
        }

        if (mMeasureListeners != null) {
          rootWidth = localLayoutState.getWidth();
          rootHeight = localLayoutState.getHeight();
        }
      }

      if (layoutStateStateHandler != null) {
        mInitialStateContainer.unregisterStateHandler(layoutStateStateHandler);
      }

      // Resetting the count after layout calculation is complete and it was triggered from within
      // layout creation
      if (!isCreateLayoutInProgress) {
        mStateUpdatesFromCreateLayoutCount = 0;
      }
    }

    if (committedNewLayout) {
      final List<MeasureListener> measureListeners;
      synchronized (this) {
        measureListeners = mMeasureListeners == null ? null : new ArrayList<>(mMeasureListeners);
      }

      if (measureListeners != null) {
        for (MeasureListener measureListener : measureListeners) {
          measureListener.onSetRootAndSizeSpec(
              localLayoutVersion,
              rootWidth,
              rootHeight,
              source == CalculateLayoutSource.UPDATE_STATE_ASYNC
                  || source == CalculateLayoutSource.UPDATE_STATE_SYNC);
        }
      }
    }

    if (scopedComponentInfos != null) {
      bindEventAndTriggerHandlers(scopedComponentInfos);
    }

    if (committedNewLayout) {
      postBackgroundLayoutStateUpdated();
    }

    if (mPreAllocateMountContentHandler != null) {
      mPreAllocateMountContentHandler.remove(mPreAllocateMountContentRunnable);

      String tag = EMPTY_STRING;
      if (mPreAllocateMountContentHandler.isTracing()) {
        tag = "preallocateLayout ";
        if (root != null) {
          tag = tag + root.getSimpleName();
        }
      }
      mPreAllocateMountContentHandler.post(mPreAllocateMountContentRunnable, tag);
    }
  }