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