Back to index

lightning-sunbird  0.9+nobinonly
nsSVGElement.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Alex Fritze <alex.fritze@crocodile-clips.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsSVGElement.h"
00040 #include "nsIDocument.h"
00041 #include "nsRange.h"
00042 #include "nsIDOMAttr.h"
00043 #include "nsIDOMEventReceiver.h"
00044 #include "nsIDOMMutationEvent.h"
00045 #include "nsMutationEvent.h"
00046 #include "nsIBindingManager.h"
00047 #include "nsXBLBinding.h"
00048 #include "nsStyleConsts.h"
00049 #include "nsDOMError.h"
00050 #include "nsIPresShell.h"
00051 #include "nsIDOMViewCSS.h"
00052 #include "nsIDOMCSSStyleDeclaration.h"
00053 #include "nsIServiceManager.h"
00054 #include "nsIXBLService.h"
00055 #include "nsSVGAtoms.h"
00056 #include "nsLayoutAtoms.h"
00057 #include "nsHTMLAtoms.h"
00058 #include "nsICSSStyleRule.h"
00059 #include "nsISVGSVGElement.h"
00060 #include "nsRuleWalker.h"
00061 #include "nsCSSDeclaration.h"
00062 #include "nsCSSProps.h"
00063 #include "nsICSSParser.h"
00064 #include "nsICSSLoader.h"
00065 #include "nsGenericHTMLElement.h"
00066 #include "nsNodeInfoManager.h"
00067 #include "nsIScriptGlobalObject.h"
00068 
00069 nsSVGElement::nsSVGElement(nsINodeInfo *aNodeInfo)
00070   : nsGenericElement(aNodeInfo)
00071 {
00072 
00073 }
00074 
00075 nsSVGElement::~nsSVGElement()
00076 {
00077   PRUint32 i, count = mMappedAttributes.AttrCount();
00078   for (i = 0; i < count; ++i) {
00079     mMappedAttributes.AttrAt(i)->GetSVGValue()->RemoveObserver(this);
00080   }
00081 }
00082 
00083 //----------------------------------------------------------------------
00084 // nsISupports methods
00085 
00086 NS_IMPL_ADDREF_INHERITED(nsSVGElement,nsGenericElement)
00087 NS_IMPL_RELEASE_INHERITED(nsSVGElement,nsGenericElement)
00088 
00089 NS_INTERFACE_MAP_BEGIN(nsSVGElement)
00090   NS_INTERFACE_MAP_ENTRY(nsIXMLContent)
00091   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOM3Node, new nsNode3Tearoff(this))
00092   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
00093   NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver)
00094   NS_INTERFACE_MAP_ENTRY(nsISVGContent)
00095 // provided by nsGenericElement:
00096 //  NS_INTERFACE_MAP_ENTRY(nsIStyledContent)
00097 //  NS_INTERFACE_MAP_ENTRY(nsIContent)
00098 NS_INTERFACE_MAP_END_INHERITING(nsGenericElement)
00099 
00100 //----------------------------------------------------------------------
00101 // Implementation
00102   
00103 //----------------------------------------------------------------------
00104 // nsIContent methods
00105 
00106 nsresult
00107 nsSVGElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
00108                          nsIContent* aBindingParent,
00109                          PRBool aCompileEventHandlers)
00110 {
00111   nsresult rv = nsGenericElement::BindToTree(aDocument, aParent,
00112                                              aBindingParent,
00113                                              aCompileEventHandlers);
00114   NS_ENSURE_SUCCESS(rv, rv);
00115 
00116   ParentChainChanged();
00117 
00118   return rv;
00119 }
00120 
00121 void
00122 nsSVGElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
00123 {
00124   nsGenericElement::UnbindFromTree(aDeep, aNullParent);
00125   ParentChainChanged();
00126 }
00127 
00128 nsIAtom *
00129 nsSVGElement::GetIDAttributeName() const
00130 {
00131   return nsSVGAtoms::id;
00132 }
00133 
00134 nsIAtom *
00135 nsSVGElement::GetClassAttributeName() const
00136 {
00137   return nsSVGAtoms::_class;
00138 }
00139 
00140 nsresult
00141 nsSVGElement::SetAttr(PRInt32 aNamespaceID, nsIAtom* aName, nsIAtom* aPrefix,
00142                       const nsAString& aValue,
00143                       PRBool aNotify)
00144 {
00145   NS_ENSURE_ARG_POINTER(aName);
00146   NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
00147                "Don't call SetAttr with unknown namespace");
00148   
00149   nsresult rv;
00150   nsAutoString oldValue;
00151   PRBool hasListeners = PR_FALSE;
00152   PRBool modification = PR_FALSE;
00153   
00154   PRInt32 index = mAttrsAndChildren.IndexOfAttr(aName, aNamespaceID);
00155   
00156   if (IsInDoc()) {
00157     hasListeners = nsGenericElement::HasMutationListeners(this,
00158       NS_EVENT_BITS_MUTATION_ATTRMODIFIED);
00159     
00160     // If we have no listeners and aNotify is false, we are almost certainly
00161     // coming from the content sink and will almost certainly have no previous
00162     // value.  Even if we do, setting the value is cheap when we have no
00163     // listeners and don't plan to notify.  The check for aNotify here is an
00164     // optimization, the check for haveListeners is a correctness issue.
00165     if (index >= 0 && (hasListeners || aNotify)) {
00166       modification = PR_TRUE;
00167       // don't do any update if old == new.
00168       mAttrsAndChildren.AttrAt(index)->ToString(oldValue);
00169       if (aValue.Equals(oldValue) &&
00170           aPrefix == mAttrsAndChildren.GetSafeAttrNameAt(index)->GetPrefix()) {
00171         return NS_OK;
00172       }
00173     }
00174   }
00175   
00176   // If this is an svg presentation attribute we need to map it into
00177   // the content stylerule.
00178   // XXX For some reason incremental mapping doesn't work, so for now
00179   // just delete the style rule and lazily reconstruct it in
00180   // GetContentStyleRule()
00181   if(aNamespaceID == kNameSpaceID_None && IsAttributeMapped(aName)) 
00182     mContentStyleRule = nsnull;
00183   
00184   // Parse value
00185   nsAttrValue attrValue;
00186   nsCOMPtr<nsISVGValue> svg_value;
00187   if (index >= 0) {
00188     // Found the attr in the list.
00189     const nsAttrValue* currVal = mAttrsAndChildren.AttrAt(index);
00190     if (currVal->Type() == nsAttrValue::eSVGValue) {
00191       svg_value = currVal->GetSVGValue();
00192     }
00193   }
00194   else {
00195     // Could be a mapped attribute.
00196     svg_value = GetMappedAttribute(aNamespaceID, aName);
00197   }
00198   
00199   if (svg_value) {
00200     if (NS_FAILED(svg_value->SetValueString(aValue))) {
00201       // The value was rejected. This happens e.g. in a XUL template
00202       // when trying to set a value like "?x" on a value object that
00203       // expects a length.
00204       // To accomodate this "erronous" value, we'll insert a proxy
00205       // object between ourselves and the actual value object:
00206       nsCOMPtr<nsISVGValue> proxy;
00207       rv = NS_CreateSVGStringProxyValue(svg_value, getter_AddRefs(proxy));
00208       NS_ENSURE_SUCCESS(rv, rv);
00209 
00210       svg_value->RemoveObserver(this);
00211       proxy->SetValueString(aValue);
00212       proxy->AddObserver(this);
00213       attrValue.SetTo(proxy);
00214     }
00215     else {
00216       attrValue.SetTo(svg_value);
00217     }
00218   }
00219   else if (aName == nsSVGAtoms::style && aNamespaceID == kNameSpaceID_None) {
00220     nsGenericHTMLElement::ParseStyleAttribute(this, PR_TRUE, aValue, attrValue);
00221   }
00222   // We don't have an nsISVGValue attribute.
00223   else if (aName == nsSVGAtoms::id && aNamespaceID == kNameSpaceID_None){
00224     attrValue.ParseAtom(aValue);
00225   } 
00226   else {
00227     attrValue.SetTo(aValue);
00228   }
00229 
00230   if (aNamespaceID == kNameSpaceID_None && IsEventName(aName)) {
00231     nsCOMPtr<nsIEventListenerManager> manager;
00232     GetListenerManager(getter_AddRefs(manager));
00233     if (manager) {
00234       nsIAtom* eventName = GetEventNameForAttr(aName);
00235       nsIDocument *ownerDoc = GetOwnerDoc();
00236       manager->AddScriptEventListener(NS_STATIC_CAST(nsIContent*, this), eventName,
00237                                       aValue, PR_TRUE,
00238                                       !nsContentUtils::IsChromeDoc(ownerDoc));
00239     }
00240   }
00241   
00242   return SetAttrAndNotify(aNamespaceID, aName, aPrefix, oldValue, attrValue,
00243                           modification, hasListeners, aNotify);
00244 }
00245 
00246 nsresult
00247 nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
00248                         PRBool aNotify)
00249 {
00250   if (aNamespaceID == kNameSpaceID_None && IsEventName(aName)) {
00251     nsCOMPtr<nsIEventListenerManager> manager;
00252     GetListenerManager(getter_AddRefs(manager));
00253     if (manager) {
00254       nsIAtom* eventName = GetEventNameForAttr(aName);
00255       manager->RemoveScriptEventListener(eventName);
00256     }
00257   }
00258 
00259   return nsGenericElement::UnsetAttr(aNamespaceID, aName, aNotify);
00260 }
00261 
00262 PRBool
00263 nsSVGElement::IsContentOfType(PRUint32 aFlags) const
00264 {
00265   return !(aFlags & ~(eELEMENT | eSVG));
00266 }
00267 
00268 //----------------------------------------------------------------------
00269 // nsIStyledContent methods
00270 
00271 NS_IMETHODIMP
00272 nsSVGElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker)
00273 {
00274 #ifdef DEBUG
00275 //  printf("nsSVGElement(%p)::WalkContentStyleRules()\n", this);
00276 #endif
00277   if (!mContentStyleRule)
00278     UpdateContentStyleRule();
00279 
00280   if (mContentStyleRule)  
00281     aRuleWalker->Forward(mContentStyleRule);
00282 
00283   return NS_OK;
00284 }
00285 
00286 NS_IMETHODIMP
00287 nsSVGElement::SetInlineStyleRule(nsICSSStyleRule* aStyleRule, PRBool aNotify)
00288 {
00289   PRBool hasListeners = PR_FALSE;
00290   PRBool modification = PR_FALSE;
00291   nsAutoString oldValueStr;
00292 
00293   if (IsInDoc()) {
00294     hasListeners = nsGenericElement::HasMutationListeners(this,
00295       NS_EVENT_BITS_MUTATION_ATTRMODIFIED);
00296 
00297     // There's no point in comparing the stylerule pointers since we're always
00298     // getting a new stylerule here. And we can't compare the stringvalues of
00299     // the old and the new rules since both will point to the same declaration
00300     // and thus will be the same.
00301     if (hasListeners || aNotify) {
00302       // save the old attribute so we can set up the mutation event properly
00303       const nsAttrValue* value = mAttrsAndChildren.GetAttr(nsSVGAtoms::style);
00304       if (value) {
00305         modification = PR_TRUE;
00306         if (hasListeners) {
00307           value->ToString(oldValueStr);
00308         }
00309       }
00310     }
00311   }
00312 
00313   nsAttrValue attrValue(aStyleRule);
00314 
00315   return SetAttrAndNotify(kNameSpaceID_None, nsSVGAtoms::style, nsnull,
00316                           oldValueStr, attrValue, modification, hasListeners,
00317                           aNotify);
00318 }
00319 
00320 nsICSSStyleRule*
00321 nsSVGElement::GetInlineStyleRule()
00322 {
00323   const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(nsSVGAtoms::style);
00324 
00325   if (attrVal && attrVal->Type() == nsAttrValue::eCSSStyleRule) {
00326     return attrVal->GetCSSStyleRuleValue();
00327   }
00328 
00329   return nsnull;
00330 }
00331 
00332 // PresentationAttributes-FillStroke
00333 /* static */ const nsGenericElement::MappedAttributeEntry
00334 nsSVGElement::sFillStrokeMap[] = {
00335   { &nsSVGAtoms::fill },
00336   { &nsSVGAtoms::fill_opacity },
00337   { &nsSVGAtoms::fill_rule },
00338   { &nsSVGAtoms::stroke },
00339   { &nsSVGAtoms::stroke_dasharray },
00340   { &nsSVGAtoms::stroke_dashoffset },
00341   { &nsSVGAtoms::stroke_linecap },
00342   { &nsSVGAtoms::stroke_linejoin },
00343   { &nsSVGAtoms::stroke_miterlimit },
00344   { &nsSVGAtoms::stroke_opacity },
00345   { &nsSVGAtoms::stroke_width },
00346   { nsnull }
00347 };
00348 
00349 // PresentationAttributes-Graphics
00350 /* static */ const nsGenericElement::MappedAttributeEntry
00351 nsSVGElement::sGraphicsMap[] = {
00352   { &nsSVGAtoms::clip_path },
00353   { &nsSVGAtoms::clip_rule },
00354   { &nsSVGAtoms::cursor },
00355   { &nsSVGAtoms::display },
00356   { &nsSVGAtoms::filter },
00357   { &nsSVGAtoms::image_rendering },
00358   { &nsSVGAtoms::mask },
00359   { &nsSVGAtoms::opacity },
00360   { &nsSVGAtoms::pointer_events },
00361   { &nsSVGAtoms::shape_rendering },
00362   { &nsSVGAtoms::text_rendering },
00363   { &nsSVGAtoms::visibility },
00364   { nsnull }
00365 };
00366 
00367 // PresentationAttributes-TextContentElements
00368 /* static */ const nsGenericElement::MappedAttributeEntry
00369 nsSVGElement::sTextContentElementsMap[] = {
00370   { &nsSVGAtoms::alignment_baseline },
00371   { &nsSVGAtoms::baseline_shift },
00372   { &nsSVGAtoms::direction },
00373   { &nsSVGAtoms::dominant_baseline },
00374   { &nsSVGAtoms::glyph_orientation_horizontal },
00375   { &nsSVGAtoms::glyph_orientation_vertical },
00376   { &nsSVGAtoms::kerning },
00377   { &nsSVGAtoms::letter_spacing },
00378   { &nsSVGAtoms::text_anchor },
00379   { &nsSVGAtoms::text_decoration },
00380   { &nsSVGAtoms::unicode_bidi },
00381   { &nsSVGAtoms::word_spacing },
00382   { nsnull }
00383 };
00384 
00385 // PresentationAttributes-FontSpecification
00386 /* static */ const nsGenericElement::MappedAttributeEntry
00387 nsSVGElement::sFontSpecificationMap[] = {
00388   { &nsSVGAtoms::font_family },
00389   { &nsSVGAtoms::font_size },
00390   { &nsSVGAtoms::font_size_adjust },
00391   { &nsSVGAtoms::font_stretch },
00392   { &nsSVGAtoms::font_style },
00393   { &nsSVGAtoms::font_variant },
00394   { &nsSVGAtoms::font_weight },  
00395   { nsnull }
00396 };
00397 
00398 // PresentationAttributes-GradientStop
00399 /* static */ const nsGenericElement::MappedAttributeEntry
00400 nsSVGElement::sGradientStopMap[] = {
00401   { &nsSVGAtoms::stop_color },
00402   { &nsSVGAtoms::stop_opacity },
00403   { nsnull }
00404 };
00405 
00406 // PresentationAttributes-Viewports
00407 /* static */ const nsGenericElement::MappedAttributeEntry
00408 nsSVGElement::sViewportsMap[] = {
00409   { &nsSVGAtoms::overflow },
00410   { &nsSVGAtoms::clip },
00411   { nsnull }
00412 };
00413 
00414 // PresentationAttributes-Makers
00415 /* static */ const nsGenericElement::MappedAttributeEntry
00416 nsSVGElement::sMarkersMap[] = {
00417   { &nsSVGAtoms::marker_end },
00418   { &nsSVGAtoms::marker_mid },
00419   { &nsSVGAtoms::marker_start },
00420   { nsnull }
00421 };
00422 
00423 // PresentationAttributes-Color
00424 /* static */ const nsGenericElement::MappedAttributeEntry
00425 nsSVGElement::sColorMap[] = {
00426   { &nsSVGAtoms::color },
00427   { nsnull }
00428 };
00429 
00430 
00431 //----------------------------------------------------------------------
00432 // nsIDOMNode methods
00433 
00434 NS_IMETHODIMP
00435 nsSVGElement::IsSupported(const nsAString& aFeature, const nsAString& aVersion, PRBool* aReturn)
00436 {
00437   NS_NOTYETIMPLEMENTED("nsSVGElement::IsSupported");
00438   return NS_ERROR_NOT_IMPLEMENTED;
00439 }
00440 
00441 //----------------------------------------------------------------------
00442 // nsIDOMElement methods
00443 
00444 // forwarded to nsGenericElement implementations
00445 
00446 
00447 //----------------------------------------------------------------------
00448 // nsIDOMSVGElement methods
00449 
00450 /* attribute DOMString id; */
00451 NS_IMETHODIMP nsSVGElement::GetId(nsAString & aId)
00452 {
00453   return GetAttr(kNameSpaceID_None, nsSVGAtoms::id, aId);
00454 }
00455 
00456 NS_IMETHODIMP nsSVGElement::SetId(const nsAString & aId)
00457 {
00458   return SetAttr(kNameSpaceID_None, nsSVGAtoms::id, aId, PR_TRUE);
00459 }
00460 
00461 /* readonly attribute nsIDOMSVGSVGElement ownerSVGElement; */
00462 NS_IMETHODIMP
00463 nsSVGElement::GetOwnerSVGElement(nsIDOMSVGSVGElement * *aOwnerSVGElement)
00464 {
00465   *aOwnerSVGElement = nsnull;
00466 
00467   nsIBindingManager *bindingManager = nsnull;
00468   // XXXbz I _think_ this is right.  We want to be using the binding manager
00469   // that would have attached the binding that gives us our anonymous parent.
00470   // That's the binding manager for the document we actually belong to, which
00471   // is our owner doc.
00472   nsIDocument* ownerDoc = GetOwnerDoc();
00473   if (ownerDoc) {
00474     bindingManager = ownerDoc->BindingManager();
00475   }
00476 
00477   nsCOMPtr<nsIContent> parent;
00478   
00479   if (bindingManager) {
00480     // we have a binding manager -- do we have an anonymous parent?
00481     bindingManager->GetInsertionParent(this, getter_AddRefs(parent));
00482   }
00483 
00484   if (!parent) {
00485     // if we didn't find an anonymous parent, use the explicit one,
00486     // whether it's null or not...
00487     parent = GetParent();
00488   }
00489 
00490   while (parent) {    
00491     nsCOMPtr<nsIDOMSVGSVGElement> SVGSVGElement = do_QueryInterface(parent);
00492     if (SVGSVGElement) {
00493       *aOwnerSVGElement = SVGSVGElement;
00494       NS_ADDREF(*aOwnerSVGElement);
00495       return NS_OK;
00496     }
00497     nsCOMPtr<nsIContent> next;
00498 
00499     if (bindingManager) {
00500       bindingManager->GetInsertionParent(parent, getter_AddRefs(next));
00501     }
00502 
00503     if (!next) {
00504       // no anonymous parent, so use explicit one
00505       next = parent->GetParent();
00506     }
00507     
00508     parent = next;
00509   }
00510 
00511   // we don't have a parent SVG element...
00512 
00513   // are _we_ the outermost SVG element? If yes, return nsnull, but don't fail
00514   nsCOMPtr<nsIDOMSVGSVGElement> SVGSVGElement = do_QueryInterface((nsIDOMSVGElement*)this);
00515   if (SVGSVGElement) return NS_OK;
00516   
00517   // no owner found and we aren't the outermost SVG element either.
00518   // this situation can e.g. occur during content tree teardown. 
00519   return NS_ERROR_FAILURE;
00520 }
00521 
00522 /* readonly attribute nsIDOMSVGElement viewportElement; */
00523 NS_IMETHODIMP
00524 nsSVGElement::GetViewportElement(nsIDOMSVGElement * *aViewportElement)
00525 {
00526   *aViewportElement = nsnull;
00527   nsCOMPtr<nsIDOMSVGSVGElement> SVGSVGElement;
00528   nsresult rv = GetOwnerSVGElement(getter_AddRefs(SVGSVGElement));
00529   NS_ENSURE_SUCCESS(rv,rv);
00530   if (SVGSVGElement) {
00531     nsCOMPtr<nsIDOMSVGElement> SVGElement = do_QueryInterface(SVGSVGElement);
00532     *aViewportElement = SVGElement;
00533     NS_IF_ADDREF(*aViewportElement);
00534   }
00535   return NS_OK;
00536 }
00537 
00538 //----------------------------------------------------------------------
00539 // nsISVGValueObserver methods:
00540 
00541 NS_IMETHODIMP
00542 nsSVGElement::WillModifySVGObservable(nsISVGValue* observable,
00543                                       nsISVGValue::modificationType aModType)
00544 {
00545   return NS_OK;
00546 }
00547 
00548 
00549 NS_IMETHODIMP
00550 nsSVGElement::DidModifySVGObservable(nsISVGValue* aObservable,
00551                                      nsISVGValue::modificationType aModType)
00552 {
00553   // Return without setting DOM attributes as markup attributes if the
00554   // attribute's element is being inserted into an SVG document fragment,
00555   // which provides a context which percentage lengths are relative to.
00556   // Bug 274886
00557   if (aModType == nsISVGValue::mod_context)
00558     return NS_OK;
00559 
00560   PRUint32 i, count = mMappedAttributes.AttrCount();
00561   const nsAttrValue* attrValue = nsnull;
00562   for (i = 0; i < count; ++i) {
00563     attrValue = mMappedAttributes.AttrAt(i);
00564     if (attrValue->GetSVGValue() == aObservable) {
00565       break;
00566     }
00567   }
00568 
00569   if (i == count) {
00570     NS_NOTREACHED("unknown nsISVGValue");
00571 
00572     return NS_ERROR_UNEXPECTED;
00573   }
00574 
00575   const nsAttrName* attrName = mMappedAttributes.GetSafeAttrNameAt(i);
00576   PRBool modification = PR_FALSE;
00577   PRBool hasListeners = PR_FALSE;
00578   if (IsInDoc()) {
00579     modification = !!mAttrsAndChildren.GetAttr(attrName->LocalName(),
00580                                                attrName->NamespaceID());
00581     hasListeners = nsGenericElement::HasMutationListeners(this,
00582       NS_EVENT_BITS_MUTATION_ATTRMODIFIED);
00583   }
00584 
00585   nsAttrValue newValue(aObservable);
00586 
00587   return SetAttrAndNotify(attrName->NamespaceID(), attrName->LocalName(),
00588                           attrName->GetPrefix(), EmptyString(), newValue,
00589                           modification, hasListeners, PR_TRUE);
00590 }
00591 
00592 //----------------------------------------------------------------------
00593 // nsISVGContent methods:
00594 
00595 // recursive helper to call ParentChainChanged across non-SVG elements
00596 static void CallParentChainChanged(nsIContent*elem)
00597 {
00598   NS_ASSERTION(elem, "null element");
00599   
00600   PRUint32 count = elem->GetChildCount();
00601   for (PRUint32 i=0; i<count; ++i) {
00602     nsIContent* child = elem->GetChildAt(i);
00603 
00604     nsCOMPtr<nsISVGContent> svgChild = do_QueryInterface(child);
00605     if (svgChild) {
00606       svgChild->ParentChainChanged();
00607     }
00608     else {
00609       // non-svg element might have an svg child, so recurse
00610       CallParentChainChanged(child);
00611     }
00612   }
00613 }
00614 
00615 void
00616 nsSVGElement::ParentChainChanged()
00617 {
00618   CallParentChainChanged(this);
00619 }
00620 
00621 
00622 //----------------------------------------------------------------------
00623 // Implementation Helpers:
00624 
00625 PRBool
00626 nsSVGElement::IsEventName(nsIAtom* aName)
00627 {
00628   return PR_FALSE;
00629 }
00630 
00631 nsresult
00632 nsSVGElement::SetAttrAndNotify(PRInt32 aNamespaceID, nsIAtom* aAttribute,
00633                                nsIAtom* aPrefix, const nsAString& aOldValue,
00634                                nsAttrValue& aParsedValue, PRBool aModification,
00635                                PRBool aFireMutation, PRBool aNotify)
00636 {
00637   nsresult rv;
00638   PRUint8 modType = aModification ?
00639     NS_STATIC_CAST(PRUint8, nsIDOMMutationEvent::MODIFICATION) :
00640     NS_STATIC_CAST(PRUint8, nsIDOMMutationEvent::ADDITION);
00641 
00642   nsIDocument* document = GetCurrentDoc();
00643   mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify);
00644   if (aNotify && document) {
00645     document->AttributeWillChange(this, aNamespaceID, aAttribute);
00646   }
00647 
00648   if (aNamespaceID == kNameSpaceID_None) {
00649     // XXX doesn't check IsAttributeMapped here.
00650     rv = mAttrsAndChildren.SetAndTakeAttr(aAttribute, aParsedValue);
00651     NS_ENSURE_SUCCESS(rv, rv);
00652   }
00653   else {
00654     nsCOMPtr<nsINodeInfo> ni;
00655     rv = mNodeInfo->NodeInfoManager()->GetNodeInfo(aAttribute, aPrefix,
00656                                                    aNamespaceID,
00657                                                    getter_AddRefs(ni));
00658     NS_ENSURE_SUCCESS(rv, rv);
00659 
00660     rv = mAttrsAndChildren.SetAndTakeAttr(ni, aParsedValue);
00661     NS_ENSURE_SUCCESS(rv, rv);
00662   }
00663 
00664   if (document) {
00665     nsXBLBinding *binding = document->BindingManager()->GetBinding(this);
00666     if (binding) {
00667       binding->AttributeChanged(aAttribute, aNamespaceID, PR_FALSE, aNotify);
00668     }
00669 
00670     if (aFireMutation) {
00671       nsCOMPtr<nsIDOMEventTarget> node = do_QueryInterface(NS_STATIC_CAST(nsIContent *, this));
00672       nsMutationEvent mutation(PR_TRUE, NS_MUTATION_ATTRMODIFIED, node);
00673 
00674       nsAutoString attrName;
00675       aAttribute->ToString(attrName);
00676       nsCOMPtr<nsIDOMAttr> attrNode;
00677       GetAttributeNode(attrName, getter_AddRefs(attrNode));
00678       mutation.mRelatedNode = attrNode;
00679 
00680       mutation.mAttrName = aAttribute;
00681       nsAutoString newValue;
00682       // We don't really need to call GetAttr here, but lets try to keep this
00683       // code as similar to nsGenericHTMLElement::SetAttrAndNotify as possible
00684       // so that they can merge sometime in the future.
00685       GetAttr(aNamespaceID, aAttribute, newValue);
00686       if (!newValue.IsEmpty()) {
00687         mutation.mNewAttrValue = do_GetAtom(newValue);
00688       }
00689       if (!aOldValue.IsEmpty()) {
00690         mutation.mPrevAttrValue = do_GetAtom(aOldValue);
00691       }
00692       mutation.mAttrChange = modType;
00693       nsEventStatus status = nsEventStatus_eIgnore;
00694       HandleDOMEvent(nsnull, &mutation, nsnull,
00695                      NS_EVENT_FLAG_INIT, &status);
00696     }
00697 
00698     if (aNotify) {
00699       document->AttributeChanged(this, aNamespaceID, aAttribute, modType);
00700     }
00701   }
00702 
00703   if (aNamespaceID == kNameSpaceID_XMLEvents && 
00704       aAttribute == nsHTMLAtoms::_event && GetOwnerDoc()) {
00705     GetOwnerDoc()->AddXMLEventsContent(this);
00706   }
00707 
00708   return NS_OK;
00709 }
00710 
00711 void
00712 nsSVGElement::UpdateContentStyleRule()
00713 {
00714   NS_ASSERTION(!mContentStyleRule, "we already have a content style rule");
00715   
00716   // Bail early if there are no child attributes.
00717   // If this code were moved into the for-loop we could
00718   // avoid creating an mDeclaration even when there 
00719   // are non-style related attributes. See bug 270251.
00720   if (mAttrsAndChildren.AttrCount() == 0)
00721     return;
00722 
00723   nsIDocument* doc = GetOwnerDoc();
00724   if (!doc) {
00725     NS_ERROR("SVG element without owner document");
00726     return;
00727   }
00728   
00729   nsCOMPtr<nsIURI> baseURI = GetBaseURI();
00730   nsIURI *docURI = doc->GetDocumentURI();
00731   
00732   // Create the nsCSSDeclaration.
00733   nsCSSDeclaration* declaration = new nsCSSDeclaration();
00734   if (!declaration) {
00735     NS_WARNING("Failed to allocate nsCSSDeclaration");
00736     return;
00737   }
00738   if(!declaration->InitializeEmpty()) {
00739     NS_WARNING("could not initialize nsCSSDeclaration");
00740     declaration->RuleAbort();
00741     return;
00742   }
00743 
00744   // Try to fetch the CSS Parser from the document.
00745   nsICSSLoader* cssLoader = doc->CSSLoader();
00746   
00747   nsCOMPtr<nsICSSParser> parser;
00748   nsresult rv = NS_OK; 
00749   rv = cssLoader->GetParserFor(nsnull, getter_AddRefs(parser));
00750   
00751   if (NS_FAILED(rv)) {
00752     NS_WARNING("failed to get a css parser");
00753     declaration->RuleAbort();
00754     return;
00755   }
00756 
00757   // SVG and CSS differ slightly in their interpretation of some of
00758   // the attributes.  SVG allows attributes of the form: font-size="5" 
00759   // (style="font-size: 5" if using a style attribute)
00760   // where CSS requires units: font-size="5pt" (style="font-size: 5pt")
00761   // Set a flag to pass information to the parser so that we can use
00762   // the CSS parser to parse the font-size attribute.  Note that this
00763   // does *not* effect the use of CSS stylesheets, which will still
00764   // require units
00765   parser->SetSVGMode(PR_TRUE);
00766 
00767   PRUint32 attrCount = mAttrsAndChildren.AttrCount();
00768   for (PRUint32 i = 0; i < attrCount; ++i) {
00769     const nsAttrName* attrName = mAttrsAndChildren.GetSafeAttrNameAt(i);
00770     if (!attrName->IsAtom() || !IsAttributeMapped(attrName->Atom()))
00771       continue;
00772 
00773     nsAutoString name;
00774     attrName->Atom()->ToString(name);
00775 
00776     nsAutoString value;
00777     mAttrsAndChildren.AttrAt(i)->ToString(value);
00778 
00779     PRBool changed;
00780     parser->ParseProperty(nsCSSProps::LookupProperty(name), value,
00781                           docURI, baseURI,
00782                           declaration, &changed);
00783   }
00784   
00785   rv = NS_NewCSSStyleRule(getter_AddRefs(mContentStyleRule), nsnull, declaration);
00786   if (NS_FAILED(rv)) {
00787     NS_WARNING("could not create contentstylerule");
00788     declaration->RuleAbort();
00789   }
00790   
00791   // Recycle the parser
00792   parser->SetSVGMode(PR_FALSE);
00793   cssLoader->RecycleParser(parser);
00794 }
00795 
00796 nsISVGValue*
00797 nsSVGElement::GetMappedAttribute(PRInt32 aNamespaceID, nsIAtom* aName)
00798 {
00799   const nsAttrValue* attrVal = mMappedAttributes.GetAttr(aName, aNamespaceID);
00800   if (!attrVal)
00801     return nsnull;
00802 
00803   return attrVal->GetSVGValue();
00804 }
00805 
00806 nsresult
00807 nsSVGElement::AddMappedSVGValue(nsIAtom* aName, nsISupports* aValue,
00808                                 PRInt32 aNamespaceID)
00809 {
00810   nsresult rv;
00811   nsCOMPtr<nsISVGValue> svg_value = do_QueryInterface(aValue);
00812   svg_value->AddObserver(this);
00813   nsAttrValue attrVal(svg_value);
00814 
00815   if (aNamespaceID == kNameSpaceID_None) {
00816     rv = mMappedAttributes.SetAndTakeAttr(aName, attrVal);
00817     NS_ENSURE_SUCCESS(rv, rv);
00818   }
00819   else {
00820     nsCOMPtr<nsINodeInfo> ni;
00821     rv = mNodeInfo->NodeInfoManager()->GetNodeInfo(aName, nsnull,
00822                                                    aNamespaceID,
00823                                                    getter_AddRefs(ni));
00824     NS_ENSURE_SUCCESS(rv, rv);
00825 
00826     rv = mMappedAttributes.SetAndTakeAttr(ni, attrVal);
00827     NS_ENSURE_SUCCESS(rv, rv);
00828   }
00829 
00830   return NS_OK;
00831 }
00832 
00833 /* static */
00834 PRBool
00835 nsSVGElement::IsGraphicElementEventName(nsIAtom* aName)
00836 {
00837   const char* name;
00838   aName->GetUTF8String(&name);
00839 
00840   if (name[0] != 'o' || name[1] != 'n') {
00841     return PR_FALSE;
00842   }
00843 
00844   return (aName == nsSVGAtoms::onabort     ||
00845           aName == nsSVGAtoms::onclick     ||
00846           aName == nsSVGAtoms::onerror     ||
00847           aName == nsSVGAtoms::onload      ||
00848           aName == nsSVGAtoms::onmousedown ||
00849           aName == nsSVGAtoms::onmouseup   ||
00850           aName == nsSVGAtoms::onmouseover ||
00851           aName == nsSVGAtoms::onmousemove ||
00852           aName == nsSVGAtoms::onmouseout);
00853 }
00854 
00855 /* static */
00856 nsIAtom* nsSVGElement::GetEventNameForAttr(nsIAtom* aAttr)
00857 {
00858   if (aAttr == nsSVGAtoms::onload)
00859     return nsLayoutAtoms::onSVGLoad;
00860   if (aAttr == nsSVGAtoms::onunload)
00861     return nsLayoutAtoms::onSVGUnload;
00862   if (aAttr == nsSVGAtoms::onabort)
00863     return nsLayoutAtoms::onSVGAbort;
00864   if (aAttr == nsSVGAtoms::onerror)
00865     return nsLayoutAtoms::onSVGError;
00866   if (aAttr == nsSVGAtoms::onresize)
00867     return nsLayoutAtoms::onSVGResize;
00868   if (aAttr == nsSVGAtoms::onscroll)
00869     return nsLayoutAtoms::onSVGScroll;
00870   if (aAttr == nsSVGAtoms::onzoom)
00871     return nsLayoutAtoms::onSVGZoom;
00872 
00873   return aAttr;
00874 }