Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/layout/svg/nsSVGForeignObjectFrame.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 "nsSVGForeignObjectFrame.h"
9
10
// Keep others in (case-insensitive) order:
11
#include "ImgDrawResult.h"
12
#include "gfxContext.h"
13
#include "nsDisplayList.h"
14
#include "nsGkAtoms.h"
15
#include "nsNameSpaceManager.h"
16
#include "nsLayoutUtils.h"
17
#include "nsRegion.h"
18
#include "nsSVGContainerFrame.h"
19
#include "SVGObserverUtils.h"
20
#include "mozilla/dom/SVGForeignObjectElement.h"
21
#include "nsSVGIntegrationUtils.h"
22
#include "nsSVGOuterSVGFrame.h"
23
#include "nsSVGUtils.h"
24
#include "mozilla/AutoRestore.h"
25
26
using namespace mozilla;
27
using namespace mozilla::dom;
28
using namespace mozilla::image;
29
30
//----------------------------------------------------------------------
31
// Implementation
32
33
nsContainerFrame*
34
NS_NewSVGForeignObjectFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle)
35
0
{
36
0
  return new (aPresShell) nsSVGForeignObjectFrame(aStyle);
37
0
}
38
39
NS_IMPL_FRAMEARENA_HELPERS(nsSVGForeignObjectFrame)
40
41
nsSVGForeignObjectFrame::nsSVGForeignObjectFrame(ComputedStyle* aStyle)
42
  : nsContainerFrame(aStyle, kClassID)
43
  , mInReflow(false)
44
0
{
45
0
  AddStateBits(NS_FRAME_REFLOW_ROOT | NS_FRAME_MAY_BE_TRANSFORMED |
46
0
               NS_FRAME_SVG_LAYOUT);
47
0
}
48
49
//----------------------------------------------------------------------
50
// nsIFrame methods
51
52
0
NS_QUERYFRAME_HEAD(nsSVGForeignObjectFrame)
53
0
  NS_QUERYFRAME_ENTRY(nsSVGDisplayableFrame)
54
0
NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
55
56
void
57
nsSVGForeignObjectFrame::Init(nsIContent*       aContent,
58
                              nsContainerFrame* aParent,
59
                              nsIFrame*         aPrevInFlow)
60
0
{
61
0
  NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::foreignObject),
62
0
               "Content is not an SVG foreignObject!");
63
0
64
0
  nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
65
0
  AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD);
66
0
  AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER |
67
0
               NS_FRAME_FONT_INFLATION_FLOW_ROOT);
68
0
  if (!(mState & NS_FRAME_IS_NONDISPLAY)) {
69
0
    nsSVGUtils::GetOuterSVGFrame(this)->RegisterForeignObject(this);
70
0
  }
71
0
}
72
73
void nsSVGForeignObjectFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData)
74
0
{
75
0
  // Only unregister if we registered in the first place:
76
0
  if (!(mState & NS_FRAME_IS_NONDISPLAY)) {
77
0
      nsSVGUtils::GetOuterSVGFrame(this)->UnregisterForeignObject(this);
78
0
  }
79
0
  nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
80
0
}
81
82
nsresult
83
nsSVGForeignObjectFrame::AttributeChanged(int32_t  aNameSpaceID,
84
                                          nsAtom *aAttribute,
85
                                          int32_t  aModType)
