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/. */
7 #include "nsSVGOuterSVGFrame.h"
9 // Keep others in (case-insensitive) order:
10 #include "nsDisplayList.h"
11 #include "nsIDocument.h"
12 #include "nsIDOMWindow.h"
13 #include "nsIInterfaceRequestorUtils.h"
14 #include "nsIObjectLoadingContent.h"
15 #include "nsRenderingContext.h"
16 #include "nsSVGIntegrationUtils.h"
17 #include "nsSVGForeignObjectFrame.h"
18 #include "mozilla/dom/SVGSVGElement.h"
19 #include "mozilla/dom/SVGViewElement.h"
20 #include "nsSubDocumentFrame.h"
22 using namespace mozilla
;
23 using namespace mozilla::dom
;
25 //----------------------------------------------------------------------
26 // Implementation helpers
29 nsSVGOuterSVGFrame::RegisterForeignObject(nsSVGForeignObjectFrame
* aFrame
)
31 NS_ASSERTION(aFrame
, "Who on earth is calling us?!");
33 if (!mForeignObjectHash
) {
34 mForeignObjectHash
= new nsTHashtable
<nsPtrHashKey
<nsSVGForeignObjectFrame
> >();
37 NS_ASSERTION(!mForeignObjectHash
->GetEntry(aFrame
),
38 "nsSVGForeignObjectFrame already registered!");
40 mForeignObjectHash
->PutEntry(aFrame
);
42 NS_ASSERTION(mForeignObjectHash
->GetEntry(aFrame
),
43 "Failed to register nsSVGForeignObjectFrame!");
47 nsSVGOuterSVGFrame::UnregisterForeignObject(nsSVGForeignObjectFrame
* aFrame
)
49 NS_ASSERTION(aFrame
, "Who on earth is calling us?!");
50 NS_ASSERTION(mForeignObjectHash
&& mForeignObjectHash
->GetEntry(aFrame
),
51 "nsSVGForeignObjectFrame not in registry!");
52 return mForeignObjectHash
->RemoveEntry(aFrame
);
55 //----------------------------------------------------------------------
59 NS_NewSVGOuterSVGFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
61 return new (aPresShell
) nsSVGOuterSVGFrame(aContext
);
64 NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGFrame
)
66 nsSVGOuterSVGFrame::nsSVGOuterSVGFrame(nsStyleContext
* aContext
)
67 : nsSVGOuterSVGFrameBase(aContext
)
68 , mFullZoom(aContext
->PresContext()->GetFullZoom())
69 , mViewportInitialized(false)
70 , mIsRootContent(false)
72 // Outer-<svg> has CSS layout, so remove this bit:
73 RemoveStateBits(NS_FRAME_SVG_LAYOUT
);
77 nsSVGOuterSVGFrame::Init(nsIContent
* aContent
,
79 nsIFrame
* aPrevInFlow
)
81 NS_ASSERTION(aContent
->IsSVG(nsGkAtoms::svg
),
82 "Content is not an SVG 'svg' element!");
84 AddStateBits(NS_STATE_IS_OUTER_SVG
|
85 NS_FRAME_FONT_INFLATION_CONTAINER
|
86 NS_FRAME_FONT_INFLATION_FLOW_ROOT
);
88 // Check for conditional processing attributes here rather than in
89 // nsCSSFrameConstructor::FindSVGData because we want to avoid
90 // simply giving failing outer <svg> elements an nsSVGContainerFrame.
91 // We don't create other SVG frames if PassesConditionalProcessingTests
92 // returns false, but since we do create nsSVGOuterSVGFrame frames we
93 // prevent them from painting by [ab]use NS_FRAME_IS_NONDISPLAY. The
94 // frame will be recreated via an nsChangeHint_ReconstructFrame restyle if
95 // the value returned by PassesConditionalProcessingTests changes.
96 SVGSVGElement
*svg
= static_cast<SVGSVGElement
*>(aContent
);
97 if (!svg
->PassesConditionalProcessingTests()) {
98 AddStateBits(NS_FRAME_IS_NONDISPLAY
);
101 nsSVGOuterSVGFrameBase::Init(aContent
, aParent
, aPrevInFlow
);
103 nsIDocument
* doc
= mContent
->GetCurrentDoc();
105 // we only care about our content's zoom and pan values if it's the root element
106 if (doc
->GetRootElement() == mContent
) {
107 mIsRootContent
= true;
112 //----------------------------------------------------------------------
113 // nsQueryFrame methods
115 NS_QUERYFRAME_HEAD(nsSVGOuterSVGFrame
)
116 NS_QUERYFRAME_ENTRY(nsISVGSVGFrame
)
117 NS_QUERYFRAME_TAIL_INHERITING(nsSVGOuterSVGFrameBase
)
119 //----------------------------------------------------------------------
122 //----------------------------------------------------------------------
125 /* virtual */ nscoord
126 nsSVGOuterSVGFrame::GetMinWidth(nsRenderingContext
*aRenderingContext
)
129 DISPLAY_MIN_WIDTH(this, result
);
136 /* virtual */ nscoord
137 nsSVGOuterSVGFrame::GetPrefWidth(nsRenderingContext
*aRenderingContext
)
140 DISPLAY_PREF_WIDTH(this, result
);
142 SVGSVGElement
*svg
= static_cast<SVGSVGElement
*>(mContent
);
143 nsSVGLength2
&width
= svg
->mLengthAttributes
[SVGSVGElement::ATTR_WIDTH
];
145 if (width
.IsPercentage()) {
146 // It looks like our containing block's width may depend on our width. In
147 // that case our behavior is undefined according to CSS 2.1 section 10.3.2,
151 result
= nsPresContext::CSSPixelsToAppUnits(width
.GetAnimValue(svg
));
160 /* virtual */ IntrinsicSize
161 nsSVGOuterSVGFrame::GetIntrinsicSize()
163 // XXXjwatt Note that here we want to return the CSS width/height if they're
164 // specified and we're embedded inside an nsIObjectLoadingContent.
166 IntrinsicSize intrinsicSize
;
168 SVGSVGElement
*content
= static_cast<SVGSVGElement
*>(mContent
);
169 nsSVGLength2
&width
= content
->mLengthAttributes
[SVGSVGElement::ATTR_WIDTH
];
170 nsSVGLength2
&height
= content
->mLengthAttributes
[SVGSVGElement::ATTR_HEIGHT
];
172 if (!width
.IsPercentage()) {
173 nscoord val
= nsPresContext::CSSPixelsToAppUnits(width
.GetAnimValue(content
));
174 if (val
< 0) val
= 0;
175 intrinsicSize
.width
.SetCoordValue(val
);
178 if (!height
.IsPercentage()) {
179 nscoord val
= nsPresContext::CSSPixelsToAppUnits(height
.GetAnimValue(content
));
180 if (val
< 0) val
= 0;
181 intrinsicSize
.height
.SetCoordValue(val
);
184 return intrinsicSize
;
188 nsSVGOuterSVGFrame::GetIntrinsicRatio()
190 // We only have an intrinsic size/ratio if our width and height attributes
191 // are both specified and set to non-percentage values, or we have a viewBox
192 // rect: http://www.w3.org/TR/SVGMobile12/coords.html#IntrinsicSizing
194 SVGSVGElement
*content
= static_cast<SVGSVGElement
*>(mContent
);
195 nsSVGLength2
&width
= content
->mLengthAttributes
[SVGSVGElement::ATTR_WIDTH
];
196 nsSVGLength2
&height
= content
->mLengthAttributes
[SVGSVGElement::ATTR_HEIGHT
];
198 if (!width
.IsPercentage() && !height
.IsPercentage()) {
199 nsSize
ratio(NSToCoordRoundWithClamp(width
.GetAnimValue(content
)),
200 NSToCoordRoundWithClamp(height
.GetAnimValue(content
)));
201 if (ratio
.width
< 0) {
204 if (ratio
.height
< 0) {
210 SVGViewElement
* viewElement
= content
->GetCurrentViewElement();
211 const nsSVGViewBoxRect
* viewbox
= nullptr;
213 // The logic here should match HasViewBox().
214 if (viewElement
&& viewElement
->mViewBox
.HasRect()) {
215 viewbox
= &viewElement
->mViewBox
.GetAnimValue();
216 } else if (content
->mViewBox
.HasRect()) {
217 viewbox
= &content
->mViewBox
.GetAnimValue();
221 float viewBoxWidth
= viewbox
->width
;
222 float viewBoxHeight
= viewbox
->height
;
224 if (viewBoxWidth
< 0.0f
) {
227 if (viewBoxHeight
< 0.0f
) {
228 viewBoxHeight
= 0.0f
;
230 return nsSize(NSToCoordRoundWithClamp(viewBoxWidth
),
231 NSToCoordRoundWithClamp(viewBoxHeight
));
234 return nsSVGOuterSVGFrameBase::GetIntrinsicRatio();
238 nsSVGOuterSVGFrame::ComputeSize(nsRenderingContext
*aRenderingContext
,
239 nsSize aCBSize
, nscoord aAvailableWidth
,
240 nsSize aMargin
, nsSize aBorder
, nsSize aPadding
,
243 if (IsRootOfImage() || IsRootOfReplacedElementSubDoc()) {
244 // The embedding element has sized itself using the CSS replaced element
245 // sizing rules, using our intrinsic dimensions as necessary. The SVG spec
246 // says that the width and height of embedded SVG is overridden by the
247 // width and height of the embedding element, so we just need to size to
248 // the viewport that the embedding element has established for us.
252 nsSize cbSize
= aCBSize
;
253 IntrinsicSize intrinsicSize
= GetIntrinsicSize();
255 if (!mContent
->GetParent()) {
256 // We're the root of the outermost browsing context, so we need to scale
257 // cbSize by the full-zoom so that SVGs with percentage width/height zoom:
259 NS_ASSERTION(aCBSize
.width
!= NS_AUTOHEIGHT
&&
260 aCBSize
.height
!= NS_AUTOHEIGHT
,
261 "root should not have auto-width/height containing block");
262 cbSize
.width
*= PresContext()->GetFullZoom();
263 cbSize
.height
*= PresContext()->GetFullZoom();
265 // We also need to honour the width and height attributes' default values
266 // of 100% when we're the root of a browsing context. (GetIntrinsicSize()
267 // doesn't report these since there's no such thing as a percentage
268 // intrinsic size. Also note that explicit percentage values are mapped
269 // into style, so the following isn't for them.)
271 SVGSVGElement
* content
= static_cast<SVGSVGElement
*>(mContent
);
273 nsSVGLength2
&width
=
274 content
->mLengthAttributes
[SVGSVGElement::ATTR_WIDTH
];
275 if (width
.IsPercentage()) {
276 NS_ABORT_IF_FALSE(intrinsicSize
.width
.GetUnit() == eStyleUnit_None
,
277 "GetIntrinsicSize should have reported no "
279 float val
= width
.GetAnimValInSpecifiedUnits() / 100.0f
;
280 if (val
< 0.0f
) val
= 0.0f
;
281 intrinsicSize
.width
.SetCoordValue(val
* cbSize
.width
);
284 nsSVGLength2
&height
=
285 content
->mLengthAttributes
[SVGSVGElement::ATTR_HEIGHT
];
286 NS_ASSERTION(aCBSize
.height
!= NS_AUTOHEIGHT
,
287 "root should not have auto-height containing block");
288 if (height
.IsPercentage()) {
289 NS_ABORT_IF_FALSE(intrinsicSize
.height
.GetUnit() == eStyleUnit_None
,
290 "GetIntrinsicSize should have reported no "
292 float val
= height
.GetAnimValInSpecifiedUnits() / 100.0f
;
293 if (val
< 0.0f
) val
= 0.0f
;
294 intrinsicSize
.height
.SetCoordValue(val
* cbSize
.height
);
296 NS_ABORT_IF_FALSE(intrinsicSize
.height
.GetUnit() == eStyleUnit_Coord
&&
297 intrinsicSize
.width
.GetUnit() == eStyleUnit_Coord
,
298 "We should have just handled the only situation where"
299 "we lack an intrinsic height or width.");
302 return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(
303 aRenderingContext
, this,
304 intrinsicSize
, GetIntrinsicRatio(), cbSize
,
305 aMargin
, aBorder
, aPadding
);
309 nsSVGOuterSVGFrame::Reflow(nsPresContext
* aPresContext
,
310 nsHTMLReflowMetrics
& aDesiredSize
,
311 const nsHTMLReflowState
& aReflowState
,
312 nsReflowStatus
& aStatus
)
314 DO_GLOBAL_REFLOW_COUNT("nsSVGOuterSVGFrame");
315 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aDesiredSize
, aStatus
);
316 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS
,
317 ("enter nsSVGOuterSVGFrame::Reflow: availSize=%d,%d",
318 aReflowState
.AvailableWidth(), aReflowState
.AvailableHeight()));
320 NS_PRECONDITION(mState
& NS_FRAME_IN_REFLOW
, "frame is not in reflow");
322 aStatus
= NS_FRAME_COMPLETE
;
324 aDesiredSize
.Width() = aReflowState
.ComputedWidth() +
325 aReflowState
.ComputedPhysicalBorderPadding().LeftRight();
326 aDesiredSize
.Height() = aReflowState
.ComputedHeight() +
327 aReflowState
.ComputedPhysicalBorderPadding().TopBottom();
329 NS_ASSERTION(!GetPrevInFlow(), "SVG can't currently be broken across pages.");
331 SVGSVGElement
*svgElem
= static_cast<SVGSVGElement
*>(mContent
);
333 nsSVGOuterSVGAnonChildFrame
*anonKid
=
334 static_cast<nsSVGOuterSVGAnonChildFrame
*>(GetFirstPrincipalChild());
336 if (mState
& NS_FRAME_FIRST_REFLOW
) {
338 svgElem
->UpdateHasChildrenOnlyTransform();
341 // If our SVG viewport has changed, update our content and notify.
342 // http://www.w3.org/TR/SVG11/coords.html#ViewportSpace
344 svgFloatSize
newViewportSize(
345 nsPresContext::AppUnitsToFloatCSSPixels(aReflowState
.ComputedWidth()),
346 nsPresContext::AppUnitsToFloatCSSPixels(aReflowState
.ComputedHeight()));
348 svgFloatSize oldViewportSize
= svgElem
->GetViewportSize();
350 uint32_t changeBits
= 0;
351 if (newViewportSize
!= oldViewportSize
) {
352 // When our viewport size changes, we may need to update the overflow rects
353 // of our child frames. This is the case if:
355 // * We have a real/synthetic viewBox (a children-only transform), since
356 // the viewBox transform will change as the viewport dimensions change.
358 // * We do not have a real/synthetic viewBox, but the last time we
359 // reflowed (or the last time UpdateOverflow() was called) we did.
361 // We only handle the former case here, in which case we mark all our child
362 // frames as dirty so that we reflow them below and update their overflow
365 // In the latter case, updating of overflow rects is handled for removal of
366 // real viewBox (the viewBox attribute) in AttributeChanged. Synthetic
367 // viewBox "removal" (e.g. a document references the same SVG via both an
368 // <svg:image> and then as a CSS background image (a synthetic viewBox is
369 // used when painting the former, but not when painting the latter)) is
370 // handled in SVGSVGElement::FlushImageTransformInvalidation.
372 if (svgElem
->HasViewBoxOrSyntheticViewBox()) {
373 nsIFrame
* anonChild
= GetFirstPrincipalChild();
374 anonChild
->AddStateBits(NS_FRAME_IS_DIRTY
);
375 for (nsIFrame
* child
= anonChild
->GetFirstPrincipalChild(); child
;
376 child
= child
->GetNextSibling()) {
377 child
->AddStateBits(NS_FRAME_IS_DIRTY
);
380 changeBits
|= COORD_CONTEXT_CHANGED
;
381 svgElem
->SetViewportSize(newViewportSize
);
383 if (mFullZoom
!= PresContext()->GetFullZoom()) {
384 changeBits
|= FULL_ZOOM_CHANGED
;
385 mFullZoom
= PresContext()->GetFullZoom();
388 NotifyViewportOrTransformChanged(changeBits
);
390 mViewportInitialized
= true;
392 // Now that we've marked the necessary children as dirty, call
393 // ReflowSVG() or ReflowSVGNonDisplayText() on them, depending
394 // on whether we are non-display.
395 mCallingReflowSVG
= true;
396 if (GetStateBits() & NS_FRAME_IS_NONDISPLAY
) {
397 ReflowSVGNonDisplayText(this);
399 // Update the mRects and visual overflow rects of all our descendants,
400 // including our anonymous wrapper kid:
401 anonKid
->AddStateBits(mState
& NS_FRAME_IS_DIRTY
);
402 anonKid
->ReflowSVG();
403 NS_ABORT_IF_FALSE(!anonKid
->GetNextSibling(),
404 "We should have one anonymous child frame wrapping our real children");
406 mCallingReflowSVG
= false;
408 // Set our anonymous kid's offset from our border box:
409 anonKid
->SetPosition(GetContentRectRelativeToSelf().TopLeft());
411 // Including our size in our overflow rects regardless of the value of
412 // 'background', 'border', etc. makes sure that we usually (when we clip to
413 // our content area) don't have to keep changing our overflow rects as our
414 // descendants move about (see perf comment below). Including our size in our
415 // scrollable overflow rect also makes sure that we scroll if we're too big
418 // <svg> never allows scrolling to anything outside its mRect (only panning),
419 // so we must always keep our scrollable overflow set to our size.
421 // With regards to visual overflow, we always clip root-<svg> (see our
422 // BuildDisplayList method) regardless of the value of the 'overflow'
423 // property since that is per-spec, even for the initial 'visible' value. For
424 // that reason there's no point in adding descendant visual overflow to our
425 // own when this frame is for a root-<svg>. That said, there's also a very
426 // good performance reason for us wanting to avoid doing so. If we did, then
427 // the frame's overflow would often change as descendants that are partially
428 // or fully outside its rect moved (think animation on/off screen), and that
429 // would cause us to do a full NS_FRAME_IS_DIRTY reflow and repaint of the
430 // entire document tree each such move (see bug 875175).
432 // So it's only non-root outer-<svg> that has the visual overflow of its
433 // descendants added to its own. (Note that the default user-agent style
434 // sheet makes 'hidden' the default value for :not(root(svg)), so usually
435 // FinishAndStoreOverflow will still clip this back to the frame's rect.)
437 // WARNING!! Keep UpdateBounds below in sync with whatever we do for our
438 // overflow rects here! (Again, see bug 875175.)
440 aDesiredSize
.SetOverflowAreasToDesiredBounds();
441 if (!mIsRootContent
) {
442 aDesiredSize
.mOverflowAreas
.VisualOverflow().UnionRect(
443 aDesiredSize
.mOverflowAreas
.VisualOverflow(),
444 anonKid
->GetVisualOverflowRect() + anonKid
->GetPosition());
446 FinishAndStoreOverflow(&aDesiredSize
);
448 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS
,
449 ("exit nsSVGOuterSVGFrame::Reflow: size=%d,%d",
450 aDesiredSize
.Width(), aDesiredSize
.Height()));
451 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
455 nsSVGOuterSVGFrame::DidReflow(nsPresContext
* aPresContext
,
456 const nsHTMLReflowState
* aReflowState
,
457 nsDidReflowStatus aStatus
)
459 nsSVGOuterSVGFrameBase::DidReflow(aPresContext
,aReflowState
,aStatus
);
461 // Make sure elements styled by :hover get updated if script/animation moves
462 // them under or out from under the pointer:
463 PresContext()->PresShell()->SynthesizeMouseMove(false);
467 nsSVGOuterSVGFrame::UpdateOverflow()
469 // See the comments in Reflow above.
471 // WARNING!! Keep this in sync with Reflow above!
473 nsRect
rect(nsPoint(0, 0), GetSize());
474 nsOverflowAreas
overflowAreas(rect
, rect
);
476 if (!mIsRootContent
) {
477 nsIFrame
*anonKid
= GetFirstPrincipalChild();
478 overflowAreas
.VisualOverflow().UnionRect(
479 overflowAreas
.VisualOverflow(),
480 anonKid
->GetVisualOverflowRect() + anonKid
->GetPosition());
483 return FinishAndStoreOverflow(overflowAreas
, GetSize());
487 //----------------------------------------------------------------------
491 * Used to paint/hit-test SVG when SVG display lists are disabled.
493 class nsDisplayOuterSVG
: public nsDisplayItem
{
495 nsDisplayOuterSVG(nsDisplayListBuilder
* aBuilder
,
496 nsSVGOuterSVGFrame
* aFrame
) :
497 nsDisplayItem(aBuilder
, aFrame
) {
498 MOZ_COUNT_CTOR(nsDisplayOuterSVG
);
500 #ifdef NS_BUILD_REFCNT_LOGGING
501 virtual ~nsDisplayOuterSVG() {
502 MOZ_COUNT_DTOR(nsDisplayOuterSVG
);
506 virtual void HitTest(nsDisplayListBuilder
* aBuilder
, const nsRect
& aRect
,
507 HitTestState
* aState
,
508 nsTArray
<nsIFrame
*> *aOutFrames
) MOZ_OVERRIDE
;
509 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
510 nsRenderingContext
* aCtx
) MOZ_OVERRIDE
;
512 virtual void ComputeInvalidationRegion(nsDisplayListBuilder
* aBuilder
,
513 const nsDisplayItemGeometry
* aGeometry
,
514 nsRegion
* aInvalidRegion
) MOZ_OVERRIDE
;
516 NS_DISPLAY_DECL_NAME("SVGOuterSVG", TYPE_SVG_OUTER_SVG
)
520 nsDisplayOuterSVG::HitTest(nsDisplayListBuilder
* aBuilder
, const nsRect
& aRect
,
521 HitTestState
* aState
, nsTArray
<nsIFrame
*> *aOutFrames
)
523 nsSVGOuterSVGFrame
*outerSVGFrame
= static_cast<nsSVGOuterSVGFrame
*>(mFrame
);
524 nsRect rectAtOrigin
= aRect
- ToReferenceFrame();
525 nsRect
thisRect(nsPoint(0,0), outerSVGFrame
->GetSize());
526 if (!thisRect
.Intersects(rectAtOrigin
))
529 nsPoint
rectCenter(rectAtOrigin
.x
+ rectAtOrigin
.width
/ 2,
530 rectAtOrigin
.y
+ rectAtOrigin
.height
/ 2);
532 nsSVGOuterSVGAnonChildFrame
*anonKid
=
533 static_cast<nsSVGOuterSVGAnonChildFrame
*>(
534 outerSVGFrame
->GetFirstPrincipalChild());
535 nsIFrame
* frame
= nsSVGUtils::HitTestChildren(
536 anonKid
, rectCenter
+ outerSVGFrame
->GetPosition() -
537 outerSVGFrame
->GetContentRect().TopLeft());
539 aOutFrames
->AppendElement(frame
);
544 nsDisplayOuterSVG::Paint(nsDisplayListBuilder
* aBuilder
,
545 nsRenderingContext
* aContext
)
547 #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
548 PRTime start
= PR_Now();
551 // Create an SVGAutoRenderState so we can call SetPaintingToWindow on
552 // it, but do so without changing the render mode:
553 SVGAutoRenderState
state(aContext
, SVGAutoRenderState::GetRenderMode(aContext
));
555 if (aBuilder
->IsPaintingToWindow()) {
556 state
.SetPaintingToWindow(true);
559 nsRect viewportRect
=
560 mFrame
->GetContentRectRelativeToSelf() + ToReferenceFrame();
562 nsRect clipRect
= mVisibleRect
.Intersect(viewportRect
);
564 nsIntRect contentAreaDirtyRect
=
565 (clipRect
- viewportRect
.TopLeft()).
566 ToOutsidePixels(mFrame
->PresContext()->AppUnitsPerDevPixel());
568 aContext
->PushState();
569 aContext
->Translate(viewportRect
.TopLeft());
570 nsSVGUtils::PaintFrameWithEffects(aContext
, &contentAreaDirtyRect
, mFrame
);
571 aContext
->PopState();
573 NS_ASSERTION(!aContext
->ThebesContext()->HasError(), "Cairo in error state");
575 #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
576 PRTime end
= PR_Now();
577 printf("SVG Paint Timing: %f ms\n", (end
-start
)/1000.0);
581 static PLDHashOperator
CheckForeignObjectInvalidatedArea(nsPtrHashKey
<nsSVGForeignObjectFrame
>* aEntry
, void* aData
)
583 nsRegion
* region
= static_cast<nsRegion
*>(aData
);
584 region
->Or(*region
, aEntry
->GetKey()->GetInvalidRegion());
585 return PL_DHASH_NEXT
;
589 nsSVGOuterSVGFrame::FindInvalidatedForeignObjectFrameChildren(nsIFrame
* aFrame
)
592 if (mForeignObjectHash
&& mForeignObjectHash
->Count()) {
593 mForeignObjectHash
->EnumerateEntries(CheckForeignObjectInvalidatedArea
, &result
);
599 nsDisplayOuterSVG::ComputeInvalidationRegion(nsDisplayListBuilder
* aBuilder
,
600 const nsDisplayItemGeometry
* aGeometry
,
601 nsRegion
* aInvalidRegion
)
603 nsSVGOuterSVGFrame
*frame
= static_cast<nsSVGOuterSVGFrame
*>(mFrame
);
604 frame
->InvalidateSVG(frame
->FindInvalidatedForeignObjectFrameChildren(frame
));
606 nsRegion result
= frame
->GetInvalidRegion();
607 result
.MoveBy(ToReferenceFrame());
608 frame
->ClearInvalidRegion();
610 nsDisplayItem::ComputeInvalidationRegion(aBuilder
, aGeometry
, aInvalidRegion
);
611 aInvalidRegion
->Or(*aInvalidRegion
, result
);
616 DependsOnIntrinsicSize(const nsIFrame
* aEmbeddingFrame
)
618 const nsStylePosition
*pos
= aEmbeddingFrame
->StylePosition();
619 const nsStyleCoord
&width
= pos
->mWidth
;
620 const nsStyleCoord
&height
= pos
->mHeight
;
622 // XXX it would be nice to know if the size of aEmbeddingFrame's containing
623 // block depends on aEmbeddingFrame, then we'd know if we can return false
624 // for eStyleUnit_Percent too.
625 return !width
.ConvertsToLength() ||
626 !height
.ConvertsToLength();
630 nsSVGOuterSVGFrame::AttributeChanged(int32_t aNameSpaceID
,
634 if (aNameSpaceID
== kNameSpaceID_None
&&
635 !(GetStateBits() & (NS_FRAME_FIRST_REFLOW
| NS_FRAME_IS_NONDISPLAY
))) {
636 if (aAttribute
== nsGkAtoms::viewBox
||
637 aAttribute
== nsGkAtoms::preserveAspectRatio
||
638 aAttribute
== nsGkAtoms::transform
) {
640 // make sure our cached transform matrix gets (lazily) updated
643 nsSVGUtils::NotifyChildrenOfSVGChange(GetFirstPrincipalChild(),
644 aAttribute
== nsGkAtoms::viewBox
?
645 TRANSFORM_CHANGED
| COORD_CONTEXT_CHANGED
: TRANSFORM_CHANGED
);
647 if (aAttribute
!= nsGkAtoms::transform
) {
648 static_cast<SVGSVGElement
*>(mContent
)->ChildrenOnlyTransformChanged();
651 } else if (aAttribute
== nsGkAtoms::width
||
652 aAttribute
== nsGkAtoms::height
) {
654 // Don't call ChildrenOnlyTransformChanged() here, since we call it
655 // under Reflow if the width/height actually changed.
657 nsIFrame
* embeddingFrame
;
658 if (IsRootOfReplacedElementSubDoc(&embeddingFrame
) && embeddingFrame
) {
659 if (DependsOnIntrinsicSize(embeddingFrame
)) {
660 // Tell embeddingFrame's presShell it needs to be reflowed (which takes
661 // care of reflowing us too).
662 embeddingFrame
->PresContext()->PresShell()->
663 FrameNeedsReflow(embeddingFrame
, nsIPresShell::eStyleChange
, NS_FRAME_IS_DIRTY
);
665 // else our width and height is overridden - don't reflow anything
667 // We are not embedded by reference, so our 'width' and 'height'
668 // attributes are not overridden - we need to reflow.
669 PresContext()->PresShell()->
670 FrameNeedsReflow(this, nsIPresShell::eStyleChange
, NS_FRAME_IS_DIRTY
);
678 //----------------------------------------------------------------------
682 nsSVGOuterSVGFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
683 const nsRect
& aDirtyRect
,
684 const nsDisplayListSet
& aLists
)
686 if (GetStateBits() & NS_FRAME_IS_NONDISPLAY
) {
690 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
692 // Per-spec, we always clip root-<svg> even when 'overflow' has its initial
693 // value of 'visible'. See also the "visual overflow" comments in Reflow.
694 DisplayListClipState::AutoSaveRestore
autoSR(aBuilder
);
695 if (mIsRootContent
||
696 StyleDisplay()->IsScrollableOverflow()) {
697 autoSR
.ClipContainingBlockDescendantsToContentBox(aBuilder
, this);
700 if ((aBuilder
->IsForEventDelivery() &&
701 NS_SVGDisplayListHitTestingEnabled()) ||
702 NS_SVGDisplayListPaintingEnabled()) {
703 nsDisplayList
*contentList
= aLists
.Content();
704 nsDisplayListSet
set(contentList
, contentList
, contentList
,
705 contentList
, contentList
, contentList
);
706 BuildDisplayListForNonBlockChildren(aBuilder
, aDirtyRect
, set
);
708 aLists
.Content()->AppendNewToTop(
709 new (aBuilder
) nsDisplayOuterSVG(aBuilder
, this));
714 nsSVGOuterSVGFrame::GetSplittableType() const
716 return NS_FRAME_NOT_SPLITTABLE
;
720 nsSVGOuterSVGFrame::GetType() const
722 return nsGkAtoms::svgOuterSVGFrame
;
725 //----------------------------------------------------------------------
726 // nsISVGSVGFrame methods:
729 nsSVGOuterSVGFrame::NotifyViewportOrTransformChanged(uint32_t aFlags
)
731 NS_ABORT_IF_FALSE(aFlags
&&
732 !(aFlags
& ~(COORD_CONTEXT_CHANGED
| TRANSFORM_CHANGED
|
734 "Unexpected aFlags value");
736 // No point in doing anything when were not init'ed yet:
737 if (!mViewportInitialized
) {
741 SVGSVGElement
*content
= static_cast<SVGSVGElement
*>(mContent
);
743 if (aFlags
& COORD_CONTEXT_CHANGED
) {
744 if (content
->HasViewBoxRect()) {
745 // Percentage lengths on children resolve against the viewBox rect so we
746 // don't need to notify them of the viewport change, but the viewBox
747 // transform will have changed, so we need to notify them of that instead.
748 aFlags
= TRANSFORM_CHANGED
;
750 else if (content
->ShouldSynthesizeViewBox()) {
751 // In the case of a synthesized viewBox, the synthetic viewBox's rect
752 // changes as the viewport changes. As a result we need to maintain the
753 // COORD_CONTEXT_CHANGED flag.
754 aFlags
|= TRANSFORM_CHANGED
;
756 else if (mCanvasTM
&& mCanvasTM
->IsSingular()) {
757 // A width/height of zero will result in us having a singular mCanvasTM
758 // even when we don't have a viewBox. So we also want to recompute our
759 // mCanvasTM for this width/height change even though we don't have a
761 aFlags
|= TRANSFORM_CHANGED
;
765 bool haveNonFulLZoomTransformChange
= (aFlags
& TRANSFORM_CHANGED
);
767 if (aFlags
& FULL_ZOOM_CHANGED
) {
768 // Convert FULL_ZOOM_CHANGED to TRANSFORM_CHANGED:
769 aFlags
= (aFlags
& ~FULL_ZOOM_CHANGED
) | TRANSFORM_CHANGED
;
772 if (aFlags
& TRANSFORM_CHANGED
) {
773 // Make sure our canvas transform matrix gets (lazily) recalculated:
776 if (haveNonFulLZoomTransformChange
&&
777 !(mState
& NS_FRAME_IS_NONDISPLAY
)) {
778 uint32_t flags
= (mState
& NS_FRAME_IN_REFLOW
) ?
779 SVGSVGElement::eDuringReflow
: 0;
780 content
->ChildrenOnlyTransformChanged(flags
);
784 nsSVGUtils::NotifyChildrenOfSVGChange(GetFirstPrincipalChild(), aFlags
);
787 //----------------------------------------------------------------------
788 // nsISVGChildFrame methods:
791 nsSVGOuterSVGFrame::PaintSVG(nsRenderingContext
* aContext
,
792 const nsIntRect
*aDirtyRect
,
793 nsIFrame
* aTransformRoot
)
795 NS_ASSERTION(GetFirstPrincipalChild()->GetType() ==
796 nsGkAtoms::svgOuterSVGAnonChildFrame
&&
797 !GetFirstPrincipalChild()->GetNextSibling(),
798 "We should have a single, anonymous, child");
799 nsSVGOuterSVGAnonChildFrame
*anonKid
=
800 static_cast<nsSVGOuterSVGAnonChildFrame
*>(GetFirstPrincipalChild());
801 return anonKid
->PaintSVG(aContext
, aDirtyRect
, aTransformRoot
);
805 nsSVGOuterSVGFrame::GetBBoxContribution(const gfx::Matrix
&aToBBoxUserspace
,
808 NS_ASSERTION(GetFirstPrincipalChild()->GetType() ==
809 nsGkAtoms::svgOuterSVGAnonChildFrame
&&
810 !GetFirstPrincipalChild()->GetNextSibling(),
811 "We should have a single, anonymous, child");
812 // We must defer to our child so that we don't include our
813 // content->PrependLocalTransformsTo() transforms.
814 nsSVGOuterSVGAnonChildFrame
*anonKid
=
815 static_cast<nsSVGOuterSVGAnonChildFrame
*>(GetFirstPrincipalChild());
816 return anonKid
->GetBBoxContribution(aToBBoxUserspace
, aFlags
);
819 //----------------------------------------------------------------------
820 // nsSVGContainerFrame methods:
823 nsSVGOuterSVGFrame::GetCanvasTM(uint32_t aFor
, nsIFrame
* aTransformRoot
)
825 if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY
) && !aTransformRoot
) {
826 if ((aFor
== FOR_PAINTING
&& NS_SVGDisplayListPaintingEnabled()) ||
827 (aFor
== FOR_HIT_TESTING
&& NS_SVGDisplayListHitTestingEnabled())) {
828 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this);
832 NS_ASSERTION(!aTransformRoot
, "transform root will be ignored here");
833 SVGSVGElement
*content
= static_cast<SVGSVGElement
*>(mContent
);
835 float devPxPerCSSPx
=
836 1.0f
/ PresContext()->AppUnitsToFloatCSSPixels(
837 PresContext()->AppUnitsPerDevPixel());
839 gfxMatrix tm
= content
->PrependLocalTransformsTo(
840 gfxMatrix().Scale(devPxPerCSSPx
, devPxPerCSSPx
));
841 mCanvasTM
= new gfxMatrix(tm
);
846 //----------------------------------------------------------------------
847 // Implementation helpers
850 nsSVGOuterSVGFrame::IsRootOfReplacedElementSubDoc(nsIFrame
**aEmbeddingFrame
)
852 if (!mContent
->GetParent()) {
853 // Our content is the document element
854 nsCOMPtr
<nsISupports
> container
= PresContext()->GetContainerWeak();
855 nsCOMPtr
<nsIDOMWindow
> window
= do_GetInterface(container
);
857 nsCOMPtr
<nsIDOMElement
> frameElement
;
858 window
->GetFrameElement(getter_AddRefs(frameElement
));
859 nsCOMPtr
<nsIObjectLoadingContent
> olc
= do_QueryInterface(frameElement
);
861 // Our document is inside an HTML 'object', 'embed' or 'applet' element
862 if (aEmbeddingFrame
) {
863 nsCOMPtr
<nsIContent
> element
= do_QueryInterface(frameElement
);
864 *aEmbeddingFrame
= element
->GetPrimaryFrame();
865 NS_ASSERTION(*aEmbeddingFrame
, "Yikes, no embedding frame!");
871 if (aEmbeddingFrame
) {
872 *aEmbeddingFrame
= nullptr;
878 nsSVGOuterSVGFrame::IsRootOfImage()
880 if (!mContent
->GetParent()) {
881 // Our content is the document element
882 nsIDocument
* doc
= mContent
->GetCurrentDoc();
883 if (doc
&& doc
->IsBeingUsedAsImage()) {
884 // Our document is being used as an image
893 nsSVGOuterSVGFrame::VerticalScrollbarNotNeeded() const
895 nsSVGLength2
&height
= static_cast<SVGSVGElement
*>(mContent
)->
896 mLengthAttributes
[SVGSVGElement::ATTR_HEIGHT
];
897 return height
.IsPercentage() && height
.GetBaseValInSpecifiedUnits() <= 100;
901 //----------------------------------------------------------------------
902 // Implementation of nsSVGOuterSVGAnonChildFrame
905 NS_NewSVGOuterSVGAnonChildFrame(nsIPresShell
* aPresShell
,
906 nsStyleContext
* aContext
)
908 return new (aPresShell
) nsSVGOuterSVGAnonChildFrame(aContext
);
911 NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGAnonChildFrame
)
915 nsSVGOuterSVGAnonChildFrame::Init(nsIContent
* aContent
,
917 nsIFrame
* aPrevInFlow
)
919 NS_ABORT_IF_FALSE(aParent
->GetType() == nsGkAtoms::svgOuterSVGFrame
,
920 "Unexpected parent");
921 nsSVGOuterSVGAnonChildFrameBase::Init(aContent
, aParent
, aPrevInFlow
);
926 nsSVGOuterSVGAnonChildFrame::GetType() const
928 return nsGkAtoms::svgOuterSVGAnonChildFrame
;
932 nsSVGOuterSVGAnonChildFrame::HasChildrenOnlyTransform(gfx::Matrix
*aTransform
) const
934 // We must claim our nsSVGOuterSVGFrame's children-only transforms as our own
935 // so that the children we are used to wrap are transformed properly.
937 SVGSVGElement
*content
= static_cast<SVGSVGElement
*>(mContent
);
939 bool hasTransform
= content
->HasChildrenOnlyTransform();
941 if (hasTransform
&& aTransform
) {
942 // Outer-<svg> doesn't use x/y, so we can pass eChildToUserSpace here.
944 *aTransform
= gfx::ToMatrix(
945 content
->PrependLocalTransformsTo(identity
,
946 nsSVGElement::eChildToUserSpace
));