Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/svg/nsSVGViewportFrame.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
// Main header first:
8
#include "nsSVGViewportFrame.h"
9
10
// Keep others in (case-insensitive) order:
11
#include "gfx2DGlue.h"
12
#include "gfxContext.h"
13
#include "nsIFrame.h"
14
#include "nsSVGDisplayableFrame.h"
15
#include "nsSVGContainerFrame.h"
16
#include "nsSVGIntegrationUtils.h"
17
#include "mozilla/dom/SVGViewportElement.h"
18
19
using namespace mozilla;
20
using namespace mozilla::dom;
21
using namespace mozilla::gfx;
22
using namespace mozilla::image;
23
24
//----------------------------------------------------------------------
25
// nsSVGDisplayableFrame methods
26
27
void
28
nsSVGViewportFrame::PaintSVG(gfxContext& aContext,
29
                             const gfxMatrix& aTransform,
30
                             imgDrawingParams& aImgParams,
31
                             const nsIntRect *aDirtyRect)
32
0
{
33
0
  NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
34
0
               (mState & NS_FRAME_IS_NONDISPLAY),
35
0
               "If display lists are enabled, only painting of non-display "
36
0
               "SVG should take this code path");
37
0
38
0
  gfxContextAutoSaveRestore autoSR;
39
0
40
0
  if (StyleDisplay()->IsScrollableOverflow()) {
41
0
    float x, y, width, height;
42
0
    static_cast<SVGViewportElement*>(GetContent())->
43
0
      GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
44
0
45
0
    if (width <= 0 || height <= 0) {
46
0
      return;
47
0
    }
48
0
49
0
    autoSR.SetContext(&aContext);
50
0
    gfxRect clipRect =
51
0
      nsSVGUtils::GetClipRectForFrame(this, x, y, width, height);
52
0
    nsSVGUtils::SetClipRect(&aContext, aTransform, clipRect);
53
0
  }
54
0
55
0
  nsSVGDisplayContainerFrame::PaintSVG(aContext, aTransform, aImgParams,
56
0
                                       aDirtyRect);
57
0
}
58
59
void
60
nsSVGViewportFrame::ReflowSVG()
61
0
{
62
0
  // mRect must be set before FinishAndStoreOverflow is called in order
63
0
  // for our overflow areas to be clipped correctly.
64
0
  float x, y, width, height;
65
0
  static_cast<SVGViewportElement*>(GetContent())->
66
0
    GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
67
0
  mRect = nsLayoutUtils::RoundGfxRectToAppRect(
68
0
                           gfxRect(x, y, width, height),
69
0
                           AppUnitsPerCSSPixel());
70
0
71
0
  // If we have a filter, we need to invalidate ourselves because filter
72
0
  // output can change even if none of our descendants need repainting.
73
0
  if (StyleEffects()->HasFilters()) {
74
0
    InvalidateFrame();
75
0
  }
76
0
77
0
  nsSVGDisplayContainerFrame::ReflowSVG();
78
0
}
79
80
void
81
nsSVGViewportFrame::NotifySVGChanged(uint32_t aFlags)
82
0
{
83
0
  MOZ_ASSERT(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
84
0
             "Invalidation logic may need adjusting");
85
0
86
0
  if (aFlags & COORD_CONTEXT_CHANGED) {
87
0
88
0
    SVGViewportElement *svg = static_cast<SVGViewportElement*>(GetContent());
89
0
90
0
    bool xOrYIsPercentage =
91
0
      svg->mLengthAttributes[SVGViewportElement::ATTR_X].IsPercentage() ||
92
0
      svg->mLengthAttributes[SVGViewportElement::ATTR_Y].IsPercentage();
93
0
    bool widthOrHeightIsPercentage =
94
0
      svg->mLengthAttributes[SVGViewportElement::ATTR_WIDTH].IsPercentage() ||
95
0
      svg->mLengthAttributes[SVGViewportElement::ATTR_HEIGHT].IsPercentage();
96
0
97
0
    if (xOrYIsPercentage || widthOrHeightIsPercentage) {
98
0
      // Ancestor changes can't affect how we render from the perspective of
99
0
      // any rendering observers that we may have, so we don't need to
100
0
      // invalidate them. We also don't need to invalidate ourself, since our
101
0
      // changed ancestor will have invalidated its entire area, which includes
102
0
      // our area.
103
0
      // For perf reasons we call this before calling NotifySVGChanged() below.
104
0
      nsSVGUtils::ScheduleReflowSVG(this);
105
0
    }
106
0
107
0
    // Coordinate context changes affect mCanvasTM if we have a
108
0
    // percentage 'x' or 'y', or if we have a percentage 'width' or 'height' AND
109
0
    // a 'viewBox'.
110
0
111
0
    if (!(aFlags & TRANSFORM_CHANGED) &&
112
0
        (xOrYIsPercentage ||
113
0
         (widthOrHeightIsPercentage && svg->HasViewBoxRect()))) {
114
0
      aFlags |= TRANSFORM_CHANGED;
115
0
    }
116
0
117
0
    if (svg->HasViewBoxRect() || !widthOrHeightIsPercentage) {
118
0
      // Remove COORD_CONTEXT_CHANGED, since we establish the coordinate
119
0
      // context for our descendants and this notification won't change its
120
0
      // dimensions:
121
0
      aFlags &= ~COORD_CONTEXT_CHANGED;
122
0
123
0
      if (!aFlags) {
124
0
        return; // No notification flags left
125
0
      }
126
0
    }
127
0
  }
128
0
129
0
  nsSVGDisplayContainerFrame::NotifySVGChanged(aFlags);
130
0
}
131
132
SVGBBox
133
nsSVGViewportFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace,
134
                                        uint32_t aFlags)
