1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 // Main header first:
8 #include "nsSVGOuterSVGFrame.h"
9 
10 // Keep others in (case-insensitive) order:
11 #include "gfxContext.h"
12 #include "nsDisplayList.h"
13 #include "mozilla/PresShell.h"
14 #include "mozilla/dom/Document.h"
15 #include "nsIInterfaceRequestorUtils.h"
16 #include "nsIObjectLoadingContent.h"
17 #include "nsSVGIntegrationUtils.h"
18 #include "nsSVGForeignObjectFrame.h"
19 #include "mozilla/dom/Element.h"
20 #include "mozilla/dom/SVGSVGElement.h"
21 #include "mozilla/dom/SVGViewElement.h"
22 #include "nsSubDocumentFrame.h"
23 
24 using namespace mozilla;
25 using namespace mozilla::dom;
26 using namespace mozilla::gfx;
27 using namespace mozilla::image;
28 
29 //----------------------------------------------------------------------
30 // Implementation helpers
31 
RegisterForeignObject(nsSVGForeignObjectFrame * aFrame)32 void nsSVGOuterSVGFrame::RegisterForeignObject(
33     nsSVGForeignObjectFrame* aFrame) {
34   NS_ASSERTION(aFrame, "Who on earth is calling us?!");
35 
36   if (!mForeignObjectHash) {
37     mForeignObjectHash =
38         MakeUnique<nsTHashtable<nsPtrHashKey<nsSVGForeignObjectFrame>>>();
39   }
40 
41   NS_ASSERTION(!mForeignObjectHash->GetEntry(aFrame),
42                "nsSVGForeignObjectFrame already registered!");
43 
44   mForeignObjectHash->PutEntry(aFrame);
45 
46   NS_ASSERTION(mForeignObjectHash->GetEntry(aFrame),
47                "Failed to register nsSVGForeignObjectFrame!");
48 }
49 
UnregisterForeignObject(nsSVGForeignObjectFrame * aFrame)50 void nsSVGOuterSVGFrame::UnregisterForeignObject(
51     nsSVGForeignObjectFrame* aFrame) {
52   NS_ASSERTION(aFrame, "Who on earth is calling us?!");
53   NS_ASSERTION(mForeignObjectHash && mForeignObjectHash->GetEntry(aFrame),
54                "nsSVGForeignObjectFrame not in registry!");
55   return mForeignObjectHash->RemoveEntry(aFrame);
56 }
57 
58 //----------------------------------------------------------------------
59 // Implementation
60 
NS_NewSVGOuterSVGFrame(PresShell * aPresShell,ComputedStyle * aStyle)61 nsContainerFrame* NS_NewSVGOuterSVGFrame(PresShell* aPresShell,
62                                          ComputedStyle* aStyle) {
63   return new (aPresShell)
64       nsSVGOuterSVGFrame(aStyle, aPresShell->GetPresContext());
65 }
66 
NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGFrame)67 NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGFrame)
68 
69 nsSVGOuterSVGFrame::nsSVGOuterSVGFrame(ComputedStyle* aStyle,
70                                        nsPresContext* aPresContext)
71     : nsSVGDisplayContainerFrame(aStyle, aPresContext, kClassID),
72       mCallingReflowSVG(false),
73       mFullZoom(PresContext()->GetFullZoom()),
74       mViewportInitialized(false),
75       mIsRootContent(false) {
76   // Outer-<svg> has CSS layout, so remove this bit:
77   RemoveStateBits(NS_FRAME_SVG_LAYOUT);
78 }
79 
80 // helper
DependsOnIntrinsicSize(const nsIFrame * aEmbeddingFrame)81 static inline bool DependsOnIntrinsicSize(const nsIFrame* aEmbeddingFrame) {
82   const nsStylePosition* pos = aEmbeddingFrame->StylePosition();
83 
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 !pos->mWidth.ConvertsToLength() || !pos->mHeight.ConvertsToLength();
88 }
89 
90 // The CSS Containment spec says that size-contained replaced elements must be
91 // treated as having an intrinsic width and height of 0.  That's applicable to
92 // outer SVG frames, unless they're the outermost element (in which case
93 // they're not really "replaced", and there's no outer context to contain sizes
94 // from leaking into). Hence, we check for a parent element before we bother
95 // testing for 'contain:size'.
IsReplacedAndContainSize(const nsSVGOuterSVGFrame * aFrame)96 static inline bool IsReplacedAndContainSize(const nsSVGOuterSVGFrame* aFrame) {
97   return aFrame->GetContent()->GetParent() &&
98          aFrame->StyleDisplay()->IsContainSize();
99 }
100 
Init(nsIContent * aContent,nsContainerFrame * aParent,nsIFrame * aPrevInFlow)101 void nsSVGOuterSVGFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
102                               nsIFrame* aPrevInFlow) {
103   NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::svg),
104                "Content is not an SVG 'svg' element!");
105 
106   AddStateBits(NS_FRAME_REFLOW_ROOT | NS_FRAME_FONT_INFLATION_CONTAINER |
107                NS_FRAME_FONT_INFLATION_FLOW_ROOT);
108 
109   // Check for conditional processing attributes here rather than in
110   // nsCSSFrameConstructor::FindSVGData because we want to avoid
111   // simply giving failing outer <svg> elements an nsSVGContainerFrame.
112   // We don't create other SVG frames if PassesConditionalProcessingTests
113   // returns false, but since we do create nsSVGOuterSVGFrame frames we
114   // prevent them from painting by [ab]use NS_FRAME_IS_NONDISPLAY. The
115   // frame will be recreated via an nsChangeHint_ReconstructFrame restyle if
116   // the value returned by PassesConditionalProcessingTests changes.
117   SVGSVGElement* svg = static_cast<SVGSVGElement*>(aContent);
118   if (!svg->PassesConditionalProcessingTests()) {
119     AddStateBits(NS_FRAME_IS_NONDISPLAY);
120   }
121 
122   nsSVGDisplayContainerFrame::Init(aContent, aParent, aPrevInFlow);
123 
124   Document* doc = mContent->GetUncomposedDoc();
125   if (doc) {
126     // we only care about our content's zoom and pan values if it's the root
127     // element
128     if (doc->GetRootElement() == mContent) {
129       mIsRootContent = true;
130 
131       nsIFrame* embeddingFrame;
132       if (IsRootOfReplacedElementSubDoc(&embeddingFrame) && embeddingFrame) {
133         if (MOZ_UNLIKELY(!embeddingFrame->HasAllStateBits(NS_FRAME_IS_DIRTY))) {
134           bool dependsOnIntrinsicSize = DependsOnIntrinsicSize(embeddingFrame);
135           if (dependsOnIntrinsicSize ||
136               embeddingFrame->StylePosition()->mObjectFit !=
137                   StyleObjectFit::Fill) {
138             // Looks like this document is loading after the embedding element
139             // has had its first reflow, and it cares about our intrinsic size
140             // (either for determining its own size, or for sizing/positioning
141             // its view to honor "object-fit").  We need it to reflow itself to
142             // use our (now-available) intrinsic size:
143             auto dirtyHint = dependsOnIntrinsicSize
144                                  ? IntrinsicDirty::StyleChange
145                                  : IntrinsicDirty::Resize;
146             embeddingFrame->PresShell()->FrameNeedsReflow(
147                 embeddingFrame, dirtyHint, NS_FRAME_IS_DIRTY);
148           }
149         }
150       }
151     }
152   }
153 }
154 
155 //----------------------------------------------------------------------
156 // nsQueryFrame methods
157 
158 NS_QUERYFRAME_HEAD(nsSVGOuterSVGFrame)
NS_QUERYFRAME_ENTRY(nsISVGSVGFrame)159   NS_QUERYFRAME_ENTRY(nsISVGSVGFrame)
160 NS_QUERYFRAME_TAIL_INHERITING(nsSVGDisplayContainerFrame)
161 
162 //----------------------------------------------------------------------
163 // nsIFrame methods
164 //----------------------------------------------------------------------
165 // reflowing
166 
167 /* virtual */
168 nscoord nsSVGOuterSVGFrame::GetMinISize(gfxContext* aRenderingContext) {
169   nscoord result;
170   DISPLAY_MIN_INLINE_SIZE(this, result);
171 
172   result = nscoord(0);
173 
174   return result;
175 }
176 
177 /* virtual */
GetPrefISize(gfxContext * aRenderingContext)178 nscoord nsSVGOuterSVGFrame::GetPrefISize(gfxContext* aRenderingContext) {
179   nscoord result;
180   DISPLAY_PREF_INLINE_SIZE(this, result);
181 
182   SVGSVGElement* svg = static_cast<SVGSVGElement*>(GetContent());
183   WritingMode wm = GetWritingMode();
184   const SVGAnimatedLength& isize =
185       wm.IsVertical() ? svg->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT]
186                       : svg->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
187 
188   if (IsReplacedAndContainSize(this)) {
189     result = nscoord(0);
190   } else if (isize.IsPercentage()) {
191     // It looks like our containing block's isize may depend on our isize. In
192     // that case our behavior is undefined according to CSS 2.1 section 10.3.2.
193     // As a last resort, we'll fall back to returning zero.
194     result = nscoord(0);
195 
196     // Returning zero may be unhelpful, however, as it leads to unexpected
197     // disappearance of %-sized SVGs in orthogonal contexts, where our
198     // containing block wants to shrink-wrap. So let's look for an ancestor
199     // with non-zero size in this dimension, and use that as a (somewhat
200     // arbitrary) result instead.
201     nsIFrame* parent = GetParent();
202     while (parent) {
203       nscoord parentISize = parent->GetLogicalSize(wm).ISize(wm);
204       if (parentISize > 0 && parentISize != NS_UNCONSTRAINEDSIZE) {
205         result = parentISize;
206         break;
207       }
208       parent = parent->GetParent();
209     }
210   } else {
211     result = nsPresContext::CSSPixelsToAppUnits(isize.GetAnimValue(svg));
212     if (result < 0) {
213       result = nscoord(0);
214     }
215   }
216 
217   return result;
218 }
219 
220 /* virtual */
GetIntrinsicSize()221 IntrinsicSize nsSVGOuterSVGFrame::GetIntrinsicSize() {
222   // XXXjwatt Note that here we want to return the CSS width/height if they're
223   // specified and we're embedded inside an nsIObjectLoadingContent.
224 
225   if (IsReplacedAndContainSize(this)) {
226     // Intrinsic size of 'contain:size' replaced elements is 0,0.
227     return IntrinsicSize(0, 0);
228   }
229 
230   SVGSVGElement* content = static_cast<SVGSVGElement*>(GetContent());
231   const SVGAnimatedLength& width =
232       content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
233   const SVGAnimatedLength& height =
234       content->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT];
235 
236   IntrinsicSize intrinsicSize;
237 
238   if (!width.IsPercentage()) {
239     nscoord val =
240         nsPresContext::CSSPixelsToAppUnits(width.GetAnimValue(content));
241     intrinsicSize.width.emplace(std::max(val, 0));
242   }
243 
244   if (!height.IsPercentage()) {
245     nscoord val =
246         nsPresContext::CSSPixelsToAppUnits(height.GetAnimValue(content));
247     intrinsicSize.height.emplace(std::max(val, 0));
248   }
249 
250   return intrinsicSize;
251 }
252 
253 /* virtual */
GetIntrinsicRatio()254 AspectRatio nsSVGOuterSVGFrame::GetIntrinsicRatio() {
255   if (IsReplacedAndContainSize(this)) {
256     return AspectRatio();
257   }
258 
259   // We only have an intrinsic size/ratio if our width and height attributes
260   // are both specified and set to non-percentage values, or we have a viewBox
261   // rect: http://www.w3.org/TR/SVGMobile12/coords.html#IntrinsicSizing
262   // Unfortunately we have to return the ratio as two nscoords whereas what
263   // we have are two floats. Using app units allows for some floating point
264   // values to work but really small or large numbers will fail.
265 
266   SVGSVGElement* content = static_cast<SVGSVGElement*>(GetContent());
267   const SVGAnimatedLength& width =
268       content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
269   const SVGAnimatedLength& height =
270       content->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT];
271 
272   if (!width.IsPercentage() && !height.IsPercentage()) {
273     return AspectRatio::FromSize(width.GetAnimValue(content),
274                                  height.GetAnimValue(content));
275   }
276 
277   SVGViewElement* viewElement = content->GetCurrentViewElement();
278   const SVGViewBox* viewbox = nullptr;
279 
280   // The logic here should match HasViewBox().
281   if (viewElement && viewElement->mViewBox.HasRect()) {
282     viewbox = &viewElement->mViewBox.GetAnimValue();
283   } else if (content->mViewBox.HasRect()) {
284     viewbox = &content->mViewBox.GetAnimValue();
285   }
286 
287   if (viewbox) {
288     return AspectRatio::FromSize(viewbox->width, viewbox->height);
289   }
290 
291   return nsSVGDisplayContainerFrame::GetIntrinsicRatio();
292 }
293 
294 /* virtual */
ComputeSize(gfxContext * aRenderingContext,WritingMode aWritingMode,const LogicalSize & aCBSize,nscoord aAvailableISize,const LogicalSize & aMargin,const LogicalSize & aBorder,const LogicalSize & aPadding,ComputeSizeFlags aFlags)295 LogicalSize nsSVGOuterSVGFrame::ComputeSize(
296     gfxContext* aRenderingContext, WritingMode aWritingMode,
297     const LogicalSize& aCBSize, nscoord aAvailableISize,
298     const LogicalSize& aMargin, const LogicalSize& aBorder,
299     const LogicalSize& aPadding, ComputeSizeFlags aFlags) {
300   if (IsRootOfImage() || IsRootOfReplacedElementSubDoc()) {
301     // The embedding element has sized itself using the CSS replaced element
302     // sizing rules, using our intrinsic dimensions as necessary. The SVG spec
303     // says that the width and height of embedded SVG is overridden by the
304     // width and height of the embedding element, so we just need to size to
305     // the viewport that the embedding element has established for us.
306     return aCBSize;
307   }
308 
309   LogicalSize cbSize = aCBSize;
310   IntrinsicSize intrinsicSize = GetIntrinsicSize();
311 
312   if (!mContent->GetParent()) {
313     // We're the root of the outermost browsing context, so we need to scale
314     // cbSize by the full-zoom so that SVGs with percentage width/height zoom:
315 
316     NS_ASSERTION(aCBSize.ISize(aWritingMode) != NS_UNCONSTRAINEDSIZE &&
317                      aCBSize.BSize(aWritingMode) != NS_UNCONSTRAINEDSIZE,
318                  "root should not have auto-width/height containing block");
319 
320     if (!IsContainingWindowElementOfType(nullptr, nsGkAtoms::iframe)) {
321       cbSize.ISize(aWritingMode) *= PresContext()->GetFullZoom();
322       cbSize.BSize(aWritingMode) *= PresContext()->GetFullZoom();
323     }
324 
325     // We also need to honour the width and height attributes' default values
326     // of 100% when we're the root of a browsing context.  (GetIntrinsicSize()
327     // doesn't report these since there's no such thing as a percentage
328     // intrinsic size.  Also note that explicit percentage values are mapped
329     // into style, so the following isn't for them.)
330 
331     SVGSVGElement* content = static_cast<SVGSVGElement*>(GetContent());
332 
333     const SVGAnimatedLength& width =
334         content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
335     if (width.IsPercentage()) {
336       MOZ_ASSERT(!intrinsicSize.width,
337                  "GetIntrinsicSize should have reported no intrinsic width");
338       float val = width.GetAnimValInSpecifiedUnits() / 100.0f;
339       intrinsicSize.width.emplace(std::max(val, 0.0f) *
340                                   cbSize.Width(aWritingMode));
341     }
342 
343     const SVGAnimatedLength& height =
344         content->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT];
345     NS_ASSERTION(aCBSize.BSize(aWritingMode) != NS_UNCONSTRAINEDSIZE,
346                  "root should not have auto-height containing block");
347     if (height.IsPercentage()) {
348       MOZ_ASSERT(!intrinsicSize.height,
349                  "GetIntrinsicSize should have reported no intrinsic height");
350       float val = height.GetAnimValInSpecifiedUnits() / 100.0f;
351       intrinsicSize.height.emplace(std::max(val, 0.0f) *
352                                    cbSize.Height(aWritingMode));
353     }
354     MOZ_ASSERT(intrinsicSize.height && intrinsicSize.width,
355                "We should have just handled the only situation where"
356                "we lack an intrinsic height or width.");
357   }
358 
359   return ComputeSizeWithIntrinsicDimensions(
360       aRenderingContext, aWritingMode, intrinsicSize, GetIntrinsicRatio(),
361       cbSize, aMargin, aBorder, aPadding, aFlags);
362 }
363 
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)364 void nsSVGOuterSVGFrame::Reflow(nsPresContext* aPresContext,
365                                 ReflowOutput& aDesiredSize,
366                                 const ReflowInput& aReflowInput,
367                                 nsReflowStatus& aStatus) {
368   MarkInReflow();
369   DO_GLOBAL_REFLOW_COUNT("nsSVGOuterSVGFrame");
370   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
371   MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
372   NS_FRAME_TRACE(
373       NS_FRAME_TRACE_CALLS,
374       ("enter nsSVGOuterSVGFrame::Reflow: availSize=%d,%d",
375        aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
376 
377   MOZ_ASSERT(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
378 
379   aDesiredSize.Width() =
380       aReflowInput.ComputedWidth() +
381       aReflowInput.ComputedPhysicalBorderPadding().LeftRight();
382   aDesiredSize.Height() =
383       aReflowInput.ComputedHeight() +
384       aReflowInput.ComputedPhysicalBorderPadding().TopBottom();
385 
386   NS_ASSERTION(!GetPrevInFlow(), "SVG can't currently be broken across pages.");
387 
388   SVGSVGElement* svgElem = static_cast<SVGSVGElement*>(GetContent());
389 
390   nsSVGOuterSVGAnonChildFrame* anonKid =
391       static_cast<nsSVGOuterSVGAnonChildFrame*>(
392           PrincipalChildList().FirstChild());
393 
394   if (mState & NS_FRAME_FIRST_REFLOW) {
395     // Initialize
396     svgElem->UpdateHasChildrenOnlyTransform();
397   }
398 
399   // If our SVG viewport has changed, update our content and notify.
400   // http://www.w3.org/TR/SVG11/coords.html#ViewportSpace
401 
402   svgFloatSize newViewportSize(
403       nsPresContext::AppUnitsToFloatCSSPixels(aReflowInput.ComputedWidth()),
404       nsPresContext::AppUnitsToFloatCSSPixels(aReflowInput.ComputedHeight()));
405 
406   svgFloatSize oldViewportSize = svgElem->GetViewportSize();
407 
408   uint32_t changeBits = 0;
409   if (newViewportSize != oldViewportSize) {
410     // When our viewport size changes, we may need to update the overflow rects
411     // of our child frames. This is the case if:
412     //
413     //  * We have a real/synthetic viewBox (a children-only transform), since
414     //    the viewBox transform will change as the viewport dimensions change.
415     //
416     //  * We do not have a real/synthetic viewBox, but the last time we
417     //    reflowed (or the last time UpdateOverflow() was called) we did.
418     //
419     // We only handle the former case here, in which case we mark all our child
420     // frames as dirty so that we reflow them below and update their overflow
421     // rects.
422     //
423     // In the latter case, updating of overflow rects is handled for removal of
424     // real viewBox (the viewBox attribute) in AttributeChanged. Synthetic
425     // viewBox "removal" (e.g. a document references the same SVG via both an
426     // <svg:image> and then as a CSS background image (a synthetic viewBox is
427     // used when painting the former, but not when painting the latter)) is
428     // handled in SVGSVGElement::FlushImageTransformInvalidation.
429     //
430     if (svgElem->HasViewBoxOrSyntheticViewBox()) {
431       nsIFrame* anonChild = PrincipalChildList().FirstChild();
432       anonChild->MarkSubtreeDirty();
433       for (nsIFrame* child : anonChild->PrincipalChildList()) {
434         child->MarkSubtreeDirty();
435       }
436     }
437     changeBits |= COORD_CONTEXT_CHANGED;
438     svgElem->SetViewportSize(newViewportSize);
439   }
440   if (mFullZoom != PresContext()->GetFullZoom() &&
441       !IsContainingWindowElementOfType(nullptr, nsGkAtoms::iframe)) {
442     changeBits |= FULL_ZOOM_CHANGED;
443     mFullZoom = PresContext()->GetFullZoom();
444   }
445   if (changeBits) {
446     NotifyViewportOrTransformChanged(changeBits);
447   }
448   mViewportInitialized = true;
449 
450   // Now that we've marked the necessary children as dirty, call
451   // ReflowSVG() or ReflowSVGNonDisplayText() on them, depending
452   // on whether we are non-display.
453   mCallingReflowSVG = true;
454   if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
455     ReflowSVGNonDisplayText(this);
456   } else {
457     // Update the mRects and visual overflow rects of all our descendants,
458     // including our anonymous wrapper kid:
459     anonKid->ReflowSVG();
460     MOZ_ASSERT(!anonKid->GetNextSibling(),
461                "We should have one anonymous child frame wrapping our real "
462                "children");
463   }
464   mCallingReflowSVG = false;
465 
466   // Set our anonymous kid's offset from our border box:
467   anonKid->SetPosition(GetContentRectRelativeToSelf().TopLeft());
468 
469   // Including our size in our overflow rects regardless of the value of
470   // 'background', 'border', etc. makes sure that we usually (when we clip to
471   // our content area) don't have to keep changing our overflow rects as our
472   // descendants move about (see perf comment below). Including our size in our
473   // scrollable overflow rect also makes sure that we scroll if we're too big
474   // for our viewport.
475   //
476   // <svg> never allows scrolling to anything outside its mRect (only panning),
477   // so we must always keep our scrollable overflow set to our size.
478   //
479   // With regards to visual overflow, we always clip root-<svg> (see our
480   // BuildDisplayList method) regardless of the value of the 'overflow'
481   // property since that is per-spec, even for the initial 'visible' value. For
482   // that reason there's no point in adding descendant visual overflow to our
483   // own when this frame is for a root-<svg>. That said, there's also a very
484   // good performance reason for us wanting to avoid doing so. If we did, then
485   // the frame's overflow would often change as descendants that are partially
486   // or fully outside its rect moved (think animation on/off screen), and that
487   // would cause us to do a full NS_FRAME_IS_DIRTY reflow and repaint of the
488   // entire document tree each such move (see bug 875175).
489   //
490   // So it's only non-root outer-<svg> that has the visual overflow of its
491   // descendants added to its own. (Note that the default user-agent style
492   // sheet makes 'hidden' the default value for :not(root(svg)), so usually
493   // FinishAndStoreOverflow will still clip this back to the frame's rect.)
494   //
495   // WARNING!! Keep UpdateBounds below in sync with whatever we do for our
496   // overflow rects here! (Again, see bug 875175.)
497   //
498   aDesiredSize.SetOverflowAreasToDesiredBounds();
499 
500   // An outer SVG will be here as a nondisplay if it fails the conditional
501   // processing test. In that case, we don't maintain its overflow.
502   if (!HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
503     if (!mIsRootContent) {
504       aDesiredSize.mOverflowAreas.VisualOverflow().UnionRect(
505           aDesiredSize.mOverflowAreas.VisualOverflow(),
506           anonKid->GetVisualOverflowRect() + anonKid->GetPosition());
507     }
508     FinishAndStoreOverflow(&aDesiredSize);
509   }
510 
511   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
512                  ("exit nsSVGOuterSVGFrame::Reflow: size=%d,%d",
513                   aDesiredSize.Width(), aDesiredSize.Height()));
514   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
515 }
516 
DidReflow(nsPresContext * aPresContext,const ReflowInput * aReflowInput)517 void nsSVGOuterSVGFrame::DidReflow(nsPresContext* aPresContext,
518                                    const ReflowInput* aReflowInput) {
519   nsSVGDisplayContainerFrame::DidReflow(aPresContext, aReflowInput);
520 
521   // Make sure elements styled by :hover get updated if script/animation moves
522   // them under or out from under the pointer:
523   PresShell()->SynthesizeMouseMove(false);
524 }
525 
526 /* virtual */
UnionChildOverflow(nsOverflowAreas & aOverflowAreas)527 void nsSVGOuterSVGFrame::UnionChildOverflow(nsOverflowAreas& aOverflowAreas) {
528   // See the comments in Reflow above.
529 
530   // WARNING!! Keep this in sync with Reflow above!
531 
532   if (!mIsRootContent) {
533     nsIFrame* anonKid = PrincipalChildList().FirstChild();
534     aOverflowAreas.VisualOverflow().UnionRect(
535         aOverflowAreas.VisualOverflow(),
536         anonKid->GetVisualOverflowRect() + anonKid->GetPosition());
537   }
538 }
539 
540 //----------------------------------------------------------------------
541 // container methods
542 
543 /**
544  * Used to paint/hit-test SVG when SVG display lists are disabled.
545  */
546 class nsDisplayOuterSVG final : public nsPaintedDisplayItem {
547  public:
nsDisplayOuterSVG(nsDisplayListBuilder * aBuilder,nsSVGOuterSVGFrame * aFrame)548   nsDisplayOuterSVG(nsDisplayListBuilder* aBuilder, nsSVGOuterSVGFrame* aFrame)
549       : nsPaintedDisplayItem(aBuilder, aFrame) {
550     MOZ_COUNT_CTOR(nsDisplayOuterSVG);
551   }
552   MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayOuterSVG)
553 
554   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
555                        HitTestState* aState,
556                        nsTArray<nsIFrame*>* aOutFrames) override;
557   virtual void Paint(nsDisplayListBuilder* aBuilder,
558                      gfxContext* aContext) override;
559 
560   virtual void ComputeInvalidationRegion(
561       nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
562       nsRegion* aInvalidRegion) const override;
563 
AllocateGeometry(nsDisplayListBuilder * aBuilder)564   nsDisplayItemGeometry* AllocateGeometry(
565       nsDisplayListBuilder* aBuilder) override {
566     return new nsDisplayItemGenericImageGeometry(this, aBuilder);
567   }
568 
569   NS_DISPLAY_DECL_NAME("SVGOuterSVG", TYPE_SVG_OUTER_SVG)
570 };
571 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)572 void nsDisplayOuterSVG::HitTest(nsDisplayListBuilder* aBuilder,
573                                 const nsRect& aRect, HitTestState* aState,
574                                 nsTArray<nsIFrame*>* aOutFrames) {
575   nsSVGOuterSVGFrame* outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(mFrame);
576 
577   nsPoint refFrameToContentBox =
578       ToReferenceFrame() +
579       outerSVGFrame->GetContentRectRelativeToSelf().TopLeft();
580 
581   nsPoint pointRelativeToContentBox =
582       nsPoint(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2) -
583       refFrameToContentBox;
584 
585   gfxPoint svgViewportRelativePoint =
586       gfxPoint(pointRelativeToContentBox.x, pointRelativeToContentBox.y) /
587       AppUnitsPerCSSPixel();
588 
589   nsSVGOuterSVGAnonChildFrame* anonKid =
590       static_cast<nsSVGOuterSVGAnonChildFrame*>(
591           outerSVGFrame->PrincipalChildList().FirstChild());
592 
593   nsIFrame* frame =
594       nsSVGUtils::HitTestChildren(anonKid, svgViewportRelativePoint);
595   if (frame) {
596     aOutFrames->AppendElement(frame);
597   }
598 }
599 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aContext)600 void nsDisplayOuterSVG::Paint(nsDisplayListBuilder* aBuilder,
601                               gfxContext* aContext) {
602 #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
603   PRTime start = PR_Now();
604 #endif
605 
606   // Create an SVGAutoRenderState so we can call SetPaintingToWindow on it.
607   SVGAutoRenderState state(aContext->GetDrawTarget());
608 
609   if (aBuilder->IsPaintingToWindow()) {
610     state.SetPaintingToWindow(true);
611   }
612 
613   nsRect viewportRect =
614       mFrame->GetContentRectRelativeToSelf() + ToReferenceFrame();
615 
616   nsRect clipRect = GetPaintRect().Intersect(viewportRect);
617 
618   uint32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
619 
620   nsIntRect contentAreaDirtyRect =
621       (clipRect - viewportRect.TopLeft()).ToOutsidePixels(appUnitsPerDevPixel);
622 
623   gfxPoint devPixelOffset = nsLayoutUtils::PointToGfxPoint(
624       viewportRect.TopLeft(), appUnitsPerDevPixel);
625 
626   aContext->Save();
627   imgDrawingParams imgParams(aBuilder->GetImageDecodeFlags());
628   // We include the offset of our frame and a scale from device pixels to user
629   // units (i.e. CSS px) in the matrix that we pass to our children):
630   gfxMatrix tm = nsSVGUtils::GetCSSPxToDevPxMatrix(mFrame) *
631                  gfxMatrix::Translation(devPixelOffset);
632   nsSVGUtils::PaintFrameWithEffects(mFrame, *aContext, tm, imgParams,
633                                     &contentAreaDirtyRect);
634   nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, imgParams.result);
635   aContext->Restore();
636 
637 #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
638   PRTime end = PR_Now();
639   printf("SVG Paint Timing: %f ms\n", (end - start) / 1000.0);
640 #endif
641 }
642 
FindInvalidatedForeignObjectFrameChildren(nsIFrame * aFrame)643 nsRegion nsSVGOuterSVGFrame::FindInvalidatedForeignObjectFrameChildren(
644     nsIFrame* aFrame) {
645   nsRegion result;
646   if (mForeignObjectHash && mForeignObjectHash->Count()) {
647     for (auto it = mForeignObjectHash->Iter(); !it.Done(); it.Next()) {
648       result.Or(result, it.Get()->GetKey()->GetInvalidRegion());
649     }
650   }
651   return result;
652 }
653 
ComputeInvalidationRegion(nsDisplayListBuilder * aBuilder,const nsDisplayItemGeometry * aGeometry,nsRegion * aInvalidRegion) const654 void nsDisplayOuterSVG::ComputeInvalidationRegion(
655     nsDisplayListBuilder* aBuilder, const nsDisplayItemGeometry* aGeometry,
656     nsRegion* aInvalidRegion) const {
657   nsSVGOuterSVGFrame* frame = static_cast<nsSVGOuterSVGFrame*>(mFrame);
658   frame->InvalidateSVG(frame->FindInvalidatedForeignObjectFrameChildren(frame));
659 
660   nsRegion result = frame->GetInvalidRegion();
661   result.MoveBy(ToReferenceFrame());
662   frame->ClearInvalidRegion();
663 
664   nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
665   aInvalidRegion->Or(*aInvalidRegion, result);
666 
667   auto geometry =
668       static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
669 
670   if (aBuilder->ShouldSyncDecodeImages() &&
671       geometry->ShouldInvalidateToSyncDecodeImages()) {
672     bool snap;
673     aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
674   }
675 }
676 
AttributeChanged(int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType)677 nsresult nsSVGOuterSVGFrame::AttributeChanged(int32_t aNameSpaceID,
678                                               nsAtom* aAttribute,
679                                               int32_t aModType) {
680   if (aNameSpaceID == kNameSpaceID_None &&
681       !(GetStateBits() & (NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_NONDISPLAY))) {
682     if (aAttribute == nsGkAtoms::viewBox ||
683         aAttribute == nsGkAtoms::preserveAspectRatio ||
684         aAttribute == nsGkAtoms::transform) {
685       // make sure our cached transform matrix gets (lazily) updated
686       mCanvasTM = nullptr;
687 
688       nsSVGUtils::NotifyChildrenOfSVGChange(
689           PrincipalChildList().FirstChild(),
690           aAttribute == nsGkAtoms::viewBox
691               ? TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED
692               : TRANSFORM_CHANGED);
693 
694       if (aAttribute != nsGkAtoms::transform) {
695         static_cast<SVGSVGElement*>(GetContent())
696             ->ChildrenOnlyTransformChanged();
697       }
698     }
699     if (aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height ||
700         aAttribute == nsGkAtoms::viewBox) {
701       // Don't call ChildrenOnlyTransformChanged() here, since we call it
702       // under Reflow if the width/height/viewBox actually changed.
703 
704       nsIFrame* embeddingFrame;
705       if (IsRootOfReplacedElementSubDoc(&embeddingFrame) && embeddingFrame) {
706         bool dependsOnIntrinsicSize = DependsOnIntrinsicSize(embeddingFrame);
707         if (dependsOnIntrinsicSize ||
708             embeddingFrame->StylePosition()->mObjectFit !=
709                 StyleObjectFit::Fill) {
710           // Tell embeddingFrame's presShell it needs to be reflowed (which
711           // takes care of reflowing us too). And if it depends on our
712           // intrinsic size, then we need to invalidate its intrinsic sizes
713           // (via the IntrinsicDirty::StyleChange hint.)
714           auto dirtyHint = dependsOnIntrinsicSize ? IntrinsicDirty::StyleChange
715                                                   : IntrinsicDirty::Resize;
716           embeddingFrame->PresShell()->FrameNeedsReflow(
717               embeddingFrame, dirtyHint, NS_FRAME_IS_DIRTY);
718         }  // else our width/height/viewBox are irrelevant to the outer doc.
719       } else {
720         // We are not embedded by reference, so our 'width' and 'height'
721         // attributes are not overridden (and viewBox may influence our
722         // intrinsic aspect ratio).  We need to reflow.
723         PresShell()->FrameNeedsReflow(this, IntrinsicDirty::StyleChange,
724                                       NS_FRAME_IS_DIRTY);
725       }
726     }
727   }
728 
729   return NS_OK;
730 }
731 
IsSVGTransformed(Matrix * aOwnTransform,Matrix * aFromParentTransform) const732 bool nsSVGOuterSVGFrame::IsSVGTransformed(Matrix* aOwnTransform,
733                                           Matrix* aFromParentTransform) const {
734   // Our anonymous child's HasChildrenOnlyTransform() implementation makes sure
735   // our children-only transforms are applied to our children.  We only care
736   // about transforms that transform our own frame here.
737 
738   bool foundTransform = false;
739 
740   SVGSVGElement* content = static_cast<SVGSVGElement*>(GetContent());
741   SVGAnimatedTransformList* transformList = content->GetAnimatedTransformList();
742   if ((transformList && transformList->HasTransform()) ||
743       content->GetAnimateMotionTransform()) {
744     if (aOwnTransform) {
745       *aOwnTransform = gfx::ToMatrix(
746           content->PrependLocalTransformsTo(gfxMatrix(), eUserSpaceToParent));
747     }
748     foundTransform = true;
749   }
750 
751   return foundTransform;
752 }
753 
754 //----------------------------------------------------------------------
755 // painting
756 
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)757 void nsSVGOuterSVGFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
758                                           const nsDisplayListSet& aLists) {
759   if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
760     return;
761   }
762 
763   DisplayBorderBackgroundOutline(aBuilder, aLists);
764 
765   nsRect visibleRect = aBuilder->GetVisibleRect();
766   nsRect dirtyRect = aBuilder->GetDirtyRect();
767 
768   // Per-spec, we always clip root-<svg> even when 'overflow' has its initial
769   // value of 'visible'. See also the "visual overflow" comments in Reflow.
770   DisplayListClipState::AutoSaveRestore autoSR(aBuilder);
771   if (mIsRootContent || StyleDisplay()->IsScrollableOverflow()) {
772     autoSR.ClipContainingBlockDescendantsToContentBox(aBuilder, this);
773     visibleRect = visibleRect.Intersect(GetContentRectRelativeToSelf());
774     dirtyRect = dirtyRect.Intersect(GetContentRectRelativeToSelf());
775   }
776 
777   nsDisplayListBuilder::AutoBuildingDisplayList building(
778       aBuilder, this, visibleRect, dirtyRect);
779 
780   if ((aBuilder->IsForEventDelivery() &&
781        NS_SVGDisplayListHitTestingEnabled()) ||
782       (!aBuilder->IsForEventDelivery() && NS_SVGDisplayListPaintingEnabled())) {
783     nsDisplayList* contentList = aLists.Content();
784     nsDisplayListSet set(contentList, contentList, contentList, contentList,
785                          contentList, contentList);
786     BuildDisplayListForNonBlockChildren(aBuilder, set);
787   } else if (IsVisibleForPainting() || !aBuilder->IsForPainting()) {
788     aLists.Content()->AppendNewToTop<nsDisplayOuterSVG>(aBuilder, this);
789   }
790 }
791 
792 //----------------------------------------------------------------------
793 // nsISVGSVGFrame methods:
794 
NotifyViewportOrTransformChanged(uint32_t aFlags)795 void nsSVGOuterSVGFrame::NotifyViewportOrTransformChanged(uint32_t aFlags) {
796   MOZ_ASSERT(aFlags && !(aFlags & ~(COORD_CONTEXT_CHANGED | TRANSFORM_CHANGED |
797                                     FULL_ZOOM_CHANGED)),
798              "Unexpected aFlags value");
799 
800   // No point in doing anything when were not init'ed yet:
801   if (!mViewportInitialized) {
802     return;
803   }
804 
805   SVGSVGElement* content = static_cast<SVGSVGElement*>(GetContent());
806 
807   if (aFlags & COORD_CONTEXT_CHANGED) {
808     if (content->HasViewBox()) {
809       // Percentage lengths on children resolve against the viewBox rect so we
810       // don't need to notify them of the viewport change, but the viewBox
811       // transform will have changed, so we need to notify them of that instead.
812       aFlags = TRANSFORM_CHANGED;
813     } else if (content->ShouldSynthesizeViewBox()) {
814       // In the case of a synthesized viewBox, the synthetic viewBox's rect
815       // changes as the viewport changes. As a result we need to maintain the
816       // COORD_CONTEXT_CHANGED flag.
817       aFlags |= TRANSFORM_CHANGED;
818     } else if (mCanvasTM && mCanvasTM->IsSingular()) {
819       // A width/height of zero will result in us having a singular mCanvasTM
820       // even when we don't have a viewBox. So we also want to recompute our
821       // mCanvasTM for this width/height change even though we don't have a
822       // viewBox.
823       aFlags |= TRANSFORM_CHANGED;
824     }
825   }
826 
827   bool haveNonFulLZoomTransformChange = (aFlags & TRANSFORM_CHANGED);
828 
829   if (aFlags & FULL_ZOOM_CHANGED) {
830     // Convert FULL_ZOOM_CHANGED to TRANSFORM_CHANGED:
831     aFlags = (aFlags & ~FULL_ZOOM_CHANGED) | TRANSFORM_CHANGED;
832   }
833 
834   if (aFlags & TRANSFORM_CHANGED) {
835     // Make sure our canvas transform matrix gets (lazily) recalculated:
836     mCanvasTM = nullptr;
837 
838     if (haveNonFulLZoomTransformChange && !(mState & NS_FRAME_IS_NONDISPLAY)) {
839       uint32_t flags =
840           (mState & NS_FRAME_IN_REFLOW) ? SVGSVGElement::eDuringReflow : 0;
841       content->ChildrenOnlyTransformChanged(flags);
842     }
843   }
844 
845   nsSVGUtils::NotifyChildrenOfSVGChange(PrincipalChildList().FirstChild(),
846                                         aFlags);
847 }
848 
849 //----------------------------------------------------------------------
850 // nsSVGDisplayableFrame methods:
851 
PaintSVG(gfxContext & aContext,const gfxMatrix & aTransform,imgDrawingParams & aImgParams,const nsIntRect * aDirtyRect)852 void nsSVGOuterSVGFrame::PaintSVG(gfxContext& aContext,
853                                   const gfxMatrix& aTransform,
854                                   imgDrawingParams& aImgParams,
855                                   const nsIntRect* aDirtyRect) {
856   NS_ASSERTION(
857       PrincipalChildList().FirstChild()->IsSVGOuterSVGAnonChildFrame() &&
858           !PrincipalChildList().FirstChild()->GetNextSibling(),
859       "We should have a single, anonymous, child");
860   nsSVGOuterSVGAnonChildFrame* anonKid =
861       static_cast<nsSVGOuterSVGAnonChildFrame*>(
862           PrincipalChildList().FirstChild());
863   anonKid->PaintSVG(aContext, aTransform, aImgParams, aDirtyRect);
864 }
865 
GetBBoxContribution(const gfx::Matrix & aToBBoxUserspace,uint32_t aFlags)866 SVGBBox nsSVGOuterSVGFrame::GetBBoxContribution(
867     const gfx::Matrix& aToBBoxUserspace, uint32_t aFlags) {
868   NS_ASSERTION(
869       PrincipalChildList().FirstChild()->IsSVGOuterSVGAnonChildFrame() &&
870           !PrincipalChildList().FirstChild()->GetNextSibling(),
871       "We should have a single, anonymous, child");
872   // We must defer to our child so that we don't include our
873   // content->PrependLocalTransformsTo() transforms.
874   nsSVGOuterSVGAnonChildFrame* anonKid =
875       static_cast<nsSVGOuterSVGAnonChildFrame*>(
876           PrincipalChildList().FirstChild());
877   return anonKid->GetBBoxContribution(aToBBoxUserspace, aFlags);
878 }
879 
880 //----------------------------------------------------------------------
881 // nsSVGContainerFrame methods:
882 
GetCanvasTM()883 gfxMatrix nsSVGOuterSVGFrame::GetCanvasTM() {
884   if (!mCanvasTM) {
885     SVGSVGElement* content = static_cast<SVGSVGElement*>(GetContent());
886 
887     float devPxPerCSSPx = 1.0f / nsPresContext::AppUnitsToFloatCSSPixels(
888                                      PresContext()->AppUnitsPerDevPixel());
889 
890     gfxMatrix tm = content->PrependLocalTransformsTo(
891         gfxMatrix::Scaling(devPxPerCSSPx, devPxPerCSSPx));
892     mCanvasTM = MakeUnique<gfxMatrix>(tm);
893   }
894   return *mCanvasTM;
895 }
896 
897 //----------------------------------------------------------------------
898 // Implementation helpers
899 
900 template <typename... Args>
IsContainingWindowElementOfType(nsIFrame ** aContainingWindowFrame,Args...aArgs) const901 bool nsSVGOuterSVGFrame::IsContainingWindowElementOfType(
902     nsIFrame** aContainingWindowFrame, Args... aArgs) const {
903   if (!mContent->GetParent()) {
904     // Our content is the document element
905     nsCOMPtr<nsIDocShell> docShell = PresContext()->GetDocShell();
906     nsCOMPtr<nsPIDOMWindowOuter> window;
907     if (docShell) {
908       window = docShell->GetWindow();
909     }
910 
911     if (window) {
912       RefPtr<Element> frameElement = window->GetFrameElement();
913       if (frameElement && frameElement->IsAnyOfHTMLElements(aArgs...)) {
914         if (aContainingWindowFrame) {
915           *aContainingWindowFrame = frameElement->GetPrimaryFrame();
916           NS_ASSERTION(*aContainingWindowFrame, "Yikes, no frame!");
917         }
918         return true;
919       }
920     }
921   }
922   if (aContainingWindowFrame) {
923     *aContainingWindowFrame = nullptr;
924   }
925   return false;
926 }
927 
IsRootOfReplacedElementSubDoc(nsIFrame ** aEmbeddingFrame)928 bool nsSVGOuterSVGFrame::IsRootOfReplacedElementSubDoc(
929     nsIFrame** aEmbeddingFrame) {
930   return IsContainingWindowElementOfType(aEmbeddingFrame, nsGkAtoms::object,
931                                          nsGkAtoms::embed);
932 }
933 
IsRootOfImage()934 bool nsSVGOuterSVGFrame::IsRootOfImage() {
935   if (!mContent->GetParent()) {
936     // Our content is the document element
937     Document* doc = mContent->GetUncomposedDoc();
938     if (doc && doc->IsBeingUsedAsImage()) {
939       // Our document is being used as an image
940       return true;
941     }
942   }
943 
944   return false;
945 }
946 
VerticalScrollbarNotNeeded() const947 bool nsSVGOuterSVGFrame::VerticalScrollbarNotNeeded() const {
948   const SVGAnimatedLength& height =
949       static_cast<SVGSVGElement*>(GetContent())
950           ->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT];
951   return height.IsPercentage() && height.GetBaseValInSpecifiedUnits() <= 100;
952 }
953 
AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox> & aResult)954 void nsSVGOuterSVGFrame::AppendDirectlyOwnedAnonBoxes(
955     nsTArray<OwnedAnonBox>& aResult) {
956   nsIFrame* anonKid = PrincipalChildList().FirstChild();
957   MOZ_ASSERT(anonKid->IsSVGOuterSVGAnonChildFrame());
958   aResult.AppendElement(OwnedAnonBox(anonKid));
959 }
960 
961 //----------------------------------------------------------------------
962 // Implementation of nsSVGOuterSVGAnonChildFrame
963 
NS_NewSVGOuterSVGAnonChildFrame(PresShell * aPresShell,ComputedStyle * aStyle)964 nsContainerFrame* NS_NewSVGOuterSVGAnonChildFrame(PresShell* aPresShell,
965                                                   ComputedStyle* aStyle) {
966   return new (aPresShell)
967       nsSVGOuterSVGAnonChildFrame(aStyle, aPresShell->GetPresContext());
968 }
969 
NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGAnonChildFrame)970 NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGAnonChildFrame)
971 
972 #ifdef DEBUG
973 void nsSVGOuterSVGAnonChildFrame::Init(nsIContent* aContent,
974                                        nsContainerFrame* aParent,
975                                        nsIFrame* aPrevInFlow) {
976   MOZ_ASSERT(aParent->IsSVGOuterSVGFrame(), "Unexpected parent");
977   nsSVGDisplayContainerFrame::Init(aContent, aParent, aPrevInFlow);
978 }
979 #endif
980 
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)981 void nsSVGOuterSVGAnonChildFrame::BuildDisplayList(
982     nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) {
983   // Wrap our contents into an nsDisplaySVGWrapper.
984   // We wrap this frame instead of the nsSVGOuterSVGFrame so that the wrapper
985   // doesn't contain the <svg> element's CSS styles, like backgrounds or
986   // borders. Creating the nsDisplaySVGWrapper here also means that it'll be
987   // inside the nsDisplayTransform for our viewbox transform. The
988   // nsDisplaySVGWrapper's reference frame is this frame, because this frame
989   // always returns true from IsSVGTransformed.
990   nsDisplayList newList;
991   nsDisplayListSet set(&newList, &newList, &newList, &newList, &newList,
992                        &newList);
993   BuildDisplayListForNonBlockChildren(aBuilder, set);
994   aLists.Content()->AppendNewToTop<nsDisplaySVGWrapper>(aBuilder, this,
995                                                         &newList);
996 }
997 
ComputeOuterSVGAnonChildFrameTransform(const nsSVGOuterSVGAnonChildFrame * aFrame)998 static Matrix ComputeOuterSVGAnonChildFrameTransform(
999     const nsSVGOuterSVGAnonChildFrame* aFrame) {
1000   // Our elements 'transform' attribute is applied to our nsSVGOuterSVGFrame
1001   // parent, and the element's children-only transforms are applied to us, the
1002   // anonymous child frame. Since we are the child frame, we apply the
1003   // children-only transforms as if they are our own transform.
1004   SVGSVGElement* content = static_cast<SVGSVGElement*>(aFrame->GetContent());
1005 
1006   if (!content->HasChildrenOnlyTransform()) {
1007     return Matrix();
1008   }
1009 
1010   // Outer-<svg> doesn't use x/y, so we can pass eChildToUserSpace here.
1011   gfxMatrix ownMatrix =
1012       content->PrependLocalTransformsTo(gfxMatrix(), eChildToUserSpace);
1013 
1014   if (ownMatrix.HasNonTranslation()) {
1015     // viewBox, currentScale and currentTranslate should only produce a
1016     // rectilinear transform.
1017     MOZ_ASSERT(ownMatrix.IsRectilinear(),
1018                "Non-rectilinear transform will break the following logic");
1019 
1020     // The nsDisplayTransform code will apply this transform to our frame,
1021     // including to our frame position.  We don't want our frame position to
1022     // be scaled though, so we need to correct for that in the transform.
1023     // XXX Yeah, this is a bit hacky.
1024     CSSPoint pos = CSSPixel::FromAppUnits(aFrame->GetPosition());
1025     CSSPoint scaledPos = CSSPoint(ownMatrix._11 * pos.x, ownMatrix._22 * pos.y);
1026     CSSPoint deltaPos = scaledPos - pos;
1027     ownMatrix *= gfxMatrix::Translation(-deltaPos.x, -deltaPos.y);
1028   }
1029 
1030   return gfx::ToMatrix(ownMatrix);
1031 }
1032 
1033 // We want this frame to be a reference frame. An easy way to achieve that is
1034 // to always return true from this method, even for identity transforms.
1035 // This frame being a reference frame ensures that the offset between this
1036 // <svg> element and the parent reference frame is completely absorbed by the
1037 // nsDisplayTransform that's created for this frame, and that this offset does
1038 // not affect our descendants' transforms. Consequently, if the <svg> element
1039 // moves, e.g. during scrolling, the transform matrices of our contents are
1040 // unaffected. This simplifies invalidation.
IsSVGTransformed(Matrix * aOwnTransform,Matrix * aFromParentTransform) const1041 bool nsSVGOuterSVGAnonChildFrame::IsSVGTransformed(
1042     Matrix* aOwnTransform, Matrix* aFromParentTransform) const {
1043   if (aOwnTransform) {
1044     *aOwnTransform = ComputeOuterSVGAnonChildFrameTransform(this);
1045   }
1046 
1047   return true;
1048 }
1049