Coverage Report

Created: 2018-09-25 14:53

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