135
0
{
136
0
  // XXXjwatt It seems like authors would want the result to be clipped by the
137
0
  // viewport we establish if IsScrollableOverflow() is true.  We should
138
0
  // consider doing that.  See bug 1350755.
139
0
140
0
  SVGBBox bbox;
141
0
142
0
  if (aFlags & nsSVGUtils::eForGetClientRects) {
143
0
    // XXXjwatt For consistency with the old code this code includes the
144
0
    // viewport we establish in the result, but only includes the bounds of our
145
0
    // descendants if they are not clipped to that viewport.  However, this is
146
0
    // both inconsistent with Chrome and with the specs.  See bug 1350755.
147
0
    // Ideally getClientRects/getBoundingClientRect should be consistent with
148
0
    // getBBox.
149
0
    float x, y, w, h;
150
0
    static_cast<SVGViewportElement*>(GetContent())->
151
0
      GetAnimatedLengthValues(&x, &y, &w, &h, nullptr);
152
0
    if (w < 0.0f) w = 0.0f;
153
0
    if (h < 0.0f) h = 0.0f;
154
0
    Rect viewport(x, y, w, h);
155
0
    bbox = aToBBoxUserspace.TransformBounds(viewport);
156
0
    if (StyleDisplay()->IsScrollableOverflow()) {
157
0
      return bbox;
158
0
    }
159
0
    // Else we're not clipping to our viewport so we fall through and include
160
0
    // the bounds of our children.
161
0
  }
162
0
163
0
  SVGBBox descendantsBbox =
164
0
    nsSVGDisplayContainerFrame::GetBBoxContribution(aToBBoxUserspace, aFlags);
165
0
166
0
  bbox.UnionEdges(descendantsBbox);
167
0
168
0
  return bbox;
169
0
}
170
171
nsresult
172
nsSVGViewportFrame::AttributeChanged(int32_t  aNameSpaceID,
173
                                     nsAtom* aAttribute,
174
                                     int32_t  aModType)
