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