tor-browser

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

nsProgressFrame.cpp (9858B)


      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 #include "nsProgressFrame.h"
      8 
      9 #include <algorithm>
     10 
     11 #include "mozilla/PresShell.h"
     12 #include "mozilla/dom/Document.h"
     13 #include "mozilla/dom/Element.h"
     14 #include "mozilla/dom/HTMLMeterElement.h"
     15 #include "mozilla/dom/HTMLProgressElement.h"
     16 #include "nsContentCreatorFunctions.h"
     17 #include "nsFontMetrics.h"
     18 #include "nsGkAtoms.h"
     19 #include "nsIContent.h"
     20 #include "nsLayoutUtils.h"
     21 #include "nsNodeInfoManager.h"
     22 #include "nsPresContext.h"
     23 
     24 using namespace mozilla;
     25 using namespace mozilla::dom;
     26 
     27 nsIFrame* NS_NewProgressFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
     28  return new (aPresShell) nsProgressFrame(aStyle, aPresShell->GetPresContext(),
     29                                          nsProgressFrame::Type::Progress);
     30 }
     31 
     32 nsIFrame* NS_NewMeterFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
     33  return new (aPresShell) nsProgressFrame(aStyle, aPresShell->GetPresContext(),
     34                                          nsProgressFrame::Type::Meter);
     35 }
     36 
     37 NS_IMPL_FRAMEARENA_HELPERS(nsProgressFrame)
     38 
     39 nsProgressFrame::nsProgressFrame(ComputedStyle* aStyle,
     40                                 nsPresContext* aPresContext, Type aType)
     41    : nsContainerFrame(aStyle, aPresContext, kClassID), mType(aType) {}
     42 
     43 nsProgressFrame::~nsProgressFrame() = default;
     44 
     45 void nsProgressFrame::Destroy(DestroyContext& aContext) {
     46  NS_ASSERTION(!GetPrevContinuation(),
     47               "nsProgressFrame should not have continuations; if it does we "
     48               "need to call RegUnregAccessKey only for the first.");
     49  aContext.AddAnonymousContent(mBarDiv.forget());
     50  nsContainerFrame::Destroy(aContext);
     51 }
     52 
     53 nsresult nsProgressFrame::CreateAnonymousContent(
     54    nsTArray<ContentInfo>& aElements) {
     55  // Create the progress bar div.
     56  nsCOMPtr<Document> doc = mContent->GetComposedDoc();
     57  mBarDiv = doc->CreateHTMLElement(nsGkAtoms::div);
     58 
     59  if (StaticPrefs::layout_css_modern_range_pseudos_enabled()) {
     60    // TODO(emilio): Create also a slider-track pseudo-element.
     61    mBarDiv->SetPseudoElementType(PseudoStyleType::sliderFill);
     62  } else {
     63    // Associate ::-moz-{progress,meter}-bar pseudo-element to the anon child.
     64    mBarDiv->SetPseudoElementType(mType == Type::Progress
     65                                      ? PseudoStyleType::mozProgressBar
     66                                      : PseudoStyleType::mozMeterBar);
     67  }
     68 
     69  // XXX(Bug 1631371) Check if this should use a fallible operation as it
     70  // pretended earlier, or change the return type to void.
     71  aElements.AppendElement(mBarDiv);
     72 
     73  return NS_OK;
     74 }
     75 
     76 void nsProgressFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
     77                                               uint32_t aFilter) {
     78  if (mBarDiv) {
     79    aElements.AppendElement(mBarDiv);
     80  }
     81 }
     82 
     83 NS_QUERYFRAME_HEAD(nsProgressFrame)
     84  NS_QUERYFRAME_ENTRY(nsProgressFrame)
     85  NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
     86 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
     87 
     88 void nsProgressFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
     89                                       const nsDisplayListSet& aLists) {
     90  if (IsThemed()) {
     91    DisplayBorderBackgroundOutline(aBuilder, aLists);
     92  } else {
     93    BuildDisplayListForInline(aBuilder, aLists);
     94  }
     95 }
     96 
     97 void nsProgressFrame::Reflow(nsPresContext* aPresContext,
     98                             ReflowOutput& aDesiredSize,
     99                             const ReflowInput& aReflowInput,
    100                             nsReflowStatus& aStatus) {
    101  MarkInReflow();
    102  DO_GLOBAL_REFLOW_COUNT("nsProgressFrame");
    103  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
    104  MOZ_ASSERT(mBarDiv, "Progress bar div must exist!");
    105  NS_ASSERTION(!GetPrevContinuation(),
    106               "nsProgressFrame should not have continuations; if it does we "
    107               "need to call RegUnregAccessKey only for the first.");
    108 
    109  const auto wm = aReflowInput.GetWritingMode();
    110  const auto contentBoxSize = aReflowInput.ComputedSizeWithBSizeFallback(
    111      [&] { return DefaultSize().BSize(wm); });
    112  aDesiredSize.SetSize(
    113      wm,
    114      contentBoxSize + aReflowInput.ComputedLogicalBorderPadding(wm).Size(wm));
    115  aDesiredSize.SetOverflowAreasToDesiredBounds();
    116 
    117  for (nsIFrame* childFrame : PrincipalChildList()) {
    118    ReflowChildFrame(childFrame, aPresContext, aReflowInput, contentBoxSize,
    119                     aStatus);
    120    ConsiderChildOverflow(aDesiredSize.mOverflowAreas, childFrame);
    121  }
    122 
    123  FinishAndStoreOverflow(&aDesiredSize);
    124 
    125  aStatus.Reset();  // This type of frame can't be split.
    126 }
    127 
    128 void nsProgressFrame::ReflowChildFrame(nsIFrame* aChild,
    129                                       nsPresContext* aPresContext,
    130                                       const ReflowInput& aReflowInput,
    131                                       const LogicalSize& aParentContentBoxSize,
    132                                       nsReflowStatus& aStatus) {
    133  MOZ_ASSERT(aChild == mBarDiv->GetPrimaryFrame() ||
    134             aChild->IsPlaceholderFrame());
    135  bool vertical = ResolvedOrientationIsVertical();
    136  const WritingMode wm = aChild->GetWritingMode();
    137  const LogicalSize parentSizeInChildWM =
    138      aParentContentBoxSize.ConvertTo(wm, aReflowInput.GetWritingMode());
    139  const nsSize parentPhysicalSize = parentSizeInChildWM.GetPhysicalSize(wm);
    140  LogicalSize availSize = parentSizeInChildWM;
    141  availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
    142  ReflowInput reflowInput(aPresContext, aReflowInput, aChild, availSize,
    143                          Some(parentSizeInChildWM));
    144  nscoord size =
    145      vertical ? parentPhysicalSize.Height() : parentPhysicalSize.Width();
    146  nscoord xoffset = aReflowInput.ComputedPhysicalBorderPadding().left;
    147  nscoord yoffset = aReflowInput.ComputedPhysicalBorderPadding().top;
    148 
    149  double position =
    150      mType == Type::Progress
    151          ? static_cast<HTMLProgressElement*>(GetContent())->Position()
    152          : static_cast<HTMLMeterElement*>(GetContent())->Position();
    153 
    154  // Force the bar's size to match the current progress.
    155  // When indeterminate, the progress' size will be 100%.
    156  if (position >= 0.0 || mType == Type::Meter) {
    157    size = NSToCoordRound(size * position);
    158  }
    159 
    160  if (!vertical && wm.IsPhysicalRTL()) {
    161    xoffset += parentPhysicalSize.Width() - size;
    162  }
    163 
    164  // The bar size is fixed in these cases:
    165  // - the progress position is determined: the bar size is fixed according
    166  //   to it's value.
    167  // - the progress position is indeterminate and the bar appearance should be
    168  //   shown as native: the bar size is forced to 100%.
    169  // Otherwise (when the progress is indeterminate and the bar appearance isn't
    170  // native), the bar size isn't fixed and can be set by the author.
    171  if (position != -1 || ShouldUseNativeStyle()) {
    172    if (vertical) {
    173      // We want the bar to begin at the bottom.
    174      yoffset += parentPhysicalSize.Height() - size;
    175      size -= reflowInput.ComputedPhysicalMargin().TopBottom() +
    176              reflowInput.ComputedPhysicalBorderPadding().TopBottom();
    177      size = std::max(size, 0);
    178      reflowInput.SetComputedHeight(size);
    179    } else {
    180      size -= reflowInput.ComputedPhysicalMargin().LeftRight() +
    181              reflowInput.ComputedPhysicalBorderPadding().LeftRight();
    182      size = std::max(size, 0);
    183      reflowInput.SetComputedWidth(size);
    184    }
    185  } else if (vertical) {
    186    // For vertical progress bars, we need to position the bar specifically when
    187    // the width isn't constrained (position == -1 and !ShouldUseNativeStyle())
    188    // because parentPhysiscalSize.Height() - size == 0.
    189    // FIXME(emilio): This assumes that the bar's height is constrained, which
    190    // seems like a wrong assumption?
    191    yoffset += parentPhysicalSize.Height() - reflowInput.ComputedHeight();
    192  }
    193 
    194  xoffset += reflowInput.ComputedPhysicalMargin().left;
    195  yoffset += reflowInput.ComputedPhysicalMargin().top;
    196 
    197  ReflowOutput barDesiredSize(aReflowInput);
    198  ReflowChild(aChild, aPresContext, barDesiredSize, reflowInput, xoffset,
    199              yoffset, ReflowChildFlags::Default, aStatus);
    200  FinishReflowChild(aChild, aPresContext, barDesiredSize, &reflowInput, xoffset,
    201                    yoffset, ReflowChildFlags::Default);
    202 }
    203 
    204 nsresult nsProgressFrame::AttributeChanged(int32_t aNameSpaceID,
    205                                           nsAtom* aAttribute,
    206                                           AttrModType aModType) {
    207  NS_ASSERTION(mBarDiv, "Progress bar div must exist!");
    208 
    209  if (aNameSpaceID == kNameSpaceID_None &&
    210      (aAttribute == nsGkAtoms::value || aAttribute == nsGkAtoms::max ||
    211       aAttribute == nsGkAtoms::min)) {
    212    auto* presShell = PresShell();
    213    for (auto* childFrame : PrincipalChildList()) {
    214      presShell->FrameNeedsReflow(childFrame, IntrinsicDirty::None,
    215                                  NS_FRAME_IS_DIRTY);
    216    }
    217    InvalidateFrame();
    218  }
    219 
    220  return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
    221 }
    222 
    223 LogicalSize nsProgressFrame::DefaultSize() const {
    224  const auto wm = GetWritingMode();
    225  nscoord em = OneEmInAppUnits();
    226  LogicalSize size(wm, em, em);
    227  nscoord& longAxis = ResolvedOrientationIsVertical() == wm.IsVertical()
    228                          ? size.ISize(wm)
    229                          : size.BSize(wm);
    230  longAxis *= mType == Type::Progress ? 10 : 5;
    231  return size;
    232 }
    233 
    234 nscoord nsProgressFrame::IntrinsicISize(const IntrinsicSizeInput& aInput,
    235                                        IntrinsicISizeType aType) {
    236  return DefaultSize().ISize(GetWritingMode());
    237 }
    238 
    239 bool nsProgressFrame::ShouldUseNativeStyle() const {
    240  return StyleDisplay()->HasAppearance() &&
    241         !Style()->HasAuthorSpecifiedBorderOrBackground();
    242 }