86
0
{
87
0
  if (aNameSpaceID == kNameSpaceID_None) {
88
0
    if (aAttribute == nsGkAtoms::width ||
89
0
        aAttribute == nsGkAtoms::height) {
90
0
      nsLayoutUtils::PostRestyleEvent(
91
0
        mContent->AsElement(), nsRestyleHint(0),
92
0
        nsChangeHint_InvalidateRenderingObservers);
93
0
      nsSVGUtils::ScheduleReflowSVG(this);
94
0
      // XXXjwatt: why mark intrinsic widths dirty? can't we just use eResize?
95
0
      RequestReflow(nsIPresShell::eStyleChange);
96
0
    } else if (aAttribute == nsGkAtoms::x ||
97
0
               aAttribute == nsGkAtoms::y) {
98
0
      // make sure our cached transform matrix gets (lazily) updated
99
0
      mCanvasTM = nullptr;
100
0
      nsLayoutUtils::PostRestyleEvent(
101
0
        mContent->AsElement(), nsRestyleHint(0),
102
0
        nsChangeHint_InvalidateRenderingObservers);
103
0
      nsSVGUtils::ScheduleReflowSVG(this);
104
0
    } else if (aAttribute == nsGkAtoms::transform) {
105
0
      // We don't invalidate for transform changes (the layers code does that).
106
0
      // Also note that SVGTransformableElement::GetAttributeChangeHint will
107
0
      // return nsChangeHint_UpdateOverflow for "transform" attribute changes
108
0
      // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
109
0
      mCanvasTM = nullptr;
110
0
    } else if (aAttribute == nsGkAtoms::viewBox ||
111
0
               aAttribute == nsGkAtoms::preserveAspectRatio) {
112
0
      nsLayoutUtils::PostRestyleEvent(
113
0
        mContent->AsElement(), nsRestyleHint(0),
114
0
        nsChangeHint_InvalidateRenderingObservers);
115
0
    }
116
0
  }
117
0
118
0
  return NS_OK;
119
0
}
120
121
void
122
nsSVGForeignObjectFrame::Reflow(nsPresContext*           aPresContext,
123
                                ReflowOutput&     aDesiredSize,
124
                                const ReflowInput& aReflowInput,
125
                                nsReflowStatus&          aStatus)
126
0
{
127
0
  MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
128
0
  MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
129
0
             "Should not have been called");
130
0
131
0
  // Only InvalidateAndScheduleBoundsUpdate marks us with NS_FRAME_IS_DIRTY,
132
0
  // so if that bit is still set we still have a resize pending. If we hit
133
0
  // this assertion, then we should get the presShell to skip reflow roots
134
0
  // that have a dirty parent since a reflow is going to come via the
135
0
  // reflow root's parent anyway.
136
0
  NS_ASSERTION(!(GetStateBits() & NS_FRAME_IS_DIRTY),
137
0
               "Reflowing while a resize is pending is wasteful");
138
0
139
0
  // ReflowSVG makes sure mRect is up to date before we're called.
140
0
141
0
  NS_ASSERTION(!aReflowInput.mParentReflowInput,
142
0
               "should only get reflow from being reflow root");
143
0
  NS_ASSERTION(aReflowInput.ComputedWidth() == GetSize().width &&
144
0
               aReflowInput.ComputedHeight() == GetSize().height,
145
0
               "reflow roots should be reflowed at existing size and "
146
0
               "svg.css should ensure we have no padding/border/margin");
147
0
148
0
  DoReflow();
149
0
150
0
  WritingMode wm = aReflowInput.GetWritingMode();
151
0
  LogicalSize finalSize(wm, aReflowInput.ComputedISize(),
152
0
                        aReflowInput.ComputedBSize());
153
0
  aDesiredSize.SetSize(wm, finalSize);
154
0
  aDesiredSize.SetOverflowAreasToDesiredBounds();
155
0
}
156
157
void
158
nsSVGForeignObjectFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
159
                                          const nsDisplayListSet& aLists)
160
0
{
161
0
  if (!static_cast<const nsSVGElement*>(GetContent())->HasValidDimensions()) {
162
0
    return;
163
0
  }
164
0
  nsDisplayList newList;
165
0
  nsDisplayListSet set(&newList, &newList, &newList,
166
0
                       &newList, &newList, &newList);
167
0
  DisplayOutline(aBuilder, set);
168
0
  BuildDisplayListForNonBlockChildren(aBuilder, set);
169
0
  aLists.Content()->AppendToTop(MakeDisplayItem<nsDisplayForeignObject>(aBuilder, this, &newList));
170
0
}
171
172
bool
173
nsSVGForeignObjectFrame::IsSVGTransformed(Matrix *aOwnTransform,
174
                                          Matrix *aFromParentTransform) const
