1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/svg/nsSVGOuterSVGFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,953 @@ 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 "nsSVGOuterSVGFrame.h" 1.11 + 1.12 +// Keep others in (case-insensitive) order: 1.13 +#include "nsDisplayList.h" 1.14 +#include "nsIDocument.h" 1.15 +#include "nsIDOMWindow.h" 1.16 +#include "nsIInterfaceRequestorUtils.h" 1.17 +#include "nsIObjectLoadingContent.h" 1.18 +#include "nsRenderingContext.h" 1.19 +#include "nsSVGIntegrationUtils.h" 1.20 +#include "nsSVGForeignObjectFrame.h" 1.21 +#include "mozilla/dom/SVGSVGElement.h" 1.22 +#include "mozilla/dom/SVGViewElement.h" 1.23 +#include "nsSubDocumentFrame.h" 1.24 + 1.25 +using namespace mozilla; 1.26 +using namespace mozilla::dom; 1.27 + 1.28 +//---------------------------------------------------------------------- 1.29 +// Implementation helpers 1.30 + 1.31 +void 1.32 +nsSVGOuterSVGFrame::RegisterForeignObject(nsSVGForeignObjectFrame* aFrame) 1.33 +{ 1.34 + NS_ASSERTION(aFrame, "Who on earth is calling us?!"); 1.35 + 1.36 + if (!mForeignObjectHash) { 1.37 + mForeignObjectHash = new nsTHashtable<nsPtrHashKey<nsSVGForeignObjectFrame> >(); 1.38 + } 1.39 + 1.40 + NS_ASSERTION(!mForeignObjectHash->GetEntry(aFrame), 1.41 + "nsSVGForeignObjectFrame already registered!"); 1.42 + 1.43 + mForeignObjectHash->PutEntry(aFrame); 1.44 + 1.45 + NS_ASSERTION(mForeignObjectHash->GetEntry(aFrame), 1.46 + "Failed to register nsSVGForeignObjectFrame!"); 1.47 +} 1.48 + 1.49 +void 1.50 +nsSVGOuterSVGFrame::UnregisterForeignObject(nsSVGForeignObjectFrame* aFrame) 1.51 +{ 1.52 + NS_ASSERTION(aFrame, "Who on earth is calling us?!"); 1.53 + NS_ASSERTION(mForeignObjectHash && mForeignObjectHash->GetEntry(aFrame), 1.54 + "nsSVGForeignObjectFrame not in registry!"); 1.55 + return mForeignObjectHash->RemoveEntry(aFrame); 1.56 +} 1.57 + 1.58 +//---------------------------------------------------------------------- 1.59 +// Implementation 1.60 + 1.61 +nsIFrame* 1.62 +NS_NewSVGOuterSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.63 +{ 1.64 + return new (aPresShell) nsSVGOuterSVGFrame(aContext); 1.65 +} 1.66 + 1.67 +NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGFrame) 1.68 + 1.69 +nsSVGOuterSVGFrame::nsSVGOuterSVGFrame(nsStyleContext* aContext) 1.70 + : nsSVGOuterSVGFrameBase(aContext) 1.71 + , mFullZoom(aContext->PresContext()->GetFullZoom()) 1.72 + , mViewportInitialized(false) 1.73 + , mIsRootContent(false) 1.74 +{ 1.75 + // Outer-<svg> has CSS layout, so remove this bit: 1.76 + RemoveStateBits(NS_FRAME_SVG_LAYOUT); 1.77 +} 1.78 + 1.79 +void 1.80 +nsSVGOuterSVGFrame::Init(nsIContent* aContent, 1.81 + nsIFrame* aParent, 1.82 + nsIFrame* aPrevInFlow) 1.83 +{ 1.84 + NS_ASSERTION(aContent->IsSVG(nsGkAtoms::svg), 1.85 + "Content is not an SVG 'svg' element!"); 1.86 + 1.87 + AddStateBits(NS_STATE_IS_OUTER_SVG | 1.88 + NS_FRAME_FONT_INFLATION_CONTAINER | 1.89 + NS_FRAME_FONT_INFLATION_FLOW_ROOT); 1.90 + 1.91 + // Check for conditional processing attributes here rather than in 1.92 + // nsCSSFrameConstructor::FindSVGData because we want to avoid 1.93 + // simply giving failing outer <svg> elements an nsSVGContainerFrame. 1.94 + // We don't create other SVG frames if PassesConditionalProcessingTests 1.95 + // returns false, but since we do create nsSVGOuterSVGFrame frames we 1.96 + // prevent them from painting by [ab]use NS_FRAME_IS_NONDISPLAY. The 1.97 + // frame will be recreated via an nsChangeHint_ReconstructFrame restyle if 1.98 + // the value returned by PassesConditionalProcessingTests changes. 1.99 + SVGSVGElement *svg = static_cast<SVGSVGElement*>(aContent); 1.100 + if (!svg->PassesConditionalProcessingTests()) { 1.101 + AddStateBits(NS_FRAME_IS_NONDISPLAY); 1.102 + } 1.103 + 1.104 + nsSVGOuterSVGFrameBase::Init(aContent, aParent, aPrevInFlow); 1.105 + 1.106 + nsIDocument* doc = mContent->GetCurrentDoc(); 1.107 + if (doc) { 1.108 + // we only care about our content's zoom and pan values if it's the root element 1.109 + if (doc->GetRootElement() == mContent) { 1.110 + mIsRootContent = true; 1.111 + } 1.112 + } 1.113 +} 1.114 + 1.115 +//---------------------------------------------------------------------- 1.116 +// nsQueryFrame methods 1.117 + 1.118 +NS_QUERYFRAME_HEAD(nsSVGOuterSVGFrame) 1.119 + NS_QUERYFRAME_ENTRY(nsISVGSVGFrame) 1.120 +NS_QUERYFRAME_TAIL_INHERITING(nsSVGOuterSVGFrameBase) 1.121 + 1.122 +//---------------------------------------------------------------------- 1.123 +// nsIFrame methods 1.124 + 1.125 +//---------------------------------------------------------------------- 1.126 +// reflowing 1.127 + 1.128 +/* virtual */ nscoord 1.129 +nsSVGOuterSVGFrame::GetMinWidth(nsRenderingContext *aRenderingContext) 1.130 +{ 1.131 + nscoord result; 1.132 + DISPLAY_MIN_WIDTH(this, result); 1.133 + 1.134 + result = nscoord(0); 1.135 + 1.136 + return result; 1.137 +} 1.138 + 1.139 +/* virtual */ nscoord 1.140 +nsSVGOuterSVGFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) 1.141 +{ 1.142 + nscoord result; 1.143 + DISPLAY_PREF_WIDTH(this, result); 1.144 + 1.145 + SVGSVGElement *svg = static_cast<SVGSVGElement*>(mContent); 1.146 + nsSVGLength2 &width = svg->mLengthAttributes[SVGSVGElement::ATTR_WIDTH]; 1.147 + 1.148 + if (width.IsPercentage()) { 1.149 + // It looks like our containing block's width may depend on our width. In 1.150 + // that case our behavior is undefined according to CSS 2.1 section 10.3.2, 1.151 + // so return zero. 1.152 + result = nscoord(0); 1.153 + } else { 1.154 + result = nsPresContext::CSSPixelsToAppUnits(width.GetAnimValue(svg)); 1.155 + if (result < 0) { 1.156 + result = nscoord(0); 1.157 + } 1.158 + } 1.159 + 1.160 + return result; 1.161 +} 1.162 + 1.163 +/* virtual */ IntrinsicSize 1.164 +nsSVGOuterSVGFrame::GetIntrinsicSize() 1.165 +{ 1.166 + // XXXjwatt Note that here we want to return the CSS width/height if they're 1.167 + // specified and we're embedded inside an nsIObjectLoadingContent. 1.168 + 1.169 + IntrinsicSize intrinsicSize; 1.170 + 1.171 + SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent); 1.172 + nsSVGLength2 &width = content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH]; 1.173 + nsSVGLength2 &height = content->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT]; 1.174 + 1.175 + if (!width.IsPercentage()) { 1.176 + nscoord val = nsPresContext::CSSPixelsToAppUnits(width.GetAnimValue(content)); 1.177 + if (val < 0) val = 0; 1.178 + intrinsicSize.width.SetCoordValue(val); 1.179 + } 1.180 + 1.181 + if (!height.IsPercentage()) { 1.182 + nscoord val = nsPresContext::CSSPixelsToAppUnits(height.GetAnimValue(content)); 1.183 + if (val < 0) val = 0; 1.184 + intrinsicSize.height.SetCoordValue(val); 1.185 + } 1.186 + 1.187 + return intrinsicSize; 1.188 +} 1.189 + 1.190 +/* virtual */ nsSize 1.191 +nsSVGOuterSVGFrame::GetIntrinsicRatio() 1.192 +{ 1.193 + // We only have an intrinsic size/ratio if our width and height attributes 1.194 + // are both specified and set to non-percentage values, or we have a viewBox 1.195 + // rect: http://www.w3.org/TR/SVGMobile12/coords.html#IntrinsicSizing 1.196 + 1.197 + SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent); 1.198 + nsSVGLength2 &width = content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH]; 1.199 + nsSVGLength2 &height = content->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT]; 1.200 + 1.201 + if (!width.IsPercentage() && !height.IsPercentage()) { 1.202 + nsSize ratio(NSToCoordRoundWithClamp(width.GetAnimValue(content)), 1.203 + NSToCoordRoundWithClamp(height.GetAnimValue(content))); 1.204 + if (ratio.width < 0) { 1.205 + ratio.width = 0; 1.206 + } 1.207 + if (ratio.height < 0) { 1.208 + ratio.height = 0; 1.209 + } 1.210 + return ratio; 1.211 + } 1.212 + 1.213 + SVGViewElement* viewElement = content->GetCurrentViewElement(); 1.214 + const nsSVGViewBoxRect* viewbox = nullptr; 1.215 + 1.216 + // The logic here should match HasViewBox(). 1.217 + if (viewElement && viewElement->mViewBox.HasRect()) { 1.218 + viewbox = &viewElement->mViewBox.GetAnimValue(); 1.219 + } else if (content->mViewBox.HasRect()) { 1.220 + viewbox = &content->mViewBox.GetAnimValue(); 1.221 + } 1.222 + 1.223 + if (viewbox) { 1.224 + float viewBoxWidth = viewbox->width; 1.225 + float viewBoxHeight = viewbox->height; 1.226 + 1.227 + if (viewBoxWidth < 0.0f) { 1.228 + viewBoxWidth = 0.0f; 1.229 + } 1.230 + if (viewBoxHeight < 0.0f) { 1.231 + viewBoxHeight = 0.0f; 1.232 + } 1.233 + return nsSize(NSToCoordRoundWithClamp(viewBoxWidth), 1.234 + NSToCoordRoundWithClamp(viewBoxHeight)); 1.235 + } 1.236 + 1.237 + return nsSVGOuterSVGFrameBase::GetIntrinsicRatio(); 1.238 +} 1.239 + 1.240 +/* virtual */ nsSize 1.241 +nsSVGOuterSVGFrame::ComputeSize(nsRenderingContext *aRenderingContext, 1.242 + nsSize aCBSize, nscoord aAvailableWidth, 1.243 + nsSize aMargin, nsSize aBorder, nsSize aPadding, 1.244 + uint32_t aFlags) 1.245 +{ 1.246 + if (IsRootOfImage() || IsRootOfReplacedElementSubDoc()) { 1.247 + // The embedding element has sized itself using the CSS replaced element 1.248 + // sizing rules, using our intrinsic dimensions as necessary. The SVG spec 1.249 + // says that the width and height of embedded SVG is overridden by the 1.250 + // width and height of the embedding element, so we just need to size to 1.251 + // the viewport that the embedding element has established for us. 1.252 + return aCBSize; 1.253 + } 1.254 + 1.255 + nsSize cbSize = aCBSize; 1.256 + IntrinsicSize intrinsicSize = GetIntrinsicSize(); 1.257 + 1.258 + if (!mContent->GetParent()) { 1.259 + // We're the root of the outermost browsing context, so we need to scale 1.260 + // cbSize by the full-zoom so that SVGs with percentage width/height zoom: 1.261 + 1.262 + NS_ASSERTION(aCBSize.width != NS_AUTOHEIGHT && 1.263 + aCBSize.height != NS_AUTOHEIGHT, 1.264 + "root should not have auto-width/height containing block"); 1.265 + cbSize.width *= PresContext()->GetFullZoom(); 1.266 + cbSize.height *= PresContext()->GetFullZoom(); 1.267 + 1.268 + // We also need to honour the width and height attributes' default values 1.269 + // of 100% when we're the root of a browsing context. (GetIntrinsicSize() 1.270 + // doesn't report these since there's no such thing as a percentage 1.271 + // intrinsic size. Also note that explicit percentage values are mapped 1.272 + // into style, so the following isn't for them.) 1.273 + 1.274 + SVGSVGElement* content = static_cast<SVGSVGElement*>(mContent); 1.275 + 1.276 + nsSVGLength2 &width = 1.277 + content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH]; 1.278 + if (width.IsPercentage()) { 1.279 + NS_ABORT_IF_FALSE(intrinsicSize.width.GetUnit() == eStyleUnit_None, 1.280 + "GetIntrinsicSize should have reported no " 1.281 + "intrinsic width"); 1.282 + float val = width.GetAnimValInSpecifiedUnits() / 100.0f; 1.283 + if (val < 0.0f) val = 0.0f; 1.284 + intrinsicSize.width.SetCoordValue(val * cbSize.width); 1.285 + } 1.286 + 1.287 + nsSVGLength2 &height = 1.288 + content->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT]; 1.289 + NS_ASSERTION(aCBSize.height != NS_AUTOHEIGHT, 1.290 + "root should not have auto-height containing block"); 1.291 + if (height.IsPercentage()) { 1.292 + NS_ABORT_IF_FALSE(intrinsicSize.height.GetUnit() == eStyleUnit_None, 1.293 + "GetIntrinsicSize should have reported no " 1.294 + "intrinsic height"); 1.295 + float val = height.GetAnimValInSpecifiedUnits() / 100.0f; 1.296 + if (val < 0.0f) val = 0.0f; 1.297 + intrinsicSize.height.SetCoordValue(val * cbSize.height); 1.298 + } 1.299 + NS_ABORT_IF_FALSE(intrinsicSize.height.GetUnit() == eStyleUnit_Coord && 1.300 + intrinsicSize.width.GetUnit() == eStyleUnit_Coord, 1.301 + "We should have just handled the only situation where" 1.302 + "we lack an intrinsic height or width."); 1.303 + } 1.304 + 1.305 + return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions( 1.306 + aRenderingContext, this, 1.307 + intrinsicSize, GetIntrinsicRatio(), cbSize, 1.308 + aMargin, aBorder, aPadding); 1.309 +} 1.310 + 1.311 +nsresult 1.312 +nsSVGOuterSVGFrame::Reflow(nsPresContext* aPresContext, 1.313 + nsHTMLReflowMetrics& aDesiredSize, 1.314 + const nsHTMLReflowState& aReflowState, 1.315 + nsReflowStatus& aStatus) 1.316 +{ 1.317 + DO_GLOBAL_REFLOW_COUNT("nsSVGOuterSVGFrame"); 1.318 + DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); 1.319 + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, 1.320 + ("enter nsSVGOuterSVGFrame::Reflow: availSize=%d,%d", 1.321 + aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); 1.322 + 1.323 + NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); 1.324 + 1.325 + aStatus = NS_FRAME_COMPLETE; 1.326 + 1.327 + aDesiredSize.Width() = aReflowState.ComputedWidth() + 1.328 + aReflowState.ComputedPhysicalBorderPadding().LeftRight(); 1.329 + aDesiredSize.Height() = aReflowState.ComputedHeight() + 1.330 + aReflowState.ComputedPhysicalBorderPadding().TopBottom(); 1.331 + 1.332 + NS_ASSERTION(!GetPrevInFlow(), "SVG can't currently be broken across pages."); 1.333 + 1.334 + SVGSVGElement *svgElem = static_cast<SVGSVGElement*>(mContent); 1.335 + 1.336 + nsSVGOuterSVGAnonChildFrame *anonKid = 1.337 + static_cast<nsSVGOuterSVGAnonChildFrame*>(GetFirstPrincipalChild()); 1.338 + 1.339 + if (mState & NS_FRAME_FIRST_REFLOW) { 1.340 + // Initialize 1.341 + svgElem->UpdateHasChildrenOnlyTransform(); 1.342 + } 1.343 + 1.344 + // If our SVG viewport has changed, update our content and notify. 1.345 + // http://www.w3.org/TR/SVG11/coords.html#ViewportSpace 1.346 + 1.347 + svgFloatSize newViewportSize( 1.348 + nsPresContext::AppUnitsToFloatCSSPixels(aReflowState.ComputedWidth()), 1.349 + nsPresContext::AppUnitsToFloatCSSPixels(aReflowState.ComputedHeight())); 1.350 + 1.351 + svgFloatSize oldViewportSize = svgElem->GetViewportSize(); 1.352 + 1.353 + uint32_t changeBits = 0; 1.354 + if (newViewportSize != oldViewportSize) { 1.355 + // When our viewport size changes, we may need to update the overflow rects 1.356 + // of our child frames. This is the case if: 1.357 + // 1.358 + // * We have a real/synthetic viewBox (a children-only transform), since 1.359 + // the viewBox transform will change as the viewport dimensions change. 1.360 + // 1.361 + // * We do not have a real/synthetic viewBox, but the last time we 1.362 + // reflowed (or the last time UpdateOverflow() was called) we did. 1.363 + // 1.364 + // We only handle the former case here, in which case we mark all our child 1.365 + // frames as dirty so that we reflow them below and update their overflow 1.366 + // rects. 1.367 + // 1.368 + // In the latter case, updating of overflow rects is handled for removal of 1.369 + // real viewBox (the viewBox attribute) in AttributeChanged. Synthetic 1.370 + // viewBox "removal" (e.g. a document references the same SVG via both an 1.371 + // <svg:image> and then as a CSS background image (a synthetic viewBox is 1.372 + // used when painting the former, but not when painting the latter)) is 1.373 + // handled in SVGSVGElement::FlushImageTransformInvalidation. 1.374 + // 1.375 + if (svgElem->HasViewBoxOrSyntheticViewBox()) { 1.376 + nsIFrame* anonChild = GetFirstPrincipalChild(); 1.377 + anonChild->AddStateBits(NS_FRAME_IS_DIRTY); 1.378 + for (nsIFrame* child = anonChild->GetFirstPrincipalChild(); child; 1.379 + child = child->GetNextSibling()) { 1.380 + child->AddStateBits(NS_FRAME_IS_DIRTY); 1.381 + } 1.382 + } 1.383 + changeBits |= COORD_CONTEXT_CHANGED; 1.384 + svgElem->SetViewportSize(newViewportSize); 1.385 + } 1.386 + if (mFullZoom != PresContext()->GetFullZoom()) { 1.387 + changeBits |= FULL_ZOOM_CHANGED; 1.388 + mFullZoom = PresContext()->GetFullZoom(); 1.389 + } 1.390 + if (changeBits) { 1.391 + NotifyViewportOrTransformChanged(changeBits); 1.392 + } 1.393 + mViewportInitialized = true; 1.394 + 1.395 + // Now that we've marked the necessary children as dirty, call 1.396 + // ReflowSVG() or ReflowSVGNonDisplayText() on them, depending 1.397 + // on whether we are non-display. 1.398 + mCallingReflowSVG = true; 1.399 + if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) { 1.400 + ReflowSVGNonDisplayText(this); 1.401 + } else { 1.402 + // Update the mRects and visual overflow rects of all our descendants, 1.403 + // including our anonymous wrapper kid: 1.404 + anonKid->AddStateBits(mState & NS_FRAME_IS_DIRTY); 1.405 + anonKid->ReflowSVG(); 1.406 + NS_ABORT_IF_FALSE(!anonKid->GetNextSibling(), 1.407 + "We should have one anonymous child frame wrapping our real children"); 1.408 + } 1.409 + mCallingReflowSVG = false; 1.410 + 1.411 + // Set our anonymous kid's offset from our border box: 1.412 + anonKid->SetPosition(GetContentRectRelativeToSelf().TopLeft()); 1.413 + 1.414 + // Including our size in our overflow rects regardless of the value of 1.415 + // 'background', 'border', etc. makes sure that we usually (when we clip to 1.416 + // our content area) don't have to keep changing our overflow rects as our 1.417 + // descendants move about (see perf comment below). Including our size in our 1.418 + // scrollable overflow rect also makes sure that we scroll if we're too big 1.419 + // for our viewport. 1.420 + // 1.421 + // <svg> never allows scrolling to anything outside its mRect (only panning), 1.422 + // so we must always keep our scrollable overflow set to our size. 1.423 + // 1.424 + // With regards to visual overflow, we always clip root-<svg> (see our 1.425 + // BuildDisplayList method) regardless of the value of the 'overflow' 1.426 + // property since that is per-spec, even for the initial 'visible' value. For 1.427 + // that reason there's no point in adding descendant visual overflow to our 1.428 + // own when this frame is for a root-<svg>. That said, there's also a very 1.429 + // good performance reason for us wanting to avoid doing so. If we did, then 1.430 + // the frame's overflow would often change as descendants that are partially 1.431 + // or fully outside its rect moved (think animation on/off screen), and that 1.432 + // would cause us to do a full NS_FRAME_IS_DIRTY reflow and repaint of the 1.433 + // entire document tree each such move (see bug 875175). 1.434 + // 1.435 + // So it's only non-root outer-<svg> that has the visual overflow of its 1.436 + // descendants added to its own. (Note that the default user-agent style 1.437 + // sheet makes 'hidden' the default value for :not(root(svg)), so usually 1.438 + // FinishAndStoreOverflow will still clip this back to the frame's rect.) 1.439 + // 1.440 + // WARNING!! Keep UpdateBounds below in sync with whatever we do for our 1.441 + // overflow rects here! (Again, see bug 875175.) 1.442 + // 1.443 + aDesiredSize.SetOverflowAreasToDesiredBounds(); 1.444 + if (!mIsRootContent) { 1.445 + aDesiredSize.mOverflowAreas.VisualOverflow().UnionRect( 1.446 + aDesiredSize.mOverflowAreas.VisualOverflow(), 1.447 + anonKid->GetVisualOverflowRect() + anonKid->GetPosition()); 1.448 + } 1.449 + FinishAndStoreOverflow(&aDesiredSize); 1.450 + 1.451 + NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, 1.452 + ("exit nsSVGOuterSVGFrame::Reflow: size=%d,%d", 1.453 + aDesiredSize.Width(), aDesiredSize.Height())); 1.454 + NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); 1.455 + return NS_OK; 1.456 +} 1.457 + 1.458 +nsresult 1.459 +nsSVGOuterSVGFrame::DidReflow(nsPresContext* aPresContext, 1.460 + const nsHTMLReflowState* aReflowState, 1.461 + nsDidReflowStatus aStatus) 1.462 +{ 1.463 + nsresult rv = nsSVGOuterSVGFrameBase::DidReflow(aPresContext,aReflowState,aStatus); 1.464 + 1.465 + // Make sure elements styled by :hover get updated if script/animation moves 1.466 + // them under or out from under the pointer: 1.467 + PresContext()->PresShell()->SynthesizeMouseMove(false); 1.468 + 1.469 + return rv; 1.470 +} 1.471 + 1.472 +/* virtual */ bool 1.473 +nsSVGOuterSVGFrame::UpdateOverflow() 1.474 +{ 1.475 + // See the comments in Reflow above. 1.476 + 1.477 + // WARNING!! Keep this in sync with Reflow above! 1.478 + 1.479 + nsRect rect(nsPoint(0, 0), GetSize()); 1.480 + nsOverflowAreas overflowAreas(rect, rect); 1.481 + 1.482 + if (!mIsRootContent) { 1.483 + nsIFrame *anonKid = GetFirstPrincipalChild(); 1.484 + overflowAreas.VisualOverflow().UnionRect( 1.485 + overflowAreas.VisualOverflow(), 1.486 + anonKid->GetVisualOverflowRect() + anonKid->GetPosition()); 1.487 + } 1.488 + 1.489 + return FinishAndStoreOverflow(overflowAreas, GetSize()); 1.490 +} 1.491 + 1.492 + 1.493 +//---------------------------------------------------------------------- 1.494 +// container methods 1.495 + 1.496 +/** 1.497 + * Used to paint/hit-test SVG when SVG display lists are disabled. 1.498 + */ 1.499 +class nsDisplayOuterSVG : public nsDisplayItem { 1.500 +public: 1.501 + nsDisplayOuterSVG(nsDisplayListBuilder* aBuilder, 1.502 + nsSVGOuterSVGFrame* aFrame) : 1.503 + nsDisplayItem(aBuilder, aFrame) { 1.504 + MOZ_COUNT_CTOR(nsDisplayOuterSVG); 1.505 + } 1.506 +#ifdef NS_BUILD_REFCNT_LOGGING 1.507 + virtual ~nsDisplayOuterSVG() { 1.508 + MOZ_COUNT_DTOR(nsDisplayOuterSVG); 1.509 + } 1.510 +#endif 1.511 + 1.512 + virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, 1.513 + HitTestState* aState, 1.514 + nsTArray<nsIFrame*> *aOutFrames) MOZ_OVERRIDE; 1.515 + virtual void Paint(nsDisplayListBuilder* aBuilder, 1.516 + nsRenderingContext* aCtx) MOZ_OVERRIDE; 1.517 + 1.518 + virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, 1.519 + const nsDisplayItemGeometry* aGeometry, 1.520 + nsRegion* aInvalidRegion) MOZ_OVERRIDE; 1.521 + 1.522 + NS_DISPLAY_DECL_NAME("SVGOuterSVG", TYPE_SVG_OUTER_SVG) 1.523 +}; 1.524 + 1.525 +void 1.526 +nsDisplayOuterSVG::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, 1.527 + HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) 1.528 +{ 1.529 + nsSVGOuterSVGFrame *outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(mFrame); 1.530 + nsRect rectAtOrigin = aRect - ToReferenceFrame(); 1.531 + nsRect thisRect(nsPoint(0,0), outerSVGFrame->GetSize()); 1.532 + if (!thisRect.Intersects(rectAtOrigin)) 1.533 + return; 1.534 + 1.535 + nsPoint rectCenter(rectAtOrigin.x + rectAtOrigin.width / 2, 1.536 + rectAtOrigin.y + rectAtOrigin.height / 2); 1.537 + 1.538 + nsSVGOuterSVGAnonChildFrame *anonKid = 1.539 + static_cast<nsSVGOuterSVGAnonChildFrame*>( 1.540 + outerSVGFrame->GetFirstPrincipalChild()); 1.541 + nsIFrame* frame = nsSVGUtils::HitTestChildren( 1.542 + anonKid, rectCenter + outerSVGFrame->GetPosition() - 1.543 + outerSVGFrame->GetContentRect().TopLeft()); 1.544 + if (frame) { 1.545 + aOutFrames->AppendElement(frame); 1.546 + } 1.547 +} 1.548 + 1.549 +void 1.550 +nsDisplayOuterSVG::Paint(nsDisplayListBuilder* aBuilder, 1.551 + nsRenderingContext* aContext) 1.552 +{ 1.553 +#if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING) 1.554 + PRTime start = PR_Now(); 1.555 +#endif 1.556 + 1.557 + // Create an SVGAutoRenderState so we can call SetPaintingToWindow on 1.558 + // it, but do so without changing the render mode: 1.559 + SVGAutoRenderState state(aContext, SVGAutoRenderState::GetRenderMode(aContext)); 1.560 + 1.561 + if (aBuilder->IsPaintingToWindow()) { 1.562 + state.SetPaintingToWindow(true); 1.563 + } 1.564 + 1.565 + nsRect viewportRect = 1.566 + mFrame->GetContentRectRelativeToSelf() + ToReferenceFrame(); 1.567 + 1.568 + nsRect clipRect = mVisibleRect.Intersect(viewportRect); 1.569 + 1.570 + nsIntRect contentAreaDirtyRect = 1.571 + (clipRect - viewportRect.TopLeft()). 1.572 + ToOutsidePixels(mFrame->PresContext()->AppUnitsPerDevPixel()); 1.573 + 1.574 + aContext->PushState(); 1.575 + aContext->Translate(viewportRect.TopLeft()); 1.576 + nsSVGUtils::PaintFrameWithEffects(aContext, &contentAreaDirtyRect, mFrame); 1.577 + aContext->PopState(); 1.578 + 1.579 + NS_ASSERTION(!aContext->ThebesContext()->HasError(), "Cairo in error state"); 1.580 + 1.581 +#if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING) 1.582 + PRTime end = PR_Now(); 1.583 + printf("SVG Paint Timing: %f ms\n", (end-start)/1000.0); 1.584 +#endif 1.585 +} 1.586 + 1.587 +static PLDHashOperator CheckForeignObjectInvalidatedArea(nsPtrHashKey<nsSVGForeignObjectFrame>* aEntry, void* aData) 1.588 +{ 1.589 + nsRegion* region = static_cast<nsRegion*>(aData); 1.590 + region->Or(*region, aEntry->GetKey()->GetInvalidRegion()); 1.591 + return PL_DHASH_NEXT; 1.592 +} 1.593 + 1.594 +nsRegion 1.595 +nsSVGOuterSVGFrame::FindInvalidatedForeignObjectFrameChildren(nsIFrame* aFrame) 1.596 +{ 1.597 + nsRegion result; 1.598 + if (mForeignObjectHash && mForeignObjectHash->Count()) { 1.599 + mForeignObjectHash->EnumerateEntries(CheckForeignObjectInvalidatedArea, &result); 1.600 + } 1.601 + return result; 1.602 +} 1.603 + 1.604 +void 1.605 +nsDisplayOuterSVG::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, 1.606 + const nsDisplayItemGeometry* aGeometry, 1.607 + nsRegion* aInvalidRegion) 1.608 +{ 1.609 + nsSVGOuterSVGFrame *frame = static_cast<nsSVGOuterSVGFrame*>(mFrame); 1.610 + frame->InvalidateSVG(frame->FindInvalidatedForeignObjectFrameChildren(frame)); 1.611 + 1.612 + nsRegion result = frame->GetInvalidRegion(); 1.613 + result.MoveBy(ToReferenceFrame()); 1.614 + frame->ClearInvalidRegion(); 1.615 + 1.616 + nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); 1.617 + aInvalidRegion->Or(*aInvalidRegion, result); 1.618 +} 1.619 + 1.620 +// helper 1.621 +static inline bool 1.622 +DependsOnIntrinsicSize(const nsIFrame* aEmbeddingFrame) 1.623 +{ 1.624 + const nsStylePosition *pos = aEmbeddingFrame->StylePosition(); 1.625 + const nsStyleCoord &width = pos->mWidth; 1.626 + const nsStyleCoord &height = pos->mHeight; 1.627 + 1.628 + // XXX it would be nice to know if the size of aEmbeddingFrame's containing 1.629 + // block depends on aEmbeddingFrame, then we'd know if we can return false 1.630 + // for eStyleUnit_Percent too. 1.631 + return !width.ConvertsToLength() || 1.632 + !height.ConvertsToLength(); 1.633 +} 1.634 + 1.635 +nsresult 1.636 +nsSVGOuterSVGFrame::AttributeChanged(int32_t aNameSpaceID, 1.637 + nsIAtom* aAttribute, 1.638 + int32_t aModType) 1.639 +{ 1.640 + if (aNameSpaceID == kNameSpaceID_None && 1.641 + !(GetStateBits() & (NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_NONDISPLAY))) { 1.642 + if (aAttribute == nsGkAtoms::viewBox || 1.643 + aAttribute == nsGkAtoms::preserveAspectRatio || 1.644 + aAttribute == nsGkAtoms::transform) { 1.645 + 1.646 + // make sure our cached transform matrix gets (lazily) updated 1.647 + mCanvasTM = nullptr; 1.648 + 1.649 + nsSVGUtils::NotifyChildrenOfSVGChange(GetFirstPrincipalChild(), 1.650 + aAttribute == nsGkAtoms::viewBox ? 1.651 + TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED : TRANSFORM_CHANGED); 1.652 + 1.653 + if (aAttribute != nsGkAtoms::transform) { 1.654 + static_cast<SVGSVGElement*>(mContent)->ChildrenOnlyTransformChanged(); 1.655 + } 1.656 + 1.657 + } else if (aAttribute == nsGkAtoms::width || 1.658 + aAttribute == nsGkAtoms::height) { 1.659 + 1.660 + // Don't call ChildrenOnlyTransformChanged() here, since we call it 1.661 + // under Reflow if the width/height actually changed. 1.662 + 1.663 + nsIFrame* embeddingFrame; 1.664 + if (IsRootOfReplacedElementSubDoc(&embeddingFrame) && embeddingFrame) { 1.665 + if (DependsOnIntrinsicSize(embeddingFrame)) { 1.666 + // Tell embeddingFrame's presShell it needs to be reflowed (which takes 1.667 + // care of reflowing us too). 1.668 + embeddingFrame->PresContext()->PresShell()-> 1.669 + FrameNeedsReflow(embeddingFrame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); 1.670 + } 1.671 + // else our width and height is overridden - don't reflow anything 1.672 + } else { 1.673 + // We are not embedded by reference, so our 'width' and 'height' 1.674 + // attributes are not overridden - we need to reflow. 1.675 + PresContext()->PresShell()-> 1.676 + FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); 1.677 + } 1.678 + } 1.679 + } 1.680 + 1.681 + return NS_OK; 1.682 +} 1.683 + 1.684 +//---------------------------------------------------------------------- 1.685 +// painting 1.686 + 1.687 +void 1.688 +nsSVGOuterSVGFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.689 + const nsRect& aDirtyRect, 1.690 + const nsDisplayListSet& aLists) 1.691 +{ 1.692 + if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) { 1.693 + return; 1.694 + } 1.695 + 1.696 + DisplayBorderBackgroundOutline(aBuilder, aLists); 1.697 + 1.698 + // Per-spec, we always clip root-<svg> even when 'overflow' has its initial 1.699 + // value of 'visible'. See also the "visual overflow" comments in Reflow. 1.700 + DisplayListClipState::AutoSaveRestore autoSR(aBuilder); 1.701 + if (mIsRootContent || 1.702 + StyleDisplay()->IsScrollableOverflow()) { 1.703 + autoSR.ClipContainingBlockDescendantsToContentBox(aBuilder, this); 1.704 + } 1.705 + 1.706 + if ((aBuilder->IsForEventDelivery() && 1.707 + NS_SVGDisplayListHitTestingEnabled()) || 1.708 + NS_SVGDisplayListPaintingEnabled()) { 1.709 + nsDisplayList *contentList = aLists.Content(); 1.710 + nsDisplayListSet set(contentList, contentList, contentList, 1.711 + contentList, contentList, contentList); 1.712 + BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, set); 1.713 + } else { 1.714 + aLists.Content()->AppendNewToTop( 1.715 + new (aBuilder) nsDisplayOuterSVG(aBuilder, this)); 1.716 + } 1.717 +} 1.718 + 1.719 +nsSplittableType 1.720 +nsSVGOuterSVGFrame::GetSplittableType() const 1.721 +{ 1.722 + return NS_FRAME_NOT_SPLITTABLE; 1.723 +} 1.724 + 1.725 +nsIAtom * 1.726 +nsSVGOuterSVGFrame::GetType() const 1.727 +{ 1.728 + return nsGkAtoms::svgOuterSVGFrame; 1.729 +} 1.730 + 1.731 +//---------------------------------------------------------------------- 1.732 +// nsISVGSVGFrame methods: 1.733 + 1.734 +void 1.735 +nsSVGOuterSVGFrame::NotifyViewportOrTransformChanged(uint32_t aFlags) 1.736 +{ 1.737 + NS_ABORT_IF_FALSE(aFlags && 1.738 + !(aFlags & ~(COORD_CONTEXT_CHANGED | TRANSFORM_CHANGED | 1.739 + FULL_ZOOM_CHANGED)), 1.740 + "Unexpected aFlags value"); 1.741 + 1.742 + // No point in doing anything when were not init'ed yet: 1.743 + if (!mViewportInitialized) { 1.744 + return; 1.745 + } 1.746 + 1.747 + SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent); 1.748 + 1.749 + if (aFlags & COORD_CONTEXT_CHANGED) { 1.750 + if (content->HasViewBoxRect()) { 1.751 + // Percentage lengths on children resolve against the viewBox rect so we 1.752 + // don't need to notify them of the viewport change, but the viewBox 1.753 + // transform will have changed, so we need to notify them of that instead. 1.754 + aFlags = TRANSFORM_CHANGED; 1.755 + } 1.756 + else if (content->ShouldSynthesizeViewBox()) { 1.757 + // In the case of a synthesized viewBox, the synthetic viewBox's rect 1.758 + // changes as the viewport changes. As a result we need to maintain the 1.759 + // COORD_CONTEXT_CHANGED flag. 1.760 + aFlags |= TRANSFORM_CHANGED; 1.761 + } 1.762 + else if (mCanvasTM && mCanvasTM->IsSingular()) { 1.763 + // A width/height of zero will result in us having a singular mCanvasTM 1.764 + // even when we don't have a viewBox. So we also want to recompute our 1.765 + // mCanvasTM for this width/height change even though we don't have a 1.766 + // viewBox. 1.767 + aFlags |= TRANSFORM_CHANGED; 1.768 + } 1.769 + } 1.770 + 1.771 + bool haveNonFulLZoomTransformChange = (aFlags & TRANSFORM_CHANGED); 1.772 + 1.773 + if (aFlags & FULL_ZOOM_CHANGED) { 1.774 + // Convert FULL_ZOOM_CHANGED to TRANSFORM_CHANGED: 1.775 + aFlags = (aFlags & ~FULL_ZOOM_CHANGED) | TRANSFORM_CHANGED; 1.776 + } 1.777 + 1.778 + if (aFlags & TRANSFORM_CHANGED) { 1.779 + // Make sure our canvas transform matrix gets (lazily) recalculated: 1.780 + mCanvasTM = nullptr; 1.781 + 1.782 + if (haveNonFulLZoomTransformChange && 1.783 + !(mState & NS_FRAME_IS_NONDISPLAY)) { 1.784 + uint32_t flags = (mState & NS_FRAME_IN_REFLOW) ? 1.785 + SVGSVGElement::eDuringReflow : 0; 1.786 + content->ChildrenOnlyTransformChanged(flags); 1.787 + } 1.788 + } 1.789 + 1.790 + nsSVGUtils::NotifyChildrenOfSVGChange(GetFirstPrincipalChild(), aFlags); 1.791 +} 1.792 + 1.793 +//---------------------------------------------------------------------- 1.794 +// nsISVGChildFrame methods: 1.795 + 1.796 +nsresult 1.797 +nsSVGOuterSVGFrame::PaintSVG(nsRenderingContext* aContext, 1.798 + const nsIntRect *aDirtyRect, 1.799 + nsIFrame* aTransformRoot) 1.800 +{ 1.801 + NS_ASSERTION(GetFirstPrincipalChild()->GetType() == 1.802 + nsGkAtoms::svgOuterSVGAnonChildFrame && 1.803 + !GetFirstPrincipalChild()->GetNextSibling(), 1.804 + "We should have a single, anonymous, child"); 1.805 + nsSVGOuterSVGAnonChildFrame *anonKid = 1.806 + static_cast<nsSVGOuterSVGAnonChildFrame*>(GetFirstPrincipalChild()); 1.807 + return anonKid->PaintSVG(aContext, aDirtyRect, aTransformRoot); 1.808 +} 1.809 + 1.810 +SVGBBox 1.811 +nsSVGOuterSVGFrame::GetBBoxContribution(const gfx::Matrix &aToBBoxUserspace, 1.812 + uint32_t aFlags) 1.813 +{ 1.814 + NS_ASSERTION(GetFirstPrincipalChild()->GetType() == 1.815 + nsGkAtoms::svgOuterSVGAnonChildFrame && 1.816 + !GetFirstPrincipalChild()->GetNextSibling(), 1.817 + "We should have a single, anonymous, child"); 1.818 + // We must defer to our child so that we don't include our 1.819 + // content->PrependLocalTransformsTo() transforms. 1.820 + nsSVGOuterSVGAnonChildFrame *anonKid = 1.821 + static_cast<nsSVGOuterSVGAnonChildFrame*>(GetFirstPrincipalChild()); 1.822 + return anonKid->GetBBoxContribution(aToBBoxUserspace, aFlags); 1.823 +} 1.824 + 1.825 +//---------------------------------------------------------------------- 1.826 +// nsSVGContainerFrame methods: 1.827 + 1.828 +gfxMatrix 1.829 +nsSVGOuterSVGFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot) 1.830 +{ 1.831 + if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY) && !aTransformRoot) { 1.832 + if ((aFor == FOR_PAINTING && NS_SVGDisplayListPaintingEnabled()) || 1.833 + (aFor == FOR_HIT_TESTING && NS_SVGDisplayListHitTestingEnabled())) { 1.834 + return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this); 1.835 + } 1.836 + } 1.837 + if (!mCanvasTM) { 1.838 + NS_ASSERTION(!aTransformRoot, "transform root will be ignored here"); 1.839 + SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent); 1.840 + 1.841 + float devPxPerCSSPx = 1.842 + 1.0f / PresContext()->AppUnitsToFloatCSSPixels( 1.843 + PresContext()->AppUnitsPerDevPixel()); 1.844 + 1.845 + gfxMatrix tm = content->PrependLocalTransformsTo( 1.846 + gfxMatrix().Scale(devPxPerCSSPx, devPxPerCSSPx)); 1.847 + mCanvasTM = new gfxMatrix(tm); 1.848 + } 1.849 + return *mCanvasTM; 1.850 +} 1.851 + 1.852 +//---------------------------------------------------------------------- 1.853 +// Implementation helpers 1.854 + 1.855 +bool 1.856 +nsSVGOuterSVGFrame::IsRootOfReplacedElementSubDoc(nsIFrame **aEmbeddingFrame) 1.857 +{ 1.858 + if (!mContent->GetParent()) { 1.859 + // Our content is the document element 1.860 + nsCOMPtr<nsISupports> container = PresContext()->GetContainerWeak(); 1.861 + nsCOMPtr<nsIDOMWindow> window = do_GetInterface(container); 1.862 + if (window) { 1.863 + nsCOMPtr<nsIDOMElement> frameElement; 1.864 + window->GetFrameElement(getter_AddRefs(frameElement)); 1.865 + nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(frameElement); 1.866 + if (olc) { 1.867 + // Our document is inside an HTML 'object', 'embed' or 'applet' element 1.868 + if (aEmbeddingFrame) { 1.869 + nsCOMPtr<nsIContent> element = do_QueryInterface(frameElement); 1.870 + *aEmbeddingFrame = element->GetPrimaryFrame(); 1.871 + NS_ASSERTION(*aEmbeddingFrame, "Yikes, no embedding frame!"); 1.872 + } 1.873 + return true; 1.874 + } 1.875 + } 1.876 + } 1.877 + if (aEmbeddingFrame) { 1.878 + *aEmbeddingFrame = nullptr; 1.879 + } 1.880 + return false; 1.881 +} 1.882 + 1.883 +bool 1.884 +nsSVGOuterSVGFrame::IsRootOfImage() 1.885 +{ 1.886 + if (!mContent->GetParent()) { 1.887 + // Our content is the document element 1.888 + nsIDocument* doc = mContent->GetCurrentDoc(); 1.889 + if (doc && doc->IsBeingUsedAsImage()) { 1.890 + // Our document is being used as an image 1.891 + return true; 1.892 + } 1.893 + } 1.894 + 1.895 + return false; 1.896 +} 1.897 + 1.898 +bool 1.899 +nsSVGOuterSVGFrame::VerticalScrollbarNotNeeded() const 1.900 +{ 1.901 + nsSVGLength2 &height = static_cast<SVGSVGElement*>(mContent)-> 1.902 + mLengthAttributes[SVGSVGElement::ATTR_HEIGHT]; 1.903 + return height.IsPercentage() && height.GetBaseValInSpecifiedUnits() <= 100; 1.904 +} 1.905 + 1.906 + 1.907 +//---------------------------------------------------------------------- 1.908 +// Implementation of nsSVGOuterSVGAnonChildFrame 1.909 + 1.910 +nsIFrame* 1.911 +NS_NewSVGOuterSVGAnonChildFrame(nsIPresShell* aPresShell, 1.912 + nsStyleContext* aContext) 1.913 +{ 1.914 + return new (aPresShell) nsSVGOuterSVGAnonChildFrame(aContext); 1.915 +} 1.916 + 1.917 +NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGAnonChildFrame) 1.918 + 1.919 +#ifdef DEBUG 1.920 +void 1.921 +nsSVGOuterSVGAnonChildFrame::Init(nsIContent* aContent, 1.922 + nsIFrame* aParent, 1.923 + nsIFrame* aPrevInFlow) 1.924 +{ 1.925 + NS_ABORT_IF_FALSE(aParent->GetType() == nsGkAtoms::svgOuterSVGFrame, 1.926 + "Unexpected parent"); 1.927 + nsSVGOuterSVGAnonChildFrameBase::Init(aContent, aParent, aPrevInFlow); 1.928 +} 1.929 +#endif 1.930 + 1.931 +nsIAtom * 1.932 +nsSVGOuterSVGAnonChildFrame::GetType() const 1.933 +{ 1.934 + return nsGkAtoms::svgOuterSVGAnonChildFrame; 1.935 +} 1.936 + 1.937 +bool 1.938 +nsSVGOuterSVGAnonChildFrame::HasChildrenOnlyTransform(gfx::Matrix *aTransform) const 1.939 +{ 1.940 + // We must claim our nsSVGOuterSVGFrame's children-only transforms as our own 1.941 + // so that the children we are used to wrap are transformed properly. 1.942 + 1.943 + SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent); 1.944 + 1.945 + bool hasTransform = content->HasChildrenOnlyTransform(); 1.946 + 1.947 + if (hasTransform && aTransform) { 1.948 + // Outer-<svg> doesn't use x/y, so we can pass eChildToUserSpace here. 1.949 + gfxMatrix identity; 1.950 + *aTransform = gfx::ToMatrix( 1.951 + content->PrependLocalTransformsTo(identity, 1.952 + nsSVGElement::eChildToUserSpace)); 1.953 + } 1.954 + 1.955 + return hasTransform; 1.956 +}