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
);
78 DependsOnIntrinsicSize(const nsIFrame
* aEmbeddingFrame
)
80 const nsStylePosition
*pos
= aEmbeddingFrame
->StylePosition();
81 const nsStyleCoord
&width
= pos
->mWidth
;
82 const nsStyleCoord
&height
= pos
->mHeight
;
84 // XXX it would be nice to know if the size of aEmbeddingFrame's containing
85 // block depends on aEmbeddingFrame, then we'd know if we can return false
86 // for eStyleUnit_Percent too.
87 return !width
.ConvertsToLength() ||
88 !height
.ConvertsToLength();
92 nsSVGOuterSVGFrame::Init(nsIContent
* aContent
,
93 nsContainerFrame
* aParent
,
94 nsIFrame
* aPrevInFlow
)
96 NS_ASSERTION(aContent
->IsSVG(nsGkAtoms::svg
),
97 "Content is not an SVG 'svg' element!");
99 AddStateBits(NS_STATE_IS_OUTER_SVG
|
100 NS_FRAME_FONT_INFLATION_CONTAINER
|
101 NS_FRAME_FONT_INFLATION_FLOW_ROOT
);
103 // Check for conditional processing attributes here rather than in
104 // nsCSSFrameConstructor::FindSVGData because we want to avoid
105 // simply giving failing outer <svg> elements an nsSVGContainerFrame.
106 // We don't create other SVG frames if PassesConditionalProcessingTests
107 // returns false, but since we do create nsSVGOuterSVGFrame frames we
108 // prevent them from painting by [ab]use NS_FRAME_IS_NONDISPLAY. The
109 // frame will be recreated via an nsChangeHint_ReconstructFrame restyle if
110 // the value returned by PassesConditionalProcessingTests changes.
111 SVGSVGElement
*svg
= static_cast<SVGSVGElement
*>(aContent
);
112 if (!svg
->PassesConditionalProcessingTests()) {
113 AddStateBits(NS_FRAME_IS_NONDISPLAY
);
116 nsSVGOuterSVGFrameBase::Init(aContent
, aParent
, aPrevInFlow
);
118 nsIDocument
* doc
= mContent
->GetCurrentDoc();
120 // we only care about our content's zoom and pan values if it's the root element
121 if (doc
->GetRootElement() == mContent
) {
122 mIsRootContent
= true;
124 nsIFrame
* embeddingFrame
;
125 if (IsRootOfReplacedElementSubDoc(&embeddingFrame
) && embeddingFrame
) {
126 if (MOZ_UNLIKELY(!embeddingFrame
->HasAllStateBits(NS_FRAME_IS_DIRTY
)) &&
127 DependsOnIntrinsicSize(embeddingFrame
)) {
128 // Looks like this document is loading after the embedding element
129 // has had its first reflow, and that its size depends on our
130 // intrinsic size. We need it to resize itself to use our (now
131 // available) intrinsic size:
132 embeddingFrame
->PresContext()->PresShell()->
133 FrameNeedsReflow(embeddingFrame
, nsIPresShell::eStyleChange
, NS_FRAME_IS_DIRTY
);
140 //----------------------------------------------------------------------
141 // nsQueryFrame methods
143 NS_QUERYFRAME_HEAD(nsSVGOuterSVGFrame
)
144 NS_QUERYFRAME_ENTRY(nsISVGSVGFrame
)
145 NS_QUERYFRAME_TAIL_INHERITING(nsSVGOuterSVGFrameBase
)
147 //----------------------------------------------------------------------
150 //----------------------------------------------------------------------
153 /* virtual */ nscoord
154 nsSVGOuterSVGFrame::GetMinISize(nsRenderingContext
*aRenderingContext
)
157 DISPLAY_MIN_WIDTH(this, result
);
164 /* virtual */ nscoord
165 nsSVGOuterSVGFrame::GetPrefISize(nsRenderingContext
*aRenderingContext
)
168 DISPLAY_PREF_WIDTH(this, result
);
170 SVGSVGElement
*svg
= static_cast<SVGSVGElement
*>(mContent
);
171 nsSVGLength2
&width
= svg
->mLengthAttributes
[SVGSVGElement::ATTR_WIDTH
];
173 if (width
.IsPercentage()) {
174 // It looks like our containing block's width may depend on our width. In
175 // that case our behavior is undefined according to CSS 2.1 section 10.3.2,
179 result
= nsPresContext::CSSPixelsToAppUnits(width
.GetAnimValue(svg
));
188 /* virtual */ IntrinsicSize
189 nsSVGOuterSVGFrame::GetIntrinsicSize()
191 // XXXjwatt Note that here we want to return the CSS width/height if they're
192 // specified and we're embedded inside an nsIObjectLoadingContent.
194 IntrinsicSize intrinsicSize
;
196 SVGSVGElement
*content
= static_cast<SVGSVGElement
*>(mContent
);
197 nsSVGLength2
&width
= content
->mLengthAttributes
[SVGSVGElement::ATTR_WIDTH
];
198 nsSVGLength2
&height
= content
->mLengthAttributes
[SVGSVGElement::ATTR_HEIGHT
];
200 if (!width
.IsPercentage()) {
201 nscoord val
= nsPresContext::CSSPixelsToAppUnits(width
.GetAnimValue(content
));
202 if (val
< 0) val
= 0;
203 intrinsicSize
.width
.SetCoordValue(val
);
206 if (!height
.IsPercentage()) {
207 nscoord val
= nsPresContext::CSSPixelsToAppUnits(height
.GetAnimValue(content
));
208 if (val
< 0) val
= 0;
209 intrinsicSize
.height
.SetCoordValue(val
);
212 return intrinsicSize
;
216 nsSVGOuterSVGFrame::GetIntrinsicRatio()
218 // We only have an intrinsic size/ratio if our width and height attributes
219 // are both specified and set to non-percentage values, or we have a viewBox
220 // rect: http://www.w3.org/TR/SVGMobile12/coords.html#IntrinsicSizing
222 SVGSVGElement
*content
= static_cast<SVGSVGElement
*>(mContent
);
223 nsSVGLength2
&width
= content
->mLengthAttributes
[SVGSVGElement::ATTR_WIDTH
];
224 nsSVGLength2
&height
= content
->mLengthAttributes
[SVGSVGElement::ATTR_HEIGHT
];
226 if (!width
.IsPercentage() && !height
.IsPercentage()) {
227 nsSize
ratio(NSToCoordRoundWithClamp(width
.GetAnimValue(content
)),
228 NSToCoordRoundWithClamp(height
.GetAnimValue(content
)));
229 if (ratio
.width
< 0) {
232 if (ratio
.height
< 0) {
238 SVGViewElement
* viewElement
= content
->GetCurrentViewElement();
239 const nsSVGViewBoxRect
* viewbox
= nullptr;
241 // The logic here should match HasViewBox().
242 if (viewElement
&& viewElement
->mViewBox
.HasRect()) {
243 viewbox
= &viewElement
->mViewBox
.GetAnimValue();
244 } else if (content
->mViewBox
.HasRect()) {
245 viewbox
= &content
->mViewBox
.GetAnimValue();
249 float viewBoxWidth
= viewbox
->width
;
250 float viewBoxHeight
= viewbox
->height
;
252 if (viewBoxWidth
< 0.0f
) {
255 if (viewBoxHeight
< 0.0f
) {
256 viewBoxHeight
= 0.0f
;
258 return nsSize(NSToCoordRoundWithClamp(viewBoxWidth
),
259 NSToCoordRoundWithClamp(viewBoxHeight
));
262 return nsSVGOuterSVGFrameBase::GetIntrinsicRatio();
267 nsSVGOuterSVGFrame::ComputeSize(nsRenderingContext
*aRenderingContext
,
269 const LogicalSize
& aCBSize
,
270 nscoord aAvailableISize
,
271 const LogicalSize
& aMargin
,
272 const LogicalSize
& aBorder
,
273 const LogicalSize
& aPadding
,
276 if (IsRootOfImage() || IsRootOfReplacedElementSubDoc()) {
277 // The embedding element has sized itself using the CSS replaced element
278 // sizing rules, using our intrinsic dimensions as necessary. The SVG spec
279 // says that the width and height of embedded SVG is overridden by the
280 // width and height of the embedding element, so we just need to size to
281 // the viewport that the embedding element has established for us.
285 LogicalSize cbSize
= aCBSize
;
286 IntrinsicSize intrinsicSize
= GetIntrinsicSize();
288 if (!mContent
->GetParent()) {
289 // We're the root of the outermost browsing context, so we need to scale
290 // cbSize by the full-zoom so that SVGs with percentage width/height zoom:
292 NS_ASSERTION(aCBSize
.ISize(aWM
) != NS_AUTOHEIGHT
&&
293 aCBSize
.BSize(aWM
) != NS_AUTOHEIGHT
,
294 "root should not have auto-width/height containing block");
295 cbSize
.ISize(aWM
) *= PresContext()->GetFullZoom();
296 cbSize
.BSize(aWM
) *= PresContext()->GetFullZoom();
298 // We also need to honour the width and height attributes' default values
299 // of 100% when we're the root of a browsing context. (GetIntrinsicSize()
300 // doesn't report these since there's no such thing as a percentage
301 // intrinsic size. Also note that explicit percentage values are mapped
302 // into style, so the following isn't for them.)
304 SVGSVGElement
* content
= static_cast<SVGSVGElement
*>(mContent
);
306 nsSVGLength2
&width
=
307 content
->mLengthAttributes
[SVGSVGElement::ATTR_WIDTH
];
308 if (width
.IsPercentage()) {
309 NS_ABORT_IF_FALSE(intrinsicSize
.width
.GetUnit() == eStyleUnit_None
,
310 "GetIntrinsicSize should have reported no "
312 float val
= width
.GetAnimValInSpecifiedUnits() / 100.0f
;
313 if (val
< 0.0f
) val
= 0.0f
;
314 intrinsicSize
.width
.SetCoordValue(val
* cbSize
.Width(aWM
));
317 nsSVGLength2
&height
=
318 content
->mLengthAttributes
[SVGSVGElement::ATTR_HEIGHT
];
319 NS_ASSERTION(aCBSize
.BSize(aWM
) != NS_AUTOHEIGHT
,
320 "root should not have auto-height containing block");
321 if (height
.IsPercentage()) {
322 NS_ABORT_IF_FALSE(intrinsicSize
.height
.GetUnit() == eStyleUnit_None
,
323 "GetIntrinsicSize should have reported no "
325 float val
= height
.GetAnimValInSpecifiedUnits() / 100.0f
;
326 if (val
< 0.0f
) val
= 0.0f
;
327 intrinsicSize
.height
.SetCoordValue(val
* cbSize
.Height(aWM
));
329 NS_ABORT_IF_FALSE(intrinsicSize
.height
.GetUnit() == eStyleUnit_Coord
&&
330 intrinsicSize
.width
.GetUnit() == eStyleUnit_Coord
,
331 "We should have just handled the only situation where"
332 "we lack an intrinsic height or width.");
335 return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(aWM
,
336 aRenderingContext
, this,
337 intrinsicSize
, GetIntrinsicRatio(),
345 nsSVGOuterSVGFrame::Reflow(nsPresContext
* aPresContext
,
346 nsHTMLReflowMetrics
& aDesiredSize
,
347 const nsHTMLReflowState
& aReflowState
,
348 nsReflowStatus
& aStatus
)
350 DO_GLOBAL_REFLOW_COUNT("nsSVGOuterSVGFrame");
351 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aDesiredSize
, aStatus
);
352 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS
,
353 ("enter nsSVGOuterSVGFrame::Reflow: availSize=%d,%d",
354 aReflowState
.AvailableWidth(), aReflowState
.AvailableHeight()));
356 NS_PRECONDITION(mState
& NS_FRAME_IN_REFLOW
, "frame is not in reflow");
358 aStatus
= NS_FRAME_COMPLETE
;
360 aDesiredSize
.Width() = aReflowState
.ComputedWidth() +
361 aReflowState
.ComputedPhysicalBorderPadding().LeftRight();
362 aDesiredSize
.Height() = aReflowState
.ComputedHeight() +
363 aReflowState
.ComputedPhysicalBorderPadding().TopBottom();
365 NS_ASSERTION(!GetPrevInFlow(), "SVG can't currently be broken across pages.");
367 SVGSVGElement
*svgElem
= static_cast<SVGSVGElement
*>(mContent
);
369 nsSVGOuterSVGAnonChildFrame
*anonKid
=
370 static_cast<nsSVGOuterSVGAnonChildFrame
*>(GetFirstPrincipalChild());
372 if (mState
& NS_FRAME_FIRST_REFLOW
) {
374 svgElem
->UpdateHasChildrenOnlyTransform();
377 // If our SVG viewport has changed, update our content and notify.
378 // http://www.w3.org/TR/SVG11/coords.html#ViewportSpace
380 svgFloatSize
newViewportSize(
381 nsPresContext::AppUnitsToFloatCSSPixels(aReflowState
.ComputedWidth()),
382 nsPresContext::AppUnitsToFloatCSSPixels(aReflowState
.ComputedHeight()));
384 svgFloatSize oldViewportSize
= svgElem
->GetViewportSize();
386 uint32_t changeBits
= 0;
387 if (newViewportSize
!= oldViewportSize
) {
388 // When our viewport size changes, we may need to update the overflow rects
389 // of our child frames. This is the case if:
391 // * We have a real/synthetic viewBox (a children-only transform), since
392 // the viewBox transform will change as the viewport dimensions change.
394 // * We do not have a real/synthetic viewBox, but the last time we
395 // reflowed (or the last time UpdateOverflow() was called) we did.
397 // We only handle the former case here, in which case we mark all our child
398 // frames as dirty so that we reflow them below and update their overflow
401 // In the latter case, updating of overflow rects is handled for removal of
402 // real viewBox (the viewBox attribute) in AttributeChanged. Synthetic
403 // viewBox "removal" (e.g. a document references the same SVG via both an
404 // <svg:image> and then as a CSS background image (a synthetic viewBox is
405 // used when painting the former, but not when painting the latter)) is
406 // handled in SVGSVGElement::FlushImageTransformInvalidation.
408 if (svgElem
->HasViewBoxOrSyntheticViewBox()) {
409 nsIFrame
* anonChild
= GetFirstPrincipalChild();
410 anonChild
->AddStateBits(NS_FRAME_IS_DIRTY
);
411 for (nsIFrame
* child
= anonChild
->GetFirstPrincipalChild(); child
;
412 child
= child
->GetNextSibling()) {
413 child
->AddStateBits(NS_FRAME_IS_DIRTY
);
416 changeBits
|= COORD_CONTEXT_CHANGED
;
417 svgElem
->SetViewportSize(newViewportSize
);
419 if (mFullZoom
!= PresContext()->GetFullZoom()) {
420 changeBits
|= FULL_ZOOM_CHANGED
;
421 mFullZoom
= PresContext()->GetFullZoom();
424 NotifyViewportOrTransformChanged(changeBits
);
426 mViewportInitialized
= true;
428 // Now that we've marked the necessary children as dirty, call
429 // ReflowSVG() or ReflowSVGNonDisplayText() on them, depending
430 // on whether we are non-display.
431 mCallingReflowSVG
= true;
432 if (GetStateBits() & NS_FRAME_IS_NONDISPLAY
) {
433 ReflowSVGNonDisplayText(this);
435 // Update the mRects and visual overflow rects of all our descendants,
436 // including our anonymous wrapper kid:
437 anonKid
->AddStateBits(mState
& NS_FRAME_IS_DIRTY
);
438 anonKid
->ReflowSVG();
439 NS_ABORT_IF_FALSE(!anonKid
->GetNextSibling(),
440 "We should have one anonymous child frame wrapping our real children");
442 mCallingReflowSVG
= false;
444 // Set our anonymous kid's offset from our border box:
445 anonKid
->SetPosition(GetContentRectRelativeToSelf().TopLeft());
447 // Including our size in our overflow rects regardless of the value of
448 // 'background', 'border', etc. makes sure that we usually (when we clip to
449 // our content area) don't have to keep changing our overflow rects as our
450 // descendants move about (see perf comment below). Including our size in our
451 // scrollable overflow rect also makes sure that we scroll if we're too big
454 // <svg> never allows scrolling to anything outside its mRect (only panning),
455 // so we must always keep our scrollable overflow set to our size.
457 // With regards to visual overflow, we always clip root-<svg> (see our
458 // BuildDisplayList method) regardless of the value of the 'overflow'
459 // property since that is per-spec, even for the initial 'visible' value. For
460 // that reason there's no point in adding descendant visual overflow to our
461 // own when this frame is for a root-<svg>. That said, there's also a very
462 // good performance reason for us wanting to avoid doing so. If we did, then
463 // the frame's overflow would often change as descendants that are partially
464 // or fully outside its rect moved (think animation on/off screen), and that
465 // would cause us to do a full NS_FRAME_IS_DIRTY reflow and repaint of the
466 // entire document tree each such move (see bug 875175).
468 // So it's only non-root outer-<svg> that has the visual overflow of its
469 // descendants added to its own. (Note that the default user-agent style
470 // sheet makes 'hidden' the default value for :not(root(svg)), so usually
471 // FinishAndStoreOverflow will still clip this back to the frame's rect.)
473 // WARNING!! Keep UpdateBounds below in sync with whatever we do for our
474 // overflow rects here! (Again, see bug 875175.)
476 aDesiredSize
.SetOverflowAreasToDesiredBounds();
477 if (!mIsRootContent
) {
478 aDesiredSize
.mOverflowAreas
.VisualOverflow().UnionRect(
479 aDesiredSize
.mOverflowAreas
.VisualOverflow(),
480 anonKid
->GetVisualOverflowRect() + anonKid
->GetPosition());
482 FinishAndStoreOverflow(&aDesiredSize
);
484 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS
,
485 ("exit nsSVGOuterSVGFrame::Reflow: size=%d,%d",
486 aDesiredSize
.Width(), aDesiredSize
.Height()));
487 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
491 nsSVGOuterSVGFrame::DidReflow(nsPresContext
* aPresContext
,
492 const nsHTMLReflowState
* aReflowState
,
493 nsDidReflowStatus aStatus
)
495 nsSVGOuterSVGFrameBase::DidReflow(aPresContext
,aReflowState
,aStatus
);
497 // Make sure elements styled by :hover get updated if script/animation moves
498 // them under or out from under the pointer:
499 PresContext()->PresShell()->SynthesizeMouseMove(false);
503 nsSVGOuterSVGFrame::UpdateOverflow()
505 // See the comments in Reflow above.
507 // WARNING!! Keep this in sync with Reflow above!
509 nsRect
rect(nsPoint(0, 0), GetSize());
510 nsOverflowAreas
overflowAreas(rect
, rect
);
512 if (!mIsRootContent
) {
513 nsIFrame
*anonKid
= GetFirstPrincipalChild();
514 overflowAreas
.VisualOverflow().UnionRect(
515 overflowAreas
.VisualOverflow(),
516 anonKid
->GetVisualOverflowRect() + anonKid
->GetPosition());
519 return FinishAndStoreOverflow(overflowAreas
, GetSize());
523 //----------------------------------------------------------------------
527 * Used to paint/hit-test SVG when SVG display lists are disabled.
529 class nsDisplayOuterSVG
: public nsDisplayItem
{
531 nsDisplayOuterSVG(nsDisplayListBuilder
* aBuilder
,
532 nsSVGOuterSVGFrame
* aFrame
) :
533 nsDisplayItem(aBuilder
, aFrame
) {
534 MOZ_COUNT_CTOR(nsDisplayOuterSVG
);
536 #ifdef NS_BUILD_REFCNT_LOGGING
537 virtual ~nsDisplayOuterSVG() {
538 MOZ_COUNT_DTOR(nsDisplayOuterSVG
);
542 virtual void HitTest(nsDisplayListBuilder
* aBuilder
, const nsRect
& aRect
,
543 HitTestState
* aState
,
544 nsTArray
<nsIFrame
*> *aOutFrames
) MOZ_OVERRIDE
;
545 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
546 nsRenderingContext
* aCtx
) MOZ_OVERRIDE
;
548 virtual void ComputeInvalidationRegion(nsDisplayListBuilder
* aBuilder
,
549 const nsDisplayItemGeometry
* aGeometry
,
550 nsRegion
* aInvalidRegion
) MOZ_OVERRIDE
;
552 NS_DISPLAY_DECL_NAME("SVGOuterSVG", TYPE_SVG_OUTER_SVG
)
556 nsDisplayOuterSVG::HitTest(nsDisplayListBuilder
* aBuilder
, const nsRect
& aRect
,
557 HitTestState
* aState
, nsTArray
<nsIFrame
*> *aOutFrames
)
559 nsSVGOuterSVGFrame
*outerSVGFrame
= static_cast<nsSVGOuterSVGFrame
*>(mFrame
);
561 nsPoint refFrameToContentBox
=
562 ToReferenceFrame() + outerSVGFrame
->GetContentRectRelativeToSelf().TopLeft();
564 nsPoint pointRelativeToContentBox
=
565 nsPoint(aRect
.x
+ aRect
.width
/ 2, aRect
.y
+ aRect
.height
/ 2) -
566 refFrameToContentBox
;
568 gfxPoint svgViewportRelativePoint
=
569 gfxPoint(pointRelativeToContentBox
.x
, pointRelativeToContentBox
.y
) /
570 outerSVGFrame
->PresContext()->AppUnitsPerCSSPixel();
572 nsSVGOuterSVGAnonChildFrame
*anonKid
=
573 static_cast<nsSVGOuterSVGAnonChildFrame
*>(
574 outerSVGFrame
->GetFirstPrincipalChild());
577 nsSVGUtils::HitTestChildren(anonKid
, svgViewportRelativePoint
);
579 aOutFrames
->AppendElement(frame
);
584 nsDisplayOuterSVG::Paint(nsDisplayListBuilder
* aBuilder
,
585 nsRenderingContext
* aContext
)
587 #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
588 PRTime start
= PR_Now();
591 // Create an SVGAutoRenderState so we can call SetPaintingToWindow on
592 // it, but do so without changing the render mode:
593 SVGAutoRenderState
state(aContext
, SVGAutoRenderState::GetRenderMode(aContext
));
595 if (aBuilder
->IsPaintingToWindow()) {
596 state
.SetPaintingToWindow(true);
599 nsRect viewportRect
=
600 mFrame
->GetContentRectRelativeToSelf() + ToReferenceFrame();
602 nsRect clipRect
= mVisibleRect
.Intersect(viewportRect
);
604 nsIntRect contentAreaDirtyRect
=
605 (clipRect
- viewportRect
.TopLeft()).
606 ToOutsidePixels(mFrame
->PresContext()->AppUnitsPerDevPixel());
608 aContext
->PushState();
609 aContext
->Translate(viewportRect
.TopLeft());
610 nsSVGUtils::PaintFrameWithEffects(aContext
, &contentAreaDirtyRect
, mFrame
);
611 aContext
->PopState();
613 NS_ASSERTION(!aContext
->ThebesContext()->HasError(), "Cairo in error state");
615 #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
616 PRTime end
= PR_Now();
617 printf("SVG Paint Timing: %f ms\n", (end
-start
)/1000.0);
621 static PLDHashOperator
CheckForeignObjectInvalidatedArea(nsPtrHashKey
<nsSVGForeignObjectFrame
>* aEntry
, void* aData
)
623 nsRegion
* region
= static_cast<nsRegion
*>(aData
);
624 region
->Or(*region
, aEntry
->GetKey()->GetInvalidRegion());
625 return PL_DHASH_NEXT
;
629 nsSVGOuterSVGFrame::FindInvalidatedForeignObjectFrameChildren(nsIFrame
* aFrame
)
632 if (mForeignObjectHash
&& mForeignObjectHash
->Count()) {
633 mForeignObjectHash
->EnumerateEntries(CheckForeignObjectInvalidatedArea
, &result
);
639 nsDisplayOuterSVG::ComputeInvalidationRegion(nsDisplayListBuilder
* aBuilder
,
640 const nsDisplayItemGeometry
* aGeometry
,
641 nsRegion
* aInvalidRegion
)
643 nsSVGOuterSVGFrame
*frame
= static_cast<nsSVGOuterSVGFrame
*>(mFrame
);
644 frame
->InvalidateSVG(frame
->FindInvalidatedForeignObjectFrameChildren(frame
));
646 nsRegion result
= frame
->GetInvalidRegion();
647 result
.MoveBy(ToReferenceFrame());
648 frame
->ClearInvalidRegion();
650 nsDisplayItem::ComputeInvalidationRegion(aBuilder
, aGeometry
, aInvalidRegion
);
651 aInvalidRegion
->Or(*aInvalidRegion
, result
);
655 nsSVGOuterSVGFrame::AttributeChanged(int32_t aNameSpaceID
,
659 if (aNameSpaceID
== kNameSpaceID_None
&&
660 !(GetStateBits() & (NS_FRAME_FIRST_REFLOW
| NS_FRAME_IS_NONDISPLAY
))) {
661 if (aAttribute
== nsGkAtoms::viewBox
||
662 aAttribute
== nsGkAtoms::preserveAspectRatio
||
663 aAttribute
== nsGkAtoms::transform
) {
665 // make sure our cached transform matrix gets (lazily) updated
668 nsSVGUtils::NotifyChildrenOfSVGChange(GetFirstPrincipalChild(),
669 aAttribute
== nsGkAtoms::viewBox
?
670 TRANSFORM_CHANGED
| COORD_CONTEXT_CHANGED
: TRANSFORM_CHANGED
);
672 if (aAttribute
!= nsGkAtoms::transform
) {
673 static_cast<SVGSVGElement
*>(mContent
)->ChildrenOnlyTransformChanged();
676 } else if (aAttribute
== nsGkAtoms::width
||
677 aAttribute
== nsGkAtoms::height
) {
679 // Don't call ChildrenOnlyTransformChanged() here, since we call it
680 // under Reflow if the width/height actually changed.
682 nsIFrame
* embeddingFrame
;
683 if (IsRootOfReplacedElementSubDoc(&embeddingFrame
) && embeddingFrame
) {
684 if (DependsOnIntrinsicSize(embeddingFrame
)) {
685 // Tell embeddingFrame's presShell it needs to be reflowed (which takes
686 // care of reflowing us too).
687 embeddingFrame
->PresContext()->PresShell()->
688 FrameNeedsReflow(embeddingFrame
, nsIPresShell::eStyleChange
, NS_FRAME_IS_DIRTY
);
690 // else our width and height is overridden - don't reflow anything
692 // We are not embedded by reference, so our 'width' and 'height'
693 // attributes are not overridden - we need to reflow.
694 PresContext()->PresShell()->
695 FrameNeedsReflow(this, nsIPresShell::eStyleChange
, NS_FRAME_IS_DIRTY
);
703 //----------------------------------------------------------------------
707 nsSVGOuterSVGFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
708 const nsRect
& aDirtyRect
,
709 const nsDisplayListSet
& aLists
)
711 if (GetStateBits() & NS_FRAME_IS_NONDISPLAY
) {
715 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
717 // Per-spec, we always clip root-<svg> even when 'overflow' has its initial
718 // value of 'visible'. See also the "visual overflow" comments in Reflow.
719 DisplayListClipState::AutoSaveRestore
autoSR(aBuilder
);
720 if (mIsRootContent
||
721 StyleDisplay()->IsScrollableOverflow()) {
722 autoSR
.ClipContainingBlockDescendantsToContentBox(aBuilder
, this);
725 if ((aBuilder
->IsForEventDelivery() &&
726 NS_SVGDisplayListHitTestingEnabled()) ||
727 (!aBuilder
->IsForEventDelivery() &&
728 NS_SVGDisplayListPaintingEnabled())) {
729 nsDisplayList
*contentList
= aLists
.Content();
730 nsDisplayListSet
set(contentList
, contentList
, contentList
,
731 contentList
, contentList
, contentList
);
732 BuildDisplayListForNonBlockChildren(aBuilder
, aDirtyRect
, set
);
734 aLists
.Content()->AppendNewToTop(
735 new (aBuilder
) nsDisplayOuterSVG(aBuilder
, this));
740 nsSVGOuterSVGFrame::GetSplittableType() const
742 return NS_FRAME_NOT_SPLITTABLE
;
746 nsSVGOuterSVGFrame::GetType() const
748 return nsGkAtoms::svgOuterSVGFrame
;
751 //----------------------------------------------------------------------
752 // nsISVGSVGFrame methods:
755 nsSVGOuterSVGFrame::NotifyViewportOrTransformChanged(uint32_t aFlags
)
757 NS_ABORT_IF_FALSE(aFlags
&&
758 !(aFlags
& ~(COORD_CONTEXT_CHANGED
| TRANSFORM_CHANGED
|
760 "Unexpected aFlags value");
762 // No point in doing anything when were not init'ed yet:
763 if (!mViewportInitialized
) {
767 SVGSVGElement
*content
= static_cast<SVGSVGElement
*>(mContent
);
769 if (aFlags
& COORD_CONTEXT_CHANGED
) {
770 if (content
->HasViewBoxRect()) {
771 // Percentage lengths on children resolve against the viewBox rect so we
772 // don't need to notify them of the viewport change, but the viewBox
773 // transform will have changed, so we need to notify them of that instead.
774 aFlags
= TRANSFORM_CHANGED
;
776 else if (content
->ShouldSynthesizeViewBox()) {
777 // In the case of a synthesized viewBox, the synthetic viewBox's rect
778 // changes as the viewport changes. As a result we need to maintain the
779 // COORD_CONTEXT_CHANGED flag.
780 aFlags
|= TRANSFORM_CHANGED
;
782 else if (mCanvasTM
&& mCanvasTM
->IsSingular()) {
783 // A width/height of zero will result in us having a singular mCanvasTM
784 // even when we don't have a viewBox. So we also want to recompute our
785 // mCanvasTM for this width/height change even though we don't have a
787 aFlags
|= TRANSFORM_CHANGED
;
791 bool haveNonFulLZoomTransformChange
= (aFlags
& TRANSFORM_CHANGED
);
793 if (aFlags
& FULL_ZOOM_CHANGED
) {
794 // Convert FULL_ZOOM_CHANGED to TRANSFORM_CHANGED:
795 aFlags
= (aFlags
& ~FULL_ZOOM_CHANGED
) | TRANSFORM_CHANGED
;
798 if (aFlags
& TRANSFORM_CHANGED
) {
799 // Make sure our canvas transform matrix gets (lazily) recalculated:
802 if (haveNonFulLZoomTransformChange
&&
803 !(mState
& NS_FRAME_IS_NONDISPLAY
)) {
804 uint32_t flags
= (mState
& NS_FRAME_IN_REFLOW
) ?
805 SVGSVGElement::eDuringReflow
: 0;
806 content
->ChildrenOnlyTransformChanged(flags
);
810 nsSVGUtils::NotifyChildrenOfSVGChange(GetFirstPrincipalChild(), aFlags
);
813 //----------------------------------------------------------------------
814 // nsISVGChildFrame methods:
817 nsSVGOuterSVGFrame::PaintSVG(nsRenderingContext
* aContext
,
818 const nsIntRect
*aDirtyRect
,
819 nsIFrame
* aTransformRoot
)
821 NS_ASSERTION(GetFirstPrincipalChild()->GetType() ==
822 nsGkAtoms::svgOuterSVGAnonChildFrame
&&
823 !GetFirstPrincipalChild()->GetNextSibling(),
824 "We should have a single, anonymous, child");
825 nsSVGOuterSVGAnonChildFrame
*anonKid
=
826 static_cast<nsSVGOuterSVGAnonChildFrame
*>(GetFirstPrincipalChild());
827 return anonKid
->PaintSVG(aContext
, aDirtyRect
, aTransformRoot
);
831 nsSVGOuterSVGFrame::GetBBoxContribution(const gfx::Matrix
&aToBBoxUserspace
,
834 NS_ASSERTION(GetFirstPrincipalChild()->GetType() ==
835 nsGkAtoms::svgOuterSVGAnonChildFrame
&&
836 !GetFirstPrincipalChild()->GetNextSibling(),
837 "We should have a single, anonymous, child");
838 // We must defer to our child so that we don't include our
839 // content->PrependLocalTransformsTo() transforms.
840 nsSVGOuterSVGAnonChildFrame
*anonKid
=
841 static_cast<nsSVGOuterSVGAnonChildFrame
*>(GetFirstPrincipalChild());
842 return anonKid
->GetBBoxContribution(aToBBoxUserspace
, aFlags
);
845 //----------------------------------------------------------------------
846 // nsSVGContainerFrame methods:
849 nsSVGOuterSVGFrame::GetCanvasTM(uint32_t aFor
, nsIFrame
* aTransformRoot
)
851 if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY
) && !aTransformRoot
) {
852 if (aFor
== FOR_PAINTING
&& NS_SVGDisplayListPaintingEnabled()) {
853 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this);
857 NS_ASSERTION(!aTransformRoot
, "transform root will be ignored here");
858 SVGSVGElement
*content
= static_cast<SVGSVGElement
*>(mContent
);
860 float devPxPerCSSPx
=
861 1.0f
/ PresContext()->AppUnitsToFloatCSSPixels(
862 PresContext()->AppUnitsPerDevPixel());
864 gfxMatrix tm
= content
->PrependLocalTransformsTo(
865 gfxMatrix().Scale(devPxPerCSSPx
, devPxPerCSSPx
));
866 mCanvasTM
= new gfxMatrix(tm
);
871 //----------------------------------------------------------------------
872 // Implementation helpers
875 nsSVGOuterSVGFrame::IsRootOfReplacedElementSubDoc(nsIFrame
**aEmbeddingFrame
)
877 if (!mContent
->GetParent()) {
878 // Our content is the document element
879 nsCOMPtr
<nsIDocShell
> docShell
= PresContext()->GetDocShell();
880 nsCOMPtr
<nsIDOMWindow
> window
;
882 window
= docShell
->GetWindow();
886 nsCOMPtr
<nsIDOMElement
> frameElement
;
887 window
->GetFrameElement(getter_AddRefs(frameElement
));
888 nsCOMPtr
<nsIObjectLoadingContent
> olc
= do_QueryInterface(frameElement
);
890 // Our document is inside an HTML 'object', 'embed' or 'applet' element
891 if (aEmbeddingFrame
) {
892 nsCOMPtr
<nsIContent
> element
= do_QueryInterface(frameElement
);
893 *aEmbeddingFrame
= element
->GetPrimaryFrame();
894 NS_ASSERTION(*aEmbeddingFrame
, "Yikes, no embedding frame!");
900 if (aEmbeddingFrame
) {
901 *aEmbeddingFrame
= nullptr;
907 nsSVGOuterSVGFrame::IsRootOfImage()
909 if (!mContent
->GetParent()) {
910 // Our content is the document element
911 nsIDocument
* doc
= mContent
->GetCurrentDoc();
912 if (doc
&& doc
->IsBeingUsedAsImage()) {
913 // Our document is being used as an image
922 nsSVGOuterSVGFrame::VerticalScrollbarNotNeeded() const
924 nsSVGLength2
&height
= static_cast<SVGSVGElement
*>(mContent
)->
925 mLengthAttributes
[SVGSVGElement::ATTR_HEIGHT
];
926 return height
.IsPercentage() && height
.GetBaseValInSpecifiedUnits() <= 100;
930 //----------------------------------------------------------------------
931 // Implementation of nsSVGOuterSVGAnonChildFrame
934 NS_NewSVGOuterSVGAnonChildFrame(nsIPresShell
* aPresShell
,
935 nsStyleContext
* aContext
)
937 return new (aPresShell
) nsSVGOuterSVGAnonChildFrame(aContext
);
940 NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGAnonChildFrame
)
944 nsSVGOuterSVGAnonChildFrame::Init(nsIContent
* aContent
,
945 nsContainerFrame
* aParent
,
946 nsIFrame
* aPrevInFlow
)
948 NS_ABORT_IF_FALSE(aParent
->GetType() == nsGkAtoms::svgOuterSVGFrame
,
949 "Unexpected parent");
950 nsSVGOuterSVGAnonChildFrameBase::Init(aContent
, aParent
, aPrevInFlow
);
955 nsSVGOuterSVGAnonChildFrame::GetType() const
957 return nsGkAtoms::svgOuterSVGAnonChildFrame
;
961 nsSVGOuterSVGAnonChildFrame::HasChildrenOnlyTransform(gfx::Matrix
*aTransform
) const
963 // We must claim our nsSVGOuterSVGFrame's children-only transforms as our own
964 // so that the children we are used to wrap are transformed properly.
966 SVGSVGElement
*content
= static_cast<SVGSVGElement
*>(mContent
);
968 bool hasTransform
= content
->HasChildrenOnlyTransform();
970 if (hasTransform
&& aTransform
) {
971 // Outer-<svg> doesn't use x/y, so we can pass eChildToUserSpace here.
973 *aTransform
= gfx::ToMatrix(
974 content
->PrependLocalTransformsTo(identity
,
975 nsSVGElement::eChildToUserSpace
));