175
0
{
176
0
  bool foundTransform = false;
177
0
178
0
  // Check if our parent has children-only transforms:
179
0
  nsIFrame *parent = GetParent();
180
0
  if (parent &&
181
0
      parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
182
0
    foundTransform = static_cast<nsSVGContainerFrame*>(parent)->
183
0
                       HasChildrenOnlyTransform(aFromParentTransform);
184
0
  }
185
0
186
0
  nsSVGElement *content = static_cast<nsSVGElement*>(GetContent());
187
0
  nsSVGAnimatedTransformList* transformList =
188
0
    content->GetAnimatedTransformList();
189
0
  if ((transformList && transformList->HasTransform()) ||
190
0
      content->GetAnimateMotionTransform()) {
191
0
    if (aOwnTransform) {
192
0
      *aOwnTransform = gfx::ToMatrix(content->PrependLocalTransformsTo(
193
0
                                       gfxMatrix(),
194
0
                                       eUserSpaceToParent));
195
0
    }
196
0
    foundTransform = true;
197
0
  }
198
0
  return foundTransform;
199
0
}
200
201
void
202
nsSVGForeignObjectFrame::PaintSVG(gfxContext& aContext,
203
                                  const gfxMatrix& aTransform,
204
                                  imgDrawingParams& aImgParams,
205
                                  const nsIntRect* aDirtyRect)
206
0
{
207
0
  NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
208
0
               (mState & NS_FRAME_IS_NONDISPLAY),
209
0
               "If display lists are enabled, only painting of non-display "
210
0
               "SVG should take this code path");
211
0
212
0
  if (IsDisabled()) {
213
0
    return;
214
0
  }
215
0
216
0
  nsIFrame* kid = PrincipalChildList().FirstChild();
217
0
  if (!kid) {
218
0
    return;
219
0
  }
220
0
221
0
  if (aTransform.IsSingular()) {
222
0
    NS_WARNING("Can't render foreignObject element!");
223
0
    return;
224
0
  }
225
0
226
0
  nsRect kidDirtyRect = kid->GetVisualOverflowRect();
227
0
228
0
  /* Check if we need to draw anything. */
229
0
  if (aDirtyRect) {
230
0
    NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
231
0
                 (mState & NS_FRAME_IS_NONDISPLAY),
232
0
                 "Display lists handle dirty rect intersection test");
233
0
    // Transform the dirty rect into app units in our userspace.
234
0
    gfxMatrix invmatrix = aTransform;
235
0
    DebugOnly<bool> ok = invmatrix.Invert();
236
0
    NS_ASSERTION(ok, "inverse of non-singular matrix should be non-singular");
237
0
238
0
    gfxRect transDirtyRect = gfxRect(aDirtyRect->x, aDirtyRect->y,
239
0
                                     aDirtyRect->width, aDirtyRect->height);
240
0
    transDirtyRect = invmatrix.TransformBounds(transDirtyRect);
241
0
242
0
    kidDirtyRect.IntersectRect(kidDirtyRect,
243
0
      nsLayoutUtils::RoundGfxRectToAppRect(transDirtyRect,
244
0
                       AppUnitsPerCSSPixel()));
245
0
246
0
    // XXX after bug 614732 is fixed, we will compare mRect with aDirtyRect,
247
0
    // not with kidDirtyRect. I.e.
248
0
    // int32_t appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel();
249
0
    // mRect.ToOutsidePixels(appUnitsPerDevPx).Intersects(*aDirtyRect)
250
0
    if (kidDirtyRect.IsEmpty())
251
0
      return;
252
0
  }
253
0
254
0
  aContext.Save();
