tor-browser

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

SVGOuterSVGFrame.cpp (33526B)


      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 "SVGOuterSVGFrame.h"
      9 
     10 // Keep others in (case-insensitive) order:
     11 #include "gfxContext.h"
     12 #include "mozilla/PresShell.h"
     13 #include "mozilla/SVGUtils.h"
     14 #include "mozilla/dom/BrowserChild.h"
     15 #include "mozilla/dom/Document.h"
     16 #include "mozilla/dom/Element.h"
     17 #include "mozilla/dom/SVGSVGElement.h"
     18 #include "nsDisplayList.h"
     19 #include "nsIInterfaceRequestorUtils.h"
     20 #include "nsLayoutUtils.h"
     21 #include "nsObjectLoadingContent.h"
     22 #include "nsSubDocumentFrame.h"
     23 
     24 using namespace mozilla::dom;
     25 using namespace mozilla::gfx;
     26 using namespace mozilla::image;
     27 
     28 //----------------------------------------------------------------------
     29 // Implementation
     30 
     31 nsContainerFrame* NS_NewSVGOuterSVGFrame(mozilla::PresShell* aPresShell,
     32                                         mozilla::ComputedStyle* aStyle) {
     33  return new (aPresShell)
     34      mozilla::SVGOuterSVGFrame(aStyle, aPresShell->GetPresContext());
     35 }
     36 
     37 namespace mozilla {
     38 
     39 NS_IMPL_FRAMEARENA_HELPERS(SVGOuterSVGFrame)
     40 
     41 SVGOuterSVGFrame::SVGOuterSVGFrame(ComputedStyle* aStyle,
     42                                   nsPresContext* aPresContext)
     43    : SVGDisplayContainerFrame(aStyle, aPresContext, kClassID) {
     44  // Outer-<svg> has CSS layout, so remove this bit:
     45  RemoveStateBits(NS_FRAME_SVG_LAYOUT);
     46  AddStateBits(NS_FRAME_REFLOW_ROOT | NS_FRAME_FONT_INFLATION_CONTAINER |
     47               NS_FRAME_FONT_INFLATION_FLOW_ROOT);
     48 }
     49 
     50 // The CSS Containment spec says that size-contained replaced elements must be
     51 // treated as having an intrinsic width and height of 0.  That's applicable to
     52 // outer SVG frames, unless they're the outermost element (in which case
     53 // they're not really "replaced", and there's no outer context to contain sizes
     54 // from leaking into). Hence, we check for a parent element before we bother
     55 // testing for 'contain:size'.
     56 static inline ContainSizeAxes ContainSizeAxesIfApplicable(
     57    const SVGOuterSVGFrame* aFrame) {
     58  if (!aFrame->GetContent()->GetParent()) {
     59    return ContainSizeAxes(false, false);
     60  }
     61  return aFrame->GetContainSizeAxes();
     62 }
     63 
     64 // This should match ImageDocument::GetZoomLevel.
     65 float SVGOuterSVGFrame::ComputeFullZoom() const {
     66  MOZ_ASSERT(mIsRootContent);
     67  MOZ_ASSERT(!mIsInIframe);
     68  if (BrowsingContext* bc = PresContext()->Document()->GetBrowsingContext()) {
     69    return bc->FullZoom();
     70  }
     71  return 1.0f;
     72 }
     73 
     74 class AsyncSendIntrinsicSizeAndRatioToEmbedder final : public Runnable {
     75 public:
     76  explicit AsyncSendIntrinsicSizeAndRatioToEmbedder(SVGOuterSVGFrame* aFrame)
     77      : Runnable("AsyncSendIntrinsicSizeAndRatioToEmbedder") {
     78    mElement = aFrame->GetContent()->AsElement();
     79  }
     80  NS_IMETHOD Run() override {
     81    AUTO_PROFILER_LABEL("AsyncSendIntrinsicSizeAndRatioToEmbedder::Run", OTHER);
     82    // Check we're still an outer svg frame. We could have been
     83    // moved inside another svg element and now be an SVGInnerSVGFrame.
     84    if (SVGOuterSVGFrame* frame = do_QueryFrame(mElement->GetPrimaryFrame())) {
     85      frame->MaybeSendIntrinsicSizeAndRatioToEmbedder();
     86    }
     87    return NS_OK;
     88  }
     89 
     90 private:
     91  RefPtr<Element> mElement;
     92 };
     93 
     94 void SVGOuterSVGFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
     95                            nsIFrame* aPrevInFlow) {
     96  NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::svg),
     97               "Content is not an SVG 'svg' element!");
     98 
     99  // Check for conditional processing attributes here rather than in
    100  // nsCSSFrameConstructor::FindSVGData because we want to avoid
    101  // simply giving failing outer <svg> elements an SVGContainerFrame.
    102  // We don't create other SVG frames if PassesConditionalProcessingTests
    103  // returns false, but since we do create SVGOuterSVGFrame frames we
    104  // prevent them from painting by [ab]use NS_FRAME_IS_NONDISPLAY. The
    105  // frame will be recreated via an nsChangeHint_ReconstructFrame restyle if
    106  // the value returned by PassesConditionalProcessingTests changes.
    107  auto* svg = static_cast<SVGSVGElement*>(aContent);
    108  if (!svg->PassesConditionalProcessingTests()) {
    109    AddStateBits(NS_FRAME_IS_NONDISPLAY);
    110  }
    111 
    112  SVGDisplayContainerFrame::Init(aContent, aParent, aPrevInFlow);
    113 
    114  Document* doc = mContent->GetUncomposedDoc();
    115  mIsRootContent = doc && doc->GetRootElement() == mContent;
    116 
    117  if (mIsRootContent) {
    118    if (nsCOMPtr<nsIDocShell> docShell = PresContext()->GetDocShell()) {
    119      RefPtr<BrowsingContext> bc = docShell->GetBrowsingContext();
    120      if (const Maybe<nsString>& type = bc->GetEmbedderElementType()) {
    121        mIsInObjectOrEmbed =
    122            nsGkAtoms::object->Equals(*type) || nsGkAtoms::embed->Equals(*type);
    123        mIsInIframe = nsGkAtoms::iframe->Equals(*type);
    124      }
    125    }
    126    if (!mIsInIframe) {
    127      mFullZoom = ComputeFullZoom();
    128    }
    129  }
    130 
    131  // We need to do this async in order to get the right ordering with
    132  // respect to `Destroy()` when reframed.
    133  nsContentUtils::AddScriptRunner(
    134      new AsyncSendIntrinsicSizeAndRatioToEmbedder(this));
    135 }
    136 
    137 //----------------------------------------------------------------------
    138 // nsQueryFrame methods
    139 
    140 NS_QUERYFRAME_HEAD(SVGOuterSVGFrame)
    141  NS_QUERYFRAME_ENTRY(SVGOuterSVGFrame)
    142  NS_QUERYFRAME_ENTRY(ISVGSVGFrame)
    143 NS_QUERYFRAME_TAIL_INHERITING(SVGDisplayContainerFrame)
    144 
    145 //----------------------------------------------------------------------
    146 // nsIFrame methods
    147 
    148 nscoord SVGOuterSVGFrame::IntrinsicISize(const IntrinsicSizeInput& aInput,
    149                                         IntrinsicISizeType aType) {
    150  if (aType == IntrinsicISizeType::MinISize) {
    151    return GetIntrinsicSize().ISize(GetWritingMode()).valueOr(0);
    152  }
    153 
    154  nscoord result;
    155  SVGSVGElement* svg = static_cast<SVGSVGElement*>(GetContent());
    156  WritingMode wm = GetWritingMode();
    157  const SVGAnimatedLength& isize =
    158      wm.IsVertical() ? svg->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT]
    159                      : svg->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
    160 
    161  if (Maybe<nscoord> containISize =
    162          ContainSizeAxesIfApplicable(this).ContainIntrinsicISize(*this)) {
    163    result = *containISize;
    164  } else if (isize.IsPercentage()) {
    165    // If we are here, our inline size attribute is a percentage either
    166    // explicitly (via an attribute value) or implicitly (by being unset, which
    167    // is treated as 100%). The following if-condition, deciding to return
    168    // either the fallback intrinsic size or zero, is made to match blink and
    169    // webkit's behavior for webcompat.
    170    if (isize.IsExplicitlySet() ||
    171        StylePosition()
    172            ->ISize(wm, AnchorPosResolutionParams::From(this))
    173            ->HasPercent() ||
    174        !GetAspectRatio()) {
    175      result = wm.IsVertical() ? kFallbackIntrinsicSize.height
    176                               : kFallbackIntrinsicSize.width;
    177    } else {
    178      result = nscoord(0);
    179    }
    180  } else {
    181    result =
    182        nsPresContext::CSSPixelsToAppUnits(isize.GetAnimValueWithZoom(svg));
    183    if (result < 0) {
    184      result = nscoord(0);
    185    }
    186  }
    187 
    188  return result;
    189 }
    190 
    191 /* virtual */
    192 IntrinsicSize SVGOuterSVGFrame::GetIntrinsicSize() {
    193  // XXXjwatt Note that here we want to return the CSS width/height if they're
    194  // specified and we're embedded inside an nsIObjectLoadingContent.
    195 
    196  const auto containAxes = ContainSizeAxesIfApplicable(this);
    197  if (containAxes.IsBoth()) {
    198    // Intrinsic size of 'contain:size' replaced elements is determined by
    199    // contain-intrinsic-size, defaulting to 0x0.
    200    return FinishIntrinsicSize(containAxes, IntrinsicSize(0, 0));
    201  }
    202 
    203  SVGSVGElement* content = static_cast<SVGSVGElement*>(GetContent());
    204  const SVGAnimatedLength& width =
    205      content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
    206  const SVGAnimatedLength& height =
    207      content->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT];
    208 
    209  IntrinsicSize intrinsicSize;
    210 
    211  if (!width.IsPercentage()) {
    212    nscoord val =
    213        nsPresContext::CSSPixelsToAppUnits(width.GetAnimValueWithZoom(content));
    214    intrinsicSize.width.emplace(std::max(val, 0));
    215  }
    216 
    217  if (!height.IsPercentage()) {
    218    nscoord val = nsPresContext::CSSPixelsToAppUnits(
    219        height.GetAnimValueWithZoom(content));
    220    intrinsicSize.height.emplace(std::max(val, 0));
    221  }
    222 
    223  return FinishIntrinsicSize(containAxes, intrinsicSize);
    224 }
    225 
    226 /* virtual */
    227 AspectRatio SVGOuterSVGFrame::GetIntrinsicRatio() const {
    228  if (ContainSizeAxesIfApplicable(this).IsAny()) {
    229    return AspectRatio();
    230  }
    231 
    232  // We only have an intrinsic size/ratio if our width and height attributes
    233  // are both specified and set to non-percentage values, or we have a viewBox
    234  // rect: https://svgwg.org/svg2-draft/coords.html#SizingSVGInCSS
    235 
    236  auto* content = static_cast<SVGSVGElement*>(GetContent());
    237  const SVGAnimatedLength& width =
    238      content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
    239  const SVGAnimatedLength& height =
    240      content->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT];
    241  if (!width.IsPercentage() && !height.IsPercentage()) {
    242    // Use width/height ratio only if
    243    // 1. it's not a degenerate ratio, and
    244    // 2. width and height are non-negative numbers.
    245    // Otherwise, we use the viewbox rect.
    246    // https://github.com/w3c/csswg-drafts/issues/6286
    247    // Note width/height may have different units and therefore be
    248    // affected by zoom in different ways.
    249    const float w = width.GetAnimValueWithZoom(content);
    250    const float h = height.GetAnimValueWithZoom(content);
    251    if (w > 0.0f && h > 0.0f) {
    252      return AspectRatio::FromSize(w, h);
    253    }
    254  }
    255 
    256  const auto& viewBox = content->GetViewBoxInternal();
    257  if (viewBox.HasRect()) {
    258    float zoom = Style()->EffectiveZoom().ToFloat();
    259    const auto& anim = viewBox.GetAnimValue() * zoom;
    260    return AspectRatio::FromSize(anim.width, anim.height);
    261  }
    262 
    263  return SVGDisplayContainerFrame::GetIntrinsicRatio();
    264 }
    265 
    266 /* virtual */
    267 nsIFrame::SizeComputationResult SVGOuterSVGFrame::ComputeSize(
    268    const SizeComputationInput& aSizingInput, WritingMode aWritingMode,
    269    const LogicalSize& aCBSize, nscoord aAvailableISize,
    270    const LogicalSize& aMargin, const LogicalSize& aBorderPadding,
    271    const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) {
    272  if (IsRootOfImage() || mIsInObjectOrEmbed) {
    273    // The embedding element has sized itself using the CSS replaced element
    274    // sizing rules, using our intrinsic dimensions as necessary. The SVG spec
    275    // says that the width and height of embedded SVG is overridden by the
    276    // width and height of the embedding element, so we just need to size to
    277    // the viewport that the embedding element has established for us.
    278    return {aCBSize, AspectRatioUsage::None};
    279  }
    280 
    281  LogicalSize cbSize = aCBSize;
    282  IntrinsicSize intrinsicSize = GetIntrinsicSize();
    283 
    284  if (mIsRootContent) {
    285    // We're the root of the outermost browsing context, so we need to scale
    286    // cbSize by the full-zoom so that SVGs with percentage width/height zoom:
    287 
    288    NS_ASSERTION(aCBSize.ISize(aWritingMode) != NS_UNCONSTRAINEDSIZE &&
    289                     aCBSize.BSize(aWritingMode) != NS_UNCONSTRAINEDSIZE,
    290                 "root should not have auto-width/height containing block");
    291 
    292    if (!mIsInIframe) {
    293      // NOTE: We can't just use mFullZoom because this can run before Reflow()
    294      // updates it.
    295      const float zoom = ComputeFullZoom();
    296      cbSize.ISize(aWritingMode) *= zoom;
    297      cbSize.BSize(aWritingMode) *= zoom;
    298    }
    299 
    300    // We also need to honour the width and height attributes' default values
    301    // of 100% when we're the root of a browsing context.  (GetIntrinsicSize()
    302    // doesn't report these since there's no such thing as a percentage
    303    // intrinsic size.  Also note that explicit percentage values are mapped
    304    // into style, so the following isn't for them.)
    305 
    306    auto* content = static_cast<SVGSVGElement*>(GetContent());
    307 
    308    const SVGAnimatedLength& width =
    309        content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
    310    if (width.IsPercentage()) {
    311      MOZ_ASSERT(!intrinsicSize.width,
    312                 "GetIntrinsicSize should have reported no intrinsic width");
    313      float val = width.GetAnimValInSpecifiedUnits() / 100.0f;
    314      intrinsicSize.width.emplace(std::max(val, 0.0f) *
    315                                  cbSize.Width(aWritingMode));
    316    }
    317 
    318    const SVGAnimatedLength& height =
    319        content->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT];
    320    NS_ASSERTION(aCBSize.BSize(aWritingMode) != NS_UNCONSTRAINEDSIZE,
    321                 "root should not have auto-height containing block");
    322    if (height.IsPercentage()) {
    323      MOZ_ASSERT(!intrinsicSize.height,
    324                 "GetIntrinsicSize should have reported no intrinsic height");
    325      float val = height.GetAnimValInSpecifiedUnits() / 100.0f;
    326      intrinsicSize.height.emplace(std::max(val, 0.0f) *
    327                                   cbSize.Height(aWritingMode));
    328    }
    329    MOZ_ASSERT(intrinsicSize.height && intrinsicSize.width,
    330               "We should have just handled the only situation where"
    331               "we lack an intrinsic height or width.");
    332  }
    333 
    334  return {ComputeSizeWithIntrinsicDimensions(
    335              aSizingInput.mRenderingContext, aWritingMode, intrinsicSize,
    336              GetAspectRatio(), cbSize, aMargin, aBorderPadding, aSizeOverrides,
    337              aFlags),
    338          AspectRatioUsage::None};
    339 }
    340 
    341 void SVGOuterSVGFrame::Reflow(nsPresContext* aPresContext,
    342                              ReflowOutput& aDesiredSize,
    343                              const ReflowInput& aReflowInput,
    344                              nsReflowStatus& aStatus) {
    345  MarkInReflow();
    346  DO_GLOBAL_REFLOW_COUNT("SVGOuterSVGFrame");
    347  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
    348  NS_FRAME_TRACE(
    349      NS_FRAME_TRACE_CALLS,
    350      ("enter SVGOuterSVGFrame::Reflow: availSize=%d,%d",
    351       aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
    352 
    353  MOZ_ASSERT(HasAnyStateBits(NS_FRAME_IN_REFLOW), "frame is not in reflow");
    354 
    355  const auto wm = GetWritingMode();
    356  aDesiredSize.SetSize(wm, aReflowInput.ComputedSizeWithBorderPadding(wm));
    357 
    358  NS_ASSERTION(!GetPrevInFlow(), "SVG can't currently be broken across pages.");
    359 
    360  SVGSVGElement* svgElem = static_cast<SVGSVGElement*>(GetContent());
    361 
    362  auto* anonKid = static_cast<SVGOuterSVGAnonChildFrame*>(
    363      PrincipalChildList().FirstChild());
    364 
    365  if (HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
    366    // Initialize
    367    svgElem->UpdateHasChildrenOnlyTransform();
    368  }
    369 
    370  // If our SVG viewport has changed, update our content and notify.
    371  // http://www.w3.org/TR/SVG11/coords.html#ViewportSpace
    372 
    373  gfx::Size newViewportSize(
    374      nsPresContext::AppUnitsToFloatCSSPixels(aReflowInput.ComputedWidth()),
    375      nsPresContext::AppUnitsToFloatCSSPixels(aReflowInput.ComputedHeight()));
    376 
    377  ChangeFlags changeBits;
    378  if (newViewportSize != svgElem->GetViewportSize()) {
    379    // When our viewport size changes, we may need to update the overflow rects
    380    // of our child frames. This is the case if:
    381    //
    382    //  * We have a real/synthetic viewBox (a children-only transform), since
    383    //    the viewBox transform will change as the viewport dimensions change.
    384    //
    385    //  * We do not have a real/synthetic viewBox, but the last time we
    386    //    reflowed (or the last time UpdateOverflow() was called) we did.
    387    //
    388    // We only handle the former case here, in which case we mark all our child
    389    // frames as dirty so that we reflow them below and update their overflow
    390    // rects.
    391    //
    392    // In the latter case, updating of overflow rects is handled for removal of
    393    // real viewBox (the viewBox attribute) in AttributeChanged. Synthetic
    394    // viewBox "removal" (e.g. a document references the same SVG via both an
    395    // <svg:image> and then as a CSS background image (a synthetic viewBox is
    396    // used when painting the former, but not when painting the latter)) is
    397    // handled in SVGSVGElement::FlushImageTransformInvalidation.
    398    //
    399    if (svgElem->HasViewBoxOrSyntheticViewBox()) {
    400      nsIFrame* anonChild = PrincipalChildList().FirstChild();
    401      anonChild->MarkSubtreeDirty();
    402      for (nsIFrame* child : anonChild->PrincipalChildList()) {
    403        child->MarkSubtreeDirty();
    404      }
    405    }
    406    changeBits += ChangeFlag::CoordContextChanged;
    407    svgElem->SetViewportSize(newViewportSize);
    408  }
    409  if (mIsRootContent && !mIsInIframe) {
    410    const auto oldZoom = mFullZoom;
    411    mFullZoom = ComputeFullZoom();
    412    if (oldZoom != mFullZoom) {
    413      changeBits += ChangeFlag::FullZoomChanged;
    414    }
    415  }
    416  if (!changeBits.isEmpty() && !HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
    417    NotifyViewportOrTransformChanged(changeBits);
    418  }
    419 
    420  // Now that we've marked the necessary children as dirty, call
    421  // ReflowSVG() or ReflowSVGNonDisplayText() on them, depending
    422  // on whether we are non-display.
    423  mCallingReflowSVG = true;
    424  if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
    425    ReflowSVGNonDisplayText(this);
    426  } else {
    427    // Update the mRects and ink overflow rects of all our descendants,
    428    // including our anonymous wrapper kid:
    429    anonKid->ReflowSVG();
    430    MOZ_ASSERT(!anonKid->GetNextSibling(),
    431               "We should have one anonymous child frame wrapping our real "
    432               "children");
    433  }
    434  mCallingReflowSVG = false;
    435 
    436  // Set our anonymous kid's offset from our border box:
    437  anonKid->SetPosition(GetContentRectRelativeToSelf().TopLeft());
    438 
    439  // Including our size in our overflow rects regardless of the value of
    440  // 'background', 'border', etc. makes sure that we usually (when we clip to
    441  // our content area) don't have to keep changing our overflow rects as our
    442  // descendants move about (see perf comment below). Including our size in our
    443  // scrollable overflow rect also makes sure that we scroll if we're too big
    444  // for our viewport.
    445  //
    446  // <svg> never allows scrolling to anything outside its mRect (only panning),
    447  // so we must always keep our scrollable overflow set to our size.
    448  //
    449  // With regards to ink overflow, we always clip root-<svg> (see our
    450  // BuildDisplayList method) regardless of the value of the 'overflow'
    451  // property since that is per-spec, even for the initial 'visible' value. For
    452  // that reason there's no point in adding descendant ink overflow to our
    453  // own when this frame is for a root-<svg>. That said, there's also a very
    454  // good performance reason for us wanting to avoid doing so. If we did, then
    455  // the frame's overflow would often change as descendants that are partially
    456  // or fully outside its rect moved (think animation on/off screen), and that
    457  // would cause us to do a full NS_FRAME_IS_DIRTY reflow and repaint of the
    458  // entire document tree each such move (see bug 875175).
    459  //
    460  // So it's only non-root outer-<svg> that has the ink overflow of its
    461  // descendants added to its own. (Note that the default user-agent style
    462  // sheet makes 'hidden' the default value for :not(root(svg)), so usually
    463  // FinishAndStoreOverflow will still clip this back to the frame's rect.)
    464  //
    465  // WARNING!! Keep UpdateBounds below in sync with whatever we do for our
    466  // overflow rects here! (Again, see bug 875175.)
    467  //
    468  aDesiredSize.SetOverflowAreasToDesiredBounds();
    469 
    470  // An outer SVG will be here as a nondisplay if it fails the conditional
    471  // processing test. In that case, we don't maintain its overflow.
    472  if (!HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
    473    if (!mIsRootContent) {
    474      aDesiredSize.mOverflowAreas.InkOverflow().UnionRect(
    475          aDesiredSize.mOverflowAreas.InkOverflow(),
    476          anonKid->InkOverflowRect() + anonKid->GetPosition());
    477    }
    478    FinishAndStoreOverflow(&aDesiredSize);
    479  }
    480 
    481  NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
    482                 ("exit SVGOuterSVGFrame::Reflow: size=%d,%d",
    483                  aDesiredSize.Width(), aDesiredSize.Height()));
    484 }
    485 
    486 /* virtual */
    487 void SVGOuterSVGFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas,
    488                                          bool aAsIfScrolled) {
    489  // See the comments in Reflow above.
    490 
    491  // WARNING!! Keep this in sync with Reflow above!
    492 
    493  if (!mIsRootContent) {
    494    nsIFrame* anonKid = PrincipalChildList().FirstChild();
    495    aOverflowAreas.InkOverflow().UnionRect(
    496        aOverflowAreas.InkOverflow(),
    497        anonKid->InkOverflowRect() + anonKid->GetPosition());
    498  }
    499 }
    500 
    501 //----------------------------------------------------------------------
    502 // container methods
    503 
    504 nsresult SVGOuterSVGFrame::AttributeChanged(int32_t aNameSpaceID,
    505                                            nsAtom* aAttribute, AttrModType) {
    506  if (aNameSpaceID == kNameSpaceID_None &&
    507      !HasAnyStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_NONDISPLAY)) {
    508    if (aAttribute == nsGkAtoms::viewBox ||
    509        aAttribute == nsGkAtoms::preserveAspectRatio) {
    510      // make sure our cached transform matrix gets (lazily) updated
    511      mCanvasTM = nullptr;
    512 
    513      SVGUtils::NotifyChildrenOfSVGChange(
    514          PrincipalChildList().FirstChild(),
    515          aAttribute == nsGkAtoms::viewBox
    516              ? ChangeFlags(ChangeFlag::TransformChanged,
    517                            ChangeFlag::CoordContextChanged)
    518              : ChangeFlag::TransformChanged);
    519 
    520      if (aAttribute != nsGkAtoms::transform) {
    521        static_cast<SVGSVGElement*>(GetContent())
    522            ->ChildrenOnlyTransformChanged();
    523      }
    524    }
    525    if (aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height ||
    526        aAttribute == nsGkAtoms::viewBox) {
    527      // Don't call ChildrenOnlyTransformChanged() here, since we call it
    528      // under Reflow if the width/height/viewBox actually changed.
    529 
    530      MaybeSendIntrinsicSizeAndRatioToEmbedder();
    531 
    532      if (!mIsInObjectOrEmbed) {
    533        // We are not embedded by reference, so our 'width' and 'height'
    534        // attributes are not overridden (and viewBox may influence our
    535        // intrinsic aspect ratio).  We need to reflow.
    536        PresShell()->FrameNeedsReflow(
    537            this, IntrinsicDirty::FrameAncestorsAndDescendants,
    538            NS_FRAME_IS_DIRTY);
    539      }
    540    }
    541  }
    542 
    543  return NS_OK;
    544 }
    545 
    546 //----------------------------------------------------------------------
    547 // painting
    548 
    549 void SVGOuterSVGFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
    550                                        const nsDisplayListSet& aLists) {
    551  if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
    552    return;
    553  }
    554 
    555  DisplayBorderBackgroundOutline(aBuilder, aLists);
    556 
    557  nsRect visibleRect = aBuilder->GetVisibleRect();
    558  nsRect dirtyRect = aBuilder->GetDirtyRect();
    559 
    560  // Per-spec, we always clip root-<svg> even when 'overflow' has its initial
    561  // value of 'visible'. See also the "ink overflow" comments in Reflow.
    562  DisplayListClipState::AutoSaveRestore autoSR(aBuilder);
    563  if (mIsRootContent || StyleDisplay()->IsScrollableOverflow()) {
    564    autoSR.ClipContainingBlockDescendantsToContentBox(aBuilder, this);
    565    visibleRect = visibleRect.Intersect(GetContentRectRelativeToSelf());
    566    dirtyRect = dirtyRect.Intersect(GetContentRectRelativeToSelf());
    567  }
    568 
    569  nsDisplayListBuilder::AutoBuildingDisplayList building(
    570      aBuilder, this, visibleRect, dirtyRect);
    571 
    572  nsDisplayList* contentList = aLists.Content();
    573  nsDisplayListSet set(contentList, contentList, contentList, contentList,
    574                       contentList, contentList);
    575  BuildDisplayListForNonBlockChildren(aBuilder, set);
    576 }
    577 
    578 //----------------------------------------------------------------------
    579 // ISVGSVGFrame methods:
    580 
    581 void SVGOuterSVGFrame::NotifyViewportOrTransformChanged(ChangeFlags aFlags) {
    582  auto* content = static_cast<SVGSVGElement*>(GetContent());
    583  if (aFlags.contains(ChangeFlag::CoordContextChanged)) {
    584    if (content->HasViewBox()) {
    585      // Percentage lengths on children resolve against the viewBox rect so we
    586      // don't need to notify them of the viewport change, but the viewBox
    587      // transform will have changed, so we need to notify them of that instead.
    588      aFlags = ChangeFlag::TransformChanged;
    589    } else if (content->ShouldSynthesizeViewBox()) {
    590      // In the case of a synthesized viewBox, the synthetic viewBox's rect
    591      // changes as the viewport changes. As a result we need to maintain the
    592      // COORD_CONTEXT_CHANGED flag.
    593      aFlags += ChangeFlag::TransformChanged;
    594    } else if (mCanvasTM && mCanvasTM->IsSingular()) {
    595      // A width/height of zero will result in us having a singular mCanvasTM
    596      // even when we don't have a viewBox. So we also want to recompute our
    597      // mCanvasTM for this width/height change even though we don't have a
    598      // viewBox.
    599      aFlags += ChangeFlag::TransformChanged;
    600    }
    601  }
    602 
    603  bool haveNonFullZoomTransformChange =
    604      aFlags.contains(ChangeFlag::TransformChanged);
    605 
    606  if (aFlags.contains(ChangeFlag::FullZoomChanged)) {
    607    // Convert FullZoomChanged to TransformChanged.
    608    aFlags -= ChangeFlag::FullZoomChanged;
    609    aFlags += ChangeFlag::TransformChanged;
    610  }
    611 
    612  if (aFlags.contains(ChangeFlag::TransformChanged)) {
    613    // Make sure our canvas transform matrix gets (lazily) recalculated:
    614    mCanvasTM = nullptr;
    615 
    616    if (haveNonFullZoomTransformChange &&
    617        !HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
    618      SVGViewportElement::ChildrenOnlyTransformChangedFlags flags;
    619      if (HasAnyStateBits(NS_FRAME_IN_REFLOW)) {
    620        flags +=
    621            SVGViewportElement::ChildrenOnlyTransformChangedFlag::DuringReflow;
    622      }
    623      content->ChildrenOnlyTransformChanged(flags);
    624    }
    625  }
    626 
    627  SVGUtils::NotifyChildrenOfSVGChange(PrincipalChildList().FirstChild(),
    628                                      aFlags);
    629 }
    630 
    631 //----------------------------------------------------------------------
    632 // ISVGDisplayableFrame methods:
    633 
    634 void SVGOuterSVGFrame::PaintSVG(gfxContext& aContext,
    635                                const gfxMatrix& aTransform,
    636                                imgDrawingParams& aImgParams) {
    637  NS_ASSERTION(
    638      PrincipalChildList().FirstChild()->IsSVGOuterSVGAnonChildFrame() &&
    639          !PrincipalChildList().FirstChild()->GetNextSibling(),
    640      "We should have a single, anonymous, child");
    641  auto* anonKid = static_cast<SVGOuterSVGAnonChildFrame*>(
    642      PrincipalChildList().FirstChild());
    643  anonKid->PaintSVG(aContext, aTransform, aImgParams);
    644 }
    645 
    646 SVGBBox SVGOuterSVGFrame::GetBBoxContribution(
    647    const gfx::Matrix& aToBBoxUserspace, uint32_t aFlags) {
    648  NS_ASSERTION(
    649      PrincipalChildList().FirstChild()->IsSVGOuterSVGAnonChildFrame() &&
    650          !PrincipalChildList().FirstChild()->GetNextSibling(),
    651      "We should have a single, anonymous, child");
    652  // We must defer to our child so that we don't include our
    653  // content->ChildToUserSpaceTransform() transform.
    654  auto* anonKid = static_cast<SVGOuterSVGAnonChildFrame*>(
    655      PrincipalChildList().FirstChild());
    656  return anonKid->GetBBoxContribution(aToBBoxUserspace, aFlags);
    657 }
    658 
    659 //----------------------------------------------------------------------
    660 // SVGContainerFrame methods:
    661 
    662 gfxMatrix SVGOuterSVGFrame::GetCanvasTM() {
    663  if (!mCanvasTM) {
    664    auto* content = static_cast<SVGSVGElement*>(GetContent());
    665    float devPxPerCSSPx = 1.0f / nsPresContext::AppUnitsToFloatCSSPixels(
    666                                     PresContext()->AppUnitsPerDevPixel());
    667 
    668    gfxMatrix tm = content->ChildToUserSpaceTransform().PostScale(
    669        devPxPerCSSPx, devPxPerCSSPx);
    670    mCanvasTM = MakeUnique<gfxMatrix>(tm);
    671  }
    672  return *mCanvasTM;
    673 }
    674 
    675 //----------------------------------------------------------------------
    676 // Implementation helpers
    677 
    678 bool SVGOuterSVGFrame::IsRootOfImage() {
    679  if (!mContent->GetParent()) {
    680    // Our content is the document element
    681    Document* doc = mContent->GetUncomposedDoc();
    682    if (doc && doc->IsBeingUsedAsImage()) {
    683      // Our document is being used as an image
    684      return true;
    685    }
    686  }
    687 
    688  return false;
    689 }
    690 
    691 bool SVGOuterSVGFrame::VerticalScrollbarNotNeeded() const {
    692  const SVGAnimatedLength& height =
    693      static_cast<SVGSVGElement*>(GetContent())
    694          ->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT];
    695  return height.IsPercentage() && height.GetBaseValInSpecifiedUnits() <= 100;
    696 }
    697 
    698 void SVGOuterSVGFrame::AppendDirectlyOwnedAnonBoxes(
    699    nsTArray<OwnedAnonBox>& aResult) {
    700  nsIFrame* anonKid = PrincipalChildList().FirstChild();
    701  MOZ_ASSERT(anonKid->IsSVGOuterSVGAnonChildFrame());
    702  aResult.AppendElement(OwnedAnonBox(anonKid));
    703 }
    704 
    705 void SVGOuterSVGFrame::MaybeSendIntrinsicSizeAndRatioToEmbedder() {
    706  MaybeSendIntrinsicSizeAndRatioToEmbedder(Some(GetIntrinsicSize()),
    707                                           Some(GetAspectRatio()));
    708 }
    709 
    710 void SVGOuterSVGFrame::MaybeSendIntrinsicSizeAndRatioToEmbedder(
    711    Maybe<IntrinsicSize> aIntrinsicSize, Maybe<AspectRatio> aIntrinsicRatio) {
    712  if (!mIsInObjectOrEmbed) {
    713    return;
    714  }
    715 
    716  nsCOMPtr<nsIDocShell> docShell = PresContext()->GetDocShell();
    717  if (!docShell) {
    718    return;
    719  }
    720 
    721  BrowsingContext* bc = docShell->GetBrowsingContext();
    722  MOZ_ASSERT(bc->IsContentSubframe());
    723 
    724  if (bc->GetParent()->IsInProcess()) {
    725    if (Element* embedder = bc->GetEmbedderElement()) {
    726      if (nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(embedder)) {
    727        static_cast<nsObjectLoadingContent*>(olc.get())
    728            ->SubdocumentIntrinsicSizeOrRatioChanged(aIntrinsicSize,
    729                                                     aIntrinsicRatio);
    730      }
    731      return;
    732    }
    733  }
    734 
    735  if (BrowserChild* browserChild = BrowserChild::GetFrom(docShell)) {
    736    (void)browserChild->SendIntrinsicSizeOrRatioChanged(aIntrinsicSize,
    737                                                        aIntrinsicRatio);
    738  }
    739 }
    740 
    741 void SVGOuterSVGFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
    742  SVGDisplayContainerFrame::DidSetComputedStyle(aOldComputedStyle);
    743 
    744  if (!aOldComputedStyle) {
    745    return;
    746  }
    747 
    748  if (aOldComputedStyle->StylePosition()->mAspectRatio !=
    749      StylePosition()->mAspectRatio) {
    750    // Our aspect-ratio property value changed, and an embedding <object> or
    751    // <embed> might care about that.
    752    MaybeSendIntrinsicSizeAndRatioToEmbedder();
    753  }
    754 }
    755 
    756 void SVGOuterSVGFrame::Destroy(DestroyContext& aContext) {
    757  // This handles both the case when the root <svg> element is made display:none
    758  // (and thus loses its intrinsic size and aspect ratio), and when the frame
    759  // is navigated elsewhere & we need to reset parent <object>/<embed>'s
    760  // recorded intrinsic size/ratio values.
    761  MaybeSendIntrinsicSizeAndRatioToEmbedder(Nothing(), Nothing());
    762 
    763  SVGDisplayContainerFrame::Destroy(aContext);
    764 }
    765 
    766 }  // namespace mozilla
    767 
    768 //----------------------------------------------------------------------
    769 // Implementation of SVGOuterSVGAnonChildFrame
    770 
    771 nsContainerFrame* NS_NewSVGOuterSVGAnonChildFrame(
    772    mozilla::PresShell* aPresShell, mozilla::ComputedStyle* aStyle) {
    773  return new (aPresShell)
    774      mozilla::SVGOuterSVGAnonChildFrame(aStyle, aPresShell->GetPresContext());
    775 }
    776 
    777 namespace mozilla {
    778 
    779 NS_IMPL_FRAMEARENA_HELPERS(SVGOuterSVGAnonChildFrame)
    780 
    781 #ifdef DEBUG
    782 void SVGOuterSVGAnonChildFrame::Init(nsIContent* aContent,
    783                                     nsContainerFrame* aParent,
    784                                     nsIFrame* aPrevInFlow) {
    785  MOZ_ASSERT(aParent->IsSVGOuterSVGFrame(), "Unexpected parent");
    786  SVGDisplayContainerFrame::Init(aContent, aParent, aPrevInFlow);
    787 }
    788 #endif
    789 
    790 void SVGOuterSVGAnonChildFrame::BuildDisplayList(
    791    nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) {
    792  // Wrap our contents into an nsDisplaySVGWrapper.
    793  // We wrap this frame instead of the SVGOuterSVGFrame so that the wrapper
    794  // doesn't contain the <svg> element's CSS styles, like backgrounds or
    795  // borders. Creating the nsDisplaySVGWrapper here also means that it'll be
    796  // inside the nsDisplayTransform for our viewbox transform. The
    797  // nsDisplaySVGWrapper's reference frame is this frame, because this frame
    798  // always returns true from IsSVGTransformed.
    799  nsDisplayList newList(aBuilder);
    800  nsDisplayListSet set(&newList, &newList, &newList, &newList, &newList,
    801                       &newList);
    802  BuildDisplayListForNonBlockChildren(aBuilder, set);
    803  aLists.Content()->AppendNewToTop<nsDisplaySVGWrapper>(aBuilder, this,
    804                                                        &newList);
    805 }
    806 
    807 bool SVGOuterSVGFrame::HasChildrenOnlyTransform(Matrix* aTransform) const {
    808  auto* content = static_cast<SVGSVGElement*>(GetContent());
    809  if (!content->HasChildrenOnlyTransform()) {
    810    return false;
    811  }
    812  if (aTransform) {
    813    // Outer-<svg> doesn't use x/y, so we can use the child-to-user-space
    814    // transform here.
    815    *aTransform = gfx::ToMatrix(content->ChildToUserSpaceTransform());
    816  }
    817  return true;
    818 }
    819 
    820 bool SVGOuterSVGAnonChildFrame::DoGetParentSVGTransforms(
    821    Matrix* aFromParentTransform) const {
    822  // We want this frame to be a reference frame. An easy way to achieve that is
    823  // to always return true from this method, even for identity transforms.
    824  // This frame being a reference frame ensures that the offset between this
    825  // <svg> element and the parent reference frame is completely absorbed by the
    826  // nsDisplayTransform that's created for this frame, and that this offset does
    827  // not affect our descendants' transforms. Consequently, if the <svg> element
    828  // moves, e.g. during scrolling, the transform matrices of our contents are
    829  // unaffected. This simplifies invalidation.
    830  // TODO(emilio): Is the comment above true for WebRender nowadays?
    831  SVGUtils::GetParentSVGTransforms(this, aFromParentTransform);
    832  return true;
    833 }
    834 
    835 }  // namespace mozilla