tor-browser

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

SVGSVGElement.cpp (21187B)


      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 "mozilla/dom/SVGSVGElement.h"
      8 
      9 #include "DOMSVGAngle.h"
     10 #include "DOMSVGLength.h"
     11 #include "DOMSVGNumber.h"
     12 #include "DOMSVGPoint.h"
     13 #include "ISVGSVGFrame.h"
     14 #include "mozilla/ContentEvents.h"
     15 #include "mozilla/EventDispatcher.h"
     16 #include "mozilla/ISVGDisplayableFrame.h"
     17 #include "mozilla/PresShell.h"
     18 #include "mozilla/SMILAnimationController.h"
     19 #include "mozilla/SMILTimeContainer.h"
     20 #include "mozilla/SVGUtils.h"
     21 #include "mozilla/dom/BindContext.h"
     22 #include "mozilla/dom/DOMMatrix.h"
     23 #include "mozilla/dom/SVGMatrix.h"
     24 #include "mozilla/dom/SVGRect.h"
     25 #include "mozilla/dom/SVGSVGElementBinding.h"
     26 #include "mozilla/dom/SVGViewElement.h"
     27 #include "nsFrameSelection.h"
     28 #include "nsIFrame.h"
     29 
     30 NS_IMPL_NS_NEW_SVG_ELEMENT_CHECK_PARSER(SVG)
     31 
     32 using namespace mozilla::gfx;
     33 
     34 namespace mozilla::dom {
     35 
     36 using namespace SVGPreserveAspectRatio_Binding;
     37 using namespace SVGSVGElement_Binding;
     38 
     39 SVGEnumMapping SVGSVGElement::sZoomAndPanMap[] = {
     40    {nsGkAtoms::disable, SVG_ZOOMANDPAN_DISABLE},
     41    {nsGkAtoms::magnify, SVG_ZOOMANDPAN_MAGNIFY},
     42    {nullptr, 0}};
     43 
     44 SVGElement::EnumInfo SVGSVGElement::sEnumInfo[1] = {
     45    {nsGkAtoms::zoomAndPan, sZoomAndPanMap, SVG_ZOOMANDPAN_MAGNIFY}};
     46 
     47 JSObject* SVGSVGElement::WrapNode(JSContext* aCx,
     48                                  JS::Handle<JSObject*> aGivenProto) {
     49  return SVGSVGElement_Binding::Wrap(aCx, this, aGivenProto);
     50 }
     51 
     52 //----------------------------------------------------------------------
     53 // nsISupports methods
     54 
     55 NS_IMPL_CYCLE_COLLECTION_CLASS(SVGSVGElement)
     56 
     57 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGSVGElement,
     58                                                SVGSVGElementBase)
     59  if (tmp->mTimedDocumentRoot) {
     60    tmp->mTimedDocumentRoot->Unlink();
     61  }
     62 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     63 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGSVGElement,
     64                                                  SVGSVGElementBase)
     65  if (tmp->mTimedDocumentRoot) {
     66    tmp->mTimedDocumentRoot->Traverse(&cb);
     67  }
     68 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     69 
     70 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGSVGElement)
     71  NS_INTERFACE_MAP_ENTRY_CONCRETE(SVGSVGElement)
     72 NS_INTERFACE_MAP_END_INHERITING(SVGSVGElementBase);
     73 
     74 NS_IMPL_ADDREF_INHERITED(SVGSVGElement, SVGSVGElementBase)
     75 NS_IMPL_RELEASE_INHERITED(SVGSVGElement, SVGSVGElementBase)
     76 
     77 SVGView::SVGView() {
     78  mZoomAndPan.Init(SVGSVGElement::ZOOMANDPAN, SVG_ZOOMANDPAN_MAGNIFY);
     79  mViewBox.Init();
     80  mPreserveAspectRatio.Init();
     81 }
     82 
     83 //----------------------------------------------------------------------
     84 // Implementation
     85 
     86 SVGSVGElement::SVGSVGElement(
     87    already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
     88    FromParser aFromParser)
     89    : SVGSVGElementBase(std::move(aNodeInfo)),
     90      mCurrentTranslate(0.0f, 0.0f),
     91      mCurrentScale(1.0f),
     92      mStartAnimationOnBindToTree(aFromParser == NOT_FROM_PARSER ||
     93                                  aFromParser == FROM_PARSER_FRAGMENT ||
     94                                  aFromParser == FROM_PARSER_XSLT),
     95      mImageNeedsTransformInvalidation(false) {}
     96 
     97 //----------------------------------------------------------------------
     98 // nsINode methods
     99 
    100 NS_IMPL_ELEMENT_CLONE_WITH_INIT_AND_PARSER(SVGSVGElement)
    101 
    102 //----------------------------------------------------------------------
    103 // nsIDOMSVGSVGElement methods:
    104 
    105 already_AddRefed<DOMSVGAnimatedLength> SVGSVGElement::X() {
    106  return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this);
    107 }
    108 
    109 already_AddRefed<DOMSVGAnimatedLength> SVGSVGElement::Y() {
    110  return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this);
    111 }
    112 
    113 already_AddRefed<DOMSVGAnimatedLength> SVGSVGElement::Width() {
    114  return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this);
    115 }
    116 
    117 already_AddRefed<DOMSVGAnimatedLength> SVGSVGElement::Height() {
    118  return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
    119 }
    120 
    121 bool SVGSVGElement::UseCurrentView() const {
    122  return mSVGView || mCurrentViewID;
    123 }
    124 
    125 float SVGSVGElement::CurrentScale() const { return mCurrentScale; }
    126 
    127 #define CURRENT_SCALE_MAX 16.0f
    128 #define CURRENT_SCALE_MIN 0.0625f
    129 
    130 void SVGSVGElement::SetCurrentScale(float aCurrentScale) {
    131  // Prevent bizarre behaviour and maxing out of CPU and memory by clamping
    132  aCurrentScale =
    133      std::clamp(aCurrentScale, CURRENT_SCALE_MIN, CURRENT_SCALE_MAX);
    134 
    135  if (aCurrentScale == mCurrentScale) {
    136    return;
    137  }
    138  mCurrentScale = aCurrentScale;
    139 
    140  if (IsRootSVGSVGElement()) {
    141    InvalidateTransformNotifyFrame();
    142  }
    143 }
    144 
    145 already_AddRefed<DOMSVGPoint> SVGSVGElement::CurrentTranslate() {
    146  return DOMSVGPoint::GetTranslateTearOff(&mCurrentTranslate, this);
    147 }
    148 
    149 uint32_t SVGSVGElement::SuspendRedraw(uint32_t max_wait_milliseconds) {
    150  // suspendRedraw is a no-op in Mozilla, so it doesn't matter what
    151  // we return
    152  return 1;
    153 }
    154 
    155 void SVGSVGElement::UnsuspendRedraw(uint32_t suspend_handle_id) {
    156  // no-op
    157 }
    158 
    159 void SVGSVGElement::UnsuspendRedrawAll() {
    160  // no-op
    161 }
    162 
    163 void SVGSVGElement::ForceRedraw() {
    164  // no-op
    165 }
    166 
    167 void SVGSVGElement::PauseAnimations() {
    168  if (mTimedDocumentRoot) {
    169    mTimedDocumentRoot->Pause(SMILTimeContainer::PAUSE_SCRIPT);
    170  }
    171  // else we're not the outermost <svg> or not bound to a tree, so silently fail
    172 }
    173 
    174 static SMILTime SecondsToSMILTime(float aSeconds) {
    175  double milliseconds = double(aSeconds) * PR_MSEC_PER_SEC;
    176  // Round to nearest whole number before converting, to avoid precision
    177  // errors
    178  return SVGUtils::ClampToInt64(NS_round(milliseconds));
    179 }
    180 
    181 void SVGSVGElement::PauseAnimationsAt(float aSeconds) {
    182  if (mTimedDocumentRoot) {
    183    mTimedDocumentRoot->PauseAt(SecondsToSMILTime(aSeconds));
    184  }
    185  // else we're not the outermost <svg> or not bound to a tree, so silently fail
    186 }
    187 
    188 void SVGSVGElement::UnpauseAnimations() {
    189  if (mTimedDocumentRoot) {
    190    mTimedDocumentRoot->Resume(SMILTimeContainer::PAUSE_SCRIPT);
    191  }
    192  // else we're not the outermost <svg> or not bound to a tree, so silently fail
    193 }
    194 
    195 bool SVGSVGElement::AnimationsPaused() {
    196  SMILTimeContainer* root = GetTimedDocumentRoot();
    197  return root && root->IsPausedByType(SMILTimeContainer::PAUSE_SCRIPT);
    198 }
    199 
    200 float SVGSVGElement::GetCurrentTimeAsFloat() {
    201  SMILTimeContainer* root = GetTimedDocumentRoot();
    202  if (root) {
    203    double fCurrentTimeMs = double(root->GetCurrentTimeAsSMILTime());
    204    return (float)(fCurrentTimeMs / PR_MSEC_PER_SEC);
    205  }
    206  return 0.f;
    207 }
    208 
    209 void SVGSVGElement::SetCurrentTime(float seconds) {
    210  if (!mTimedDocumentRoot) {
    211    // we're not the outermost <svg> or not bound to a tree, so silently fail
    212    return;
    213  }
    214  // Make sure the timegraph is up-to-date
    215  if (auto* currentDoc = GetComposedDoc()) {
    216    currentDoc->FlushPendingNotifications(FlushType::Style);
    217  }
    218  if (!mTimedDocumentRoot) {
    219    return;
    220  }
    221  FlushAnimations();
    222  mTimedDocumentRoot->SetCurrentTime(SecondsToSMILTime(seconds));
    223  AnimationNeedsResample();
    224  // Trigger synchronous sample now, to:
    225  //  - Make sure we get an up-to-date paint after this method
    226  //  - re-enable event firing (it got disabled during seeking, and it
    227  //  doesn't get re-enabled until the first sample after the seek -- so
    228  //  let's make that happen now.)
    229  FlushAnimations();
    230 }
    231 
    232 void SVGSVGElement::DeselectAll() {
    233  nsIFrame* frame = GetPrimaryFrame();
    234  if (frame) {
    235    RefPtr<nsFrameSelection> frameSelection = frame->GetFrameSelection();
    236    frameSelection->ClearNormalSelection();
    237  }
    238 }
    239 
    240 already_AddRefed<DOMSVGNumber> SVGSVGElement::CreateSVGNumber() {
    241  return do_AddRef(new DOMSVGNumber(this));
    242 }
    243 
    244 already_AddRefed<DOMSVGLength> SVGSVGElement::CreateSVGLength() {
    245  return do_AddRef(new DOMSVGLength());
    246 }
    247 
    248 already_AddRefed<DOMSVGAngle> SVGSVGElement::CreateSVGAngle() {
    249  return do_AddRef(new DOMSVGAngle(this));
    250 }
    251 
    252 already_AddRefed<DOMSVGPoint> SVGSVGElement::CreateSVGPoint() {
    253  return do_AddRef(new DOMSVGPoint(Point(0, 0)));
    254 }
    255 
    256 already_AddRefed<SVGMatrix> SVGSVGElement::CreateSVGMatrix() {
    257  return do_AddRef(new SVGMatrix());
    258 }
    259 
    260 already_AddRefed<SVGRect> SVGSVGElement::CreateSVGRect() {
    261  return do_AddRef(new SVGRect(this));
    262 }
    263 
    264 already_AddRefed<DOMSVGTransform> SVGSVGElement::CreateSVGTransform() {
    265  return do_AddRef(new DOMSVGTransform());
    266 }
    267 
    268 already_AddRefed<DOMSVGTransform> SVGSVGElement::CreateSVGTransformFromMatrix(
    269    const DOMMatrix2DInit& matrix, ErrorResult& rv) {
    270  return do_AddRef(new DOMSVGTransform(matrix, rv));
    271 }
    272 
    273 void SVGSVGElement::DidChangeTranslate() {
    274  if (Document* doc = GetUncomposedDoc()) {
    275    RefPtr<PresShell> presShell = doc->GetPresShell();
    276    // now dispatch the appropriate event if we are the root element
    277    if (presShell && IsRootSVGSVGElement()) {
    278      nsEventStatus status = nsEventStatus_eIgnore;
    279      WidgetEvent svgScrollEvent(true, eSVGScroll);
    280      presShell->HandleDOMEventWithTarget(this, &svgScrollEvent, &status);
    281      InvalidateTransformNotifyFrame();
    282    }
    283  }
    284 }
    285 
    286 //----------------------------------------------------------------------
    287 // SVGZoomAndPanValues
    288 uint16_t SVGSVGElement::ZoomAndPan() const {
    289  return mEnumAttributes[ZOOMANDPAN].GetAnimValue();
    290 }
    291 
    292 void SVGSVGElement::SetZoomAndPan(uint16_t aZoomAndPan, ErrorResult& rv) {
    293  if (aZoomAndPan == SVG_ZOOMANDPAN_DISABLE ||
    294      aZoomAndPan == SVG_ZOOMANDPAN_MAGNIFY) {
    295    ErrorResult nestedRv;
    296    mEnumAttributes[ZOOMANDPAN].SetBaseValue(aZoomAndPan, this, nestedRv);
    297    MOZ_ASSERT(!nestedRv.Failed(),
    298               "We already validated our aZoomAndPan value!");
    299    return;
    300  }
    301 
    302  rv.ThrowRangeError<MSG_INVALID_ZOOMANDPAN_VALUE_ERROR>();
    303 }
    304 
    305 //----------------------------------------------------------------------
    306 SMILTimeContainer* SVGSVGElement::GetTimedDocumentRoot() {
    307  if (mTimedDocumentRoot) {
    308    return mTimedDocumentRoot.get();
    309  }
    310 
    311  // We must not be the outermost <svg> element, try to find it
    312  SVGSVGElement* outerSVGElement = SVGContentUtils::GetOuterSVGElement(this);
    313 
    314  if (outerSVGElement) {
    315    return outerSVGElement->GetTimedDocumentRoot();
    316  }
    317  // invalid structure
    318  return nullptr;
    319 }
    320 //----------------------------------------------------------------------
    321 // SVGElement
    322 nsresult SVGSVGElement::BindToTree(BindContext& aContext, nsINode& aParent) {
    323  SMILAnimationController* smilController = nullptr;
    324 
    325  if (Document* doc = aContext.GetComposedDoc()) {
    326    if ((smilController = doc->GetAnimationController())) {
    327      // SMIL is enabled in this document
    328      if (WillBeOutermostSVG(aParent)) {
    329        // We'll be the outermost <svg> element.  We'll need a time container.
    330        if (!mTimedDocumentRoot) {
    331          mTimedDocumentRoot = MakeUnique<SMILTimeContainer>();
    332        }
    333      } else {
    334        // We're a child of some other <svg> element, so we don't need our own
    335        // time container. However, we need to make sure that we'll get a
    336        // kick-start if we get promoted to be outermost later on.
    337        mTimedDocumentRoot = nullptr;
    338        mStartAnimationOnBindToTree = true;
    339      }
    340    }
    341  }
    342 
    343  nsresult rv = SVGGraphicsElement::BindToTree(aContext, aParent);
    344  NS_ENSURE_SUCCESS(rv, rv);
    345 
    346  if (mTimedDocumentRoot && smilController) {
    347    rv = mTimedDocumentRoot->SetParent(smilController);
    348    if (mStartAnimationOnBindToTree) {
    349      mTimedDocumentRoot->Begin();
    350      mStartAnimationOnBindToTree = false;
    351    }
    352  }
    353 
    354  return rv;
    355 }
    356 
    357 void SVGSVGElement::UnbindFromTree(UnbindContext& aContext) {
    358  if (mTimedDocumentRoot) {
    359    mTimedDocumentRoot->SetParent(nullptr);
    360  }
    361 
    362  SVGGraphicsElement::UnbindFromTree(aContext);
    363 }
    364 
    365 SVGAnimatedTransformList* SVGSVGElement::GetAnimatedTransformList(
    366    uint32_t aFlags) {
    367  if (!(aFlags & DO_ALLOCATE) && mSVGView && mSVGView->mTransforms) {
    368    return mSVGView->mTransforms.get();
    369  }
    370  return SVGGraphicsElement::GetAnimatedTransformList(aFlags);
    371 }
    372 
    373 void SVGSVGElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
    374  if (aVisitor.mEvent->mMessage == eSVGLoad) {
    375    if (mTimedDocumentRoot) {
    376      mTimedDocumentRoot->Begin();
    377      // Set 'resample needed' flag, so that if any script calls a DOM method
    378      // that requires up-to-date animations before our first sample callback,
    379      // we'll force a synchronous sample.
    380      AnimationNeedsResample();
    381    }
    382  }
    383  SVGSVGElementBase::GetEventTargetParent(aVisitor);
    384 }
    385 
    386 bool SVGSVGElement::IsEventAttributeNameInternal(nsAtom* aName) {
    387  /* The events in EventNameType_SVGSVG are for events that are only
    388     applicable to outermost 'svg' elements. We don't check if we're an outer
    389     'svg' element in case we're not inserted into the document yet, but since
    390     the target of the events in question will always be the outermost 'svg'
    391     element, this shouldn't cause any real problems.
    392  */
    393  return nsContentUtils::IsEventAttributeName(
    394      aName, (EventNameType_SVGGraphic | EventNameType_SVGSVG));
    395 }
    396 
    397 LengthPercentage SVGSVGElement::GetIntrinsicWidthOrHeight(int aAttr) {
    398  MOZ_ASSERT(aAttr == ATTR_WIDTH || aAttr == ATTR_HEIGHT);
    399 
    400  int otherAttr = (aAttr == ATTR_HEIGHT) ? ATTR_WIDTH : ATTR_HEIGHT;
    401 
    402  if (!mLengthAttributes[aAttr].IsExplicitlySet() &&
    403      mLengthAttributes[otherAttr].IsExplicitlySet()) {
    404    const auto& viewBox = GetViewBoxInternal();
    405    if (viewBox.HasRect()) {
    406      auto aspectRatio = AspectRatio::FromSize(viewBox.GetAnimValue().width,
    407                                               viewBox.GetAnimValue().height);
    408      if (aAttr == ATTR_HEIGHT && aspectRatio) {
    409        aspectRatio = aspectRatio.Inverted();
    410      }
    411      if (aspectRatio) {
    412        if (mLengthAttributes[otherAttr].IsPercentage()) {
    413          float rawSize = aspectRatio.ApplyToFloat(
    414              mLengthAttributes[otherAttr].GetAnimValInSpecifiedUnits());
    415          return LengthPercentage::FromPercentage(rawSize);
    416        }
    417 
    418        float rawSize = aspectRatio.ApplyToFloat(
    419            mLengthAttributes[otherAttr].GetAnimValueWithZoom(this));
    420        return LengthPercentage::FromPixels(rawSize);
    421      }
    422    }
    423  }
    424 
    425  if (mLengthAttributes[aAttr].IsPercentage()) {
    426    float rawSize = mLengthAttributes[aAttr].GetAnimValInSpecifiedUnits();
    427    return LengthPercentage::FromPercentage(rawSize);
    428  }
    429 
    430  // Passing |this| as a SVGViewportElement* invokes the variant of GetAnimValue
    431  // that uses the passed argument as the context, but that's fine since we
    432  // know the length isn't a percentage so the context won't be used (and we
    433  // need to pass the element to be able to resolve em/ex units).
    434  float rawSize = mLengthAttributes[aAttr].GetAnimValueWithZoom(this);
    435  return LengthPercentage::FromPixels(rawSize);
    436 }
    437 
    438 //----------------------------------------------------------------------
    439 // public helpers:
    440 
    441 LengthPercentage SVGSVGElement::GetIntrinsicWidth() {
    442  return GetIntrinsicWidthOrHeight(ATTR_WIDTH);
    443 }
    444 
    445 LengthPercentage SVGSVGElement::GetIntrinsicHeight() {
    446  return GetIntrinsicWidthOrHeight(ATTR_HEIGHT);
    447 }
    448 
    449 void SVGSVGElement::FlushImageTransformInvalidation() {
    450  MOZ_ASSERT(!GetParent(), "Should only be called on root node");
    451  MOZ_ASSERT(OwnerDoc()->IsBeingUsedAsImage(),
    452             "Should only be called on image documents");
    453 
    454  if (mImageNeedsTransformInvalidation) {
    455    InvalidateTransformNotifyFrame();
    456    mImageNeedsTransformInvalidation = false;
    457  }
    458 }
    459 
    460 //----------------------------------------------------------------------
    461 // implementation helpers
    462 
    463 bool SVGSVGElement::WillBeOutermostSVG(nsINode& aParent) const {
    464  nsINode* parent = &aParent;
    465  while (parent && parent->IsSVGElement()) {
    466    if (parent->IsSVGElement(nsGkAtoms::foreignObject)) {
    467      // SVG in a foreignObject must have its own <svg> (SVGOuterSVGFrame).
    468      return false;
    469    }
    470    if (parent->IsSVGElement(nsGkAtoms::svg)) {
    471      return false;
    472    }
    473    parent = parent->GetParentOrShadowHostNode();
    474  }
    475 
    476  return true;
    477 }
    478 
    479 void SVGSVGElement::DidChangeSVGView() {
    480  InvalidateTransformNotifyFrame();
    481  // We map the SVGView transform as the transform css property, so need to
    482  // schedule attribute mapping.
    483  if (!IsPendingMappedAttributeEvaluation() &&
    484      mAttrs.MarkAsPendingPresAttributeEvaluation()) {
    485    OwnerDoc()->ScheduleForPresAttrEvaluation(this);
    486  }
    487 }
    488 
    489 void SVGSVGElement::InvalidateTransformNotifyFrame() {
    490  // might fail this check if we've failed conditional processing
    491  if (ISVGSVGFrame* svgframe = do_QueryFrame(GetPrimaryFrame())) {
    492    svgframe->NotifyViewportOrTransformChanged(
    493        ISVGDisplayableFrame::ChangeFlag::TransformChanged);
    494  }
    495 }
    496 
    497 SVGElement::EnumAttributesInfo SVGSVGElement::GetEnumInfo() {
    498  return EnumAttributesInfo(mEnumAttributes, sEnumInfo, std::size(sEnumInfo));
    499 }
    500 
    501 void SVGSVGElement::SetImageOverridePreserveAspectRatio(
    502    const SVGPreserveAspectRatio& aPAR) {
    503  MOZ_ASSERT(OwnerDoc()->IsBeingUsedAsImage(),
    504             "should only override preserveAspectRatio in images");
    505 
    506  bool hasViewBox = HasViewBox();
    507  if (!hasViewBox && ShouldSynthesizeViewBox()) {
    508    // My non-<svg:image> clients will have been painting me with a synthesized
    509    // viewBox, but my <svg:image> client that's about to paint me now does NOT
    510    // want that.  Need to tell ourselves to flush our transform.
    511    mImageNeedsTransformInvalidation = true;
    512  }
    513 
    514  if (!hasViewBox) {
    515    return;  // preserveAspectRatio irrelevant (only matters if we have viewBox)
    516  }
    517 
    518  if (SetPreserveAspectRatioProperty(aPAR)) {
    519    mImageNeedsTransformInvalidation = true;
    520  }
    521 }
    522 
    523 void SVGSVGElement::ClearImageOverridePreserveAspectRatio() {
    524  MOZ_ASSERT(OwnerDoc()->IsBeingUsedAsImage(),
    525             "should only override image preserveAspectRatio in images");
    526 
    527  if (!HasViewBox() && ShouldSynthesizeViewBox()) {
    528    // My non-<svg:image> clients will want to paint me with a synthesized
    529    // viewBox, but my <svg:image> client that just painted me did NOT
    530    // use that.  Need to tell ourselves to flush our transform.
    531    mImageNeedsTransformInvalidation = true;
    532  }
    533 
    534  if (ClearPreserveAspectRatioProperty()) {
    535    mImageNeedsTransformInvalidation = true;
    536  }
    537 }
    538 
    539 bool SVGSVGElement::SetPreserveAspectRatioProperty(
    540    const SVGPreserveAspectRatio& aPAR) {
    541  SVGPreserveAspectRatio* pAROverridePtr = new SVGPreserveAspectRatio(aPAR);
    542  nsresult rv =
    543      SetProperty(nsGkAtoms::overridePreserveAspectRatio, pAROverridePtr,
    544                  nsINode::DeleteProperty<SVGPreserveAspectRatio>, true);
    545  MOZ_ASSERT(rv != NS_PROPTABLE_PROP_OVERWRITTEN,
    546             "Setting override value when it's already set...?");
    547 
    548  if (MOZ_UNLIKELY(NS_FAILED(rv))) {
    549    // property-insertion failed (e.g. OOM in property-table code)
    550    delete pAROverridePtr;
    551    return false;
    552  }
    553  return true;
    554 }
    555 
    556 const SVGPreserveAspectRatio* SVGSVGElement::GetPreserveAspectRatioProperty()
    557    const {
    558  void* valPtr = GetProperty(nsGkAtoms::overridePreserveAspectRatio);
    559  if (valPtr) {
    560    return static_cast<SVGPreserveAspectRatio*>(valPtr);
    561  }
    562  return nullptr;
    563 }
    564 
    565 bool SVGSVGElement::ClearPreserveAspectRatioProperty() {
    566  void* valPtr = TakeProperty(nsGkAtoms::overridePreserveAspectRatio);
    567  bool didHaveProperty = !!valPtr;
    568  delete static_cast<SVGPreserveAspectRatio*>(valPtr);
    569  return didHaveProperty;
    570 }
    571 
    572 SVGPreserveAspectRatio SVGSVGElement::GetPreserveAspectRatioWithOverride()
    573    const {
    574  Document* doc = GetUncomposedDoc();
    575  if (doc && doc->IsBeingUsedAsImage()) {
    576    const SVGPreserveAspectRatio* pAROverridePtr =
    577        GetPreserveAspectRatioProperty();
    578    if (pAROverridePtr) {
    579      return *pAROverridePtr;
    580    }
    581  }
    582 
    583  SVGViewElement* viewElement = GetCurrentViewElement();
    584 
    585  // This check is equivalent to "!HasViewBox() &&
    586  // ShouldSynthesizeViewBox()". We're just holding onto the viewElement that
    587  // HasViewBox() would look up, so that we don't have to look it up again
    588  // later.
    589  if (!((viewElement && viewElement->mViewBox.HasRect()) ||
    590        (mSVGView && mSVGView->mViewBox.HasRect()) || mViewBox.HasRect()) &&
    591      ShouldSynthesizeViewBox()) {
    592    // If we're synthesizing a viewBox, use preserveAspectRatio="none";
    593    return SVGPreserveAspectRatio(SVG_PRESERVEASPECTRATIO_NONE,
    594                                  SVG_MEETORSLICE_SLICE);
    595  }
    596 
    597  if (viewElement && viewElement->mPreserveAspectRatio.IsExplicitlySet()) {
    598    return viewElement->mPreserveAspectRatio.GetAnimValue();
    599  }
    600  if (mSVGView && mSVGView->mPreserveAspectRatio.IsExplicitlySet()) {
    601    return mSVGView->mPreserveAspectRatio.GetAnimValue();
    602  }
    603  return mPreserveAspectRatio.GetAnimValue();
    604 }
    605 
    606 SVGViewElement* SVGSVGElement::GetCurrentViewElement() const {
    607  if (mCurrentViewID) {
    608    // XXXsmaug It is unclear how this should work in case we're in Shadow DOM.
    609    Document* doc = GetUncomposedDoc();
    610    if (doc) {
    611      Element* element = doc->GetElementById(*mCurrentViewID);
    612      return SVGViewElement::FromNodeOrNull(element);
    613    }
    614  }
    615  return nullptr;
    616 }
    617 
    618 const SVGAnimatedViewBox& SVGSVGElement::GetViewBoxInternal() const {
    619  SVGViewElement* viewElement = GetCurrentViewElement();
    620 
    621  if (viewElement && viewElement->mViewBox.HasRect()) {
    622    return viewElement->mViewBox;
    623  }
    624  if (mSVGView && mSVGView->mViewBox.HasRect()) {
    625    return mSVGView->mViewBox;
    626  }
    627 
    628  return mViewBox;
    629 }
    630 
    631 }  // namespace mozilla::dom