255
0
256
0
  if (StyleDisplay()->IsScrollableOverflow()) {
257
0
    float x, y, width, height;
258
0
    static_cast<nsSVGElement*>(GetContent())->
259
0
      GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
260
0
261
0
    gfxRect clipRect =
262
0
      nsSVGUtils::GetClipRectForFrame(this, 0.0f, 0.0f, width, height);
263
0
    nsSVGUtils::SetClipRect(&aContext, aTransform, clipRect);
264
0
  }
265
0
266
0
  // SVG paints in CSS px, but normally frames paint in dev pixels. Here we
267
0
  // multiply a CSS-px-to-dev-pixel factor onto aTransform so our children
268
0
  // paint correctly.
269
0
  float cssPxPerDevPx = PresContext()->
270
0
    AppUnitsToFloatCSSPixels(PresContext()->AppUnitsPerDevPixel());
271
0
  gfxMatrix canvasTMForChildren = aTransform;
272
0
  canvasTMForChildren.PreScale(cssPxPerDevPx, cssPxPerDevPx);
273
0
274
0
  aContext.Multiply(canvasTMForChildren);
275
0
276
0
  using PaintFrameFlags = nsLayoutUtils::PaintFrameFlags;
277
0
  PaintFrameFlags flags = PaintFrameFlags::PAINT_IN_TRANSFORM;
278
0
  if (SVGAutoRenderState::IsPaintingToWindow(aContext.GetDrawTarget())) {
279
0
    flags |= PaintFrameFlags::PAINT_TO_WINDOW;
280
0
  }
281
0
  if (aImgParams.imageFlags & imgIContainer::FLAG_SYNC_DECODE) {
282
0
    flags |= PaintFrameFlags::PAINT_SYNC_DECODE_IMAGES;
283
0
  }
284
0
  Unused << nsLayoutUtils::PaintFrame(&aContext, kid, nsRegion(kidDirtyRect),
285
0
                                      NS_RGBA(0,0,0,0),
286
0
                                      nsDisplayListBuilderMode::PAINTING,
287
0
                                      flags);
288
0
289
0
  aContext.Restore();
290
0
}
291
292
nsIFrame*
293
nsSVGForeignObjectFrame::GetFrameForPoint(const gfxPoint& aPoint)
294
0
{
295
0
  NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
296
0
               (mState & NS_FRAME_IS_NONDISPLAY),
297
0
               "If display lists are enabled, only hit-testing of a "
298
0
               "clipPath's contents should take this code path");
299
0
300
0
  if (IsDisabled() || (GetStateBits() & NS_FRAME_IS_NONDISPLAY))
301
0
    return nullptr;
302
0
303
0
  nsIFrame* kid = PrincipalChildList().FirstChild();
304
0
  if (!kid)
305
0
    return nullptr;
306
0
307
0
  float x, y, width, height;
308
0
  static_cast<nsSVGElement*>(GetContent())->
309
0
    GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
310
0
311
0
  if (!gfxRect(x, y, width, height).Contains(aPoint) ||
312
0
      !nsSVGUtils::HitTestClip(this, aPoint)) {
313
0
    return nullptr;
314
0
  }
315
0
316
0
  // Convert the point to app units relative to the top-left corner of the
317
0
  // viewport that's established by the foreignObject element:
318
0
319
0
  gfxPoint pt = (aPoint + gfxPoint(x, y)) * AppUnitsPerCSSPixel();
320
0
  nsPoint point = nsPoint(NSToIntRound(pt.x), NSToIntRound(pt.y));
321
0
322
0
  return nsLayoutUtils::GetFrameForPoint(kid, point);
323
0
}
324
325
void
326
nsSVGForeignObjectFrame::ReflowSVG()
327
0
{
328
0
  NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
329
0
               "This call is probably a wasteful mistake");
330
0
331
0
  MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
332
0
             "ReflowSVG mechanism not designed for this");
333
0
334
0
  if (!nsSVGUtils::NeedsReflowSVG(this)) {
335
0
    return;
336
0
  }
337
0
338
0
  // We update mRect before the DoReflow call so that DoReflow uses the
