Bug 1012740 - Change as not to abort in GrallocTextureHostOGL::GrallocTextureHostOGL...
[gecko.git] / layout / svg / nsSVGOuterSVGFrame.cpp
blob313c8344ae4703d4505c4efb0b306e2a444b960b
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/. */
6 // Main header first:
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
28 void
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!");
46 void
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 //----------------------------------------------------------------------
56 // Implementation
58 nsIFrame*
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);
76 void
77 nsSVGOuterSVGFrame::Init(nsIContent* aContent,
78 nsIFrame* aParent,
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();
104 if (doc) {
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 //----------------------------------------------------------------------
120 // nsIFrame methods
122 //----------------------------------------------------------------------
123 // reflowing
125 /* virtual */ nscoord
126 nsSVGOuterSVGFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
128 nscoord result;
129 DISPLAY_MIN_WIDTH(this, result);
131 result = nscoord(0);
133 return result;
136 /* virtual */ nscoord
137 nsSVGOuterSVGFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
139 nscoord result;
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,
148 // so return zero.
149 result = nscoord(0);
150 } else {
151 result = nsPresContext::CSSPixelsToAppUnits(width.GetAnimValue(svg));
152 if (result < 0) {
153 result = nscoord(0);
157 return result;
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;
187 /* virtual */ nsSize
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) {
202 ratio.width = 0;
204 if (ratio.height < 0) {
205 ratio.height = 0;
207 return ratio;
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();
220 if (viewbox) {
221 float viewBoxWidth = viewbox->width;
222 float viewBoxHeight = viewbox->height;
224 if (viewBoxWidth < 0.0f) {
225 viewBoxWidth = 0.0f;
227 if (viewBoxHeight < 0.0f) {
228 viewBoxHeight = 0.0f;
230 return nsSize(NSToCoordRoundWithClamp(viewBoxWidth),
231 NSToCoordRoundWithClamp(viewBoxHeight));
234 return nsSVGOuterSVGFrameBase::GetIntrinsicRatio();
237 /* virtual */ nsSize
238 nsSVGOuterSVGFrame::ComputeSize(nsRenderingContext *aRenderingContext,
239 nsSize aCBSize, nscoord aAvailableWidth,
240 nsSize aMargin, nsSize aBorder, nsSize aPadding,
241 uint32_t aFlags)
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.
249 return aCBSize;
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 "
278 "intrinsic width");
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 "
291 "intrinsic height");
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);
308 void
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) {
337 // Initialize
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
363 // rects.
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();
387 if (changeBits) {
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);
398 } else {
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
416 // for our viewport.
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);
454 void
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);
466 /* virtual */ bool
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 //----------------------------------------------------------------------
488 // container methods
491 * Used to paint/hit-test SVG when SVG display lists are disabled.
493 class nsDisplayOuterSVG : public nsDisplayItem {
494 public:
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);
504 #endif
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)
519 void
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))
527 return;
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());
538 if (frame) {
539 aOutFrames->AppendElement(frame);
543 void
544 nsDisplayOuterSVG::Paint(nsDisplayListBuilder* aBuilder,
545 nsRenderingContext* aContext)
547 #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
548 PRTime start = PR_Now();
549 #endif
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);
578 #endif
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;
588 nsRegion
589 nsSVGOuterSVGFrame::FindInvalidatedForeignObjectFrameChildren(nsIFrame* aFrame)
591 nsRegion result;
592 if (mForeignObjectHash && mForeignObjectHash->Count()) {
593 mForeignObjectHash->EnumerateEntries(CheckForeignObjectInvalidatedArea, &result);
595 return result;
598 void
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);
614 // helper
615 static inline bool
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();
629 nsresult
630 nsSVGOuterSVGFrame::AttributeChanged(int32_t aNameSpaceID,
631 nsIAtom* aAttribute,
632 int32_t aModType)
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
641 mCanvasTM = nullptr;
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
666 } else {
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);
675 return NS_OK;
678 //----------------------------------------------------------------------
679 // painting
681 void
682 nsSVGOuterSVGFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
683 const nsRect& aDirtyRect,
684 const nsDisplayListSet& aLists)
686 if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
687 return;
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);
707 } else {
708 aLists.Content()->AppendNewToTop(
709 new (aBuilder) nsDisplayOuterSVG(aBuilder, this));
713 nsSplittableType
714 nsSVGOuterSVGFrame::GetSplittableType() const
716 return NS_FRAME_NOT_SPLITTABLE;
719 nsIAtom *
720 nsSVGOuterSVGFrame::GetType() const
722 return nsGkAtoms::svgOuterSVGFrame;
725 //----------------------------------------------------------------------
726 // nsISVGSVGFrame methods:
728 void
729 nsSVGOuterSVGFrame::NotifyViewportOrTransformChanged(uint32_t aFlags)
731 NS_ABORT_IF_FALSE(aFlags &&
732 !(aFlags & ~(COORD_CONTEXT_CHANGED | TRANSFORM_CHANGED |
733 FULL_ZOOM_CHANGED)),
734 "Unexpected aFlags value");
736 // No point in doing anything when were not init'ed yet:
737 if (!mViewportInitialized) {
738 return;
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
760 // viewBox.
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:
774 mCanvasTM = nullptr;
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:
790 nsresult
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);
804 SVGBBox
805 nsSVGOuterSVGFrame::GetBBoxContribution(const gfx::Matrix &aToBBoxUserspace,
806 uint32_t aFlags)
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:
822 gfxMatrix
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);
831 if (!mCanvasTM) {
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);
843 return *mCanvasTM;
846 //----------------------------------------------------------------------
847 // Implementation helpers
849 bool
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);
856 if (window) {
857 nsCOMPtr<nsIDOMElement> frameElement;
858 window->GetFrameElement(getter_AddRefs(frameElement));
859 nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(frameElement);
860 if (olc) {
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!");
867 return true;
871 if (aEmbeddingFrame) {
872 *aEmbeddingFrame = nullptr;
874 return false;
877 bool
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
885 return true;
889 return false;
892 bool
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
904 nsIFrame*
905 NS_NewSVGOuterSVGAnonChildFrame(nsIPresShell* aPresShell,
906 nsStyleContext* aContext)
908 return new (aPresShell) nsSVGOuterSVGAnonChildFrame(aContext);
911 NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGAnonChildFrame)
913 #ifdef DEBUG
914 void
915 nsSVGOuterSVGAnonChildFrame::Init(nsIContent* aContent,
916 nsIFrame* aParent,
917 nsIFrame* aPrevInFlow)
919 NS_ABORT_IF_FALSE(aParent->GetType() == nsGkAtoms::svgOuterSVGFrame,
920 "Unexpected parent");
921 nsSVGOuterSVGAnonChildFrameBase::Init(aContent, aParent, aPrevInFlow);
923 #endif
925 nsIAtom *
926 nsSVGOuterSVGAnonChildFrame::GetType() const
928 return nsGkAtoms::svgOuterSVGAnonChildFrame;
931 bool
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.
943 gfxMatrix identity;
944 *aTransform = gfx::ToMatrix(
945 content->PrependLocalTransformsTo(identity,
946 nsSVGElement::eChildToUserSpace));
949 return hasTransform;