1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is the Mozilla SVG project.
17 * The Initial Developer of the Original Code is
18 * Crocodile Clips Ltd..
19 * Portions created by the Initial Developer are Copyright (C) 2001
20 * the Initial Developer. All Rights Reserved.
23 * Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
40 #include "nsISVGChildFrame.h"
41 #include "nsSVGOuterSVGFrame.h"
42 #include "nsIDOMSVGAnimatedRect.h"
43 #include "nsSVGMatrix.h"
44 #include "nsSVGSVGElement.h"
45 #include "nsSVGContainerFrame.h"
46 #include "gfxContext.h"
48 typedef nsSVGDisplayContainerFrame nsSVGInnerSVGFrameBase
;
50 class nsSVGInnerSVGFrame
: public nsSVGInnerSVGFrameBase
,
51 public nsISVGValueObserver
,
55 NS_NewSVGInnerSVGFrame(nsIPresShell
* aPresShell
, nsIContent
* aContent
, nsStyleContext
* aContext
);
57 nsSVGInnerSVGFrame(nsStyleContext
* aContext
) :
58 nsSVGInnerSVGFrameBase(aContext
) {}
60 // nsISupports interface:
61 NS_IMETHOD
QueryInterface(const nsIID
& aIID
, void** aInstancePtr
);
63 NS_IMETHOD_(nsrefcnt
) AddRef() { return 1; }
64 NS_IMETHOD_(nsrefcnt
) Release() { return 1; }
67 // We don't define an AttributeChanged method since changes to the
68 // 'x', 'y', 'width' and 'height' attributes of our content object
69 // are handled in nsSVGSVGElement::DidModifySVGObservable
72 * Get the "type" of the frame
74 * @see nsGkAtoms::svgInnerSVGFrame
76 virtual nsIAtom
* GetType() const;
79 NS_IMETHOD
GetFrameName(nsAString
& aResult
) const
81 return MakeFrameName(NS_LITERAL_STRING("SVGInnerSVG"), aResult
);
85 // nsISVGChildFrame interface:
86 NS_IMETHOD
PaintSVG(nsSVGRenderState
*aContext
, const nsIntRect
*aDirtyRect
);
87 virtual void NotifySVGChanged(PRUint32 aFlags
);
88 NS_IMETHOD_(nsIFrame
*) GetFrameForPoint(const nsPoint
&aPoint
);
90 // nsSVGContainerFrame methods:
91 virtual already_AddRefed
<nsIDOMSVGMatrix
> GetCanvasTM();
93 // nsISVGValueObserver
94 NS_IMETHOD
WillModifySVGObservable(nsISVGValue
* observable
,
95 nsISVGValue::modificationType aModType
);
96 NS_IMETHOD
DidModifySVGObservable (nsISVGValue
* observable
,
97 nsISVGValue::modificationType aModType
);
99 // nsISupportsWeakReference
100 // implementation inherited from nsSupportsWeakReference
102 // nsISVGSVGFrame interface:
103 NS_IMETHOD
SuspendRedraw();
104 NS_IMETHOD
UnsuspendRedraw();
105 NS_IMETHOD
NotifyViewportChange();
109 nsCOMPtr
<nsIDOMSVGMatrix
> mCanvasTM
;
112 //----------------------------------------------------------------------
116 NS_NewSVGInnerSVGFrame(nsIPresShell
* aPresShell
, nsIContent
* aContent
, nsStyleContext
* aContext
)
118 nsCOMPtr
<nsIDOMSVGSVGElement
> svg
= do_QueryInterface(aContent
);
120 NS_ERROR("Can't create frame! Content is not an SVG 'svg' element!");
124 return new (aPresShell
) nsSVGInnerSVGFrame(aContext
);
127 //----------------------------------------------------------------------
128 // nsISupports methods
130 NS_INTERFACE_MAP_BEGIN(nsSVGInnerSVGFrame
)
131 NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver
)
132 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
133 NS_INTERFACE_MAP_ENTRY(nsISVGSVGFrame
)
134 NS_INTERFACE_MAP_END_INHERITING(nsSVGInnerSVGFrameBase
)
137 //----------------------------------------------------------------------
141 nsSVGInnerSVGFrame::GetType() const
143 return nsGkAtoms::svgInnerSVGFrame
;
146 //----------------------------------------------------------------------
147 // nsISVGChildFrame methods
150 nsSVGInnerSVGFrame::PaintSVG(nsSVGRenderState
*aContext
,
151 const nsIntRect
*aDirtyRect
)
153 gfxContextAutoSaveRestore autoSR
;
155 if (GetStyleDisplay()->IsScrollableOverflow()) {
156 float x
, y
, width
, height
;
157 static_cast<nsSVGSVGElement
*>(mContent
)->
158 GetAnimatedLengthValues(&x
, &y
, &width
, &height
, nsnull
);
160 if (width
<= 0 || height
<= 0) {
164 nsCOMPtr
<nsIDOMSVGMatrix
> clipTransform
;
165 if (!GetMatrixPropagation()) {
166 NS_NewSVGMatrix(getter_AddRefs(clipTransform
));
168 clipTransform
= static_cast<nsSVGContainerFrame
*>(mParent
)->GetCanvasTM();
172 gfxContext
*gfx
= aContext
->GetGfxContext();
173 autoSR
.SetContext(gfx
);
174 nsSVGUtils::SetClipRect(gfx
, clipTransform
, x
, y
, width
, height
);
178 return nsSVGInnerSVGFrameBase::PaintSVG(aContext
, aDirtyRect
);
182 nsSVGInnerSVGFrame::NotifySVGChanged(PRUint32 aFlags
)
184 if (aFlags
& COORD_CONTEXT_CHANGED
) {
186 nsSVGSVGElement
*svg
= static_cast<nsSVGSVGElement
*>(mContent
);
188 // Coordinate context changes affect mCanvasTM if we have a
189 // percentage 'x' or 'y', or if we have a percentage 'width' or 'height' AND
192 if (!(aFlags
& TRANSFORM_CHANGED
) &&
193 (svg
->mLengthAttributes
[nsSVGSVGElement::X
].IsPercentage() ||
194 svg
->mLengthAttributes
[nsSVGSVGElement::Y
].IsPercentage() ||
195 (mContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::viewBox
) &&
196 (svg
->mLengthAttributes
[nsSVGSVGElement::WIDTH
].IsPercentage() ||
197 svg
->mLengthAttributes
[nsSVGSVGElement::HEIGHT
].IsPercentage())))) {
199 aFlags
|= TRANSFORM_CHANGED
;
202 // XXX We could clear the COORD_CONTEXT_CHANGED flag in some circumstances
203 // if we have a non-percentage 'width' AND 'height, or if we have a 'viewBox'
204 // rect. This is because, when we have a viewBox rect, the viewBox rect
205 // is the coordinate context for our children, and it isn't changing.
206 // Percentage lengths on our children will continue to resolve to the
207 // same number of user units because they're relative to our viewBox rect. The
208 // same is true if we have a non-percentage width and height and don't have a
209 // viewBox. We (the <svg>) establish the coordinate context for our children. Our
210 // children don't care about changes to our parent coordinate context unless that
211 // change results in a change to the coordinate context that _we_ establish. Hence
212 // we can (should, really) stop propagating COORD_CONTEXT_CHANGED in these cases.
213 // We'd actually need to check that we have a viewBox rect and not just
214 // that viewBox is set, since it could be set to none.
215 // Take care not to break the testcase for bug 394463 when implementing this
218 if (aFlags
& TRANSFORM_CHANGED
) {
219 // make sure our cached transform matrix gets (lazily) updated
223 nsSVGInnerSVGFrameBase::NotifySVGChanged(aFlags
);
226 NS_IMETHODIMP_(nsIFrame
*)
227 nsSVGInnerSVGFrame::GetFrameForPoint(const nsPoint
&aPoint
)
229 if (GetStyleDisplay()->IsScrollableOverflow()) {
230 float clipX
, clipY
, clipWidth
, clipHeight
;
231 nsCOMPtr
<nsIDOMSVGMatrix
> clipTransform
;
233 nsSVGElement
*svg
= static_cast<nsSVGElement
*>(mContent
);
234 svg
->GetAnimatedLengthValues(&clipX
, &clipY
, &clipWidth
, &clipHeight
, nsnull
);
236 nsSVGContainerFrame
*parent
= static_cast<nsSVGContainerFrame
*>
238 clipTransform
= parent
->GetCanvasTM();
240 if (!nsSVGUtils::HitTestRect(clipTransform
,
241 clipX
, clipY
, clipWidth
, clipHeight
,
242 PresContext()->AppUnitsToDevPixels(aPoint
.x
),
243 PresContext()->AppUnitsToDevPixels(aPoint
.y
))) {
248 return nsSVGInnerSVGFrameBase::GetFrameForPoint(aPoint
);
251 //----------------------------------------------------------------------
252 // nsISVGSVGFrame methods:
255 nsSVGInnerSVGFrame::SuspendRedraw()
257 nsSVGOuterSVGFrame
*outerSVGFrame
= nsSVGUtils::GetOuterSVGFrame(this);
258 if (!outerSVGFrame
) {
259 NS_ERROR("no outer svg frame");
260 return NS_ERROR_FAILURE
;
262 return outerSVGFrame
->SuspendRedraw();
266 nsSVGInnerSVGFrame::UnsuspendRedraw()
268 nsSVGOuterSVGFrame
*outerSVGFrame
= nsSVGUtils::GetOuterSVGFrame(this);
269 if (!outerSVGFrame
) {
270 NS_ERROR("no outer svg frame");
271 return NS_ERROR_FAILURE
;
273 return outerSVGFrame
->UnsuspendRedraw();
277 nsSVGInnerSVGFrame::NotifyViewportChange()
279 PRUint32 flags
= COORD_CONTEXT_CHANGED
;
282 // XXX nsSVGSVGElement::InvalidateTransformNotifyFrame calls us for changes
283 // to 'x' and 'y'. Until this is fixed, add TRANSFORM_CHANGED to flags
286 flags
|= TRANSFORM_CHANGED
;
288 // make sure canvas transform matrix gets (lazily) recalculated:
291 // viewport changes only affect our transform if we have a viewBox attribute
292 if (mContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::viewBox
)) {
293 // make sure canvas transform matrix gets (lazily) recalculated:
296 flags
|= TRANSFORM_CHANGED
;
302 nsSVGUtils::NotifyChildrenOfSVGChange(this, flags
);
307 //----------------------------------------------------------------------
308 // nsSVGContainerFrame methods:
310 already_AddRefed
<nsIDOMSVGMatrix
>
311 nsSVGInnerSVGFrame::GetCanvasTM()
313 if (!GetMatrixPropagation()) {
314 nsIDOMSVGMatrix
*retval
;
315 NS_NewSVGMatrix(&retval
);
319 // parentTM * Translate(x,y) * viewBoxTM
322 // get the transform from our parent's coordinate system to ours:
323 NS_ASSERTION(mParent
, "null parent");
324 nsSVGContainerFrame
*containerFrame
= static_cast<nsSVGContainerFrame
*>
326 nsCOMPtr
<nsIDOMSVGMatrix
> parentTM
= containerFrame
->GetCanvasTM();
327 NS_ASSERTION(parentTM
, "null TM");
329 // append the transform due to the 'x' and 'y' attributes:
331 nsSVGSVGElement
*svg
= static_cast<nsSVGSVGElement
*>(mContent
);
332 svg
->GetAnimatedLengthValues(&x
, &y
, nsnull
);
334 nsCOMPtr
<nsIDOMSVGMatrix
> xyTM
;
335 parentTM
->Translate(x
, y
, getter_AddRefs(xyTM
));
337 // append the viewbox to viewport transform:
338 nsCOMPtr
<nsIDOMSVGMatrix
> viewBoxTM
;
339 nsSVGSVGElement
*svgElement
= static_cast<nsSVGSVGElement
*>(mContent
);
341 svgElement
->GetViewboxToViewportTransform(getter_AddRefs(viewBoxTM
));
342 if (NS_SUCCEEDED(res
) && viewBoxTM
) {
343 xyTM
->Multiply(viewBoxTM
, getter_AddRefs(mCanvasTM
));
345 NS_WARNING("We should propagate the fact that the viewBox is invalid.");
350 nsIDOMSVGMatrix
* retval
= mCanvasTM
.get();
351 NS_IF_ADDREF(retval
);
355 //----------------------------------------------------------------------
356 // nsISVGValueObserver methods:
359 nsSVGInnerSVGFrame::WillModifySVGObservable(nsISVGValue
* observable
,
360 nsISVGValue::modificationType aModType
)
366 nsSVGInnerSVGFrame::DidModifySVGObservable (nsISVGValue
* observable
,
367 nsISVGValue::modificationType aModType
)
369 NotifyViewportChange();