339
0
  // correct dimensions:
340
0
341
0
  float x, y, w, h;
342
0
  static_cast<SVGForeignObjectElement*>(GetContent())->
343
0
    GetAnimatedLengthValues(&x, &y, &w, &h, nullptr);
344
0
345
0
  // If mRect's width or height are negative, reflow blows up! We must clamp!
346
0
  if (w < 0.0f) w = 0.0f;
347
0
  if (h < 0.0f) h = 0.0f;
348
0
349
0
  mRect = nsLayoutUtils::RoundGfxRectToAppRect(
350
0
                           gfxRect(x, y, w, h),
351
0
                           AppUnitsPerCSSPixel());
352
0
353
0
  // Fully mark our kid dirty so that it gets resized if necessary
354
0
  // (NS_FRAME_HAS_DIRTY_CHILDREN isn't enough in that case):
355
0
  nsIFrame* kid = PrincipalChildList().FirstChild();
356
0
  kid->AddStateBits(NS_FRAME_IS_DIRTY);
357
0
358
0
  // Make sure to not allow interrupts if we're not being reflown as a root:
359
0
  nsPresContext::InterruptPreventer noInterrupts(PresContext());
360
0
361
0
  DoReflow();
362
0
363
0
  if (mState & NS_FRAME_FIRST_REFLOW) {
364
0
    // Make sure we have our filter property (if any) before calling
365
0
    // FinishAndStoreOverflow (subsequent filter changes are handled off
366
0
    // nsChangeHint_UpdateEffects):
367
0
    SVGObserverUtils::UpdateEffects(this);
368
0
  }
369
0
370
0
  // If we have a filter, we need to invalidate ourselves because filter
371
0
  // output can change even if none of our descendants need repainting.
372
0
  if (StyleEffects()->HasFilters()) {
373
0
    InvalidateFrame();
374
0
  }
375
0
376
0
  // TODO: once we support |overflow:visible| on foreignObject, then we will
377
0
  // need to take account of our descendants here.
378
0
  nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
379
0
  nsOverflowAreas overflowAreas(overflow, overflow);
380
0
  FinishAndStoreOverflow(overflowAreas, mRect.Size());
381
0
382
0
  // Now unset the various reflow bits:
383
0
  RemoveStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
384
0
                  NS_FRAME_HAS_DIRTY_CHILDREN);
