nsSVGUseFrame.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. // Keep in (case-insensitive) order:
  6. #include "nsIAnonymousContentCreator.h"
  7. #include "nsSVGEffects.h"
  8. #include "nsSVGGFrame.h"
  9. #include "mozilla/dom/SVGUseElement.h"
  10. #include "nsContentList.h"
  11. using namespace mozilla::dom;
  12. class nsSVGUseFrame : public nsSVGGFrame
  13. , public nsIAnonymousContentCreator
  14. {
  15. friend nsIFrame*
  16. NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
  17. protected:
  18. explicit nsSVGUseFrame(nsStyleContext* aContext)
  19. : nsSVGGFrame(aContext)
  20. , mHasValidDimensions(true)
  21. {}
  22. public:
  23. NS_DECL_QUERYFRAME
  24. NS_DECL_FRAMEARENA_HELPERS
  25. // nsIFrame interface:
  26. virtual void Init(nsIContent* aContent,
  27. nsContainerFrame* aParent,
  28. nsIFrame* aPrevInFlow) override;
  29. virtual nsresult AttributeChanged(int32_t aNameSpaceID,
  30. nsIAtom* aAttribute,
  31. int32_t aModType) override;
  32. virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
  33. /**
  34. * Get the "type" of the frame
  35. *
  36. * @see nsGkAtoms::svgUseFrame
  37. */
  38. virtual nsIAtom* GetType() const override;
  39. virtual bool IsLeaf() const override;
  40. #ifdef DEBUG_FRAME_DUMP
  41. virtual nsresult GetFrameName(nsAString& aResult) const override
  42. {
  43. return MakeFrameName(NS_LITERAL_STRING("SVGUse"), aResult);
  44. }
  45. #endif
  46. // nsISVGChildFrame interface:
  47. virtual void ReflowSVG() override;
  48. virtual void NotifySVGChanged(uint32_t aFlags) override;
  49. // nsIAnonymousContentCreator
  50. virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) override;
  51. virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
  52. uint32_t aFilter) override;
  53. private:
  54. bool mHasValidDimensions;
  55. };
  56. //----------------------------------------------------------------------
  57. // Implementation
  58. nsIFrame*
  59. NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  60. {
  61. return new (aPresShell) nsSVGUseFrame(aContext);
  62. }
  63. NS_IMPL_FRAMEARENA_HELPERS(nsSVGUseFrame)
  64. nsIAtom *
  65. nsSVGUseFrame::GetType() const
  66. {
  67. return nsGkAtoms::svgUseFrame;
  68. }
  69. //----------------------------------------------------------------------
  70. // nsQueryFrame methods
  71. NS_QUERYFRAME_HEAD(nsSVGUseFrame)
  72. NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
  73. NS_QUERYFRAME_TAIL_INHERITING(nsSVGGFrame)
  74. //----------------------------------------------------------------------
  75. // nsIFrame methods:
  76. void
  77. nsSVGUseFrame::Init(nsIContent* aContent,
  78. nsContainerFrame* aParent,
  79. nsIFrame* aPrevInFlow)
  80. {
  81. NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::use),
  82. "Content is not an SVG use!");
  83. mHasValidDimensions =
  84. static_cast<SVGUseElement*>(aContent)->HasValidDimensions();
  85. nsSVGGFrame::Init(aContent, aParent, aPrevInFlow);
  86. }
  87. nsresult
  88. nsSVGUseFrame::AttributeChanged(int32_t aNameSpaceID,
  89. nsIAtom* aAttribute,
  90. int32_t aModType)
  91. {
  92. SVGUseElement *useElement = static_cast<SVGUseElement*>(mContent);
  93. if (aNameSpaceID == kNameSpaceID_None) {
  94. if (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y) {
  95. // make sure our cached transform matrix gets (lazily) updated
  96. mCanvasTM = nullptr;
  97. nsLayoutUtils::PostRestyleEvent(
  98. useElement, nsRestyleHint(0),
  99. nsChangeHint_InvalidateRenderingObservers);
  100. nsSVGUtils::ScheduleReflowSVG(this);
  101. nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED);
  102. } else if (aAttribute == nsGkAtoms::width ||
  103. aAttribute == nsGkAtoms::height) {
  104. bool invalidate = false;
  105. if (mHasValidDimensions != useElement->HasValidDimensions()) {
  106. mHasValidDimensions = !mHasValidDimensions;
  107. invalidate = true;
  108. }
  109. if (useElement->OurWidthAndHeightAreUsed()) {
  110. invalidate = true;
  111. useElement->SyncWidthOrHeight(aAttribute);
  112. }
  113. if (invalidate) {
  114. nsLayoutUtils::PostRestyleEvent(
  115. useElement, nsRestyleHint(0),
  116. nsChangeHint_InvalidateRenderingObservers);
  117. nsSVGUtils::ScheduleReflowSVG(this);
  118. }
  119. }
  120. }
  121. if ((aNameSpaceID == kNameSpaceID_XLink ||
  122. aNameSpaceID == kNameSpaceID_None) &&
  123. aAttribute == nsGkAtoms::href) {
  124. // we're changing our nature, clear out the clone information
  125. nsLayoutUtils::PostRestyleEvent(
  126. useElement, nsRestyleHint(0),
  127. nsChangeHint_InvalidateRenderingObservers);
  128. nsSVGUtils::ScheduleReflowSVG(this);
  129. useElement->mOriginal = nullptr;
  130. useElement->UnlinkSource();
  131. useElement->TriggerReclone();
  132. }
  133. return nsSVGGFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
  134. }
  135. void
  136. nsSVGUseFrame::DestroyFrom(nsIFrame* aDestructRoot)
  137. {
  138. RefPtr<SVGUseElement> use = static_cast<SVGUseElement*>(mContent);
  139. nsSVGGFrame::DestroyFrom(aDestructRoot);
  140. use->DestroyAnonymousContent();
  141. }
  142. bool
  143. nsSVGUseFrame::IsLeaf() const
  144. {
  145. return true;
  146. }
  147. //----------------------------------------------------------------------
  148. // nsISVGChildFrame methods
  149. void
  150. nsSVGUseFrame::ReflowSVG()
  151. {
  152. // We only handle x/y offset here, since any width/height that is in force is
  153. // handled by the nsSVGOuterSVGFrame for the anonymous <svg> that will be
  154. // created for that purpose.
  155. float x, y;
  156. static_cast<SVGUseElement*>(mContent)->
  157. GetAnimatedLengthValues(&x, &y, nullptr);
  158. mRect.MoveTo(nsLayoutUtils::RoundGfxRectToAppRect(
  159. gfxRect(x, y, 0.0, 0.0),
  160. PresContext()->AppUnitsPerCSSPixel()).TopLeft());
  161. // If we have a filter, we need to invalidate ourselves because filter
  162. // output can change even if none of our descendants need repainting.
  163. if (StyleEffects()->HasFilters()) {
  164. InvalidateFrame();
  165. }
  166. nsSVGGFrame::ReflowSVG();
  167. }
  168. void
  169. nsSVGUseFrame::NotifySVGChanged(uint32_t aFlags)
  170. {
  171. if (aFlags & COORD_CONTEXT_CHANGED &&
  172. !(aFlags & TRANSFORM_CHANGED)) {
  173. // Coordinate context changes affect mCanvasTM if we have a
  174. // percentage 'x' or 'y'
  175. SVGUseElement *use = static_cast<SVGUseElement*>(mContent);
  176. if (use->mLengthAttributes[SVGUseElement::ATTR_X].IsPercentage() ||
  177. use->mLengthAttributes[SVGUseElement::ATTR_Y].IsPercentage()) {
  178. aFlags |= TRANSFORM_CHANGED;
  179. // Ancestor changes can't affect how we render from the perspective of
  180. // any rendering observers that we may have, so we don't need to
  181. // invalidate them. We also don't need to invalidate ourself, since our
  182. // changed ancestor will have invalidated its entire area, which includes
  183. // our area.
  184. // For perf reasons we call this before calling NotifySVGChanged() below.
  185. nsSVGUtils::ScheduleReflowSVG(this);
  186. }
  187. }
  188. // We don't remove the TRANSFORM_CHANGED flag here if we have a viewBox or
  189. // non-percentage width/height, since if they're set then they are cloned to
  190. // an anonymous child <svg>, and its nsSVGInnerSVGFrame will do that.
  191. nsSVGGFrame::NotifySVGChanged(aFlags);
  192. }
  193. //----------------------------------------------------------------------
  194. // nsIAnonymousContentCreator methods:
  195. nsresult
  196. nsSVGUseFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
  197. {
  198. SVGUseElement *use = static_cast<SVGUseElement*>(mContent);
  199. nsIContent* clone = use->CreateAnonymousContent();
  200. nsLayoutUtils::PostRestyleEvent(
  201. use, nsRestyleHint(0), nsChangeHint_InvalidateRenderingObservers);
  202. if (!clone)
  203. return NS_ERROR_FAILURE;
  204. if (!aElements.AppendElement(clone))
  205. return NS_ERROR_OUT_OF_MEMORY;
  206. return NS_OK;
  207. }
  208. void
  209. nsSVGUseFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
  210. uint32_t aFilter)
  211. {
  212. SVGUseElement *use = static_cast<SVGUseElement*>(mContent);
  213. nsIContent* clone = use->GetAnonymousContent();
  214. if (clone) {
  215. aElements.AppendElement(clone);
  216. }
  217. }