Bumping manifests a=b2g-bump
[gecko.git] / layout / svg / nsSVGOuterSVGFrame.cpp
blobe4d320203caf7d4b11c0285780405b605127641e
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 nsContainerFrame*
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 // helper
77 static inline bool
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();
91 void
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();
119 if (doc) {
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 //----------------------------------------------------------------------
148 // nsIFrame methods
150 //----------------------------------------------------------------------
151 // reflowing
153 /* virtual */ nscoord
154 nsSVGOuterSVGFrame::GetMinISize(nsRenderingContext *aRenderingContext)
156 nscoord result;
157 DISPLAY_MIN_WIDTH(this, result);
159 result = nscoord(0);
161 return result;
164 /* virtual */ nscoord
165 nsSVGOuterSVGFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
167 nscoord result;
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,
176 // so return zero.
177 result = nscoord(0);
178 } else {
179 result = nsPresContext::CSSPixelsToAppUnits(width.GetAnimValue(svg));
180 if (result < 0) {
181 result = nscoord(0);
185 return result;
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;
215 /* virtual */ nsSize
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) {
230 ratio.width = 0;
232 if (ratio.height < 0) {
233 ratio.height = 0;
235 return ratio;
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();
248 if (viewbox) {
249 float viewBoxWidth = viewbox->width;
250 float viewBoxHeight = viewbox->height;
252 if (viewBoxWidth < 0.0f) {
253 viewBoxWidth = 0.0f;
255 if (viewBoxHeight < 0.0f) {
256 viewBoxHeight = 0.0f;
258 return nsSize(NSToCoordRoundWithClamp(viewBoxWidth),
259 NSToCoordRoundWithClamp(viewBoxHeight));
262 return nsSVGOuterSVGFrameBase::GetIntrinsicRatio();
265 /* virtual */
266 LogicalSize
267 nsSVGOuterSVGFrame::ComputeSize(nsRenderingContext *aRenderingContext,
268 WritingMode aWM,
269 const LogicalSize& aCBSize,
270 nscoord aAvailableISize,
271 const LogicalSize& aMargin,
272 const LogicalSize& aBorder,
273 const LogicalSize& aPadding,
274 uint32_t aFlags)
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.
282 return aCBSize;
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 "
311 "intrinsic width");
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 "
324 "intrinsic height");
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(),
338 cbSize,
339 aMargin,
340 aBorder,
341 aPadding);
344 void
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) {
373 // Initialize
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
399 // rects.
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();
423 if (changeBits) {
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);
434 } else {
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
452 // for our viewport.
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);
490 void
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);
502 /* virtual */ bool
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 //----------------------------------------------------------------------
524 // container methods
527 * Used to paint/hit-test SVG when SVG display lists are disabled.
529 class nsDisplayOuterSVG : public nsDisplayItem {
530 public:
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);
540 #endif
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)
555 void
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());
576 nsIFrame* frame =
577 nsSVGUtils::HitTestChildren(anonKid, svgViewportRelativePoint);
578 if (frame) {
579 aOutFrames->AppendElement(frame);
583 void
584 nsDisplayOuterSVG::Paint(nsDisplayListBuilder* aBuilder,
585 nsRenderingContext* aContext)
587 #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
588 PRTime start = PR_Now();
589 #endif
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);
618 #endif
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;
628 nsRegion
629 nsSVGOuterSVGFrame::FindInvalidatedForeignObjectFrameChildren(nsIFrame* aFrame)
631 nsRegion result;
632 if (mForeignObjectHash && mForeignObjectHash->Count()) {
633 mForeignObjectHash->EnumerateEntries(CheckForeignObjectInvalidatedArea, &result);
635 return result;
638 void
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);
654 nsresult
655 nsSVGOuterSVGFrame::AttributeChanged(int32_t aNameSpaceID,
656 nsIAtom* aAttribute,
657 int32_t aModType)
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
666 mCanvasTM = nullptr;
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
691 } else {
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);
700 return NS_OK;
703 //----------------------------------------------------------------------
704 // painting
706 void
707 nsSVGOuterSVGFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
708 const nsRect& aDirtyRect,
709 const nsDisplayListSet& aLists)
711 if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
712 return;
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);
733 } else {
734 aLists.Content()->AppendNewToTop(
735 new (aBuilder) nsDisplayOuterSVG(aBuilder, this));
739 nsSplittableType
740 nsSVGOuterSVGFrame::GetSplittableType() const
742 return NS_FRAME_NOT_SPLITTABLE;
745 nsIAtom *
746 nsSVGOuterSVGFrame::GetType() const
748 return nsGkAtoms::svgOuterSVGFrame;
751 //----------------------------------------------------------------------
752 // nsISVGSVGFrame methods:
754 void
755 nsSVGOuterSVGFrame::NotifyViewportOrTransformChanged(uint32_t aFlags)
757 NS_ABORT_IF_FALSE(aFlags &&
758 !(aFlags & ~(COORD_CONTEXT_CHANGED | TRANSFORM_CHANGED |
759 FULL_ZOOM_CHANGED)),
760 "Unexpected aFlags value");
762 // No point in doing anything when were not init'ed yet:
763 if (!mViewportInitialized) {
764 return;
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
786 // viewBox.
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:
800 mCanvasTM = nullptr;
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:
816 nsresult
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);
830 SVGBBox
831 nsSVGOuterSVGFrame::GetBBoxContribution(const gfx::Matrix &aToBBoxUserspace,
832 uint32_t aFlags)
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:
848 gfxMatrix
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);
856 if (!mCanvasTM) {
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);
868 return *mCanvasTM;
871 //----------------------------------------------------------------------
872 // Implementation helpers
874 bool
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;
881 if (docShell) {
882 window = docShell->GetWindow();
885 if (window) {
886 nsCOMPtr<nsIDOMElement> frameElement;
887 window->GetFrameElement(getter_AddRefs(frameElement));
888 nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(frameElement);
889 if (olc) {
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!");
896 return true;
900 if (aEmbeddingFrame) {
901 *aEmbeddingFrame = nullptr;
903 return false;
906 bool
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
914 return true;
918 return false;
921 bool
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
933 nsContainerFrame*
934 NS_NewSVGOuterSVGAnonChildFrame(nsIPresShell* aPresShell,
935 nsStyleContext* aContext)
937 return new (aPresShell) nsSVGOuterSVGAnonChildFrame(aContext);
940 NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGAnonChildFrame)
942 #ifdef DEBUG
943 void
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);
952 #endif
954 nsIAtom *
955 nsSVGOuterSVGAnonChildFrame::GetType() const
957 return nsGkAtoms::svgOuterSVGAnonChildFrame;
960 bool
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.
972 gfxMatrix identity;
973 *aTransform = gfx::ToMatrix(
974 content->PrependLocalTransformsTo(identity,
975 nsSVGElement::eChildToUserSpace));
978 return hasTransform;