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