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