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 "gfxMatrix.h"
11 #include "nsDisplayList.h"
12 #include "nsIDocument.h"
13 #include "nsIDOMWindow.h"
14 #include "nsIInterfaceRequestorUtils.h"
15 #include "nsIObjectLoadingContent.h"
16 #include "nsRenderingContext.h"
17 #include "nsStubMutationObserver.h"
18 #include "nsSVGIntegrationUtils.h"
19 #include "nsSVGForeignObjectFrame.h"
20 #include "mozilla/dom/SVGSVGElement.h"
21 #include "nsSVGTextFrame.h"
22 #include "mozilla/dom/SVGViewElement.h"
23 #include "nsSubDocumentFrame.h"
25 using namespace mozilla
;
26 using namespace mozilla::dom
;
28 class nsSVGMutationObserver
: public nsStubMutationObserver
31 // nsIMutationObserver interface
32 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
34 // nsISupports interface:
35 NS_IMETHOD
QueryInterface(const nsIID
& aIID
, void** aInstancePtr
);
37 NS_IMETHOD_(nsrefcnt
) AddRef() { return 1; }
38 NS_IMETHOD_(nsrefcnt
) Release() { return 1; }
40 static void UpdateTextFragmentTrees(nsIFrame
*aFrame
);
43 //----------------------------------------------------------------------
44 // nsISupports methods
46 NS_INTERFACE_MAP_BEGIN(nsSVGMutationObserver
)
47 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver
)
50 static nsSVGMutationObserver sSVGMutationObserver
;
52 //----------------------------------------------------------------------
53 // nsIMutationObserver methods
56 nsSVGMutationObserver::AttributeChanged(nsIDocument
* aDocument
,
62 if (aNameSpaceID
!= kNameSpaceID_XML
|| aAttribute
!= nsGkAtoms::space
) {
66 nsIFrame
* frame
= aElement
->GetPrimaryFrame();
71 // is the content a child of a text element
72 nsSVGTextContainerFrame
* containerFrame
= do_QueryFrame(frame
);
74 containerFrame
->NotifyGlyphMetricsChange();
77 // if not, are there text elements amongst its descendents
78 UpdateTextFragmentTrees(frame
);
81 //----------------------------------------------------------------------
82 // Implementation helpers
85 nsSVGOuterSVGFrame::RegisterForeignObject(nsSVGForeignObjectFrame
* aFrame
)
87 NS_ASSERTION(aFrame
, "Who on earth is calling us?!");
89 if (!mForeignObjectHash
.IsInitialized()) {
90 mForeignObjectHash
.Init();
93 NS_ASSERTION(!mForeignObjectHash
.GetEntry(aFrame
),
94 "nsSVGForeignObjectFrame already registered!");
96 mForeignObjectHash
.PutEntry(aFrame
);
98 NS_ASSERTION(mForeignObjectHash
.GetEntry(aFrame
),
99 "Failed to register nsSVGForeignObjectFrame!");
103 nsSVGOuterSVGFrame::UnregisterForeignObject(nsSVGForeignObjectFrame
* aFrame
)
105 NS_ASSERTION(aFrame
, "Who on earth is calling us?!");
106 NS_ASSERTION(mForeignObjectHash
.GetEntry(aFrame
),
107 "nsSVGForeignObjectFrame not in registry!");
108 return mForeignObjectHash
.RemoveEntry(aFrame
);
112 nsSVGMutationObserver::UpdateTextFragmentTrees(nsIFrame
*aFrame
)
114 nsIFrame
* kid
= aFrame
->GetFirstPrincipalChild();
116 if (kid
->GetType() == nsGkAtoms::svgTextFrame
) {
117 nsSVGTextFrame
* textFrame
= static_cast<nsSVGTextFrame
*>(kid
);
118 textFrame
->NotifyGlyphMetricsChange();
120 UpdateTextFragmentTrees(kid
);
122 kid
= kid
->GetNextSibling();
126 //----------------------------------------------------------------------
130 NS_NewSVGOuterSVGFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
132 return new (aPresShell
) nsSVGOuterSVGFrame(aContext
);
135 NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGFrame
)
137 nsSVGOuterSVGFrame::nsSVGOuterSVGFrame(nsStyleContext
* aContext
)
138 : nsSVGOuterSVGFrameBase(aContext
)
139 , mFullZoom(aContext
->PresContext()->GetFullZoom())
140 , mViewportInitialized(false)
141 , mIsRootContent(false)
143 // Outer-<svg> has CSS layout, so remove this bit:
144 RemoveStateBits(NS_FRAME_SVG_LAYOUT
);
148 nsSVGOuterSVGFrame::Init(nsIContent
* aContent
,
150 nsIFrame
* aPrevInFlow
)
152 NS_ASSERTION(aContent
->IsSVG(nsGkAtoms::svg
),
153 "Content is not an SVG 'svg' element!");
155 AddStateBits(NS_STATE_IS_OUTER_SVG
|
156 NS_FRAME_FONT_INFLATION_CONTAINER
|
157 NS_FRAME_FONT_INFLATION_FLOW_ROOT
);
159 // Check for conditional processing attributes here rather than in
160 // nsCSSFrameConstructor::FindSVGData because we want to avoid
161 // simply giving failing outer <svg> elements an nsSVGContainerFrame.
162 // We don't create other SVG frames if PassesConditionalProcessingTests
163 // returns false, but since we do create nsSVGOuterSVGFrame frames we
164 // prevent them from painting by [ab]use NS_STATE_SVG_NONDISPLAY_CHILD. The
165 // frame will be recreated via an nsChangeHint_ReconstructFrame restyle if
166 // the value returned by PassesConditionalProcessingTests changes.
167 SVGSVGElement
*svg
= static_cast<SVGSVGElement
*>(aContent
);
168 if (!svg
->PassesConditionalProcessingTests()) {
169 AddStateBits(NS_STATE_SVG_NONDISPLAY_CHILD
);
172 nsSVGOuterSVGFrameBase::Init(aContent
, aParent
, aPrevInFlow
);
174 nsIDocument
* doc
= mContent
->GetCurrentDoc();
176 // we only care about our content's zoom and pan values if it's the root element
177 if (doc
->GetRootElement() == mContent
) {
178 mIsRootContent
= true;
180 // sSVGMutationObserver has the same lifetime as the document so does
181 // not need to be removed
182 doc
->AddMutationObserverUnlessExists(&sSVGMutationObserver
);
186 //----------------------------------------------------------------------
187 // nsQueryFrame methods
189 NS_QUERYFRAME_HEAD(nsSVGOuterSVGFrame
)
190 NS_QUERYFRAME_ENTRY(nsISVGSVGFrame
)
191 NS_QUERYFRAME_TAIL_INHERITING(nsSVGOuterSVGFrameBase
)
193 //----------------------------------------------------------------------
196 //----------------------------------------------------------------------
199 /* virtual */ nscoord
200 nsSVGOuterSVGFrame::GetMinWidth(nsRenderingContext
*aRenderingContext
)
203 DISPLAY_MIN_WIDTH(this, result
);
210 /* virtual */ nscoord
211 nsSVGOuterSVGFrame::GetPrefWidth(nsRenderingContext
*aRenderingContext
)
214 DISPLAY_PREF_WIDTH(this, result
);
216 SVGSVGElement
*svg
= static_cast<SVGSVGElement
*>(mContent
);
217 nsSVGLength2
&width
= svg
->mLengthAttributes
[SVGSVGElement::ATTR_WIDTH
];
219 if (width
.IsPercentage()) {
220 // It looks like our containing block's width may depend on our width. In
221 // that case our behavior is undefined according to CSS 2.1 section 10.3.2,
225 result
= nsPresContext::CSSPixelsToAppUnits(width
.GetAnimValue(svg
));
234 /* virtual */ nsIFrame::IntrinsicSize
235 nsSVGOuterSVGFrame::GetIntrinsicSize()
237 // XXXjwatt Note that here we want to return the CSS width/height if they're
238 // specified and we're embedded inside an nsIObjectLoadingContent.
240 IntrinsicSize intrinsicSize
;
242 SVGSVGElement
*content
= static_cast<SVGSVGElement
*>(mContent
);
243 nsSVGLength2
&width
= content
->mLengthAttributes
[SVGSVGElement::ATTR_WIDTH
];
244 nsSVGLength2
&height
= content
->mLengthAttributes
[SVGSVGElement::ATTR_HEIGHT
];
246 if (!width
.IsPercentage()) {
247 nscoord val
= nsPresContext::CSSPixelsToAppUnits(width
.GetAnimValue(content
));
248 if (val
< 0) val
= 0;
249 intrinsicSize
.width
.SetCoordValue(val
);
252 if (!height
.IsPercentage()) {
253 nscoord val
= nsPresContext::CSSPixelsToAppUnits(height
.GetAnimValue(content
));
254 if (val
< 0) val
= 0;
255 intrinsicSize
.height
.SetCoordValue(val
);
258 return intrinsicSize
;
262 nsSVGOuterSVGFrame::GetIntrinsicRatio()
264 // We only have an intrinsic size/ratio if our width and height attributes
265 // are both specified and set to non-percentage values, or we have a viewBox
266 // rect: http://www.w3.org/TR/SVGMobile12/coords.html#IntrinsicSizing
268 SVGSVGElement
*content
= static_cast<SVGSVGElement
*>(mContent
);
269 nsSVGLength2
&width
= content
->mLengthAttributes
[SVGSVGElement::ATTR_WIDTH
];
270 nsSVGLength2
&height
= content
->mLengthAttributes
[SVGSVGElement::ATTR_HEIGHT
];
272 if (!width
.IsPercentage() && !height
.IsPercentage()) {
273 nsSize
ratio(NSToCoordRoundWithClamp(width
.GetAnimValue(content
)),
274 NSToCoordRoundWithClamp(height
.GetAnimValue(content
)));
275 if (ratio
.width
< 0) {
278 if (ratio
.height
< 0) {
284 SVGViewElement
* viewElement
= content
->GetCurrentViewElement();
285 const nsSVGViewBoxRect
* viewbox
= nullptr;
287 // The logic here should match HasViewBox().
288 if (viewElement
&& viewElement
->mViewBox
.HasRect()) {
289 viewbox
= &viewElement
->mViewBox
.GetAnimValue();
290 } else if (content
->mViewBox
.HasRect()) {
291 viewbox
= &content
->mViewBox
.GetAnimValue();
295 float viewBoxWidth
= viewbox
->width
;
296 float viewBoxHeight
= viewbox
->height
;
298 if (viewBoxWidth
< 0.0f
) {
301 if (viewBoxHeight
< 0.0f
) {
302 viewBoxHeight
= 0.0f
;
304 return nsSize(NSToCoordRoundWithClamp(viewBoxWidth
),
305 NSToCoordRoundWithClamp(viewBoxHeight
));
308 return nsSVGOuterSVGFrameBase::GetIntrinsicRatio();
312 nsSVGOuterSVGFrame::ComputeSize(nsRenderingContext
*aRenderingContext
,
313 nsSize aCBSize
, nscoord aAvailableWidth
,
314 nsSize aMargin
, nsSize aBorder
, nsSize aPadding
,
317 if (IsRootOfImage() || IsRootOfReplacedElementSubDoc()) {
318 // The embedding element has sized itself using the CSS replaced element
319 // sizing rules, using our intrinsic dimensions as necessary. The SVG spec
320 // says that the width and height of embedded SVG is overridden by the
321 // width and height of the embedding element, so we just need to size to
322 // the viewport that the embedding element has established for us.
326 nsSize cbSize
= aCBSize
;
327 IntrinsicSize intrinsicSize
= GetIntrinsicSize();
329 if (!mContent
->GetParent()) {
330 // We're the root of the outermost browsing context, so we need to scale
331 // cbSize by the full-zoom so that SVGs with percentage width/height zoom:
333 NS_ASSERTION(aCBSize
.width
!= NS_AUTOHEIGHT
&&
334 aCBSize
.height
!= NS_AUTOHEIGHT
,
335 "root should not have auto-width/height containing block");
336 cbSize
.width
*= PresContext()->GetFullZoom();
337 cbSize
.height
*= PresContext()->GetFullZoom();
339 // We also need to honour the width and height attributes' default values
340 // of 100% when we're the root of a browsing context. (GetIntrinsicSize()
341 // doesn't report these since there's no such thing as a percentage
342 // intrinsic size. Also note that explicit percentage values are mapped
343 // into style, so the following isn't for them.)
345 SVGSVGElement
* content
= static_cast<SVGSVGElement
*>(mContent
);
347 nsSVGLength2
&width
=
348 content
->mLengthAttributes
[SVGSVGElement::ATTR_WIDTH
];
349 if (width
.IsPercentage()) {
350 NS_ABORT_IF_FALSE(intrinsicSize
.width
.GetUnit() == eStyleUnit_None
,
351 "GetIntrinsicSize should have reported no "
353 float val
= width
.GetAnimValInSpecifiedUnits() / 100.0f
;
354 if (val
< 0.0f
) val
= 0.0f
;
355 intrinsicSize
.width
.SetCoordValue(val
* cbSize
.width
);
358 nsSVGLength2
&height
=
359 content
->mLengthAttributes
[SVGSVGElement::ATTR_HEIGHT
];
360 NS_ASSERTION(aCBSize
.height
!= NS_AUTOHEIGHT
,
361 "root should not have auto-height containing block");
362 if (height
.IsPercentage()) {
363 NS_ABORT_IF_FALSE(intrinsicSize
.height
.GetUnit() == eStyleUnit_None
,
364 "GetIntrinsicSize should have reported no "
366 float val
= height
.GetAnimValInSpecifiedUnits() / 100.0f
;
367 if (val
< 0.0f
) val
= 0.0f
;
368 intrinsicSize
.height
.SetCoordValue(val
* cbSize
.height
);
370 NS_ABORT_IF_FALSE(intrinsicSize
.height
.GetUnit() == eStyleUnit_Coord
&&
371 intrinsicSize
.width
.GetUnit() == eStyleUnit_Coord
,
372 "We should have just handled the only situation where"
373 "we lack an intrinsic height or width.");
376 return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(
377 aRenderingContext
, this,
378 intrinsicSize
, GetIntrinsicRatio(), cbSize
,
379 aMargin
, aBorder
, aPadding
);
383 nsSVGOuterSVGFrame::Reflow(nsPresContext
* aPresContext
,
384 nsHTMLReflowMetrics
& aDesiredSize
,
385 const nsHTMLReflowState
& aReflowState
,
386 nsReflowStatus
& aStatus
)
388 DO_GLOBAL_REFLOW_COUNT("nsSVGOuterSVGFrame");
389 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aDesiredSize
, aStatus
);
390 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS
,
391 ("enter nsSVGOuterSVGFrame::Reflow: availSize=%d,%d",
392 aReflowState
.availableWidth
, aReflowState
.availableHeight
));
394 NS_PRECONDITION(mState
& NS_FRAME_IN_REFLOW
, "frame is not in reflow");
396 aStatus
= NS_FRAME_COMPLETE
;
398 aDesiredSize
.width
= aReflowState
.ComputedWidth() +
399 aReflowState
.mComputedBorderPadding
.LeftRight();
400 aDesiredSize
.height
= aReflowState
.ComputedHeight() +
401 aReflowState
.mComputedBorderPadding
.TopBottom();
403 NS_ASSERTION(!GetPrevInFlow(), "SVG can't currently be broken across pages.");
405 SVGSVGElement
*svgElem
= static_cast<SVGSVGElement
*>(mContent
);
407 nsSVGOuterSVGAnonChildFrame
*anonKid
=
408 static_cast<nsSVGOuterSVGAnonChildFrame
*>(GetFirstPrincipalChild());
410 if (mState
& NS_FRAME_FIRST_REFLOW
) {
412 svgElem
->UpdateHasChildrenOnlyTransform();
415 // If our SVG viewport has changed, update our content and notify.
416 // http://www.w3.org/TR/SVG11/coords.html#ViewportSpace
418 svgFloatSize
newViewportSize(
419 nsPresContext::AppUnitsToFloatCSSPixels(aReflowState
.ComputedWidth()),
420 nsPresContext::AppUnitsToFloatCSSPixels(aReflowState
.ComputedHeight()));
422 svgFloatSize oldViewportSize
= svgElem
->GetViewportSize();
424 uint32_t changeBits
= 0;
425 if (newViewportSize
!= oldViewportSize
) {
426 if (oldViewportSize
.width
<= 0.0f
|| oldViewportSize
.height
<= 0.0f
) {
427 // The overflow rects of our child frames will be empty if we had a
428 // [synthetic] viewBox during our last reflow, since under
429 // FinishAndStoreOverflow() the nsDisplayTransform::TransformRect call
430 // will have ended up calling SVGSVGElement::GetViewBoxTransform()
431 // which will have returned the identity matrix due to our viewport
432 // having been zero-sized. Mark all our child frames as dirty so that we
433 // reflow them below and update their overflow rects:
434 nsIFrame
* anonChild
= GetFirstPrincipalChild();
435 anonChild
->AddStateBits(NS_FRAME_IS_DIRTY
);
436 for (nsIFrame
* child
= anonChild
->GetFirstPrincipalChild(); child
;
437 child
= child
->GetNextSibling()) {
438 child
->AddStateBits(NS_FRAME_IS_DIRTY
);
441 changeBits
|= COORD_CONTEXT_CHANGED
;
442 svgElem
->SetViewportSize(newViewportSize
);
444 if (mFullZoom
!= PresContext()->GetFullZoom()) {
445 changeBits
|= FULL_ZOOM_CHANGED
;
446 mFullZoom
= PresContext()->GetFullZoom();
449 NotifyViewportOrTransformChanged(changeBits
);
451 mViewportInitialized
= true;
453 if (!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD
)) {
454 // Now that we've marked the necessary children as dirty, call
455 // ReflowSVG() on them:
457 mCallingReflowSVG
= true;
459 // Update the mRects and visual overflow rects of all our descendants,
460 // including our anonymous wrapper kid:
461 anonKid
->AddStateBits(mState
& NS_FRAME_IS_DIRTY
);
462 anonKid
->ReflowSVG();
463 NS_ABORT_IF_FALSE(!anonKid
->GetNextSibling(),
464 "We should have one anonymous child frame wrapping our real children");
466 mCallingReflowSVG
= false;
469 // Make sure we scroll if we're too big:
470 // XXX Use the bounding box of our descendants? (See bug 353460 comment 14.)
471 aDesiredSize
.SetOverflowAreasToDesiredBounds();
472 FinishAndStoreOverflow(&aDesiredSize
);
474 // Set our anonymous kid's offset from our border box:
475 anonKid
->SetPosition(GetContentRectRelativeToSelf().TopLeft());
477 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS
,
478 ("exit nsSVGOuterSVGFrame::Reflow: size=%d,%d",
479 aDesiredSize
.width
, aDesiredSize
.height
));
480 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowState
, aDesiredSize
);
485 nsSVGOuterSVGFrame::DidReflow(nsPresContext
* aPresContext
,
486 const nsHTMLReflowState
* aReflowState
,
487 nsDidReflowStatus aStatus
)
489 nsresult rv
= nsSVGOuterSVGFrameBase::DidReflow(aPresContext
,aReflowState
,aStatus
);
491 // Make sure elements styled by :hover get updated if script/animation moves
492 // them under or out from under the pointer:
493 PresContext()->PresShell()->SynthesizeMouseMove(false);
498 //----------------------------------------------------------------------
502 * Used to paint/hit-test SVG when SVG display lists are disabled.
504 class nsDisplayOuterSVG
: public nsDisplayItem
{
506 nsDisplayOuterSVG(nsDisplayListBuilder
* aBuilder
,
507 nsSVGOuterSVGFrame
* aFrame
) :
508 nsDisplayItem(aBuilder
, aFrame
) {
509 MOZ_COUNT_CTOR(nsDisplayOuterSVG
);
511 #ifdef NS_BUILD_REFCNT_LOGGING
512 virtual ~nsDisplayOuterSVG() {
513 MOZ_COUNT_DTOR(nsDisplayOuterSVG
);
517 virtual void HitTest(nsDisplayListBuilder
* aBuilder
, const nsRect
& aRect
,
518 HitTestState
* aState
, nsTArray
<nsIFrame
*> *aOutFrames
);
519 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
520 nsRenderingContext
* aCtx
);
522 virtual void ComputeInvalidationRegion(nsDisplayListBuilder
* aBuilder
,
523 const nsDisplayItemGeometry
* aGeometry
,
524 nsRegion
* aInvalidRegion
);
526 NS_DISPLAY_DECL_NAME("SVGOuterSVG", TYPE_SVG_OUTER_SVG
)
530 nsDisplayOuterSVG::HitTest(nsDisplayListBuilder
* aBuilder
, const nsRect
& aRect
,
531 HitTestState
* aState
, nsTArray
<nsIFrame
*> *aOutFrames
)
533 nsSVGOuterSVGFrame
*outerSVGFrame
= static_cast<nsSVGOuterSVGFrame
*>(mFrame
);
534 nsRect rectAtOrigin
= aRect
- ToReferenceFrame();
535 nsRect
thisRect(nsPoint(0,0), outerSVGFrame
->GetSize());
536 if (!thisRect
.Intersects(rectAtOrigin
))
539 nsPoint
rectCenter(rectAtOrigin
.x
+ rectAtOrigin
.width
/ 2,
540 rectAtOrigin
.y
+ rectAtOrigin
.height
/ 2);
542 nsSVGOuterSVGAnonChildFrame
*anonKid
=
543 static_cast<nsSVGOuterSVGAnonChildFrame
*>(
544 outerSVGFrame
->GetFirstPrincipalChild());
545 nsIFrame
* frame
= nsSVGUtils::HitTestChildren(
546 anonKid
, rectCenter
+ outerSVGFrame
->GetPosition() -
547 outerSVGFrame
->GetContentRect().TopLeft());
549 aOutFrames
->AppendElement(frame
);
554 nsDisplayOuterSVG::Paint(nsDisplayListBuilder
* aBuilder
,
555 nsRenderingContext
* aContext
)
557 #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
558 PRTime start
= PR_Now();
561 // Create an SVGAutoRenderState so we can call SetPaintingToWindow on
562 // it, but do so without changing the render mode:
563 SVGAutoRenderState
state(aContext
, SVGAutoRenderState::GetRenderMode(aContext
));
565 if (aBuilder
->IsPaintingToWindow()) {
566 state
.SetPaintingToWindow(true);
569 nsRect viewportRect
=
570 mFrame
->GetContentRectRelativeToSelf() + ToReferenceFrame();
572 nsRect clipRect
= mVisibleRect
.Intersect(viewportRect
);
574 nsIntRect contentAreaDirtyRect
=
575 (clipRect
- viewportRect
.TopLeft()).
576 ToOutsidePixels(mFrame
->PresContext()->AppUnitsPerDevPixel());
578 aContext
->PushState();
579 aContext
->Translate(viewportRect
.TopLeft());
580 nsSVGUtils::PaintFrameWithEffects(aContext
, &contentAreaDirtyRect
, mFrame
);
581 aContext
->PopState();
583 NS_ASSERTION(!aContext
->ThebesContext()->HasError(), "Cairo in error state");
585 #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
586 PRTime end
= PR_Now();
587 printf("SVG Paint Timing: %f ms\n", (end
-start
)/1000.0);
591 static PLDHashOperator
CheckForeignObjectInvalidatedArea(nsPtrHashKey
<nsSVGForeignObjectFrame
>* aEntry
, void* aData
)
593 nsRegion
* region
= static_cast<nsRegion
*>(aData
);
594 region
->Or(*region
, aEntry
->GetKey()->GetInvalidRegion());
595 return PL_DHASH_NEXT
;
599 nsSVGOuterSVGFrame::FindInvalidatedForeignObjectFrameChildren(nsIFrame
* aFrame
)
602 if (mForeignObjectHash
.Count()) {
603 mForeignObjectHash
.EnumerateEntries(CheckForeignObjectInvalidatedArea
, &result
);
609 nsDisplayOuterSVG::ComputeInvalidationRegion(nsDisplayListBuilder
* aBuilder
,
610 const nsDisplayItemGeometry
* aGeometry
,
611 nsRegion
* aInvalidRegion
)
613 nsSVGOuterSVGFrame
*frame
= static_cast<nsSVGOuterSVGFrame
*>(mFrame
);
614 frame
->InvalidateSVG(frame
->FindInvalidatedForeignObjectFrameChildren(frame
));
616 nsRegion result
= frame
->GetInvalidRegion();
617 result
.MoveBy(ToReferenceFrame());
618 frame
->ClearInvalidRegion();
620 nsDisplayItem::ComputeInvalidationRegion(aBuilder
, aGeometry
, aInvalidRegion
);
621 aInvalidRegion
->Or(*aInvalidRegion
, result
);
626 DependsOnIntrinsicSize(const nsIFrame
* aEmbeddingFrame
)
628 const nsStylePosition
*pos
= aEmbeddingFrame
->StylePosition();
629 const nsStyleCoord
&width
= pos
->mWidth
;
630 const nsStyleCoord
&height
= pos
->mHeight
;
632 // XXX it would be nice to know if the size of aEmbeddingFrame's containing
633 // block depends on aEmbeddingFrame, then we'd know if we can return false
634 // for eStyleUnit_Percent too.
635 return !width
.ConvertsToLength() ||
636 !height
.ConvertsToLength();
640 nsSVGOuterSVGFrame::AttributeChanged(int32_t aNameSpaceID
,
644 if (aNameSpaceID
== kNameSpaceID_None
&&
645 !(GetStateBits() & (NS_FRAME_FIRST_REFLOW
| NS_STATE_SVG_NONDISPLAY_CHILD
))) {
646 if (aAttribute
== nsGkAtoms::viewBox
||
647 aAttribute
== nsGkAtoms::preserveAspectRatio
||
648 aAttribute
== nsGkAtoms::transform
) {
650 // make sure our cached transform matrix gets (lazily) updated
653 nsSVGUtils::NotifyChildrenOfSVGChange(GetFirstPrincipalChild(),
654 aAttribute
== nsGkAtoms::viewBox
?
655 TRANSFORM_CHANGED
| COORD_CONTEXT_CHANGED
: TRANSFORM_CHANGED
);
657 static_cast<SVGSVGElement
*>(mContent
)->ChildrenOnlyTransformChanged();
659 } else if (aAttribute
== nsGkAtoms::width
||
660 aAttribute
== nsGkAtoms::height
) {
662 // Don't call ChildrenOnlyTransformChanged() here, since we call it
663 // under Reflow if the width/height actually changed.
665 nsIFrame
* embeddingFrame
;
666 if (IsRootOfReplacedElementSubDoc(&embeddingFrame
) && embeddingFrame
) {
667 if (DependsOnIntrinsicSize(embeddingFrame
)) {
668 // Tell embeddingFrame's presShell it needs to be reflowed (which takes
669 // care of reflowing us too).
670 embeddingFrame
->PresContext()->PresShell()->
671 FrameNeedsReflow(embeddingFrame
, nsIPresShell::eStyleChange
, NS_FRAME_IS_DIRTY
);
673 // else our width and height is overridden - don't reflow anything
675 // We are not embedded by reference, so our 'width' and 'height'
676 // attributes are not overridden - we need to reflow.
677 PresContext()->PresShell()->
678 FrameNeedsReflow(this, nsIPresShell::eStyleChange
, NS_FRAME_IS_DIRTY
);
686 //----------------------------------------------------------------------
690 nsSVGOuterSVGFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
691 const nsRect
& aDirtyRect
,
692 const nsDisplayListSet
& aLists
)
694 if (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD
) {
698 DisplayBorderBackgroundOutline(aBuilder
, aLists
);
700 DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox
clip(aBuilder
, this);
702 if ((aBuilder
->IsForEventDelivery() &&
703 NS_SVGDisplayListHitTestingEnabled()) ||
704 NS_SVGDisplayListPaintingEnabled()) {
705 nsDisplayList
*contentList
= aLists
.Content();
706 nsDisplayListSet
set(contentList
, contentList
, contentList
,
707 contentList
, contentList
, contentList
);
708 BuildDisplayListForNonBlockChildren(aBuilder
, aDirtyRect
, set
);
710 aLists
.Content()->AppendNewToTop(
711 new (aBuilder
) nsDisplayOuterSVG(aBuilder
, this));
716 nsSVGOuterSVGFrame::GetSplittableType() const
718 return NS_FRAME_NOT_SPLITTABLE
;
722 nsSVGOuterSVGFrame::GetType() const
724 return nsGkAtoms::svgOuterSVGFrame
;
727 //----------------------------------------------------------------------
728 // nsISVGSVGFrame methods:
731 nsSVGOuterSVGFrame::NotifyViewportOrTransformChanged(uint32_t aFlags
)
733 NS_ABORT_IF_FALSE(aFlags
&&
734 !(aFlags
& ~(COORD_CONTEXT_CHANGED
| TRANSFORM_CHANGED
|
736 "Unexpected aFlags value");
738 // No point in doing anything when were not init'ed yet:
739 if (!mViewportInitialized
) {
743 SVGSVGElement
*content
= static_cast<SVGSVGElement
*>(mContent
);
745 if (aFlags
& COORD_CONTEXT_CHANGED
) {
746 if (content
->HasViewBoxRect()) {
747 // Percentage lengths on children resolve against the viewBox rect so we
748 // don't need to notify them of the viewport change, but the viewBox
749 // transform will have changed, so we need to notify them of that instead.
750 aFlags
= TRANSFORM_CHANGED
;
752 else if (content
->ShouldSynthesizeViewBox()) {
753 // In the case of a synthesized viewBox, the synthetic viewBox's rect
754 // changes as the viewport changes. As a result we need to maintain the
755 // COORD_CONTEXT_CHANGED flag.
756 aFlags
|= TRANSFORM_CHANGED
;
758 else if (mCanvasTM
&& mCanvasTM
->IsSingular()) {
759 // A width/height of zero will result in us having a singular mCanvasTM
760 // even when we don't have a viewBox. So we also want to recompute our
761 // mCanvasTM for this width/height change even though we don't have a
763 aFlags
|= TRANSFORM_CHANGED
;
767 bool haveNonFulLZoomTransformChange
= (aFlags
& TRANSFORM_CHANGED
);
769 if (aFlags
& FULL_ZOOM_CHANGED
) {
770 // Convert FULL_ZOOM_CHANGED to TRANSFORM_CHANGED:
771 aFlags
= (aFlags
& ~FULL_ZOOM_CHANGED
) | TRANSFORM_CHANGED
;
774 if (aFlags
& TRANSFORM_CHANGED
) {
775 // Make sure our canvas transform matrix gets (lazily) recalculated:
778 if (haveNonFulLZoomTransformChange
&&
779 !(mState
& NS_STATE_SVG_NONDISPLAY_CHILD
)) {
780 uint32_t flags
= (mState
& NS_FRAME_IN_REFLOW
) ?
781 SVGSVGElement::eDuringReflow
: 0;
782 content
->ChildrenOnlyTransformChanged(flags
);
786 nsSVGUtils::NotifyChildrenOfSVGChange(GetFirstPrincipalChild(), aFlags
);
789 //----------------------------------------------------------------------
790 // nsISVGChildFrame methods:
793 nsSVGOuterSVGFrame::PaintSVG(nsRenderingContext
* aContext
,
794 const nsIntRect
*aDirtyRect
)
796 NS_ASSERTION(GetFirstPrincipalChild()->GetType() ==
797 nsGkAtoms::svgOuterSVGAnonChildFrame
&&
798 !GetFirstPrincipalChild()->GetNextSibling(),
799 "We should have a single, anonymous, child");
800 nsSVGOuterSVGAnonChildFrame
*anonKid
=
801 static_cast<nsSVGOuterSVGAnonChildFrame
*>(GetFirstPrincipalChild());
802 return anonKid
->PaintSVG(aContext
, aDirtyRect
);
806 nsSVGOuterSVGFrame::GetBBoxContribution(const gfxMatrix
&aToBBoxUserspace
,
809 NS_ASSERTION(GetFirstPrincipalChild()->GetType() ==
810 nsGkAtoms::svgOuterSVGAnonChildFrame
&&
811 !GetFirstPrincipalChild()->GetNextSibling(),
812 "We should have a single, anonymous, child");
813 // We must defer to our child so that we don't include our
814 // content->PrependLocalTransformsTo() transforms.
815 nsSVGOuterSVGAnonChildFrame
*anonKid
=
816 static_cast<nsSVGOuterSVGAnonChildFrame
*>(GetFirstPrincipalChild());
817 return anonKid
->GetBBoxContribution(aToBBoxUserspace
, aFlags
);
820 //----------------------------------------------------------------------
821 // nsSVGContainerFrame methods:
824 nsSVGOuterSVGFrame::GetCanvasTM(uint32_t aFor
)
826 if (!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD
)) {
827 if ((aFor
== FOR_PAINTING
&& NS_SVGDisplayListPaintingEnabled()) ||
828 (aFor
== FOR_HIT_TESTING
&& NS_SVGDisplayListHitTestingEnabled())) {
829 return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this);
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()->GetContainer();
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(gfxMatrix
*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.
945 content
->PrependLocalTransformsTo(identity
,
946 nsSVGElement::eChildToUserSpace
);