1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/svg/nsSVGForeignObjectFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,594 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +// Main header first: 1.10 +#include "nsSVGForeignObjectFrame.h" 1.11 + 1.12 +// Keep others in (case-insensitive) order: 1.13 +#include "gfxContext.h" 1.14 +#include "nsGkAtoms.h" 1.15 +#include "nsNameSpaceManager.h" 1.16 +#include "nsLayoutUtils.h" 1.17 +#include "nsRegion.h" 1.18 +#include "nsRenderingContext.h" 1.19 +#include "nsSVGContainerFrame.h" 1.20 +#include "nsSVGEffects.h" 1.21 +#include "mozilla/dom/SVGForeignObjectElement.h" 1.22 +#include "nsSVGIntegrationUtils.h" 1.23 +#include "nsSVGOuterSVGFrame.h" 1.24 +#include "nsSVGUtils.h" 1.25 +#include "mozilla/AutoRestore.h" 1.26 + 1.27 +using namespace mozilla; 1.28 +using namespace mozilla::dom; 1.29 + 1.30 +//---------------------------------------------------------------------- 1.31 +// Implementation 1.32 + 1.33 +nsIFrame* 1.34 +NS_NewSVGForeignObjectFrame(nsIPresShell *aPresShell, 1.35 + nsStyleContext *aContext) 1.36 +{ 1.37 + return new (aPresShell) nsSVGForeignObjectFrame(aContext); 1.38 +} 1.39 + 1.40 +NS_IMPL_FRAMEARENA_HELPERS(nsSVGForeignObjectFrame) 1.41 + 1.42 +nsSVGForeignObjectFrame::nsSVGForeignObjectFrame(nsStyleContext* aContext) 1.43 + : nsSVGForeignObjectFrameBase(aContext), 1.44 + mInReflow(false) 1.45 +{ 1.46 + AddStateBits(NS_FRAME_REFLOW_ROOT | NS_FRAME_MAY_BE_TRANSFORMED | 1.47 + NS_FRAME_SVG_LAYOUT); 1.48 +} 1.49 + 1.50 +//---------------------------------------------------------------------- 1.51 +// nsIFrame methods 1.52 + 1.53 +NS_QUERYFRAME_HEAD(nsSVGForeignObjectFrame) 1.54 + NS_QUERYFRAME_ENTRY(nsISVGChildFrame) 1.55 +NS_QUERYFRAME_TAIL_INHERITING(nsSVGForeignObjectFrameBase) 1.56 + 1.57 +void 1.58 +nsSVGForeignObjectFrame::Init(nsIContent* aContent, 1.59 + nsIFrame* aParent, 1.60 + nsIFrame* aPrevInFlow) 1.61 +{ 1.62 + NS_ASSERTION(aContent->IsSVG(nsGkAtoms::foreignObject), 1.63 + "Content is not an SVG foreignObject!"); 1.64 + 1.65 + nsSVGForeignObjectFrameBase::Init(aContent, aParent, aPrevInFlow); 1.66 + AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD); 1.67 + AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER | 1.68 + NS_FRAME_FONT_INFLATION_FLOW_ROOT); 1.69 + if (!(mState & NS_FRAME_IS_NONDISPLAY)) { 1.70 + nsSVGUtils::GetOuterSVGFrame(this)->RegisterForeignObject(this); 1.71 + } 1.72 +} 1.73 + 1.74 +void nsSVGForeignObjectFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.75 +{ 1.76 + // Only unregister if we registered in the first place: 1.77 + if (!(mState & NS_FRAME_IS_NONDISPLAY)) { 1.78 + nsSVGUtils::GetOuterSVGFrame(this)->UnregisterForeignObject(this); 1.79 + } 1.80 + nsSVGForeignObjectFrameBase::DestroyFrom(aDestructRoot); 1.81 +} 1.82 + 1.83 +nsIAtom * 1.84 +nsSVGForeignObjectFrame::GetType() const 1.85 +{ 1.86 + return nsGkAtoms::svgForeignObjectFrame; 1.87 +} 1.88 + 1.89 +nsresult 1.90 +nsSVGForeignObjectFrame::AttributeChanged(int32_t aNameSpaceID, 1.91 + nsIAtom *aAttribute, 1.92 + int32_t aModType) 1.93 +{ 1.94 + if (aNameSpaceID == kNameSpaceID_None) { 1.95 + if (aAttribute == nsGkAtoms::width || 1.96 + aAttribute == nsGkAtoms::height) { 1.97 + nsSVGEffects::InvalidateRenderingObservers(this); 1.98 + nsSVGUtils::ScheduleReflowSVG(this); 1.99 + // XXXjwatt: why mark intrinsic widths dirty? can't we just use eResize? 1.100 + RequestReflow(nsIPresShell::eStyleChange); 1.101 + } else if (aAttribute == nsGkAtoms::x || 1.102 + aAttribute == nsGkAtoms::y) { 1.103 + // make sure our cached transform matrix gets (lazily) updated 1.104 + mCanvasTM = nullptr; 1.105 + nsSVGEffects::InvalidateRenderingObservers(this); 1.106 + nsSVGUtils::ScheduleReflowSVG(this); 1.107 + } else if (aAttribute == nsGkAtoms::transform) { 1.108 + // We don't invalidate for transform changes (the layers code does that). 1.109 + // Also note that SVGTransformableElement::GetAttributeChangeHint will 1.110 + // return nsChangeHint_UpdateOverflow for "transform" attribute changes 1.111 + // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call. 1.112 + mCanvasTM = nullptr; 1.113 + } else if (aAttribute == nsGkAtoms::viewBox || 1.114 + aAttribute == nsGkAtoms::preserveAspectRatio) { 1.115 + nsSVGEffects::InvalidateRenderingObservers(this); 1.116 + } 1.117 + } 1.118 + 1.119 + return NS_OK; 1.120 +} 1.121 + 1.122 +nsresult 1.123 +nsSVGForeignObjectFrame::Reflow(nsPresContext* aPresContext, 1.124 + nsHTMLReflowMetrics& aDesiredSize, 1.125 + const nsHTMLReflowState& aReflowState, 1.126 + nsReflowStatus& aStatus) 1.127 +{ 1.128 + NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY), 1.129 + "Should not have been called"); 1.130 + 1.131 + // Only InvalidateAndScheduleBoundsUpdate marks us with NS_FRAME_IS_DIRTY, 1.132 + // so if that bit is still set we still have a resize pending. If we hit 1.133 + // this assertion, then we should get the presShell to skip reflow roots 1.134 + // that have a dirty parent since a reflow is going to come via the 1.135 + // reflow root's parent anyway. 1.136 + NS_ASSERTION(!(GetStateBits() & NS_FRAME_IS_DIRTY), 1.137 + "Reflowing while a resize is pending is wasteful"); 1.138 + 1.139 + // ReflowSVG makes sure mRect is up to date before we're called. 1.140 + 1.141 + NS_ASSERTION(!aReflowState.parentReflowState, 1.142 + "should only get reflow from being reflow root"); 1.143 + NS_ASSERTION(aReflowState.ComputedWidth() == GetSize().width && 1.144 + aReflowState.ComputedHeight() == GetSize().height, 1.145 + "reflow roots should be reflowed at existing size and " 1.146 + "svg.css should ensure we have no padding/border/margin"); 1.147 + 1.148 + DoReflow(); 1.149 + 1.150 + aDesiredSize.Width() = aReflowState.ComputedWidth(); 1.151 + aDesiredSize.Height() = aReflowState.ComputedHeight(); 1.152 + aDesiredSize.SetOverflowAreasToDesiredBounds(); 1.153 + aStatus = NS_FRAME_COMPLETE; 1.154 + 1.155 + return NS_OK; 1.156 +} 1.157 + 1.158 +void 1.159 +nsSVGForeignObjectFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.160 + const nsRect& aDirtyRect, 1.161 + const nsDisplayListSet& aLists) 1.162 +{ 1.163 + if (!static_cast<const nsSVGElement*>(mContent)->HasValidDimensions()) { 1.164 + return; 1.165 + } 1.166 + BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists); 1.167 +} 1.168 + 1.169 +bool 1.170 +nsSVGForeignObjectFrame::IsSVGTransformed(Matrix *aOwnTransform, 1.171 + Matrix *aFromParentTransform) const 1.172 +{ 1.173 + bool foundTransform = false; 1.174 + 1.175 + // Check if our parent has children-only transforms: 1.176 + nsIFrame *parent = GetParent(); 1.177 + if (parent && 1.178 + parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) { 1.179 + foundTransform = static_cast<nsSVGContainerFrame*>(parent)-> 1.180 + HasChildrenOnlyTransform(aFromParentTransform); 1.181 + } 1.182 + 1.183 + nsSVGElement *content = static_cast<nsSVGElement*>(mContent); 1.184 + nsSVGAnimatedTransformList* transformList = 1.185 + content->GetAnimatedTransformList(); 1.186 + if ((transformList && transformList->HasTransform()) || 1.187 + content->GetAnimateMotionTransform()) { 1.188 + if (aOwnTransform) { 1.189 + *aOwnTransform = gfx::ToMatrix(content->PrependLocalTransformsTo(gfxMatrix(), 1.190 + nsSVGElement::eUserSpaceToParent)); 1.191 + } 1.192 + foundTransform = true; 1.193 + } 1.194 + return foundTransform; 1.195 +} 1.196 + 1.197 +nsresult 1.198 +nsSVGForeignObjectFrame::PaintSVG(nsRenderingContext *aContext, 1.199 + const nsIntRect *aDirtyRect, 1.200 + nsIFrame* aTransformRoot) 1.201 +{ 1.202 + NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || 1.203 + (mState & NS_FRAME_IS_NONDISPLAY), 1.204 + "If display lists are enabled, only painting of non-display " 1.205 + "SVG should take this code path"); 1.206 + 1.207 + if (IsDisabled()) 1.208 + return NS_OK; 1.209 + 1.210 + nsIFrame* kid = GetFirstPrincipalChild(); 1.211 + if (!kid) 1.212 + return NS_OK; 1.213 + 1.214 + gfxMatrix canvasTM = GetCanvasTM(FOR_PAINTING, aTransformRoot); 1.215 + 1.216 + if (canvasTM.IsSingular()) { 1.217 + NS_WARNING("Can't render foreignObject element!"); 1.218 + return NS_ERROR_FAILURE; 1.219 + } 1.220 + 1.221 + nsRect kidDirtyRect = kid->GetVisualOverflowRect(); 1.222 + 1.223 + /* Check if we need to draw anything. */ 1.224 + if (aDirtyRect) { 1.225 + NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || 1.226 + (mState & NS_FRAME_IS_NONDISPLAY), 1.227 + "Display lists handle dirty rect intersection test"); 1.228 + // Transform the dirty rect into app units in our userspace. 1.229 + gfxMatrix invmatrix = canvasTM; 1.230 + invmatrix.Invert(); 1.231 + NS_ASSERTION(!invmatrix.IsSingular(), 1.232 + "inverse of non-singular matrix should be non-singular"); 1.233 + 1.234 + gfxRect transDirtyRect = gfxRect(aDirtyRect->x, aDirtyRect->y, 1.235 + aDirtyRect->width, aDirtyRect->height); 1.236 + transDirtyRect = invmatrix.TransformBounds(transDirtyRect); 1.237 + 1.238 + kidDirtyRect.IntersectRect(kidDirtyRect, 1.239 + nsLayoutUtils::RoundGfxRectToAppRect(transDirtyRect, 1.240 + PresContext()->AppUnitsPerCSSPixel())); 1.241 + 1.242 + // XXX after bug 614732 is fixed, we will compare mRect with aDirtyRect, 1.243 + // not with kidDirtyRect. I.e. 1.244 + // int32_t appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel(); 1.245 + // mRect.ToOutsidePixels(appUnitsPerDevPx).Intersects(*aDirtyRect) 1.246 + if (kidDirtyRect.IsEmpty()) 1.247 + return NS_OK; 1.248 + } 1.249 + 1.250 + gfxContext *gfx = aContext->ThebesContext(); 1.251 + 1.252 + gfx->Save(); 1.253 + 1.254 + if (StyleDisplay()->IsScrollableOverflow()) { 1.255 + float x, y, width, height; 1.256 + static_cast<nsSVGElement*>(mContent)-> 1.257 + GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); 1.258 + 1.259 + gfxRect clipRect = 1.260 + nsSVGUtils::GetClipRectForFrame(this, 0.0f, 0.0f, width, height); 1.261 + nsSVGUtils::SetClipRect(gfx, canvasTM, clipRect); 1.262 + } 1.263 + 1.264 + // SVG paints in CSS px, but normally frames paint in dev pixels. Here we 1.265 + // multiply a CSS-px-to-dev-pixel factor onto canvasTM so our children paint 1.266 + // correctly. 1.267 + float cssPxPerDevPx = PresContext()-> 1.268 + AppUnitsToFloatCSSPixels(PresContext()->AppUnitsPerDevPixel()); 1.269 + gfxMatrix canvasTMForChildren = canvasTM; 1.270 + canvasTMForChildren.Scale(cssPxPerDevPx, cssPxPerDevPx); 1.271 + 1.272 + gfx->Multiply(canvasTMForChildren); 1.273 + 1.274 + uint32_t flags = nsLayoutUtils::PAINT_IN_TRANSFORM; 1.275 + if (SVGAutoRenderState::IsPaintingToWindow(aContext)) { 1.276 + flags |= nsLayoutUtils::PAINT_TO_WINDOW; 1.277 + } 1.278 + nsresult rv = nsLayoutUtils::PaintFrame(aContext, kid, nsRegion(kidDirtyRect), 1.279 + NS_RGBA(0,0,0,0), flags); 1.280 + 1.281 + gfx->Restore(); 1.282 + 1.283 + return rv; 1.284 +} 1.285 + 1.286 +nsIFrame* 1.287 +nsSVGForeignObjectFrame::GetFrameForPoint(const nsPoint &aPoint) 1.288 +{ 1.289 + NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() || 1.290 + (mState & NS_FRAME_IS_NONDISPLAY), 1.291 + "If display lists are enabled, only hit-testing of a " 1.292 + "clipPath's contents should take this code path"); 1.293 + 1.294 + if (IsDisabled() || (GetStateBits() & NS_FRAME_IS_NONDISPLAY)) 1.295 + return nullptr; 1.296 + 1.297 + nsIFrame* kid = GetFirstPrincipalChild(); 1.298 + if (!kid) 1.299 + return nullptr; 1.300 + 1.301 + float x, y, width, height; 1.302 + static_cast<nsSVGElement*>(mContent)-> 1.303 + GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); 1.304 + 1.305 + gfxMatrix tm = GetCanvasTM(FOR_HIT_TESTING).Invert(); 1.306 + if (tm.IsSingular()) 1.307 + return nullptr; 1.308 + 1.309 + // Convert aPoint from app units in canvas space to user space: 1.310 + 1.311 + gfxPoint pt = gfxPoint(aPoint.x, aPoint.y) / PresContext()->AppUnitsPerDevPixel(); 1.312 + pt = tm.Transform(pt); 1.313 + 1.314 + if (!gfxRect(0.0f, 0.0f, width, height).Contains(pt)) 1.315 + return nullptr; 1.316 + 1.317 + // Convert pt to app units in *local* space: 1.318 + 1.319 + pt = pt * nsPresContext::AppUnitsPerCSSPixel(); 1.320 + nsPoint point = nsPoint(NSToIntRound(pt.x), NSToIntRound(pt.y)); 1.321 + 1.322 + nsIFrame *frame = nsLayoutUtils::GetFrameForPoint(kid, point); 1.323 + if (frame && nsSVGUtils::HitTestClip(this, aPoint)) 1.324 + return frame; 1.325 + 1.326 + return nullptr; 1.327 +} 1.328 + 1.329 +nsRect 1.330 +nsSVGForeignObjectFrame::GetCoveredRegion() 1.331 +{ 1.332 + float x, y, w, h; 1.333 + static_cast<SVGForeignObjectElement*>(mContent)-> 1.334 + GetAnimatedLengthValues(&x, &y, &w, &h, nullptr); 1.335 + if (w < 0.0f) w = 0.0f; 1.336 + if (h < 0.0f) h = 0.0f; 1.337 + // GetCanvasTM includes the x,y translation 1.338 + return nsSVGUtils::ToCanvasBounds(gfxRect(0.0, 0.0, w, h), 1.339 + GetCanvasTM(FOR_OUTERSVG_TM), 1.340 + PresContext()); 1.341 +} 1.342 + 1.343 +void 1.344 +nsSVGForeignObjectFrame::ReflowSVG() 1.345 +{ 1.346 + NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this), 1.347 + "This call is probably a wasteful mistake"); 1.348 + 1.349 + NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY), 1.350 + "ReflowSVG mechanism not designed for this"); 1.351 + 1.352 + if (!nsSVGUtils::NeedsReflowSVG(this)) { 1.353 + return; 1.354 + } 1.355 + 1.356 + // We update mRect before the DoReflow call so that DoReflow uses the 1.357 + // correct dimensions: 1.358 + 1.359 + float x, y, w, h; 1.360 + static_cast<SVGForeignObjectElement*>(mContent)-> 1.361 + GetAnimatedLengthValues(&x, &y, &w, &h, nullptr); 1.362 + 1.363 + // If mRect's width or height are negative, reflow blows up! We must clamp! 1.364 + if (w < 0.0f) w = 0.0f; 1.365 + if (h < 0.0f) h = 0.0f; 1.366 + 1.367 + mRect = nsLayoutUtils::RoundGfxRectToAppRect( 1.368 + gfxRect(x, y, w, h), 1.369 + PresContext()->AppUnitsPerCSSPixel()); 1.370 + 1.371 + // Fully mark our kid dirty so that it gets resized if necessary 1.372 + // (NS_FRAME_HAS_DIRTY_CHILDREN isn't enough in that case): 1.373 + nsIFrame* kid = GetFirstPrincipalChild(); 1.374 + kid->AddStateBits(NS_FRAME_IS_DIRTY); 1.375 + 1.376 + // Make sure to not allow interrupts if we're not being reflown as a root: 1.377 + nsPresContext::InterruptPreventer noInterrupts(PresContext()); 1.378 + 1.379 + DoReflow(); 1.380 + 1.381 + if (mState & NS_FRAME_FIRST_REFLOW) { 1.382 + // Make sure we have our filter property (if any) before calling 1.383 + // FinishAndStoreOverflow (subsequent filter changes are handled off 1.384 + // nsChangeHint_UpdateEffects): 1.385 + nsSVGEffects::UpdateEffects(this); 1.386 + } 1.387 + 1.388 + // If we have a filter, we need to invalidate ourselves because filter 1.389 + // output can change even if none of our descendants need repainting. 1.390 + if (StyleSVGReset()->HasFilters()) { 1.391 + InvalidateFrame(); 1.392 + } 1.393 + 1.394 + // TODO: once we support |overflow:visible| on foreignObject, then we will 1.395 + // need to take account of our descendants here. 1.396 + nsRect overflow = nsRect(nsPoint(0,0), mRect.Size()); 1.397 + nsOverflowAreas overflowAreas(overflow, overflow); 1.398 + FinishAndStoreOverflow(overflowAreas, mRect.Size()); 1.399 + 1.400 + // Now unset the various reflow bits: 1.401 + mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY | 1.402 + NS_FRAME_HAS_DIRTY_CHILDREN); 1.403 +} 1.404 + 1.405 +void 1.406 +nsSVGForeignObjectFrame::NotifySVGChanged(uint32_t aFlags) 1.407 +{ 1.408 + NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED), 1.409 + "Invalidation logic may need adjusting"); 1.410 + 1.411 + bool needNewBounds = false; // i.e. mRect or visual overflow rect 1.412 + bool needReflow = false; 1.413 + bool needNewCanvasTM = false; 1.414 + 1.415 + if (aFlags & COORD_CONTEXT_CHANGED) { 1.416 + SVGForeignObjectElement *fO = 1.417 + static_cast<SVGForeignObjectElement*>(mContent); 1.418 + // Coordinate context changes affect mCanvasTM if we have a 1.419 + // percentage 'x' or 'y' 1.420 + if (fO->mLengthAttributes[SVGForeignObjectElement::ATTR_X].IsPercentage() || 1.421 + fO->mLengthAttributes[SVGForeignObjectElement::ATTR_Y].IsPercentage()) { 1.422 + needNewBounds = true; 1.423 + needNewCanvasTM = true; 1.424 + } 1.425 + // Our coordinate context's width/height has changed. If we have a 1.426 + // percentage width/height our dimensions will change so we must reflow. 1.427 + if (fO->mLengthAttributes[SVGForeignObjectElement::ATTR_WIDTH].IsPercentage() || 1.428 + fO->mLengthAttributes[SVGForeignObjectElement::ATTR_HEIGHT].IsPercentage()) { 1.429 + needNewBounds = true; 1.430 + needReflow = true; 1.431 + } 1.432 + } 1.433 + 1.434 + if (aFlags & TRANSFORM_CHANGED) { 1.435 + if (mCanvasTM && mCanvasTM->IsSingular()) { 1.436 + needNewBounds = true; // old bounds are bogus 1.437 + } 1.438 + needNewCanvasTM = true; 1.439 + // In an ideal world we would reflow when our CTM changes. This is because 1.440 + // glyph metrics do not necessarily scale uniformly with change in scale 1.441 + // and, as a result, CTM changes may require text to break at different 1.442 + // points. The problem would be how to keep performance acceptable when 1.443 + // e.g. the transform of an ancestor is animated. 1.444 + // We also seem to get some sort of infinite loop post bug 421584 if we 1.445 + // reflow. 1.446 + } 1.447 + 1.448 + if (needNewBounds) { 1.449 + // Ancestor changes can't affect how we render from the perspective of 1.450 + // any rendering observers that we may have, so we don't need to 1.451 + // invalidate them. We also don't need to invalidate ourself, since our 1.452 + // changed ancestor will have invalidated its entire area, which includes 1.453 + // our area. 1.454 + nsSVGUtils::ScheduleReflowSVG(this); 1.455 + } 1.456 + 1.457 + // If we're called while the PresShell is handling reflow events then we 1.458 + // must have been called as a result of the NotifyViewportChange() call in 1.459 + // our nsSVGOuterSVGFrame's Reflow() method. We must not call RequestReflow 1.460 + // at this point (i.e. during reflow) because it could confuse the 1.461 + // PresShell and prevent it from reflowing us properly in future. Besides 1.462 + // that, nsSVGOuterSVGFrame::DidReflow will take care of reflowing us 1.463 + // synchronously, so there's no need. 1.464 + if (needReflow && !PresContext()->PresShell()->IsReflowLocked()) { 1.465 + RequestReflow(nsIPresShell::eResize); 1.466 + } 1.467 + 1.468 + if (needNewCanvasTM) { 1.469 + // Do this after calling InvalidateAndScheduleBoundsUpdate in case we 1.470 + // change the code and it needs to use it. 1.471 + mCanvasTM = nullptr; 1.472 + } 1.473 +} 1.474 + 1.475 +SVGBBox 1.476 +nsSVGForeignObjectFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace, 1.477 + uint32_t aFlags) 1.478 +{ 1.479 + SVGForeignObjectElement *content = 1.480 + static_cast<SVGForeignObjectElement*>(mContent); 1.481 + 1.482 + float x, y, w, h; 1.483 + content->GetAnimatedLengthValues(&x, &y, &w, &h, nullptr); 1.484 + 1.485 + if (w < 0.0f) w = 0.0f; 1.486 + if (h < 0.0f) h = 0.0f; 1.487 + 1.488 + if (aToBBoxUserspace.IsSingular()) { 1.489 + // XXX ReportToConsole 1.490 + return SVGBBox(); 1.491 + } 1.492 + return aToBBoxUserspace.TransformBounds(gfx::Rect(0.0, 0.0, w, h)); 1.493 +} 1.494 + 1.495 +//---------------------------------------------------------------------- 1.496 + 1.497 +gfxMatrix 1.498 +nsSVGForeignObjectFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot) 1.499 +{ 1.500 + if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY) && !aTransformRoot) { 1.501 + if ((aFor == FOR_PAINTING && NS_SVGDisplayListPaintingEnabled()) || 1.502 + (aFor == FOR_HIT_TESTING && NS_SVGDisplayListHitTestingEnabled())) { 1.503 + return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this); 1.504 + } 1.505 + } 1.506 + if (!mCanvasTM) { 1.507 + NS_ASSERTION(mParent, "null parent"); 1.508 + 1.509 + nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent); 1.510 + SVGForeignObjectElement *content = 1.511 + static_cast<SVGForeignObjectElement*>(mContent); 1.512 + 1.513 + gfxMatrix tm = content->PrependLocalTransformsTo( 1.514 + this == aTransformRoot ? gfxMatrix() : 1.515 + parent->GetCanvasTM(aFor, aTransformRoot)); 1.516 + 1.517 + mCanvasTM = new gfxMatrix(tm); 1.518 + } 1.519 + return *mCanvasTM; 1.520 +} 1.521 + 1.522 +//---------------------------------------------------------------------- 1.523 +// Implementation helpers 1.524 + 1.525 +void nsSVGForeignObjectFrame::RequestReflow(nsIPresShell::IntrinsicDirty aType) 1.526 +{ 1.527 + if (GetStateBits() & NS_FRAME_FIRST_REFLOW) 1.528 + // If we haven't had a ReflowSVG() yet, nothing to do. 1.529 + return; 1.530 + 1.531 + nsIFrame* kid = GetFirstPrincipalChild(); 1.532 + if (!kid) 1.533 + return; 1.534 + 1.535 + PresContext()->PresShell()->FrameNeedsReflow(kid, aType, NS_FRAME_IS_DIRTY); 1.536 +} 1.537 + 1.538 +void 1.539 +nsSVGForeignObjectFrame::DoReflow() 1.540 +{ 1.541 + // Skip reflow if we're zero-sized, unless this is our first reflow. 1.542 + if (IsDisabled() && 1.543 + !(GetStateBits() & NS_FRAME_FIRST_REFLOW)) 1.544 + return; 1.545 + 1.546 + nsPresContext *presContext = PresContext(); 1.547 + nsIFrame* kid = GetFirstPrincipalChild(); 1.548 + if (!kid) 1.549 + return; 1.550 + 1.551 + // initiate a synchronous reflow here and now: 1.552 + nsRefPtr<nsRenderingContext> renderingContext = 1.553 + presContext->PresShell()->CreateReferenceRenderingContext(); 1.554 + 1.555 + mInReflow = true; 1.556 + 1.557 + nsHTMLReflowState reflowState(presContext, kid, 1.558 + renderingContext, 1.559 + nsSize(mRect.width, NS_UNCONSTRAINEDSIZE)); 1.560 + nsHTMLReflowMetrics desiredSize(reflowState); 1.561 + nsReflowStatus status; 1.562 + 1.563 + // We don't use mRect.height above because that tells the child to do 1.564 + // page/column breaking at that height. 1.565 + NS_ASSERTION(reflowState.ComputedPhysicalBorderPadding() == nsMargin(0, 0, 0, 0) && 1.566 + reflowState.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0), 1.567 + "style system should ensure that :-moz-svg-foreign-content " 1.568 + "does not get styled"); 1.569 + NS_ASSERTION(reflowState.ComputedWidth() == mRect.width, 1.570 + "reflow state made child wrong size"); 1.571 + reflowState.SetComputedHeight(mRect.height); 1.572 + 1.573 + ReflowChild(kid, presContext, desiredSize, reflowState, 0, 0, 1.574 + NS_FRAME_NO_MOVE_FRAME, status); 1.575 + NS_ASSERTION(mRect.width == desiredSize.Width() && 1.576 + mRect.height == desiredSize.Height(), "unexpected size"); 1.577 + FinishReflowChild(kid, presContext, desiredSize, &reflowState, 0, 0, 1.578 + NS_FRAME_NO_MOVE_FRAME); 1.579 + 1.580 + mInReflow = false; 1.581 +} 1.582 + 1.583 +nsRect 1.584 +nsSVGForeignObjectFrame::GetInvalidRegion() 1.585 +{ 1.586 + nsIFrame* kid = GetFirstPrincipalChild(); 1.587 + if (kid->HasInvalidFrameInSubtree()) { 1.588 + gfxRect r(mRect.x, mRect.y, mRect.width, mRect.height); 1.589 + r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel()); 1.590 + nsRect rect = nsSVGUtils::ToCanvasBounds(r, GetCanvasTM(FOR_PAINTING), PresContext()); 1.591 + rect = nsSVGUtils::GetPostFilterVisualOverflowRect(this, rect); 1.592 + return rect; 1.593 + } 1.594 + return nsRect(); 1.595 +} 1.596 + 1.597 +