nsRubyTextContainerFrame.cpp (7084B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* rendering object for CSS "display: ruby-text-container" */ 8 9 #include "nsRubyTextContainerFrame.h" 10 11 #include "mozilla/ComputedStyle.h" 12 #include "mozilla/PresShell.h" 13 #include "mozilla/WritingModes.h" 14 #include "nsLayoutUtils.h" 15 #include "nsLineLayout.h" 16 #include "nsPresContext.h" 17 18 using namespace mozilla; 19 20 //---------------------------------------------------------------------- 21 22 // Frame class boilerplate 23 // ======================= 24 25 NS_QUERYFRAME_HEAD(nsRubyTextContainerFrame) 26 NS_QUERYFRAME_ENTRY(nsRubyTextContainerFrame) 27 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) 28 29 NS_IMPL_FRAMEARENA_HELPERS(nsRubyTextContainerFrame) 30 31 nsContainerFrame* NS_NewRubyTextContainerFrame(PresShell* aPresShell, 32 ComputedStyle* aStyle) { 33 return new (aPresShell) 34 nsRubyTextContainerFrame(aStyle, aPresShell->GetPresContext()); 35 } 36 37 //---------------------------------------------------------------------- 38 39 // nsRubyTextContainerFrame Method Implementations 40 // =============================================== 41 42 #ifdef DEBUG_FRAME_DUMP 43 nsresult nsRubyTextContainerFrame::GetFrameName(nsAString& aResult) const { 44 return MakeFrameName(u"RubyTextContainer"_ns, aResult); 45 } 46 #endif 47 48 /* virtual */ 49 void nsRubyTextContainerFrame::SetInitialChildList(ChildListID aListID, 50 nsFrameList&& aChildList) { 51 nsContainerFrame::SetInitialChildList(aListID, std::move(aChildList)); 52 if (aListID == FrameChildListID::Principal) { 53 UpdateSpanFlag(); 54 } 55 } 56 57 /* virtual */ 58 void nsRubyTextContainerFrame::AppendFrames(ChildListID aListID, 59 nsFrameList&& aFrameList) { 60 nsContainerFrame::AppendFrames(aListID, std::move(aFrameList)); 61 UpdateSpanFlag(); 62 } 63 64 /* virtual */ 65 void nsRubyTextContainerFrame::InsertFrames( 66 ChildListID aListID, nsIFrame* aPrevFrame, 67 const nsLineList::iterator* aPrevFrameLine, nsFrameList&& aFrameList) { 68 nsContainerFrame::InsertFrames(aListID, aPrevFrame, aPrevFrameLine, 69 std::move(aFrameList)); 70 UpdateSpanFlag(); 71 } 72 73 /* virtual */ 74 void nsRubyTextContainerFrame::RemoveFrame(DestroyContext& aContext, 75 ChildListID aListID, 76 nsIFrame* aOldFrame) { 77 nsContainerFrame::RemoveFrame(aContext, aListID, aOldFrame); 78 UpdateSpanFlag(); 79 } 80 81 void nsRubyTextContainerFrame::UpdateSpanFlag() { 82 bool isSpan = false; 83 // The continuation checks are safe here because spans never break. 84 if (!GetPrevContinuation() && !GetNextContinuation()) { 85 nsIFrame* onlyChild = mFrames.OnlyChild(); 86 if (onlyChild && onlyChild->IsPseudoFrame(GetContent())) { 87 // Per CSS Ruby spec, if the only child of an rtc frame is 88 // a pseudo rt frame, it spans all bases in the segment. 89 isSpan = true; 90 } 91 } 92 93 if (isSpan) { 94 AddStateBits(NS_RUBY_TEXT_CONTAINER_IS_SPAN); 95 } else { 96 RemoveStateBits(NS_RUBY_TEXT_CONTAINER_IS_SPAN); 97 } 98 } 99 100 /* virtual */ 101 void nsRubyTextContainerFrame::Reflow(nsPresContext* aPresContext, 102 ReflowOutput& aDesiredSize, 103 const ReflowInput& aReflowInput, 104 nsReflowStatus& aStatus) { 105 MarkInReflow(); 106 DO_GLOBAL_REFLOW_COUNT("nsRubyTextContainerFrame"); 107 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!"); 108 109 // Although a ruby text container may have continuations, returning 110 // complete reflow status is still safe, since its parent, ruby frame, 111 // ignores the status, and continuations of the ruby base container 112 // will take care of our continuations. 113 WritingMode rtcWM = GetWritingMode(); 114 115 nscoord minBCoord = nscoord_MAX; 116 nscoord maxBCoord = nscoord_MIN; 117 // The container size is not yet known, so we use a dummy (0, 0) size. 118 // The block-dir position will be corrected below after containerSize 119 // is finalized. 120 const nsSize dummyContainerSize; 121 for (nsIFrame* child : mFrames) { 122 MOZ_ASSERT(child->IsRubyTextFrame()); 123 LogicalRect rect = child->GetLogicalRect(rtcWM, dummyContainerSize); 124 LogicalMargin margin = child->GetLogicalUsedMargin(rtcWM); 125 nscoord blockStart = rect.BStart(rtcWM) - margin.BStart(rtcWM); 126 minBCoord = std::min(minBCoord, blockStart); 127 nscoord blockEnd = rect.BEnd(rtcWM) + margin.BEnd(rtcWM); 128 maxBCoord = std::max(maxBCoord, blockEnd); 129 } 130 131 if (!mFrames.IsEmpty()) { 132 if (MOZ_UNLIKELY(minBCoord > maxBCoord)) { 133 // XXX When bug 765861 gets fixed, this warning should be upgraded. 134 NS_WARNING("bad block coord"); 135 minBCoord = maxBCoord = 0; 136 } 137 LogicalSize size(rtcWM, mISize, maxBCoord - minBCoord); 138 nsSize containerSize = size.GetPhysicalSize(rtcWM); 139 for (nsIFrame* child : mFrames) { 140 // We reflowed the child with a dummy container size, as the true size 141 // was not yet known at that time. 142 LogicalPoint pos = child->GetLogicalPosition(rtcWM, dummyContainerSize); 143 // Adjust block position to account for minBCoord, 144 // then reposition child based on the true container width. 145 pos.B(rtcWM) -= minBCoord; 146 // Relative positioning hasn't happened yet. 147 // So MovePositionBy should not be used here. 148 child->SetPosition(rtcWM, pos, containerSize); 149 } 150 aDesiredSize.SetSize(rtcWM, size); 151 } else { 152 // If this ruby text container is empty, size it as if there were 153 // an empty inline child inside. 154 // Border and padding are suppressed on ruby text container, so we 155 // create a dummy zero-sized borderPadding for setting BSize. 156 aDesiredSize.ISize(rtcWM) = mISize; 157 LogicalMargin borderPadding(rtcWM); 158 nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, borderPadding, 159 rtcWM, rtcWM); 160 } 161 } 162 163 RubyMetrics nsRubyTextContainerFrame::RubyMetrics( 164 float aRubyMetricsFactor) const { 165 mozilla::RubyMetrics result; 166 WritingMode containerWM = GetWritingMode(); 167 bool foundAnyFrames = false; 168 for (const auto* f : mFrames) { 169 WritingMode wm = f->GetWritingMode(); 170 if (wm.IsOrthogonalTo(containerWM) || f->IsPlaceholderFrame()) { 171 continue; 172 } 173 mozilla::RubyMetrics m = f->RubyMetrics(aRubyMetricsFactor); 174 const LogicalMargin borderPadding = f->GetLogicalUsedBorderAndPadding(wm); 175 m.mAscent += borderPadding.BStart(wm); 176 m.mDescent += borderPadding.BEnd(wm); 177 const LogicalMargin margin = f->GetLogicalUsedMargin(wm); 178 m.mAscent += margin.BStart(wm); 179 m.mDescent += margin.BEnd(wm); 180 result.CombineWith(m); 181 foundAnyFrames = true; 182 } 183 if (!foundAnyFrames) { 184 result = nsIFrame::RubyMetrics(aRubyMetricsFactor); 185 } 186 return result; 187 }