Back to index

lightning-sunbird  0.9+nobinonly
nsTextNode.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  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsGenericDOMDataNode.h"
00039 #include "nsIDOMText.h"
00040 #include "nsLayoutAtoms.h"
00041 #include "nsContentUtils.h"
00042 #include "nsIDOMEventListener.h"
00043 #include "nsIDOMEventTarget.h"
00044 #include "nsIDOMMutationEvent.h"
00045 #include "nsIAttribute.h"
00046 #include "nsIDocument.h"
00047 
00051 class nsTextNode : public nsGenericDOMDataNode,
00052                    public nsIDOMText
00053 {
00054 public:
00055   nsTextNode(nsNodeInfoManager *aNodeInfoManager);
00056   virtual ~nsTextNode();
00057 
00058   // nsISupports
00059   NS_DECL_ISUPPORTS_INHERITED
00060 
00061   // nsIDOMNode
00062   NS_IMPL_NSIDOMNODE_USING_GENERIC_DOM_DATA
00063 
00064   // nsIDOMCharacterData
00065   NS_FORWARD_NSIDOMCHARACTERDATA(nsGenericDOMDataNode::)
00066 
00067   // nsIDOMText
00068   NS_FORWARD_NSIDOMTEXT(nsGenericDOMDataNode::)
00069 
00070   // nsIContent
00071   virtual nsIAtom *Tag() const;
00072   virtual PRBool IsContentOfType(PRUint32 aFlags) const;
00073 #ifdef DEBUG
00074   virtual void List(FILE* out, PRInt32 aIndent) const;
00075   virtual void DumpContent(FILE* out, PRInt32 aIndent, PRBool aDumpAll) const;
00076 #endif
00077 
00078   virtual already_AddRefed<nsITextContent> CloneContent(PRBool aCloneText,
00079                                                         nsNodeInfoManager *aNodeInfoManager);
00080 };
00081 
00085 class nsAttributeTextNode : public nsTextNode {
00086 public:
00087   class nsAttrChangeListener : public nsIDOMEventListener {
00088   public:
00089     NS_DECL_ISUPPORTS
00090     NS_DECL_NSIDOMEVENTLISTENER
00091   
00092     nsAttrChangeListener(PRInt32 aNameSpaceID, nsIAtom* aAttrName,
00093                          nsITextContent* aContent) :
00094       mNameSpaceID(aNameSpaceID),
00095       mAttrName(aAttrName),
00096       mContent(aContent)
00097     {
00098       NS_ASSERTION(mNameSpaceID != kNameSpaceID_Unknown, "Must know namespace");
00099       NS_ASSERTION(mAttrName, "Must have attr name");
00100       NS_ASSERTION(mContent, "Must have text content");
00101     }
00102     
00103     virtual PRBool IsNativeAnonymous() const { return PR_TRUE; }
00104     virtual void SetNativeAnonymous(PRBool aAnonymous) {
00105       NS_ASSERTION(aAnonymous,
00106                    "Attempt to set nsAttributeTextNode not anonymous!");
00107     }
00108   protected:
00109     friend class nsAttributeTextNode;
00110     PRInt32 mNameSpaceID;
00111     nsCOMPtr<nsIAtom> mAttrName;
00112     nsITextContent* mContent;  // Weak ref; it owns us
00113   };
00114 
00115   nsAttributeTextNode(nsNodeInfoManager *aNodeInfoManager)
00116     : nsTextNode(aNodeInfoManager)
00117   {
00118   }
00119   virtual ~nsAttributeTextNode() {
00120     DetachListener();
00121   }
00122 
00123   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
00124                               nsIContent* aBindingParent,
00125                               PRBool aCompileEventHandlers);
00126   virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
00127                               PRBool aNullParent = PR_TRUE);
00128 
00129   nsRefPtr<nsAttrChangeListener> mListener;  // our listener
00130 private:
00131   void DetachListener();
00132 };
00133 
00134 nsresult
00135 NS_NewTextNode(nsITextContent** aInstancePtrResult,
00136                nsNodeInfoManager *aNodeInfoManager)
00137 {
00138   NS_PRECONDITION(aNodeInfoManager, "Missing nodeInfoManager");
00139 
00140   *aInstancePtrResult = nsnull;
00141 
00142   nsCOMPtr<nsITextContent> instance = new nsTextNode(aNodeInfoManager);
00143   if (!instance) {
00144     return NS_ERROR_OUT_OF_MEMORY;
00145   }
00146 
00147   NS_ADDREF(*aInstancePtrResult = instance);
00148 
00149   return NS_OK;
00150 }
00151 
00152 nsTextNode::nsTextNode(nsNodeInfoManager *aNodeInfoManager)
00153   : nsGenericDOMDataNode(aNodeInfoManager)
00154 {
00155 }
00156 
00157 nsTextNode::~nsTextNode()
00158 {
00159 }
00160 
00161 NS_IMPL_ADDREF_INHERITED(nsTextNode, nsGenericDOMDataNode)
00162 NS_IMPL_RELEASE_INHERITED(nsTextNode, nsGenericDOMDataNode)
00163 
00164 
00165 // QueryInterface implementation for nsTextNode
00166 NS_INTERFACE_MAP_BEGIN(nsTextNode)
00167   NS_INTERFACE_MAP_ENTRY(nsITextContent)
00168   NS_INTERFACE_MAP_ENTRY(nsIDOMNode)
00169   NS_INTERFACE_MAP_ENTRY(nsIDOMText)
00170   NS_INTERFACE_MAP_ENTRY(nsIDOMCharacterData)
00171   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(Text)
00172 NS_INTERFACE_MAP_END_INHERITING(nsGenericDOMDataNode)
00173 
00174 nsIAtom *
00175 nsTextNode::Tag() const
00176 {
00177   return nsLayoutAtoms::textTagName;
00178 }
00179 
00180 NS_IMETHODIMP
00181 nsTextNode::GetNodeName(nsAString& aNodeName)
00182 {
00183   aNodeName.AssignLiteral("#text");
00184   return NS_OK;
00185 }
00186 
00187 NS_IMETHODIMP
00188 nsTextNode::GetNodeValue(nsAString& aNodeValue)
00189 {
00190   return nsGenericDOMDataNode::GetNodeValue(aNodeValue);
00191 }
00192 
00193 NS_IMETHODIMP
00194 nsTextNode::SetNodeValue(const nsAString& aNodeValue)
00195 {
00196   return nsGenericDOMDataNode::SetNodeValue(aNodeValue);
00197 }
00198 
00199 NS_IMETHODIMP
00200 nsTextNode::GetNodeType(PRUint16* aNodeType)
00201 {
00202   *aNodeType = (PRUint16)nsIDOMNode::TEXT_NODE;
00203   return NS_OK;
00204 }
00205 
00206 NS_IMETHODIMP
00207 nsTextNode::CloneNode(PRBool aDeep, nsIDOMNode** aReturn)
00208 {
00209   nsCOMPtr<nsITextContent> textContent = CloneContent(PR_TRUE,
00210                                                       GetNodeInfoManager());
00211   NS_ENSURE_TRUE(textContent, NS_ERROR_OUT_OF_MEMORY);
00212 
00213   return CallQueryInterface(textContent, aReturn);
00214 }
00215 
00216 already_AddRefed<nsITextContent>
00217 nsTextNode::CloneContent(PRBool aCloneText, nsNodeInfoManager *aNodeInfoManager)
00218 {
00219   nsTextNode* it = new nsTextNode(aNodeInfoManager);
00220   if (!it)
00221     return nsnull;
00222 
00223   if (aCloneText) {
00224     it->mText = mText;
00225   }
00226 
00227   NS_ADDREF(it);
00228 
00229   return it;
00230 }
00231 
00232 PRBool
00233 nsTextNode::IsContentOfType(PRUint32 aFlags) const
00234 {
00235   return !(aFlags & ~eTEXT);
00236 }
00237 
00238 #ifdef DEBUG
00239 void
00240 nsTextNode::List(FILE* out, PRInt32 aIndent) const
00241 {
00242   NS_PRECONDITION(IsInDoc(), "bad content");
00243 
00244   PRInt32 index;
00245   for (index = aIndent; --index >= 0; ) fputs("  ", out);
00246 
00247   fprintf(out, "Text@%p refcount=%d<", this, mRefCnt.get());
00248 
00249   nsAutoString tmp;
00250   ToCString(tmp, 0, mText.GetLength());
00251   fputs(NS_LossyConvertUCS2toASCII(tmp).get(), out);
00252 
00253   fputs(">\n", out);
00254 }
00255 
00256 void
00257 nsTextNode::DumpContent(FILE* out, PRInt32 aIndent, PRBool aDumpAll) const
00258 {
00259   NS_PRECONDITION(IsInDoc(), "bad content");
00260 
00261   if(aDumpAll) {
00262     PRInt32 index;
00263     for (index = aIndent; --index >= 0; ) fputs("  ", out);
00264 
00265     nsAutoString tmp;
00266     ToCString(tmp, 0, mText.GetLength());
00267 
00268     if(!tmp.EqualsLiteral("\\n")) {
00269       fputs(NS_LossyConvertUCS2toASCII(tmp).get(), out);
00270       if(aIndent) fputs("\n", out);
00271     }
00272   }
00273 }
00274 #endif
00275 
00276 NS_IMPL_ISUPPORTS1(nsAttributeTextNode::nsAttrChangeListener,
00277                    nsIDOMEventListener)
00278 
00279 NS_IMETHODIMP
00280 nsAttributeTextNode::nsAttrChangeListener::HandleEvent(nsIDOMEvent* aEvent)
00281 {
00282   // If mContent is null while we still get events, something is very wrong
00283   NS_ENSURE_TRUE(mContent, NS_ERROR_UNEXPECTED);
00284   
00285   nsCOMPtr<nsIDOMMutationEvent> event(do_QueryInterface(aEvent));
00286   NS_ENSURE_TRUE(event, NS_ERROR_UNEXPECTED);
00287 
00288   nsCOMPtr<nsIDOMEventTarget> target;
00289   event->GetTarget(getter_AddRefs(target));
00290   // Can't compare as an nsIDOMEventTarget, since that's implemented via a
00291   // tearoff...
00292   nsCOMPtr<nsIContent> targetContent(do_QueryInterface(target));
00293   if (targetContent != mContent->GetParent()) {
00294     // not the right node
00295     return NS_OK;
00296   }
00297   
00298   nsCOMPtr<nsIDOMNode> relatedNode;
00299   event->GetRelatedNode(getter_AddRefs(relatedNode));
00300   NS_ENSURE_TRUE(relatedNode, NS_ERROR_UNEXPECTED);
00301   nsCOMPtr<nsIAttribute> attr(do_QueryInterface(relatedNode));
00302   NS_ENSURE_TRUE(attr, NS_ERROR_UNEXPECTED);
00303 
00304   if (attr->NodeInfo()->Equals(mAttrName, mNameSpaceID)) {
00305     nsAutoString value;
00306     targetContent->GetAttr(mNameSpaceID, mAttrName, value);
00307     mContent->SetText(value, PR_TRUE);
00308   }
00309   
00310   return NS_OK;
00311 }
00312 
00313 nsresult
00314 NS_NewAttributeContent(nsNodeInfoManager *aNodeInfoManager,
00315                        PRInt32 aNameSpaceID, nsIAtom* aAttrName,
00316                        nsIContent** aResult)
00317 {
00318   NS_PRECONDITION(aNodeInfoManager, "Missing nodeInfoManager");
00319   NS_PRECONDITION(aAttrName, "Must have an attr name");
00320   NS_PRECONDITION(aNameSpaceID != kNameSpaceID_Unknown, "Must know namespace");
00321   
00322   *aResult = nsnull;
00323   
00324   nsRefPtr<nsAttributeTextNode> textNode =
00325     new nsAttributeTextNode(aNodeInfoManager);
00326   if (!textNode) {
00327     return NS_ERROR_OUT_OF_MEMORY;
00328   }
00329 
00330   textNode->mListener =
00331     new nsAttributeTextNode::nsAttrChangeListener(aNameSpaceID,
00332                                                   aAttrName,
00333                                                   textNode);
00334   NS_ENSURE_TRUE(textNode->mListener, NS_ERROR_OUT_OF_MEMORY);
00335 
00336   NS_ADDREF(*aResult = textNode);
00337   return NS_OK;
00338 }
00339 
00340 nsresult
00341 nsAttributeTextNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
00342                                 nsIContent* aBindingParent,
00343                                 PRBool aCompileEventHandlers)
00344 {
00345   NS_PRECONDITION(aParent, "This node can't be a child of the document");
00346 
00347   nsresult rv = nsTextNode::BindToTree(aDocument, aParent,
00348                                        aBindingParent, aCompileEventHandlers);
00349   NS_ENSURE_SUCCESS(rv, rv);
00350   
00351   if (mListener) {
00352     // Attach it to the new parent
00353     
00354     nsCOMPtr<nsIDOMEventTarget> eventTarget(do_QueryInterface(aParent));
00355     NS_ENSURE_TRUE(eventTarget, NS_ERROR_UNEXPECTED);
00356   
00357     rv = eventTarget->AddEventListener(NS_LITERAL_STRING("DOMAttrModified"),
00358                                        mListener,
00359                                        PR_FALSE);
00360     NS_ENSURE_SUCCESS(rv, rv);
00361 
00362     nsAutoString attrValue;
00363     aParent->GetAttr(mListener->mNameSpaceID, mListener->mAttrName,
00364                      attrValue);
00365     SetData(attrValue);
00366   }
00367 
00368   return NS_OK;
00369 }
00370 
00371 void
00372 nsAttributeTextNode::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
00373 {
00374   // Detach the listener while we know who our parent is!
00375   if (aNullParent) {
00376     DetachListener();
00377   }
00378   nsTextNode::UnbindFromTree(aDeep, aNullParent);
00379 }
00380 
00381 void
00382 nsAttributeTextNode::DetachListener()
00383 {
00384   if (!mListener)
00385     return;
00386 
00387   nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(GetParent()));
00388   if (target) {
00389 #ifdef DEBUG
00390     nsresult rv =
00391 #endif
00392       target->RemoveEventListener(NS_LITERAL_STRING("DOMAttrModified"),
00393                                   mListener,
00394                                   PR_FALSE);
00395     NS_ASSERTION(NS_SUCCEEDED(rv), "'Leaking' listener for lifetime of this page");
00396   }
00397   mListener->mContent = nsnull;  // Make it forget us
00398   mListener = nsnull; // Goodbye, listener
00399 }