michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // Main header first: michael@0: #include "nsSVGForeignObjectFrame.h" michael@0: michael@0: // Keep others in (case-insensitive) order: michael@0: #include "gfxContext.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "nsRegion.h" michael@0: #include "nsRenderingContext.h" michael@0: #include "nsSVGContainerFrame.h" michael@0: #include "nsSVGEffects.h" michael@0: #include "mozilla/dom/SVGForeignObjectElement.h" michael@0: #include "nsSVGIntegrationUtils.h" michael@0: #include "nsSVGOuterSVGFrame.h" michael@0: #include "nsSVGUtils.h" michael@0: #include "mozilla/AutoRestore.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // Implementation michael@0: michael@0: nsIFrame* michael@0: NS_NewSVGForeignObjectFrame(nsIPresShell *aPresShell, michael@0: nsStyleContext *aContext) michael@0: { michael@0: return new (aPresShell) nsSVGForeignObjectFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsSVGForeignObjectFrame) michael@0: michael@0: nsSVGForeignObjectFrame::nsSVGForeignObjectFrame(nsStyleContext* aContext) michael@0: : nsSVGForeignObjectFrameBase(aContext), michael@0: mInReflow(false) michael@0: { michael@0: AddStateBits(NS_FRAME_REFLOW_ROOT | NS_FRAME_MAY_BE_TRANSFORMED | michael@0: NS_FRAME_SVG_LAYOUT); michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // nsIFrame methods michael@0: michael@0: NS_QUERYFRAME_HEAD(nsSVGForeignObjectFrame) michael@0: NS_QUERYFRAME_ENTRY(nsISVGChildFrame) michael@0: NS_QUERYFRAME_TAIL_INHERITING(nsSVGForeignObjectFrameBase) michael@0: michael@0: void michael@0: nsSVGForeignObjectFrame::Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) michael@0: { michael@0: NS_ASSERTION(aContent->IsSVG(nsGkAtoms::foreignObject), michael@0: "Content is not an SVG foreignObject!"); michael@0: michael@0: nsSVGForeignObjectFrameBase::Init(aContent, aParent, aPrevInFlow); michael@0: AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD); michael@0: AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER | michael@0: NS_FRAME_FONT_INFLATION_FLOW_ROOT); michael@0: if (!(mState & NS_FRAME_IS_NONDISPLAY)) { michael@0: nsSVGUtils::GetOuterSVGFrame(this)->RegisterForeignObject(this); michael@0: } michael@0: } michael@0: michael@0: void nsSVGForeignObjectFrame::DestroyFrom(nsIFrame* aDestructRoot) michael@0: { michael@0: // Only unregister if we registered in the first place: michael@0: if (!(mState & NS_FRAME_IS_NONDISPLAY)) { michael@0: nsSVGUtils::GetOuterSVGFrame(this)->UnregisterForeignObject(this); michael@0: } michael@0: nsSVGForeignObjectFrameBase::DestroyFrom(aDestructRoot); michael@0: } michael@0: michael@0: nsIAtom * michael@0: nsSVGForeignObjectFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::svgForeignObjectFrame; michael@0: } michael@0: michael@0: nsresult michael@0: nsSVGForeignObjectFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom *aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: if (aNameSpaceID == kNameSpaceID_None) { michael@0: if (aAttribute == nsGkAtoms::width || michael@0: aAttribute == nsGkAtoms::height) { michael@0: nsSVGEffects::InvalidateRenderingObservers(this); michael@0: nsSVGUtils::ScheduleReflowSVG(this); michael@0: // XXXjwatt: why mark intrinsic widths dirty? can't we just use eResize? michael@0: RequestReflow(nsIPresShell::eStyleChange); michael@0: } else if (aAttribute == nsGkAtoms::x || michael@0: aAttribute == nsGkAtoms::y) { michael@0: // make sure our cached transform matrix gets (lazily) updated michael@0: mCanvasTM = nullptr; michael@0: nsSVGEffects::InvalidateRenderingObservers(this); michael@0: nsSVGUtils::ScheduleReflowSVG(this); michael@0: } else if (aAttribute == nsGkAtoms::transform) { michael@0: // We don't invalidate for transform changes (the layers code does that). michael@0: // Also note that SVGTransformableElement::GetAttributeChangeHint will michael@0: // return nsChangeHint_UpdateOverflow for "transform" attribute changes michael@0: // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call. michael@0: mCanvasTM = nullptr; michael@0: } else if (aAttribute == nsGkAtoms::viewBox || michael@0: aAttribute == nsGkAtoms::preserveAspectRatio) { michael@0: nsSVGEffects::InvalidateRenderingObservers(this); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsSVGForeignObjectFrame::Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY), michael@0: "Should not have been called"); michael@0: michael@0: // Only InvalidateAndScheduleBoundsUpdate marks us with NS_FRAME_IS_DIRTY, michael@0: // so if that bit is still set we still have a resize pending. If we hit michael@0: // this assertion, then we should get the presShell to skip reflow roots michael@0: // that have a dirty parent since a reflow is going to come via the michael@0: // reflow root's parent anyway. michael@0: NS_ASSERTION(!(GetStateBits() & NS_FRAME_IS_DIRTY), michael@0: "Reflowing while a resize is pending is wasteful"); michael@0: michael@0: // ReflowSVG makes sure mRect is up to date before we're called. michael@0: michael@0: NS_ASSERTION(!aReflowState.parentReflowState, michael@0: "should only get reflow from being reflow root"); michael@0: NS_ASSERTION(aReflowState.ComputedWidth() == GetSize().width && michael@0: aReflowState.ComputedHeight() == GetSize().height, michael@0: "reflow roots should be reflowed at existing size and " michael@0: "svg.css should ensure we have no padding/border/margin"); michael@0: michael@0: DoReflow(); michael@0: michael@0: aDesiredSize.Width() = aReflowState.ComputedWidth(); michael@0: aDesiredSize.Height() = aReflowState.ComputedHeight(); michael@0: aDesiredSize.SetOverflowAreasToDesiredBounds(); michael@0: aStatus = NS_FRAME_COMPLETE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsSVGForeignObjectFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: if (!static_cast(mContent)->HasValidDimensions()) { michael@0: return; michael@0: } michael@0: BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists); michael@0: } michael@0: michael@0: bool michael@0: nsSVGForeignObjectFrame::IsSVGTransformed(Matrix *aOwnTransform, michael@0: Matrix *aFromParentTransform) const michael@0: { michael@0: bool foundTransform = false; michael@0: michael@0: // Check if our parent has children-only transforms: michael@0: nsIFrame *parent = GetParent(); michael@0: if (parent && michael@0: parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) { michael@0: foundTransform = static_cast(parent)-> michael@0: HasChildrenOnlyTransform(aFromParentTransform); michael@0: } michael@0: michael@0: nsSVGElement *content = static_cast(mContent); michael@0: nsSVGAnimatedTransformList* transformList = michael@0: content->GetAnimatedTransformList(); michael@0: if ((transformList && transformList->HasTransform()) || michael@0: content->GetAnimateMotionTransform()) { michael@0: if (aOwnTransform) { michael@0: *aOwnTransform = gfx::ToMatrix(content->PrependLocalTransformsTo(gfxMatrix(), michael@0: nsSVGElement::eUserSpaceToParent)); michael@0: } michael@0: foundTransform = true; michael@0: } michael@0: return foundTransform; michael@0: } michael@0: michael@0: nsresult michael@0: nsSVGForeignObjectFrame::PaintSVG(nsRenderingContext *aContext, michael@0: const nsIntRect *aDirtyRect, michael@0: nsIFrame* aTransformRoot) michael@0: { michael@0: NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || michael@0: (mState & NS_FRAME_IS_NONDISPLAY), michael@0: "If display lists are enabled, only painting of non-display " michael@0: "SVG should take this code path"); michael@0: michael@0: if (IsDisabled()) michael@0: return NS_OK; michael@0: michael@0: nsIFrame* kid = GetFirstPrincipalChild(); michael@0: if (!kid) michael@0: return NS_OK; michael@0: michael@0: gfxMatrix canvasTM = GetCanvasTM(FOR_PAINTING, aTransformRoot); michael@0: michael@0: if (canvasTM.IsSingular()) { michael@0: NS_WARNING("Can't render foreignObject element!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsRect kidDirtyRect = kid->GetVisualOverflowRect(); michael@0: michael@0: /* Check if we need to draw anything. */ michael@0: if (aDirtyRect) { michael@0: NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || michael@0: (mState & NS_FRAME_IS_NONDISPLAY), michael@0: "Display lists handle dirty rect intersection test"); michael@0: // Transform the dirty rect into app units in our userspace. michael@0: gfxMatrix invmatrix = canvasTM; michael@0: invmatrix.Invert(); michael@0: NS_ASSERTION(!invmatrix.IsSingular(), michael@0: "inverse of non-singular matrix should be non-singular"); michael@0: michael@0: gfxRect transDirtyRect = gfxRect(aDirtyRect->x, aDirtyRect->y, michael@0: aDirtyRect->width, aDirtyRect->height); michael@0: transDirtyRect = invmatrix.TransformBounds(transDirtyRect); michael@0: michael@0: kidDirtyRect.IntersectRect(kidDirtyRect, michael@0: nsLayoutUtils::RoundGfxRectToAppRect(transDirtyRect, michael@0: PresContext()->AppUnitsPerCSSPixel())); michael@0: michael@0: // XXX after bug 614732 is fixed, we will compare mRect with aDirtyRect, michael@0: // not with kidDirtyRect. I.e. michael@0: // int32_t appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel(); michael@0: // mRect.ToOutsidePixels(appUnitsPerDevPx).Intersects(*aDirtyRect) michael@0: if (kidDirtyRect.IsEmpty()) michael@0: return NS_OK; michael@0: } michael@0: michael@0: gfxContext *gfx = aContext->ThebesContext(); michael@0: michael@0: gfx->Save(); michael@0: michael@0: if (StyleDisplay()->IsScrollableOverflow()) { michael@0: float x, y, width, height; michael@0: static_cast(mContent)-> michael@0: GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); michael@0: michael@0: gfxRect clipRect = michael@0: nsSVGUtils::GetClipRectForFrame(this, 0.0f, 0.0f, width, height); michael@0: nsSVGUtils::SetClipRect(gfx, canvasTM, clipRect); michael@0: } michael@0: michael@0: // SVG paints in CSS px, but normally frames paint in dev pixels. Here we michael@0: // multiply a CSS-px-to-dev-pixel factor onto canvasTM so our children paint michael@0: // correctly. michael@0: float cssPxPerDevPx = PresContext()-> michael@0: AppUnitsToFloatCSSPixels(PresContext()->AppUnitsPerDevPixel()); michael@0: gfxMatrix canvasTMForChildren = canvasTM; michael@0: canvasTMForChildren.Scale(cssPxPerDevPx, cssPxPerDevPx); michael@0: michael@0: gfx->Multiply(canvasTMForChildren); michael@0: michael@0: uint32_t flags = nsLayoutUtils::PAINT_IN_TRANSFORM; michael@0: if (SVGAutoRenderState::IsPaintingToWindow(aContext)) { michael@0: flags |= nsLayoutUtils::PAINT_TO_WINDOW; michael@0: } michael@0: nsresult rv = nsLayoutUtils::PaintFrame(aContext, kid, nsRegion(kidDirtyRect), michael@0: NS_RGBA(0,0,0,0), flags); michael@0: michael@0: gfx->Restore(); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsSVGForeignObjectFrame::GetFrameForPoint(const nsPoint &aPoint) michael@0: { michael@0: NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() || michael@0: (mState & NS_FRAME_IS_NONDISPLAY), michael@0: "If display lists are enabled, only hit-testing of a " michael@0: "clipPath's contents should take this code path"); michael@0: michael@0: if (IsDisabled() || (GetStateBits() & NS_FRAME_IS_NONDISPLAY)) michael@0: return nullptr; michael@0: michael@0: nsIFrame* kid = GetFirstPrincipalChild(); michael@0: if (!kid) michael@0: return nullptr; michael@0: michael@0: float x, y, width, height; michael@0: static_cast(mContent)-> michael@0: GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); michael@0: michael@0: gfxMatrix tm = GetCanvasTM(FOR_HIT_TESTING).Invert(); michael@0: if (tm.IsSingular()) michael@0: return nullptr; michael@0: michael@0: // Convert aPoint from app units in canvas space to user space: michael@0: michael@0: gfxPoint pt = gfxPoint(aPoint.x, aPoint.y) / PresContext()->AppUnitsPerDevPixel(); michael@0: pt = tm.Transform(pt); michael@0: michael@0: if (!gfxRect(0.0f, 0.0f, width, height).Contains(pt)) michael@0: return nullptr; michael@0: michael@0: // Convert pt to app units in *local* space: michael@0: michael@0: pt = pt * nsPresContext::AppUnitsPerCSSPixel(); michael@0: nsPoint point = nsPoint(NSToIntRound(pt.x), NSToIntRound(pt.y)); michael@0: michael@0: nsIFrame *frame = nsLayoutUtils::GetFrameForPoint(kid, point); michael@0: if (frame && nsSVGUtils::HitTestClip(this, aPoint)) michael@0: return frame; michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRect michael@0: nsSVGForeignObjectFrame::GetCoveredRegion() michael@0: { michael@0: float x, y, w, h; michael@0: static_cast(mContent)-> michael@0: GetAnimatedLengthValues(&x, &y, &w, &h, nullptr); michael@0: if (w < 0.0f) w = 0.0f; michael@0: if (h < 0.0f) h = 0.0f; michael@0: // GetCanvasTM includes the x,y translation michael@0: return nsSVGUtils::ToCanvasBounds(gfxRect(0.0, 0.0, w, h), michael@0: GetCanvasTM(FOR_OUTERSVG_TM), michael@0: PresContext()); michael@0: } michael@0: michael@0: void michael@0: nsSVGForeignObjectFrame::ReflowSVG() michael@0: { michael@0: NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this), michael@0: "This call is probably a wasteful mistake"); michael@0: michael@0: NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY), michael@0: "ReflowSVG mechanism not designed for this"); michael@0: michael@0: if (!nsSVGUtils::NeedsReflowSVG(this)) { michael@0: return; michael@0: } michael@0: michael@0: // We update mRect before the DoReflow call so that DoReflow uses the michael@0: // correct dimensions: michael@0: michael@0: float x, y, w, h; michael@0: static_cast(mContent)-> michael@0: GetAnimatedLengthValues(&x, &y, &w, &h, nullptr); michael@0: michael@0: // If mRect's width or height are negative, reflow blows up! We must clamp! michael@0: if (w < 0.0f) w = 0.0f; michael@0: if (h < 0.0f) h = 0.0f; michael@0: michael@0: mRect = nsLayoutUtils::RoundGfxRectToAppRect( michael@0: gfxRect(x, y, w, h), michael@0: PresContext()->AppUnitsPerCSSPixel()); michael@0: michael@0: // Fully mark our kid dirty so that it gets resized if necessary michael@0: // (NS_FRAME_HAS_DIRTY_CHILDREN isn't enough in that case): michael@0: nsIFrame* kid = GetFirstPrincipalChild(); michael@0: kid->AddStateBits(NS_FRAME_IS_DIRTY); michael@0: michael@0: // Make sure to not allow interrupts if we're not being reflown as a root: michael@0: nsPresContext::InterruptPreventer noInterrupts(PresContext()); michael@0: michael@0: DoReflow(); michael@0: michael@0: if (mState & NS_FRAME_FIRST_REFLOW) { michael@0: // Make sure we have our filter property (if any) before calling michael@0: // FinishAndStoreOverflow (subsequent filter changes are handled off michael@0: // nsChangeHint_UpdateEffects): michael@0: nsSVGEffects::UpdateEffects(this); michael@0: } michael@0: michael@0: // If we have a filter, we need to invalidate ourselves because filter michael@0: // output can change even if none of our descendants need repainting. michael@0: if (StyleSVGReset()->HasFilters()) { michael@0: InvalidateFrame(); michael@0: } michael@0: michael@0: // TODO: once we support |overflow:visible| on foreignObject, then we will michael@0: // need to take account of our descendants here. michael@0: nsRect overflow = nsRect(nsPoint(0,0), mRect.Size()); michael@0: nsOverflowAreas overflowAreas(overflow, overflow); michael@0: FinishAndStoreOverflow(overflowAreas, mRect.Size()); michael@0: michael@0: // Now unset the various reflow bits: michael@0: mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY | michael@0: NS_FRAME_HAS_DIRTY_CHILDREN); michael@0: } michael@0: michael@0: void michael@0: nsSVGForeignObjectFrame::NotifySVGChanged(uint32_t aFlags) michael@0: { michael@0: NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED), michael@0: "Invalidation logic may need adjusting"); michael@0: michael@0: bool needNewBounds = false; // i.e. mRect or visual overflow rect michael@0: bool needReflow = false; michael@0: bool needNewCanvasTM = false; michael@0: michael@0: if (aFlags & COORD_CONTEXT_CHANGED) { michael@0: SVGForeignObjectElement *fO = michael@0: static_cast(mContent); michael@0: // Coordinate context changes affect mCanvasTM if we have a michael@0: // percentage 'x' or 'y' michael@0: if (fO->mLengthAttributes[SVGForeignObjectElement::ATTR_X].IsPercentage() || michael@0: fO->mLengthAttributes[SVGForeignObjectElement::ATTR_Y].IsPercentage()) { michael@0: needNewBounds = true; michael@0: needNewCanvasTM = true; michael@0: } michael@0: // Our coordinate context's width/height has changed. If we have a michael@0: // percentage width/height our dimensions will change so we must reflow. michael@0: if (fO->mLengthAttributes[SVGForeignObjectElement::ATTR_WIDTH].IsPercentage() || michael@0: fO->mLengthAttributes[SVGForeignObjectElement::ATTR_HEIGHT].IsPercentage()) { michael@0: needNewBounds = true; michael@0: needReflow = true; michael@0: } michael@0: } michael@0: michael@0: if (aFlags & TRANSFORM_CHANGED) { michael@0: if (mCanvasTM && mCanvasTM->IsSingular()) { michael@0: needNewBounds = true; // old bounds are bogus michael@0: } michael@0: needNewCanvasTM = true; michael@0: // In an ideal world we would reflow when our CTM changes. This is because michael@0: // glyph metrics do not necessarily scale uniformly with change in scale michael@0: // and, as a result, CTM changes may require text to break at different michael@0: // points. The problem would be how to keep performance acceptable when michael@0: // e.g. the transform of an ancestor is animated. michael@0: // We also seem to get some sort of infinite loop post bug 421584 if we michael@0: // reflow. michael@0: } michael@0: michael@0: if (needNewBounds) { michael@0: // Ancestor changes can't affect how we render from the perspective of michael@0: // any rendering observers that we may have, so we don't need to michael@0: // invalidate them. We also don't need to invalidate ourself, since our michael@0: // changed ancestor will have invalidated its entire area, which includes michael@0: // our area. michael@0: nsSVGUtils::ScheduleReflowSVG(this); michael@0: } michael@0: michael@0: // If we're called while the PresShell is handling reflow events then we michael@0: // must have been called as a result of the NotifyViewportChange() call in michael@0: // our nsSVGOuterSVGFrame's Reflow() method. We must not call RequestReflow michael@0: // at this point (i.e. during reflow) because it could confuse the michael@0: // PresShell and prevent it from reflowing us properly in future. Besides michael@0: // that, nsSVGOuterSVGFrame::DidReflow will take care of reflowing us michael@0: // synchronously, so there's no need. michael@0: if (needReflow && !PresContext()->PresShell()->IsReflowLocked()) { michael@0: RequestReflow(nsIPresShell::eResize); michael@0: } michael@0: michael@0: if (needNewCanvasTM) { michael@0: // Do this after calling InvalidateAndScheduleBoundsUpdate in case we michael@0: // change the code and it needs to use it. michael@0: mCanvasTM = nullptr; michael@0: } michael@0: } michael@0: michael@0: SVGBBox michael@0: nsSVGForeignObjectFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace, michael@0: uint32_t aFlags) michael@0: { michael@0: SVGForeignObjectElement *content = michael@0: static_cast(mContent); michael@0: michael@0: float x, y, w, h; michael@0: content->GetAnimatedLengthValues(&x, &y, &w, &h, nullptr); michael@0: michael@0: if (w < 0.0f) w = 0.0f; michael@0: if (h < 0.0f) h = 0.0f; michael@0: michael@0: if (aToBBoxUserspace.IsSingular()) { michael@0: // XXX ReportToConsole michael@0: return SVGBBox(); michael@0: } michael@0: return aToBBoxUserspace.TransformBounds(gfx::Rect(0.0, 0.0, w, h)); michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: michael@0: gfxMatrix michael@0: nsSVGForeignObjectFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot) michael@0: { michael@0: if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY) && !aTransformRoot) { michael@0: if ((aFor == FOR_PAINTING && NS_SVGDisplayListPaintingEnabled()) || michael@0: (aFor == FOR_HIT_TESTING && NS_SVGDisplayListHitTestingEnabled())) { michael@0: return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this); michael@0: } michael@0: } michael@0: if (!mCanvasTM) { michael@0: NS_ASSERTION(mParent, "null parent"); michael@0: michael@0: nsSVGContainerFrame *parent = static_cast(mParent); michael@0: SVGForeignObjectElement *content = michael@0: static_cast(mContent); michael@0: michael@0: gfxMatrix tm = content->PrependLocalTransformsTo( michael@0: this == aTransformRoot ? gfxMatrix() : michael@0: parent->GetCanvasTM(aFor, aTransformRoot)); michael@0: michael@0: mCanvasTM = new gfxMatrix(tm); michael@0: } michael@0: return *mCanvasTM; michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // Implementation helpers michael@0: michael@0: void nsSVGForeignObjectFrame::RequestReflow(nsIPresShell::IntrinsicDirty aType) michael@0: { michael@0: if (GetStateBits() & NS_FRAME_FIRST_REFLOW) michael@0: // If we haven't had a ReflowSVG() yet, nothing to do. michael@0: return; michael@0: michael@0: nsIFrame* kid = GetFirstPrincipalChild(); michael@0: if (!kid) michael@0: return; michael@0: michael@0: PresContext()->PresShell()->FrameNeedsReflow(kid, aType, NS_FRAME_IS_DIRTY); michael@0: } michael@0: michael@0: void michael@0: nsSVGForeignObjectFrame::DoReflow() michael@0: { michael@0: // Skip reflow if we're zero-sized, unless this is our first reflow. michael@0: if (IsDisabled() && michael@0: !(GetStateBits() & NS_FRAME_FIRST_REFLOW)) michael@0: return; michael@0: michael@0: nsPresContext *presContext = PresContext(); michael@0: nsIFrame* kid = GetFirstPrincipalChild(); michael@0: if (!kid) michael@0: return; michael@0: michael@0: // initiate a synchronous reflow here and now: michael@0: nsRefPtr renderingContext = michael@0: presContext->PresShell()->CreateReferenceRenderingContext(); michael@0: michael@0: mInReflow = true; michael@0: michael@0: nsHTMLReflowState reflowState(presContext, kid, michael@0: renderingContext, michael@0: nsSize(mRect.width, NS_UNCONSTRAINEDSIZE)); michael@0: nsHTMLReflowMetrics desiredSize(reflowState); michael@0: nsReflowStatus status; michael@0: michael@0: // We don't use mRect.height above because that tells the child to do michael@0: // page/column breaking at that height. michael@0: NS_ASSERTION(reflowState.ComputedPhysicalBorderPadding() == nsMargin(0, 0, 0, 0) && michael@0: reflowState.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0), michael@0: "style system should ensure that :-moz-svg-foreign-content " michael@0: "does not get styled"); michael@0: NS_ASSERTION(reflowState.ComputedWidth() == mRect.width, michael@0: "reflow state made child wrong size"); michael@0: reflowState.SetComputedHeight(mRect.height); michael@0: michael@0: ReflowChild(kid, presContext, desiredSize, reflowState, 0, 0, michael@0: NS_FRAME_NO_MOVE_FRAME, status); michael@0: NS_ASSERTION(mRect.width == desiredSize.Width() && michael@0: mRect.height == desiredSize.Height(), "unexpected size"); michael@0: FinishReflowChild(kid, presContext, desiredSize, &reflowState, 0, 0, michael@0: NS_FRAME_NO_MOVE_FRAME); michael@0: michael@0: mInReflow = false; michael@0: } michael@0: michael@0: nsRect michael@0: nsSVGForeignObjectFrame::GetInvalidRegion() michael@0: { michael@0: nsIFrame* kid = GetFirstPrincipalChild(); michael@0: if (kid->HasInvalidFrameInSubtree()) { michael@0: gfxRect r(mRect.x, mRect.y, mRect.width, mRect.height); michael@0: r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel()); michael@0: nsRect rect = nsSVGUtils::ToCanvasBounds(r, GetCanvasTM(FOR_PAINTING), PresContext()); michael@0: rect = nsSVGUtils::GetPostFilterVisualOverflowRect(this, rect); michael@0: return rect; michael@0: } michael@0: return nsRect(); michael@0: } michael@0: michael@0: