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