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