tor-browser

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

SVGSwitchFrame.cpp (8532B)


      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 // Keep in (case-insensitive) order:
      8 #include "SVGGFrame.h"
      9 #include "gfxRect.h"
     10 #include "mozilla/PresShell.h"
     11 #include "mozilla/SVGContainerFrame.h"
     12 #include "mozilla/SVGObserverUtils.h"
     13 #include "mozilla/SVGTextFrame.h"
     14 #include "mozilla/SVGUtils.h"
     15 #include "mozilla/dom/SVGSwitchElement.h"
     16 
     17 using namespace mozilla::dom;
     18 using namespace mozilla::gfx;
     19 using namespace mozilla::image;
     20 
     21 nsIFrame* NS_NewSVGSwitchFrame(mozilla::PresShell* aPresShell,
     22                               mozilla::ComputedStyle* aStyle);
     23 
     24 namespace mozilla {
     25 
     26 class SVGSwitchFrame final : public SVGGFrame {
     27  friend nsIFrame* ::NS_NewSVGSwitchFrame(mozilla::PresShell* aPresShell,
     28                                          ComputedStyle* aStyle);
     29 
     30 protected:
     31  explicit SVGSwitchFrame(ComputedStyle* aStyle, nsPresContext* aPresContext)
     32      : SVGGFrame(aStyle, aPresContext, kClassID) {}
     33 
     34 public:
     35  NS_DECL_FRAMEARENA_HELPERS(SVGSwitchFrame)
     36 
     37 #ifdef DEBUG
     38  void Init(nsIContent* aContent, nsContainerFrame* aParent,
     39            nsIFrame* aPrevInFlow) override;
     40 #endif
     41 
     42 #ifdef DEBUG_FRAME_DUMP
     43  nsresult GetFrameName(nsAString& aResult) const override {
     44    return MakeFrameName(u"SVGSwitch"_ns, aResult);
     45  }
     46 #endif
     47 
     48  void BuildDisplayList(nsDisplayListBuilder* aBuilder,
     49                        const nsDisplayListSet& aLists) override;
     50 
     51  // ISVGDisplayableFrame interface:
     52  void PaintSVG(gfxContext& aContext, const gfxMatrix& aTransform,
     53                imgDrawingParams& aImgParams) override;
     54  nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
     55  void ReflowSVG() override;
     56  SVGBBox GetBBoxContribution(const Matrix& aToBBoxUserspace,
     57                              uint32_t aFlags) override;
     58 
     59 private:
     60  nsIFrame* GetActiveChildFrame();
     61  void ReflowAllSVGTextFramesInsideNonActiveChildren(nsIFrame* aActiveChild);
     62  static void AlwaysReflowSVGTextFrameDoForOneKid(nsIFrame* aKid);
     63 };
     64 
     65 }  // namespace mozilla
     66 
     67 //----------------------------------------------------------------------
     68 // Implementation
     69 
     70 nsIFrame* NS_NewSVGSwitchFrame(mozilla::PresShell* aPresShell,
     71                               mozilla::ComputedStyle* aStyle) {
     72  return new (aPresShell)
     73      mozilla::SVGSwitchFrame(aStyle, aPresShell->GetPresContext());
     74 }
     75 
     76 namespace mozilla {
     77 
     78 NS_IMPL_FRAMEARENA_HELPERS(SVGSwitchFrame)
     79 
     80 #ifdef DEBUG
     81 void SVGSwitchFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
     82                          nsIFrame* aPrevInFlow) {
     83  NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::svgSwitch),
     84               "Content is not an SVG switch");
     85 
     86  SVGGFrame::Init(aContent, aParent, aPrevInFlow);
     87 }
     88 #endif /* DEBUG */
     89 
     90 void SVGSwitchFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
     91                                      const nsDisplayListSet& aLists) {
     92  if (HidesContent()) {
     93    return;
     94  }
     95  if (auto* kid = GetActiveChildFrame()) {
     96    BuildDisplayListForChild(aBuilder, kid, aLists);
     97  }
     98 }
     99 
    100 void SVGSwitchFrame::PaintSVG(gfxContext& aContext, const gfxMatrix& aTransform,
    101                              imgDrawingParams& aImgParams) {
    102  NS_ASSERTION(HasAnyStateBits(NS_FRAME_IS_NONDISPLAY),
    103               "Only painting of non-display SVG should take this code path");
    104 
    105  if (StyleEffects()->IsTransparent()) {
    106    return;
    107  }
    108 
    109  if (auto* kid = GetActiveChildFrame()) {
    110    gfxMatrix tm = aTransform;
    111    if (kid->GetContent()->IsSVGElement()) {
    112      tm = SVGUtils::GetTransformMatrixInUserSpace(kid) * tm;
    113    }
    114    SVGUtils::PaintFrameWithEffects(kid, aContext, tm, aImgParams);
    115  }
    116 }
    117 
    118 nsIFrame* SVGSwitchFrame::GetFrameForPoint(const gfxPoint& aPoint) {
    119  MOZ_ASSERT_UNREACHABLE("A clipPath cannot contain an SVGSwitch element");
    120  return nullptr;
    121 }
    122 
    123 static bool ShouldReflowSVGTextFrameInside(const nsIFrame* aFrame) {
    124  return aFrame->IsSVGContainerFrame() || aFrame->IsSVGForeignObjectFrame() ||
    125         !aFrame->IsSVGFrame();
    126 }
    127 
    128 void SVGSwitchFrame::AlwaysReflowSVGTextFrameDoForOneKid(nsIFrame* aKid) {
    129  if (!aKid->IsSubtreeDirty()) {
    130    return;
    131  }
    132 
    133  if (aKid->IsSVGTextFrame()) {
    134    MOZ_ASSERT(!aKid->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY),
    135               "A non-display SVGTextFrame directly contained in a display "
    136               "container?");
    137    static_cast<SVGTextFrame*>(aKid)->ReflowSVG();
    138  } else if (ShouldReflowSVGTextFrameInside(aKid)) {
    139    if (!aKid->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
    140      for (nsIFrame* kid : aKid->PrincipalChildList()) {
    141        AlwaysReflowSVGTextFrameDoForOneKid(kid);
    142      }
    143    } else {
    144      // This child is in a nondisplay context, something like:
    145      // <switch>
    146      //   ...
    147      //   <g><mask><text></text></mask></g>
    148      // </switch>
    149      // We should not call ReflowSVG on it.
    150      SVGContainerFrame::ReflowSVGNonDisplayText(aKid);
    151    }
    152  }
    153 }
    154 
    155 void SVGSwitchFrame::ReflowAllSVGTextFramesInsideNonActiveChildren(
    156    nsIFrame* aActiveChild) {
    157  for (auto* kid : mFrames) {
    158    if (aActiveChild == kid) {
    159      continue;
    160    }
    161 
    162    AlwaysReflowSVGTextFrameDoForOneKid(kid);
    163  }
    164 }
    165 
    166 void SVGSwitchFrame::ReflowSVG() {
    167  NS_ASSERTION(SVGUtils::OuterSVGIsCallingReflowSVG(this),
    168               "This call is probably a wasteful mistake");
    169 
    170  MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_IS_NONDISPLAY),
    171             "ReflowSVG mechanism not designed for this");
    172 
    173  if (!SVGUtils::NeedsReflowSVG(this)) {
    174    return;
    175  }
    176 
    177  // If the NS_FRAME_FIRST_REFLOW bit has been removed from our parent frame,
    178  // then our outer-<svg> has previously had its initial reflow. In that case
    179  // we need to make sure that that bit has been removed from ourself _before_
    180  // recursing over our children to ensure that they know too. Otherwise, we
    181  // need to remove it _after_ recursing over our children so that they know
    182  // the initial reflow is currently underway.
    183 
    184  bool isFirstReflow = HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
    185 
    186  bool outerSVGHasHadFirstReflow =
    187      !GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
    188 
    189  if (outerSVGHasHadFirstReflow) {
    190    RemoveStateBits(NS_FRAME_FIRST_REFLOW);  // tell our children
    191  }
    192 
    193  OverflowAreas overflowRects;
    194 
    195  auto* child = GetActiveChildFrame();
    196  ReflowAllSVGTextFramesInsideNonActiveChildren(child);
    197 
    198  ISVGDisplayableFrame* svgChild = do_QueryFrame(child);
    199  if (svgChild && !child->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
    200    svgChild->ReflowSVG();
    201 
    202    // We build up our child frame overflows here instead of using
    203    // nsLayoutUtils::UnionChildOverflow since SVG frame's all use the same
    204    // frame list, and we're iterating over that list now anyway.
    205    ConsiderChildOverflow(overflowRects, child);
    206  } else if (child && ShouldReflowSVGTextFrameInside(child)) {
    207    MOZ_ASSERT(
    208        child->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY) || !child->IsSVGFrame(),
    209        "Check for this explicitly in the |if|, then");
    210    ReflowSVGNonDisplayText(child);
    211  }
    212 
    213  if (isFirstReflow) {
    214    // Make sure we have our filter property (if any) before calling
    215    // FinishAndStoreOverflow (subsequent filter changes are handled off
    216    // nsChangeHint_UpdateEffects):
    217    SVGObserverUtils::UpdateEffects(this);
    218  }
    219 
    220  FinishAndStoreOverflow(overflowRects, mRect.Size());
    221 
    222  // Remove state bits after FinishAndStoreOverflow so that it doesn't
    223  // invalidate on first reflow:
    224  RemoveStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
    225                  NS_FRAME_HAS_DIRTY_CHILDREN);
    226 }
    227 
    228 SVGBBox SVGSwitchFrame::GetBBoxContribution(const Matrix& aToBBoxUserspace,
    229                                            uint32_t aFlags) {
    230  auto* kid = GetActiveChildFrame();
    231  if (ISVGDisplayableFrame* svgKid = do_QueryFrame(kid)) {
    232    nsIContent* content = kid->GetContent();
    233    gfxMatrix transform = ThebesMatrix(aToBBoxUserspace);
    234    if (content->IsSVGElement()) {
    235      transform =
    236          static_cast<SVGElement*>(content)->ChildToUserSpaceTransform() *
    237          SVGUtils::GetTransformMatrixInUserSpace(kid) * transform;
    238    }
    239    return svgKid->GetBBoxContribution(ToMatrix(transform), aFlags);
    240  }
    241  return SVGBBox();
    242 }
    243 
    244 nsIFrame* SVGSwitchFrame::GetActiveChildFrame() {
    245  auto* activeChild =
    246      static_cast<dom::SVGSwitchElement*>(GetContent())->GetActiveChild();
    247  return activeChild ? activeChild->GetPrimaryFrame() : nullptr;
    248 }
    249 
    250 }  // namespace mozilla