applyPositionedChildOffset static method

void applyPositionedChildOffset(
  1. RenderBoxModel parent,
  2. RenderBoxModel child
)

Implementation

static void applyPositionedChildOffset(
  RenderBoxModel parent,
  RenderBoxModel child,
) {
  RenderLayoutParentData childParentData = child.parentData as RenderLayoutParentData;
  Size size = child.boxSize!;
  Size parentSize = parent.boxSize!;

  RenderStyle parentRenderStyle = parent.renderStyle;

  CSSLengthValue parentBorderLeftWidth = parentRenderStyle.effectiveBorderLeftWidth;
  CSSLengthValue parentBorderRightWidth = parentRenderStyle.effectiveBorderRightWidth;
  CSSLengthValue parentBorderTopWidth = parentRenderStyle.effectiveBorderTopWidth;
  CSSLengthValue parentBorderBottomWidth = parentRenderStyle.effectiveBorderBottomWidth;
  CSSLengthValue parentPaddingLeft = parentRenderStyle.paddingLeft;
  CSSLengthValue parentPaddingTop = parentRenderStyle.paddingTop;

  // The containing block of not an inline box is formed by the padding edge of the ancestor.
  // Thus the final offset of child need to add the border of parent.
  // https://www.w3.org/TR/css-position-3/#def-cb
  Size containingBlockSize = Size(
      parentSize.width - parentBorderLeftWidth.computedValue - parentBorderRightWidth.computedValue,
      parentSize.height - parentBorderTopWidth.computedValue - parentBorderBottomWidth.computedValue);

  CSSRenderStyle childRenderStyle = child.renderStyle;
  CSSLengthValue left = childRenderStyle.left;
  CSSLengthValue right = childRenderStyle.right;
  CSSLengthValue top = childRenderStyle.top;
  CSSLengthValue bottom = childRenderStyle.bottom;
  CSSLengthValue marginLeft = childRenderStyle.marginLeft;
  CSSLengthValue marginRight = childRenderStyle.marginRight;
  CSSLengthValue marginTop = childRenderStyle.marginTop;
  CSSLengthValue marginBottom = childRenderStyle.marginBottom;

  // Fix side effects by render portal.
  if (child is RenderEventListener && child.child is RenderBoxModel) {
    child = child.child as RenderBoxModel;
    childParentData = child.parentData as RenderLayoutParentData;
  }

  // The static position of positioned element is its offset when its position property had been static
  // which equals to the position of its placeholder renderBox.
  // https://www.w3.org/TR/CSS2/visudet.html#static-position
  RenderPositionPlaceholder? ph = child.renderStyle.getSelfPositionPlaceHolder();
  Offset staticPositionOffset = _getPlaceholderToParentOffset(ph, parent,
      excludeScrollOffset: child.renderStyle.position != CSSPositionType.fixed);

  try {
    final pTag = parent.renderStyle.target.tagName.toLowerCase();
    final cTag = child.renderStyle.target.tagName.toLowerCase();
    final phOff = (ph != null && ph.parentData is RenderLayoutParentData)
        ? (ph.parentData as RenderLayoutParentData).offset
        : null;
    PositionedLayoutLog.log(
      impl: PositionedImpl.layout,
      feature: PositionedFeature.staticPosition,
      message: () => '<$cTag> static from placeholder: raw=${phOff == null ? 'null' : '${phOff.dx.toStringAsFixed(2)},${phOff.dy.toStringAsFixed(2)}'} '
          'toParent=${staticPositionOffset.dx.toStringAsFixed(2)},${staticPositionOffset.dy.toStringAsFixed(2)} parent=<$pTag>',
    );
  } catch (_) {}

  // Diagnostics: static-position context snapshot
  try {
    final cTag = child.renderStyle.target.tagName.toLowerCase();
    final dispSpec = child.renderStyle.display.toString().split('.').last;
    final dispEff = child.renderStyle.effectiveDisplay.toString().split('.').last;
    final posType = child.renderStyle.position.toString().split('.').last;
    final phParent = ph == null ? 'null' : ph.parent.runtimeType.toString();
    final bool phInIFC = ph != null && ph.parent is RenderFlowLayout && (ph.parent as RenderFlowLayout).establishIFC;
    PositionedLayoutLog.log(
      impl: PositionedImpl.layout,
      feature: PositionedFeature.staticPosition,
      message: () => 'context <${cTag}> pos=${posType} disp(spec=${dispSpec}, eff=${dispEff}) '
          'parentIsDocRoot=${parent.isDocumentRootBox} phParent=${phParent} phInIFC=${phInIFC}',
    );
  } catch (_) {}

  // Ensure static position accuracy for W3C compliance
  // W3C requires static position to represent where element would be in normal flow
  Offset adjustedStaticPosition = _ensureAccurateStaticPosition(
    staticPositionOffset,
    child,
    parent,
    left,
    right,
    top,
    bottom,
    parentBorderLeftWidth,
    parentBorderRightWidth,
    parentBorderTopWidth,
    parentBorderBottomWidth,
    parentPaddingLeft,
    parentPaddingTop
  );

  // Inline static-position correction (horizontal): when the placeholder sits inside
  // an IFC container (e.g., text followed by abspos inline), align the static X to the
  // inline advance within that container's content box so that `left:auto` follows the
  // preceding inline content per CSS static-position rules.
  // Only apply when the containing block is not the document root. Root cases are handled
  // specially below to preserve expected behavior.
  if (!parent.isDocumentRootBox && ph != null) {
    // Find the nearest ancestor flow container that establishes an IFC.
    RenderObject? a = ph.parent;
    RenderFlowLayout? flowParent;
    while (a != null) {
      if (a is RenderFlowLayout && a.establishIFC) {
        flowParent = a;
        break;
      }
      a = (a.parent is RenderObject) ? a.parent as RenderObject? : null;
    }
    if (flowParent != null) {
      // Only inline-level hypothetical boxes should use inline advance for static X.
      // Use specified display (not effective) to avoid misclassifying inline elements
      // that are out-of-flow as block.
      final CSSDisplay childDisp = child.renderStyle.display;
      final bool childIsBlockLike = (childDisp == CSSDisplay.block || childDisp == CSSDisplay.flex);
      // Base content-left inset inside the IFC container
      final double contentLeftInset =
          flowParent.renderStyle.effectiveBorderLeftWidth.computedValue +
          flowParent.renderStyle.paddingLeft.computedValue;
      if (!childIsBlockLike) {
        // Use IFC-provided inline advance; when unavailable (e.g., empty inline), keep 0.
        double inlineAdvance = flowParent.inlineAdvanceBefore(ph);
        try {
          PositionedLayoutLog.log(
            impl: PositionedImpl.layout,
            feature: PositionedFeature.staticPosition,
            message: () => 'IFC inline advance (non-root inline)=${inlineAdvance.toStringAsFixed(2)}',
          );
        } catch (_) {}
        if (inlineAdvance == 0.0) {
          // Fallback: if placeholder is appended after inline content within this IFC container,
          // use the paragraph visual max line width as the preceding inline advance.
          final bool hasPrecedingInline = _hasInlineContentBeforePlaceholder(flowParent, ph);
          if (hasPrecedingInline && flowParent.inlineFormattingContext != null) {
            inlineAdvance = flowParent.inlineFormattingContext!.paragraphVisualMaxLineWidth;
            try {
              PositionedLayoutLog.log(
                impl: PositionedImpl.layout,
                feature: PositionedFeature.staticPosition,
                message: () => 'fallback inline advance by paragraph width =${inlineAdvance.toStringAsFixed(2)}',
              );
            } catch (_) {}
          }
        }
        // Do not use inline advance when the abspos has percentage width (e.g., width:100%),
        // since the horizontal insets equation will use the static position as 'left' and a
        // percentage width that fills the containing block; browsers effectively align such
        // overlays at the content-left (no inline advance).
        final bool widthIsPercentage = child.renderStyle.width.type == CSSLengthType.PERCENTAGE;
        final double effAdvance = widthIsPercentage ? 0.0 : inlineAdvance;
        // Compute flow content-left in CB space and add inline advance.
        final Offset _phToFlow = _getPlaceholderToParentOffset(ph, flowParent, excludeScrollOffset: true);
        final double targetX = staticPositionOffset.dx - _phToFlow.dx + contentLeftInset + effAdvance;
        adjustedStaticPosition = Offset(targetX, adjustedStaticPosition.dy);
        try {
          PositionedLayoutLog.log(
            impl: PositionedImpl.layout,
            feature: PositionedFeature.staticPosition,
            message: () => 'adjust static pos by IFC inline advance '
                'contentLeft=${contentLeftInset.toStringAsFixed(2)} '
                'advance=${effAdvance.toStringAsFixed(2)} '
                '→ (${adjustedStaticPosition.dx.toStringAsFixed(2)},${adjustedStaticPosition.dy.toStringAsFixed(2)})',
          );
        } catch (_) {}
        // Vertical: when both top and bottom are auto, align to the IFC container's
        // content-top in CB coordinates so the abspos sits at the top of the line box.
        if (top.isAuto && bottom.isAuto) {
          final double contentTopInset = flowParent.renderStyle.paddingTop.computedValue +
              flowParent.renderStyle.effectiveBorderTopWidth.computedValue;
          final double targetY = staticPositionOffset.dy - _phToFlow.dy + contentTopInset;
          adjustedStaticPosition = Offset(adjustedStaticPosition.dx, targetY);
          try {
            PositionedLayoutLog.log(
              impl: PositionedImpl.layout,
              feature: PositionedFeature.staticPosition,
              message: () => 'adjust static Y by IFC content-top '
                  'flowContentTopInCB=${targetY.toStringAsFixed(2)}',
            );
          } catch (_) {}
        }
      } else {
        // Block-level hypothetical box: anchor to flow content-left in CB space.
        if (contentLeftInset != 0.0) {
          final Offset _phToFlow = _getPlaceholderToParentOffset(ph, flowParent, excludeScrollOffset: true);
          final double targetX = staticPositionOffset.dx - _phToFlow.dx + contentLeftInset;
          adjustedStaticPosition = Offset(targetX, adjustedStaticPosition.dy);
          try {
            PositionedLayoutLog.log(
              impl: PositionedImpl.layout,
              feature: PositionedFeature.staticPosition,
              message: () => 'adjust static pos by IFC content-left for block '
                  'contentLeft=${contentLeftInset.toStringAsFixed(2)} '
                  '→ (${adjustedStaticPosition.dx.toStringAsFixed(2)},${adjustedStaticPosition.dy.toStringAsFixed(2)})',
            );
          } catch (_) {}
        }
        // Block-level vertical: place below the inline line box height when top/bottom are auto
        // AND there is preceding inline content before the placeholder.
        if (top.isAuto && bottom.isAuto) {
          // Determine preceding inline by structural scan when width sums are unavailable.
          final bool hasPrecedingInline = _hasInlineContentBeforePlaceholder(flowParent, ph);
          try {
            PositionedLayoutLog.log(
              impl: PositionedImpl.layout,
              feature: PositionedFeature.staticPosition,
              message: () => 'non-root IFC block: hasPrecedingInline=${hasPrecedingInline}',
            );
          } catch (_) {}
          if (hasPrecedingInline) {
            final InlineFormattingContext? ifc = flowParent.inlineFormattingContext;
            if (ifc != null) {
              double paraH;
              final lines = ifc.paragraphLineMetrics;
              if (lines.isNotEmpty) {
                paraH = lines.fold<double>(0.0, (sum, lm) => sum + lm.height);
              } else {
                paraH = ifc.paragraph?.height ?? 0.0;
              }
              if (paraH != 0.0 && adjustedStaticPosition.dy.abs() < 0.5) {
                adjustedStaticPosition = adjustedStaticPosition.translate(0, paraH);
                try {
                PositionedLayoutLog.log(
                  impl: PositionedImpl.layout,
                  feature: PositionedFeature.staticPosition,
                  message: () => 'adjust static pos by IFC paragraph height for block '
                      'lines=${lines.length} h=${paraH.toStringAsFixed(2)} '
                      '→ (${adjustedStaticPosition.dx.toStringAsFixed(2)},${adjustedStaticPosition.dy.toStringAsFixed(2)})',
                );
                } catch (_) {}
              }
            }
          }
        }
      }

      // Vertical static position for top/bottom auto in IFC:
      // Anchor to the IFC container's content top so the abspos aligns with
      // the line box where the placeholder sits (top of the first line).
      if (top.isAuto && bottom.isAuto) {
        final double padTop = flowParent.renderStyle.paddingTop.computedValue;
        final double borderTop = flowParent.renderStyle.effectiveBorderTopWidth.computedValue;
        final double contentTopInset = padTop + borderTop;
        if (contentTopInset != 0.0 && adjustedStaticPosition.dy.abs() < 0.5) {
          adjustedStaticPosition = adjustedStaticPosition.translate(0, contentTopInset);
          try {
            PositionedLayoutLog.log(
              impl: PositionedImpl.layout,
              feature: PositionedFeature.staticPosition,
              message: () => 'adjust static pos by IFC contentTop '
                  'inset=${contentTopInset.toStringAsFixed(2)} '
                  '→ (${adjustedStaticPosition.dx.toStringAsFixed(2)},${adjustedStaticPosition.dy.toStringAsFixed(2)})',
            );
          } catch (_) {}
        }
      }
    }
  }

  // If the containing block is the document root (<html>) and the placeholder lives
  // under the block formatting context of <body>, align the static position vertically
  // with the first in-flow block-level child’s collapsed top (ignoring parent collapse).
  // This matches browser behavior where the first in-flow child’s top margin effectively
  // offsets the visible content from the root. The positioned element’s static position
  // should reflect that visual start so the out-of-flow element and the following in-flow
  // element align vertically when no insets are specified.
  if (parent.isDocumentRootBox && ph != null) {
    final RenderObject? phParent = ph.parent;
    if (phParent is RenderBoxModel) {
      final RenderBoxModel phContainer = phParent;
      final RenderStyle cStyle = phContainer.renderStyle;
      final bool qualifiesBFC =
          cStyle.isLayoutBox() &&
          cStyle.effectiveDisplay == CSSDisplay.block &&
          (cStyle.effectiveOverflowY == CSSOverflowType.visible || cStyle.effectiveOverflowY == CSSOverflowType.clip) &&
          cStyle.paddingTop.computedValue == 0 &&
          cStyle.effectiveBorderTopWidth.computedValue == 0;

      // Only adjust when placeholder is the first attached child (no previous in-flow block)
      final bool isFirstChild = (ph.parentData is RenderLayoutParentData) &&
          ((ph.parentData as RenderLayoutParentData).previousSibling == null);

      if (qualifiesBFC && isFirstChild) {
        final RenderBoxModel? firstFlow = _resolveNextInFlowSiblingModel(ph);
        if (firstFlow != null) {
          final double childTopIgnoringParent = firstFlow.renderStyle.collapsedMarginTopIgnoringParent;
          if (childTopIgnoringParent != 0) {
            adjustedStaticPosition = adjustedStaticPosition.translate(0, childTopIgnoringParent);
            try {
              PositionedLayoutLog.log(
                impl: PositionedImpl.layout,
                feature: PositionedFeature.staticPosition,
                message: () => 'adjust static pos by first in-flow child top(${childTopIgnoringParent.toStringAsFixed(2)}) '
                    '→ (${adjustedStaticPosition.dx.toStringAsFixed(2)},${adjustedStaticPosition.dy.toStringAsFixed(2)})',
              );
            } catch (_) {}
          }
        }
      }

      // Horizontal static-position in IFC under document root: when placeholder lives in an
      // inline formatting context (e.g., <div><span>text</span><abspos/></div>) but the containing
      // block is <html>, the static X should reflect the inline advance within the IFC container.
      // Compute inline advance from the IFC paragraph; if the placeholder is the last child,
      // fall back to the paragraph’s visual max line width.
      // Use the nearest IFC container up the chain for horizontal inline advance.
      RenderFlowLayout? flowParent;
      if (phParent is RenderFlowLayout && phParent.establishIFC) {
        flowParent = phParent as RenderFlowLayout;
      } else {
        RenderObject? a = phParent.parent;
        while (a != null) {
          if (a is RenderFlowLayout && a.establishIFC) {
            flowParent = a;
            break;
          }
          a = (a.parent is RenderObject) ? a.parent as RenderObject? : null;
        }
      }
      if (flowParent != null) {
        // Base inset: content-left inside the IFC container
        final double contentLeftInset =
            flowParent.renderStyle.effectiveBorderLeftWidth.computedValue +
            flowParent.renderStyle.paddingLeft.computedValue;
        // Under document root, honor block-level vs inline-level behavior:
        // - Block/flex: anchor to content-left only (x = content-left), no vertical shift.
        // - Inline-level: add horizontal inline advance before placeholder.
        // Use the specified display for block-vs-inline determination; effectiveDisplay
        // is normalized for out-of-flow and may not reflect original block-vs-inline.
        final CSSDisplay childDispSpecified = child.renderStyle.display;
        final bool childIsBlockLike = (childDispSpecified == CSSDisplay.block || childDispSpecified == CSSDisplay.flex);
        try {
          PositionedLayoutLog.log(
            impl: PositionedImpl.layout,
            feature: PositionedFeature.staticPosition,
            message: () => 'doc-root IFC path: dispSpecified=${childDispSpecified.toString().split('.').last} '
                'blockLike=${childIsBlockLike} contentLeft=${contentLeftInset.toStringAsFixed(2)}',
          );
        } catch (_) {}
        if (childIsBlockLike) {
          if (contentLeftInset != 0.0) {
            final Offset _phToFlow = _getPlaceholderToParentOffset(ph, flowParent, excludeScrollOffset: true);
            final double targetX = staticPositionOffset.dx - _phToFlow.dx + contentLeftInset;
            adjustedStaticPosition = Offset(targetX, adjustedStaticPosition.dy);
            try {
              PositionedLayoutLog.log(
                impl: PositionedImpl.layout,
                feature: PositionedFeature.staticPosition,
                message: () => 'adjust static pos under root by IFC content-left for block '
                    'contentLeft=${contentLeftInset.toStringAsFixed(2)} '
                    '→ (${adjustedStaticPosition.dx.toStringAsFixed(2)},${adjustedStaticPosition.dy.toStringAsFixed(2)})',
              );
            } catch (_) {}
          }
          // Vertical: if preceded by inline, move to the next line (add paragraph height).
          if (top.isAuto && bottom.isAuto) {
            final bool hasPrecedingInline = _hasInlineContentBeforePlaceholder(flowParent, ph);
            try {
              PositionedLayoutLog.log(
                impl: PositionedImpl.layout,
                feature: PositionedFeature.staticPosition,
                message: () => 'doc-root IFC block: hasPrecedingInline=${hasPrecedingInline}',
              );
            } catch (_) {}
            if (hasPrecedingInline) {
              final InlineFormattingContext? ifc = flowParent.inlineFormattingContext;
              if (ifc != null) {
                double paraH;
                final lines = ifc.paragraphLineMetrics;
                if (lines.isNotEmpty) {
                  paraH = lines.fold<double>(0.0, (sum, lm) => sum + lm.height);
                } else {
                  paraH = ifc.paragraph?.height ?? 0.0;
                }
                if (paraH != 0.0) {
                  adjustedStaticPosition = adjustedStaticPosition.translate(0, paraH);
                  try {
                    PositionedLayoutLog.log(
                      impl: PositionedImpl.layout,
                      feature: PositionedFeature.staticPosition,
                      message: () => 'adjust static pos under root by IFC paragraph height for block '
                          'lines=${lines.length} '
                          'h=${paraH.toStringAsFixed(2)} '
                          '→ (${adjustedStaticPosition.dx.toStringAsFixed(2)},${adjustedStaticPosition.dy.toStringAsFixed(2)})',
                    );
                  } catch (_) {}
                }
              }
            }
          }
        } else {
          double inlineAdvance = flowParent.inlineAdvanceBefore(ph);
          try {
            PositionedLayoutLog.log(
              impl: PositionedImpl.layout,
              feature: PositionedFeature.staticPosition,
              message: () => 'inline-level under root: inlineAdvance=${inlineAdvance.toStringAsFixed(2)}',
            );
          } catch (_) {}
          if (inlineAdvance == 0.0) {
            final bool hasPrecedingInline = _hasInlineContentBeforePlaceholder(flowParent, ph);
            if (hasPrecedingInline && flowParent.inlineFormattingContext != null) {
              inlineAdvance = flowParent.inlineFormattingContext!.paragraphVisualMaxLineWidth;
              try {
                PositionedLayoutLog.log(
                  impl: PositionedImpl.layout,
                  feature: PositionedFeature.staticPosition,
                  message: () => 'fallback inline advance under root by paragraph width =${inlineAdvance.toStringAsFixed(2)}',
                );
              } catch (_) {}
            }
          }
          final bool widthIsPercentage = child.renderStyle.width.type == CSSLengthType.PERCENTAGE;
          final double effAdvance = widthIsPercentage ? 0.0 : inlineAdvance;
          final Offset _phToFlow = _getPlaceholderToParentOffset(ph, flowParent, excludeScrollOffset: true);
          final double targetX = staticPositionOffset.dx - _phToFlow.dx + contentLeftInset + effAdvance;
          adjustedStaticPosition = Offset(targetX, adjustedStaticPosition.dy);
          try {
            PositionedLayoutLog.log(
              impl: PositionedImpl.layout,
              feature: PositionedFeature.staticPosition,
              message: () => 'adjust static pos under root by IFC inline advance '
                  'contentLeft=${contentLeftInset.toStringAsFixed(2)} '
                  'advance=${effAdvance.toStringAsFixed(2)} '
                  '→ (${adjustedStaticPosition.dx.toStringAsFixed(2)},${adjustedStaticPosition.dy.toStringAsFixed(2)})',
            );
          } catch (_) {}
        }
      }
    }
  }

  try {
    PositionedLayoutLog.log(
      impl: PositionedImpl.layout,
      feature: PositionedFeature.staticPosition,
      message: () => 'adjusted static pos = (${adjustedStaticPosition.dx.toStringAsFixed(2)},${adjustedStaticPosition.dy.toStringAsFixed(2)})',
    );
  } catch (_) {}

  // Child renderObject is reparented under its containing block at build time,
  // and staticPositionOffset is already measured relative to the containing block.
  // No additional ancestor offset adjustment is needed.
  Offset ancestorOffset = Offset.zero;

  // ScrollTop and scrollLeft will be added to offset of renderBox in the paint stage
  // for positioned fixed element.
  if (childRenderStyle.position == CSSPositionType.fixed) {
    Offset scrollOffset = child.getTotalScrollOffset();
    child.additionalPaintOffsetX = scrollOffset.dx;
    child.additionalPaintOffsetY = scrollOffset.dy;
    try {
      PositionedLayoutLog.log(
        impl: PositionedImpl.layout,
        feature: PositionedFeature.fixed,
        message: () => '<${child.renderStyle.target.tagName.toLowerCase()}>'
            ' fixed paintOffset=(${scrollOffset.dx.toStringAsFixed(2)},${scrollOffset.dy.toStringAsFixed(2)})',
      );
    } catch (_) {}
  }

  // When the parent is a scroll container (overflow on either axis not visible),
  // convert positioned offsets to the scrolling content box coordinate space.
  // Overflow paint translates children relative to the content edge, so offsets
  // computed from the padding edge must exclude border and padding for alignment.
  final bool parentIsScrollContainer =
      parent.renderStyle.effectiveOverflowX != CSSOverflowType.visible ||
      parent.renderStyle.effectiveOverflowY != CSSOverflowType.visible;

  // Determine direction for resolving 'auto' horizontal insets: use the
  // direction of the element establishing the static-position containing block
  // (typically the IFC container hosting the placeholder) when available;
  // otherwise fall back to the containing block's direction.
  TextDirection _staticContainingDir = parent.renderStyle.direction;
  if (ph != null && ph.parent is RenderFlowLayout) {
    final RenderFlowLayout flowParent = ph.parent as RenderFlowLayout;
    _staticContainingDir = flowParent.renderStyle.direction;
  }

  // For sticky positioning, the insets act as constraints during scroll, not as
  // absolute offsets at layout time. Compute the base (un-stuck) offset from the
  // static position by treating both axis insets as auto for layout.
  final bool isSticky = childRenderStyle.position == CSSPositionType.sticky;

  double x = _computePositionedOffset(
    Axis.horizontal,
    _staticContainingDir,
    false,
    parentBorderLeftWidth,
    parentPaddingLeft,
    containingBlockSize.width,
    size.width,
    adjustedStaticPosition.dx,
    isSticky ? CSSLengthValue.auto : left,
    isSticky ? CSSLengthValue.auto : right,
    marginLeft,
    marginRight,
  );

  double y = _computePositionedOffset(
    Axis.vertical,
    parent.renderStyle.direction,
    false,
    parentBorderTopWidth,
    parentPaddingTop,
    containingBlockSize.height,
    size.height,
    adjustedStaticPosition.dy,
    isSticky ? CSSLengthValue.auto : top,
    isSticky ? CSSLengthValue.auto : bottom,
    marginTop,
    marginBottom,
  );
  try {
    final dir = parent.renderStyle.direction;
    PositionedLayoutLog.log(
      impl: PositionedImpl.layout,
      feature: PositionedFeature.offsets,
      message: () => 'compute offset for <${child.renderStyle.target.tagName.toLowerCase()}>'
          ' dir=$dir parentScroll=$parentIsScrollContainer left=${left.cssText()} right=${right.cssText()} '
          'top=${top.cssText()} bottom=${bottom.cssText()} → (${x.toStringAsFixed(2)},${y.toStringAsFixed(2)})',
    );
  } catch (_) {}

  final Offset finalOffset = Offset(x, y) - ancestorOffset;
  // If this positioned element is wrapped (e.g., by RenderEventListener), ensure
  // the wrapper is placed at the positioned offset so its background/border align
  // with the child content. The child uses internal offsets relative to the wrapper.
  bool placedWrapper = false;
  final RenderObject? directParent = child.parent;
  if (directParent is RenderEventListener) {
    final RenderLayoutParentData pd = directParent.parentData as RenderLayoutParentData;
    pd.offset = finalOffset;
    placedWrapper = true;
  }
  if (!placedWrapper) {
    childParentData.offset = finalOffset;
  }

  // Diagnostics: compute instantaneous on-screen right edge and parent bounds to detect overflow.
  try {
    // Parent padding-right edge in parent's border-box coordinate space.
    final double parentPadRightX = parent.boxSize!.width - parentBorderRightWidth.computedValue;
    // Child transform translation applied at paint time.
    final Matrix4 eff = child.renderStyle.effectiveTransformMatrix;
    final Vector3 tr = eff.getTranslation();
    final double tx = tr.x;
    final double rightEdgeX = finalOffset.dx + tx + size.width;
    PositionedLayoutLog.log(
      impl: PositionedImpl.layout,
      feature: PositionedFeature.offsets,
      message: () => 'bounds check: childRight=${rightEdgeX.toStringAsFixed(2)} '
          'parentPadRight=${parentPadRightX.toStringAsFixed(2)} '
          'overflowX=${(rightEdgeX - parentPadRightX).toStringAsFixed(2)}',
    );
  } catch (_) {}

  try {
    PositionedLayoutLog.log(
      impl: PositionedImpl.layout,
      feature: PositionedFeature.offsets,
      message: () => 'apply offset final=(${finalOffset.dx.toStringAsFixed(2)},${finalOffset.dy.toStringAsFixed(2)}) '
          'from x=${x.toStringAsFixed(2)} y=${y.toStringAsFixed(2)} ancestor=(${ancestorOffset.dx.toStringAsFixed(2)},${ancestorOffset.dy.toStringAsFixed(2)})',
    );
  } catch (_) {}
}