1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Mozilla SVG project.
16 : *
17 : * The Initial Developer of the Original Code is IBM Corporation.
18 : * Portions created by the Initial Developer are Copyright (C) 2004
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either of the GNU General Public License Version 2 or later (the "GPL"),
25 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #include "mozilla/Util.h"
38 :
39 : #include "nsSVGUseElement.h"
40 : #include "nsIDOMSVGGElement.h"
41 : #include "nsGkAtoms.h"
42 : #include "nsIDOMDocument.h"
43 : #include "nsSVGSVGElement.h"
44 : #include "nsIDOMSVGSymbolElement.h"
45 : #include "nsIDocument.h"
46 : #include "nsIPresShell.h"
47 : #include "mozilla/dom/Element.h"
48 : #include "nsContentUtils.h"
49 :
50 : using namespace mozilla;
51 : using namespace mozilla::dom;
52 :
53 : ////////////////////////////////////////////////////////////////////////
54 : // implementation
55 :
56 : nsSVGElement::LengthInfo nsSVGUseElement::sLengthInfo[4] =
57 : {
58 : { &nsGkAtoms::x, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, nsSVGUtils::X },
59 : { &nsGkAtoms::y, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, nsSVGUtils::Y },
60 : { &nsGkAtoms::width, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, nsSVGUtils::X },
61 : { &nsGkAtoms::height, 0, nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER, nsSVGUtils::Y },
62 : };
63 :
64 : nsSVGElement::StringInfo nsSVGUseElement::sStringInfo[1] =
65 : {
66 : { &nsGkAtoms::href, kNameSpaceID_XLink, true }
67 : };
68 :
69 0 : NS_IMPL_NS_NEW_SVG_ELEMENT(Use)
70 :
71 : //----------------------------------------------------------------------
72 : // nsISupports methods
73 :
74 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsSVGUseElement)
75 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsSVGUseElement,
76 : nsSVGUseElementBase)
77 0 : nsAutoScriptBlocker scriptBlocker;
78 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOriginal)
79 0 : tmp->DestroyAnonymousContent();
80 0 : tmp->UnlinkSource();
81 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
82 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsSVGUseElement,
83 : nsSVGUseElementBase)
84 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOriginal)
85 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mClone)
86 0 : tmp->mSource.Traverse(&cb);
87 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
88 :
89 0 : NS_IMPL_ADDREF_INHERITED(nsSVGUseElement,nsSVGUseElementBase)
90 0 : NS_IMPL_RELEASE_INHERITED(nsSVGUseElement,nsSVGUseElementBase)
91 :
92 0 : DOMCI_NODE_DATA(SVGUseElement, nsSVGUseElement)
93 :
94 0 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsSVGUseElement)
95 0 : NS_NODE_INTERFACE_TABLE7(nsSVGUseElement, nsIDOMNode, nsIDOMElement,
96 : nsIDOMSVGElement, nsIDOMSVGTests,
97 : nsIDOMSVGURIReference,
98 : nsIDOMSVGUseElement, nsIMutationObserver)
99 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGUseElement)
100 0 : if (aIID.Equals(NS_GET_IID(nsSVGUseElement)))
101 0 : foundInterface = reinterpret_cast<nsISupports*>(this);
102 : else
103 0 : NS_INTERFACE_MAP_END_INHERITING(nsSVGUseElementBase)
104 :
105 : //----------------------------------------------------------------------
106 : // Implementation
107 :
108 : #ifdef _MSC_VER
109 : // Disable "warning C4355: 'this' : used in base member initializer list".
110 : // We can ignore that warning because we know that mSource's constructor
111 : // doesn't dereference the pointer passed to it.
112 : #pragma warning(push)
113 : #pragma warning(disable:4355)
114 : #endif
115 0 : nsSVGUseElement::nsSVGUseElement(already_AddRefed<nsINodeInfo> aNodeInfo)
116 0 : : nsSVGUseElementBase(aNodeInfo), mSource(this)
117 : #ifdef _MSC_VER
118 : #pragma warning(pop)
119 : #endif
120 : {
121 0 : }
122 :
123 0 : nsSVGUseElement::~nsSVGUseElement()
124 : {
125 0 : UnlinkSource();
126 0 : }
127 :
128 : //----------------------------------------------------------------------
129 : // nsIDOMNode methods
130 :
131 : nsresult
132 0 : nsSVGUseElement::Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const
133 : {
134 0 : *aResult = nsnull;
135 0 : nsCOMPtr<nsINodeInfo> ni = aNodeInfo;
136 0 : nsSVGUseElement *it = new nsSVGUseElement(ni.forget());
137 0 : if (!it) {
138 0 : return NS_ERROR_OUT_OF_MEMORY;
139 : }
140 :
141 0 : nsCOMPtr<nsINode> kungFuDeathGrip(it);
142 0 : nsresult rv = it->Init();
143 0 : rv |= CopyInnerTo(it);
144 :
145 : // nsSVGUseElement specific portion - record who we cloned from
146 0 : it->mOriginal = const_cast<nsSVGUseElement*>(this);
147 :
148 0 : if (NS_SUCCEEDED(rv)) {
149 0 : kungFuDeathGrip.swap(*aResult);
150 : }
151 :
152 0 : return rv;
153 : }
154 :
155 : //----------------------------------------------------------------------
156 : // nsIDOMSVGURIReference methods
157 :
158 : /* readonly attribute nsIDOMSVGAnimatedString href; */
159 0 : NS_IMETHODIMP nsSVGUseElement::GetHref(nsIDOMSVGAnimatedString * *aHref)
160 : {
161 0 : return mStringAttributes[HREF].ToDOMAnimatedString(aHref, this);
162 : }
163 :
164 : //----------------------------------------------------------------------
165 : // nsIDOMSVGUseElement methods
166 :
167 : /* readonly attribute nsIDOMSVGAnimatedLength x; */
168 0 : NS_IMETHODIMP nsSVGUseElement::GetX(nsIDOMSVGAnimatedLength * *aX)
169 : {
170 0 : return mLengthAttributes[X].ToDOMAnimatedLength(aX, this);
171 : }
172 :
173 : /* readonly attribute nsIDOMSVGAnimatedLength y; */
174 0 : NS_IMETHODIMP nsSVGUseElement::GetY(nsIDOMSVGAnimatedLength * *aY)
175 : {
176 0 : return mLengthAttributes[Y].ToDOMAnimatedLength(aY, this);
177 : }
178 :
179 : /* readonly attribute nsIDOMSVGAnimatedLength width; */
180 0 : NS_IMETHODIMP nsSVGUseElement::GetWidth(nsIDOMSVGAnimatedLength * *aWidth)
181 : {
182 0 : return mLengthAttributes[WIDTH].ToDOMAnimatedLength(aWidth, this);
183 : }
184 :
185 : /* readonly attribute nsIDOMSVGAnimatedLength height; */
186 0 : NS_IMETHODIMP nsSVGUseElement::GetHeight(nsIDOMSVGAnimatedLength * *aHeight)
187 : {
188 0 : return mLengthAttributes[HEIGHT].ToDOMAnimatedLength(aHeight, this);
189 : }
190 :
191 : //----------------------------------------------------------------------
192 : // nsIMutationObserver methods
193 :
194 : void
195 0 : nsSVGUseElement::CharacterDataChanged(nsIDocument *aDocument,
196 : nsIContent *aContent,
197 : CharacterDataChangeInfo* aInfo)
198 : {
199 0 : if (nsContentUtils::IsInSameAnonymousTree(this, aContent)) {
200 0 : TriggerReclone();
201 : }
202 0 : }
203 :
204 : void
205 0 : nsSVGUseElement::AttributeChanged(nsIDocument* aDocument,
206 : Element* aElement,
207 : PRInt32 aNameSpaceID,
208 : nsIAtom* aAttribute,
209 : PRInt32 aModType)
210 : {
211 0 : if (nsContentUtils::IsInSameAnonymousTree(this, aElement)) {
212 0 : TriggerReclone();
213 : }
214 0 : }
215 :
216 : void
217 0 : nsSVGUseElement::ContentAppended(nsIDocument *aDocument,
218 : nsIContent *aContainer,
219 : nsIContent *aFirstNewContent,
220 : PRInt32 aNewIndexInContainer)
221 : {
222 0 : if (nsContentUtils::IsInSameAnonymousTree(this, aContainer)) {
223 0 : TriggerReclone();
224 : }
225 0 : }
226 :
227 : void
228 0 : nsSVGUseElement::ContentInserted(nsIDocument *aDocument,
229 : nsIContent *aContainer,
230 : nsIContent *aChild,
231 : PRInt32 aIndexInContainer)
232 : {
233 0 : if (nsContentUtils::IsInSameAnonymousTree(this, aChild)) {
234 0 : TriggerReclone();
235 : }
236 0 : }
237 :
238 : void
239 0 : nsSVGUseElement::ContentRemoved(nsIDocument *aDocument,
240 : nsIContent *aContainer,
241 : nsIContent *aChild,
242 : PRInt32 aIndexInContainer,
243 : nsIContent *aPreviousSibling)
244 : {
245 0 : if (nsContentUtils::IsInSameAnonymousTree(this, aChild)) {
246 0 : TriggerReclone();
247 : }
248 0 : }
249 :
250 : void
251 0 : nsSVGUseElement::NodeWillBeDestroyed(const nsINode *aNode)
252 : {
253 0 : nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
254 0 : UnlinkSource();
255 0 : }
256 :
257 : //----------------------------------------------------------------------
258 :
259 : nsIContent*
260 0 : nsSVGUseElement::CreateAnonymousContent()
261 : {
262 0 : mClone = nsnull;
263 :
264 0 : if (mSource.get()) {
265 0 : mSource.get()->RemoveMutationObserver(this);
266 : }
267 :
268 0 : LookupHref();
269 0 : nsIContent* targetContent = mSource.get();
270 0 : if (!targetContent || !targetContent->IsSVG())
271 0 : return nsnull;
272 :
273 : // make sure target is valid type for <use>
274 : // QIable nsSVGGraphicsElement would eliminate enumerating all elements
275 0 : nsIAtom *tag = targetContent->Tag();
276 0 : if (tag != nsGkAtoms::svg &&
277 : tag != nsGkAtoms::symbol &&
278 : tag != nsGkAtoms::g &&
279 : tag != nsGkAtoms::path &&
280 : tag != nsGkAtoms::text &&
281 : tag != nsGkAtoms::rect &&
282 : tag != nsGkAtoms::circle &&
283 : tag != nsGkAtoms::ellipse &&
284 : tag != nsGkAtoms::line &&
285 : tag != nsGkAtoms::polyline &&
286 : tag != nsGkAtoms::polygon &&
287 : tag != nsGkAtoms::image &&
288 : tag != nsGkAtoms::use)
289 0 : return nsnull;
290 :
291 : // circular loop detection
292 :
293 : // check 1 - check if we're a document descendent of the target
294 0 : if (nsContentUtils::ContentIsDescendantOf(this, targetContent))
295 0 : return nsnull;
296 :
297 : // check 2 - check if we're a clone, and if we already exist in the hierarchy
298 0 : if (GetParent() && mOriginal) {
299 0 : for (nsCOMPtr<nsIContent> content = GetParent();
300 0 : content;
301 0 : content = content->GetParent()) {
302 0 : nsCOMPtr<nsIDOMSVGUseElement> useElement = do_QueryInterface(content);
303 :
304 0 : if (useElement) {
305 0 : nsRefPtr<nsSVGUseElement> useImpl;
306 0 : useElement->QueryInterface(NS_GET_IID(nsSVGUseElement),
307 0 : getter_AddRefs(useImpl));
308 :
309 0 : if (useImpl && useImpl->mOriginal == mOriginal)
310 0 : return nsnull;
311 : }
312 : }
313 : }
314 :
315 0 : nsCOMPtr<nsIDOMNode> newnode;
316 0 : nsCOMArray<nsINode> unused;
317 : nsNodeInfoManager* nodeInfoManager =
318 0 : targetContent->OwnerDoc() == OwnerDoc() ?
319 0 : nsnull : OwnerDoc()->NodeInfoManager();
320 : nsNodeUtils::Clone(targetContent, true, nodeInfoManager, unused,
321 0 : getter_AddRefs(newnode));
322 :
323 0 : nsCOMPtr<nsIContent> newcontent = do_QueryInterface(newnode);
324 :
325 0 : if (!newcontent)
326 0 : return nsnull;
327 :
328 0 : nsCOMPtr<nsIDOMSVGSymbolElement> symbol = do_QueryInterface(newcontent);
329 0 : nsCOMPtr<nsIDOMSVGSVGElement> svg = do_QueryInterface(newcontent);
330 :
331 0 : if (symbol) {
332 0 : nsIDocument *document = GetCurrentDoc();
333 0 : if (!document)
334 0 : return nsnull;
335 :
336 0 : nsNodeInfoManager *nodeInfoManager = document->NodeInfoManager();
337 0 : if (!nodeInfoManager)
338 0 : return nsnull;
339 :
340 0 : nsCOMPtr<nsINodeInfo> nodeInfo;
341 : nodeInfo = nodeInfoManager->GetNodeInfo(nsGkAtoms::svg, nsnull,
342 : kNameSpaceID_SVG,
343 0 : nsIDOMNode::ELEMENT_NODE);
344 0 : if (!nodeInfo)
345 0 : return nsnull;
346 :
347 0 : nsCOMPtr<nsIContent> svgNode;
348 0 : NS_NewSVGSVGElement(getter_AddRefs(svgNode), nodeInfo.forget(),
349 0 : NOT_FROM_PARSER);
350 :
351 0 : if (!svgNode)
352 0 : return nsnull;
353 :
354 : // copy attributes
355 : const nsAttrName* name;
356 : PRUint32 i;
357 0 : for (i = 0; (name = newcontent->GetAttrNameAt(i)); i++) {
358 0 : nsAutoString value;
359 0 : PRInt32 nsID = name->NamespaceID();
360 0 : nsIAtom* lname = name->LocalName();
361 :
362 0 : newcontent->GetAttr(nsID, lname, value);
363 0 : svgNode->SetAttr(nsID, lname, name->GetPrefix(), value, false);
364 : }
365 :
366 : // move the children over
367 0 : PRUint32 num = newcontent->GetChildCount();
368 0 : for (i = 0; i < num; i++) {
369 0 : nsCOMPtr<nsIContent> child = newcontent->GetFirstChild();
370 0 : newcontent->RemoveChildAt(0, false);
371 0 : svgNode->InsertChildAt(child, i, true);
372 : }
373 :
374 0 : newcontent = svgNode;
375 : }
376 :
377 0 : if (symbol || svg) {
378 0 : nsSVGElement *newElement = static_cast<nsSVGElement*>(newcontent.get());
379 :
380 0 : if (mLengthAttributes[WIDTH].IsExplicitlySet())
381 0 : newElement->SetLength(nsGkAtoms::width, mLengthAttributes[WIDTH]);
382 0 : if (mLengthAttributes[HEIGHT].IsExplicitlySet())
383 0 : newElement->SetLength(nsGkAtoms::height, mLengthAttributes[HEIGHT]);
384 : }
385 :
386 : // Set up its base URI correctly
387 0 : nsCOMPtr<nsIURI> baseURI = targetContent->GetBaseURI();
388 0 : if (!baseURI)
389 0 : return nsnull;
390 0 : newcontent->SetExplicitBaseURI(baseURI);
391 :
392 0 : targetContent->AddMutationObserver(this);
393 0 : mClone = newcontent;
394 0 : return mClone;
395 : }
396 :
397 : void
398 0 : nsSVGUseElement::DestroyAnonymousContent()
399 : {
400 0 : nsContentUtils::DestroyAnonymousContent(&mClone);
401 0 : }
402 :
403 : //----------------------------------------------------------------------
404 : // implementation helpers
405 :
406 : void
407 0 : nsSVGUseElement::SyncWidthOrHeight(nsIAtom* aName)
408 : {
409 0 : NS_ASSERTION(aName == nsGkAtoms::width || aName == nsGkAtoms::height,
410 : "The clue is in the function name");
411 :
412 0 : if (!mClone) {
413 0 : return;
414 : }
415 :
416 0 : nsCOMPtr<nsIDOMSVGSymbolElement> symbol = do_QueryInterface(mClone);
417 0 : nsCOMPtr<nsIDOMSVGSVGElement> svg = do_QueryInterface(mClone);
418 :
419 0 : if (symbol || svg) {
420 0 : nsSVGElement *target = static_cast<nsSVGElement*>(mClone.get());
421 0 : PRUint32 index = *sLengthInfo[WIDTH].mName == aName ? WIDTH : HEIGHT;
422 :
423 0 : if (mLengthAttributes[index].IsExplicitlySet()) {
424 0 : target->SetLength(aName, mLengthAttributes[index]);
425 : return;
426 : }
427 0 : if (svg) {
428 : // Our width/height attribute is now no longer explicitly set, so we
429 : // need to revert the clone's width/height to the width/height of the
430 : // content that's being cloned.
431 0 : TriggerReclone();
432 : return;
433 : }
434 : // Our width/height attribute is now no longer explicitly set, so we
435 : // need to set the value to 100%
436 : nsSVGLength2 length;
437 : length.Init(nsSVGUtils::XY, 0xff,
438 0 : 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE);
439 0 : target->SetLength(aName, length);
440 : return;
441 : }
442 : }
443 :
444 : void
445 0 : nsSVGUseElement::LookupHref()
446 : {
447 0 : nsAutoString href;
448 0 : mStringAttributes[HREF].GetAnimValue(href, this);
449 0 : if (href.IsEmpty())
450 : return;
451 :
452 0 : nsCOMPtr<nsIURI> targetURI;
453 0 : nsCOMPtr<nsIURI> baseURI = mOriginal ? mOriginal->GetBaseURI() : GetBaseURI();
454 0 : nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
455 0 : GetCurrentDoc(), baseURI);
456 :
457 0 : mSource.Reset(this, targetURI);
458 : }
459 :
460 : void
461 0 : nsSVGUseElement::TriggerReclone()
462 : {
463 0 : nsIDocument *doc = GetCurrentDoc();
464 0 : if (!doc)
465 0 : return;
466 0 : nsIPresShell *presShell = doc->GetShell();
467 0 : if (!presShell)
468 0 : return;
469 0 : presShell->PostRecreateFramesFor(this);
470 : }
471 :
472 : void
473 0 : nsSVGUseElement::UnlinkSource()
474 : {
475 0 : if (mSource.get()) {
476 0 : mSource.get()->RemoveMutationObserver(this);
477 : }
478 0 : mSource.Unlink();
479 0 : }
480 :
481 : //----------------------------------------------------------------------
482 : // nsSVGElement methods
483 :
484 : /* virtual */ gfxMatrix
485 0 : nsSVGUseElement::PrependLocalTransformsTo(const gfxMatrix &aMatrix,
486 : TransformTypes aWhich) const
487 : {
488 0 : NS_ABORT_IF_FALSE(aWhich != eChildToUserSpace || aMatrix.IsIdentity(),
489 : "Skipping eUserSpaceToParent transforms makes no sense");
490 :
491 : // 'transform' attribute:
492 : gfxMatrix fromUserSpace =
493 0 : nsSVGUseElementBase::PrependLocalTransformsTo(aMatrix, aWhich);
494 0 : if (aWhich == eUserSpaceToParent) {
495 0 : return fromUserSpace;
496 : }
497 : // our 'x' and 'y' attributes:
498 : float x, y;
499 0 : const_cast<nsSVGUseElement*>(this)->GetAnimatedLengthValues(&x, &y, nsnull);
500 0 : gfxMatrix toUserSpace = gfxMatrix().Translate(gfxPoint(x, y));
501 0 : if (aWhich == eChildToUserSpace) {
502 0 : return toUserSpace;
503 : }
504 0 : NS_ABORT_IF_FALSE(aWhich == eAllTransforms, "Unknown TransformTypes");
505 0 : return toUserSpace * fromUserSpace;
506 : }
507 :
508 : /* virtual */ bool
509 0 : nsSVGUseElement::HasValidDimensions() const
510 : {
511 0 : return (!mLengthAttributes[WIDTH].IsExplicitlySet() ||
512 0 : mLengthAttributes[WIDTH].GetAnimValInSpecifiedUnits() > 0) &&
513 0 : (!mLengthAttributes[HEIGHT].IsExplicitlySet() ||
514 0 : mLengthAttributes[HEIGHT].GetAnimValInSpecifiedUnits() > 0);
515 : }
516 :
517 : nsSVGElement::LengthAttributesInfo
518 0 : nsSVGUseElement::GetLengthInfo()
519 : {
520 : return LengthAttributesInfo(mLengthAttributes, sLengthInfo,
521 0 : ArrayLength(sLengthInfo));
522 : }
523 :
524 : nsSVGElement::StringAttributesInfo
525 0 : nsSVGUseElement::GetStringInfo()
526 : {
527 : return StringAttributesInfo(mStringAttributes, sStringInfo,
528 0 : ArrayLength(sStringInfo));
529 : }
530 :
531 : //----------------------------------------------------------------------
532 : // nsIContent methods
533 :
534 : NS_IMETHODIMP_(bool)
535 0 : nsSVGUseElement::IsAttributeMapped(const nsIAtom* name) const
536 : {
537 : static const MappedAttributeEntry* const map[] = {
538 : sFEFloodMap,
539 : sFiltersMap,
540 : sFontSpecificationMap,
541 : sGradientStopMap,
542 : sLightingEffectsMap,
543 : sMarkersMap,
544 : sTextContentElementsMap,
545 : sViewportsMap
546 : };
547 :
548 0 : return FindAttributeDependence(name, map) ||
549 0 : nsSVGUseElementBase::IsAttributeMapped(name);
550 4392 : }
551 :
|