nscoord nsTableWrapperFrame::ChildShrinkWrapISize(nsRenderingContext* aRenderingContext, nsIFrame* aChildFrame, WritingMode aWM, LogicalSize aCBSize, nscoord aAvailableISize, nscoord* aMarginResult) const { AutoMaybeDisableFontInflation an(aChildFrame); // For the caption frame, child's WM may differ from the table's main WM. WritingMode childWM = aChildFrame->GetWritingMode(); SizeComputationInput offsets(aChildFrame, aRenderingContext, aWM, aCBSize.ISize(aWM)); LogicalSize marginSize = offsets.ComputedLogicalMargin().Size(childWM).ConvertTo(aWM, childWM); LogicalSize paddingSize = offsets.ComputedLogicalPadding().Size(childWM).ConvertTo(aWM, childWM); LogicalSize bpSize = offsets.ComputedLogicalBorderPadding().Size(childWM).ConvertTo(aWM, childWM); // Shrink-wrap aChildFrame by default, except if we're a stretched grid item. auto flags = ComputeSizeFlags::eShrinkWrap; auto parent = GetParent(); nsIAtom* parentFrameType = parent ? parent->GetType() : nullptr; bool isGridItem = (parentFrameType == nsGkAtoms::gridContainerFrame && !HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)); if (MOZ_UNLIKELY(isGridItem) && !StyleMargin()->HasInlineAxisAuto(aWM)) { auto inlineAxisAlignment = aWM.IsOrthogonalTo(parent->GetWritingMode()) ? StylePosition()->UsedAlignSelf(parent->StyleContext()) : StylePosition()->UsedJustifySelf(parent->StyleContext()); if (inlineAxisAlignment == NS_STYLE_ALIGN_NORMAL || inlineAxisAlignment == NS_STYLE_ALIGN_STRETCH) { flags = nsIFrame::ComputeSizeFlags::eDefault; } } LogicalSize size = aChildFrame->ComputeSize(aRenderingContext, aWM, aCBSize, aAvailableISize, marginSize, bpSize - paddingSize, paddingSize, flags); if (aMarginResult) { *aMarginResult = offsets.ComputedLogicalMargin().IStartEnd(aWM); } return size.ISize(aWM) + marginSize.ISize(aWM) + bpSize.ISize(aWM); }
void nsRubyFrame::ReflowSegment(nsPresContext* aPresContext, const ReflowInput& aReflowInput, nsRubyBaseContainerFrame* aBaseContainer, nsReflowStatus& aStatus) { WritingMode lineWM = aReflowInput.mLineLayout->GetWritingMode(); LogicalSize availSize(lineWM, aReflowInput.AvailableISize(), aReflowInput.AvailableBSize()); WritingMode rubyWM = GetWritingMode(); NS_ASSERTION(!rubyWM.IsOrthogonalTo(lineWM), "Ruby frame writing-mode shouldn't be orthogonal to its line"); AutoRubyTextContainerArray textContainers(aBaseContainer); const uint32_t rtcCount = textContainers.Length(); ReflowOutput baseMetrics(aReflowInput); bool pushedFrame; aReflowInput.mLineLayout->ReflowFrame(aBaseContainer, aStatus, &baseMetrics, pushedFrame); if (NS_INLINE_IS_BREAK_BEFORE(aStatus)) { if (aBaseContainer != mFrames.FirstChild()) { // Some segments may have been reflowed before, hence it is not // a break-before for the ruby container. aStatus = NS_INLINE_LINE_BREAK_AFTER(NS_FRAME_NOT_COMPLETE); PushChildren(aBaseContainer, aBaseContainer->GetPrevSibling()); aReflowInput.mLineLayout->SetDirtyNextLine(); } // This base container is not placed at all, we can skip all // text containers paired with it. return; } if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) { // It always promise that if the status is incomplete, there is a // break occurs. Break before has been processed above. However, // it is possible that break after happens with the frame reflow // completed. It happens if there is a force break at the end. MOZ_ASSERT(NS_INLINE_IS_BREAK_AFTER(aStatus)); // Find the previous sibling which we will // insert new continuations after. nsIFrame* lastChild; if (rtcCount > 0) { lastChild = textContainers.LastElement(); } else { lastChild = aBaseContainer; } // Create continuations for the base container nsIFrame* newBaseContainer = CreateNextInFlow(aBaseContainer); // newBaseContainer is null if there are existing next-in-flows. // We only need to move and push if there were not. if (newBaseContainer) { // Move the new frame after all the text containers mFrames.RemoveFrame(newBaseContainer); mFrames.InsertFrame(nullptr, lastChild, newBaseContainer); // Create continuations for text containers nsIFrame* newLastChild = newBaseContainer; for (uint32_t i = 0; i < rtcCount; i++) { nsIFrame* newTextContainer = CreateNextInFlow(textContainers[i]); MOZ_ASSERT(newTextContainer, "Next-in-flow of rtc should not exist " "if the corresponding rbc does not"); mFrames.RemoveFrame(newTextContainer); mFrames.InsertFrame(nullptr, newLastChild, newTextContainer); newLastChild = newTextContainer; } } if (lastChild != mFrames.LastChild()) { // Always push the next frame after the last child in this segment. // It is possible that we pulled it back before our next-in-flow // drain our overflow. PushChildren(lastChild->GetNextSibling(), lastChild); aReflowInput.mLineLayout->SetDirtyNextLine(); } } else { // If the ruby base container is reflowed completely, the line // layout will remove the next-in-flows of that frame. But the // line layout is not aware of the ruby text containers, hence // it is necessary to remove them here. for (uint32_t i = 0; i < rtcCount; i++) { nsIFrame* nextRTC = textContainers[i]->GetNextInFlow(); if (nextRTC) { nextRTC->GetParent()->DeleteNextInFlowChild(nextRTC, true); } } } nscoord segmentISize = baseMetrics.ISize(lineWM); const nsSize dummyContainerSize; LogicalRect baseRect = aBaseContainer->GetLogicalRect(lineWM, dummyContainerSize); // We need to position our rtc frames on one side or the other of the // base container's rect, using a coordinate space that's relative to // the ruby frame. Right now, the base container's rect's block-axis // position is relative to the block container frame containing the // lines, so we use 0 instead. (i.e. we assume that the base container // is adjacent to the ruby frame's block-start edge.) // XXX We may need to add border/padding here. See bug 1055667. baseRect.BStart(lineWM) = 0; // The rect for offsets of text containers. LogicalRect offsetRect = baseRect; for (uint32_t i = 0; i < rtcCount; i++) { nsRubyTextContainerFrame* textContainer = textContainers[i]; WritingMode rtcWM = textContainer->GetWritingMode(); nsReflowStatus textReflowStatus; ReflowOutput textMetrics(aReflowInput); ReflowInput textReflowInput(aPresContext, aReflowInput, textContainer, availSize.ConvertTo(rtcWM, lineWM)); // FIXME We probably shouldn't be using the same nsLineLayout for // the text containers. But it should be fine now as we are // not actually using this line layout to reflow something, // but just read the writing mode from it. textReflowInput.mLineLayout = aReflowInput.mLineLayout; textContainer->Reflow(aPresContext, textMetrics, textReflowInput, textReflowStatus); // Ruby text containers always return NS_FRAME_COMPLETE even when // they have continuations, because the breaking has already been // handled when reflowing the base containers. NS_ASSERTION(textReflowStatus == NS_FRAME_COMPLETE, "Ruby text container must not break itself inside"); // The metrics is initialized with reflow state of this ruby frame, // hence the writing-mode is tied to rubyWM instead of rtcWM. LogicalSize size = textMetrics.Size(rubyWM).ConvertTo(lineWM, rubyWM); textContainer->SetSize(lineWM, size); nscoord reservedISize = RubyUtils::GetReservedISize(textContainer); segmentISize = std::max(segmentISize, size.ISize(lineWM) + reservedISize); uint8_t rubyPosition = textContainer->StyleText()->mRubyPosition; MOZ_ASSERT(rubyPosition == NS_STYLE_RUBY_POSITION_OVER || rubyPosition == NS_STYLE_RUBY_POSITION_UNDER); Maybe<LogicalSide> side; if (rubyPosition == NS_STYLE_RUBY_POSITION_OVER) { side.emplace(lineWM.LogicalSideForLineRelativeDir(eLineRelativeDirOver)); } else if (rubyPosition == NS_STYLE_RUBY_POSITION_UNDER) { side.emplace(lineWM.LogicalSideForLineRelativeDir(eLineRelativeDirUnder)); } else { // XXX inter-character support in bug 1055672 MOZ_ASSERT_UNREACHABLE("Unsupported ruby-position"); } LogicalPoint position(lineWM); if (side.isSome()) { if (side.value() == eLogicalSideBStart) { offsetRect.BStart(lineWM) -= size.BSize(lineWM); offsetRect.BSize(lineWM) += size.BSize(lineWM); position = offsetRect.Origin(lineWM); } else if (side.value() == eLogicalSideBEnd) { position = offsetRect.Origin(lineWM) + LogicalPoint(lineWM, 0, offsetRect.BSize(lineWM)); offsetRect.BSize(lineWM) += size.BSize(lineWM); } else { MOZ_ASSERT_UNREACHABLE("???"); } } // Using a dummy container-size here, so child positioning may not be // correct. We will fix it in nsLineLayout after the whole line is // reflowed. FinishReflowChild(textContainer, aPresContext, textMetrics, &textReflowInput, lineWM, position, dummyContainerSize, 0); } MOZ_ASSERT(baseRect.ISize(lineWM) == offsetRect.ISize(lineWM), "Annotations should only be placed on the block directions"); nscoord deltaISize = segmentISize - baseMetrics.ISize(lineWM); if (deltaISize <= 0) { RubyUtils::ClearReservedISize(aBaseContainer); } else { RubyUtils::SetReservedISize(aBaseContainer, deltaISize); aReflowInput.mLineLayout->AdvanceICoord(deltaISize); } // Set block leadings of the base container nscoord startLeading = baseRect.BStart(lineWM) - offsetRect.BStart(lineWM); nscoord endLeading = offsetRect.BEnd(lineWM) - baseRect.BEnd(lineWM); // XXX When bug 765861 gets fixed, this warning should be upgraded. NS_WARNING_ASSERTION(startLeading >= 0 && endLeading >= 0, "Leadings should be non-negative (because adding " "ruby annotation can only increase the size)"); mBStartLeading = std::max(mBStartLeading, startLeading); mBEndLeading = std::max(mBEndLeading, endLeading); }
/** * This function returns the offset of an abs/fixed-pos child's static * position, with respect to the "start" corner of its alignment container, * according to CSS Box Alignment. This function only operates in a single * axis at a time -- callers can choose which axis via the |aAbsPosCBAxis| * parameter. * * @param aKidReflowInput The ReflowInput for the to-be-aligned abspos child. * @param aKidSizeInAbsPosCBWM The child frame's size (after it's been given * the opportunity to reflow), in terms of the * containing block's WritingMode. * @param aPlaceholderContainer The parent of the child frame's corresponding * placeholder frame, cast to a nsContainerFrame. * (This will help us choose which alignment enum * we should use for the child.) * @param aAbsPosCBWM The child frame's containing block's WritingMode. * @param aAbsPosCBAxis The axis (of the containing block) that we should * be doing this computation for. */ static nscoord OffsetToAlignedStaticPos(const ReflowInput& aKidReflowInput, const LogicalSize& aKidSizeInAbsPosCBWM, nsContainerFrame* aPlaceholderContainer, WritingMode aAbsPosCBWM, LogicalAxis aAbsPosCBAxis) { if (!aPlaceholderContainer) { // (The placeholder container should be the thing that kicks this whole // process off, by setting PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN. So it // should exist... but bail gracefully if it doesn't.) NS_ERROR("Missing placeholder-container when computing a " "CSS Box Alignment static position"); return 0; } // (Most of this function is simply preparing args that we'll pass to // AlignJustifySelf at the end.) // NOTE: Our alignment container is aPlaceholderContainer's content-box // (or an area within it, if aPlaceholderContainer is a grid). So, we'll // perform most of our arithmetic/alignment in aPlaceholderContainer's // WritingMode. For brevity, we use the abbreviation "pc" for "placeholder // container" in variables below. WritingMode pcWM = aPlaceholderContainer->GetWritingMode(); // Find what axis aAbsPosCBAxis corresponds to, in placeholder's parent's // writing-mode. LogicalAxis pcAxis = (pcWM.IsOrthogonalTo(aAbsPosCBWM) ? GetOrthogonalAxis(aAbsPosCBAxis) : aAbsPosCBAxis); nsIAtom* parentType = aPlaceholderContainer->GetType(); LogicalSize alignAreaSize(pcWM); if (parentType == nsGkAtoms::flexContainerFrame) { alignAreaSize = aPlaceholderContainer->GetLogicalSize(pcWM); LogicalMargin pcBorderPadding = aPlaceholderContainer->GetLogicalUsedBorderAndPadding(pcWM); alignAreaSize -= pcBorderPadding.Size(pcWM); } else { NS_ERROR("Unsupported container for abpsos CSS Box Alignment"); return 0; // (leave the child at the start of its alignment container) } nscoord alignAreaSizeInAxis = (pcAxis == eLogicalAxisInline) ? alignAreaSize.ISize(pcWM) : alignAreaSize.BSize(pcWM); AlignJustifyFlags flags = AlignJustifyFlags::eIgnoreAutoMargins; uint16_t alignConst = aPlaceholderContainer->CSSAlignmentForAbsPosChild(aKidReflowInput, pcAxis); // XXXdholbert: Handle <overflow-position> in bug 1311892 (by conditionally // setting AlignJustifyFlags::eOverflowSafe in |flags|.) For now, we behave // as if "unsafe" was the specified value (which is basically equivalent to // the default behavior, when no value is specified -- though the default // behavior also has some [at-risk] extra nuance about scroll containers...) // For now we ignore & strip off <overflow-position> bits, until bug 1311892. alignConst &= ~NS_STYLE_ALIGN_FLAG_BITS; // Find out if placeholder-container & the OOF child have the same start-sides // in the placeholder-container's pcAxis. WritingMode kidWM = aKidReflowInput.GetWritingMode(); if (pcWM.ParallelAxisStartsOnSameSide(pcAxis, kidWM)) { flags |= AlignJustifyFlags::eSameSide; } // (baselineAdjust is unused. CSSAlignmentForAbsPosChild() should've // converted 'baseline'/'last baseline' enums to their fallback values.) const nscoord baselineAdjust = nscoord(0); // AlignJustifySelf operates in the kid's writing mode, so we need to // represent the child's size and the desired axis in that writing mode: LogicalSize kidSizeInOwnWM = aKidSizeInAbsPosCBWM.ConvertTo(kidWM, aAbsPosCBWM); LogicalAxis kidAxis = (kidWM.IsOrthogonalTo(aAbsPosCBWM) ? GetOrthogonalAxis(aAbsPosCBAxis) : aAbsPosCBAxis); nscoord offset = CSSAlignUtils::AlignJustifySelf(alignConst, kidAxis, flags, baselineAdjust, alignAreaSizeInAxis, aKidReflowInput, kidSizeInOwnWM); // "offset" is in terms of the CSS Box Alignment container (i.e. it's in // terms of pcWM). But our return value needs to in terms of the containing // block's writing mode, which might have the opposite directionality in the // given axis. In that case, we just need to negate "offset" when returning, // to make it have the right effect as an offset for coordinates in the // containing block's writing mode. if (!pcWM.ParallelAxisStartsOnSameSide(pcAxis, aAbsPosCBWM)) { return -offset; } return offset; }
void nsVideoFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics, const ReflowInput& aReflowInput, nsReflowStatus& aStatus) { MarkInReflow(); DO_GLOBAL_REFLOW_COUNT("nsVideoFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("enter nsVideoFrame::Reflow: availSize=%d,%d", aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight())); NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); aStatus = NS_FRAME_COMPLETE; const WritingMode myWM = aReflowInput.GetWritingMode(); nscoord contentBoxBSize = aReflowInput.ComputedBSize(); const nscoord borderBoxISize = aReflowInput.ComputedISize() + aReflowInput.ComputedLogicalBorderPadding().IStartEnd(myWM); const bool isBSizeShrinkWrapping = (contentBoxBSize == NS_INTRINSICSIZE); nscoord borderBoxBSize; if (!isBSizeShrinkWrapping) { borderBoxBSize = contentBoxBSize + aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM); } nsMargin borderPadding = aReflowInput.ComputedPhysicalBorderPadding(); // Reflow the child frames. We may have up to three: an image // frame (for the poster image), a container frame for the controls, // and a container frame for the caption. for (nsIFrame* child : mFrames) { nsSize oldChildSize = child->GetSize(); if (child->GetContent() == mPosterImage) { // Reflow the poster frame. nsImageFrame* imageFrame = static_cast<nsImageFrame*>(child); ReflowOutput kidDesiredSize(aReflowInput); WritingMode wm = imageFrame->GetWritingMode(); LogicalSize availableSize = aReflowInput.AvailableSize(wm); LogicalSize cbSize = aMetrics.Size(aMetrics.GetWritingMode()). ConvertTo(wm, aMetrics.GetWritingMode()); ReflowInput kidReflowInput(aPresContext, aReflowInput, imageFrame, availableSize, &cbSize); nsRect posterRenderRect; if (ShouldDisplayPoster()) { posterRenderRect = nsRect(nsPoint(borderPadding.left, borderPadding.top), nsSize(aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight())); } kidReflowInput.SetComputedWidth(posterRenderRect.width); kidReflowInput.SetComputedHeight(posterRenderRect.height); ReflowChild(imageFrame, aPresContext, kidDesiredSize, kidReflowInput, posterRenderRect.x, posterRenderRect.y, 0, aStatus); FinishReflowChild(imageFrame, aPresContext, kidDesiredSize, &kidReflowInput, posterRenderRect.x, posterRenderRect.y, 0); // Android still uses XUL media controls & hence needs this XUL-friendly // custom reflow code. This will go away in bug 1310907. #ifdef ANDROID } else if (child->GetContent() == mVideoControls) { // Reflow the video controls frame. nsBoxLayoutState boxState(PresContext(), aReflowInput.mRenderingContext); nsBoxFrame::LayoutChildAt(boxState, child, nsRect(borderPadding.left, borderPadding.top, aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight())); #endif // ANDROID } else if (child->GetContent() == mCaptionDiv || child->GetContent() == mVideoControls) { // Reflow the caption and control bar frames. WritingMode wm = child->GetWritingMode(); LogicalSize availableSize = aReflowInput.ComputedSize(wm); availableSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; ReflowInput kidReflowInput(aPresContext, aReflowInput, child, availableSize); ReflowOutput kidDesiredSize(kidReflowInput); ReflowChild(child, aPresContext, kidDesiredSize, kidReflowInput, borderPadding.left, borderPadding.top, 0, aStatus); if (child->GetContent() == mVideoControls && isBSizeShrinkWrapping) { // Resolve our own BSize based on the controls' size in the same axis. contentBoxBSize = myWM.IsOrthogonalTo(wm) ? kidDesiredSize.ISize(wm) : kidDesiredSize.BSize(wm); } FinishReflowChild(child, aPresContext, kidDesiredSize, &kidReflowInput, borderPadding.left, borderPadding.top, 0); } if (child->GetContent() == mVideoControls && child->GetSize() != oldChildSize) { RefPtr<Runnable> event = new DispatchResizeToControls(child->GetContent()); nsContentUtils::AddScriptRunner(event); } } if (isBSizeShrinkWrapping) { if (contentBoxBSize == NS_INTRINSICSIZE) { // We didn't get a BSize from our intrinsic size/ratio, nor did we // get one from our controls. Just use BSize of 0. contentBoxBSize = 0; } contentBoxBSize = NS_CSS_MINMAX(contentBoxBSize, aReflowInput.ComputedMinBSize(), aReflowInput.ComputedMaxBSize()); borderBoxBSize = contentBoxBSize + aReflowInput.ComputedLogicalBorderPadding().BStartEnd(myWM); } LogicalSize logicalDesiredSize(myWM, borderBoxISize, borderBoxBSize); aMetrics.SetSize(myWM, logicalDesiredSize); aMetrics.SetOverflowAreasToDesiredBounds(); FinishAndStoreOverflow(&aMetrics); NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("exit nsVideoFrame::Reflow: size=%d,%d", aMetrics.Width(), aMetrics.Height())); NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics); }