layout/svg/nsSVGUseFrame.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/svg/nsSVGUseFrame.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,252 @@
     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 +// Keep in (case-insensitive) order:
    1.10 +#include "nsIAnonymousContentCreator.h"
    1.11 +#include "nsSVGEffects.h"
    1.12 +#include "nsSVGGFrame.h"
    1.13 +#include "mozilla/dom/SVGUseElement.h"
    1.14 +#include "nsContentList.h"
    1.15 +
    1.16 +typedef nsSVGGFrame nsSVGUseFrameBase;
    1.17 +
    1.18 +using namespace mozilla::dom;
    1.19 +
    1.20 +class nsSVGUseFrame : public nsSVGUseFrameBase,
    1.21 +                      public nsIAnonymousContentCreator
    1.22 +{
    1.23 +  friend nsIFrame*
    1.24 +  NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
    1.25 +
    1.26 +protected:
    1.27 +  nsSVGUseFrame(nsStyleContext* aContext) :
    1.28 +    nsSVGUseFrameBase(aContext),
    1.29 +    mHasValidDimensions(true)
    1.30 +  {}
    1.31 +
    1.32 +public:
    1.33 +  NS_DECL_QUERYFRAME
    1.34 +  NS_DECL_FRAMEARENA_HELPERS
    1.35 +
    1.36 +  
    1.37 +  // nsIFrame interface:
    1.38 +  virtual void Init(nsIContent*      aContent,
    1.39 +                    nsIFrame*        aParent,
    1.40 +                    nsIFrame*        aPrevInFlow) MOZ_OVERRIDE;
    1.41 +
    1.42 +  virtual nsresult  AttributeChanged(int32_t         aNameSpaceID,
    1.43 +                                     nsIAtom*        aAttribute,
    1.44 +                                     int32_t         aModType) MOZ_OVERRIDE;
    1.45 +
    1.46 +  virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
    1.47 +
    1.48 +  /**
    1.49 +   * Get the "type" of the frame
    1.50 +   *
    1.51 +   * @see nsGkAtoms::svgUseFrame
    1.52 +   */
    1.53 +  virtual nsIAtom* GetType() const MOZ_OVERRIDE;
    1.54 +
    1.55 +  virtual bool IsLeaf() const MOZ_OVERRIDE;
    1.56 +
    1.57 +#ifdef DEBUG_FRAME_DUMP
    1.58 +  virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE
    1.59 +  {
    1.60 +    return MakeFrameName(NS_LITERAL_STRING("SVGUse"), aResult);
    1.61 +  }
    1.62 +#endif
    1.63 +
    1.64 +  // nsISVGChildFrame interface:
    1.65 +  virtual void ReflowSVG() MOZ_OVERRIDE;
    1.66 +  virtual void NotifySVGChanged(uint32_t aFlags) MOZ_OVERRIDE;
    1.67 +
    1.68 +  // nsIAnonymousContentCreator
    1.69 +  virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements) MOZ_OVERRIDE;
    1.70 +  virtual void AppendAnonymousContentTo(nsBaseContentList& aElements,
    1.71 +                                        uint32_t aFilter) MOZ_OVERRIDE;
    1.72 +
    1.73 +private:
    1.74 +  bool mHasValidDimensions;
    1.75 +};
    1.76 +
    1.77 +//----------------------------------------------------------------------
    1.78 +// Implementation
    1.79 +
    1.80 +nsIFrame*
    1.81 +NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
    1.82 +{
    1.83 +  return new (aPresShell) nsSVGUseFrame(aContext);
    1.84 +}
    1.85 +
    1.86 +NS_IMPL_FRAMEARENA_HELPERS(nsSVGUseFrame)
    1.87 +
    1.88 +nsIAtom *
    1.89 +nsSVGUseFrame::GetType() const
    1.90 +{
    1.91 +  return nsGkAtoms::svgUseFrame;
    1.92 +}
    1.93 +
    1.94 +//----------------------------------------------------------------------
    1.95 +// nsQueryFrame methods
    1.96 +
    1.97 +NS_QUERYFRAME_HEAD(nsSVGUseFrame)
    1.98 +  NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
    1.99 +NS_QUERYFRAME_TAIL_INHERITING(nsSVGUseFrameBase)
   1.100 +
   1.101 +//----------------------------------------------------------------------
   1.102 +// nsIFrame methods:
   1.103 +
   1.104 +void
   1.105 +nsSVGUseFrame::Init(nsIContent* aContent,
   1.106 +                    nsIFrame* aParent,
   1.107 +                    nsIFrame* aPrevInFlow)
   1.108 +{
   1.109 +  NS_ASSERTION(aContent->IsSVG(nsGkAtoms::use),
   1.110 +               "Content is not an SVG use!");
   1.111 +
   1.112 +  mHasValidDimensions =
   1.113 +    static_cast<SVGUseElement*>(aContent)->HasValidDimensions();
   1.114 +
   1.115 +  nsSVGUseFrameBase::Init(aContent, aParent, aPrevInFlow);
   1.116 +}
   1.117 +
   1.118 +nsresult
   1.119 +nsSVGUseFrame::AttributeChanged(int32_t         aNameSpaceID,
   1.120 +                                nsIAtom*        aAttribute,
   1.121 +                                int32_t         aModType)
   1.122 +{
   1.123 +  SVGUseElement *useElement = static_cast<SVGUseElement*>(mContent);
   1.124 +
   1.125 +  if (aNameSpaceID == kNameSpaceID_None) {
   1.126 +    if (aAttribute == nsGkAtoms::x ||
   1.127 +        aAttribute == nsGkAtoms::y) {
   1.128 +      // make sure our cached transform matrix gets (lazily) updated
   1.129 +      mCanvasTM = nullptr;
   1.130 +      nsSVGEffects::InvalidateRenderingObservers(this);
   1.131 +      nsSVGUtils::ScheduleReflowSVG(this);
   1.132 +      nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED);
   1.133 +    } else if (aAttribute == nsGkAtoms::width ||
   1.134 +               aAttribute == nsGkAtoms::height) {
   1.135 +      bool invalidate = false;
   1.136 +      if (mHasValidDimensions != useElement->HasValidDimensions()) {
   1.137 +        mHasValidDimensions = !mHasValidDimensions;
   1.138 +        invalidate = true;
   1.139 +      }
   1.140 +      if (useElement->OurWidthAndHeightAreUsed()) {
   1.141 +        invalidate = true;
   1.142 +        useElement->SyncWidthOrHeight(aAttribute);
   1.143 +      }
   1.144 +      if (invalidate) {
   1.145 +        nsSVGEffects::InvalidateRenderingObservers(this);
   1.146 +        nsSVGUtils::ScheduleReflowSVG(this);
   1.147 +      }
   1.148 +    }
   1.149 +  } else if (aNameSpaceID == kNameSpaceID_XLink &&
   1.150 +             aAttribute == nsGkAtoms::href) {
   1.151 +    // we're changing our nature, clear out the clone information
   1.152 +    nsSVGEffects::InvalidateRenderingObservers(this);
   1.153 +    nsSVGUtils::ScheduleReflowSVG(this);
   1.154 +    useElement->mOriginal = nullptr;
   1.155 +    useElement->UnlinkSource();
   1.156 +    useElement->TriggerReclone();
   1.157 +  }
   1.158 +
   1.159 +  return nsSVGUseFrameBase::AttributeChanged(aNameSpaceID,
   1.160 +                                             aAttribute, aModType);
   1.161 +}
   1.162 +
   1.163 +void
   1.164 +nsSVGUseFrame::DestroyFrom(nsIFrame* aDestructRoot)
   1.165 +{
   1.166 +  nsRefPtr<SVGUseElement> use = static_cast<SVGUseElement*>(mContent);
   1.167 +  nsSVGUseFrameBase::DestroyFrom(aDestructRoot);
   1.168 +  use->DestroyAnonymousContent();
   1.169 +}
   1.170 +
   1.171 +bool
   1.172 +nsSVGUseFrame::IsLeaf() const
   1.173 +{
   1.174 +  return true;
   1.175 +}
   1.176 +
   1.177 +
   1.178 +//----------------------------------------------------------------------
   1.179 +// nsISVGChildFrame methods
   1.180 +
   1.181 +void
   1.182 +nsSVGUseFrame::ReflowSVG()
   1.183 +{
   1.184 +  // We only handle x/y offset here, since any width/height that is in force is
   1.185 +  // handled by the nsSVGOuterSVGFrame for the anonymous <svg> that will be
   1.186 +  // created for that purpose.
   1.187 +  float x, y;
   1.188 +  static_cast<SVGUseElement*>(mContent)->
   1.189 +    GetAnimatedLengthValues(&x, &y, nullptr);
   1.190 +  mRect.MoveTo(nsLayoutUtils::RoundGfxRectToAppRect(
   1.191 +                 gfxRect(x, y, 0.0, 0.0),
   1.192 +                 PresContext()->AppUnitsPerCSSPixel()).TopLeft());
   1.193 +
   1.194 +  // If we have a filter, we need to invalidate ourselves because filter
   1.195 +  // output can change even if none of our descendants need repainting.
   1.196 +  if (StyleSVGReset()->HasFilters()) {
   1.197 +    InvalidateFrame();
   1.198 +  }
   1.199 +
   1.200 +  nsSVGUseFrameBase::ReflowSVG();
   1.201 +}
   1.202 +
   1.203 +void
   1.204 +nsSVGUseFrame::NotifySVGChanged(uint32_t aFlags)
   1.205 +{
   1.206 +  if (aFlags & COORD_CONTEXT_CHANGED &&
   1.207 +      !(aFlags & TRANSFORM_CHANGED)) {
   1.208 +    // Coordinate context changes affect mCanvasTM if we have a
   1.209 +    // percentage 'x' or 'y'
   1.210 +    SVGUseElement *use = static_cast<SVGUseElement*>(mContent);
   1.211 +    if (use->mLengthAttributes[SVGUseElement::ATTR_X].IsPercentage() ||
   1.212 +        use->mLengthAttributes[SVGUseElement::ATTR_Y].IsPercentage()) {
   1.213 +      aFlags |= TRANSFORM_CHANGED;
   1.214 +      // Ancestor changes can't affect how we render from the perspective of
   1.215 +      // any rendering observers that we may have, so we don't need to
   1.216 +      // invalidate them. We also don't need to invalidate ourself, since our
   1.217 +      // changed ancestor will have invalidated its entire area, which includes
   1.218 +      // our area.
   1.219 +      // For perf reasons we call this before calling NotifySVGChanged() below.
   1.220 +      nsSVGUtils::ScheduleReflowSVG(this);
   1.221 +    }
   1.222 +  }
   1.223 +
   1.224 +  // We don't remove the TRANSFORM_CHANGED flag here if we have a viewBox or
   1.225 +  // non-percentage width/height, since if they're set then they are cloned to
   1.226 +  // an anonymous child <svg>, and its nsSVGInnerSVGFrame will do that.
   1.227 +
   1.228 +  nsSVGUseFrameBase::NotifySVGChanged(aFlags);
   1.229 +}
   1.230 +
   1.231 +//----------------------------------------------------------------------
   1.232 +// nsIAnonymousContentCreator methods:
   1.233 +
   1.234 +nsresult
   1.235 +nsSVGUseFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
   1.236 +{
   1.237 +  SVGUseElement *use = static_cast<SVGUseElement*>(mContent);
   1.238 +
   1.239 +  nsIContent* clone = use->CreateAnonymousContent();
   1.240 +  nsSVGEffects::InvalidateRenderingObservers(this);
   1.241 +  if (!clone)
   1.242 +    return NS_ERROR_FAILURE;
   1.243 +  if (!aElements.AppendElement(clone))
   1.244 +    return NS_ERROR_OUT_OF_MEMORY;
   1.245 +  return NS_OK;
   1.246 +}
   1.247 +
   1.248 +void
   1.249 +nsSVGUseFrame::AppendAnonymousContentTo(nsBaseContentList& aElements,
   1.250 +                                        uint32_t aFilter)
   1.251 +{
   1.252 +  SVGUseElement *use = static_cast<SVGUseElement*>(mContent);
   1.253 +  nsIContent* clone = use->GetAnonymousContent();
   1.254 +  aElements.MaybeAppendElement(clone);
   1.255 +}

mercurial