385
0
}
386
387
void
388
nsSVGForeignObjectFrame::NotifySVGChanged(uint32_t aFlags)
389
0
{
390
0
  MOZ_ASSERT(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
391
0
             "Invalidation logic may need adjusting");
392
0
393
0
  bool needNewBounds = false; // i.e. mRect or visual overflow rect
394
0
  bool needReflow = false;
395
0
  bool needNewCanvasTM = false;
396
0
397
0
  if (aFlags & COORD_CONTEXT_CHANGED) {
398
0
    SVGForeignObjectElement *fO =
399
0
      static_cast<SVGForeignObjectElement*>(GetContent());
400
0
    // Coordinate context changes affect mCanvasTM if we have a
401
0
    // percentage 'x' or 'y'
402
0
    if (fO->mLengthAttributes[SVGForeignObjectElement::ATTR_X].IsPercentage() ||
403
0
        fO->mLengthAttributes[SVGForeignObjectElement::ATTR_Y].IsPercentage()) {
404
0
      needNewBounds = true;
405
0
      needNewCanvasTM = true;
406
0
    }
407
0
    // Our coordinate context's width/height has changed. If we have a
408
0
    // percentage width/height our dimensions will change so we must reflow.
409
0
    if (fO->mLengthAttributes[SVGForeignObjectElement::ATTR_WIDTH].IsPercentage() ||
410
0
        fO->mLengthAttributes[SVGForeignObjectElement::ATTR_HEIGHT].IsPercentage()) {
411
0
      needNewBounds = true;
412
0
      needReflow = true;
413
0
    }
414
0
  }
415
0
416
0
  if (aFlags & TRANSFORM_CHANGED) {
417
0
    if (mCanvasTM && mCanvasTM->IsSingular()) {
418
0
      needNewBounds = true; // old bounds are bogus
419
0
    }
420
0
    needNewCanvasTM = true;
421
0
    // In an ideal world we would reflow when our CTM changes. This is because
422
0
    // glyph metrics do not necessarily scale uniformly with change in scale
423
0
    // and, as a result, CTM changes may require text to break at different
424
0
    // points. The problem would be how to keep performance acceptable when
425
0
    // e.g. the transform of an ancestor is animated.
426
0
    // We also seem to get some sort of infinite loop post bug 421584 if we
427
0
    // reflow.
428
0
  }
429
0
430
0
  if (needNewBounds) {
431
0
    // Ancestor changes can't affect how we render from the perspective of
432
0
    // any rendering observers that we may have, so we don't need to
433
0
    // invalidate them. We also don't need to invalidate ourself, since our
434
0
    // changed ancestor will have invalidated its entire area, which includes
435
0
    // our area.
436
0
    nsSVGUtils::ScheduleReflowSVG(this);
437
0
  }
438
0
439
0
  // If we're called while the PresShell is handling reflow events then we
440
0
  // must have been called as a result of the NotifyViewportChange() call in
441
0
  // our nsSVGOuterSVGFrame's Reflow() method. We must not call RequestReflow
442
0
  // at this point (i.e. during reflow) because it could confuse the
443
0
  // PresShell and prevent it from reflowing us properly in future. Besides
444
0
  // that, nsSVGOuterSVGFrame::DidReflow will take care of reflowing us
445
0
  // synchronously, so there's no need.
446
0
  if (needReflow && !PresShell()->IsReflowLocked()) {
447
0
    RequestReflow(nsIPresShell::eResize);
448
0
  }
449
0
450
0
  if (needNewCanvasTM) {
451
0
    // Do this after calling InvalidateAndScheduleBoundsUpdate in case we
452
0
    // change the code and it needs to use it.
453
0
    mCanvasTM = nullptr;
454
0
  }
455
0
}
456
457
SVGBBox
458
nsSVGForeignObjectFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace,
459
                                             uint32_t aFlags)
460
0
{
461
0
  SVGForeignObjectElement *content =
462
0
    static_cast<SVGForeignObjectElement*>(GetContent());
463
0
464
0
  float x, y, w, h;
465
0
  content->GetAnimatedLengthValues(&x, &y, &w, &h, nullptr);
466
0
467
0
  if (w < 0.0f) w = 0.0f;
468
0
  if (h < 0.0f) h = 0.0f;
469
0
470
0
  if (aToBBoxUserspace.IsSingular()) {
471
0
    // XXX ReportToConsole
472
0
    return SVGBBox();
473
0
  }
474
0
  return aToBBoxUserspace.TransformBounds(gfx::Rect(0.0, 0.0, w, h));
475
0
}
476
477
//----------------------------------------------------------------------
478
479
gfxMatrix
480
nsSVGForeignObjectFrame::GetCanvasTM()
481
0
{
482
0
  if (!mCanvasTM) {
483
0
    NS_ASSERTION(GetParent(), "null parent");
484
0
485
0
    nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(GetParent());
486
0
    SVGForeignObjectElement *content =
487
0
      static_cast<SVGForeignObjectElement*>(GetContent());
488
0
489
0
    gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM());
490
0
491
0
    mCanvasTM = new gfxMatrix(tm);
492
0
  }
493
0
  return *mCanvasTM;
