1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Mozilla SVG project.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Crocodile Clips Ltd..
19 : * Portions created by the Initial Developer are Copyright (C) 2001
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsSVGForeignObjectFrame.h"
40 :
41 : #include "nsIDOMSVGForeignObjectElem.h"
42 : #include "nsIDOMSVGSVGElement.h"
43 : #include "nsSVGOuterSVGFrame.h"
44 : #include "nsRegion.h"
45 : #include "nsGkAtoms.h"
46 : #include "nsLayoutUtils.h"
47 : #include "nsSVGUtils.h"
48 : #include "nsIURI.h"
49 : #include "nsSVGRect.h"
50 : #include "nsINameSpaceManager.h"
51 : #include "nsSVGForeignObjectElement.h"
52 : #include "nsSVGContainerFrame.h"
53 : #include "gfxContext.h"
54 : #include "gfxMatrix.h"
55 :
56 : //----------------------------------------------------------------------
57 : // Implementation
58 :
59 : nsIFrame*
60 0 : NS_NewSVGForeignObjectFrame(nsIPresShell *aPresShell,
61 : nsStyleContext *aContext)
62 : {
63 0 : return new (aPresShell) nsSVGForeignObjectFrame(aContext);
64 : }
65 :
66 0 : NS_IMPL_FRAMEARENA_HELPERS(nsSVGForeignObjectFrame)
67 :
68 0 : nsSVGForeignObjectFrame::nsSVGForeignObjectFrame(nsStyleContext* aContext)
69 : : nsSVGForeignObjectFrameBase(aContext),
70 0 : mInReflow(false)
71 : {
72 0 : AddStateBits(NS_FRAME_REFLOW_ROOT | NS_FRAME_MAY_BE_TRANSFORMED);
73 0 : }
74 :
75 : //----------------------------------------------------------------------
76 : // nsIFrame methods
77 :
78 0 : NS_QUERYFRAME_HEAD(nsSVGForeignObjectFrame)
79 0 : NS_QUERYFRAME_ENTRY(nsISVGChildFrame)
80 0 : NS_QUERYFRAME_TAIL_INHERITING(nsSVGForeignObjectFrameBase)
81 :
82 : NS_IMETHODIMP
83 0 : nsSVGForeignObjectFrame::Init(nsIContent* aContent,
84 : nsIFrame* aParent,
85 : nsIFrame* aPrevInFlow)
86 : {
87 : #ifdef DEBUG
88 0 : nsCOMPtr<nsIDOMSVGForeignObjectElement> foreignObject = do_QueryInterface(aContent);
89 0 : NS_ASSERTION(foreignObject, "Content is not an SVG foreignObject!");
90 : #endif
91 :
92 0 : nsresult rv = nsSVGForeignObjectFrameBase::Init(aContent, aParent, aPrevInFlow);
93 0 : AddStateBits(aParent->GetStateBits() &
94 : (NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_CLIPPATH_CHILD |
95 0 : NS_STATE_SVG_REDRAW_SUSPENDED));
96 0 : if (NS_SUCCEEDED(rv)) {
97 0 : nsSVGUtils::GetOuterSVGFrame(this)->RegisterForeignObject(this);
98 : }
99 0 : return rv;
100 : }
101 :
102 0 : void nsSVGForeignObjectFrame::DestroyFrom(nsIFrame* aDestructRoot)
103 : {
104 0 : nsSVGUtils::GetOuterSVGFrame(this)->UnregisterForeignObject(this);
105 0 : nsSVGForeignObjectFrameBase::DestroyFrom(aDestructRoot);
106 0 : }
107 :
108 : nsIAtom *
109 0 : nsSVGForeignObjectFrame::GetType() const
110 : {
111 0 : return nsGkAtoms::svgForeignObjectFrame;
112 : }
113 :
114 : NS_IMETHODIMP
115 0 : nsSVGForeignObjectFrame::AttributeChanged(PRInt32 aNameSpaceID,
116 : nsIAtom *aAttribute,
117 : PRInt32 aModType)
118 : {
119 0 : if (aNameSpaceID == kNameSpaceID_None) {
120 0 : if (aAttribute == nsGkAtoms::width ||
121 : aAttribute == nsGkAtoms::height) {
122 0 : UpdateGraphic(); // update mRect before requesting reflow
123 : // XXXjwatt: why mark intrinsic widths dirty? can't we just use eResize?
124 0 : RequestReflow(nsIPresShell::eStyleChange);
125 0 : } else if (aAttribute == nsGkAtoms::x ||
126 : aAttribute == nsGkAtoms::y ||
127 : aAttribute == nsGkAtoms::viewBox ||
128 : aAttribute == nsGkAtoms::preserveAspectRatio ||
129 : aAttribute == nsGkAtoms::transform) {
130 : // make sure our cached transform matrix gets (lazily) updated
131 0 : mCanvasTM = nsnull;
132 0 : UpdateGraphic();
133 : }
134 : }
135 :
136 0 : return NS_OK;
137 : }
138 :
139 : /* virtual */ void
140 0 : nsSVGForeignObjectFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
141 : {
142 0 : nsSVGForeignObjectFrameBase::DidSetStyleContext(aOldStyleContext);
143 :
144 : // No need to invalidate before first reflow - that will happen elsewhere.
145 : // Moreover we haven't been initialised properly yet so we may not have the
146 : // right state bits.
147 0 : if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
148 0 : UpdateGraphic();
149 : }
150 0 : }
151 :
152 : NS_IMETHODIMP
153 0 : nsSVGForeignObjectFrame::Reflow(nsPresContext* aPresContext,
154 : nsHTMLReflowMetrics& aDesiredSize,
155 : const nsHTMLReflowState& aReflowState,
156 : nsReflowStatus& aStatus)
157 : {
158 : // InitialUpdate and AttributeChanged make sure mRect is up to date before
159 : // we're called (UpdateCoveredRegion sets mRect).
160 :
161 0 : NS_ASSERTION(!aReflowState.parentReflowState,
162 : "should only get reflow from being reflow root");
163 0 : NS_ASSERTION(aReflowState.ComputedWidth() == GetSize().width &&
164 : aReflowState.ComputedHeight() == GetSize().height,
165 : "reflow roots should be reflowed at existing size and "
166 : "svg.css should ensure we have no padding/border/margin");
167 :
168 0 : DoReflow();
169 :
170 0 : aDesiredSize.width = aReflowState.ComputedWidth();
171 0 : aDesiredSize.height = aReflowState.ComputedHeight();
172 0 : aDesiredSize.SetOverflowAreasToDesiredBounds();
173 0 : aStatus = NS_FRAME_COMPLETE;
174 :
175 0 : return NS_OK;
176 : }
177 :
178 : void
179 0 : nsSVGForeignObjectFrame::InvalidateInternal(const nsRect& aDamageRect,
180 : nscoord aX, nscoord aY,
181 : nsIFrame* aForChild,
182 : PRUint32 aFlags)
183 : {
184 : // This is called by our descendants when they change.
185 :
186 0 : if (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)
187 0 : return;
188 :
189 : nsRegion* region = (aFlags & INVALIDATE_CROSS_DOC)
190 0 : ? &mSubDocDirtyRegion : &mSameDocDirtyRegion;
191 0 : region->Or(*region, aDamageRect + nsPoint(aX, aY));
192 0 : FlushDirtyRegion(aFlags);
193 : }
194 :
195 :
196 : /**
197 : * Returns the app unit canvas bounds of a userspace rect.
198 : *
199 : * @param aToCanvas Transform from userspace to canvas device space.
200 : */
201 : static nsRect
202 0 : ToCanvasBounds(const gfxRect &aUserspaceRect,
203 : const gfxMatrix &aToCanvas,
204 : const nsPresContext *presContext)
205 : {
206 : return nsLayoutUtils::RoundGfxRectToAppRect(
207 : aToCanvas.TransformBounds(aUserspaceRect),
208 0 : presContext->AppUnitsPerDevPixel());
209 : }
210 :
211 : NS_IMETHODIMP
212 0 : nsSVGForeignObjectFrame::PaintSVG(nsRenderingContext *aContext,
213 : const nsIntRect *aDirtyRect)
214 : {
215 0 : if (IsDisabled())
216 0 : return NS_OK;
217 :
218 0 : nsIFrame* kid = GetFirstPrincipalChild();
219 0 : if (!kid)
220 0 : return NS_OK;
221 :
222 0 : gfxMatrix matrixForChildren = GetCanvasTMForChildren();
223 0 : gfxMatrix matrix = GetCanvasTM();
224 :
225 0 : if (matrixForChildren.IsSingular()) {
226 0 : NS_WARNING("Can't render foreignObject element!");
227 0 : return NS_ERROR_FAILURE;
228 : }
229 :
230 0 : nsRect kidDirtyRect = kid->GetVisualOverflowRect();
231 :
232 : /* Check if we need to draw anything. */
233 0 : if (aDirtyRect) {
234 : // Transform the dirty rect into app units in our userspace.
235 0 : gfxMatrix invmatrix = matrix;
236 0 : invmatrix.Invert();
237 0 : NS_ASSERTION(!invmatrix.IsSingular(),
238 : "inverse of non-singular matrix should be non-singular");
239 :
240 : gfxRect transDirtyRect = gfxRect(aDirtyRect->x, aDirtyRect->y,
241 0 : aDirtyRect->width, aDirtyRect->height);
242 0 : transDirtyRect = invmatrix.TransformBounds(transDirtyRect);
243 :
244 : kidDirtyRect.IntersectRect(kidDirtyRect,
245 : nsLayoutUtils::RoundGfxRectToAppRect(transDirtyRect,
246 0 : PresContext()->AppUnitsPerCSSPixel()));
247 :
248 : // XXX after bug 614732 is fixed, we will compare mRect with aDirtyRect,
249 : // not with kidDirtyRect. I.e.
250 : // PRInt32 appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel();
251 : // mRect.ToOutsidePixels(appUnitsPerDevPx).Intersects(*aDirtyRect)
252 0 : if (kidDirtyRect.IsEmpty())
253 0 : return NS_OK;
254 : }
255 :
256 0 : gfxContext *gfx = aContext->ThebesContext();
257 :
258 0 : gfx->Save();
259 :
260 0 : if (GetStyleDisplay()->IsScrollableOverflow()) {
261 : float x, y, width, height;
262 : static_cast<nsSVGElement*>(mContent)->
263 0 : GetAnimatedLengthValues(&x, &y, &width, &height, nsnull);
264 :
265 : gfxRect clipRect =
266 0 : nsSVGUtils::GetClipRectForFrame(this, 0.0f, 0.0f, width, height);
267 0 : nsSVGUtils::SetClipRect(gfx, matrix, clipRect);
268 : }
269 :
270 0 : gfx->Multiply(matrixForChildren);
271 :
272 0 : PRUint32 flags = nsLayoutUtils::PAINT_IN_TRANSFORM;
273 0 : if (SVGAutoRenderState::IsPaintingToWindow(aContext)) {
274 0 : flags |= nsLayoutUtils::PAINT_TO_WINDOW;
275 : }
276 0 : nsresult rv = nsLayoutUtils::PaintFrame(aContext, kid, nsRegion(kidDirtyRect),
277 0 : NS_RGBA(0,0,0,0), flags);
278 :
279 0 : gfx->Restore();
280 :
281 0 : return rv;
282 : }
283 :
284 : gfx3DMatrix
285 0 : nsSVGForeignObjectFrame::GetTransformMatrix(nsIFrame* aAncestor,
286 : nsIFrame **aOutAncestor)
287 : {
288 0 : NS_PRECONDITION(aOutAncestor, "We need an ancestor to write to!");
289 :
290 : /* Set the ancestor to be the outer frame. */
291 0 : *aOutAncestor = nsSVGUtils::GetOuterSVGFrame(this);
292 0 : NS_ASSERTION(*aOutAncestor, "How did we end up without an outer frame?");
293 :
294 0 : if (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) {
295 0 : return gfx3DMatrix::From2D(gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
296 : }
297 :
298 : /* Return the matrix back to the root, factoring in the x and y offsets. */
299 0 : return gfx3DMatrix::From2D(GetCanvasTMForChildren());
300 : }
301 :
302 : NS_IMETHODIMP_(nsIFrame*)
303 0 : nsSVGForeignObjectFrame::GetFrameForPoint(const nsPoint &aPoint)
304 : {
305 0 : if (IsDisabled() || (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD))
306 0 : return nsnull;
307 :
308 0 : nsIFrame* kid = GetFirstPrincipalChild();
309 0 : if (!kid)
310 0 : return nsnull;
311 :
312 : float x, y, width, height;
313 : static_cast<nsSVGElement*>(mContent)->
314 0 : GetAnimatedLengthValues(&x, &y, &width, &height, nsnull);
315 :
316 0 : gfxMatrix tm = GetCanvasTM().Invert();
317 0 : if (tm.IsSingular())
318 0 : return nsnull;
319 :
320 : // Convert aPoint from app units in canvas space to user space:
321 :
322 0 : gfxPoint pt = gfxPoint(aPoint.x, aPoint.y) / PresContext()->AppUnitsPerDevPixel();
323 0 : pt = tm.Transform(pt);
324 :
325 0 : if (!gfxRect(0.0f, 0.0f, width, height).Contains(pt))
326 0 : return nsnull;
327 :
328 : // Convert pt to app units in *local* space:
329 :
330 0 : pt = pt * nsPresContext::AppUnitsPerCSSPixel();
331 0 : nsPoint point = nsPoint(NSToIntRound(pt.x), NSToIntRound(pt.y));
332 :
333 0 : nsIFrame *frame = nsLayoutUtils::GetFrameForPoint(kid, point);
334 0 : if (frame && nsSVGUtils::HitTestClip(this, aPoint))
335 0 : return frame;
336 :
337 0 : return nsnull;
338 : }
339 :
340 : NS_IMETHODIMP_(nsRect)
341 0 : nsSVGForeignObjectFrame::GetCoveredRegion()
342 : {
343 : // See bug 614732 comment 32:
344 : //return nsSVGUtils::TransformFrameRectToOuterSVG(mRect, GetCanvasTM(), PresContext());
345 0 : return mCoveredRegion;
346 : }
347 :
348 : NS_IMETHODIMP
349 0 : nsSVGForeignObjectFrame::UpdateCoveredRegion()
350 : {
351 0 : if (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)
352 0 : return NS_ERROR_FAILURE;
353 :
354 : float x, y, w, h;
355 : static_cast<nsSVGForeignObjectElement*>(mContent)->
356 0 : GetAnimatedLengthValues(&x, &y, &w, &h, nsnull);
357 :
358 : // If mRect's width or height are negative, reflow blows up! We must clamp!
359 0 : if (w < 0.0f) w = 0.0f;
360 0 : if (h < 0.0f) h = 0.0f;
361 :
362 : // GetCanvasTM includes the x,y translation
363 : mRect = nsLayoutUtils::RoundGfxRectToAppRect(
364 : gfxRect(0.0, 0.0, w, h),
365 0 : PresContext()->AppUnitsPerCSSPixel());
366 0 : mCoveredRegion = ToCanvasBounds(gfxRect(0.0, 0.0, w, h), GetCanvasTM(), PresContext());
367 :
368 0 : return NS_OK;
369 : }
370 :
371 : NS_IMETHODIMP
372 0 : nsSVGForeignObjectFrame::InitialUpdate()
373 : {
374 0 : NS_ASSERTION(GetStateBits() & NS_FRAME_FIRST_REFLOW,
375 : "Yikes! We've been called already! Hopefully we weren't called "
376 : "before our nsSVGOuterSVGFrame's initial Reflow()!!!");
377 :
378 0 : UpdateCoveredRegion();
379 :
380 : // Make sure to not allow interrupts if we're not being reflown as a root
381 0 : nsPresContext::InterruptPreventer noInterrupts(PresContext());
382 0 : DoReflow();
383 :
384 0 : NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW),
385 : "We don't actually participate in reflow");
386 :
387 : // Do unset the various reflow bits, though.
388 : mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
389 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
390 :
391 0 : return NS_OK;
392 : }
393 :
394 : void
395 0 : nsSVGForeignObjectFrame::NotifySVGChanged(PRUint32 aFlags)
396 : {
397 0 : NS_ABORT_IF_FALSE(!(aFlags & DO_NOT_NOTIFY_RENDERING_OBSERVERS) ||
398 : (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
399 : "Must be NS_STATE_SVG_NONDISPLAY_CHILD!");
400 :
401 0 : NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
402 : "Invalidation logic may need adjusting");
403 :
404 0 : bool reflow = false;
405 :
406 0 : if (aFlags & TRANSFORM_CHANGED) {
407 : // In an ideal world we would reflow when our CTM changes. This is because
408 : // glyph metrics do not necessarily scale uniformly with change in scale
409 : // and, as a result, CTM changes may require text to break at different
410 : // points. The problem would be how to keep performance acceptable when
411 : // e.g. the transform of an ancestor is animated.
412 : // We also seem to get some sort of infinite loop post bug 421584 if we
413 : // reflow.
414 0 : mCanvasTM = nsnull;
415 0 : if (!(aFlags & DO_NOT_NOTIFY_RENDERING_OBSERVERS)) {
416 0 : UpdateGraphic();
417 : }
418 :
419 0 : } else if (aFlags & COORD_CONTEXT_CHANGED) {
420 : nsSVGForeignObjectElement *fO =
421 0 : static_cast<nsSVGForeignObjectElement*>(mContent);
422 : // Coordinate context changes affect mCanvasTM if we have a
423 : // percentage 'x' or 'y'
424 0 : if (fO->mLengthAttributes[nsSVGForeignObjectElement::X].IsPercentage() ||
425 0 : fO->mLengthAttributes[nsSVGForeignObjectElement::Y].IsPercentage()) {
426 0 : mCanvasTM = nsnull;
427 : }
428 : // Our coordinate context's width/height has changed. If we have a
429 : // percentage width/height our dimensions will change so we must reflow.
430 0 : if (fO->mLengthAttributes[nsSVGForeignObjectElement::WIDTH].IsPercentage() ||
431 0 : fO->mLengthAttributes[nsSVGForeignObjectElement::HEIGHT].IsPercentage()) {
432 0 : reflow = true;
433 : }
434 : }
435 :
436 0 : if (reflow) {
437 : // If we're called while the PresShell is handling reflow events then we
438 : // must have been called as a result of the NotifyViewportChange() call in
439 : // our nsSVGOuterSVGFrame's Reflow() method. We must not call RequestReflow
440 : // at this point (i.e. during reflow) because it could confuse the
441 : // PresShell and prevent it from reflowing us properly in future. Besides
442 : // that, nsSVGOuterSVGFrame::DidReflow will take care of reflowing us
443 : // synchronously, so there's no need.
444 0 : if (!PresContext()->PresShell()->IsReflowLocked()) {
445 0 : UpdateGraphic(); // update mRect before requesting reflow
446 0 : RequestReflow(nsIPresShell::eResize);
447 : }
448 : }
449 0 : }
450 :
451 : void
452 0 : nsSVGForeignObjectFrame::NotifyRedrawSuspended()
453 : {
454 0 : AddStateBits(NS_STATE_SVG_REDRAW_SUSPENDED);
455 0 : }
456 :
457 : void
458 0 : nsSVGForeignObjectFrame::NotifyRedrawUnsuspended()
459 : {
460 0 : RemoveStateBits(NS_STATE_SVG_REDRAW_SUSPENDED);
461 :
462 0 : if (!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) {
463 0 : if (GetStateBits() & NS_STATE_SVG_DIRTY) {
464 0 : UpdateGraphic(); // invalidate our entire area
465 : } else {
466 0 : FlushDirtyRegion(0); // only invalidate areas dirtied by our descendants
467 : }
468 : }
469 0 : }
470 :
471 : gfxRect
472 0 : nsSVGForeignObjectFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
473 : PRUint32 aFlags)
474 : {
475 : nsSVGForeignObjectElement *content =
476 0 : static_cast<nsSVGForeignObjectElement*>(mContent);
477 :
478 : float x, y, w, h;
479 0 : content->GetAnimatedLengthValues(&x, &y, &w, &h, nsnull);
480 :
481 0 : if (w < 0.0f) w = 0.0f;
482 0 : if (h < 0.0f) h = 0.0f;
483 :
484 0 : if (aToBBoxUserspace.IsSingular()) {
485 : // XXX ReportToConsole
486 0 : return gfxRect(0.0, 0.0, 0.0, 0.0);
487 : }
488 0 : return aToBBoxUserspace.TransformBounds(gfxRect(0.0, 0.0, w, h));
489 : }
490 :
491 : //----------------------------------------------------------------------
492 :
493 : gfxMatrix
494 0 : nsSVGForeignObjectFrame::GetCanvasTM()
495 : {
496 0 : if (!mCanvasTM) {
497 0 : NS_ASSERTION(mParent, "null parent");
498 :
499 0 : nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent);
500 : nsSVGForeignObjectElement *content =
501 0 : static_cast<nsSVGForeignObjectElement*>(mContent);
502 :
503 0 : gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM());
504 :
505 0 : mCanvasTM = new gfxMatrix(tm);
506 : }
507 0 : return *mCanvasTM;
508 : }
509 :
510 : //----------------------------------------------------------------------
511 : // Implementation helpers
512 :
513 : gfxMatrix
514 0 : nsSVGForeignObjectFrame::GetCanvasTMForChildren()
515 : {
516 0 : float cssPxPerDevPx = PresContext()->
517 0 : AppUnitsToFloatCSSPixels(PresContext()->AppUnitsPerDevPixel());
518 :
519 0 : return GetCanvasTM().Scale(cssPxPerDevPx, cssPxPerDevPx);
520 : }
521 :
522 0 : void nsSVGForeignObjectFrame::RequestReflow(nsIPresShell::IntrinsicDirty aType)
523 : {
524 0 : if (GetStateBits() & NS_FRAME_FIRST_REFLOW)
525 : // If we haven't had an InitialUpdate yet, nothing to do.
526 0 : return;
527 :
528 0 : nsIFrame* kid = GetFirstPrincipalChild();
529 0 : if (!kid)
530 0 : return;
531 :
532 0 : PresContext()->PresShell()->FrameNeedsReflow(kid, aType, NS_FRAME_IS_DIRTY);
533 : }
534 :
535 0 : void nsSVGForeignObjectFrame::UpdateGraphic()
536 : {
537 0 : nsSVGUtils::UpdateGraphic(this);
538 :
539 : // We just invalidated our entire area, so clear the caches of areas dirtied
540 : // by our descendants:
541 0 : mSameDocDirtyRegion.SetEmpty();
542 0 : mSubDocDirtyRegion.SetEmpty();
543 0 : }
544 :
545 : void
546 0 : nsSVGForeignObjectFrame::MaybeReflowFromOuterSVGFrame()
547 : {
548 : // If IsDisabled() is true, then we know that our DoReflow() call will return
549 : // early, leaving us with a marked-dirty but not-reflowed kid. That'd be bad;
550 : // it'd mean that all future calls to this method would be doomed to take the
551 : // NS_FRAME_IS_DIRTY early-return below. To avoid that problem, we need to
552 : // bail out *before* we mark our kid as dirty.
553 0 : if (IsDisabled()) {
554 0 : return;
555 : }
556 :
557 0 : nsIFrame* kid = GetFirstPrincipalChild();
558 :
559 : // If we're already scheduled to reflow (if we or our kid is dirty) we don't
560 : // want to reflow now or else our presShell will do extra work trying to
561 : // reflow us a second time. (It will also complain if it finds that a reflow
562 : // root scheduled for reflow isn't dirty).
563 :
564 0 : if (kid->GetStateBits() & NS_FRAME_IS_DIRTY) {
565 0 : return;
566 : }
567 0 : kid->AddStateBits(NS_FRAME_IS_DIRTY); // we must be fully marked dirty
568 0 : if (kid->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN) {
569 0 : return;
570 : }
571 :
572 : // Make sure to not allow interrupts if we're not being reflown as a root
573 0 : nsPresContext::InterruptPreventer noInterrupts(PresContext());
574 0 : DoReflow();
575 : }
576 :
577 : void
578 0 : nsSVGForeignObjectFrame::DoReflow()
579 : {
580 0 : NS_ASSERTION(!(nsSVGUtils::GetOuterSVGFrame(this)->
581 : GetStateBits() & NS_FRAME_FIRST_REFLOW),
582 : "Calling InitialUpdate too early - must not call DoReflow!!!");
583 :
584 : // Skip reflow if we're zero-sized, unless this is our first reflow.
585 0 : if (IsDisabled() &&
586 0 : !(GetStateBits() & NS_FRAME_FIRST_REFLOW))
587 0 : return;
588 :
589 0 : if (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)
590 0 : return;
591 :
592 0 : nsPresContext *presContext = PresContext();
593 0 : nsIFrame* kid = GetFirstPrincipalChild();
594 0 : if (!kid)
595 0 : return;
596 :
597 : // initiate a synchronous reflow here and now:
598 0 : nsIPresShell* presShell = presContext->PresShell();
599 0 : NS_ASSERTION(presShell, "null presShell");
600 : nsRefPtr<nsRenderingContext> renderingContext =
601 0 : presShell->GetReferenceRenderingContext();
602 0 : if (!renderingContext)
603 : return;
604 :
605 : nsSVGForeignObjectElement *fO = static_cast<nsSVGForeignObjectElement*>
606 0 : (mContent);
607 :
608 : float width =
609 0 : fO->mLengthAttributes[nsSVGForeignObjectElement::WIDTH].GetAnimValue(fO);
610 : float height =
611 0 : fO->mLengthAttributes[nsSVGForeignObjectElement::HEIGHT].GetAnimValue(fO);
612 :
613 : // Clamp height & width to be non-negative (to match UpdateCoveredRegion).
614 0 : width = NS_MAX(width, 0.0f);
615 0 : height = NS_MAX(height, 0.0f);
616 :
617 : nsSize size(nsPresContext::CSSPixelsToAppUnits(width),
618 0 : nsPresContext::CSSPixelsToAppUnits(height));
619 :
620 0 : mInReflow = true;
621 :
622 : nsHTMLReflowState reflowState(presContext, kid,
623 : renderingContext,
624 0 : nsSize(size.width, NS_UNCONSTRAINEDSIZE));
625 0 : nsHTMLReflowMetrics desiredSize;
626 : nsReflowStatus status;
627 :
628 : // We don't use size.height above because that tells the child to do
629 : // page/column breaking at that height.
630 0 : NS_ASSERTION(reflowState.mComputedBorderPadding == nsMargin(0, 0, 0, 0) &&
631 : reflowState.mComputedMargin == nsMargin(0, 0, 0, 0),
632 : "style system should ensure that :-moz-svg-foreign-content "
633 : "does not get styled");
634 0 : NS_ASSERTION(reflowState.ComputedWidth() == size.width,
635 : "reflow state made child wrong size");
636 0 : reflowState.SetComputedHeight(size.height);
637 :
638 : ReflowChild(kid, presContext, desiredSize, reflowState, 0, 0,
639 0 : NS_FRAME_NO_MOVE_FRAME, status);
640 0 : NS_ASSERTION(size.width == desiredSize.width &&
641 : size.height == desiredSize.height, "unexpected size");
642 : FinishReflowChild(kid, presContext, &reflowState, desiredSize, 0, 0,
643 0 : NS_FRAME_NO_MOVE_FRAME);
644 :
645 0 : mInReflow = false;
646 0 : FlushDirtyRegion(0);
647 : }
648 :
649 : void
650 0 : nsSVGForeignObjectFrame::InvalidateDirtyRect(nsSVGOuterSVGFrame* aOuter,
651 : const nsRect& aRect, PRUint32 aFlags)
652 : {
653 0 : if (aRect.IsEmpty())
654 0 : return;
655 :
656 : // Don't invalidate areas outside our bounds:
657 0 : nsRect rect = aRect.Intersect(mRect);
658 0 : if (rect.IsEmpty())
659 : return;
660 :
661 : // The areas dirtied by children are in app units, relative to this frame.
662 : // We need to convert the rect from app units in our userspace to app units
663 : // relative to our nsSVGOuterSVGFrame's content rect.
664 :
665 0 : gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
666 0 : r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel());
667 0 : rect = ToCanvasBounds(r, GetCanvasTM(), PresContext());
668 0 : rect = nsSVGUtils::FindFilterInvalidation(this, rect);
669 0 : aOuter->InvalidateWithFlags(rect, aFlags);
670 : }
671 :
672 : void
673 0 : nsSVGForeignObjectFrame::FlushDirtyRegion(PRUint32 aFlags)
674 : {
675 0 : if ((mSameDocDirtyRegion.IsEmpty() && mSubDocDirtyRegion.IsEmpty()) ||
676 : mInReflow ||
677 0 : (GetStateBits() & NS_STATE_SVG_REDRAW_SUSPENDED))
678 0 : return;
679 :
680 0 : nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(this);
681 0 : if (!outerSVGFrame) {
682 0 : NS_ERROR("null outerSVGFrame");
683 0 : return;
684 : }
685 :
686 0 : InvalidateDirtyRect(outerSVGFrame, mSameDocDirtyRegion.GetBounds(), aFlags);
687 0 : InvalidateDirtyRect(outerSVGFrame, mSubDocDirtyRegion.GetBounds(),
688 0 : aFlags | INVALIDATE_CROSS_DOC);
689 :
690 0 : mSameDocDirtyRegion.SetEmpty();
691 0 : mSubDocDirtyRegion.SetEmpty();
692 : }
|