tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }