1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* 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 "nsSVGOuterSVGFrame.h"
- // Keep others in (case-insensitive) order:
- #include "nsDisplayList.h"
- #include "nsIDocument.h"
- #include "nsIDOMHTMLIFrameElement.h"
- #include "nsIInterfaceRequestorUtils.h"
- #include "nsIObjectLoadingContent.h"
- #include "nsRenderingContext.h"
- #include "nsSVGIntegrationUtils.h"
- #include "nsSVGForeignObjectFrame.h"
- #include "mozilla/dom/SVGSVGElement.h"
- #include "mozilla/dom/SVGViewElement.h"
- #include "nsSubDocumentFrame.h"
- #include "gfxMatrix.h"
- using namespace mozilla;
- using namespace mozilla::dom;
- using namespace mozilla::gfx;
- using namespace mozilla::image;
- //----------------------------------------------------------------------
- // Implementation helpers
- void
- nsSVGOuterSVGFrame::RegisterForeignObject(nsSVGForeignObjectFrame* aFrame)
- {
- NS_ASSERTION(aFrame, "Who on earth is calling us?!");
- if (!mForeignObjectHash) {
- mForeignObjectHash = new nsTHashtable<nsPtrHashKey<nsSVGForeignObjectFrame> >();
- }
- NS_ASSERTION(!mForeignObjectHash->GetEntry(aFrame),
- "nsSVGForeignObjectFrame already registered!");
- mForeignObjectHash->PutEntry(aFrame);
- NS_ASSERTION(mForeignObjectHash->GetEntry(aFrame),
- "Failed to register nsSVGForeignObjectFrame!");
- }
- void
- nsSVGOuterSVGFrame::UnregisterForeignObject(nsSVGForeignObjectFrame* aFrame)
- {
- NS_ASSERTION(aFrame, "Who on earth is calling us?!");
- NS_ASSERTION(mForeignObjectHash && mForeignObjectHash->GetEntry(aFrame),
- "nsSVGForeignObjectFrame not in registry!");
- return mForeignObjectHash->RemoveEntry(aFrame);
- }
- //----------------------------------------------------------------------
- // Implementation
- nsContainerFrame*
- NS_NewSVGOuterSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
- {
- return new (aPresShell) nsSVGOuterSVGFrame(aContext);
- }
- NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGFrame)
- nsSVGOuterSVGFrame::nsSVGOuterSVGFrame(nsStyleContext* aContext)
- : nsSVGDisplayContainerFrame(aContext)
- , mFullZoom(aContext->PresContext()->GetFullZoom())
- , mViewportInitialized(false)
- , mIsRootContent(false)
- {
- // Outer-<svg> has CSS layout, so remove this bit:
- RemoveStateBits(NS_FRAME_SVG_LAYOUT);
- }
- // helper
- static inline bool
- DependsOnIntrinsicSize(const nsIFrame* aEmbeddingFrame)
- {
- const nsStylePosition *pos = aEmbeddingFrame->StylePosition();
- const nsStyleCoord &width = pos->mWidth;
- const nsStyleCoord &height = pos->mHeight;
- // XXX it would be nice to know if the size of aEmbeddingFrame's containing
- // block depends on aEmbeddingFrame, then we'd know if we can return false
- // for eStyleUnit_Percent too.
- return !width.ConvertsToLength() ||
- !height.ConvertsToLength();
- }
- void
- nsSVGOuterSVGFrame::Init(nsIContent* aContent,
- nsContainerFrame* aParent,
- nsIFrame* aPrevInFlow)
- {
- NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::svg),
- "Content is not an SVG 'svg' element!");
- AddStateBits(NS_STATE_IS_OUTER_SVG |
- NS_FRAME_FONT_INFLATION_CONTAINER |
- NS_FRAME_FONT_INFLATION_FLOW_ROOT);
- // Check for conditional processing attributes here rather than in
- // nsCSSFrameConstructor::FindSVGData because we want to avoid
- // simply giving failing outer <svg> elements an nsSVGContainerFrame.
- // We don't create other SVG frames if PassesConditionalProcessingTests
- // returns false, but since we do create nsSVGOuterSVGFrame 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.
- SVGSVGElement *svg = static_cast<SVGSVGElement*>(aContent);
- if (!svg->PassesConditionalProcessingTests()) {
- AddStateBits(NS_FRAME_IS_NONDISPLAY);
- }
- nsSVGDisplayContainerFrame::Init(aContent, aParent, aPrevInFlow);
- nsIDocument* doc = mContent->GetUncomposedDoc();
- if (doc) {
- // we only care about our content's zoom and pan values if it's the root element
- if (doc->GetRootElement() == mContent) {
- mIsRootContent = true;
- nsIFrame* embeddingFrame;
- if (IsRootOfReplacedElementSubDoc(&embeddingFrame) && embeddingFrame) {
- if (MOZ_UNLIKELY(!embeddingFrame->HasAllStateBits(NS_FRAME_IS_DIRTY)) &&
- DependsOnIntrinsicSize(embeddingFrame)) {
- // Looks like this document is loading after the embedding element
- // has had its first reflow, and that its size depends on our
- // intrinsic size. We need it to resize itself to use our (now
- // available) intrinsic size:
- embeddingFrame->PresContext()->PresShell()->
- FrameNeedsReflow(embeddingFrame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
- }
- }
- }
- }
- }
- //----------------------------------------------------------------------
- // nsQueryFrame methods
- NS_QUERYFRAME_HEAD(nsSVGOuterSVGFrame)
- NS_QUERYFRAME_ENTRY(nsISVGSVGFrame)
- NS_QUERYFRAME_TAIL_INHERITING(nsSVGDisplayContainerFrame)
- //----------------------------------------------------------------------
- // nsIFrame methods
- //----------------------------------------------------------------------
- // reflowing
- /* virtual */ nscoord
- nsSVGOuterSVGFrame::GetMinISize(nsRenderingContext *aRenderingContext)
- {
- nscoord result;
- DISPLAY_MIN_WIDTH(this, result);
- result = nscoord(0);
- return result;
- }
- /* virtual */ nscoord
- nsSVGOuterSVGFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
- {
- nscoord result;
- DISPLAY_PREF_WIDTH(this, result);
- SVGSVGElement *svg = static_cast<SVGSVGElement*>(mContent);
- WritingMode wm = GetWritingMode();
- const nsSVGLength2& isize = wm.IsVertical()
- ? svg->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT]
- : svg->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
- if (isize.IsPercentage()) {
- // It looks like our containing block's isize may depend on our isize. In
- // that case our behavior is undefined according to CSS 2.1 section 10.3.2.
- // As a last resort, we'll fall back to returning zero.
- result = nscoord(0);
- // Returning zero may be unhelpful, however, as it leads to unexpected
- // disappearance of %-sized SVGs in orthogonal contexts, where our
- // containing block wants to shrink-wrap. So let's look for an ancestor
- // with non-zero size in this dimension, and use that as a (somewhat
- // arbitrary) result instead.
- nsIFrame *parent = GetParent();
- while (parent) {
- nscoord parentISize = parent->GetLogicalSize(wm).ISize(wm);
- if (parentISize > 0 && parentISize != NS_UNCONSTRAINEDSIZE) {
- result = parentISize;
- break;
- }
- parent = parent->GetParent();
- }
- } else {
- result = nsPresContext::CSSPixelsToAppUnits(isize.GetAnimValue(svg));
- if (result < 0) {
- result = nscoord(0);
- }
- }
- return result;
- }
- /* virtual */ IntrinsicSize
- nsSVGOuterSVGFrame::GetIntrinsicSize()
- {
- // XXXjwatt Note that here we want to return the CSS width/height if they're
- // specified and we're embedded inside an nsIObjectLoadingContent.
- IntrinsicSize intrinsicSize;
- SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent);
- nsSVGLength2 &width = content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
- nsSVGLength2 &height = content->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT];
- if (!width.IsPercentage()) {
- nscoord val = nsPresContext::CSSPixelsToAppUnits(width.GetAnimValue(content));
- if (val < 0) val = 0;
- intrinsicSize.width.SetCoordValue(val);
- }
- if (!height.IsPercentage()) {
- nscoord val = nsPresContext::CSSPixelsToAppUnits(height.GetAnimValue(content));
- if (val < 0) val = 0;
- intrinsicSize.height.SetCoordValue(val);
- }
- return intrinsicSize;
- }
- /* virtual */ AspectRatio
- nsSVGOuterSVGFrame::GetIntrinsicRatio()
- {
- // When 'contain: size' is implemented, make sure to check for it.
- /*
- if (this->GetContent->GetParent() && this->StyleDisplay()->IsContainSize()) {
- return AspectRatio();
- }
- */
- // We only have an intrinsic size/ratio if our width and height attributes
- // are both specified and set to non-percentage values, or we have a viewBox
- // rect: http://www.w3.org/TR/SVGMobile12/coords.html#IntrinsicSizing
- // Unfortunately we have to return the ratio as two nscoords whereas what
- // we have are two floats. Using app units allows for some floating point
- // values to work but really small or large numbers will fail.
- SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent);
- nsSVGLength2 &width = content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
- nsSVGLength2 &height = content->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT];
- if (!width.IsPercentage() && !height.IsPercentage()) {
- return AspectRatio::FromSize(width.GetAnimValue(content),
- height.GetAnimValue(content));
- }
- SVGViewElement* viewElement = content->GetCurrentViewElement();
- const nsSVGViewBoxRect* viewbox = nullptr;
- // The logic here should match HasViewBox().
- if (viewElement && viewElement->mViewBox.HasRect()) {
- viewbox = &viewElement->mViewBox.GetAnimValue();
- } else if (content->mViewBox.HasRect()) {
- viewbox = &content->mViewBox.GetAnimValue();
- }
- if (viewbox) {
- return AspectRatio::FromSize(viewbox->width, viewbox->height);
- }
- return nsSVGDisplayContainerFrame::GetIntrinsicRatio();
- }
- /* virtual */
- LogicalSize
- nsSVGOuterSVGFrame::ComputeSize(nsRenderingContext *aRenderingContext,
- WritingMode aWM,
- const LogicalSize& aCBSize,
- nscoord aAvailableISize,
- const LogicalSize& aMargin,
- const LogicalSize& aBorder,
- const LogicalSize& aPadding,
- ComputeSizeFlags aFlags)
- {
- if (IsRootOfImage() || IsRootOfReplacedElementSubDoc()) {
- // 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;
- }
- LogicalSize cbSize = aCBSize;
- IntrinsicSize intrinsicSize = GetIntrinsicSize();
- if (!mContent->GetParent()) {
- // 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(aWM) != NS_AUTOHEIGHT &&
- aCBSize.BSize(aWM) != NS_AUTOHEIGHT,
- "root should not have auto-width/height containing block");
- cbSize.ISize(aWM) *= PresContext()->GetFullZoom();
- cbSize.BSize(aWM) *= PresContext()->GetFullZoom();
- // 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.)
- SVGSVGElement* content = static_cast<SVGSVGElement*>(mContent);
- nsSVGLength2 &width =
- content->mLengthAttributes[SVGSVGElement::ATTR_WIDTH];
- if (width.IsPercentage()) {
- MOZ_ASSERT(intrinsicSize.width.GetUnit() == eStyleUnit_None,
- "GetIntrinsicSize should have reported no intrinsic width");
- float val = width.GetAnimValInSpecifiedUnits() / 100.0f;
- if (val < 0.0f) val = 0.0f;
- intrinsicSize.width.SetCoordValue(val * cbSize.Width(aWM));
- }
- nsSVGLength2 &height =
- content->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT];
- NS_ASSERTION(aCBSize.BSize(aWM) != NS_AUTOHEIGHT,
- "root should not have auto-height containing block");
- if (height.IsPercentage()) {
- MOZ_ASSERT(intrinsicSize.height.GetUnit() == eStyleUnit_None,
- "GetIntrinsicSize should have reported no intrinsic height");
- float val = height.GetAnimValInSpecifiedUnits() / 100.0f;
- if (val < 0.0f) val = 0.0f;
- intrinsicSize.height.SetCoordValue(val * cbSize.Height(aWM));
- }
- MOZ_ASSERT(intrinsicSize.height.GetUnit() == eStyleUnit_Coord &&
- intrinsicSize.width.GetUnit() == eStyleUnit_Coord,
- "We should have just handled the only situation where"
- "we lack an intrinsic height or width.");
- }
- return ComputeSizeWithIntrinsicDimensions(aRenderingContext, aWM,
- intrinsicSize, GetIntrinsicRatio(),
- cbSize, aMargin, aBorder, aPadding,
- aFlags);
- }
- void
- nsSVGOuterSVGFrame::Reflow(nsPresContext* aPresContext,
- ReflowOutput& aDesiredSize,
- const ReflowInput& aReflowInput,
- nsReflowStatus& aStatus)
- {
- MarkInReflow();
- DO_GLOBAL_REFLOW_COUNT("nsSVGOuterSVGFrame");
- DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
- NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
- ("enter nsSVGOuterSVGFrame::Reflow: availSize=%d,%d",
- aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
- NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
- aStatus = NS_FRAME_COMPLETE;
- aDesiredSize.Width() = aReflowInput.ComputedWidth() +
- aReflowInput.ComputedPhysicalBorderPadding().LeftRight();
- aDesiredSize.Height() = aReflowInput.ComputedHeight() +
- aReflowInput.ComputedPhysicalBorderPadding().TopBottom();
- NS_ASSERTION(!GetPrevInFlow(), "SVG can't currently be broken across pages.");
- SVGSVGElement *svgElem = static_cast<SVGSVGElement*>(mContent);
- nsSVGOuterSVGAnonChildFrame *anonKid =
- static_cast<nsSVGOuterSVGAnonChildFrame*>(PrincipalChildList().FirstChild());
- if (mState & NS_FRAME_FIRST_REFLOW) {
- // Initialize
- svgElem->UpdateHasChildrenOnlyTransform();
- }
- // If our SVG viewport has changed, update our content and notify.
- // http://www.w3.org/TR/SVG11/coords.html#ViewportSpace
- svgFloatSize newViewportSize(
- nsPresContext::AppUnitsToFloatCSSPixels(aReflowInput.ComputedWidth()),
- nsPresContext::AppUnitsToFloatCSSPixels(aReflowInput.ComputedHeight()));
- svgFloatSize oldViewportSize = svgElem->GetViewportSize();
- uint32_t changeBits = 0;
- if (newViewportSize != oldViewportSize) {
- // 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->AddStateBits(NS_FRAME_IS_DIRTY);
- for (nsIFrame* child : anonChild->PrincipalChildList()) {
- child->AddStateBits(NS_FRAME_IS_DIRTY);
- }
- }
- changeBits |= COORD_CONTEXT_CHANGED;
- svgElem->SetViewportSize(newViewportSize);
- }
- if (mFullZoom != PresContext()->GetFullZoom()) {
- changeBits |= FULL_ZOOM_CHANGED;
- mFullZoom = PresContext()->GetFullZoom();
- }
- if (changeBits) {
- NotifyViewportOrTransformChanged(changeBits);
- }
- mViewportInitialized = true;
- // 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 (GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
- ReflowSVGNonDisplayText(this);
- } else {
- // Update the mRects and visual overflow rects of all our descendants,
- // including our anonymous wrapper kid:
- anonKid->AddStateBits(mState & NS_FRAME_IS_DIRTY);
- 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 visual 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 visual 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 visual 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();
- if (!mIsRootContent) {
- aDesiredSize.mOverflowAreas.VisualOverflow().UnionRect(
- aDesiredSize.mOverflowAreas.VisualOverflow(),
- anonKid->GetVisualOverflowRect() + anonKid->GetPosition());
- }
- FinishAndStoreOverflow(&aDesiredSize);
- NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
- ("exit nsSVGOuterSVGFrame::Reflow: size=%d,%d",
- aDesiredSize.Width(), aDesiredSize.Height()));
- NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
- }
- void
- nsSVGOuterSVGFrame::DidReflow(nsPresContext* aPresContext,
- const ReflowInput* aReflowInput,
- nsDidReflowStatus aStatus)
- {
- nsSVGDisplayContainerFrame::DidReflow(aPresContext,aReflowInput,aStatus);
- // Make sure elements styled by :hover get updated if script/animation moves
- // them under or out from under the pointer:
- PresContext()->PresShell()->SynthesizeMouseMove(false);
- }
- /* virtual */ void
- nsSVGOuterSVGFrame::UnionChildOverflow(nsOverflowAreas& aOverflowAreas)
- {
- // See the comments in Reflow above.
- // WARNING!! Keep this in sync with Reflow above!
- if (!mIsRootContent) {
- nsIFrame *anonKid = PrincipalChildList().FirstChild();
- aOverflowAreas.VisualOverflow().UnionRect(
- aOverflowAreas.VisualOverflow(),
- anonKid->GetVisualOverflowRect() + anonKid->GetPosition());
- }
- }
- //----------------------------------------------------------------------
- // container methods
- /**
- * Used to paint/hit-test SVG when SVG display lists are disabled.
- */
- class nsDisplayOuterSVG : public nsDisplayItem {
- public:
- nsDisplayOuterSVG(nsDisplayListBuilder* aBuilder,
- nsSVGOuterSVGFrame* aFrame) :
- nsDisplayItem(aBuilder, aFrame) {
- MOZ_COUNT_CTOR(nsDisplayOuterSVG);
- }
- #ifdef NS_BUILD_REFCNT_LOGGING
- virtual ~nsDisplayOuterSVG() {
- MOZ_COUNT_DTOR(nsDisplayOuterSVG);
- }
- #endif
- virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
- HitTestState* aState,
- nsTArray<nsIFrame*> *aOutFrames) override;
- virtual void Paint(nsDisplayListBuilder* aBuilder,
- nsRenderingContext* aCtx) override;
- virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
- const nsDisplayItemGeometry* aGeometry,
- nsRegion* aInvalidRegion) override;
- nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override
- {
- return new nsDisplayItemGenericImageGeometry(this, aBuilder);
- }
- NS_DISPLAY_DECL_NAME("SVGOuterSVG", TYPE_SVG_OUTER_SVG)
- };
- void
- nsDisplayOuterSVG::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
- HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
- {
- nsSVGOuterSVGFrame *outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(mFrame);
- nsPoint refFrameToContentBox =
- ToReferenceFrame() + outerSVGFrame->GetContentRectRelativeToSelf().TopLeft();
- nsPoint pointRelativeToContentBox =
- nsPoint(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2) -
- refFrameToContentBox;
- gfxPoint svgViewportRelativePoint =
- gfxPoint(pointRelativeToContentBox.x, pointRelativeToContentBox.y) /
- outerSVGFrame->PresContext()->AppUnitsPerCSSPixel();
- nsSVGOuterSVGAnonChildFrame *anonKid =
- static_cast<nsSVGOuterSVGAnonChildFrame*>(
- outerSVGFrame->PrincipalChildList().FirstChild());
- nsIFrame* frame =
- nsSVGUtils::HitTestChildren(anonKid, svgViewportRelativePoint);
- if (frame) {
- aOutFrames->AppendElement(frame);
- }
- }
- void
- nsDisplayOuterSVG::Paint(nsDisplayListBuilder* aBuilder,
- nsRenderingContext* aContext)
- {
- #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
- PRTime start = PR_Now();
- #endif
- // Create an SVGAutoRenderState so we can call SetPaintingToWindow on it.
- SVGAutoRenderState state(aContext->GetDrawTarget());
- if (aBuilder->IsPaintingToWindow()) {
- state.SetPaintingToWindow(true);
- }
- nsRect viewportRect =
- mFrame->GetContentRectRelativeToSelf() + ToReferenceFrame();
- nsRect clipRect = mVisibleRect.Intersect(viewportRect);
- uint32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
- nsIntRect contentAreaDirtyRect =
- (clipRect - viewportRect.TopLeft()).
- ToOutsidePixels(appUnitsPerDevPixel);
- gfxPoint devPixelOffset =
- nsLayoutUtils::PointToGfxPoint(viewportRect.TopLeft(), appUnitsPerDevPixel);
- aContext->ThebesContext()->Save();
- // We include the offset of our frame and a scale from device pixels to user
- // units (i.e. CSS px) in the matrix that we pass to our children):
- gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(mFrame) *
- gfxMatrix::Translation(devPixelOffset);
- DrawResult result =
- nsSVGUtils::PaintFrameWithEffects(mFrame, *aContext->ThebesContext(), tm,
- &contentAreaDirtyRect);
- nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
- aContext->ThebesContext()->Restore();
- #if defined(DEBUG) && defined(SVG_DEBUG_PAINT_TIMING)
- PRTime end = PR_Now();
- printf("SVG Paint Timing: %f ms\n", (end-start)/1000.0);
- #endif
- }
- nsRegion
- nsSVGOuterSVGFrame::FindInvalidatedForeignObjectFrameChildren(nsIFrame* aFrame)
- {
- nsRegion result;
- if (mForeignObjectHash && mForeignObjectHash->Count()) {
- for (auto it = mForeignObjectHash->Iter(); !it.Done(); it.Next()) {
- result.Or(result, it.Get()->GetKey()->GetInvalidRegion());
- }
- }
- return result;
- }
- void
- nsDisplayOuterSVG::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
- const nsDisplayItemGeometry* aGeometry,
- nsRegion* aInvalidRegion)
- {
- nsSVGOuterSVGFrame *frame = static_cast<nsSVGOuterSVGFrame*>(mFrame);
- frame->InvalidateSVG(frame->FindInvalidatedForeignObjectFrameChildren(frame));
- nsRegion result = frame->GetInvalidRegion();
- result.MoveBy(ToReferenceFrame());
- frame->ClearInvalidRegion();
- nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
- aInvalidRegion->Or(*aInvalidRegion, result);
- auto geometry =
- static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
- if (aBuilder->ShouldSyncDecodeImages() &&
- geometry->ShouldInvalidateToSyncDecodeImages()) {
- bool snap;
- aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
- }
- }
- nsresult
- nsSVGOuterSVGFrame::AttributeChanged(int32_t aNameSpaceID,
- nsIAtom* aAttribute,
- int32_t aModType)
- {
- if (aNameSpaceID == kNameSpaceID_None &&
- !(GetStateBits() & (NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_NONDISPLAY))) {
- if (aAttribute == nsGkAtoms::viewBox ||
- aAttribute == nsGkAtoms::preserveAspectRatio ||
- aAttribute == nsGkAtoms::transform) {
- // make sure our cached transform matrix gets (lazily) updated
- mCanvasTM = nullptr;
- nsSVGUtils::NotifyChildrenOfSVGChange(PrincipalChildList().FirstChild(),
- aAttribute == nsGkAtoms::viewBox ?
- TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED : TRANSFORM_CHANGED);
- if (aAttribute != nsGkAtoms::transform) {
- static_cast<SVGSVGElement*>(mContent)->ChildrenOnlyTransformChanged();
- }
- } else if (aAttribute == nsGkAtoms::width ||
- aAttribute == nsGkAtoms::height) {
- // Don't call ChildrenOnlyTransformChanged() here, since we call it
- // under Reflow if the width/height actually changed.
- nsIFrame* embeddingFrame;
- if (IsRootOfReplacedElementSubDoc(&embeddingFrame) && embeddingFrame) {
- if (DependsOnIntrinsicSize(embeddingFrame)) {
- // Tell embeddingFrame's presShell it needs to be reflowed (which takes
- // care of reflowing us too).
- embeddingFrame->PresContext()->PresShell()->
- FrameNeedsReflow(embeddingFrame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
- }
- // else our width and height is overridden - don't reflow anything
- } else {
- // We are not embedded by reference, so our 'width' and 'height'
- // attributes are not overridden - we need to reflow.
- PresContext()->PresShell()->
- FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
- }
- }
- }
- return NS_OK;
- }
- //----------------------------------------------------------------------
- // painting
- void
- nsSVGOuterSVGFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
- const nsDisplayListSet& aLists)
- {
- if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
- return;
- }
- DisplayBorderBackgroundOutline(aBuilder, aLists);
- // Per-spec, we always clip root-<svg> even when 'overflow' has its initial
- // value of 'visible'. See also the "visual overflow" comments in Reflow.
- DisplayListClipState::AutoSaveRestore autoSR(aBuilder);
- if (mIsRootContent ||
- StyleDisplay()->IsScrollableOverflow()) {
- autoSR.ClipContainingBlockDescendantsToContentBox(aBuilder, this);
- }
- if ((aBuilder->IsForEventDelivery() &&
- NS_SVGDisplayListHitTestingEnabled()) ||
- (!aBuilder->IsForEventDelivery() &&
- NS_SVGDisplayListPaintingEnabled())) {
- nsDisplayList *contentList = aLists.Content();
- nsDisplayListSet set(contentList, contentList, contentList,
- contentList, contentList, contentList);
- BuildDisplayListForNonBlockChildren(aBuilder, set);
- } else if (IsVisibleForPainting(aBuilder) || !aBuilder->IsForPainting()) {
- aLists.Content()->AppendNewToTop(
- new (aBuilder) nsDisplayOuterSVG(aBuilder, this));
- }
- }
- nsSplittableType
- nsSVGOuterSVGFrame::GetSplittableType() const
- {
- return NS_FRAME_NOT_SPLITTABLE;
- }
- nsIAtom *
- nsSVGOuterSVGFrame::GetType() const
- {
- return nsGkAtoms::svgOuterSVGFrame;
- }
- //----------------------------------------------------------------------
- // nsISVGSVGFrame methods:
- void
- nsSVGOuterSVGFrame::NotifyViewportOrTransformChanged(uint32_t aFlags)
- {
- MOZ_ASSERT(aFlags &&
- !(aFlags & ~(COORD_CONTEXT_CHANGED | TRANSFORM_CHANGED |
- FULL_ZOOM_CHANGED)),
- "Unexpected aFlags value");
- // No point in doing anything when were not init'ed yet:
- if (!mViewportInitialized) {
- return;
- }
- SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent);
- if (aFlags & COORD_CONTEXT_CHANGED) {
- if (content->HasViewBoxRect()) {
- // 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;
- }
- else if (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;
- }
- else if (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;
- }
- }
- bool haveNonFulLZoomTransformChange = (aFlags & TRANSFORM_CHANGED);
- if (aFlags & FULL_ZOOM_CHANGED) {
- // Convert FULL_ZOOM_CHANGED to TRANSFORM_CHANGED:
- aFlags = (aFlags & ~FULL_ZOOM_CHANGED) | TRANSFORM_CHANGED;
- }
- if (aFlags & TRANSFORM_CHANGED) {
- // Make sure our canvas transform matrix gets (lazily) recalculated:
- mCanvasTM = nullptr;
- if (haveNonFulLZoomTransformChange &&
- !(mState & NS_FRAME_IS_NONDISPLAY)) {
- uint32_t flags = (mState & NS_FRAME_IN_REFLOW) ?
- SVGSVGElement::eDuringReflow : 0;
- content->ChildrenOnlyTransformChanged(flags);
- }
- }
- nsSVGUtils::NotifyChildrenOfSVGChange(PrincipalChildList().FirstChild(), aFlags);
- }
- //----------------------------------------------------------------------
- // nsISVGChildFrame methods:
- DrawResult
- nsSVGOuterSVGFrame::PaintSVG(gfxContext& aContext,
- const gfxMatrix& aTransform,
- const nsIntRect* aDirtyRect)
- {
- NS_ASSERTION(PrincipalChildList().FirstChild()->GetType() ==
- nsGkAtoms::svgOuterSVGAnonChildFrame &&
- !PrincipalChildList().FirstChild()->GetNextSibling(),
- "We should have a single, anonymous, child");
- nsSVGOuterSVGAnonChildFrame *anonKid =
- static_cast<nsSVGOuterSVGAnonChildFrame*>(PrincipalChildList().FirstChild());
- return anonKid->PaintSVG(aContext, aTransform, aDirtyRect);
- }
- SVGBBox
- nsSVGOuterSVGFrame::GetBBoxContribution(const gfx::Matrix &aToBBoxUserspace,
- uint32_t aFlags)
- {
- NS_ASSERTION(PrincipalChildList().FirstChild()->GetType() ==
- nsGkAtoms::svgOuterSVGAnonChildFrame &&
- !PrincipalChildList().FirstChild()->GetNextSibling(),
- "We should have a single, anonymous, child");
- // We must defer to our child so that we don't include our
- // content->PrependLocalTransformsTo() transforms.
- nsSVGOuterSVGAnonChildFrame *anonKid =
- static_cast<nsSVGOuterSVGAnonChildFrame*>(PrincipalChildList().FirstChild());
- return anonKid->GetBBoxContribution(aToBBoxUserspace, aFlags);
- }
- //----------------------------------------------------------------------
- // nsSVGContainerFrame methods:
- gfxMatrix
- nsSVGOuterSVGFrame::GetCanvasTM()
- {
- if (!mCanvasTM) {
- SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent);
- float devPxPerCSSPx =
- 1.0f / PresContext()->AppUnitsToFloatCSSPixels(
- PresContext()->AppUnitsPerDevPixel());
- gfxMatrix tm = content->PrependLocalTransformsTo(
- gfxMatrix::Scaling(devPxPerCSSPx, devPxPerCSSPx));
- mCanvasTM = new gfxMatrix(tm);
- }
- return *mCanvasTM;
- }
- //----------------------------------------------------------------------
- // Implementation helpers
- bool
- nsSVGOuterSVGFrame::IsRootOfReplacedElementSubDoc(nsIFrame **aEmbeddingFrame)
- {
- if (!mContent->GetParent()) {
- // Our content is the document element
- nsCOMPtr<nsIDocShell> docShell = PresContext()->GetDocShell();
- nsCOMPtr<nsPIDOMWindowOuter> window;
- if (docShell) {
- window = docShell->GetWindow();
- }
- if (window) {
- nsCOMPtr<nsIDOMElement> frameElement = window->GetFrameElement();
- nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(frameElement);
- nsCOMPtr<nsIDOMHTMLIFrameElement> iframeElement =
- do_QueryInterface(frameElement);
- if (olc || iframeElement) {
- // Our document is inside an HTML 'object', 'embed', 'applet'
- // or 'iframe' element
- if (aEmbeddingFrame) {
- nsCOMPtr<nsIContent> element = do_QueryInterface(frameElement);
- *aEmbeddingFrame = element->GetPrimaryFrame();
- NS_ASSERTION(*aEmbeddingFrame, "Yikes, no embedding frame!");
- }
- return true;
- }
- }
- }
- if (aEmbeddingFrame) {
- *aEmbeddingFrame = nullptr;
- }
- return false;
- }
- bool
- nsSVGOuterSVGFrame::IsRootOfImage()
- {
- if (!mContent->GetParent()) {
- // Our content is the document element
- nsIDocument* doc = mContent->GetUncomposedDoc();
- if (doc && doc->IsBeingUsedAsImage()) {
- // Our document is being used as an image
- return true;
- }
- }
- return false;
- }
- bool
- nsSVGOuterSVGFrame::VerticalScrollbarNotNeeded() const
- {
- nsSVGLength2 &height = static_cast<SVGSVGElement*>(mContent)->
- mLengthAttributes[SVGSVGElement::ATTR_HEIGHT];
- return height.IsPercentage() && height.GetBaseValInSpecifiedUnits() <= 100;
- }
- //----------------------------------------------------------------------
- // Implementation of nsSVGOuterSVGAnonChildFrame
- nsContainerFrame*
- NS_NewSVGOuterSVGAnonChildFrame(nsIPresShell* aPresShell,
- nsStyleContext* aContext)
- {
- return new (aPresShell) nsSVGOuterSVGAnonChildFrame(aContext);
- }
- NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGAnonChildFrame)
- #ifdef DEBUG
- void
- nsSVGOuterSVGAnonChildFrame::Init(nsIContent* aContent,
- nsContainerFrame* aParent,
- nsIFrame* aPrevInFlow)
- {
- MOZ_ASSERT(aParent->GetType() == nsGkAtoms::svgOuterSVGFrame,
- "Unexpected parent");
- nsSVGDisplayContainerFrame::Init(aContent, aParent, aPrevInFlow);
- }
- #endif
- nsIAtom *
- nsSVGOuterSVGAnonChildFrame::GetType() const
- {
- return nsGkAtoms::svgOuterSVGAnonChildFrame;
- }
- static Matrix
- ComputeOuterSVGAnonChildFrameTransform(const nsSVGOuterSVGAnonChildFrame* aFrame)
- {
- // Our elements 'transform' attribute is applied to our nsSVGOuterSVGFrame
- // parent, and the element's children-only transforms are applied to us, the
- // anonymous child frame. Since we are the child frame, we apply the
- // children-only transforms as if they are our own transform.
- SVGSVGElement* content = static_cast<SVGSVGElement*>(aFrame->GetContent());
- if (!content->HasChildrenOnlyTransform()) {
- return Matrix();
- }
- // Outer-<svg> doesn't use x/y, so we can pass eChildToUserSpace here.
- gfxMatrix ownMatrix =
- content->PrependLocalTransformsTo(gfxMatrix(), eChildToUserSpace);
- if (ownMatrix.HasNonTranslation()) {
- // viewBox, currentScale and currentTranslate should only produce a
- // rectilinear transform.
- MOZ_ASSERT(ownMatrix.IsRectilinear(),
- "Non-rectilinear transform will break the following logic");
- // The nsDisplayTransform code will apply this transform to our frame,
- // including to our frame position. We don't want our frame position to
- // be scaled though, so we need to correct for that in the transform.
- CSSPoint pos = CSSPixel::FromAppUnits(aFrame->GetPosition());
- CSSPoint scaledPos = CSSPoint(ownMatrix._11 * pos.x, ownMatrix._22 * pos.y);
- CSSPoint deltaPos = scaledPos - pos;
- ownMatrix *= gfxMatrix::Translation(-deltaPos.x, -deltaPos.y);
- }
- return gfx::ToMatrix(ownMatrix);
- }
- // 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.
- bool
- nsSVGOuterSVGAnonChildFrame::IsSVGTransformed(Matrix* aOwnTransform,
- Matrix* aFromParentTransform) const
- {
- if (aOwnTransform) {
- *aOwnTransform = ComputeOuterSVGAnonChildFrameTransform(this);
- }
- return true;
- }
|