175
0
{
176
0
  if (aNameSpaceID == kNameSpaceID_None &&
177
0
      !(GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
178
0
179
0
    SVGViewportElement* content = static_cast<SVGViewportElement*>(GetContent());
180
0
181
0
    if (aAttribute == nsGkAtoms::width ||
182
0
        aAttribute == nsGkAtoms::height) {
183
0
      nsLayoutUtils::PostRestyleEvent(
184
0
        mContent->AsElement(), nsRestyleHint(0),
185
0
        nsChangeHint_InvalidateRenderingObservers);
186
0
      nsSVGUtils::ScheduleReflowSVG(this);
187
0
188
0
      if (content->HasViewBoxOrSyntheticViewBox()) {
189
0
        // make sure our cached transform matrix gets (lazily) updated
190
0
        mCanvasTM = nullptr;
191
0
        content->ChildrenOnlyTransformChanged();
192
0
        nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED);
193
0
      } else {
194
0
        uint32_t flags = COORD_CONTEXT_CHANGED;
195
0
        if (mCanvasTM && mCanvasTM->IsSingular()) {
196
0
          mCanvasTM = nullptr;
197
0
          flags |= TRANSFORM_CHANGED;
198
0
        }
199
0
        nsSVGUtils::NotifyChildrenOfSVGChange(this, flags);
200
0
      }
201
0
202
0
    } else if (aAttribute == nsGkAtoms::transform ||
203
0
               aAttribute == nsGkAtoms::preserveAspectRatio ||
204
0
               aAttribute == nsGkAtoms::viewBox ||
205
0
               aAttribute == nsGkAtoms::x ||
206
0
               aAttribute == nsGkAtoms::y) {
207
0
      // make sure our cached transform matrix gets (lazily) updated
208
0
      mCanvasTM = nullptr;
209
0
210
0
      nsSVGUtils::NotifyChildrenOfSVGChange(
211
0
          this, aAttribute == nsGkAtoms::viewBox ?
212
0
                  TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED : TRANSFORM_CHANGED);
213
0
214
0
      // We don't invalidate for transform changes (the layers code does that).
215
0
      // Also note that SVGTransformableElement::GetAttributeChangeHint will
216
0
      // return nsChangeHint_UpdateOverflow for "transform" attribute changes
217
0
      // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
218
0
219
0
      if (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y) {
220
0
        nsLayoutUtils::PostRestyleEvent(
221
0
          mContent->AsElement(), nsRestyleHint(0),
222
0
          nsChangeHint_InvalidateRenderingObservers);
223
0
        nsSVGUtils::ScheduleReflowSVG(this);
224
0
      } else if (aAttribute == nsGkAtoms::viewBox ||
225
0
                 (aAttribute == nsGkAtoms::preserveAspectRatio &&
226
0
                  content->HasViewBoxOrSyntheticViewBox())) {
227
0
        content->ChildrenOnlyTransformChanged();
228
0
        // SchedulePaint sets a global state flag so we only need to call it once
229
0
        // (on ourself is fine), not once on each child (despite bug 828240).
230
0
        SchedulePaint();
231
0
      }
232
0
    }
233
0
  }
234
0
235
0
  return NS_OK;
236
0
}
237
238
nsIFrame*
239
nsSVGViewportFrame::GetFrameForPoint(const gfxPoint& aPoint)
240
0
{
241
0
  NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
242
0
               (mState & NS_FRAME_IS_NONDISPLAY),
243
0
               "If display lists are enabled, only hit-testing of non-display "
244
0
               "SVG should take this code path");
245
0
246
0
  if (StyleDisplay()->IsScrollableOverflow()) {
247
0
    Rect clip;
248
0
    static_cast<nsSVGElement*>(GetContent())->
249
0
      GetAnimatedLengthValues(&clip.x, &clip.y,
250
0
                              &clip.width, &clip.height, nullptr);
251
0
    if (!clip.Contains(ToPoint(aPoint))) {
252
0
      return nullptr;
253
0
    }
254
0
  }
255
0
256
0
  return nsSVGDisplayContainerFrame::GetFrameForPoint(aPoint);
257
0
}
258
259
//----------------------------------------------------------------------
260
// nsISVGSVGFrame methods:
261
262
void
263
nsSVGViewportFrame::NotifyViewportOrTransformChanged(uint32_t aFlags)
264
0
{
265
0
  // The dimensions of inner-<svg> frames are purely defined by their "width"
266
0
  // and "height" attributes, and transform changes can only occur as a result
267
0
  // of changes to their "width", "height", "viewBox" or "preserveAspectRatio"
268
0
  // attributes. Changes to all of these attributes are handled in
269
0
  // AttributeChanged(), so we should never be called.
270
0
  NS_ERROR("Not called for nsSVGViewportFrame");
271
0
}
272
273
//----------------------------------------------------------------------
274
// nsSVGContainerFrame methods:
275
276
bool
277
nsSVGViewportFrame::HasChildrenOnlyTransform(gfx::Matrix *aTransform) const
278
0
{
279
0
  SVGViewportElement *content = static_cast<SVGViewportElement*>(GetContent());
280
0
281
0
  if (content->HasViewBoxOrSyntheticViewBox()) {
282
0
    // XXX Maybe return false if the transform is the identity transform?
283
0
    if (aTransform) {
284
0
      *aTransform = content->GetViewBoxTransform();
285
0
    }
286
0
    return true;
287
0
  }
288
0
  return false;
289
0
}