/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Main header first: #include"SVGOuterSVGFrame.h"
SVGOuterSVGFrame::SVGOuterSVGFrame(ComputedStyle* aStyle,
nsPresContext* aPresContext)
: SVGDisplayContainerFrame(aStyle, aPresContext, kClassID) { // Outer-<svg> has CSS layout, so remove this bit:
RemoveStateBits(NS_FRAME_SVG_LAYOUT);
AddStateBits(NS_FRAME_REFLOW_ROOT | NS_FRAME_FONT_INFLATION_CONTAINER |
NS_FRAME_FONT_INFLATION_FLOW_ROOT);
}
// The CSS Containment spec says that size-contained replaced elements must be // treated as having an intrinsic width and height of 0. That's applicable to // outer SVG frames, unless they're the outermost element (in which case // they're not really "replaced", and there's no outer context to contain sizes // from leaking into). Hence, we check for a parent element before we bother // testing for 'contain:size'. staticinline ContainSizeAxes ContainSizeAxesIfApplicable( const SVGOuterSVGFrame* aFrame) { if (!aFrame->GetContent()->GetParent()) { return ContainSizeAxes(false, false);
} return aFrame->GetContainSizeAxes();
}
// This should match ImageDocument::GetZoomLevel. float SVGOuterSVGFrame::ComputeFullZoom() const {
MOZ_ASSERT(mIsRootContent);
MOZ_ASSERT(!mIsInIframe); if (BrowsingContext* bc = PresContext()->Document()->GetBrowsingContext()) { return bc->FullZoom();
} return 1.0f;
}
void SVGOuterSVGFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
nsIFrame* aPrevInFlow) {
NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::svg), "Content is not an SVG 'svg' element!");
// Check for conditional processing attributes here rather than in // nsCSSFrameConstructor::FindSVGData because we want to avoid // simply giving failing outer <svg> elements an SVGContainerFrame. // We don't create other SVG frames if PassesConditionalProcessingTests // returns false, but since we do create SVGOuterSVGFrame frames we // prevent them from painting by [ab]use NS_FRAME_IS_NONDISPLAY. The // frame will be recreated via an nsChangeHint_ReconstructFrame restyle if // the value returned by PassesConditionalProcessingTests changes. auto* svg = static_cast<SVGSVGElement*>(aContent); if (!svg->PassesConditionalProcessingTests()) {
AddStateBits(NS_FRAME_IS_NONDISPLAY);
}
if (Maybe<nscoord> containISize =
ContainSizeAxesIfApplicable(this).ContainIntrinsicISize(*this)) {
result = *containISize;
} elseif (isize.IsPercentage()) { // If we are here, our inline size attribute is a percentage either // explicitly (via an attribute value) or implicitly (by being unset, which // is treated as 100%). The following if-condition, deciding to return // either the fallback intrinsic size or zero, is made to match blink and // webkit's behavior for webcompat. if (isize.IsExplicitlySet() || StylePosition()->ISize(wm).HasPercent() ||
!GetAspectRatio()) {
result = wm.IsVertical() ? kFallbackIntrinsicSize.height
: kFallbackIntrinsicSize.width;
} else {
result = nscoord(0);
}
} else {
result =
nsPresContext::CSSPixelsToAppUnits(isize.GetAnimValueWithZoom(svg)); if (result < 0) {
result = nscoord(0);
}
}
return result;
}
/* virtual */
IntrinsicSize SVGOuterSVGFrame::GetIntrinsicSize() { // XXXjwatt Note that here we want to return the CSS width/height if they're // specified and we're embedded inside an nsIObjectLoadingContent.
constauto containAxes = ContainSizeAxesIfApplicable(this); if (containAxes.IsBoth()) { // Intrinsic size of 'contain:size' replaced elements is determined by // contain-intrinsic-size, defaulting to 0x0. return FinishIntrinsicSize(containAxes, IntrinsicSize(0, 0));
}
if (!width.IsPercentage()) {
nscoord val =
nsPresContext::CSSPixelsToAppUnits(width.GetAnimValueWithZoom(content));
intrinsicSize.width.emplace(std::max(val, 0));
}
if (!height.IsPercentage()) {
nscoord val = nsPresContext::CSSPixelsToAppUnits(
height.GetAnimValueWithZoom(content));
intrinsicSize.height.emplace(std::max(val, 0));
}
auto* content = static_cast<SVGSVGElement*>(GetContent()); const SVGAnimatedLength& width =
content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH]; const SVGAnimatedLength& height =
content->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT]; if (!width.IsPercentage() && !height.IsPercentage()) { // Use width/height ratio only if // 1. it's not a degenerate ratio, and // 2. width and height are non-negative numbers. // Otherwise, we use the viewbox rect. // https://github.com/w3c/csswg-drafts/issues/6286 // Note width/height may have different units and therefore be // affected by zoom in different ways. constfloat w = width.GetAnimValueWithZoom(content); constfloat h = height.GetAnimValueWithZoom(content); if (w > 0.0f && h > 0.0f) { return AspectRatio::FromSize(w, h);
}
}
/* virtual */
nsIFrame::SizeComputationResult SVGOuterSVGFrame::ComputeSize(
gfxContext* aRenderingContext, WritingMode aWritingMode, const LogicalSize& aCBSize, nscoord aAvailableISize, const LogicalSize& aMargin, const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) { if (IsRootOfImage() || mIsInObjectOrEmbed) { // The embedding element has sized itself using the CSS replaced element // sizing rules, using our intrinsic dimensions as necessary. The SVG spec // says that the width and height of embedded SVG is overridden by the // width and height of the embedding element, so we just need to size to // the viewport that the embedding element has established for us. return {aCBSize, AspectRatioUsage::None};
}
if (mIsRootContent) { // We're the root of the outermost browsing context, so we need to scale // cbSize by the full-zoom so that SVGs with percentage width/height zoom:
NS_ASSERTION(aCBSize.ISize(aWritingMode) != NS_UNCONSTRAINEDSIZE &&
aCBSize.BSize(aWritingMode) != NS_UNCONSTRAINEDSIZE, "root should not have auto-width/height containing block");
if (!mIsInIframe) { // NOTE: We can't just use mFullZoom because this can run before Reflow() // updates it. constfloat zoom = ComputeFullZoom();
cbSize.ISize(aWritingMode) *= zoom;
cbSize.BSize(aWritingMode) *= zoom;
}
// We also need to honour the width and height attributes' default values // of 100% when we're the root of a browsing context. (GetIntrinsicSize() // doesn't report these since there's no such thing as a percentage // intrinsic size. Also note that explicit percentage values are mapped // into style, so the following isn't for them.)
const SVGAnimatedLength& width =
content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH]; if (width.IsPercentage()) {
MOZ_ASSERT(!intrinsicSize.width, "GetIntrinsicSize should have reported no intrinsic width"); float val = width.GetAnimValInSpecifiedUnits() / 100.0f;
intrinsicSize.width.emplace(std::max(val, 0.0f) *
cbSize.Width(aWritingMode));
}
const SVGAnimatedLength& height =
content->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT];
NS_ASSERTION(aCBSize.BSize(aWritingMode) != NS_UNCONSTRAINEDSIZE, "root should not have auto-height containing block"); if (height.IsPercentage()) {
MOZ_ASSERT(!intrinsicSize.height, "GetIntrinsicSize should have reported no intrinsic height"); float val = height.GetAnimValInSpecifiedUnits() / 100.0f;
intrinsicSize.height.emplace(std::max(val, 0.0f) *
cbSize.Height(aWritingMode));
}
MOZ_ASSERT(intrinsicSize.height && intrinsicSize.width, "We should have just handled the only situation where" "we lack an intrinsic height or width.");
}
uint32_t changeBits = 0; if (newViewportSize != svgElem->GetViewportSize()) { // When our viewport size changes, we may need to update the overflow rects // of our child frames. This is the case if: // // * We have a real/synthetic viewBox (a children-only transform), since // the viewBox transform will change as the viewport dimensions change. // // * We do not have a real/synthetic viewBox, but the last time we // reflowed (or the last time UpdateOverflow() was called) we did. // // We only handle the former case here, in which case we mark all our child // frames as dirty so that we reflow them below and update their overflow // rects. // // In the latter case, updating of overflow rects is handled for removal of // real viewBox (the viewBox attribute) in AttributeChanged. Synthetic // viewBox "removal" (e.g. a document references the same SVG via both an // <svg:image> and then as a CSS background image (a synthetic viewBox is // used when painting the former, but not when painting the latter)) is // handled in SVGSVGElement::FlushImageTransformInvalidation. // if (svgElem->HasViewBoxOrSyntheticViewBox()) {
nsIFrame* anonChild = PrincipalChildList().FirstChild();
anonChild->MarkSubtreeDirty(); for (nsIFrame* child : anonChild->PrincipalChildList()) {
child->MarkSubtreeDirty();
}
}
changeBits |= COORD_CONTEXT_CHANGED;
svgElem->SetViewportSize(newViewportSize);
} if (mIsRootContent && !mIsInIframe) { constauto oldZoom = mFullZoom;
mFullZoom = ComputeFullZoom(); if (oldZoom != mFullZoom) {
changeBits |= FULL_ZOOM_CHANGED;
}
} if (changeBits && !HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
NotifyViewportOrTransformChanged(changeBits);
}
// Now that we've marked the necessary children as dirty, call // ReflowSVG() or ReflowSVGNonDisplayText() on them, depending // on whether we are non-display.
mCallingReflowSVG = true; if (HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
ReflowSVGNonDisplayText(this);
} else { // Update the mRects and ink overflow rects of all our descendants, // including our anonymous wrapper kid:
anonKid->ReflowSVG();
MOZ_ASSERT(!anonKid->GetNextSibling(), "We should have one anonymous child frame wrapping our real " "children");
}
mCallingReflowSVG = false;
// Set our anonymous kid's offset from our border box:
anonKid->SetPosition(GetContentRectRelativeToSelf().TopLeft());
// Including our size in our overflow rects regardless of the value of // 'background', 'border', etc. makes sure that we usually (when we clip to // our content area) don't have to keep changing our overflow rects as our // descendants move about (see perf comment below). Including our size in our // scrollable overflow rect also makes sure that we scroll if we're too big // for our viewport. // // <svg> never allows scrolling to anything outside its mRect (only panning), // so we must always keep our scrollable overflow set to our size. // // With regards to ink overflow, we always clip root-<svg> (see our // BuildDisplayList method) regardless of the value of the 'overflow' // property since that is per-spec, even for the initial 'visible' value. For // that reason there's no point in adding descendant ink overflow to our // own when this frame is for a root-<svg>. That said, there's also a very // good performance reason for us wanting to avoid doing so. If we did, then // the frame's overflow would often change as descendants that are partially // or fully outside its rect moved (think animation on/off screen), and that // would cause us to do a full NS_FRAME_IS_DIRTY reflow and repaint of the // entire document tree each such move (see bug 875175). // // So it's only non-root outer-<svg> that has the ink overflow of its // descendants added to its own. (Note that the default user-agent style // sheet makes 'hidden' the default value for :not(root(svg)), so usually // FinishAndStoreOverflow will still clip this back to the frame's rect.) // // WARNING!! Keep UpdateBounds below in sync with whatever we do for our // overflow rects here! (Again, see bug 875175.) //
aDesiredSize.SetOverflowAreasToDesiredBounds();
// An outer SVG will be here as a nondisplay if it fails the conditional // processing test. In that case, we don't maintain its overflow. if (!HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) { if (!mIsRootContent) {
aDesiredSize.mOverflowAreas.InkOverflow().UnionRect(
aDesiredSize.mOverflowAreas.InkOverflow(),
anonKid->InkOverflowRect() + anonKid->GetPosition());
}
FinishAndStoreOverflow(&aDesiredSize);
}
if (aAttribute != nsGkAtoms::transform) { static_cast<SVGSVGElement*>(GetContent())
->ChildrenOnlyTransformChanged();
}
} if (aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height ||
aAttribute == nsGkAtoms::viewBox) { // Don't call ChildrenOnlyTransformChanged() here, since we call it // under Reflow if the width/height/viewBox actually changed.
MaybeSendIntrinsicSizeAndRatioToEmbedder();
if (!mIsInObjectOrEmbed) { // We are not embedded by reference, so our 'width' and 'height' // attributes are not overridden (and viewBox may influence our // intrinsic aspect ratio). We need to reflow.
PresShell()->FrameNeedsReflow( this, IntrinsicDirty::FrameAncestorsAndDescendants,
NS_FRAME_IS_DIRTY);
}
}
}
// Per-spec, we always clip root-<svg> even when 'overflow' has its initial // value of 'visible'. See also the "ink overflow" comments in Reflow.
DisplayListClipState::AutoSaveRestore autoSR(aBuilder); if (mIsRootContent || StyleDisplay()->IsScrollableOverflow()) {
autoSR.ClipContainingBlockDescendantsToContentBox(aBuilder, this);
visibleRect = visibleRect.Intersect(GetContentRectRelativeToSelf());
dirtyRect = dirtyRect.Intersect(GetContentRectRelativeToSelf());
}
auto* content = static_cast<SVGSVGElement*>(GetContent()); if (aFlags & COORD_CONTEXT_CHANGED) { if (content->HasViewBox()) { // Percentage lengths on children resolve against the viewBox rect so we // don't need to notify them of the viewport change, but the viewBox // transform will have changed, so we need to notify them of that instead.
aFlags = TRANSFORM_CHANGED;
} elseif (content->ShouldSynthesizeViewBox()) { // In the case of a synthesized viewBox, the synthetic viewBox's rect // changes as the viewport changes. As a result we need to maintain the // COORD_CONTEXT_CHANGED flag.
aFlags |= TRANSFORM_CHANGED;
} elseif (mCanvasTM && mCanvasTM->IsSingular()) { // A width/height of zero will result in us having a singular mCanvasTM // even when we don't have a viewBox. So we also want to recompute our // mCanvasTM for this width/height change even though we don't have a // viewBox.
aFlags |= TRANSFORM_CHANGED;
}
}
void SVGOuterSVGFrame::PaintSVG(gfxContext& aContext, const gfxMatrix& aTransform,
imgDrawingParams& aImgParams) {
NS_ASSERTION(
PrincipalChildList().FirstChild()->IsSVGOuterSVGAnonChildFrame() &&
!PrincipalChildList().FirstChild()->GetNextSibling(), "We should have a single, anonymous, child"); auto* anonKid = static_cast<SVGOuterSVGAnonChildFrame*>(
PrincipalChildList().FirstChild());
anonKid->PaintSVG(aContext, aTransform, aImgParams);
}
SVGBBox SVGOuterSVGFrame::GetBBoxContribution( const gfx::Matrix& aToBBoxUserspace, uint32_t aFlags) {
NS_ASSERTION(
PrincipalChildList().FirstChild()->IsSVGOuterSVGAnonChildFrame() &&
!PrincipalChildList().FirstChild()->GetNextSibling(), "We should have a single, anonymous, child"); // We must defer to our child so that we don't include our // content->ChildToUserSpaceTransform() transform. auto* anonKid = static_cast<SVGOuterSVGAnonChildFrame*>(
PrincipalChildList().FirstChild()); return anonKid->GetBBoxContribution(aToBBoxUserspace, aFlags);
}
bool SVGOuterSVGFrame::IsRootOfImage() { if (!mContent->GetParent()) { // Our content is the document element
Document* doc = mContent->GetUncomposedDoc(); if (doc && doc->IsBeingUsedAsImage()) { // Our document is being used as an image returntrue;
}
}
if (aOldComputedStyle->StylePosition()->mAspectRatio !=
StylePosition()->mAspectRatio) { // Our aspect-ratio property value changed, and an embedding <object> or // <embed> might care about that.
MaybeSendIntrinsicSizeAndRatioToEmbedder();
}
}
void SVGOuterSVGFrame::Destroy(DestroyContext& aContext) { // This handles both the case when the root <svg> element is made display:none // (and thus loses its intrinsic size and aspect ratio), and when the frame // is navigated elsewhere & we need to reset parent <object>/<embed>'s // recorded intrinsic size/ratio values.
MaybeSendIntrinsicSizeAndRatioToEmbedder(Nothing(), Nothing());
SVGDisplayContainerFrame::Destroy(aContext);
}
} // namespace mozilla
//---------------------------------------------------------------------- // Implementation of SVGOuterSVGAnonChildFrame
void SVGOuterSVGAnonChildFrame::BuildDisplayList(
nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) { // Wrap our contents into an nsDisplaySVGWrapper. // We wrap this frame instead of the SVGOuterSVGFrame so that the wrapper // doesn't contain the <svg> element's CSS styles, like backgrounds or // borders. Creating the nsDisplaySVGWrapper here also means that it'll be // inside the nsDisplayTransform for our viewbox transform. The // nsDisplaySVGWrapper's reference frame is this frame, because this frame // always returns true from IsSVGTransformed.
nsDisplayList newList(aBuilder);
nsDisplayListSet set(&newList, &newList, &newList, &newList, &newList,
&newList);
BuildDisplayListForNonBlockChildren(aBuilder, set);
aLists.Content()->AppendNewToTop<nsDisplaySVGWrapper>(aBuilder, this,
&newList);
}
bool SVGOuterSVGFrame::HasChildrenOnlyTransform(Matrix* aTransform) const { auto* content = static_cast<SVGSVGElement*>(GetContent()); if (!content->HasChildrenOnlyTransform()) { returnfalse;
} if (aTransform) { // Outer-<svg> doesn't use x/y, so we can use the child-to-user-space // transform here.
*aTransform = gfx::ToMatrix(content->ChildToUserSpaceTransform());
} returntrue;
}
bool SVGOuterSVGAnonChildFrame::DoGetParentSVGTransforms(
Matrix* aFromParentTransform) const { // We want this frame to be a reference frame. An easy way to achieve that is // to always return true from this method, even for identity transforms. // This frame being a reference frame ensures that the offset between this // <svg> element and the parent reference frame is completely absorbed by the // nsDisplayTransform that's created for this frame, and that this offset does // not affect our descendants' transforms. Consequently, if the <svg> element // moves, e.g. during scrolling, the transform matrices of our contents are // unaffected. This simplifies invalidation. // TODO(emilio): Is the comment above true for WebRender nowadays?
SVGUtils::GetParentSVGTransforms(this, aFromParentTransform); returntrue;
}
} // namespace mozilla
¤ Dauer der Verarbeitung: 0.36 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.