Coverage Report

Created: 2018-09-25 14:53

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