tor-browser

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

SVGViewportFrame.cpp (9764B)


      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 // Main header first:
      8 #include "SVGViewportFrame.h"
      9 
     10 // Keep others in (case-insensitive) order:
     11 #include "gfx2DGlue.h"
     12 #include "gfxContext.h"
     13 #include "mozilla/ISVGDisplayableFrame.h"
     14 #include "mozilla/SVGContainerFrame.h"
     15 #include "mozilla/SVGUtils.h"
     16 #include "mozilla/dom/SVGViewportElement.h"
     17 #include "nsLayoutUtils.h"
     18 
     19 using namespace mozilla::dom;
     20 using namespace mozilla::gfx;
     21 using namespace mozilla::image;
     22 
     23 namespace mozilla {
     24 
     25 //----------------------------------------------------------------------
     26 // ISVGDisplayableFrame methods
     27 
     28 void SVGViewportFrame::PaintSVG(gfxContext& aContext,
     29                                const gfxMatrix& aTransform,
     30                                imgDrawingParams& aImgParams) {
     31  NS_ASSERTION(HasAnyStateBits(NS_FRAME_IS_NONDISPLAY),
     32               "Only painting of non-display SVG should take this code path");
     33 
     34  gfxClipAutoSaveRestore autoSaveClip(&aContext);
     35 
     36  if (StyleDisplay()->IsScrollableOverflow()) {
     37    float x, y, width, height;
     38    static_cast<SVGViewportElement*>(GetContent())
     39        ->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
     40 
     41    if (width <= 0 || height <= 0) {
     42      return;
     43    }
     44 
     45    gfxRect clipRect = SVGUtils::GetClipRectForFrame(this, x, y, width, height);
     46    autoSaveClip.TransformedClip(aTransform, clipRect);
     47  }
     48 
     49  SVGDisplayContainerFrame::PaintSVG(aContext, aTransform, aImgParams);
     50 }
     51 
     52 void SVGViewportFrame::ReflowSVG() {
     53  // mRect must be set before FinishAndStoreOverflow is called in order
     54  // for our overflow areas to be clipped correctly.
     55  float x, y, width, height;
     56  static_cast<SVGViewportElement*>(GetContent())
     57      ->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
     58  if (width < 0.0f) {
     59    width = 0.0f;
     60  }
     61  if (height < 0.0f) {
     62    height = 0.0f;
     63  }
     64  mRect = nsLayoutUtils::RoundGfxRectToAppRect(gfxRect(x, y, width, height),
     65                                               AppUnitsPerCSSPixel());
     66 
     67  // If we have a filter, we need to invalidate ourselves because filter
     68  // output can change even if none of our descendants need repainting.
     69  if (StyleEffects()->HasFilters()) {
     70    InvalidateFrame();
     71  }
     72 
     73  SVGDisplayContainerFrame::ReflowSVG();
     74 }
     75 
     76 void SVGViewportFrame::NotifySVGChanged(ChangeFlags aFlags) {
     77  MOZ_ASSERT(aFlags.contains(ChangeFlag::TransformChanged) ||
     78                 aFlags.contains(ChangeFlag::CoordContextChanged),
     79             "Invalidation logic may need adjusting");
     80 
     81  if (aFlags.contains(ChangeFlag::CoordContextChanged)) {
     82    SVGViewportElement* svg = static_cast<SVGViewportElement*>(GetContent());
     83 
     84    bool xOrYIsPercentage =
     85        svg->mLengthAttributes[SVGViewportElement::ATTR_X].IsPercentage() ||
     86        svg->mLengthAttributes[SVGViewportElement::ATTR_Y].IsPercentage();
     87    bool widthOrHeightIsPercentage =
     88        svg->mLengthAttributes[SVGViewportElement::ATTR_WIDTH].IsPercentage() ||
     89        svg->mLengthAttributes[SVGViewportElement::ATTR_HEIGHT].IsPercentage();
     90 
     91    if (xOrYIsPercentage || widthOrHeightIsPercentage) {
     92      // Ancestor changes can't affect how we render from the perspective of
     93      // any rendering observers that we may have, so we don't need to
     94      // invalidate them. We also don't need to invalidate ourself, since our
     95      // changed ancestor will have invalidated its entire area, which includes
     96      // our area.
     97      // For perf reasons we call this before calling NotifySVGChanged() below.
     98      SVGUtils::ScheduleReflowSVG(this);
     99    }
    100 
    101    // Coordinate context changes affect mCanvasTM if we have a
    102    // percentage 'x' or 'y', or if we have a percentage 'width' or 'height' AND
    103    // a 'viewBox'.
    104 
    105    if (!aFlags.contains(ChangeFlag::TransformChanged) &&
    106        (xOrYIsPercentage ||
    107         (widthOrHeightIsPercentage && svg->HasViewBox()))) {
    108      aFlags += ChangeFlag::TransformChanged;
    109    }
    110 
    111    if (svg->HasViewBox() || !widthOrHeightIsPercentage) {
    112      // Remove COORD_CONTEXT_CHANGED, since we establish the coordinate
    113      // context for our descendants and this notification won't change its
    114      // dimensions:
    115      aFlags -= ChangeFlag::CoordContextChanged;
    116 
    117      if (aFlags.isEmpty()) {
    118        return;  // No notification flags left
    119      }
    120    }
    121  }
    122 
    123  SVGDisplayContainerFrame::NotifySVGChanged(aFlags);
    124 }
    125 
    126 SVGBBox SVGViewportFrame::GetBBoxContribution(const Matrix& aToBBoxUserspace,
    127                                              uint32_t aFlags) {
    128  // XXXjwatt It seems like authors would want the result to be clipped by the
    129  // viewport we establish if IsScrollableOverflow() is true.  We should
    130  // consider doing that.  See bug 1350755.
    131 
    132  SVGBBox bbox;
    133 
    134  if (aFlags & SVGUtils::eForGetClientRects) {
    135    // XXXjwatt For consistency with the old code this code includes the
    136    // viewport we establish in the result, but only includes the bounds of our
    137    // descendants if they are not clipped to that viewport.  However, this is
    138    // both inconsistent with Chrome and with the specs.  See bug 1350755.
    139    // Ideally getClientRects/getBoundingClientRect should be consistent with
    140    // getBBox.
    141    float x, y, w, h;
    142    static_cast<SVGViewportElement*>(GetContent())
    143        ->GetAnimatedLengthValues(&x, &y, &w, &h, nullptr);
    144    if (w < 0.0f) {
    145      w = 0.0f;
    146    }
    147    if (h < 0.0f) {
    148      h = 0.0f;
    149    }
    150    Rect viewport(x, y, w, h);
    151    bbox = aToBBoxUserspace.TransformBounds(viewport);
    152    if (StyleDisplay()->IsScrollableOverflow()) {
    153      return bbox;
    154    }
    155    // Else we're not clipping to our viewport so we fall through and include
    156    // the bounds of our children.
    157  }
    158 
    159  SVGBBox descendantsBbox =
    160      SVGDisplayContainerFrame::GetBBoxContribution(aToBBoxUserspace, aFlags);
    161 
    162  bbox.UnionEdges(descendantsBbox);
    163 
    164  return bbox;
    165 }
    166 
    167 nsresult SVGViewportFrame::AttributeChanged(int32_t aNameSpaceID,
    168                                            nsAtom* aAttribute, AttrModType) {
    169  if (aNameSpaceID == kNameSpaceID_None &&
    170      !HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
    171    SVGViewportElement* content =
    172        static_cast<SVGViewportElement*>(GetContent());
    173 
    174    if (aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height) {
    175      nsLayoutUtils::PostRestyleEvent(
    176          mContent->AsElement(), RestyleHint{0},
    177          nsChangeHint_InvalidateRenderingObservers);
    178      SVGUtils::ScheduleReflowSVG(this);
    179 
    180      if (content->HasViewBoxOrSyntheticViewBox()) {
    181        // make sure our cached transform matrix gets (lazily) updated
    182        mCanvasTM = nullptr;
    183        content->ChildrenOnlyTransformChanged();
    184        SVGUtils::NotifyChildrenOfSVGChange(this, ChangeFlag::TransformChanged);
    185      } else {
    186        ChangeFlags flags(ChangeFlag::CoordContextChanged);
    187        if (mCanvasTM && mCanvasTM->IsSingular()) {
    188          mCanvasTM = nullptr;
    189          flags += ChangeFlag::TransformChanged;
    190        }
    191        SVGUtils::NotifyChildrenOfSVGChange(this, flags);
    192      }
    193 
    194    } else if (aAttribute == nsGkAtoms::preserveAspectRatio ||
    195               aAttribute == nsGkAtoms::viewBox || aAttribute == nsGkAtoms::x ||
    196               aAttribute == nsGkAtoms::y) {
    197      // make sure our cached transform matrix gets (lazily) updated
    198      mCanvasTM = nullptr;
    199 
    200      SVGUtils::NotifyChildrenOfSVGChange(
    201          this, aAttribute == nsGkAtoms::viewBox
    202                    ? ChangeFlags(ChangeFlag::TransformChanged,
    203                                  ChangeFlag::CoordContextChanged)
    204                    : ChangeFlag::TransformChanged);
    205 
    206      if (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y) {
    207        nsLayoutUtils::PostRestyleEvent(
    208            mContent->AsElement(), RestyleHint{0},
    209            nsChangeHint_InvalidateRenderingObservers);
    210        SVGUtils::ScheduleReflowSVG(this);
    211      } else if (aAttribute == nsGkAtoms::viewBox ||
    212                 (aAttribute == nsGkAtoms::preserveAspectRatio &&
    213                  content->HasViewBoxOrSyntheticViewBox())) {
    214        content->ChildrenOnlyTransformChanged();
    215        // SchedulePaint sets a global state flag so we only need to call it
    216        // once (on ourself is fine), not once on each child (despite bug
    217        // 828240).
    218        SchedulePaint();
    219      }
    220    }
    221  }
    222 
    223  return NS_OK;
    224 }
    225 
    226 nsIFrame* SVGViewportFrame::GetFrameForPoint(const gfxPoint& aPoint) {
    227  MOZ_ASSERT_UNREACHABLE("A clipPath cannot contain svg or symbol elements");
    228  return nullptr;
    229 }
    230 
    231 //----------------------------------------------------------------------
    232 // ISVGSVGFrame methods:
    233 
    234 void SVGViewportFrame::NotifyViewportOrTransformChanged(ChangeFlags aFlags) {
    235  // The dimensions of inner-<svg> frames are purely defined by their "width"
    236  // and "height" attributes, and transform changes can only occur as a result
    237  // of changes to their "width", "height", "viewBox" or "preserveAspectRatio"
    238  // attributes. Changes to all of these attributes are handled in
    239  // AttributeChanged(), so we should never be called.
    240  NS_ERROR("Not called for SVGViewportFrame");
    241 }
    242 
    243 //----------------------------------------------------------------------
    244 // SVGContainerFrame methods:
    245 
    246 bool SVGViewportFrame::HasChildrenOnlyTransform(gfx::Matrix* aTransform) const {
    247  auto* content = static_cast<SVGViewportElement*>(GetContent());
    248  if (!content->HasViewBoxOrSyntheticViewBox()) {
    249    return false;
    250  }
    251  // XXX Maybe return false if the transform is the identity transform?
    252  if (aTransform) {
    253    *aTransform = content->GetViewBoxTransform();
    254  }
    255  return true;
    256 }
    257 
    258 }  // namespace mozilla