computedValue property

double computedValue

Implementation

double get computedValue {

  switch (type) {
    case CSSLengthType.PX:
      _computedValue = value;
      break;
    case CSSLengthType.EM:
      // Font size of the parent, in the case of typographical properties like font-size,
      // and font size of the element itself, in the case of other properties like width.
      if (propertyName == FONT_SIZE) {
        // If root element set fontSize as em unit.
        if (renderStyle!.parent == null) {
          _computedValue = value! * 16;
        } else {
          _computedValue = value! * renderStyle!.parent!.fontSize.computedValue;
        }
      } else {
        _computedValue = value! * renderStyle!.fontSize.computedValue;
      }
      break;
    case CSSLengthType.REM:
      // If root element set fontSize as rem unit.
      if (renderStyle!.parent == null) {
        _computedValue = value! * 16;
      } else {
        // Font rem is calculated against the root element's font size.
        _computedValue = value! * renderStyle!.rootFontSize;
      }
      break;
    case CSSLengthType.VH:
      _computedValue = value! * renderStyle!.viewportSize.height;
      break;
    case CSSLengthType.VW:
      _computedValue = value! * renderStyle!.viewportSize.width;
      break;
    // 1% of viewport's smaller (vw or vh) dimension.
    // If the height of the viewport is less than its width, 1vmin will be equivalent to 1vh.
    // If the width of the viewport is less than it’s height, 1vmin is equvialent to 1vw.
    case CSSLengthType.VMIN:
      _computedValue = value! * renderStyle!.viewportSize.shortestSide;
      break;
    case CSSLengthType.VMAX:
      _computedValue = value! * renderStyle!.viewportSize.longestSide;
      break;
    case CSSLengthType.PERCENTAGE:
      CSSPositionType positionType = renderStyle!.position;
      bool isPositioned = positionType == CSSPositionType.absolute ||
        positionType == CSSPositionType.fixed;

      RenderBoxModel? renderBoxModel = renderStyle!.renderBoxModel;
      // Should access the renderStyle of renderBoxModel parent but not renderStyle parent
      // cause the element of renderStyle parent may not equal to containing block.
      RenderStyle? parentRenderStyle;
      if (renderBoxModel?.parent is RenderBoxModel) {
        RenderBoxModel parentRenderBoxModel = renderBoxModel?.parent as RenderBoxModel;
        // Get the renderStyle of outer scrolling box cause the renderStyle of scrolling
        // content box is only a fraction of the complete renderStyle.
        parentRenderStyle = parentRenderBoxModel.isScrollingContentBox
          ? (parentRenderBoxModel.parent as RenderBoxModel).renderStyle
          : parentRenderBoxModel.renderStyle;
      }

      // Percentage relative width priority: logical width > renderer width
      double? parentPaddingBoxWidth =
        parentRenderStyle?.paddingBoxLogicalWidth
        ?? parentRenderStyle?.paddingBoxWidth;
      double? parentContentBoxWidth =
        parentRenderStyle?.contentBoxLogicalWidth
        ?? parentRenderStyle?.contentBoxWidth;
      // Percentage relative height priority: logical height > renderer height
      double? parentPaddingBoxHeight =
        parentRenderStyle?.paddingBoxLogicalHeight
        ?? parentRenderStyle?.paddingBoxHeight;
      double? parentContentBoxHeight =
        parentRenderStyle?.contentBoxLogicalHeight
        ?? parentRenderStyle?.contentBoxHeight;

      // Positioned element is positioned relative to the padding box of its containing block
      // while the others relative to the content box.
      double? relativeParentWidth = isPositioned
        ? parentPaddingBoxWidth
        : parentContentBoxWidth;
      double? relativeParentHeight = isPositioned
        ? parentPaddingBoxHeight
        : parentContentBoxHeight;

      switch (propertyName) {
        case FONT_SIZE:
          // Relative to the parent font size.
          if (renderStyle!.parent == null) {
            _computedValue = value! * 16;
          } else {
            _computedValue = value! * renderStyle!.parent!.fontSize.computedValue;
          }
          break;
        case LINE_HEIGHT:
          // Relative to the font size of the element itself.
          _computedValue = value! * renderStyle!.fontSize.computedValue;
          break;
        case WIDTH:
        case MIN_WIDTH:
        case MAX_WIDTH:
          if (relativeParentWidth != null) {
            _computedValue = value! * relativeParentWidth;
          } else {
            // Mark parent to relayout to get renderer width of parent.
            if (renderBoxModel != null) {
              renderBoxModel.markParentNeedsRelayout();
            }
            _computedValue = double.infinity;
          }
          break;
        case HEIGHT:
        case MIN_HEIGHT:
        case MAX_HEIGHT:
          // The percentage of height is calculated with respect to the height of the generated box's containing block.
          // If the height of the containing block is not specified explicitly (i.e., it depends on content height),
          // and this element is not absolutely positioned, the value computes to 'auto'.
          // https://www.w3.org/TR/CSS2/visudet.html#propdef-height
          // There are two exceptions when percentage height is resolved against actual render height of parent:
          // 1. positioned element
          // 2. parent is flex item
          RenderStyle? grandParentRenderStyle = parentRenderStyle?.parent;
          bool isGrandParentFlexLayout = grandParentRenderStyle?.display == CSSDisplay.flex ||
            grandParentRenderStyle?.display == CSSDisplay.inlineFlex;

          // The percentage height of positioned element and flex item resolves against the rendered height
          // of parent, mark parent as needs relayout if rendered height is not ready yet.
          if (isPositioned || isGrandParentFlexLayout) {
            if (relativeParentHeight  != null) {
              _computedValue = value! * relativeParentHeight;
            } else {
              // Mark parent to relayout to get renderer height of parent.
              if (renderBoxModel != null) {
                renderBoxModel.markParentNeedsRelayout();
              }
              _computedValue = double.infinity;
            }
          } else {
            double? relativeParentHeight = parentRenderStyle?.contentBoxLogicalHeight;
            if (relativeParentHeight != null) {
              _computedValue = value! * relativeParentHeight;
            } else {
              // Resolves height as auto if parent has no height specified.
              _computedValue = double.infinity;
            }
          }
          break;
        case PADDING_TOP:
        case PADDING_RIGHT:
        case PADDING_BOTTOM:
        case PADDING_LEFT:
        case MARGIN_LEFT:
        case MARGIN_RIGHT:
        case MARGIN_TOP:
        case MARGIN_BOTTOM:
          // https://www.w3.org/TR/css-box-3/#padding-physical
          // Percentage refer to logical width of containing block
          if (relativeParentWidth != null) {
            _computedValue = value! * relativeParentWidth;
          } else {
            // Mark parent to relayout to get renderer height of parent.
            if (renderBoxModel != null) {
              renderBoxModel.markParentNeedsRelayout();
            }
            _computedValue = 0;
          }
          break;
        case FLEX_BASIS:
          // Flex-basis computation is called in RenderFlexLayout which
          // will ensure parent exists.
          RenderStyle parentRenderStyle = renderStyle!.parent!;
          double? mainContentSize = parentRenderStyle.flexDirection == FlexDirection.row ?
            parentRenderStyle.contentBoxLogicalWidth :
            parentRenderStyle.contentBoxLogicalHeight;
          if (mainContentSize != null) {
            _computedValue = mainContentSize * value!;
          } else {
            // @TODO: Not supported when parent has no logical main size.
            _computedValue = 0;
          }
          // Refer to the flex container's inner main size.
          break;

        // https://www.w3.org/TR/css-position-3/#valdef-top-percentage
        // The inset is a percentage relative to the containing block’s size in the corresponding
        // axis (e.g. width for left or right, height for top and bottom). For sticky positioned boxes,
        // the inset is instead relative to the relevant scrollport’s size. Negative values are allowed.
        case TOP:
        case BOTTOM:
          // Offset of positioned element starts from the edge of padding box of containing block.
          if (parentPaddingBoxHeight != null) {
            _computedValue = value! * parentPaddingBoxHeight;
          } else {
            // Mark parent to relayout to get renderer height of parent.
            if (renderBoxModel != null) {
              renderBoxModel.markParentNeedsRelayout();
            }
            // Set as initial value, use infinity as auto value.
            _computedValue = double.infinity;
          }
          break;
        case LEFT:
        case RIGHT:
          // Offset of positioned element starts from the edge of padding box of containing block.
          if (parentPaddingBoxWidth != null) {
            _computedValue = value! * parentPaddingBoxWidth;
          } else {
            // Mark parent to relayout to get renderer height of parent.
            if (renderBoxModel != null) {
              renderBoxModel.markParentNeedsRelayout();
            }
            _computedValue = double.infinity;
          }
          break;

        case TRANSLATE:
        case BACKGROUND_SIZE:
        case BORDER_TOP_LEFT_RADIUS:
        case BORDER_TOP_RIGHT_RADIUS:
        case BORDER_BOTTOM_LEFT_RADIUS:
        case BORDER_BOTTOM_RIGHT_RADIUS:
          // Percentages for the horizontal axis refer to the width of the box.
          // Percentages for the vertical axis refer to the height of the box.
          double? borderBoxWidth = renderStyle!.borderBoxWidth ?? renderStyle!.borderBoxLogicalWidth;
          double? borderBoxHeight = renderStyle!.borderBoxHeight ?? renderStyle!.borderBoxLogicalHeight;
          double? borderBoxDimension = axisType == Axis.horizontal ? borderBoxWidth : borderBoxHeight;

          if (borderBoxDimension != null) {
            _computedValue = value! * borderBoxDimension;
          } else {
            _computedValue = propertyName == TRANSLATE
            // Transform will be cached once resolved, so avoid resolve if width not defined.
            // Use double.infinity to indicate percentage not resolved.
              ? double.infinity
              : 0;
          }
        break;
      }
      break;
    default:
      // @FIXME: Type AUTO not always resolves to 0, in cases such as `margin: auto`, `width: auto`.
      return 0;
  }
  return _computedValue!;
}