494
0
}
495
496
//----------------------------------------------------------------------
497
// Implementation helpers
498
499
void nsSVGForeignObjectFrame::RequestReflow(nsIPresShell::IntrinsicDirty aType)
500
0
{
501
0
  if (GetStateBits() & NS_FRAME_FIRST_REFLOW)
502
0
    // If we haven't had a ReflowSVG() yet, nothing to do.
503
0
    return;
504
0
505
0
  nsIFrame* kid = PrincipalChildList().FirstChild();
506
0
  if (!kid)
507
0
    return;
508
0
509
0
  PresShell()->FrameNeedsReflow(kid, aType, NS_FRAME_IS_DIRTY);
510
0
}
511
512
void
513
nsSVGForeignObjectFrame::DoReflow()
514
0
{
515
0
  MarkInReflow();
516
0
  // Skip reflow if we're zero-sized, unless this is our first reflow.
517
0
  if (IsDisabled() &&
518
0
      !(GetStateBits() & NS_FRAME_FIRST_REFLOW))
519
0
    return;
520
0
521
0
  nsPresContext *presContext = PresContext();
522
0
  nsIFrame* kid = PrincipalChildList().FirstChild();
523
0
  if (!kid)
524
0
    return;
525
0
526
0
  // initiate a synchronous reflow here and now:
527
0
  RefPtr<gfxContext> renderingContext =
528
0
    presContext->PresShell()->CreateReferenceRenderingContext();
529
0
530
0
  mInReflow = true;
531
0
532
0
  WritingMode wm = kid->GetWritingMode();
533
0
  ReflowInput reflowInput(presContext, kid,
534
0
                                renderingContext,
535
0
                                LogicalSize(wm, ISize(wm),
536
0
                                            NS_UNCONSTRAINEDSIZE));
537
0
  ReflowOutput desiredSize(reflowInput);
538
0
  nsReflowStatus status;
539
0
540
0
  // We don't use mRect.height above because that tells the child to do
541
0
  // page/column breaking at that height.
542
0
  NS_ASSERTION(reflowInput.ComputedPhysicalBorderPadding() == nsMargin(0, 0, 0, 0) &&
543
0
               reflowInput.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0),
544
0
               "style system should ensure that :-moz-svg-foreign-content "
545
0
               "does not get styled");
546
0
  NS_ASSERTION(reflowInput.ComputedISize() == ISize(wm),
547
0
               "reflow state made child wrong size");
548
0
  reflowInput.SetComputedBSize(BSize(wm));
549
0
550
0
  ReflowChild(kid, presContext, desiredSize, reflowInput, 0, 0,
551
0
              NS_FRAME_NO_MOVE_FRAME, status);
552
0
  NS_ASSERTION(mRect.width == desiredSize.Width() &&
553
0
               mRect.height == desiredSize.Height(), "unexpected size");
554
0
  FinishReflowChild(kid, presContext, desiredSize, &reflowInput, 0, 0,
555
0
                    NS_FRAME_NO_MOVE_FRAME);
556
0
557
0
  mInReflow = false;
558
0
}
559
560
nsRect
561
nsSVGForeignObjectFrame::GetInvalidRegion()
562
0
{
563
0
  MOZ_ASSERT(!NS_SVGDisplayListPaintingEnabled(),
564
0
             "Only called by nsDisplayOuterSVG code");
565
0
566
0
  nsIFrame* kid = PrincipalChildList().FirstChild();
567
0
  if (kid->HasInvalidFrameInSubtree()) {
568
0
    gfxRect r(mRect.x, mRect.y, mRect.width, mRect.height);
569
0
    r.Scale(1.0 / AppUnitsPerCSSPixel());
570
0
    nsRect rect = nsSVGUtils::ToCanvasBounds(r, GetCanvasTM(), PresContext());
571
0
    rect = nsSVGUtils::GetPostFilterVisualOverflowRect(this, rect);
572
0
    return rect;
573
0
  }
574
0
  return nsRect();
575
0
}
576
577
void
578
nsSVGForeignObjectFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult)
579
0
{
580
0
  MOZ_ASSERT(PrincipalChildList().FirstChild(), "Must have our anon box");
581
0
  aResult.AppendElement(OwnedAnonBox(PrincipalChildList().FirstChild()));
582
0
}