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 #ifndef mozilla_dom_SVGSVGElement_h 8 #define mozilla_dom_SVGSVGElement_h 9 10 #include "mozilla/dom/FromParser.h" 11 #include "nsAutoPtr.h" 12 #include "nsIContentInlines.h" 13 #include "nsISVGPoint.h" 14 #include "nsSVGEnum.h" 15 #include "nsSVGLength2.h" 16 #include "SVGGraphicsElement.h" 17 #include "SVGImageContext.h" 18 #include "nsSVGViewBox.h" 19 #include "SVGPreserveAspectRatio.h" 20 #include "SVGAnimatedPreserveAspectRatio.h" 21 #include "mozilla/Attributes.h" 22 23 nsresult NS_NewSVGSVGElement(nsIContent **aResult, 24 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, 25 mozilla::dom::FromParser aFromParser); 26 27 class nsSMILTimeContainer; 28 class nsSVGOuterSVGFrame; 29 class nsSVGInnerSVGFrame; 30 31 namespace mozilla { 32 class AutoSVGRenderingState; 33 class DOMSVGAnimatedPreserveAspectRatio; 34 class DOMSVGLength; 35 class DOMSVGNumber; 36 class EventChainPreVisitor; 37 class SVGFragmentIdentifier; 38 class AutoSVGViewHandler; 39 40 namespace dom { 41 class SVGAngle; 42 class SVGAnimatedRect; 43 class SVGMatrix; 44 class SVGTransform; 45 class SVGViewElement; 46 class SVGIRect; 47 48 class SVGSVGElement; 49 50 class DOMSVGTranslatePoint final : public nsISVGPoint { 51 public: DOMSVGTranslatePoint(SVGPoint * aPt,SVGSVGElement * aElement)52 DOMSVGTranslatePoint(SVGPoint* aPt, SVGSVGElement *aElement) 53 : nsISVGPoint(aPt, true), mElement(aElement) {} 54 DOMSVGTranslatePoint(DOMSVGTranslatePoint * aPt)55 explicit DOMSVGTranslatePoint(DOMSVGTranslatePoint* aPt) 56 : nsISVGPoint(&aPt->mPt, true), mElement(aPt->mElement) {} 57 58 NS_DECL_ISUPPORTS_INHERITED 59 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DOMSVGTranslatePoint, nsISVGPoint) 60 61 virtual DOMSVGPoint* Copy() override; 62 63 // WebIDL X()64 virtual float X() override { return mPt.GetX(); } Y()65 virtual float Y() override { return mPt.GetY(); } 66 virtual void SetX(float aValue, ErrorResult& rv) override; 67 virtual void SetY(float aValue, ErrorResult& rv) override; 68 virtual already_AddRefed<nsISVGPoint> MatrixTransform(SVGMatrix& matrix) override; 69 70 virtual nsISupports* GetParentObject() override; 71 72 RefPtr<SVGSVGElement> mElement; 73 74 private: ~DOMSVGTranslatePoint()75 ~DOMSVGTranslatePoint() {} 76 }; 77 78 class svgFloatSize { 79 public: svgFloatSize(float aWidth,float aHeight)80 svgFloatSize(float aWidth, float aHeight) 81 : width(aWidth) 82 , height(aHeight) 83 {} 84 bool operator!=(const svgFloatSize& rhs) { 85 return width != rhs.width || height != rhs.height; 86 } 87 float width; 88 float height; 89 }; 90 91 // Stores svgView arguments of SVG fragment identifiers. 92 class SVGView { 93 friend class mozilla::AutoSVGViewHandler; 94 friend class mozilla::dom::SVGSVGElement; 95 public: 96 SVGView(); 97 98 private: 99 nsSVGEnum mZoomAndPan; 100 nsSVGViewBox mViewBox; 101 SVGAnimatedPreserveAspectRatio mPreserveAspectRatio; 102 nsAutoPtr<nsSVGAnimatedTransformList> mTransforms; 103 }; 104 105 typedef SVGGraphicsElement SVGSVGElementBase; 106 107 class SVGSVGElement final : public SVGSVGElementBase 108 { 109 friend class ::nsSVGOuterSVGFrame; 110 friend class ::nsSVGInnerSVGFrame; 111 friend class mozilla::dom::SVGView; 112 friend class mozilla::SVGFragmentIdentifier; 113 friend class mozilla::AutoSVGViewHandler; 114 friend class mozilla::AutoSVGRenderingState; 115 116 SVGSVGElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo, 117 FromParser aFromParser); 118 virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override; 119 120 friend nsresult (::NS_NewSVGSVGElement(nsIContent **aResult, 121 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, 122 mozilla::dom::FromParser aFromParser)); 123 124 ~SVGSVGElement(); 125 126 public: 127 // interfaces: 128 NS_DECL_ISUPPORTS_INHERITED 129 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGSVGElement, SVGSVGElementBase) 130 131 /** 132 * For use by zoom controls to allow currentScale, currentTranslate.x and 133 * currentTranslate.y to be set by a single operation that dispatches a 134 * single SVGZoom event (instead of one SVGZoom and two SVGScroll events). 135 */ 136 void SetCurrentScaleTranslate(float s, float x, float y); 137 138 /** 139 * Retrieve the value of currentScale and currentTranslate. 140 */ GetCurrentTranslate()141 const SVGPoint& GetCurrentTranslate() { return mCurrentTranslate; } GetCurrentScale()142 float GetCurrentScale() { return mCurrentScale; } 143 144 /** 145 * Retrieve the value of currentScale, currentTranslate.x or 146 * currentTranslate.y prior to the last change made to any one of them. 147 */ GetPreviousTranslate()148 const SVGPoint& GetPreviousTranslate() { return mPreviousTranslate; } GetPreviousScale()149 float GetPreviousScale() { return mPreviousScale; } 150 151 nsSMILTimeContainer* GetTimedDocumentRoot(); 152 153 // nsIContent interface 154 NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const override; 155 virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; 156 157 virtual bool IsEventAttributeName(nsIAtom* aName) override; 158 159 // nsSVGElement specializations: 160 virtual gfxMatrix PrependLocalTransformsTo( 161 const gfxMatrix &aMatrix, 162 SVGTransformTypes aWhich = eAllTransforms) const override; 163 virtual nsSVGAnimatedTransformList* 164 GetAnimatedTransformList(uint32_t aFlags = 0) override; 165 virtual bool HasValidDimensions() const override; 166 167 // SVGSVGElement methods: 168 float GetLength(uint8_t mCtxType); 169 170 // public helpers: 171 172 /** 173 * Returns -1 if the width/height is a percentage, else returns the user unit 174 * length clamped to fit in a int32_t. 175 * XXX see bug 1112533 comment 3 - we should fix drawImage so that we can 176 * change these methods to make zero the error flag for percentages. 177 */ 178 int32_t GetIntrinsicWidth(); 179 int32_t GetIntrinsicHeight(); 180 181 /** 182 * Returns true if this element has a base/anim value for its "viewBox" 183 * attribute that defines a viewBox rectangle with finite values, or 184 * if there is a view element overriding this element's viewBox and it 185 * has a valid viewBox. 186 * 187 * Note that this does not check whether we need to synthesize a viewBox, 188 * so you must call ShouldSynthesizeViewBox() if you need to check that too. 189 * 190 * Note also that this method does not pay attention to whether the width or 191 * height values of the viewBox rect are positive! 192 */ 193 bool HasViewBoxRect() const; 194 195 /** 196 * Returns true if we should synthesize a viewBox for ourselves (that is, if 197 * we're the root element in an image document, and we're not currently being 198 * painted for an <svg:image> element). 199 * 200 * Only call this method if HasViewBoxRect() returns false. 201 */ 202 bool ShouldSynthesizeViewBox() const; 203 HasViewBoxOrSyntheticViewBox()204 bool HasViewBoxOrSyntheticViewBox() const { 205 return HasViewBoxRect() || ShouldSynthesizeViewBox(); 206 } 207 208 gfx::Matrix GetViewBoxTransform() const; 209 HasChildrenOnlyTransform()210 bool HasChildrenOnlyTransform() const { 211 return mHasChildrenOnlyTransform; 212 } 213 214 void UpdateHasChildrenOnlyTransform(); 215 216 enum ChildrenOnlyTransformChangedFlags { 217 eDuringReflow = 1 218 }; 219 220 /** 221 * This method notifies the style system that the overflow rects of our 222 * immediate childrens' frames need to be updated. It is called by our own 223 * frame when changes (e.g. to currentScale) cause our children-only 224 * transform to change. 225 * 226 * The reason we have this method instead of overriding 227 * GetAttributeChangeHint is because we need to act on non-attribute (e.g. 228 * currentScale) changes in addition to attribute (e.g. viewBox) changes. 229 */ 230 void ChildrenOnlyTransformChanged(uint32_t aFlags = 0); 231 232 // This services any pending notifications for the transform on on this root 233 // <svg> node needing to be recalculated. (Only applicable in 234 // SVG-as-an-image documents.) 235 virtual void FlushImageTransformInvalidation(); 236 237 virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override; 238 239 // Returns true IFF our attributes are currently overridden by a <view> 240 // element and that element's ID matches the passed-in string. IsOverriddenBy(const nsAString & aViewID)241 bool IsOverriddenBy(const nsAString &aViewID) const { 242 return mCurrentViewID && mCurrentViewID->Equals(aViewID); 243 } 244 GetViewportSize()245 svgFloatSize GetViewportSize() const { 246 return svgFloatSize(mViewportWidth, mViewportHeight); 247 } 248 SetViewportSize(const svgFloatSize & aSize)249 void SetViewportSize(const svgFloatSize& aSize) { 250 mViewportWidth = aSize.width; 251 mViewportHeight = aSize.height; 252 } 253 254 // WebIDL 255 already_AddRefed<SVGAnimatedLength> X(); 256 already_AddRefed<SVGAnimatedLength> Y(); 257 already_AddRefed<SVGAnimatedLength> Width(); 258 already_AddRefed<SVGAnimatedLength> Height(); 259 float PixelUnitToMillimeterX(); 260 float PixelUnitToMillimeterY(); 261 float ScreenPixelToMillimeterX(); 262 float ScreenPixelToMillimeterY(); 263 bool UseCurrentView(); 264 float CurrentScale(); 265 void SetCurrentScale(float aCurrentScale); 266 already_AddRefed<nsISVGPoint> CurrentTranslate(); 267 void SetCurrentTranslate(float x, float y); 268 uint32_t SuspendRedraw(uint32_t max_wait_milliseconds); 269 void UnsuspendRedraw(uint32_t suspend_handle_id); 270 void UnsuspendRedrawAll(); 271 void ForceRedraw(); 272 void PauseAnimations(); 273 void UnpauseAnimations(); 274 bool AnimationsPaused(); 275 float GetCurrentTime(); 276 void SetCurrentTime(float seconds); 277 void DeselectAll(); 278 already_AddRefed<DOMSVGNumber> CreateSVGNumber(); 279 already_AddRefed<DOMSVGLength> CreateSVGLength(); 280 already_AddRefed<SVGAngle> CreateSVGAngle(); 281 already_AddRefed<nsISVGPoint> CreateSVGPoint(); 282 already_AddRefed<SVGMatrix> CreateSVGMatrix(); 283 already_AddRefed<SVGIRect> CreateSVGRect(); 284 already_AddRefed<SVGTransform> CreateSVGTransform(); 285 already_AddRefed<SVGTransform> CreateSVGTransformFromMatrix(SVGMatrix& matrix); 286 using nsINode::GetElementById; // This does what we want 287 already_AddRefed<SVGAnimatedRect> ViewBox(); 288 already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio(); 289 uint16_t ZoomAndPan(); 290 void SetZoomAndPan(uint16_t aZoomAndPan, ErrorResult& rv); 291 virtual nsSVGViewBox* GetViewBox() override; 292 293 private: 294 // nsSVGElement overrides 295 296 virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, 297 nsIContent* aBindingParent, 298 bool aCompileEventHandlers) override; 299 virtual void UnbindFromTree(bool aDeep, bool aNullParent) override; 300 301 // implementation helpers: 302 303 SVGViewElement* GetCurrentViewElement() const; 304 305 // Methods for <image> elements to override my "PreserveAspectRatio" value. 306 // These are private so that only our friends (AutoSVGRenderingState in 307 // particular) have access. 308 void SetImageOverridePreserveAspectRatio(const SVGPreserveAspectRatio& aPAR); 309 void ClearImageOverridePreserveAspectRatio(); 310 311 // Set/Clear properties to hold old version of preserveAspectRatio 312 // when it's being overridden by an <image> element that we are inside of. 313 bool SetPreserveAspectRatioProperty(const SVGPreserveAspectRatio& aPAR); 314 const SVGPreserveAspectRatio* GetPreserveAspectRatioProperty() const; 315 bool ClearPreserveAspectRatioProperty(); 316 317 void SetIsPaintingForSVGImageElement(bool aIsPaintingSVGImageElement); 318 IsRoot()319 bool IsRoot() const { 320 NS_ASSERTION((IsInUncomposedDoc() && !GetParent()) == 321 (OwnerDoc() && (OwnerDoc()->GetRootElement() == this)), 322 "Can't determine if we're root"); 323 return IsInUncomposedDoc() && !GetParent(); 324 } 325 326 /** 327 * Returns true if this is an SVG <svg> element that is the child of 328 * another non-foreignObject SVG element. 329 */ IsInner()330 bool IsInner() const { 331 const nsIContent *parent = GetFlattenedTreeParent(); 332 return parent && parent->IsSVGElement() && 333 !parent->IsSVGElement(nsGkAtoms::foreignObject); 334 } 335 336 /* 337 * While binding to the tree we need to determine if we will be the outermost 338 * <svg> element _before_ the children are bound (as they want to know what 339 * timed document root to register with) and therefore _before_ our parent is 340 * set (both actions are performed by Element::BindToTree) so we 341 * can't use GetOwnerSVGElement() as it relies on GetParent(). This code is 342 * basically a simplified version of GetOwnerSVGElement that uses the parent 343 * parameters passed in instead. 344 */ 345 bool WillBeOutermostSVG(nsIContent* aParent, 346 nsIContent* aBindingParent) const; 347 348 // invalidate viewbox -> viewport xform & inform frames 349 void InvalidateTransformNotifyFrame(); 350 351 // Returns true if we have at least one of the following: 352 // - a (valid or invalid) value for the preserveAspectRatio attribute 353 // - a SMIL-animated value for the preserveAspectRatio attribute 354 bool HasPreserveAspectRatio(); 355 356 /** 357 * Returns the explicit viewBox rect, if specified, or else a synthesized 358 * viewBox, if appropriate, or else a viewBox matching the dimensions of the 359 * SVG viewport. 360 */ 361 nsSVGViewBoxRect GetViewBoxWithSynthesis( 362 float aViewportWidth, float aViewportHeight) const; 363 /** 364 * Returns the explicit or default preserveAspectRatio, unless we're 365 * synthesizing a viewBox, in which case it returns the "none" value. 366 */ 367 SVGPreserveAspectRatio GetPreserveAspectRatioWithOverride() const; 368 369 virtual LengthAttributesInfo GetLengthInfo() override; 370 371 enum { ATTR_X, ATTR_Y, ATTR_WIDTH, ATTR_HEIGHT }; 372 nsSVGLength2 mLengthAttributes[4]; 373 static LengthInfo sLengthInfo[4]; 374 375 virtual EnumAttributesInfo GetEnumInfo() override; 376 377 enum { ZOOMANDPAN }; 378 nsSVGEnum mEnumAttributes[1]; 379 static nsSVGEnumMapping sZoomAndPanMap[]; 380 static EnumInfo sEnumInfo[1]; 381 382 virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio() override; 383 384 nsSVGViewBox mViewBox; 385 SVGAnimatedPreserveAspectRatio mPreserveAspectRatio; 386 387 // mCurrentViewID and mSVGView are mutually exclusive; we can have 388 // at most one non-null. 389 nsAutoPtr<nsString> mCurrentViewID; 390 nsAutoPtr<SVGView> mSVGView; 391 392 // The size of the rectangular SVG viewport into which we render. This is 393 // not (necessarily) the same as the content area. See: 394 // 395 // http://www.w3.org/TR/SVG11/coords.html#ViewportSpace 396 // 397 // XXXjwatt Currently only used for outer <svg>, but maybe we could use -1 to 398 // flag this as an inner <svg> to save the overhead of GetCtx calls? 399 // XXXjwatt our frame should probably reset these when it's destroyed. 400 float mViewportWidth, mViewportHeight; 401 402 // The time container for animations within this SVG document fragment. Set 403 // for all outermost <svg> elements (not nested <svg> elements). 404 nsAutoPtr<nsSMILTimeContainer> mTimedDocumentRoot; 405 406 // zoom and pan 407 // IMPORTANT: see the comment in RecordCurrentScaleTranslate before writing 408 // code to change any of these! 409 SVGPoint mCurrentTranslate; 410 float mCurrentScale; 411 SVGPoint mPreviousTranslate; 412 float mPreviousScale; 413 414 // For outermost <svg> elements created from parsing, animation is started by 415 // the onload event in accordance with the SVG spec, but for <svg> elements 416 // created by script or promoted from inner <svg> to outermost <svg> we need 417 // to manually kick off animation when they are bound to the tree. 418 bool mStartAnimationOnBindToTree; 419 bool mImageNeedsTransformInvalidation; 420 bool mIsPaintingSVGImageElement; 421 bool mHasChildrenOnlyTransform; 422 }; 423 424 } // namespace dom 425 426 // Helper class to automatically manage temporary changes to an SVG document's 427 // state for rendering purposes. 428 class MOZ_RAII AutoSVGRenderingState 429 { 430 public: AutoSVGRenderingState(const Maybe<SVGImageContext> & aSVGContext,float aFrameTime,dom::SVGSVGElement * aRootElem MOZ_GUARD_OBJECT_NOTIFIER_PARAM)431 AutoSVGRenderingState(const Maybe<SVGImageContext>& aSVGContext, 432 float aFrameTime, 433 dom::SVGSVGElement* aRootElem 434 MOZ_GUARD_OBJECT_NOTIFIER_PARAM) 435 : mHaveOverrides(aSVGContext.isSome() && 436 aSVGContext->GetPreserveAspectRatio().isSome()) 437 , mRootElem(aRootElem) 438 { 439 MOZ_GUARD_OBJECT_NOTIFIER_INIT; 440 MOZ_ASSERT(mRootElem, "No SVG node to manage?"); 441 if (mHaveOverrides) { 442 // Override preserveAspectRatio in our helper document. 443 // XXXdholbert We should technically be overriding the helper doc's clip 444 // and overflow properties here, too. See bug 272288 comment 36. 445 mRootElem->SetImageOverridePreserveAspectRatio( 446 *aSVGContext->GetPreserveAspectRatio()); 447 mRootElem->SetIsPaintingForSVGImageElement( 448 aSVGContext->IsPaintingForSVGImageElement()); 449 } 450 451 mOriginalTime = mRootElem->GetCurrentTime(); 452 mRootElem->SetCurrentTime(aFrameTime); // Does nothing if there's no change. 453 } 454 ~AutoSVGRenderingState()455 ~AutoSVGRenderingState() 456 { 457 mRootElem->SetCurrentTime(mOriginalTime); 458 if (mHaveOverrides) { 459 mRootElem->ClearImageOverridePreserveAspectRatio(); 460 mRootElem->SetIsPaintingForSVGImageElement(false); 461 } 462 } 463 464 private: 465 const bool mHaveOverrides; 466 float mOriginalTime; 467 const RefPtr<dom::SVGSVGElement> mRootElem; 468 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER 469 }; 470 471 } // namespace mozilla 472 473 #endif // SVGSVGElement_h 474