nsSVGSwitchFrame.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  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 "gfxRect.h"
  7. #include "nsSVGEffects.h"
  8. #include "nsSVGGFrame.h"
  9. #include "mozilla/dom/SVGSwitchElement.h"
  10. #include "nsSVGUtils.h"
  11. using namespace mozilla::gfx;
  12. using namespace mozilla::image;
  13. class nsSVGSwitchFrame : public nsSVGGFrame
  14. {
  15. friend nsIFrame*
  16. NS_NewSVGSwitchFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
  17. protected:
  18. explicit nsSVGSwitchFrame(nsStyleContext* aContext)
  19. : nsSVGGFrame(aContext) {}
  20. public:
  21. NS_DECL_FRAMEARENA_HELPERS
  22. #ifdef DEBUG
  23. virtual void Init(nsIContent* aContent,
  24. nsContainerFrame* aParent,
  25. nsIFrame* aPrevInFlow) override;
  26. #endif
  27. /**
  28. * Get the "type" of the frame
  29. *
  30. * @see nsGkAtoms::svgSwitchFrame
  31. */
  32. virtual nsIAtom* GetType() const override;
  33. #ifdef DEBUG_FRAME_DUMP
  34. virtual nsresult GetFrameName(nsAString& aResult) const override
  35. {
  36. return MakeFrameName(NS_LITERAL_STRING("SVGSwitch"), aResult);
  37. }
  38. #endif
  39. virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
  40. const nsDisplayListSet& aLists) override;
  41. // nsISVGChildFrame interface:
  42. virtual DrawResult PaintSVG(gfxContext& aContext,
  43. const gfxMatrix& aTransform,
  44. const nsIntRect* aDirtyRect = nullptr) override;
  45. nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
  46. nsRect GetCoveredRegion() override;
  47. virtual void ReflowSVG() override;
  48. virtual SVGBBox GetBBoxContribution(const Matrix &aToBBoxUserspace,
  49. uint32_t aFlags) override;
  50. private:
  51. nsIFrame *GetActiveChildFrame();
  52. };
  53. //----------------------------------------------------------------------
  54. // Implementation
  55. nsIFrame*
  56. NS_NewSVGSwitchFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
  57. {
  58. return new (aPresShell) nsSVGSwitchFrame(aContext);
  59. }
  60. NS_IMPL_FRAMEARENA_HELPERS(nsSVGSwitchFrame)
  61. #ifdef DEBUG
  62. void
  63. nsSVGSwitchFrame::Init(nsIContent* aContent,
  64. nsContainerFrame* aParent,
  65. nsIFrame* aPrevInFlow)
  66. {
  67. NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::svgSwitch),
  68. "Content is not an SVG switch");
  69. nsSVGGFrame::Init(aContent, aParent, aPrevInFlow);
  70. }
  71. #endif /* DEBUG */
  72. nsIAtom *
  73. nsSVGSwitchFrame::GetType() const
  74. {
  75. return nsGkAtoms::svgSwitchFrame;
  76. }
  77. void
  78. nsSVGSwitchFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
  79. const nsDisplayListSet& aLists)
  80. {
  81. nsIFrame* kid = GetActiveChildFrame();
  82. if (kid) {
  83. BuildDisplayListForChild(aBuilder, kid, aLists);
  84. }
  85. }
  86. DrawResult
  87. nsSVGSwitchFrame::PaintSVG(gfxContext& aContext,
  88. const gfxMatrix& aTransform,
  89. const nsIntRect* aDirtyRect)
  90. {
  91. NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
  92. (mState & NS_FRAME_IS_NONDISPLAY),
  93. "If display lists are enabled, only painting of non-display "
  94. "SVG should take this code path");
  95. if (StyleEffects()->mOpacity == 0.0)
  96. return DrawResult::SUCCESS;
  97. DrawResult result = DrawResult::SUCCESS;
  98. nsIFrame *kid = GetActiveChildFrame();
  99. if (kid) {
  100. gfxMatrix tm = aTransform;
  101. if (kid->GetContent()->IsSVGElement()) {
  102. tm = static_cast<nsSVGElement*>(kid->GetContent())->
  103. PrependLocalTransformsTo(tm, eUserSpaceToParent);
  104. }
  105. result = nsSVGUtils::PaintFrameWithEffects(kid, aContext, tm, aDirtyRect);
  106. }
  107. return result;
  108. }
  109. nsIFrame*
  110. nsSVGSwitchFrame::GetFrameForPoint(const gfxPoint& aPoint)
  111. {
  112. NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
  113. (mState & NS_FRAME_IS_NONDISPLAY),
  114. "If display lists are enabled, only hit-testing of non-display "
  115. "SVG should take this code path");
  116. nsIFrame *kid = GetActiveChildFrame();
  117. nsISVGChildFrame* svgFrame = do_QueryFrame(kid);
  118. if (svgFrame) {
  119. // Transform the point from our SVG user space to our child's.
  120. gfxPoint point = aPoint;
  121. gfxMatrix m =
  122. static_cast<const nsSVGElement*>(mContent)->
  123. PrependLocalTransformsTo(gfxMatrix(), eChildToUserSpace);
  124. m = static_cast<const nsSVGElement*>(kid->GetContent())->
  125. PrependLocalTransformsTo(m, eUserSpaceToParent);
  126. if (!m.IsIdentity()) {
  127. if (!m.Invert()) {
  128. return nullptr;
  129. }
  130. point = m.Transform(point);
  131. }
  132. return svgFrame->GetFrameForPoint(point);
  133. }
  134. return nullptr;
  135. }
  136. nsRect
  137. nsSVGSwitchFrame::GetCoveredRegion()
  138. {
  139. nsRect rect;
  140. nsIFrame *kid = GetActiveChildFrame();
  141. nsISVGChildFrame* child = do_QueryFrame(kid);
  142. if (child) {
  143. rect = child->GetCoveredRegion();
  144. }
  145. return rect;
  146. }
  147. void
  148. nsSVGSwitchFrame::ReflowSVG()
  149. {
  150. NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
  151. "This call is probably a wasteful mistake");
  152. MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
  153. "ReflowSVG mechanism not designed for this");
  154. if (!nsSVGUtils::NeedsReflowSVG(this)) {
  155. return;
  156. }
  157. // If the NS_FRAME_FIRST_REFLOW bit has been removed from our parent frame,
  158. // then our outer-<svg> has previously had its initial reflow. In that case
  159. // we need to make sure that that bit has been removed from ourself _before_
  160. // recursing over our children to ensure that they know too. Otherwise, we
  161. // need to remove it _after_ recursing over our children so that they know
  162. // the initial reflow is currently underway.
  163. bool isFirstReflow = (mState & NS_FRAME_FIRST_REFLOW);
  164. bool outerSVGHasHadFirstReflow =
  165. (GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) == 0;
  166. if (outerSVGHasHadFirstReflow) {
  167. mState &= ~NS_FRAME_FIRST_REFLOW; // tell our children
  168. }
  169. nsOverflowAreas overflowRects;
  170. nsIFrame *child = GetActiveChildFrame();
  171. nsISVGChildFrame* svgChild = do_QueryFrame(child);
  172. if (svgChild) {
  173. MOZ_ASSERT(!(child->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
  174. "Check for this explicitly in the |if|, then");
  175. svgChild->ReflowSVG();
  176. // We build up our child frame overflows here instead of using
  177. // nsLayoutUtils::UnionChildOverflow since SVG frame's all use the same
  178. // frame list, and we're iterating over that list now anyway.
  179. ConsiderChildOverflow(overflowRects, child);
  180. }
  181. if (isFirstReflow) {
  182. // Make sure we have our filter property (if any) before calling
  183. // FinishAndStoreOverflow (subsequent filter changes are handled off
  184. // nsChangeHint_UpdateEffects):
  185. nsSVGEffects::UpdateEffects(this);
  186. }
  187. FinishAndStoreOverflow(overflowRects, mRect.Size());
  188. // Remove state bits after FinishAndStoreOverflow so that it doesn't
  189. // invalidate on first reflow:
  190. mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
  191. NS_FRAME_HAS_DIRTY_CHILDREN);
  192. }
  193. SVGBBox
  194. nsSVGSwitchFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace,
  195. uint32_t aFlags)
  196. {
  197. nsIFrame* kid = GetActiveChildFrame();
  198. nsISVGChildFrame* svgKid = do_QueryFrame(kid);
  199. if (svgKid) {
  200. nsIContent *content = kid->GetContent();
  201. gfxMatrix transform = ThebesMatrix(aToBBoxUserspace);
  202. if (content->IsSVGElement()) {
  203. transform = static_cast<nsSVGElement*>(content)->
  204. PrependLocalTransformsTo(transform);
  205. }
  206. return svgKid->GetBBoxContribution(ToMatrix(transform), aFlags);
  207. }
  208. return SVGBBox();
  209. }
  210. nsIFrame *
  211. nsSVGSwitchFrame::GetActiveChildFrame()
  212. {
  213. nsIContent *activeChild =
  214. static_cast<mozilla::dom::SVGSwitchElement*>(mContent)->GetActiveChild();
  215. if (activeChild) {
  216. for (nsIFrame* kid = mFrames.FirstChild(); kid;
  217. kid = kid->GetNextSibling()) {
  218. if (activeChild == kid->GetContent()) {
  219. return kid;
  220. }
  221. }
  222. }
  223. return nullptr;
  224. }