Back to index

lightning-sunbird  0.9+nobinonly
nsHTMLEditorStyle.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.org 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  *   Daniel Glazman <glazman@netscape.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 #include "nsUnicharUtils.h"
00039 
00040 #include "nsHTMLEditor.h"
00041 #include "nsHTMLEditRules.h"
00042 #include "nsTextEditUtils.h"
00043 #include "nsHTMLEditUtils.h"
00044 #include "nsIDOMNodeList.h"
00045 #include "nsIDOMAttr.h"
00046 #include "nsIDOMKeyListener.h" 
00047 #include "nsIDOMMouseListener.h"
00048 #include "nsIDOMMouseEvent.h"
00049 #include "nsISelection.h"
00050 #include "nsISelectionPrivate.h"
00051 #include "nsIDOMHTMLImageElement.h"
00052 #include "nsISelectionController.h"
00053 #include "nsICSSLoader.h"
00054 #include "nsICSSStyleSheet.h"
00055 #include "nsIDocumentObserver.h"
00056 #include "TypeInState.h"
00057 
00058 #include "nsIEnumerator.h"
00059 #include "nsIContent.h"
00060 #include "nsIContentIterator.h"
00061 
00062 
00063 NS_IMETHODIMP nsHTMLEditor::AddDefaultProperty(nsIAtom *aProperty, 
00064                                             const nsAString & aAttribute, 
00065                                             const nsAString & aValue)
00066 {
00067   nsString outValue;
00068   PRInt32 index;
00069   nsString attr(aAttribute);
00070   if (TypeInState::FindPropInList(aProperty, attr, &outValue, mDefaultStyles, index))
00071   {
00072     PropItem *item = (PropItem*)mDefaultStyles[index];
00073     item->value = aValue;
00074   }
00075   else
00076   {
00077     nsString value(aValue);
00078     PropItem *propItem = new PropItem(aProperty, attr, value);
00079     mDefaultStyles.AppendElement((void*)propItem);
00080   }
00081   return NS_OK;
00082 }
00083 
00084 NS_IMETHODIMP nsHTMLEditor::RemoveDefaultProperty(nsIAtom *aProperty, 
00085                                    const nsAString & aAttribute, 
00086                                    const nsAString & aValue)
00087 {
00088   nsString outValue;
00089   PRInt32 index;
00090   nsString attr(aAttribute);
00091   if (TypeInState::FindPropInList(aProperty, attr, &outValue, mDefaultStyles, index))
00092   {
00093     PropItem *item = (PropItem*)mDefaultStyles[index];
00094     if (item) delete item;
00095     mDefaultStyles.RemoveElementAt(index);
00096   }
00097   return NS_OK;
00098 }
00099 
00100 NS_IMETHODIMP nsHTMLEditor::RemoveAllDefaultProperties()
00101 {
00102   PRInt32 j, defcon = mDefaultStyles.Count();
00103   for (j=0; j<defcon; j++)
00104   {
00105     PropItem *item = (PropItem*)mDefaultStyles[j];
00106     if (item) delete item;
00107   }
00108   mDefaultStyles.Clear();
00109   return NS_OK;
00110 }
00111 
00112 
00113 // Add the CSS style corresponding to the HTML inline style defined
00114 // by aProperty aAttribute and aValue to the selection
00115 NS_IMETHODIMP nsHTMLEditor::SetCSSInlineProperty(nsIAtom *aProperty, 
00116                             const nsAString & aAttribute, 
00117                             const nsAString & aValue)
00118 {
00119   PRBool useCSS;
00120   GetIsCSSEnabled(&useCSS);
00121   if (useCSS) {
00122     return SetInlineProperty(aProperty, aAttribute, aValue);
00123   }
00124   return NS_OK;
00125 }
00126 
00127 NS_IMETHODIMP nsHTMLEditor::SetInlineProperty(nsIAtom *aProperty, 
00128                             const nsAString & aAttribute, 
00129                             const nsAString & aValue)
00130 {
00131   if (!aProperty) { return NS_ERROR_NULL_POINTER; }
00132   if (!mRules) { return NS_ERROR_NOT_INITIALIZED; }
00133   ForceCompositionEnd();
00134 
00135   nsCOMPtr<nsISelection>selection;
00136   nsresult res = GetSelection(getter_AddRefs(selection));
00137   if (NS_FAILED(res)) return res;
00138   if (!selection) return NS_ERROR_NULL_POINTER;
00139   nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
00140 
00141   PRBool isCollapsed;
00142   selection->GetIsCollapsed(&isCollapsed);
00143   if (isCollapsed)
00144   {
00145     // manipulating text attributes on a collapsed selection only sets state for the next text insertion
00146     nsString tAttr(aAttribute);//MJUDGE SCC NEED HELP
00147     nsString tVal(aValue);//MJUDGE SCC NEED HELP
00148     return mTypeInState->SetProp(aProperty, tAttr, tVal);
00149   }
00150   
00151   nsAutoEditBatch batchIt(this);
00152   nsAutoRules beginRulesSniffing(this, kOpInsertElement, nsIEditor::eNext);
00153   nsAutoSelectionReset selectionResetter(selection, this);
00154   nsAutoTxnsConserveSelection dontSpazMySelection(this);
00155   
00156   PRBool cancel, handled;
00157   nsTextRulesInfo ruleInfo(nsTextEditRules::kSetTextProperty);
00158   res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
00159   if (NS_FAILED(res)) return res;
00160   if (!cancel && !handled)
00161   {
00162     // get selection range enumerator
00163     nsCOMPtr<nsIEnumerator> enumerator;
00164     res = selPriv->GetEnumerator(getter_AddRefs(enumerator));
00165     if (NS_FAILED(res)) return res;
00166     if (!enumerator)    return NS_ERROR_FAILURE;
00167 
00168     // loop thru the ranges in the selection
00169     enumerator->First(); 
00170     nsCOMPtr<nsISupports> currentItem;
00171     while ((NS_ENUMERATOR_FALSE == enumerator->IsDone()))
00172     {
00173       res = enumerator->CurrentItem(getter_AddRefs(currentItem));
00174       if (NS_FAILED(res)) return res;
00175       if (!currentItem)   return NS_ERROR_FAILURE;
00176       
00177       nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
00178 
00179       // adjust range to include any ancestors who's children are entirely selected
00180       res = PromoteInlineRange(range);
00181       if (NS_FAILED(res)) return res;
00182       
00183       // check for easy case: both range endpoints in same text node
00184       nsCOMPtr<nsIDOMNode> startNode, endNode;
00185       res = range->GetStartContainer(getter_AddRefs(startNode));
00186       if (NS_FAILED(res)) return res;
00187       res = range->GetEndContainer(getter_AddRefs(endNode));
00188       if (NS_FAILED(res)) return res;
00189       if ((startNode == endNode) && IsTextNode(startNode))
00190       {
00191         PRInt32 startOffset, endOffset;
00192         range->GetStartOffset(&startOffset);
00193         range->GetEndOffset(&endOffset);
00194         nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
00195         res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, endOffset, aProperty, &aAttribute, &aValue);
00196         if (NS_FAILED(res)) return res;
00197       }
00198       else
00199       {
00200         // not the easy case.  range not contained in single text node. 
00201         // there are up to three phases here.  There are all the nodes
00202         // reported by the subtree iterator to be processed.  And there
00203         // are potentially a starting textnode and an ending textnode
00204         // which are only partially contained by the range.
00205         
00206         // lets handle the nodes reported by the iterator.  These nodes
00207         // are entirely contained in the selection range.  We build up
00208         // a list of them (since doing operations on the document during
00209         // iteration would perturb the iterator).
00210 
00211         nsCOMPtr<nsIContentIterator> iter =
00212           do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &res);
00213         if (NS_FAILED(res)) return res;
00214         if (!iter)          return NS_ERROR_FAILURE;
00215 
00216         nsCOMArray<nsIDOMNode> arrayOfNodes;
00217         nsCOMPtr<nsIDOMNode> node;
00218         
00219         // iterate range and build up array
00220         res = iter->Init(range);
00221         // init returns an error if no nodes in range.
00222         // this can easily happen with the subtree 
00223         // iterator if the selection doesn't contain
00224         // any *whole* nodes.
00225         if (NS_SUCCEEDED(res))
00226         {
00227           while (!iter->IsDone())
00228           {
00229             node = do_QueryInterface(iter->GetCurrentNode());
00230             if (!node)
00231               return NS_ERROR_FAILURE;
00232 
00233             if (IsEditable(node))
00234             { 
00235               arrayOfNodes.AppendObject(node);
00236             }
00237 
00238             iter->Next();
00239           }
00240         }
00241         // first check the start parent of the range to see if it needs to 
00242         // be separately handled (it does if it's a text node, due to how the
00243         // subtree iterator works - it will not have reported it).
00244         if (IsTextNode(startNode) && IsEditable(startNode))
00245         {
00246           nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
00247           PRInt32 startOffset;
00248           PRUint32 textLen;
00249           range->GetStartOffset(&startOffset);
00250           nodeAsText->GetLength(&textLen);
00251           res = SetInlinePropertyOnTextNode(nodeAsText, startOffset, textLen, aProperty, &aAttribute, &aValue);
00252           if (NS_FAILED(res)) return res;
00253         }
00254         
00255         // then loop through the list, set the property on each node
00256         PRInt32 listCount = arrayOfNodes.Count();
00257         PRInt32 j;
00258         for (j = 0; j < listCount; j++)
00259         {
00260           node = arrayOfNodes[j];
00261           res = SetInlinePropertyOnNode(node, aProperty, &aAttribute, &aValue);
00262           if (NS_FAILED(res)) return res;
00263         }
00264         arrayOfNodes.Clear();
00265         
00266         // last check the end parent of the range to see if it needs to 
00267         // be separately handled (it does if it's a text node, due to how the
00268         // subtree iterator works - it will not have reported it).
00269         if (IsTextNode(endNode) && IsEditable(endNode))
00270         {
00271           nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(endNode);
00272           PRInt32 endOffset;
00273           range->GetEndOffset(&endOffset);
00274           res = SetInlinePropertyOnTextNode(nodeAsText, 0, endOffset, aProperty, &aAttribute, &aValue);
00275           if (NS_FAILED(res)) return res;
00276         }
00277       }
00278       enumerator->Next();
00279     }
00280   }
00281   if (!cancel)
00282   {
00283     // post-process
00284     res = mRules->DidDoAction(selection, &ruleInfo, res);
00285   }
00286   return res;
00287 }
00288 
00289 
00290 
00291 nsresult
00292 nsHTMLEditor::SetInlinePropertyOnTextNode( nsIDOMCharacterData *aTextNode, 
00293                                             PRInt32 aStartOffset,
00294                                             PRInt32 aEndOffset,
00295                                             nsIAtom *aProperty, 
00296                                             const nsAString *aAttribute,
00297                                             const nsAString *aValue)
00298 {
00299   if (!aTextNode) return NS_ERROR_NULL_POINTER;
00300   nsCOMPtr<nsIDOMNode> parent;
00301   nsresult res = aTextNode->GetParentNode(getter_AddRefs(parent));
00302   if (NS_FAILED(res)) return res;
00303 
00304   nsAutoString tagString;
00305   aProperty->ToString(tagString);
00306   if (!CanContainTag(parent, tagString)) return NS_OK;
00307   
00308   // don't need to do anything if no characters actually selected
00309   if (aStartOffset == aEndOffset) return NS_OK;
00310   
00311   nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aTextNode);
00312   
00313   // don't need to do anything if property already set on node
00314   PRBool bHasProp;
00315   PRBool useCSS;
00316   GetIsCSSEnabled(&useCSS);
00317 
00318   if (useCSS &&
00319       mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty, aAttribute)) {
00320     // the HTML styles defined by aProperty/aAttribute has a CSS equivalence
00321     // in this implementation for node; let's check if it carries those css styles
00322     nsAutoString value;
00323     if (aValue) value.Assign(*aValue);
00324     mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(node, aProperty, aAttribute,
00325                                                        bHasProp, value,
00326                                                        COMPUTED_STYLE_TYPE);
00327   }
00328   else
00329   {
00330     nsCOMPtr<nsIDOMNode> styleNode;
00331     IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, bHasProp, getter_AddRefs(styleNode));
00332   }
00333 
00334   if (bHasProp) return NS_OK;
00335   
00336   // do we need to split the text node?
00337   PRUint32 textLen;
00338   aTextNode->GetLength(&textLen);
00339   
00340   nsCOMPtr<nsIDOMNode> tmp;
00341   if ( (PRUint32)aEndOffset != textLen )
00342   {
00343     // we need to split off back of text node
00344     res = SplitNode(node, aEndOffset, getter_AddRefs(tmp));
00345     if (NS_FAILED(res)) return res;
00346     node = tmp;  // remember left node
00347   }
00348   if ( aStartOffset )
00349   {
00350     // we need to split off front of text node
00351     res = SplitNode(node, aStartOffset, getter_AddRefs(tmp));
00352     if (NS_FAILED(res)) return res;
00353   }
00354   
00355   // look for siblings that are correct type of node
00356   nsCOMPtr<nsIDOMNode> sibling;
00357   GetPriorHTMLSibling(node, address_of(sibling));
00358   if (sibling && NodeIsType(sibling, aProperty) &&         
00359       HasAttrVal(sibling, aAttribute, aValue) &&
00360       IsOnlyAttribute(sibling, aAttribute) )
00361   {
00362     // previous sib is already right kind of inline node; slide this over into it
00363     res = MoveNode(node, sibling, -1);
00364     return res;
00365   }
00366   sibling = nsnull;
00367   GetNextHTMLSibling(node, address_of(sibling));
00368   if (sibling && NodeIsType(sibling, aProperty) &&         
00369       HasAttrVal(sibling, aAttribute, aValue) &&
00370       IsOnlyAttribute(sibling, aAttribute) )
00371   {
00372     // following sib is already right kind of inline node; slide this over into it
00373     res = MoveNode(node, sibling, 0);
00374     return res;
00375   }
00376   
00377   // reparent the node inside inline node with appropriate {attribute,value}
00378   return SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue);
00379 }
00380 
00381 
00382 nsresult
00383 nsHTMLEditor::SetInlinePropertyOnNode( nsIDOMNode *aNode,
00384                                        nsIAtom *aProperty, 
00385                                        const nsAString *aAttribute,
00386                                        const nsAString *aValue)
00387 {
00388   if (!aNode || !aProperty) return NS_ERROR_NULL_POINTER;
00389 
00390   nsresult res = NS_OK;
00391   nsCOMPtr<nsIDOMNode> tmp;
00392   nsAutoString tag;
00393   aProperty->ToString(tag);
00394   ToLowerCase(tag);
00395   
00396   PRBool useCSS;
00397   GetIsCSSEnabled(&useCSS);
00398 
00399   if (useCSS)
00400   {
00401     // we are in CSS mode
00402     if (mHTMLCSSUtils->IsCSSEditableProperty(aNode, aProperty, aAttribute))
00403     {
00404       // the HTML style defined by aProperty/aAttribute has a CSS equivalence
00405       // in this implementation for the node aNode
00406       nsCOMPtr<nsIDOMNode> tmp = aNode;
00407       if (IsTextNode(tmp))
00408       {
00409         // we are working on a text node and need to create a span container
00410         // that will carry the styles
00411         InsertContainerAbove( aNode, 
00412                               address_of(tmp), 
00413                               NS_LITERAL_STRING("span"),
00414                               nsnull,
00415                               nsnull);
00416       }
00417       nsCOMPtr<nsIDOMElement>element;
00418       element = do_QueryInterface(tmp);
00419       // first we have to remove occurences of the same style hint in the
00420       // children of the aNode
00421       res = RemoveStyleInside(tmp, aProperty, aAttribute, PR_TRUE);
00422       if (NS_FAILED(res)) return res;
00423       PRInt32 count;
00424       // then we add the css styles corresponding to the HTML style request
00425       res = mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(element, aProperty, aAttribute, aValue, &count, PR_FALSE);
00426       if (NS_FAILED(res)) return res;
00427 
00428       nsCOMPtr<nsIDOMNode> nextSibling, previousSibling;
00429       GetNextHTMLSibling(tmp, address_of(nextSibling));
00430       GetPriorHTMLSibling(tmp, address_of(previousSibling));
00431       if (nextSibling || previousSibling)
00432       {
00433         nsCOMPtr<nsIDOMNode> mergeParent;
00434         res = tmp->GetParentNode(getter_AddRefs(mergeParent));
00435         if (NS_FAILED(res)) return res;
00436         if (previousSibling &&
00437             nsEditor::NodeIsType(previousSibling, nsEditProperty::span) &&
00438             NodesSameType(tmp, previousSibling))
00439         {
00440           res = JoinNodes(previousSibling, tmp, mergeParent);
00441           if (NS_FAILED(res)) return res;
00442         }
00443         if (nextSibling &&
00444             nsEditor::NodeIsType(nextSibling, nsEditProperty::span) &&
00445             NodesSameType(tmp, nextSibling))
00446         {
00447           res = JoinNodes(tmp, nextSibling, mergeParent);
00448         }
00449       }
00450       return res;
00451     }
00452   }
00453   
00454   // dont need to do anything if property already set on node
00455   PRBool bHasProp;
00456   nsCOMPtr<nsIDOMNode> styleNode;
00457   IsTextPropertySetByContent(aNode, aProperty, aAttribute, aValue, bHasProp, getter_AddRefs(styleNode));
00458   if (bHasProp) return NS_OK;
00459 
00460   // is it already the right kind of node, but with wrong attribute?
00461   if (NodeIsType(aNode, aProperty))
00462   {
00463     // just set the attribute on it.
00464     // but first remove any contrary style in it's children.
00465     res = RemoveStyleInside(aNode, aProperty, aAttribute, PR_TRUE);
00466     if (NS_FAILED(res)) return res;
00467     nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
00468     return SetAttribute(elem, *aAttribute, *aValue);
00469   }
00470   
00471   // can it be put inside inline node?
00472   if (TagCanContain(tag, aNode))
00473   {
00474     nsCOMPtr<nsIDOMNode> priorNode, nextNode;
00475     // is either of it's neighbors the right kind of node?
00476     GetPriorHTMLSibling(aNode, address_of(priorNode));
00477     GetNextHTMLSibling(aNode, address_of(nextNode));
00478     if (priorNode && NodeIsType(priorNode, aProperty) && 
00479         HasAttrVal(priorNode, aAttribute, aValue)     &&
00480         IsOnlyAttribute(priorNode, aAttribute) )
00481     {
00482       // previous sib is already right kind of inline node; slide this over into it
00483       res = MoveNode(aNode, priorNode, -1);
00484     }
00485     else if (nextNode && NodeIsType(nextNode, aProperty) && 
00486              HasAttrVal(nextNode, aAttribute, aValue)    &&
00487              IsOnlyAttribute(priorNode, aAttribute) )
00488     {
00489       // following sib is already right kind of inline node; slide this over into it
00490       res = MoveNode(aNode, nextNode, 0);
00491     }
00492     else
00493     {
00494       // ok, chuck it in it's very own container
00495       res = InsertContainerAbove(aNode, address_of(tmp), tag, aAttribute, aValue);
00496     }
00497     if (NS_FAILED(res)) return res;
00498     return RemoveStyleInside(aNode, aProperty, aAttribute);
00499   }
00500   // none of the above?  then cycle through the children.
00501   nsCOMPtr<nsIDOMNodeList> childNodes;
00502   res = aNode->GetChildNodes(getter_AddRefs(childNodes));
00503   if (NS_FAILED(res)) return res;
00504   if (childNodes)
00505   {
00506     PRInt32 j;
00507     PRUint32 childCount;
00508     childNodes->GetLength(&childCount);
00509     if (childCount)
00510     {
00511       nsCOMArray<nsIDOMNode> arrayOfNodes;
00512       nsCOMPtr<nsIDOMNode> node;
00513       
00514       // populate the list
00515       for (j=0 ; j < (PRInt32)childCount; j++)
00516       {
00517         nsCOMPtr<nsIDOMNode> childNode;
00518         res = childNodes->Item(j, getter_AddRefs(childNode));
00519         if ((NS_SUCCEEDED(res)) && (childNode) && IsEditable(childNode))
00520         {
00521           arrayOfNodes.AppendObject(childNode);
00522         }
00523       }
00524       
00525       // then loop through the list, set the property on each node
00526       PRInt32 listCount = arrayOfNodes.Count();
00527       for (j = 0; j < listCount; j++)
00528       {
00529         node = arrayOfNodes[j];
00530         res = SetInlinePropertyOnNode(node, aProperty, aAttribute, aValue);
00531         if (NS_FAILED(res)) return res;
00532       }
00533       arrayOfNodes.Clear();
00534     }
00535   }
00536   return res;
00537 }
00538 
00539 
00540 nsresult nsHTMLEditor::SplitStyleAboveRange(nsIDOMRange *inRange, 
00541                                             nsIAtom *aProperty, 
00542                                             const nsAString *aAttribute)
00543 {
00544   if (!inRange) return NS_ERROR_NULL_POINTER;
00545   nsresult res;
00546   nsCOMPtr<nsIDOMNode> startNode, endNode, origStartNode;
00547   PRInt32 startOffset, endOffset, origStartOffset;
00548   
00549   res = inRange->GetStartContainer(getter_AddRefs(startNode));
00550   if (NS_FAILED(res)) return res;
00551   res = inRange->GetStartOffset(&startOffset);
00552   if (NS_FAILED(res)) return res;
00553   res = inRange->GetEndContainer(getter_AddRefs(endNode));
00554   if (NS_FAILED(res)) return res;
00555   res = inRange->GetEndOffset(&endOffset);
00556   if (NS_FAILED(res)) return res;
00557   
00558   origStartNode = startNode;
00559   origStartOffset = startOffset;
00560   
00561   // split any matching style nodes above the start of range
00562   {
00563     nsAutoTrackDOMPoint tracker(mRangeUpdater, address_of(endNode), &endOffset);
00564     res = SplitStyleAbovePoint(address_of(startNode), &startOffset, aProperty, aAttribute);
00565     if (NS_FAILED(res)) return res;
00566   }
00567 
00568   // second verse, same as the first...
00569   res = SplitStyleAbovePoint(address_of(endNode), &endOffset, aProperty, aAttribute);
00570   if (NS_FAILED(res)) return res;
00571   
00572   // reset the range
00573   res = inRange->SetStart(startNode, startOffset);
00574   if (NS_FAILED(res)) return res;
00575   res = inRange->SetEnd(endNode, endOffset);
00576   return res;
00577 }
00578 
00579 nsresult nsHTMLEditor::SplitStyleAbovePoint(nsCOMPtr<nsIDOMNode> *aNode,
00580                                            PRInt32 *aOffset,
00581                                            nsIAtom *aProperty,          // null here means we split all properties
00582                                            const nsAString *aAttribute,
00583                                            nsCOMPtr<nsIDOMNode> *outLeftNode,
00584                                            nsCOMPtr<nsIDOMNode> *outRightNode)
00585 {
00586   if (!aNode || !*aNode || !aOffset) return NS_ERROR_NULL_POINTER;
00587   if (outLeftNode)  *outLeftNode  = nsnull;
00588   if (outRightNode) *outRightNode = nsnull;
00589   // split any matching style nodes above the node/offset
00590   nsCOMPtr<nsIDOMNode> parent, tmp = *aNode;
00591   PRInt32 offset;
00592 
00593   PRBool useCSS;
00594   GetIsCSSEnabled(&useCSS);
00595 
00596   PRBool isSet;
00597   while (tmp && !IsBlockNode(tmp))
00598   {
00599     isSet = PR_FALSE;
00600     if (useCSS && mHTMLCSSUtils->IsCSSEditableProperty(tmp, aProperty, aAttribute)) {
00601       // the HTML style defined by aProperty/aAttribute has a CSS equivalence
00602       // in this implementation for the node tmp; let's check if it carries those css styles
00603       nsAutoString firstValue;
00604       mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(tmp, aProperty, aAttribute,
00605                                                          isSet, firstValue,
00606                                                          SPECIFIED_STYLE_TYPE);
00607     }
00608     if ( (aProperty && NodeIsType(tmp, aProperty)) ||   // node is the correct inline prop
00609          (aProperty == nsEditProperty::href && nsHTMLEditUtils::IsLink(tmp)) ||
00610                                                         // node is href - test if really <a href=...
00611          (!aProperty && NodeIsProperty(tmp)) ||         // or node is any prop, and we asked to split them all
00612          isSet)                                         // or the style is specified in the style attribute
00613     {
00614       // found a style node we need to split
00615       SplitNodeDeep(tmp, *aNode, *aOffset, &offset, PR_FALSE, outLeftNode, outRightNode);
00616       // reset startNode/startOffset
00617       tmp->GetParentNode(getter_AddRefs(*aNode));
00618       *aOffset = offset;
00619     }
00620     tmp->GetParentNode(getter_AddRefs(parent));
00621     tmp = parent;
00622   }
00623   return NS_OK;
00624 }
00625 
00626 PRBool nsHTMLEditor::NodeIsProperty(nsIDOMNode *aNode)
00627 {
00628   if (!aNode)               return PR_FALSE;
00629   if (!IsContainer(aNode))  return PR_FALSE;
00630   if (!IsEditable(aNode))   return PR_FALSE;
00631   if (IsBlockNode(aNode))   return PR_FALSE;
00632   if (NodeIsType(aNode, nsEditProperty::a)) return PR_FALSE;
00633   return PR_TRUE;
00634 }
00635 
00636 nsresult nsHTMLEditor::ApplyDefaultProperties()
00637 {
00638   nsresult res = NS_OK;
00639   PRInt32 j, defcon = mDefaultStyles.Count();
00640   for (j=0; j<defcon; j++)
00641   {
00642     PropItem *propItem = (PropItem*)mDefaultStyles[j];
00643     if (!propItem) 
00644       return NS_ERROR_NULL_POINTER;
00645     res = SetInlineProperty(propItem->tag, propItem->attr, propItem->value);
00646     if (NS_FAILED(res)) return res;
00647   }
00648   return res;
00649 }
00650 
00651 nsresult nsHTMLEditor::RemoveStyleInside(nsIDOMNode *aNode, 
00652                                    nsIAtom *aProperty,   // null here means remove all properties
00653                                    const nsAString *aAttribute, 
00654                                    PRBool aChildrenOnly)
00655 {
00656   if (!aNode) return NS_ERROR_NULL_POINTER;
00657   if (IsTextNode(aNode)) return NS_OK;
00658   nsresult res = NS_OK;
00659 
00660   // first process the children
00661   nsCOMPtr<nsIDOMNode> child, tmp;
00662   aNode->GetFirstChild(getter_AddRefs(child));
00663   while (child)
00664   {
00665     // cache next sibling since we might remove child
00666     child->GetNextSibling(getter_AddRefs(tmp));
00667     res = RemoveStyleInside(child, aProperty, aAttribute);
00668     if (NS_FAILED(res)) return res;
00669     child = tmp;
00670   }
00671 
00672   // then process the node itself
00673   if ( !aChildrenOnly && 
00674         (aProperty && NodeIsType(aNode, aProperty) || // node is prop we asked for
00675         (aProperty == nsEditProperty::href && nsHTMLEditUtils::IsLink(aNode)) || // but check for link (<a href=...)
00676         (aProperty == nsEditProperty::name && nsHTMLEditUtils::IsNamedAnchor(aNode))) || // and for named anchors
00677         (!aProperty && NodeIsProperty(aNode)))  // or node is any prop and we asked for that
00678   {
00679     // if we weren't passed an attribute, then we want to 
00680     // remove any matching inlinestyles entirely
00681     if (!aAttribute || aAttribute->IsEmpty())
00682     {
00683       NS_NAMED_LITERAL_STRING(styleAttr, "style");
00684       NS_NAMED_LITERAL_STRING(classAttr, "class");
00685       PRBool hasStyleAttr = HasAttr(aNode, &styleAttr);
00686       PRBool hasClassAtrr = HasAttr(aNode, &classAttr);
00687       if (aProperty &&
00688           (hasStyleAttr || hasClassAtrr)) {
00689         // aNode carries inline styles or a class attribute so we can't
00690         // just remove the element... We need to create above the element
00691         // a span that will carry those styles or class, then we can delete
00692         // the node.
00693         nsCOMPtr<nsIDOMNode> spanNode;
00694         res = InsertContainerAbove(aNode, address_of(spanNode),
00695                                    NS_LITERAL_STRING("span"));
00696         if (NS_FAILED(res))
00697           return res;
00698         res = CloneAttribute(styleAttr, spanNode, aNode);
00699         if (NS_FAILED(res))
00700           return res;
00701         res = CloneAttribute(classAttr, spanNode, aNode);
00702         if (NS_FAILED(res))
00703           return res;
00704         if (hasStyleAttr)
00705         {
00706           // we need to remove the styles property corresponding to
00707           // aProperty (bug 215406)
00708           nsAutoString propertyValue;
00709           mHTMLCSSUtils->RemoveCSSEquivalentToHTMLStyle(spanNode,
00710                                                         aProperty,
00711                                                         aAttribute,
00712                                                         &propertyValue,
00713                                                         PR_FALSE);
00714           // remove the span if it's useless
00715           nsCOMPtr<nsIDOMElement> element = do_QueryInterface(spanNode);
00716           res = RemoveElementIfNoStyleOrIdOrClass(element, nsEditProperty::span);
00717         }
00718       }
00719       res = RemoveContainer(aNode);
00720     }
00721     // otherwise we just want to eliminate the attribute
00722     else
00723     {
00724       if (HasAttr(aNode, aAttribute))
00725       {
00726         // if this matching attribute is the ONLY one on the node,
00727         // then remove the whole node.  Otherwise just nix the attribute.
00728         if (IsOnlyAttribute(aNode, aAttribute))
00729         {
00730           res = RemoveContainer(aNode);
00731         }
00732         else
00733         {
00734           nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
00735           if (!elem) return NS_ERROR_NULL_POINTER;
00736           res = RemoveAttribute(elem, *aAttribute);
00737         }
00738       }
00739     }
00740   }
00741   else {
00742     PRBool useCSS;
00743     GetIsCSSEnabled(&useCSS);
00744 
00745     if (!aChildrenOnly
00746         && useCSS && mHTMLCSSUtils->IsCSSEditableProperty(aNode, aProperty, aAttribute)) {
00747       // the HTML style defined by aProperty/aAttribute has a CSS equivalence
00748       // in this implementation for the node aNode; let's check if it carries those css styles
00749       nsAutoString propertyValue;
00750       PRBool isSet;
00751       mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(aNode, aProperty, aAttribute,
00752                                                          isSet, propertyValue,
00753                                                          SPECIFIED_STYLE_TYPE);
00754       if (isSet) {
00755         // yes, tmp has the corresponding css declarations in its style attribute
00756         // let's remove them
00757         mHTMLCSSUtils->RemoveCSSEquivalentToHTMLStyle(aNode,
00758                                                       aProperty,
00759                                                       aAttribute,
00760                                                       &propertyValue,
00761                                                       PR_FALSE);
00762         // remove the node if it is a span, if its style attribute is empty or absent,
00763         // and if it does not have a class nor an id
00764         nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
00765         res = RemoveElementIfNoStyleOrIdOrClass(element, nsEditProperty::span);
00766       }
00767     }
00768   }  
00769   if ( aProperty == nsEditProperty::font &&    // or node is big or small and we are setting font size
00770        (nsHTMLEditUtils::IsBig(aNode) || nsHTMLEditUtils::IsSmall(aNode)) &&
00771        aAttribute->LowerCaseEqualsLiteral("size"))       
00772   {
00773     res = RemoveContainer(aNode);  // if we are setting font size, remove any nested bigs and smalls
00774   }
00775   return res;
00776 }
00777 
00778 PRBool nsHTMLEditor::IsOnlyAttribute(nsIDOMNode *aNode, 
00779                                      const nsAString *aAttribute)
00780 {
00781   if (!aNode || !aAttribute) return PR_FALSE;  // ooops
00782   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
00783   if (!content) return PR_FALSE;  // ooops
00784   
00785   PRInt32 nameSpaceID;
00786   nsCOMPtr<nsIAtom> attrName, prefix;
00787 
00788   PRUint32 i, attrCount = content->GetAttrCount();
00789   
00790   for (i = 0; i < attrCount; ++i)
00791   {
00792     content->GetAttrNameAt(i, &nameSpaceID, getter_AddRefs(attrName),
00793                            getter_AddRefs(prefix));
00794     nsAutoString attrString, tmp;
00795     if (!attrName) continue;  // ooops
00796     attrName->ToString(attrString);
00797     // if it's the attribute we know about, keep looking
00798     if (attrString.Equals(*aAttribute,nsCaseInsensitiveStringComparator())) continue;
00799     // if it's a special _moz... attribute, keep looking
00800     attrString.Left(tmp,4);
00801     if (tmp.LowerCaseEqualsLiteral("_moz")) continue;
00802     // otherwise, it's another attribute, so return false
00803     return PR_FALSE;
00804   }
00805   // if we made it through all of them without finding a real attribute
00806   // other than aAttribute, then return PR_TRUE
00807   return PR_TRUE;
00808 }
00809 
00810 PRBool nsHTMLEditor::HasAttr(nsIDOMNode *aNode, 
00811                              const nsAString *aAttribute)
00812 {
00813   if (!aNode) return PR_FALSE;
00814   if (!aAttribute || aAttribute->IsEmpty()) return PR_TRUE;  // everybody has the 'null' attribute
00815   
00816   // get element
00817   nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
00818   if (!elem) return PR_FALSE;
00819   
00820   // get attribute node
00821   nsCOMPtr<nsIDOMAttr> attNode;
00822   nsresult res = elem->GetAttributeNode(*aAttribute, getter_AddRefs(attNode));
00823   if ((NS_FAILED(res)) || !attNode) return PR_FALSE;
00824   return PR_TRUE;
00825 }
00826 
00827 
00828 PRBool nsHTMLEditor::HasAttrVal(nsIDOMNode *aNode, 
00829                                 const nsAString *aAttribute, 
00830                                 const nsAString *aValue)
00831 {
00832   if (!aNode) return PR_FALSE;
00833   if (!aAttribute || aAttribute->IsEmpty()) return PR_TRUE;  // everybody has the 'null' attribute
00834   
00835   // get element
00836   nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
00837   if (!elem) return PR_FALSE;
00838   
00839   // get attribute node
00840   nsCOMPtr<nsIDOMAttr> attNode;
00841   nsresult res = elem->GetAttributeNode(*aAttribute, getter_AddRefs(attNode));
00842   if ((NS_FAILED(res)) || !attNode) return PR_FALSE;
00843   
00844   // check if attribute has a value
00845   PRBool isSet;
00846   attNode->GetSpecified(&isSet);
00847   // if no value, and that's what we wanted, then return true
00848   if (!isSet && (!aValue || aValue->IsEmpty())) return PR_TRUE; 
00849   
00850   // get attribute value
00851   nsAutoString attrVal;
00852   attNode->GetValue(attrVal);
00853   
00854   // do values match?
00855   if (attrVal.Equals(*aValue,nsCaseInsensitiveStringComparator())) return PR_TRUE;
00856   return PR_FALSE;
00857 }
00858 
00859 nsresult nsHTMLEditor::PromoteRangeIfStartsOrEndsInNamedAnchor(nsIDOMRange *inRange)
00860 {
00861   if (!inRange) return NS_ERROR_NULL_POINTER;
00862   nsresult res;
00863   nsCOMPtr<nsIDOMNode> startNode, endNode, parent, tmp;
00864   PRInt32 startOffset, endOffset, tmpOffset;
00865   
00866   res = inRange->GetStartContainer(getter_AddRefs(startNode));
00867   if (NS_FAILED(res)) return res;
00868   res = inRange->GetStartOffset(&startOffset);
00869   if (NS_FAILED(res)) return res;
00870   res = inRange->GetEndContainer(getter_AddRefs(endNode));
00871   if (NS_FAILED(res)) return res;
00872   res = inRange->GetEndOffset(&endOffset);
00873   if (NS_FAILED(res)) return res;
00874 
00875   tmp = startNode;
00876   while ( tmp && 
00877           !nsTextEditUtils::IsBody(tmp) &&
00878           !nsHTMLEditUtils::IsNamedAnchor(tmp))
00879   {
00880     res = GetNodeLocation(tmp, address_of(parent), &tmpOffset);
00881     if (NS_FAILED(res)) return res;
00882     tmp = parent;
00883   }
00884   if (!tmp) return NS_ERROR_NULL_POINTER;
00885   if (nsHTMLEditUtils::IsNamedAnchor(tmp))
00886   {
00887     res = GetNodeLocation(tmp, address_of(parent), &tmpOffset);
00888     if (NS_FAILED(res)) return res;
00889     startNode = parent;
00890     startOffset = tmpOffset;
00891   }
00892 
00893   tmp = endNode;
00894   while ( tmp && 
00895           !nsTextEditUtils::IsBody(tmp) &&
00896           !nsHTMLEditUtils::IsNamedAnchor(tmp))
00897   {
00898     res = GetNodeLocation(tmp, address_of(parent), &tmpOffset);
00899     if (NS_FAILED(res)) return res;
00900     tmp = parent;
00901   }
00902   if (!tmp) return NS_ERROR_NULL_POINTER;
00903   if (nsHTMLEditUtils::IsNamedAnchor(tmp))
00904   {
00905     res = GetNodeLocation(tmp, address_of(parent), &tmpOffset);
00906     if (NS_FAILED(res)) return res;
00907     endNode = parent;
00908     endOffset = tmpOffset + 1;
00909   }
00910 
00911   res = inRange->SetStart(startNode, startOffset);
00912   if (NS_FAILED(res)) return res;
00913   res = inRange->SetEnd(endNode, endOffset);
00914   return res;
00915 }
00916 
00917 nsresult nsHTMLEditor::PromoteInlineRange(nsIDOMRange *inRange)
00918 {
00919   if (!inRange) return NS_ERROR_NULL_POINTER;
00920   nsresult res;
00921   nsCOMPtr<nsIDOMNode> startNode, endNode, parent;
00922   PRInt32 startOffset, endOffset;
00923   
00924   res = inRange->GetStartContainer(getter_AddRefs(startNode));
00925   if (NS_FAILED(res)) return res;
00926   res = inRange->GetStartOffset(&startOffset);
00927   if (NS_FAILED(res)) return res;
00928   res = inRange->GetEndContainer(getter_AddRefs(endNode));
00929   if (NS_FAILED(res)) return res;
00930   res = inRange->GetEndOffset(&endOffset);
00931   if (NS_FAILED(res)) return res;
00932   
00933   while ( startNode && 
00934           !nsTextEditUtils::IsBody(startNode) && 
00935           IsAtFrontOfNode(startNode, startOffset) )
00936   {
00937     res = GetNodeLocation(startNode, address_of(parent), &startOffset);
00938     if (NS_FAILED(res)) return res;
00939     startNode = parent;
00940   }
00941   if (!startNode) return NS_ERROR_NULL_POINTER;
00942   
00943   while ( endNode && 
00944           !nsTextEditUtils::IsBody(endNode) && 
00945           IsAtEndOfNode(endNode, endOffset) )
00946   {
00947     res = GetNodeLocation(endNode, address_of(parent), &endOffset);
00948     if (NS_FAILED(res)) return res;
00949     endNode = parent;
00950     endOffset++;  // we are AFTER this node
00951   }
00952   if (!endNode) return NS_ERROR_NULL_POINTER;
00953   
00954   res = inRange->SetStart(startNode, startOffset);
00955   if (NS_FAILED(res)) return res;
00956   res = inRange->SetEnd(endNode, endOffset);
00957   return res;
00958 }
00959 
00960 PRBool nsHTMLEditor::IsAtFrontOfNode(nsIDOMNode *aNode, PRInt32 aOffset)
00961 {
00962   if (!aNode) return PR_FALSE;  // oops
00963   if (!aOffset) return PR_TRUE;
00964   
00965   if (IsTextNode(aNode))
00966   {
00967     return PR_FALSE;
00968   }
00969   else
00970   {
00971     nsCOMPtr<nsIDOMNode> firstNode;
00972     GetFirstEditableChild(aNode, address_of(firstNode));
00973     if (!firstNode) return PR_TRUE; 
00974     PRInt32 offset;
00975     nsEditor::GetChildOffset(firstNode, aNode, offset);
00976     if (offset < aOffset) return PR_FALSE;
00977     return PR_TRUE;
00978   }
00979 }
00980 
00981 PRBool nsHTMLEditor::IsAtEndOfNode(nsIDOMNode *aNode, PRInt32 aOffset)
00982 {
00983   if (!aNode) return PR_FALSE;  // oops
00984   PRUint32 len;
00985   GetLengthOfDOMNode(aNode, len);
00986   if (aOffset == (PRInt32)len) return PR_TRUE;
00987   
00988   if (IsTextNode(aNode))
00989   {
00990     return PR_FALSE;
00991   }
00992   else
00993   {
00994     nsCOMPtr<nsIDOMNode> lastNode;
00995     GetLastEditableChild(aNode, address_of(lastNode));
00996     if (!lastNode) return PR_TRUE; 
00997     PRInt32 offset;
00998     nsEditor::GetChildOffset(lastNode, aNode, offset);
00999     if (offset < aOffset) return PR_TRUE;
01000     return PR_FALSE;
01001   }
01002 }
01003 
01004 
01005 nsresult
01006 nsHTMLEditor::GetInlinePropertyBase(nsIAtom *aProperty, 
01007                              const nsAString *aAttribute,
01008                              const nsAString *aValue,
01009                              PRBool *aFirst, 
01010                              PRBool *aAny, 
01011                              PRBool *aAll,
01012                              nsAString *outValue,
01013                              PRBool aCheckDefaults)
01014 {
01015   if (!aProperty)
01016     return NS_ERROR_NULL_POINTER;
01017 
01018   nsresult result;
01019   *aAny=PR_FALSE;
01020   *aAll=PR_TRUE;
01021   *aFirst=PR_FALSE;
01022   PRBool first=PR_TRUE;
01023 
01024   PRBool useCSS;
01025   GetIsCSSEnabled(&useCSS);
01026 
01027   nsCOMPtr<nsISelection>selection;
01028   result = GetSelection(getter_AddRefs(selection));
01029   if (NS_FAILED(result)) return result;
01030   if (!selection) return NS_ERROR_NULL_POINTER;
01031   nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
01032 
01033   PRBool isCollapsed;
01034   selection->GetIsCollapsed(&isCollapsed);
01035   nsCOMPtr<nsIDOMNode> collapsedNode;
01036   nsCOMPtr<nsIEnumerator> enumerator;
01037   result = selPriv->GetEnumerator(getter_AddRefs(enumerator));
01038   if (NS_FAILED(result)) return result;
01039   if (!enumerator) return NS_ERROR_NULL_POINTER;
01040 
01041   enumerator->First(); 
01042   nsCOMPtr<nsISupports> currentItem;
01043   result = enumerator->CurrentItem(getter_AddRefs(currentItem));
01044   // XXX: should be a while loop, to get each separate range
01045   // XXX: ERROR_HANDLING can currentItem be null?
01046   if ((NS_SUCCEEDED(result)) && currentItem)
01047   {
01048     PRBool firstNodeInRange = PR_TRUE; // for each range, set a flag 
01049     nsCOMPtr<nsIDOMRange> range(do_QueryInterface(currentItem));
01050 
01051     if (isCollapsed)
01052     {
01053       range->GetStartContainer(getter_AddRefs(collapsedNode));
01054       if (!collapsedNode) return NS_ERROR_FAILURE;
01055       PRBool isSet, theSetting;
01056       if (aAttribute)
01057       {
01058         nsString tString(*aAttribute); //MJUDGE SCC NEED HELP
01059         nsString tOutString;//MJUDGE SCC NEED HELP
01060         nsString *tPassString=nsnull;
01061         if (outValue)
01062             tPassString = &tOutString;
01063         mTypeInState->GetTypingState(isSet, theSetting, aProperty, tString, &tOutString);
01064         if (outValue)
01065           outValue->Assign(tOutString);
01066       }
01067       else
01068         mTypeInState->GetTypingState(isSet, theSetting, aProperty);
01069       if (isSet) 
01070       {
01071         *aFirst = *aAny = *aAll = theSetting;
01072         return NS_OK;
01073       }
01074       if (!useCSS) {
01075         nsCOMPtr<nsIDOMNode> resultNode;
01076         IsTextPropertySetByContent(collapsedNode, aProperty, aAttribute, aValue,
01077                                    isSet, getter_AddRefs(resultNode), outValue);
01078         *aFirst = *aAny = *aAll = isSet;
01079         
01080         if (!isSet && aCheckDefaults) 
01081         {
01082           // style not set, but if it is a default then it will appear if 
01083           // content is inserted, so we should report it as set (analogous to TypeInState).
01084           PRInt32 index;
01085           if (TypeInState::FindPropInList(aProperty, *aAttribute, outValue, mDefaultStyles, index))
01086           {
01087             *aFirst = *aAny = *aAll = PR_TRUE;
01088             if (outValue)
01089               outValue->Assign(((PropItem*)mDefaultStyles[index])->value);
01090           }
01091         }
01092         return NS_OK;
01093       }
01094     }
01095 
01096     // non-collapsed selection
01097     nsCOMPtr<nsIContentIterator> iter =
01098             do_CreateInstance("@mozilla.org/content/post-content-iterator;1");
01099     if (!iter) return NS_ERROR_NULL_POINTER;
01100 
01101     iter->Init(range);
01102     nsAutoString firstValue, theValue;
01103 
01104     nsCOMPtr<nsIDOMNode> endNode;
01105     PRInt32 endOffset;
01106     result = range->GetEndContainer(getter_AddRefs(endNode));
01107     if (NS_FAILED(result)) return result;
01108     result = range->GetEndOffset(&endOffset);
01109     if (NS_FAILED(result)) return result;
01110     while (!iter->IsDone())
01111     {
01112       nsIContent *content = iter->GetCurrentNode();
01113 
01114       nsCOMPtr<nsIDOMNode> node = do_QueryInterface(content);
01115 
01116       if (node && nsTextEditUtils::IsBody(node))
01117         break;
01118 
01119       nsCOMPtr<nsIDOMCharacterData>text;
01120       text = do_QueryInterface(content);
01121       
01122       PRBool skipNode = PR_FALSE;
01123       
01124       // just ignore any non-editable nodes
01125       if (text && !IsEditable(text))
01126       {
01127         skipNode = PR_TRUE;
01128       }
01129       else if (text)
01130       {
01131         if (!isCollapsed && first && firstNodeInRange)
01132         {
01133           firstNodeInRange = PR_FALSE;
01134           PRInt32 startOffset;
01135           range->GetStartOffset(&startOffset);
01136           PRUint32 count;
01137           text->GetLength(&count);
01138           if (startOffset==(PRInt32)count) 
01139           {
01140             skipNode = PR_TRUE;
01141           }
01142         }
01143         else if (node == endNode && !endOffset)
01144         {
01145           skipNode = PR_TRUE;
01146         }
01147       }
01148       else if (content->IsContentOfType(nsIContent::eELEMENT))
01149       { // handle non-text leaf nodes here
01150         skipNode = PR_TRUE;
01151       }
01152       if (!skipNode)
01153       {
01154         if (node)
01155         {
01156           PRBool isSet = PR_FALSE;
01157           nsCOMPtr<nsIDOMNode>resultNode;
01158           if (first)
01159           {
01160             if (useCSS &&
01161                 mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty, aAttribute)) {
01162               // the HTML styles defined by aProperty/aAttribute has a CSS equivalence
01163               // in this implementation for node; let's check if it carries those css styles
01164               if (aValue) firstValue.Assign(*aValue);
01165               mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(node, aProperty, aAttribute,
01166                                                                  isSet, firstValue,
01167                                                                  COMPUTED_STYLE_TYPE);
01168             }
01169             else {
01170               IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet,
01171                                          getter_AddRefs(resultNode), &firstValue);
01172             }
01173             *aFirst = isSet;
01174             first = PR_FALSE;
01175             if (outValue) *outValue = firstValue;
01176           }
01177           else
01178           {
01179             if (useCSS &&
01180                 mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty, aAttribute)) {
01181               // the HTML styles defined by aProperty/aAttribute has a CSS equivalence
01182               // in this implementation for node; let's check if it carries those css styles
01183               if (aValue) theValue.Assign(*aValue);
01184               mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(node, aProperty, aAttribute,
01185                                                                  isSet, theValue,
01186                                                                  COMPUTED_STYLE_TYPE);
01187             }
01188             else {
01189               IsTextPropertySetByContent(node, aProperty, aAttribute, aValue, isSet,
01190                                          getter_AddRefs(resultNode), &theValue);
01191             }
01192             if (firstValue != theValue)
01193               *aAll = PR_FALSE;
01194           }
01195           
01196           if (isSet) {
01197             *aAny = PR_TRUE;
01198           }
01199           else {
01200             *aAll = PR_FALSE;
01201           }
01202         }
01203       }
01204 
01205       iter->Next();
01206     }
01207   }
01208   if (!*aAny) 
01209   { // make sure that if none of the selection is set, we don't report all is set
01210     *aAll = PR_FALSE;
01211   }
01212   return result;
01213 }
01214 
01215 
01216 NS_IMETHODIMP nsHTMLEditor::GetInlineProperty(nsIAtom *aProperty, 
01217                                               const nsAString &aAttribute, 
01218                                               const nsAString &aValue,
01219                                               PRBool *aFirst, 
01220                                               PRBool *aAny, 
01221                                               PRBool *aAll)
01222 {
01223   if (!aProperty || !aFirst || !aAny || !aAll)
01224     return NS_ERROR_NULL_POINTER;
01225   const nsAString *att = nsnull;
01226   if (!aAttribute.IsEmpty())
01227     att = &aAttribute;
01228   const nsAString *val = nsnull;
01229   if (!aValue.IsEmpty())
01230     val = &aValue;
01231   return GetInlinePropertyBase( aProperty, att, val, aFirst, aAny, aAll, nsnull);
01232 }
01233 
01234 
01235 NS_IMETHODIMP nsHTMLEditor::GetInlinePropertyWithAttrValue(nsIAtom *aProperty, 
01236                                               const nsAString &aAttribute, 
01237                                               const nsAString &aValue,
01238                                               PRBool *aFirst, 
01239                                               PRBool *aAny, 
01240                                               PRBool *aAll,
01241                                               nsAString &outValue)
01242 {
01243   if (!aProperty || !aFirst || !aAny || !aAll)
01244     return NS_ERROR_NULL_POINTER;
01245   const nsAString *att = nsnull;
01246   if (!aAttribute.IsEmpty())
01247     att = &aAttribute;
01248   const nsAString *val = nsnull;
01249   if (!aValue.IsEmpty())
01250     val = &aValue;
01251   return GetInlinePropertyBase( aProperty, att, val, aFirst, aAny, aAll, &outValue);
01252 }
01253 
01254 
01255 NS_IMETHODIMP nsHTMLEditor::RemoveAllInlineProperties()
01256 {
01257   nsAutoEditBatch batchIt(this);
01258   nsAutoRules beginRulesSniffing(this, kOpResetTextProperties, nsIEditor::eNext);
01259 
01260   nsresult res = RemoveInlinePropertyImpl(nsnull, nsnull);
01261   if (NS_FAILED(res)) return res;
01262   return ApplyDefaultProperties();
01263 }
01264 
01265 NS_IMETHODIMP nsHTMLEditor::RemoveInlineProperty(nsIAtom *aProperty, const nsAString &aAttribute)
01266 {
01267   return RemoveInlinePropertyImpl(aProperty, &aAttribute);
01268 }
01269 
01270 nsresult nsHTMLEditor::RemoveInlinePropertyImpl(nsIAtom *aProperty, const nsAString *aAttribute)
01271 {
01272   if (!mRules)    return NS_ERROR_NOT_INITIALIZED;
01273   ForceCompositionEnd();
01274 
01275   nsresult res;
01276   nsCOMPtr<nsISelection>selection;
01277   res = GetSelection(getter_AddRefs(selection));
01278   if (NS_FAILED(res)) return res;
01279   if (!selection) return NS_ERROR_NULL_POINTER;
01280   nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
01281 
01282   PRBool isCollapsed;
01283   selection->GetIsCollapsed(&isCollapsed);
01284 
01285   PRBool useCSS;
01286   GetIsCSSEnabled(&useCSS);
01287 
01288   if (isCollapsed)
01289   {
01290     // manipulating text attributes on a collapsed selection only sets state for the next text insertion
01291 
01292     // For links, aProperty uses "href", use "a" instead
01293     if (aProperty == nsEditProperty::href ||
01294         aProperty == nsEditProperty::name)
01295       aProperty = nsEditProperty::a;
01296 
01297     if (aProperty) return mTypeInState->ClearProp(aProperty, nsAutoString(*aAttribute));
01298     else return mTypeInState->ClearAllProps();
01299   }
01300   nsAutoEditBatch batchIt(this);
01301   nsAutoRules beginRulesSniffing(this, kOpRemoveTextProperty, nsIEditor::eNext);
01302   nsAutoSelectionReset selectionResetter(selection, this);
01303   nsAutoTxnsConserveSelection dontSpazMySelection(this);
01304   
01305   PRBool cancel, handled;
01306   nsTextRulesInfo ruleInfo(nsTextEditRules::kRemoveTextProperty);
01307   res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
01308   if (NS_FAILED(res)) return res;
01309   if (!cancel && !handled)
01310   {
01311     // get selection range enumerator
01312     nsCOMPtr<nsIEnumerator> enumerator;
01313     res = selPriv->GetEnumerator(getter_AddRefs(enumerator));
01314     if (NS_FAILED(res)) return res;
01315     if (!enumerator)    return NS_ERROR_FAILURE;
01316 
01317     // loop thru the ranges in the selection
01318     enumerator->First(); 
01319     nsCOMPtr<nsISupports> currentItem;
01320     while ((NS_ENUMERATOR_FALSE == enumerator->IsDone()))
01321     {
01322       res = enumerator->CurrentItem(getter_AddRefs(currentItem));
01323       if (NS_FAILED(res)) return res;
01324       if (!currentItem)   return NS_ERROR_FAILURE;
01325       
01326       nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
01327 
01328       if (aProperty == nsEditProperty::name)
01329       {
01330         // promote range if it starts or end in a named anchor and we
01331         // want to remove named anchors
01332         res = PromoteRangeIfStartsOrEndsInNamedAnchor(range);
01333       }
01334       else {
01335         // adjust range to include any ancestors who's children are entirely selected
01336         res = PromoteInlineRange(range);
01337       }
01338       if (NS_FAILED(res)) return res;
01339 
01340       // remove this style from ancestors of our range endpoints, 
01341       // splitting them as appropriate
01342       res = SplitStyleAboveRange(range, aProperty, aAttribute);
01343       if (NS_FAILED(res)) return res;
01344 
01345       // check for easy case: both range endpoints in same text node
01346       nsCOMPtr<nsIDOMNode> startNode, endNode;
01347       res = range->GetStartContainer(getter_AddRefs(startNode));
01348       if (NS_FAILED(res)) return res;
01349       res = range->GetEndContainer(getter_AddRefs(endNode));
01350       if (NS_FAILED(res)) return res;
01351       if ((startNode == endNode) && IsTextNode(startNode))
01352       {
01353         // we're done with this range!
01354         if (useCSS && mHTMLCSSUtils->IsCSSEditableProperty(startNode, aProperty, aAttribute)) {
01355           // the HTML style defined by aProperty/aAttribute has a CSS equivalence
01356           // in this implementation for startNode
01357           nsAutoString cssValue;
01358           PRBool isSet = PR_FALSE;
01359           mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(startNode,
01360                                                     aProperty,
01361                                                     aAttribute,
01362                                                     isSet ,
01363                                                     cssValue,
01364                                                     COMPUTED_STYLE_TYPE);
01365           if (isSet) {
01366             // startNode's computed style indicates the CSS equivalence to the HTML style to
01367             // remove is applied; but we found no element in the ancestors of startNode
01368             // carrying specified styles; assume it comes from a rule and let's try to
01369             // insert a span "inverting" the style
01370             nsAutoString value; value.AssignLiteral("-moz-editor-invert-value");
01371             PRInt32 startOffset, endOffset;
01372             range->GetStartOffset(&startOffset);
01373             range->GetEndOffset(&endOffset);
01374             nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
01375             if (mHTMLCSSUtils->IsCSSInvertable(aProperty, aAttribute)) {
01376               SetInlinePropertyOnTextNode(nodeAsText, startOffset, endOffset, aProperty, aAttribute, &value);
01377             }
01378           }
01379         }
01380       }
01381       else
01382       {
01383         // not the easy case.  range not contained in single text node. 
01384         nsCOMPtr<nsIContentIterator> iter =
01385           do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &res);
01386         if (NS_FAILED(res)) return res;
01387         if (!iter)          return NS_ERROR_FAILURE;
01388 
01389         nsCOMArray<nsIDOMNode> arrayOfNodes;
01390         nsCOMPtr<nsIDOMNode> node;
01391         
01392         // iterate range and build up array
01393         iter->Init(range);
01394         while (!iter->IsDone())
01395         {
01396           node = do_QueryInterface(iter->GetCurrentNode());
01397           if (!node)
01398             return NS_ERROR_FAILURE;
01399 
01400           if (IsEditable(node))
01401           { 
01402             arrayOfNodes.AppendObject(node);
01403           }
01404 
01405           iter->Next();
01406         }
01407         
01408         // loop through the list, remove the property on each node
01409         PRInt32 listCount = arrayOfNodes.Count();
01410         PRInt32 j;
01411         for (j = 0; j < listCount; j++)
01412         {
01413           node = arrayOfNodes[j];
01414           res = RemoveStyleInside(node, aProperty, aAttribute);
01415           if (NS_FAILED(res)) return res;
01416           if (useCSS && mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty, aAttribute)) {
01417             // the HTML style defined by aProperty/aAttribute has a CSS equivalence
01418             // in this implementation for node
01419             nsAutoString cssValue;
01420             PRBool isSet = PR_FALSE;
01421             mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(node,
01422                                                                aProperty,
01423                                                                aAttribute,
01424                                                                isSet ,
01425                                                                cssValue,
01426                                                                COMPUTED_STYLE_TYPE);
01427             if (isSet) {
01428               // startNode's computed style indicates the CSS equivalence to the HTML style to
01429               // remove is applied; but we found no element in the ancestors of startNode
01430               // carrying specified styles; assume it comes from a rule and let's try to
01431               // insert a span "inverting" the style
01432               if (mHTMLCSSUtils->IsCSSInvertable(aProperty, aAttribute)) {
01433                 nsAutoString value; value.AssignLiteral("-moz-editor-invert-value");
01434                 SetInlinePropertyOnNode(node, aProperty, aAttribute, &value);
01435               }
01436             }
01437           }
01438         }
01439         arrayOfNodes.Clear();
01440       }
01441       enumerator->Next();
01442     }
01443   }
01444   if (!cancel)
01445   {
01446     // post-process 
01447     res = mRules->DidDoAction(selection, &ruleInfo, res);
01448   }
01449   return res;
01450 }
01451 
01452 NS_IMETHODIMP nsHTMLEditor::IncreaseFontSize()
01453 {
01454   return RelativeFontChange(1);
01455 }
01456 
01457 NS_IMETHODIMP nsHTMLEditor::DecreaseFontSize()
01458 {
01459   return RelativeFontChange(-1);
01460 }
01461 
01462 nsresult
01463 nsHTMLEditor::RelativeFontChange( PRInt32 aSizeChange)
01464 {
01465   // Can only change font size by + or - 1
01466   if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
01467     return NS_ERROR_ILLEGAL_VALUE;
01468   
01469   ForceCompositionEnd();
01470 
01471   // Get the selection 
01472   nsCOMPtr<nsISelection>selection;
01473   nsresult res = GetSelection(getter_AddRefs(selection));
01474   if (NS_FAILED(res)) return res;
01475   if (!selection) return NS_ERROR_FAILURE;
01476   nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));  
01477   // Is the selection collapsed?
01478   PRBool bCollapsed;
01479   res = selection->GetIsCollapsed(&bCollapsed);
01480   if (NS_FAILED(res)) return res;
01481   
01482   // if it's collapsed set typing state
01483   if (bCollapsed)
01484   {
01485     nsCOMPtr<nsIAtom> atom;
01486     if (aSizeChange==1) atom = nsEditProperty::big;
01487     else                atom = nsEditProperty::small;
01488 
01489     // Let's see in what kind of element the selection is
01490     PRInt32 offset;
01491     nsCOMPtr<nsIDOMNode> selectedNode;
01492     res = GetStartNodeAndOffset(selection, address_of(selectedNode), &offset);
01493     if (IsTextNode(selectedNode)) {
01494       nsCOMPtr<nsIDOMNode> parent;
01495       res = selectedNode->GetParentNode(getter_AddRefs(parent));
01496       if (NS_FAILED(res)) return res;
01497       selectedNode = parent;
01498     }
01499     nsAutoString tag;
01500     atom->ToString(tag);
01501     if (!CanContainTag(selectedNode, tag)) return NS_OK;
01502 
01503     // manipulating text attributes on a collapsed selection only sets state for the next text insertion
01504     return mTypeInState->SetProp(atom, EmptyString(), EmptyString());
01505   }
01506   
01507   // wrap with txn batching, rules sniffing, and selection preservation code
01508   nsAutoEditBatch batchIt(this);
01509   nsAutoRules beginRulesSniffing(this, kOpSetTextProperty, nsIEditor::eNext);
01510   nsAutoSelectionReset selectionResetter(selection, this);
01511   nsAutoTxnsConserveSelection dontSpazMySelection(this);
01512 
01513   // get selection range enumerator
01514   nsCOMPtr<nsIEnumerator> enumerator;
01515   res = selPriv->GetEnumerator(getter_AddRefs(enumerator));
01516   if (NS_FAILED(res)) return res;
01517   if (!enumerator)    return NS_ERROR_FAILURE;
01518 
01519   // loop thru the ranges in the selection
01520   enumerator->First(); 
01521   nsCOMPtr<nsISupports> currentItem;
01522   while ((NS_ENUMERATOR_FALSE == enumerator->IsDone()))
01523   {
01524     res = enumerator->CurrentItem(getter_AddRefs(currentItem));
01525     if (NS_FAILED(res)) return res;
01526     if (!currentItem)   return NS_ERROR_FAILURE;
01527     
01528     nsCOMPtr<nsIDOMRange> range( do_QueryInterface(currentItem) );
01529 
01530     // adjust range to include any ancestors who's children are entirely selected
01531     res = PromoteInlineRange(range);
01532     if (NS_FAILED(res)) return res;
01533     
01534     // check for easy case: both range endpoints in same text node
01535     nsCOMPtr<nsIDOMNode> startNode, endNode;
01536     res = range->GetStartContainer(getter_AddRefs(startNode));
01537     if (NS_FAILED(res)) return res;
01538     res = range->GetEndContainer(getter_AddRefs(endNode));
01539     if (NS_FAILED(res)) return res;
01540     if ((startNode == endNode) && IsTextNode(startNode))
01541     {
01542       PRInt32 startOffset, endOffset;
01543       range->GetStartOffset(&startOffset);
01544       range->GetEndOffset(&endOffset);
01545       nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
01546       res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, endOffset);
01547       if (NS_FAILED(res)) return res;
01548     }
01549     else
01550     {
01551       // not the easy case.  range not contained in single text node. 
01552       // there are up to three phases here.  There are all the nodes
01553       // reported by the subtree iterator to be processed.  And there
01554       // are potentially a starting textnode and an ending textnode
01555       // which are only partially contained by the range.
01556       
01557       // lets handle the nodes reported by the iterator.  These nodes
01558       // are entirely contained in the selection range.  We build up
01559       // a list of them (since doing operations on the document during
01560       // iteration would perturb the iterator).
01561 
01562       nsCOMPtr<nsIContentIterator> iter =
01563         do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1", &res);
01564       if (NS_FAILED(res)) return res;
01565       if (!iter)          return NS_ERROR_FAILURE;
01566 
01567       nsCOMArray<nsIDOMNode> arrayOfNodes;
01568       nsCOMPtr<nsIDOMNode> node;
01569       
01570       // iterate range and build up array
01571       res = iter->Init(range);
01572       if (NS_SUCCEEDED(res))
01573       {
01574         while (!iter->IsDone())
01575         {
01576           node = do_QueryInterface(iter->GetCurrentNode());
01577           if (!node)
01578             return NS_ERROR_FAILURE;
01579 
01580           if (IsEditable(node))
01581           { 
01582             arrayOfNodes.AppendObject(node);
01583           }
01584 
01585           iter->Next();
01586         }
01587         
01588         // now that we have the list, do the font size change on each node
01589         PRInt32 listCount = arrayOfNodes.Count();
01590         PRInt32 j;
01591         for (j = 0; j < listCount; j++)
01592         {
01593           node = arrayOfNodes[j];
01594           res = RelativeFontChangeOnNode(aSizeChange, node);
01595           if (NS_FAILED(res)) return res;
01596         }
01597         arrayOfNodes.Clear();
01598       }
01599       // now check the start and end parents of the range to see if they need to 
01600       // be separately handled (they do if they are text nodes, due to how the
01601       // subtree iterator works - it will not have reported them).
01602       if (IsTextNode(startNode) && IsEditable(startNode))
01603       {
01604         nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(startNode);
01605         PRInt32 startOffset;
01606         PRUint32 textLen;
01607         range->GetStartOffset(&startOffset);
01608         nodeAsText->GetLength(&textLen);
01609         res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, startOffset, textLen);
01610         if (NS_FAILED(res)) return res;
01611       }
01612       if (IsTextNode(endNode) && IsEditable(endNode))
01613       {
01614         nsCOMPtr<nsIDOMCharacterData> nodeAsText = do_QueryInterface(endNode);
01615         PRInt32 endOffset;
01616         range->GetEndOffset(&endOffset);
01617         res = RelativeFontChangeOnTextNode(aSizeChange, nodeAsText, 0, endOffset);
01618         if (NS_FAILED(res)) return res;
01619       }
01620     }
01621     enumerator->Next();
01622   }
01623   
01624   return res;  
01625 }
01626 
01627 nsresult
01628 nsHTMLEditor::RelativeFontChangeOnTextNode( PRInt32 aSizeChange, 
01629                                             nsIDOMCharacterData *aTextNode, 
01630                                             PRInt32 aStartOffset,
01631                                             PRInt32 aEndOffset)
01632 {
01633   // Can only change font size by + or - 1
01634   if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
01635     return NS_ERROR_ILLEGAL_VALUE;
01636   if (!aTextNode) return NS_ERROR_NULL_POINTER;
01637   
01638   // dont need to do anything if no characters actually selected
01639   if (aStartOffset == aEndOffset) return NS_OK;
01640   
01641   nsresult res = NS_OK;
01642   nsCOMPtr<nsIDOMNode> parent;
01643   res = aTextNode->GetParentNode(getter_AddRefs(parent));
01644   if (NS_FAILED(res)) return res;
01645   if (!CanContainTag(parent, NS_LITERAL_STRING("big"))) return NS_OK;
01646 
01647   nsCOMPtr<nsIDOMNode> tmp, node = do_QueryInterface(aTextNode);
01648 
01649   // do we need to split the text node?
01650   PRUint32 textLen;
01651   aTextNode->GetLength(&textLen);
01652   
01653   // -1 is a magic value meaning to the end of node
01654   if (aEndOffset == -1) aEndOffset = textLen;
01655   
01656   if ( (PRUint32)aEndOffset != textLen )
01657   {
01658     // we need to split off back of text node
01659     res = SplitNode(node, aEndOffset, getter_AddRefs(tmp));
01660     if (NS_FAILED(res)) return res;
01661     node = tmp;  // remember left node
01662   }
01663   if ( aStartOffset )
01664   {
01665     // we need to split off front of text node
01666     res = SplitNode(node, aStartOffset, getter_AddRefs(tmp));
01667     if (NS_FAILED(res)) return res;
01668   }
01669 
01670   NS_NAMED_LITERAL_STRING(bigSize, "big");
01671   NS_NAMED_LITERAL_STRING(smallSize, "small");
01672   const nsAString& nodeType = (aSizeChange==1) ? NS_STATIC_CAST(const nsAString&, bigSize) : NS_STATIC_CAST(const nsAString&, smallSize);
01673   // look for siblings that are correct type of node
01674   nsCOMPtr<nsIDOMNode> sibling;
01675   GetPriorHTMLSibling(node, address_of(sibling));
01676   if (sibling && NodeIsType(sibling, (aSizeChange==1) ? nsEditProperty::big : nsEditProperty::small))
01677   {
01678     // previous sib is already right kind of inline node; slide this over into it
01679     res = MoveNode(node, sibling, -1);
01680     return res;
01681   }
01682   sibling = nsnull;
01683   GetNextHTMLSibling(node, address_of(sibling));
01684   if (sibling && NodeIsType(sibling, (aSizeChange==1) ? nsEditProperty::big : nsEditProperty::small))
01685   {
01686     // following sib is already right kind of inline node; slide this over into it
01687     res = MoveNode(node, sibling, 0);
01688     return res;
01689   }
01690   
01691   // else reparent the node inside font node with appropriate relative size
01692   res = InsertContainerAbove(node, address_of(tmp), nodeType);
01693   return res;
01694 }
01695 
01696 
01697 nsresult
01698 nsHTMLEditor::RelativeFontChangeHelper( PRInt32 aSizeChange, 
01699                                         nsIDOMNode *aNode)
01700 {
01701   /*  This routine looks for all the font nodes in the tree rooted by aNode,
01702       including aNode itself, looking for font nodes that have the size attr
01703       set.  Any such nodes need to have big or small put inside them, since
01704       they override any big/small that are above them.
01705   */
01706   
01707   // Can only change font size by + or - 1
01708   if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
01709     return NS_ERROR_ILLEGAL_VALUE;
01710   if (!aNode) return NS_ERROR_NULL_POINTER;
01711 
01712   nsresult res = NS_OK;
01713   nsAutoString tag;
01714   if (aSizeChange == 1) tag.AssignLiteral("big");
01715   else tag.AssignLiteral("small");
01716   nsCOMPtr<nsIDOMNodeList> childNodes;
01717   PRInt32 j;
01718   PRUint32 childCount;
01719   nsCOMPtr<nsIDOMNode> childNode;
01720   
01721   // if this is a font node with size, put big/small inside it
01722   NS_NAMED_LITERAL_STRING(attr, "size");
01723   if (NodeIsType(aNode, nsEditProperty::font) && HasAttr(aNode, &attr))
01724   {
01725     // cycle through children and adjust relative font size
01726     res = aNode->GetChildNodes(getter_AddRefs(childNodes));
01727     if (NS_FAILED(res)) return res;
01728     if (childNodes)
01729     {
01730       childNodes->GetLength(&childCount);
01731       for (j=childCount-1; j>=0; j--)
01732       {
01733         res = childNodes->Item(j, getter_AddRefs(childNode));
01734         if ((NS_SUCCEEDED(res)) && (childNode))
01735         {
01736           res = RelativeFontChangeOnNode(aSizeChange, childNode);
01737           if (NS_FAILED(res)) return res;
01738         }
01739       }
01740     }
01741   }
01742 
01743   childNodes = nsnull;
01744   // now cycle through the children.
01745   res = aNode->GetChildNodes(getter_AddRefs(childNodes));
01746   if (NS_FAILED(res)) return res;
01747   if (childNodes)
01748   {
01749     childNodes->GetLength(&childCount);
01750     for (j=childCount-1; j>=0; j--)
01751     {
01752       res = childNodes->Item(j, getter_AddRefs(childNode));
01753       if ((NS_SUCCEEDED(res)) && (childNode))
01754       {
01755         res = RelativeFontChangeHelper(aSizeChange, childNode);
01756         if (NS_FAILED(res)) return res;
01757       }
01758     }
01759   }
01760 
01761   return res;
01762 }
01763 
01764 
01765 nsresult
01766 nsHTMLEditor::RelativeFontChangeOnNode( PRInt32 aSizeChange, 
01767                                         nsIDOMNode *aNode)
01768 {
01769   // Can only change font size by + or - 1
01770   if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
01771     return NS_ERROR_ILLEGAL_VALUE;
01772   if (!aNode) return NS_ERROR_NULL_POINTER;
01773 
01774   nsresult res = NS_OK;
01775   nsCOMPtr<nsIDOMNode> tmp;
01776   nsAutoString tag;
01777   if (aSizeChange == 1) tag.AssignLiteral("big");
01778   else tag.AssignLiteral("small");
01779   
01780   // is it the opposite of what we want?  
01781   if ( ((aSizeChange == 1) && nsHTMLEditUtils::IsSmall(aNode)) || 
01782        ((aSizeChange == -1) &&  nsHTMLEditUtils::IsBig(aNode)) )
01783   {
01784     // first populate any nested font tags that have the size attr set
01785     res = RelativeFontChangeHelper(aSizeChange, aNode);
01786     if (NS_FAILED(res)) return res;
01787     // in that case, just remove this node and pull up the children
01788     res = RemoveContainer(aNode);
01789     return res;
01790   }
01791   // can it be put inside a "big" or "small"?
01792   if (TagCanContain(tag, aNode))
01793   {
01794     // first populate any nested font tags that have the size attr set
01795     res = RelativeFontChangeHelper(aSizeChange, aNode);
01796     if (NS_FAILED(res)) return res;
01797     // ok, chuck it in.
01798     // first look at siblings of aNode for matching bigs or smalls.
01799     // if we find one, move aNode into it.
01800     nsCOMPtr<nsIDOMNode> sibling;
01801     GetPriorHTMLSibling(aNode, address_of(sibling));
01802     if (sibling && nsEditor::NodeIsType(sibling, (aSizeChange==1 ? nsEditProperty::big : nsEditProperty::small)))
01803     {
01804       // previous sib is already right kind of inline node; slide this over into it
01805       res = MoveNode(aNode, sibling, -1);
01806       return res;
01807     }
01808     sibling = nsnull;
01809     GetNextHTMLSibling(aNode, address_of(sibling));
01810     if (sibling && nsEditor::NodeIsType(sibling, (aSizeChange==1 ? nsEditProperty::big : nsEditProperty::small)))
01811     {
01812       // following sib is already right kind of inline node; slide this over into it
01813       res = MoveNode(aNode, sibling, 0);
01814       return res;
01815     }
01816     // else insert it above aNode
01817     res = InsertContainerAbove(aNode, address_of(tmp), tag);
01818     return res;
01819   }
01820   // none of the above?  then cycle through the children.
01821   // MOOSE: we should group the children together if possible
01822   // into a single "big" or "small".  For the moment they are
01823   // each getting their own.  
01824   nsCOMPtr<nsIDOMNodeList> childNodes;
01825   res = aNode->GetChildNodes(getter_AddRefs(childNodes));
01826   if (NS_FAILED(res)) return res;
01827   if (childNodes)
01828   {
01829     PRInt32 j;
01830     PRUint32 childCount;
01831     childNodes->GetLength(&childCount);
01832     for (j=childCount-1; j>=0; j--)
01833     {
01834       nsCOMPtr<nsIDOMNode> childNode;
01835       res = childNodes->Item(j, getter_AddRefs(childNode));
01836       if ((NS_SUCCEEDED(res)) && (childNode))
01837       {
01838         res = RelativeFontChangeOnNode(aSizeChange, childNode);
01839         if (NS_FAILED(res)) return res;
01840       }
01841     }
01842   }
01843   return res;
01844 }
01845 
01846 NS_IMETHODIMP 
01847 nsHTMLEditor::GetFontFaceState(PRBool *aMixed, nsAString &outFace)
01848 {
01849   if (!aMixed)
01850       return NS_ERROR_FAILURE;
01851   *aMixed = PR_TRUE;
01852   outFace.Truncate();
01853 
01854   nsresult res;
01855   PRBool first, any, all;
01856   
01857   NS_NAMED_LITERAL_STRING(attr, "face");
01858   res = GetInlinePropertyBase(nsEditProperty::font, &attr, nsnull, &first, &any, &all, &outFace);
01859   if (NS_FAILED(res)) return res;
01860   if (any && !all) return res; // mixed
01861   if (all)
01862   {
01863     *aMixed = PR_FALSE;
01864     return res;
01865   }
01866   
01867   // if there is no font face, check for tt
01868   res = GetInlinePropertyBase(nsEditProperty::tt, nsnull, nsnull, &first, &any, &all,nsnull);
01869   if (NS_FAILED(res)) return res;
01870   if (any && !all) return res; // mixed
01871   if (all)
01872   {
01873     *aMixed = PR_FALSE;
01874     nsEditProperty::tt->ToString(outFace);
01875   }
01876   
01877   if (!any)
01878   {
01879     // there was no font face attrs of any kind.  We are in normal font.
01880     outFace.Truncate();
01881     *aMixed = PR_FALSE;
01882   }
01883   return res;
01884 }
01885 
01886 NS_IMETHODIMP 
01887 nsHTMLEditor::GetFontColorState(PRBool *aMixed, nsAString &aOutColor)
01888 {
01889   if (!aMixed)
01890       return NS_ERROR_NULL_POINTER;
01891   *aMixed = PR_TRUE;
01892   aOutColor.Truncate();
01893   
01894   nsresult res;
01895   NS_NAMED_LITERAL_STRING(colorStr, "color");
01896   PRBool first, any, all;
01897   
01898   res = GetInlinePropertyBase(nsEditProperty::font, &colorStr, nsnull, &first, &any, &all, &aOutColor);
01899   if (NS_FAILED(res)) return res;
01900   if (any && !all) return res; // mixed
01901   if (all)
01902   {
01903     *aMixed = PR_FALSE;
01904     return res;
01905   }
01906   
01907   if (!any)
01908   {
01909     // there was no font color attrs of any kind..
01910     aOutColor.Truncate();
01911     *aMixed = PR_FALSE;
01912   }
01913   return res;
01914 }
01915 
01916 // the return value is true only if the instance of the HTML editor we created
01917 // can handle CSS styles (for instance, Composer can, Messenger can't) and if
01918 // the CSS preference is checked
01919 nsresult
01920 nsHTMLEditor::GetIsCSSEnabled(PRBool *aIsCSSEnabled)
01921 {
01922   *aIsCSSEnabled = PR_FALSE;
01923   if (mCSSAware) {
01924     // TBD later : removal of mCSSAware and use only the presence of mHTMLCSSUtils
01925     if (mHTMLCSSUtils) {
01926       *aIsCSSEnabled = mHTMLCSSUtils->IsCSSPrefChecked();
01927     }
01928   }
01929   return NS_OK;
01930 }
01931 
01932 nsresult
01933 nsHTMLEditor::HasStyleOrIdOrClass(nsIDOMElement * aElement, PRBool *aHasStyleOrIdOrClass)
01934 {
01935   NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
01936   nsCOMPtr<nsIDOMNode> node  = do_QueryInterface(aElement);
01937 
01938 
01939   // remove the node if its style attribute is empty or absent,
01940   // and if it does not have a class nor an id
01941   nsAutoString styleVal;
01942   PRBool isStyleSet;
01943   *aHasStyleOrIdOrClass = PR_TRUE;
01944   nsresult res = GetAttributeValue(aElement,  NS_LITERAL_STRING("style"), styleVal, &isStyleSet);
01945   if (NS_FAILED(res)) return res;
01946   if (!isStyleSet || styleVal.IsEmpty()) {
01947     res = mHTMLCSSUtils->HasClassOrID(aElement, *aHasStyleOrIdOrClass);
01948     if (NS_FAILED(res)) return res;
01949   }
01950   return res;
01951 }
01952 
01953 nsresult
01954 nsHTMLEditor::RemoveElementIfNoStyleOrIdOrClass(nsIDOMElement * aElement, nsIAtom * aTag)
01955 {
01956   NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
01957   nsCOMPtr<nsIDOMNode> node  = do_QueryInterface(aElement);
01958 
01959   // early way out if node is not the right kind of element
01960   if (!NodeIsType(node, aTag)) {
01961     return NS_OK;
01962   }
01963   PRBool hasStyleOrIdOrClass;
01964   nsresult res = HasStyleOrIdOrClass(aElement, &hasStyleOrIdOrClass);
01965   if (!hasStyleOrIdOrClass) {
01966     res = RemoveContainer(node);
01967   }
01968   return res;
01969 }