LCOV - code coverage report
Current view: top level - dom/svg - SVGUseElement.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 68 191 35.6 %
Date: 2017-07-14 16:53:18 Functions: 12 35 34.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/ArrayUtils.h"
       8             : 
       9             : #include "mozilla/dom/SVGUseElement.h"
      10             : #include "mozilla/dom/SVGUseElementBinding.h"
      11             : #include "nsGkAtoms.h"
      12             : #include "mozilla/dom/SVGSVGElement.h"
      13             : #include "nsIDocument.h"
      14             : #include "nsIPresShell.h"
      15             : #include "mozilla/dom/Element.h"
      16             : #include "nsContentUtils.h"
      17             : #include "nsIURI.h"
      18             : #include "mozilla/URLExtraData.h"
      19             : #include "nsSVGEffects.h"
      20             : 
      21          58 : NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Use)
      22             : 
      23             : namespace mozilla {
      24             : namespace dom {
      25             : 
      26             : JSObject*
      27           0 : SVGUseElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
      28             : {
      29           0 :   return SVGUseElementBinding::Wrap(aCx, this, aGivenProto);
      30             : }
      31             : 
      32             : ////////////////////////////////////////////////////////////////////////
      33             : // implementation
      34             : 
      35             : nsSVGElement::LengthInfo SVGUseElement::sLengthInfo[4] =
      36             : {
      37             :   { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X },
      38             :   { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y },
      39             :   { &nsGkAtoms::width, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::X },
      40             :   { &nsGkAtoms::height, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, SVGContentUtils::Y },
      41             : };
      42             : 
      43             : nsSVGElement::StringInfo SVGUseElement::sStringInfo[2] =
      44             : {
      45             :   { &nsGkAtoms::href, kNameSpaceID_None, true },
      46             :   { &nsGkAtoms::href, kNameSpaceID_XLink, true }
      47             : };
      48             : 
      49             : //----------------------------------------------------------------------
      50             : // nsISupports methods
      51             : 
      52             : NS_IMPL_CYCLE_COLLECTION_CLASS(SVGUseElement)
      53             : 
      54           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SVGUseElement,
      55             :                                                 SVGUseElementBase)
      56           0 :   nsAutoScriptBlocker scriptBlocker;
      57           0 :   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginal)
      58           0 :   tmp->DestroyAnonymousContent();
      59           0 :   tmp->UnlinkSource();
      60           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
      61           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SVGUseElement,
      62             :                                                   SVGUseElementBase)
      63           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginal)
      64           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClone)
      65           0 :   tmp->mSource.Traverse(&cb);
      66           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
      67             : 
      68         397 : NS_IMPL_ADDREF_INHERITED(SVGUseElement,SVGUseElementBase)
      69         318 : NS_IMPL_RELEASE_INHERITED(SVGUseElement,SVGUseElementBase)
      70             : 
      71         243 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(SVGUseElement)
      72         214 :   NS_INTERFACE_TABLE_INHERITED(SVGUseElement, nsIMutationObserver)
      73         214 : NS_INTERFACE_TABLE_TAIL_INHERITING(SVGUseElementBase)
      74             : 
      75             : //----------------------------------------------------------------------
      76             : // Implementation
      77             : 
      78          29 : SVGUseElement::SVGUseElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
      79          29 :   : SVGUseElementBase(aNodeInfo), mSource(this)
      80             : {
      81          29 : }
      82             : 
      83           0 : SVGUseElement::~SVGUseElement()
      84             : {
      85           0 :   UnlinkSource();
      86           0 : }
      87             : 
      88             : //----------------------------------------------------------------------
      89             : // nsIDOMNode methods
      90             : 
      91             : nsresult
      92           0 : SVGUseElement::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
      93             :                      bool aPreallocateChildren) const
      94             : {
      95           0 :   *aResult = nullptr;
      96           0 :   already_AddRefed<mozilla::dom::NodeInfo> ni = RefPtr<mozilla::dom::NodeInfo>(aNodeInfo).forget();
      97           0 :   SVGUseElement *it = new SVGUseElement(ni);
      98             : 
      99           0 :   nsCOMPtr<nsINode> kungFuDeathGrip(it);
     100           0 :   nsresult rv1 = it->Init();
     101           0 :   nsresult rv2 = const_cast<SVGUseElement*>(this)->CopyInnerTo(it, aPreallocateChildren);
     102             : 
     103             :   // SVGUseElement specific portion - record who we cloned from
     104           0 :   it->mOriginal = const_cast<SVGUseElement*>(this);
     105             : 
     106           0 :   if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
     107           0 :     kungFuDeathGrip.swap(*aResult);
     108             :   }
     109             : 
     110           0 :   return NS_FAILED(rv1) ? rv1 : rv2;
     111             : }
     112             : 
     113             : already_AddRefed<SVGAnimatedString>
     114           0 : SVGUseElement::Href()
     115             : {
     116           0 :   return mStringAttributes[HREF].IsExplicitlySet()
     117             :          ? mStringAttributes[HREF].ToDOMAnimatedString(this)
     118           0 :          : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this);
     119             : }
     120             : 
     121             : //----------------------------------------------------------------------
     122             : 
     123             : already_AddRefed<SVGAnimatedLength>
     124           0 : SVGUseElement::X()
     125             : {
     126           0 :   return mLengthAttributes[ATTR_X].ToDOMAnimatedLength(this);
     127             : }
     128             : 
     129             : already_AddRefed<SVGAnimatedLength>
     130           0 : SVGUseElement::Y()
     131             : {
     132           0 :   return mLengthAttributes[ATTR_Y].ToDOMAnimatedLength(this);
     133             : }
     134             : 
     135             : already_AddRefed<SVGAnimatedLength>
     136           0 : SVGUseElement::Width()
     137             : {
     138           0 :   return mLengthAttributes[ATTR_WIDTH].ToDOMAnimatedLength(this);
     139             : }
     140             : 
     141             : already_AddRefed<SVGAnimatedLength>
     142           0 : SVGUseElement::Height()
     143             : {
     144           0 :   return mLengthAttributes[ATTR_HEIGHT].ToDOMAnimatedLength(this);
     145             : }
     146             : 
     147             : //----------------------------------------------------------------------
     148             : // nsIMutationObserver methods
     149             : 
     150             : void
     151           0 : SVGUseElement::CharacterDataChanged(nsIDocument *aDocument,
     152             :                                     nsIContent *aContent,
     153             :                                     CharacterDataChangeInfo* aInfo)
     154             : {
     155           0 :   if (nsContentUtils::IsInSameAnonymousTree(this, aContent)) {
     156           0 :     TriggerReclone();
     157             :   }
     158           0 : }
     159             : 
     160             : void
     161           0 : SVGUseElement::AttributeChanged(nsIDocument* aDocument,
     162             :                                 Element* aElement,
     163             :                                 int32_t aNameSpaceID,
     164             :                                 nsIAtom* aAttribute,
     165             :                                 int32_t aModType,
     166             :                                 const nsAttrValue* aOldValue)
     167             : {
     168           0 :   if (nsContentUtils::IsInSameAnonymousTree(this, aElement)) {
     169           0 :     TriggerReclone();
     170             :   }
     171           0 : }
     172             : 
     173             : void
     174           0 : SVGUseElement::ContentAppended(nsIDocument *aDocument,
     175             :                                nsIContent *aContainer,
     176             :                                nsIContent *aFirstNewContent,
     177             :                                int32_t aNewIndexInContainer)
     178             : {
     179           0 :   if (nsContentUtils::IsInSameAnonymousTree(this, aContainer)) {
     180           0 :     TriggerReclone();
     181             :   }
     182           0 : }
     183             : 
     184             : void
     185           0 : SVGUseElement::ContentInserted(nsIDocument *aDocument,
     186             :                                nsIContent *aContainer,
     187             :                                nsIContent *aChild,
     188             :                                int32_t aIndexInContainer)
     189             : {
     190           0 :   if (nsContentUtils::IsInSameAnonymousTree(this, aChild)) {
     191           0 :     TriggerReclone();
     192             :   }
     193           0 : }
     194             : 
     195             : void
     196           0 : SVGUseElement::ContentRemoved(nsIDocument *aDocument,
     197             :                               nsIContent *aContainer,
     198             :                               nsIContent *aChild,
     199             :                               int32_t aIndexInContainer,
     200             :                               nsIContent *aPreviousSibling)
     201             : {
     202           0 :   if (nsContentUtils::IsInSameAnonymousTree(this, aChild)) {
     203           0 :     TriggerReclone();
     204             :   }
     205           0 : }
     206             : 
     207             : void
     208           0 : SVGUseElement::NodeWillBeDestroyed(const nsINode *aNode)
     209             : {
     210           0 :   nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
     211           0 :   UnlinkSource();
     212           0 : }
     213             : 
     214             : //----------------------------------------------------------------------
     215             : 
     216             : nsIContent*
     217          23 : SVGUseElement::CreateAnonymousContent()
     218             : {
     219          23 :   mClone = nullptr;
     220             : 
     221          23 :   if (mSource.get()) {
     222           0 :     mSource.get()->RemoveMutationObserver(this);
     223             :   }
     224             : 
     225          23 :   LookupHref();
     226          23 :   nsIContent* targetContent = mSource.get();
     227          23 :   if (!targetContent)
     228           0 :     return nullptr;
     229             : 
     230             :   // make sure target is valid type for <use>
     231             :   // QIable nsSVGGraphicsElement would eliminate enumerating all elements
     232          23 :   if (!targetContent->IsAnyOfSVGElements(nsGkAtoms::svg,
     233             :                                          nsGkAtoms::symbol,
     234             :                                          nsGkAtoms::g,
     235             :                                          nsGkAtoms::path,
     236             :                                          nsGkAtoms::text,
     237             :                                          nsGkAtoms::rect,
     238             :                                          nsGkAtoms::circle,
     239             :                                          nsGkAtoms::ellipse,
     240             :                                          nsGkAtoms::line,
     241             :                                          nsGkAtoms::polyline,
     242             :                                          nsGkAtoms::polygon,
     243             :                                          nsGkAtoms::image,
     244             :                                          nsGkAtoms::use))
     245           0 :     return nullptr;
     246             : 
     247             :   // circular loop detection
     248             : 
     249             :   // check 1 - check if we're a document descendent of the target
     250          23 :   if (nsContentUtils::ContentIsDescendantOf(this, targetContent))
     251           0 :     return nullptr;
     252             : 
     253             :   // check 2 - check if we're a clone, and if we already exist in the hierarchy
     254          23 :   if (GetParent() && mOriginal) {
     255           0 :     for (nsCOMPtr<nsIContent> content = GetParent();
     256             :          content;
     257           0 :          content = content->GetParent()) {
     258           0 :       if (content->IsSVGElement(nsGkAtoms::use) &&
     259           0 :           static_cast<SVGUseElement*>(content.get())->mOriginal == mOriginal) {
     260           0 :         return nullptr;
     261             :       }
     262             :     }
     263             :   }
     264             : 
     265          46 :   nsCOMPtr<nsINode> newnode;
     266             :   nsNodeInfoManager* nodeInfoManager =
     267          23 :     targetContent->OwnerDoc() == OwnerDoc() ?
     268          23 :       nullptr : OwnerDoc()->NodeInfoManager();
     269          23 :   nsNodeUtils::Clone(targetContent, true, nodeInfoManager, nullptr,
     270          46 :                      getter_AddRefs(newnode));
     271             : 
     272          46 :   nsCOMPtr<nsIContent> newcontent = do_QueryInterface(newnode);
     273             : 
     274          23 :   if (!newcontent)
     275           0 :     return nullptr;
     276             : 
     277          23 :   if (newcontent->IsAnyOfSVGElements(nsGkAtoms::svg, nsGkAtoms::symbol)) {
     278           0 :     nsSVGElement *newElement = static_cast<nsSVGElement*>(newcontent.get());
     279             : 
     280           0 :     if (mLengthAttributes[ATTR_WIDTH].IsExplicitlySet())
     281           0 :       newElement->SetLength(nsGkAtoms::width, mLengthAttributes[ATTR_WIDTH]);
     282           0 :     if (mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet())
     283           0 :       newElement->SetLength(nsGkAtoms::height, mLengthAttributes[ATTR_HEIGHT]);
     284             :   }
     285             : 
     286             :   // Store the base URI
     287          46 :   nsCOMPtr<nsIURI> baseURI = targetContent->GetBaseURI();
     288          23 :   if (!baseURI) {
     289           0 :     return nullptr;
     290             :   }
     291          46 :   mContentURLData = new URLExtraData(baseURI.forget(),
     292          46 :                                      do_AddRef(OwnerDoc()->GetDocumentURI()),
     293          69 :                                      do_AddRef(NodePrincipal()));
     294             : 
     295          23 :   targetContent->AddMutationObserver(this);
     296          23 :   mClone = newcontent;
     297             : 
     298             : #ifdef DEBUG
     299             :   // Our anonymous clone can get restyled by various things
     300             :   // (e.g. SMIL).  Reconstructing its frame is OK, though, because
     301             :   // it's going to be our _only_ child in the frame tree, so can't get
     302             :   // mis-ordered with anything.
     303          23 :   mClone->SetProperty(nsGkAtoms::restylableAnonymousNode,
     304          46 :                       reinterpret_cast<void*>(true));
     305             : #endif // DEBUG
     306             : 
     307          23 :   return mClone;
     308             : }
     309             : 
     310             : nsIURI*
     311           0 : SVGUseElement::GetSourceDocURI()
     312             : {
     313           0 :   nsIContent* targetContent = mSource.get();
     314           0 :   if (!targetContent)
     315           0 :     return nullptr;
     316             : 
     317           0 :   return targetContent->OwnerDoc()->GetDocumentURI();
     318             : }
     319             : 
     320             : void
     321           0 : SVGUseElement::DestroyAnonymousContent()
     322             : {
     323           0 :   nsContentUtils::DestroyAnonymousContent(&mClone);
     324           0 : }
     325             : 
     326             : bool
     327           0 : SVGUseElement::OurWidthAndHeightAreUsed() const
     328             : {
     329           0 :   return mClone && mClone->IsAnyOfSVGElements(nsGkAtoms::svg, nsGkAtoms::symbol);
     330             : }
     331             : 
     332             : //----------------------------------------------------------------------
     333             : // implementation helpers
     334             : 
     335             : void
     336           0 : SVGUseElement::SyncWidthOrHeight(nsIAtom* aName)
     337             : {
     338           0 :   NS_ASSERTION(aName == nsGkAtoms::width || aName == nsGkAtoms::height,
     339             :                "The clue is in the function name");
     340           0 :   NS_ASSERTION(OurWidthAndHeightAreUsed(), "Don't call this");
     341             : 
     342           0 :   if (OurWidthAndHeightAreUsed()) {
     343           0 :     nsSVGElement *target = static_cast<nsSVGElement*>(mClone.get());
     344           0 :     uint32_t index = *sLengthInfo[ATTR_WIDTH].mName == aName ? ATTR_WIDTH : ATTR_HEIGHT;
     345             : 
     346           0 :     if (mLengthAttributes[index].IsExplicitlySet()) {
     347           0 :       target->SetLength(aName, mLengthAttributes[index]);
     348           0 :       return;
     349             :     }
     350           0 :     if (mClone->IsSVGElement(nsGkAtoms::svg)) {
     351             :       // Our width/height attribute is now no longer explicitly set, so we
     352             :       // need to revert the clone's width/height to the width/height of the
     353             :       // content that's being cloned.
     354           0 :       TriggerReclone();
     355           0 :       return;
     356             :     }
     357             :     // Our width/height attribute is now no longer explicitly set, so we
     358             :     // need to set the value to 100%
     359             :     nsSVGLength2 length;
     360             :     length.Init(SVGContentUtils::XY, 0xff,
     361           0 :                 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE);
     362           0 :     target->SetLength(aName, length);
     363           0 :     return;
     364             :   }
     365             : }
     366             : 
     367             : void
     368          23 : SVGUseElement::LookupHref()
     369             : {
     370          46 :   nsAutoString href;
     371          23 :   if (mStringAttributes[HREF].IsExplicitlySet()) {
     372           2 :     mStringAttributes[HREF].GetAnimValue(href, this);
     373             :   } else {
     374          21 :     mStringAttributes[XLINK_HREF].GetAnimValue(href, this);
     375             :   }
     376             : 
     377          23 :   if (href.IsEmpty()) {
     378           0 :     return;
     379             :   }
     380             : 
     381             :   nsCOMPtr<nsIURI> originURI =
     382          46 :     mOriginal ? mOriginal->GetBaseURI() : GetBaseURI();
     383          23 :   nsCOMPtr<nsIURI> baseURI = nsContentUtils::IsLocalRefURL(href)
     384          69 :     ? nsSVGEffects::GetBaseURLForLocalRef(this, originURI)
     385          69 :     : originURI;
     386             : 
     387          46 :   nsCOMPtr<nsIURI> targetURI;
     388          46 :   nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
     389          23 :                                             GetComposedDoc(), baseURI);
     390          23 :   mSource.Reset(this, targetURI);
     391             : }
     392             : 
     393             : void
     394           0 : SVGUseElement::TriggerReclone()
     395             : {
     396           0 :   nsIDocument *doc = GetComposedDoc();
     397           0 :   if (!doc)
     398           0 :     return;
     399           0 :   nsIPresShell *presShell = doc->GetShell();
     400           0 :   if (!presShell)
     401           0 :     return;
     402           0 :   presShell->PostRecreateFramesFor(this);
     403             : }
     404             : 
     405             : void
     406           0 : SVGUseElement::UnlinkSource()
     407             : {
     408           0 :   if (mSource.get()) {
     409           0 :     mSource.get()->RemoveMutationObserver(this);
     410             :   }
     411           0 :   mSource.Unlink();
     412           0 : }
     413             : 
     414             : //----------------------------------------------------------------------
     415             : // nsSVGElement methods
     416             : 
     417             : /* virtual */ gfxMatrix
     418          12 : SVGUseElement::PrependLocalTransformsTo(
     419             :   const gfxMatrix &aMatrix, SVGTransformTypes aWhich) const
     420             : {
     421             :   // 'transform' attribute:
     422          12 :   gfxMatrix userToParent;
     423             : 
     424          12 :   if (aWhich == eUserSpaceToParent || aWhich == eAllTransforms) {
     425             :     userToParent = GetUserToParentTransform(mAnimateMotionTransform,
     426          12 :                                             mTransforms);
     427          12 :     if (aWhich == eUserSpaceToParent) {
     428          12 :       return userToParent * aMatrix;
     429             :     }
     430             :   }
     431             : 
     432             :   // our 'x' and 'y' attributes:
     433             :   float x, y;
     434           0 :   const_cast<SVGUseElement*>(this)->GetAnimatedLengthValues(&x, &y, nullptr);
     435             : 
     436           0 :   gfxMatrix childToUser = gfxMatrix::Translation(x, y);
     437             : 
     438           0 :   if (aWhich == eAllTransforms) {
     439           0 :     return childToUser * userToParent * aMatrix;
     440             :   }
     441             : 
     442           0 :   MOZ_ASSERT(aWhich == eChildToUserSpace, "Unknown TransformTypes");
     443             : 
     444             :   // The following may look broken because pre-multiplying our eChildToUserSpace
     445             :   // transform with another matrix without including our eUserSpaceToParent
     446             :   // transform between the two wouldn't make sense.  We don't expect that to
     447             :   // ever happen though.  We get here either when the identity matrix has been
     448             :   // passed because our caller just wants our eChildToUserSpace transform, or
     449             :   // when our eUserSpaceToParent transform has already been multiplied into the
     450             :   // matrix that our caller passes (such as when we're called from PaintSVG).
     451           0 :   return childToUser * aMatrix;
     452             : }
     453             : 
     454             : /* virtual */ bool
     455          39 : SVGUseElement::HasValidDimensions() const
     456             : {
     457          39 :   return (!mLengthAttributes[ATTR_WIDTH].IsExplicitlySet() ||
     458         117 :            mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0) &&
     459          39 :          (!mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() ||
     460          39 :            mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0);
     461             : }
     462             : 
     463             : nsSVGElement::LengthAttributesInfo
     464          89 : SVGUseElement::GetLengthInfo()
     465             : {
     466             :   return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
     467          89 :                               ArrayLength(sLengthInfo));
     468             : }
     469             : 
     470             : nsSVGElement::StringAttributesInfo
     471         100 : SVGUseElement::GetStringInfo()
     472             : {
     473             :   return StringAttributesInfo(mStringAttributes, sStringInfo,
     474         100 :                               ArrayLength(sStringInfo));
     475             : }
     476             : 
     477             : //----------------------------------------------------------------------
     478             : // nsIContent methods
     479             : 
     480             : NS_IMETHODIMP_(bool)
     481         139 : SVGUseElement::IsAttributeMapped(const nsIAtom* name) const
     482             : {
     483             :   static const MappedAttributeEntry* const map[] = {
     484             :     sFEFloodMap,
     485             :     sFiltersMap,
     486             :     sFontSpecificationMap,
     487             :     sGradientStopMap,
     488             :     sLightingEffectsMap,
     489             :     sMarkersMap,
     490             :     sTextContentElementsMap,
     491             :     sViewportsMap
     492             :   };
     493             : 
     494         278 :   return FindAttributeDependence(name, map) ||
     495         278 :     SVGUseElementBase::IsAttributeMapped(name);
     496             : }
     497             : 
     498             : } // namespace dom
     499             : } // namespace mozilla

Generated by: LCOV version 1.13