Back to index

lightning-sunbird  0.9+nobinonly
nsHTMLCSSUtils.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  *   Original Author: 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 
00039 #include "nsHTMLEditor.h"
00040 #include "nsCOMPtr.h"
00041 #include "nsHTMLEditUtils.h"
00042 #include "nsIPrefBranch.h"
00043 #include "nsIPrefService.h"
00044 #include "nsIServiceManager.h"
00045 #include "nsEditProperty.h"
00046 #include "ChangeCSSInlineStyleTxn.h"
00047 #include "nsIDOMElement.h"
00048 #include "TransactionFactory.h"
00049 #include "nsIDOMElementCSSInlineStyle.h"
00050 #include "nsIDOMDocument.h"
00051 #include "nsIDOMDocumentView.h"
00052 #include "nsIContent.h"
00053 #include "nsIAtom.h"
00054 #include "nsTextEditUtils.h"
00055 #include "nsReadableUtils.h"
00056 #include "nsUnicharUtils.h"
00057 #include "nsHTMLCSSUtils.h"
00058 #include "nsColor.h"
00059 #include "nsAutoPtr.h"
00060 
00061 static
00062 void ProcessBValue(const nsAString * aInputString, nsAString & aOutputString,
00063                    const char * aDefaultValueString,
00064                    const char * aPrependString, const char* aAppendString)
00065 {
00066   if (aInputString && aInputString->EqualsLiteral("-moz-editor-invert-value")) {
00067       aOutputString.AssignLiteral("normal");
00068   }
00069   else {
00070     aOutputString.AssignLiteral("bold");
00071   }
00072 }
00073 
00074 static
00075 void ProcessDefaultValue(const nsAString * aInputString, nsAString & aOutputString,
00076                          const char * aDefaultValueString,
00077                          const char * aPrependString, const char* aAppendString)
00078 {
00079   CopyASCIItoUTF16(aDefaultValueString, aOutputString);
00080 }
00081 
00082 static
00083 void ProcessSameValue(const nsAString * aInputString, nsAString & aOutputString,
00084                       const char * aDefaultValueString,
00085                       const char * aPrependString, const char* aAppendString)
00086 {
00087   if (aInputString) {
00088     aOutputString.Assign(*aInputString);
00089   }
00090   else
00091     aOutputString.Truncate();
00092 }
00093 
00094 static
00095 void ProcessExtendedValue(const nsAString * aInputString, nsAString & aOutputString,
00096                           const char * aDefaultValueString,
00097                           const char * aPrependString, const char* aAppendString)
00098 {
00099   aOutputString.Truncate();
00100   if (aInputString) {
00101     if (aPrependString) {
00102       AppendASCIItoUTF16(aPrependString, aOutputString);
00103     }
00104     aOutputString.Append(*aInputString);
00105     if (aAppendString) {
00106       AppendASCIItoUTF16(aAppendString, aOutputString);
00107     }
00108   }
00109 }
00110 
00111 static
00112 void ProcessLengthValue(const nsAString * aInputString, nsAString & aOutputString,
00113                         const char * aDefaultValueString,
00114                         const char * aPrependString, const char* aAppendString)
00115 {
00116   aOutputString.Truncate();
00117   if (aInputString) {
00118     aOutputString.Append(*aInputString);
00119     if (-1 == aOutputString.FindChar(PRUnichar('%'))) {
00120       aOutputString.AppendLiteral("px");
00121     }
00122   }
00123 }
00124 
00125 static
00126 void ProcessListStyleTypeValue(const nsAString * aInputString, nsAString & aOutputString,
00127                                const char * aDefaultValueString,
00128                                const char * aPrependString, const char* aAppendString)
00129 {
00130   aOutputString.Truncate();
00131   if (aInputString) {
00132     if (aInputString->EqualsLiteral("1")) {
00133       aOutputString.AppendLiteral("decimal");
00134     }
00135     else if (aInputString->EqualsLiteral("a")) {
00136       aOutputString.AppendLiteral("lower-alpha");
00137     }
00138     else if (aInputString->EqualsLiteral("A")) {
00139       aOutputString.AppendLiteral("upper-alpha");
00140     }
00141     else if (aInputString->EqualsLiteral("i")) {
00142       aOutputString.AppendLiteral("lower-roman");
00143     }
00144     else if (aInputString->EqualsLiteral("I")) {
00145       aOutputString.AppendLiteral("upper-roman");
00146     }
00147     else if (aInputString->EqualsLiteral("square")
00148              || aInputString->EqualsLiteral("circle")
00149              || aInputString->EqualsLiteral("disc")) {
00150       aOutputString.Append(*aInputString);
00151     }
00152   }
00153 }
00154 
00155 static
00156 void ProcessMarginLeftValue(const nsAString * aInputString, nsAString & aOutputString,
00157                             const char * aDefaultValueString,
00158                             const char * aPrependString, const char* aAppendString)
00159 {
00160   aOutputString.Truncate();
00161   if (aInputString) {
00162     if (aInputString->EqualsLiteral("center") ||
00163         aInputString->EqualsLiteral("-moz-center")) {
00164       aOutputString.AppendLiteral("auto"); 
00165     }
00166     else if (aInputString->EqualsLiteral("right") ||
00167              aInputString->EqualsLiteral("-moz-right")) {
00168       aOutputString.AppendLiteral("auto"); 
00169     }
00170     else {
00171       aOutputString.AppendLiteral("0px"); 
00172     }
00173   }
00174 }
00175 
00176 static
00177 void ProcessMarginRightValue(const nsAString * aInputString, nsAString & aOutputString,
00178                              const char * aDefaultValueString,
00179                              const char * aPrependString, const char* aAppendString)
00180 {
00181   aOutputString.Truncate();
00182   if (aInputString) {
00183     if (aInputString->EqualsLiteral("center") ||
00184         aInputString->EqualsLiteral("-moz-center")) {
00185       aOutputString.AppendLiteral("auto"); 
00186     }
00187     else if (aInputString->EqualsLiteral("left") ||
00188              aInputString->EqualsLiteral("-moz-left")) {
00189       aOutputString.AppendLiteral("auto"); 
00190     }
00191     else {
00192       aOutputString.AppendLiteral("0px"); 
00193     }
00194   }
00195 }
00196 
00197 const nsHTMLCSSUtils::CSSEquivTable boldEquivTable[] = {
00198   { nsHTMLCSSUtils::eCSSEditableProperty_font_weight, ProcessBValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
00199   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00200 };
00201 
00202 const nsHTMLCSSUtils::CSSEquivTable italicEquivTable[] = {
00203   { nsHTMLCSSUtils::eCSSEditableProperty_font_style, ProcessDefaultValue, "italic", nsnull, nsnull, PR_TRUE, PR_FALSE },
00204   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00205 };
00206 
00207 const nsHTMLCSSUtils::CSSEquivTable underlineEquivTable[] = {
00208   { nsHTMLCSSUtils::eCSSEditableProperty_text_decoration, ProcessDefaultValue, "underline", nsnull, nsnull, PR_TRUE, PR_FALSE },
00209   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00210 };
00211 
00212 const nsHTMLCSSUtils::CSSEquivTable strikeEquivTable[] = {
00213   { nsHTMLCSSUtils::eCSSEditableProperty_text_decoration, ProcessDefaultValue, "line-through", nsnull, nsnull, PR_TRUE, PR_FALSE },
00214   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00215 };
00216 
00217 const nsHTMLCSSUtils::CSSEquivTable ttEquivTable[] = {
00218   { nsHTMLCSSUtils::eCSSEditableProperty_font_family, ProcessDefaultValue, "monospace", nsnull, nsnull, PR_TRUE, PR_FALSE },
00219   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00220 };
00221 
00222 const nsHTMLCSSUtils::CSSEquivTable fontColorEquivTable[] = {
00223   { nsHTMLCSSUtils::eCSSEditableProperty_color, ProcessSameValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
00224   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00225 };
00226 
00227 const nsHTMLCSSUtils::CSSEquivTable fontFaceEquivTable[] = {
00228   { nsHTMLCSSUtils::eCSSEditableProperty_font_family, ProcessSameValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
00229   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00230 };
00231 
00232 const nsHTMLCSSUtils::CSSEquivTable bgcolorEquivTable[] = {
00233   { nsHTMLCSSUtils::eCSSEditableProperty_background_color, ProcessSameValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
00234   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00235 };
00236 
00237 const nsHTMLCSSUtils::CSSEquivTable backgroundImageEquivTable[] = {
00238   { nsHTMLCSSUtils::eCSSEditableProperty_background_image, ProcessExtendedValue, nsnull, "url(", ")", PR_TRUE, PR_TRUE },
00239   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00240 };
00241 
00242 const nsHTMLCSSUtils::CSSEquivTable textColorEquivTable[] = {
00243   { nsHTMLCSSUtils::eCSSEditableProperty_color, ProcessSameValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
00244   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00245 };
00246 
00247 const nsHTMLCSSUtils::CSSEquivTable borderEquivTable[] = {
00248   { nsHTMLCSSUtils::eCSSEditableProperty_border, ProcessExtendedValue, nsnull, nsnull, "px solid", PR_TRUE, PR_FALSE },
00249   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00250 };
00251 
00252 const nsHTMLCSSUtils::CSSEquivTable textAlignEquivTable[] = {
00253   { nsHTMLCSSUtils::eCSSEditableProperty_text_align, ProcessSameValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
00254   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00255 };
00256 
00257 const nsHTMLCSSUtils::CSSEquivTable captionAlignEquivTable[] = {
00258   { nsHTMLCSSUtils::eCSSEditableProperty_caption_side, ProcessSameValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
00259   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00260 };
00261 
00262 const nsHTMLCSSUtils::CSSEquivTable verticalAlignEquivTable[] = {
00263   { nsHTMLCSSUtils::eCSSEditableProperty_vertical_align, ProcessSameValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
00264   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00265 };
00266 
00267 const nsHTMLCSSUtils::CSSEquivTable nowrapEquivTable[] = {
00268   { nsHTMLCSSUtils::eCSSEditableProperty_whitespace, ProcessDefaultValue, "nowrap", nsnull, nsnull, PR_TRUE, PR_FALSE },
00269   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00270 };
00271 
00272 const nsHTMLCSSUtils::CSSEquivTable widthEquivTable[] = {
00273   { nsHTMLCSSUtils::eCSSEditableProperty_width, ProcessLengthValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
00274   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00275 };
00276 
00277 const nsHTMLCSSUtils::CSSEquivTable heightEquivTable[] = {
00278   { nsHTMLCSSUtils::eCSSEditableProperty_height, ProcessLengthValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
00279   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00280 };
00281 
00282 const nsHTMLCSSUtils::CSSEquivTable listStyleTypeEquivTable[] = {
00283   { nsHTMLCSSUtils::eCSSEditableProperty_list_style_type, ProcessListStyleTypeValue, nsnull, nsnull, nsnull, PR_TRUE, PR_TRUE },
00284   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00285 };
00286 
00287 const nsHTMLCSSUtils::CSSEquivTable tableAlignEquivTable[] = {
00288   { nsHTMLCSSUtils::eCSSEditableProperty_text_align, ProcessDefaultValue, "left", nsnull, nsnull, PR_FALSE, PR_FALSE },
00289   { nsHTMLCSSUtils::eCSSEditableProperty_margin_left, ProcessMarginLeftValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
00290   { nsHTMLCSSUtils::eCSSEditableProperty_margin_right, ProcessMarginRightValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
00291   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00292 };
00293 
00294 const nsHTMLCSSUtils::CSSEquivTable hrAlignEquivTable[] = {
00295   { nsHTMLCSSUtils::eCSSEditableProperty_margin_left, ProcessMarginLeftValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
00296   { nsHTMLCSSUtils::eCSSEditableProperty_margin_right, ProcessMarginRightValue, nsnull, nsnull, nsnull, PR_TRUE, PR_FALSE },
00297   { nsHTMLCSSUtils::eCSSEditableProperty_NONE, 0 }
00298 };
00299 
00300 nsHTMLCSSUtils::nsHTMLCSSUtils()
00301 : mIsCSSPrefChecked(PR_FALSE)
00302 {
00303 }
00304 
00305 nsHTMLCSSUtils::~nsHTMLCSSUtils()
00306 {
00307 }
00308 
00309 nsresult
00310 nsHTMLCSSUtils::Init(nsHTMLEditor *aEditor)
00311 {
00312   nsresult result = NS_OK;
00313   mHTMLEditor = NS_STATIC_CAST(nsHTMLEditor*, aEditor);
00314 
00315   // let's retrieve the value of the "CSS editing" pref
00316   nsCOMPtr<nsIPrefBranch> prefBranch =
00317     do_GetService(NS_PREFSERVICE_CONTRACTID, &result);
00318   if (NS_SUCCEEDED(result) && prefBranch) {
00319     result = prefBranch->GetBoolPref("editor.use_css", &mIsCSSPrefChecked);
00320     if (NS_FAILED(result)) return result;
00321   }
00322   return result;
00323 }
00324 
00325 // Answers true if we have some CSS equivalence for the HTML style defined
00326 // by aProperty and/or aAttribute for the node aNode
00327 PRBool
00328 nsHTMLCSSUtils::IsCSSEditableProperty(nsIDOMNode * aNode,
00329                                       nsIAtom * aProperty,
00330                                       const nsAString * aAttribute)
00331 {
00332   NS_ASSERTION(aNode, "Shouldn't you pass aNode? - Bug 214025");
00333 
00334   nsCOMPtr<nsIDOMNode> node = aNode;
00335   // we need an element node here
00336   if (mHTMLEditor->IsTextNode(aNode)) {
00337     aNode->GetParentNode(getter_AddRefs(node));
00338   }
00339   nsCOMPtr<nsIContent> content = do_QueryInterface(node);
00340   if (!content) return PR_FALSE;
00341 
00342   nsIAtom *tagName = content->Tag();
00343   // brade: should the above use nsEditor::GetTag(aNode)?
00344   // brade: shouldn't some of the above go below the next block?
00345 
00346   // html inline styles B I TT U STRIKE and COLOR/FACE on FONT
00347   if (nsEditProperty::b == aProperty
00348       || nsEditProperty::i == aProperty
00349       || nsEditProperty::tt == aProperty
00350       || nsEditProperty::u == aProperty
00351       || nsEditProperty::strike == aProperty
00352       || ((nsEditProperty::font == aProperty) && aAttribute &&
00353            (aAttribute->EqualsLiteral("color") ||
00354             aAttribute->EqualsLiteral("face")))) {
00355     return PR_TRUE;
00356   }
00357 
00358   // ALIGN attribute on elements supporting it
00359   if (aAttribute && (aAttribute->EqualsLiteral("align")) &&
00360       (nsEditProperty::div == tagName
00361        || nsEditProperty::p   == tagName
00362        || nsEditProperty::h1  == tagName
00363        || nsEditProperty::h2  == tagName
00364        || nsEditProperty::h3  == tagName
00365        || nsEditProperty::h4  == tagName
00366        || nsEditProperty::h5  == tagName
00367        || nsEditProperty::h6  == tagName
00368        || nsEditProperty::td  == tagName
00369        || nsEditProperty::th  == tagName
00370        || nsEditProperty::table  == tagName
00371        || nsEditProperty::hr  == tagName
00372        // brade: for the above, why not use nsHTMLEditUtils::SupportsAlignAttr
00373        // brade: but it also checks for tbody, tfoot, thead
00374        // Let's add the following elements here even if ALIGN has not
00375        // the same meaning for them
00376        || nsEditProperty::legend  == tagName
00377        || nsEditProperty::caption == tagName)) {
00378     return PR_TRUE;
00379   }
00380 
00381   if (aAttribute && (aAttribute->EqualsLiteral("valign")) &&
00382       (nsEditProperty::col == tagName
00383        || nsEditProperty::colgroup   == tagName
00384        || nsEditProperty::tbody  == tagName
00385        || nsEditProperty::td  == tagName
00386        || nsEditProperty::th  == tagName
00387        || nsEditProperty::tfoot  == tagName
00388        || nsEditProperty::thead  == tagName
00389        || nsEditProperty::tr  == tagName)) {
00390     return PR_TRUE;
00391   }
00392 
00393   // attributes TEXT, BACKGROUND and BGCOLOR on BODY
00394   if (aAttribute && (nsEditProperty::body == tagName) &&
00395       (aAttribute->EqualsLiteral("text")
00396        || aAttribute->EqualsLiteral("background")
00397        || aAttribute->EqualsLiteral("bgcolor"))) {
00398     return PR_TRUE;
00399   }
00400 
00401   // attribute BGCOLOR on other elements
00402   if (aAttribute && aAttribute->EqualsLiteral("bgcolor")) {
00403     return PR_TRUE;
00404   }
00405 
00406   // attributes HEIGHT, WIDTH and NOWRAP on TD and TH
00407   if (aAttribute && ((nsEditProperty::td == tagName)
00408                       || (nsEditProperty::th == tagName)) &&
00409       (aAttribute->EqualsLiteral("height")
00410        || aAttribute->EqualsLiteral("width")
00411        || aAttribute->EqualsLiteral("nowrap"))) {
00412     return PR_TRUE;
00413   }
00414 
00415   // attributes HEIGHT and WIDTH on TABLE
00416   if (aAttribute && (nsEditProperty::table == tagName) &&
00417       (aAttribute->EqualsLiteral("height")
00418        || aAttribute->EqualsLiteral("width"))) {
00419     return PR_TRUE;
00420   }
00421 
00422   // attributes SIZE and WIDTH on HR
00423   if (aAttribute && (nsEditProperty::hr == tagName) &&
00424       (aAttribute->EqualsLiteral("size")
00425        || aAttribute->EqualsLiteral("width"))) {
00426     return PR_TRUE;
00427   }
00428 
00429   // attribute TYPE on OL UL LI
00430   if (aAttribute && (nsEditProperty::ol == tagName
00431                      || nsEditProperty::ul == tagName
00432                      || nsEditProperty::li == tagName) &&
00433       aAttribute->EqualsLiteral("type")) {
00434     return PR_TRUE;
00435   }
00436 
00437   if (aAttribute && nsEditProperty::img == tagName &&
00438       (aAttribute->EqualsLiteral("border")
00439        || aAttribute->EqualsLiteral("width")
00440        || aAttribute->EqualsLiteral("height"))) {
00441     return PR_TRUE;
00442   }
00443 
00444   // other elements that we can align using CSS even if they
00445   // can't carry the html ALIGN attribute
00446   if (aAttribute && aAttribute->EqualsLiteral("align") &&
00447       (nsEditProperty::ul == tagName
00448        || nsEditProperty::ol == tagName
00449        || nsEditProperty::dl == tagName
00450        || nsEditProperty::li == tagName
00451        || nsEditProperty::dd == tagName
00452        || nsEditProperty::dt == tagName
00453        || nsEditProperty::address == tagName
00454        || nsEditProperty::pre == tagName
00455        || nsEditProperty::ul == tagName)) {
00456     return PR_TRUE;
00457   }
00458 
00459   return PR_FALSE;
00460 }
00461 
00462 // the lowest level above the transaction; adds the css declaration "aProperty : aValue" to
00463 // the inline styles carried by aElement
00464 nsresult
00465 nsHTMLCSSUtils::SetCSSProperty(nsIDOMElement *aElement, nsIAtom * aProperty, const nsAString & aValue,
00466                                PRBool aSuppressTransaction)
00467 {
00468   nsRefPtr<ChangeCSSInlineStyleTxn> txn;
00469   nsresult result = CreateCSSPropertyTxn(aElement, aProperty, aValue,
00470                                          getter_AddRefs(txn), PR_FALSE);
00471   if (NS_SUCCEEDED(result))  {
00472     if (aSuppressTransaction) {
00473       result = txn->DoTransaction();
00474     }
00475     else {
00476       result = mHTMLEditor->DoTransaction(txn);
00477     }
00478   }
00479   return result;
00480 }
00481 
00482 nsresult
00483 nsHTMLCSSUtils::SetCSSPropertyPixels(nsIDOMElement *aElement,
00484                                      nsIAtom *aProperty,
00485                                      PRInt32 aIntValue,
00486                                      PRBool aSuppressTransaction)
00487 {
00488   nsAutoString s;
00489   s.AppendInt(aIntValue);
00490   return SetCSSProperty(aElement, aProperty, s + NS_LITERAL_STRING("px"),
00491                         aSuppressTransaction);
00492 }
00493 
00494 // the lowest level above the transaction; removes the value aValue from the list of values
00495 // specified for the CSS property aProperty, or totally remove the declaration if this
00496 // property accepts only one value
00497 nsresult
00498 nsHTMLCSSUtils::RemoveCSSProperty(nsIDOMElement *aElement, nsIAtom * aProperty, const nsAString & aValue,
00499                                   PRBool aSuppressTransaction)
00500 {
00501   nsRefPtr<ChangeCSSInlineStyleTxn> txn;
00502   nsresult result = CreateCSSPropertyTxn(aElement, aProperty, aValue,
00503                                          getter_AddRefs(txn), PR_TRUE);
00504   if (NS_SUCCEEDED(result))  {
00505     if (aSuppressTransaction) {
00506       result = txn->DoTransaction();
00507     }
00508     else {
00509       result = mHTMLEditor->DoTransaction(txn);
00510     }
00511   }
00512   return result;
00513 }
00514 
00515 nsresult 
00516 nsHTMLCSSUtils::CreateCSSPropertyTxn(nsIDOMElement *aElement, 
00517                                      nsIAtom * aAttribute,
00518                                      const nsAString& aValue,
00519                                      ChangeCSSInlineStyleTxn ** aTxn,
00520                                      PRBool aRemoveProperty)
00521 {
00522   nsresult result = NS_ERROR_NULL_POINTER;
00523   if (aElement)
00524   {
00525     result = TransactionFactory::GetNewTransaction(ChangeCSSInlineStyleTxn::GetCID(), (EditTxn **)aTxn);
00526     if (NS_SUCCEEDED(result))  {
00527       result = (*aTxn)->Init(mHTMLEditor, aElement, aAttribute, aValue, aRemoveProperty);
00528     }
00529   }
00530   return result;
00531 }
00532 
00533 nsresult
00534 nsHTMLCSSUtils::GetSpecifiedProperty(nsIDOMNode *aNode, nsIAtom *aProperty,
00535                                      nsAString & aValue)
00536 {
00537   return GetCSSInlinePropertyBase(aNode, aProperty, aValue, nsnull, SPECIFIED_STYLE_TYPE);
00538 }
00539 
00540 nsresult
00541 nsHTMLCSSUtils::GetComputedProperty(nsIDOMNode *aNode, nsIAtom *aProperty,
00542                                     nsAString & aValue)
00543 {
00544   nsCOMPtr<nsIDOMViewCSS> viewCSS = nsnull;
00545   nsresult res = GetDefaultViewCSS(aNode, getter_AddRefs(viewCSS));
00546   if (NS_FAILED(res)) return res;
00547 
00548   return GetCSSInlinePropertyBase(aNode, aProperty, aValue, viewCSS, COMPUTED_STYLE_TYPE);
00549 }
00550 
00551 nsresult
00552 nsHTMLCSSUtils::GetCSSInlinePropertyBase(nsIDOMNode *aNode, nsIAtom *aProperty,
00553                                         nsAString &aValue,
00554                                         nsIDOMViewCSS *aViewCSS,
00555                                         PRUint8 aStyleType)
00556 {
00557   aValue.Truncate();
00558   NS_ENSURE_TRUE(aProperty, NS_ERROR_NULL_POINTER);
00559 
00560   nsCOMPtr<nsIDOMElement>element;
00561   nsresult res = GetElementContainerOrSelf(aNode, getter_AddRefs(element));
00562   if (NS_FAILED(res)) return res;
00563 
00564   switch (aStyleType) {
00565     case COMPUTED_STYLE_TYPE:
00566       if (element && aViewCSS) {
00567         nsAutoString empty, value, propString;
00568         nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
00569         aProperty->ToString(propString);
00570         // Get the all the computed css styles attached to the element node
00571         res = aViewCSS->GetComputedStyle(element, empty, getter_AddRefs(cssDecl));
00572         if (NS_FAILED(res) || !cssDecl) return res;
00573         // from these declarations, get the one we want and that one only
00574         res = cssDecl->GetPropertyValue(propString, value);
00575         if (NS_FAILED(res)) return res;
00576         aValue.Assign(value);
00577       }
00578       break;
00579     case SPECIFIED_STYLE_TYPE:
00580       if (element) {
00581         nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
00582         PRUint32 length;
00583         res = GetInlineStyles(element, getter_AddRefs(cssDecl), &length);
00584         if (NS_FAILED(res) || !cssDecl) return res;
00585         nsAutoString value, propString;
00586         aProperty->ToString(propString);
00587         res = cssDecl->GetPropertyValue(propString, value);
00588         if (NS_FAILED(res)) return res;
00589         aValue.Assign(value);
00590       }
00591       break;
00592   }
00593   return NS_OK;
00594 }
00595 
00596 nsresult
00597 nsHTMLCSSUtils::GetDefaultViewCSS(nsIDOMNode *aNode, nsIDOMViewCSS **aViewCSS)
00598 {
00599   nsCOMPtr<nsIDOMElement>element;
00600   nsresult res = GetElementContainerOrSelf(aNode, getter_AddRefs(element));
00601   if (NS_FAILED(res)) return res;
00602 
00603   // if we have an element node
00604   if (element) {
00605     // find the owner document
00606     nsCOMPtr<nsIDOMDocument> doc;
00607     nsCOMPtr<nsIDOMNode> node = do_QueryInterface(element);
00608     res = node->GetOwnerDocument(getter_AddRefs(doc));
00609     if (NS_FAILED(res)) return res;
00610     if (doc) {
00611       nsCOMPtr<nsIDOMDocumentView> documentView = do_QueryInterface(doc);
00612       nsCOMPtr<nsIDOMAbstractView> abstractView;
00613       // from the document, get the abtractView
00614       res = documentView->GetDefaultView(getter_AddRefs(abstractView));
00615       if (NS_FAILED(res)) return res;
00616       if (abstractView) {
00617         // from the abstractView, get the CSS view
00618         CallQueryInterface(abstractView, aViewCSS);
00619         return NS_OK;
00620       }
00621     }
00622   }
00623   *aViewCSS = nsnull;
00624   return NS_OK;
00625 }
00626 
00627 nsresult
00628 NS_NewHTMLCSSUtils(nsHTMLCSSUtils** aInstancePtrResult)
00629 {
00630   nsHTMLCSSUtils * rules = new nsHTMLCSSUtils();
00631   if (rules) {
00632     *aInstancePtrResult = rules;
00633     return NS_OK;
00634   }
00635 
00636   *aInstancePtrResult = nsnull;
00637   return NS_ERROR_OUT_OF_MEMORY;
00638 }
00639 
00640 // remove the CSS style "aProperty : aPropertyValue" and possibly remove the whole node
00641 // if it is a span and if its only attribute is _moz_dirty
00642 nsresult
00643 nsHTMLCSSUtils::RemoveCSSInlineStyle(nsIDOMNode *aNode, nsIAtom *aProperty, const nsAString & aPropertyValue)
00644 {
00645   nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(aNode);
00646 
00647   // remove the property from the style attribute
00648   nsresult res = RemoveCSSProperty(elem, aProperty, aPropertyValue, PR_FALSE);
00649   if (NS_FAILED(res)) return res;
00650 
00651   if (nsEditor::NodeIsType(aNode, nsEditProperty::span)) {
00652     nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
00653     PRUint32 attrCount = content->GetAttrCount();
00654 
00655     if (0 == attrCount) {
00656       // no more attributes on this span, let's remove the element
00657       res = mHTMLEditor->RemoveContainer(aNode);
00658       if (NS_FAILED(res)) return res;
00659     }
00660     else if (1 == attrCount) {
00661       // incredible hack in case the only remaining attribute is a _moz_dirty...
00662       PRInt32 nameSpaceID;
00663       nsCOMPtr<nsIAtom> attrName, prefix;
00664       res = content->GetAttrNameAt(0, &nameSpaceID, getter_AddRefs(attrName),
00665                                    getter_AddRefs(prefix));
00666       if (NS_FAILED(res)) return res;
00667       nsAutoString attrString, tmp;
00668       attrName->ToString(attrString);
00669       if (attrString.EqualsLiteral("_moz_dirty")) {
00670         res = mHTMLEditor->RemoveContainer(aNode);
00671         if (NS_FAILED(res)) return res;
00672       }
00673     }
00674   }
00675   return NS_OK;
00676 }
00677 
00678 // Answers true is the property can be removed by setting a "none" CSS value
00679 // on a node
00680 PRBool
00681 nsHTMLCSSUtils::IsCSSInvertable(nsIAtom *aProperty, const nsAString *aAttribute)
00682 {
00683   return PRBool(nsEditProperty::b == aProperty);
00684 }
00685 
00686 // Get the default browser background color if we need it for GetCSSBackgroundColorState
00687 nsresult
00688 nsHTMLCSSUtils::GetDefaultBackgroundColor(nsAString & aColor)
00689 {
00690   nsresult result;
00691   nsCOMPtr<nsIPrefBranch> prefBranch =
00692     do_GetService(NS_PREFSERVICE_CONTRACTID, &result);
00693   if (NS_FAILED(result)) return result;
00694   aColor.AssignLiteral("#ffffff");
00695   nsXPIDLCString returnColor;
00696   if (prefBranch) {
00697     PRBool useCustomColors;
00698     result = prefBranch->GetBoolPref("editor.use_custom_colors", &useCustomColors);
00699     if (NS_FAILED(result)) return result;
00700     if (useCustomColors) {
00701       result = prefBranch->GetCharPref("editor.background_color",
00702                                        getter_Copies(returnColor));
00703       if (NS_FAILED(result)) return result;
00704     }
00705     else {
00706       PRBool useSystemColors;
00707       result = prefBranch->GetBoolPref("browser.display.use_system_colors", &useSystemColors);
00708       if (NS_FAILED(result)) return result;
00709       if (!useSystemColors) {
00710         result = prefBranch->GetCharPref("browser.display.background_color",
00711                                          getter_Copies(returnColor));
00712         if (NS_FAILED(result)) return result;
00713       }
00714     }
00715   }
00716   if (returnColor) {
00717     CopyASCIItoUTF16(returnColor, aColor);
00718   }
00719   return NS_OK;
00720 }
00721 
00722 // Get the default length unit used for CSS Indent/Outdent
00723 nsresult
00724 nsHTMLCSSUtils::GetDefaultLengthUnit(nsAString & aLengthUnit)
00725 {
00726   nsresult result;
00727   nsCOMPtr<nsIPrefBranch> prefBranch =
00728     do_GetService(NS_PREFSERVICE_CONTRACTID, &result);
00729   if (NS_FAILED(result)) return result;
00730   aLengthUnit.AssignLiteral("px");
00731   if (NS_SUCCEEDED(result) && prefBranch) {
00732     nsXPIDLCString returnLengthUnit;
00733     result = prefBranch->GetCharPref("editor.css.default_length_unit",
00734                                      getter_Copies(returnLengthUnit));
00735     if (NS_FAILED(result)) return result;
00736     if (returnLengthUnit) {
00737       CopyASCIItoUTF16(returnLengthUnit, aLengthUnit);
00738     }
00739   }
00740   return NS_OK;
00741 }
00742 
00743 // Unfortunately, CSSStyleDeclaration::GetPropertyCSSValue is not yet implemented...
00744 // We need then a way to determine the number part and the unit from aString, aString
00745 // being the result of a GetPropertyValue query...
00746 void
00747 nsHTMLCSSUtils::ParseLength(const nsAString & aString, float * aValue, nsIAtom ** aUnit)
00748 {
00749   nsAString::const_iterator iter;
00750   aString.BeginReading(iter);
00751 
00752   float a = 10.0f , b = 1.0f, value = 0;
00753   PRInt8 sign = 1;
00754   PRInt32 i = 0, j = aString.Length();
00755   PRUnichar c;
00756   nsAutoString unit;
00757   PRBool floatingPointFound = PR_FALSE;
00758   c = *iter;
00759   if (PRUnichar('-') == c) { sign = -1; iter++; i++; }
00760   else if (PRUnichar('+') == c) { iter++; i++; }
00761   while (i < j) {
00762     c = *iter;
00763     if ((PRUnichar('0') == c) ||
00764         (PRUnichar('1') == c) ||
00765         (PRUnichar('2') == c) ||
00766         (PRUnichar('3') == c) ||
00767         (PRUnichar('4') == c) ||
00768         (PRUnichar('5') == c) ||
00769         (PRUnichar('6') == c) ||
00770         (PRUnichar('7') == c) ||
00771         (PRUnichar('8') == c) ||
00772         (PRUnichar('9') == c)) {
00773       value = (value * a) + (b * (c - PRUnichar('0')));
00774       b = b / 10 * a;
00775     }
00776     else if (!floatingPointFound && (PRUnichar('.') == c)) {
00777       floatingPointFound = PR_TRUE;
00778       a = 1.0f; b = 0.1f;
00779     }
00780     else break;
00781     iter++;
00782     i++;
00783   }
00784   unit = Substring(aString, aString.Length() - (j-i), j-i);
00785   *aValue = value * sign;
00786   *aUnit = NS_NewAtom(unit); 
00787 }
00788 
00789 void
00790 nsHTMLCSSUtils::GetCSSPropertyAtom(nsCSSEditableProperty aProperty, nsIAtom ** aAtom)
00791 {
00792   *aAtom = nsnull;
00793   if (0 < aProperty) {
00794     switch (aProperty) {
00795       case eCSSEditableProperty_background_color:
00796         *aAtom = nsEditProperty::cssBackgroundColor;
00797         break;
00798       case eCSSEditableProperty_background_image:
00799         *aAtom = nsEditProperty::cssBackgroundImage;
00800         break;
00801       case eCSSEditableProperty_border:
00802         *aAtom = nsEditProperty::cssBorder;
00803         break;
00804       case eCSSEditableProperty_caption_side:
00805         *aAtom = nsEditProperty::cssCaptionSide;
00806         break;
00807       case eCSSEditableProperty_color:
00808         *aAtom = nsEditProperty::cssColor;
00809         break;
00810       case eCSSEditableProperty_float:
00811         *aAtom = nsEditProperty::cssFloat;
00812         break;
00813       case eCSSEditableProperty_font_family:
00814         *aAtom = nsEditProperty::cssFontFamily;
00815         break;
00816       case eCSSEditableProperty_font_size:
00817         *aAtom = nsEditProperty::cssFontSize;
00818         break;
00819       case eCSSEditableProperty_font_style:
00820         *aAtom = nsEditProperty::cssFontStyle;
00821         break;
00822       case eCSSEditableProperty_font_weight:
00823         *aAtom = nsEditProperty::cssFontWeight;
00824         break;
00825       case eCSSEditableProperty_height:
00826         *aAtom = nsEditProperty::cssHeight;
00827         break;
00828       case eCSSEditableProperty_list_style_type:
00829         *aAtom = nsEditProperty::cssListStyleType;
00830         break;
00831       case eCSSEditableProperty_margin_left:
00832         *aAtom = nsEditProperty::cssMarginLeft;
00833         break;
00834       case eCSSEditableProperty_margin_right:
00835         *aAtom = nsEditProperty::cssMarginRight;
00836         break;
00837       case eCSSEditableProperty_text_align:
00838         *aAtom = nsEditProperty::cssTextAlign;
00839         break;
00840       case eCSSEditableProperty_text_decoration:
00841         *aAtom = nsEditProperty::cssTextDecoration;
00842         break;
00843       case eCSSEditableProperty_vertical_align:
00844         *aAtom = nsEditProperty::cssVerticalAlign;
00845         break;
00846       case eCSSEditableProperty_whitespace:
00847         *aAtom = nsEditProperty::cssWhitespace;
00848         break;
00849       case eCSSEditableProperty_width:
00850         *aAtom = nsEditProperty::cssWidth;
00851         break;
00852     }
00853   }
00854 }
00855 
00856 // Populate aProperty and aValueArray with the CSS declarations equivalent to the
00857 // value aValue according to the equivalence table aEquivTable
00858 void
00859 nsHTMLCSSUtils::BuildCSSDeclarations(nsVoidArray & aPropertyArray,
00860                                      nsStringArray & aValueArray,
00861                                      const CSSEquivTable * aEquivTable,
00862                                      const nsAString * aValue,
00863                                      PRBool aGetOrRemoveRequest)
00864 {
00865   // clear arrays
00866   aPropertyArray.Clear();
00867   aValueArray.Clear();
00868 
00869   // if we have an input value, let's use it
00870   nsAutoString value, lowerCasedValue;
00871   if (aValue) {
00872     value.Assign(*aValue);
00873     lowerCasedValue.Assign(*aValue);
00874     ToLowerCase(lowerCasedValue);
00875   }
00876 
00877   PRInt8 index = 0;
00878   nsCSSEditableProperty cssProperty = aEquivTable[index].cssProperty;
00879   while (cssProperty) {
00880     if (!aGetOrRemoveRequest|| aEquivTable[index].gettable) {
00881       nsAutoString cssValue, cssPropertyString;
00882       nsIAtom * cssPropertyAtom;
00883       // find the equivalent css value for the index-th property in
00884       // the equivalence table
00885       (*aEquivTable[index].processValueFunctor) ((!aGetOrRemoveRequest || aEquivTable[index].caseSensitiveValue) ? &value : &lowerCasedValue,
00886                                                  cssValue,
00887                                                  aEquivTable[index].defaultValue,
00888                                                  aEquivTable[index].prependValue,
00889                                                  aEquivTable[index].appendValue);
00890       GetCSSPropertyAtom(cssProperty, &cssPropertyAtom);
00891       aPropertyArray.AppendElement(cssPropertyAtom);
00892       aValueArray.AppendString(cssValue);
00893     }
00894     index++;
00895     cssProperty = aEquivTable[index].cssProperty;
00896   }
00897 }
00898 
00899 // Populate cssPropertyArray and cssValueArray with the declarations equivalent
00900 // to aHTMLProperty/aAttribute/aValue for the node aNode
00901 void
00902 nsHTMLCSSUtils::GenerateCSSDeclarationsFromHTMLStyle(nsIDOMNode * aNode,
00903                                                      nsIAtom *aHTMLProperty,
00904                                                      const nsAString * aAttribute,
00905                                                      const nsAString * aValue,
00906                                                      nsVoidArray & cssPropertyArray,
00907                                                      nsStringArray & cssValueArray,
00908                                                      PRBool aGetOrRemoveRequest)
00909 {
00910   nsCOMPtr<nsIDOMNode> node = aNode;
00911   if (mHTMLEditor->IsTextNode(aNode)) {
00912     aNode->GetParentNode(getter_AddRefs(node));
00913   }
00914   if (!node) return;
00915 
00916   nsIAtom *tagName = nsEditor::GetTag(node);
00917 
00918   if (nsEditProperty::b == aHTMLProperty) {
00919     BuildCSSDeclarations(cssPropertyArray, cssValueArray, boldEquivTable, aValue, aGetOrRemoveRequest);
00920   }
00921   else if (nsEditProperty::i == aHTMLProperty) {
00922     BuildCSSDeclarations(cssPropertyArray, cssValueArray, italicEquivTable, aValue, aGetOrRemoveRequest);
00923   }
00924   else if (nsEditProperty::u == aHTMLProperty) {
00925     BuildCSSDeclarations(cssPropertyArray, cssValueArray, underlineEquivTable, aValue, aGetOrRemoveRequest);
00926   }
00927   else if (nsEditProperty::strike == aHTMLProperty) {
00928     BuildCSSDeclarations(cssPropertyArray, cssValueArray, strikeEquivTable, aValue, aGetOrRemoveRequest);
00929   }
00930   else if (nsEditProperty::tt == aHTMLProperty) {
00931     BuildCSSDeclarations(cssPropertyArray, cssValueArray, ttEquivTable, aValue, aGetOrRemoveRequest);
00932   }
00933   else if (aAttribute) {
00934     if (nsEditProperty::font == aHTMLProperty &&
00935         aAttribute->EqualsLiteral("color")) {
00936       BuildCSSDeclarations(cssPropertyArray, cssValueArray, fontColorEquivTable, aValue, aGetOrRemoveRequest);
00937     }
00938     else if (nsEditProperty::font == aHTMLProperty &&
00939              aAttribute->EqualsLiteral("face")) {
00940       BuildCSSDeclarations(cssPropertyArray, cssValueArray, fontFaceEquivTable, aValue, aGetOrRemoveRequest);
00941     }
00942     else if (aAttribute->EqualsLiteral("bgcolor")) {
00943       BuildCSSDeclarations(cssPropertyArray, cssValueArray, bgcolorEquivTable, aValue, aGetOrRemoveRequest);
00944     }
00945     else if (aAttribute->EqualsLiteral("background")) {
00946       BuildCSSDeclarations(cssPropertyArray, cssValueArray, backgroundImageEquivTable, aValue, aGetOrRemoveRequest);
00947     }
00948     else if (aAttribute->EqualsLiteral("text")) {
00949       BuildCSSDeclarations(cssPropertyArray, cssValueArray, textColorEquivTable, aValue, aGetOrRemoveRequest);
00950     }
00951     else if (aAttribute->EqualsLiteral("border")) {
00952       BuildCSSDeclarations(cssPropertyArray, cssValueArray, borderEquivTable, aValue, aGetOrRemoveRequest);
00953     }
00954     else if (aAttribute->EqualsLiteral("align")) {
00955       if (nsEditProperty::table  == tagName) {
00956         BuildCSSDeclarations(cssPropertyArray, cssValueArray, tableAlignEquivTable, aValue, aGetOrRemoveRequest);
00957       }
00958       else if (nsEditProperty::hr  == tagName) {
00959         BuildCSSDeclarations(cssPropertyArray, cssValueArray, hrAlignEquivTable, aValue, aGetOrRemoveRequest);
00960       }
00961       else if (nsEditProperty::legend  == tagName ||
00962                nsEditProperty::caption == tagName) {
00963         BuildCSSDeclarations(cssPropertyArray, cssValueArray, captionAlignEquivTable, aValue, aGetOrRemoveRequest);
00964       }
00965       else {
00966         BuildCSSDeclarations(cssPropertyArray, cssValueArray, textAlignEquivTable, aValue, aGetOrRemoveRequest);
00967       }
00968     }
00969     else if (aAttribute->EqualsLiteral("valign")) {
00970       BuildCSSDeclarations(cssPropertyArray, cssValueArray, verticalAlignEquivTable, aValue, aGetOrRemoveRequest);
00971     }
00972     else if (aAttribute->EqualsLiteral("nowrap")) {
00973       BuildCSSDeclarations(cssPropertyArray, cssValueArray, nowrapEquivTable, aValue, aGetOrRemoveRequest);
00974     }
00975     else if (aAttribute->EqualsLiteral("width")) {
00976       BuildCSSDeclarations(cssPropertyArray, cssValueArray, widthEquivTable, aValue, aGetOrRemoveRequest);
00977     }
00978     else if (aAttribute->EqualsLiteral("height") ||
00979              (nsEditProperty::hr == tagName && aAttribute->EqualsLiteral("size"))) {
00980       BuildCSSDeclarations(cssPropertyArray, cssValueArray, heightEquivTable, aValue, aGetOrRemoveRequest);
00981     }
00982     else if (aAttribute->EqualsLiteral("type") &&
00983              (nsEditProperty::ol == tagName
00984               || nsEditProperty::ul == tagName
00985               || nsEditProperty::li == tagName)) {
00986       BuildCSSDeclarations(cssPropertyArray, cssValueArray, listStyleTypeEquivTable, aValue, aGetOrRemoveRequest);
00987     }
00988   }
00989 }
00990 
00991 // Add to aNode the CSS inline style equivalent to HTMLProperty/aAttribute/aValue for the node,
00992 // and return in aCount the number of CSS properties set by the call
00993 nsresult
00994 nsHTMLCSSUtils::SetCSSEquivalentToHTMLStyle(nsIDOMNode * aNode,
00995                                             nsIAtom *aHTMLProperty,
00996                                             const nsAString *aAttribute,
00997                                             const nsAString *aValue,
00998                                             PRInt32 * aCount,
00999                                             PRBool aSuppressTransaction)
01000 {
01001   nsCOMPtr<nsIDOMElement> theElement = do_QueryInterface(aNode);
01002   nsresult res = NS_OK;
01003   *aCount = 0;
01004   if (theElement && IsCSSEditableProperty(aNode, aHTMLProperty, aAttribute)) {
01005     // we can apply the styles only if the node is an element and if we have
01006     // an equivalence for the requested HTML style in this implementation
01007 
01008     // Find the CSS equivalence to the HTML style
01009     nsVoidArray cssPropertyArray;
01010     nsStringArray cssValueArray;
01011     GenerateCSSDeclarationsFromHTMLStyle(aNode, aHTMLProperty, aAttribute, aValue,
01012                                          cssPropertyArray, cssValueArray, PR_FALSE);
01013 
01014     // set the individual CSS inline styles
01015     *aCount = cssPropertyArray.Count();
01016     PRInt32 index;
01017     for (index = 0; index < *aCount; index++) {
01018       nsAutoString valueString;
01019       cssValueArray.StringAt(index, valueString);
01020       nsCOMPtr<nsIDOMElement> theElement = do_QueryInterface(aNode);
01021       res = SetCSSProperty(theElement, (nsIAtom *)cssPropertyArray.ElementAt(index),
01022                            valueString, aSuppressTransaction);
01023       if (NS_FAILED(res)) return res;
01024     }
01025   }
01026   return NS_OK;
01027 }
01028 
01029 // Remove from aNode the CSS inline style equivalent to HTMLProperty/aAttribute/aValue for the node
01030 nsresult
01031 nsHTMLCSSUtils::RemoveCSSEquivalentToHTMLStyle(nsIDOMNode * aNode,
01032                                                nsIAtom *aHTMLProperty,
01033                                                const nsAString *aAttribute,
01034                                                const nsAString *aValue,
01035                                                PRBool aSuppressTransaction)
01036 {
01037   nsCOMPtr<nsIDOMElement> theElement = do_QueryInterface(aNode);
01038   nsresult res = NS_OK;
01039   PRInt32 count = 0;
01040   if (theElement && IsCSSEditableProperty(aNode, aHTMLProperty, aAttribute)) {
01041     // we can apply the styles only if the node is an element and if we have
01042     // an equivalence for the requested HTML style in this implementation
01043 
01044     // Find the CSS equivalence to the HTML style
01045     nsVoidArray cssPropertyArray;
01046     nsStringArray cssValueArray;
01047     GenerateCSSDeclarationsFromHTMLStyle(aNode, aHTMLProperty, aAttribute, aValue,
01048                                          cssPropertyArray, cssValueArray, PR_TRUE);
01049 
01050     // remove the individual CSS inline styles
01051     count = cssPropertyArray.Count();
01052     PRInt32 index;
01053     for (index = 0; index < count; index++) {
01054       nsAutoString valueString;
01055       cssValueArray.StringAt(index, valueString);
01056       res = RemoveCSSProperty(theElement, (nsIAtom *)cssPropertyArray.ElementAt(index), valueString,
01057                               aSuppressTransaction);
01058       if (NS_FAILED(res)) return res;
01059     }
01060   }
01061   return NS_OK;
01062 }
01063 
01064 // aReturn is true if the element aElement carries an ID or a class.
01065 nsresult
01066 nsHTMLCSSUtils::HasClassOrID(nsIDOMElement * aElement, PRBool & aReturn)
01067 {
01068   nsAutoString classVal, idVal;
01069   PRBool isClassSet, isIdSet;
01070   aReturn = PR_FALSE;
01071 
01072   nsresult res = mHTMLEditor->GetAttributeValue(aElement,  NS_LITERAL_STRING("class"), classVal, &isClassSet);
01073   if (NS_FAILED(res)) return res;
01074   res = mHTMLEditor->GetAttributeValue(aElement,  NS_LITERAL_STRING("id"), idVal, &isIdSet);
01075   if (NS_FAILED(res)) return res;
01076 
01077   // we need to make sure that if the element has an id or a class attribute,
01078   // the attribute is not the empty string
01079   aReturn = ((isClassSet && !classVal.IsEmpty()) ||
01080              (isIdSet    && !idVal.IsEmpty()));
01081   return NS_OK;
01082 }
01083 
01084 // returns in aValueString the list of values for the CSS equivalences to
01085 // the HTML style aHTMLProperty/aAttribute/aValueString for the node aNode;
01086 // the value of aStyleType controls the styles we retrieve : specified or
01087 // computed.
01088 nsresult
01089 nsHTMLCSSUtils::GetCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode * aNode,
01090                                                      nsIAtom *aHTMLProperty,
01091                                                      const nsAString *aAttribute,
01092                                                      nsAString & aValueString,
01093                                                      PRUint8 aStyleType)
01094 {
01095   aValueString.Truncate();
01096   nsCOMPtr<nsIDOMElement> theElement;
01097   nsresult res = GetElementContainerOrSelf(aNode, getter_AddRefs(theElement));
01098   if (NS_FAILED(res)) return res;
01099 
01100   if (theElement && IsCSSEditableProperty(theElement, aHTMLProperty, aAttribute)) {
01101     // Yes, the requested HTML style has a CSS equivalence in this implementation
01102     // Retrieve the default ViewCSS if we are asked for computed styles
01103     nsCOMPtr<nsIDOMViewCSS> viewCSS = nsnull;
01104     if (COMPUTED_STYLE_TYPE == aStyleType) {
01105       res = GetDefaultViewCSS(theElement, getter_AddRefs(viewCSS));
01106       if (NS_FAILED(res)) return res;
01107     }
01108     nsVoidArray cssPropertyArray;
01109     nsStringArray cssValueArray;
01110     // get the CSS equivalence with last param PR_TRUE indicating we want only the
01111     // "gettable" properties
01112     GenerateCSSDeclarationsFromHTMLStyle(theElement, aHTMLProperty, aAttribute, nsnull,
01113                                          cssPropertyArray, cssValueArray, PR_TRUE);
01114     PRInt32 count = cssPropertyArray.Count();
01115     PRInt32 index;
01116     for (index = 0; index < count; index++) {
01117       nsAutoString valueString;
01118       // retrieve the specified/computed value of the property
01119       res = GetCSSInlinePropertyBase(theElement, (nsIAtom *)cssPropertyArray.ElementAt(index),
01120                                      valueString, viewCSS, aStyleType);
01121       if (NS_FAILED(res)) return res;
01122       // append the value to aValueString (possibly with a leading whitespace)
01123       if (index) aValueString.Append(PRUnichar(' '));
01124       aValueString.Append(valueString);
01125     }
01126   }
01127   return NS_OK;
01128 }
01129 
01130 // Does the node aNode (or his parent if it is not an element node) carries
01131 // the CSS equivalent styles to the HTML style aHTMLProperty/aAttribute/
01132 // aValueString for this node ?
01133 // The value of aStyleType controls the styles we retrieve : specified or
01134 // computed. The return value aIsSet is true is the CSS styles are set.
01135 nsresult
01136 nsHTMLCSSUtils::IsCSSEquivalentToHTMLInlineStyleSet(nsIDOMNode * aNode,
01137                                                     nsIAtom *aHTMLProperty,
01138                                                     const nsAString * aHTMLAttribute,
01139                                                     PRBool & aIsSet,
01140                                                     nsAString & valueString,
01141                                                     PRUint8 aStyleType)
01142 {
01143   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
01144 
01145   nsAutoString htmlValueString(valueString);
01146   aIsSet = PR_FALSE;
01147   nsCOMPtr<nsIDOMNode> node = aNode;
01148   NS_NAMED_LITERAL_STRING(boldStr, "bold");
01149   do {
01150     valueString.Assign(htmlValueString);
01151     // get the value of the CSS equivalent styles
01152     nsresult res = GetCSSEquivalentToHTMLInlineStyleSet(node, aHTMLProperty, aHTMLAttribute,
01153                                                         valueString, aStyleType);
01154     if (NS_FAILED(res)) return res;
01155 
01156     // early way out if we can
01157     if (valueString.IsEmpty()) return NS_OK;
01158 
01159     if (nsEditProperty::b == aHTMLProperty) {
01160       if (valueString.Equals(boldStr)) {
01161         aIsSet = PR_TRUE;
01162       }
01163       else if (valueString.EqualsLiteral("normal")) {
01164         aIsSet = PR_FALSE;
01165       }
01166       else if (valueString.EqualsLiteral("bolder")) {
01167         aIsSet = PR_TRUE;
01168         valueString.Assign(boldStr);
01169       }
01170       else {
01171         PRInt32 weight = 0;
01172         PRInt32 errorCode;
01173         nsAutoString value(valueString);
01174         weight = value.ToInteger(&errorCode, 10);
01175         if (400 < weight) {
01176           aIsSet = PR_TRUE;
01177           valueString.Assign(boldStr);
01178         }
01179         else {
01180           aIsSet = PR_FALSE;
01181           valueString.AssignLiteral("normal");
01182         }
01183       }
01184     }
01185     
01186     else if (nsEditProperty::i == aHTMLProperty) {
01187       if (valueString.EqualsLiteral("italic") ||
01188           valueString.EqualsLiteral("oblique")) {
01189         aIsSet= PR_TRUE;
01190       }
01191     }
01192 
01193     else if (nsEditProperty::u == aHTMLProperty) {
01194       nsAutoString val;
01195       val.AssignLiteral("underline");
01196       aIsSet = PRBool(ChangeCSSInlineStyleTxn::ValueIncludes(valueString, val, PR_FALSE));
01197     }
01198 
01199     else if (nsEditProperty::strike == aHTMLProperty) {
01200       nsAutoString val;
01201       val.AssignLiteral("line-through");
01202       aIsSet = PRBool(ChangeCSSInlineStyleTxn::ValueIncludes(valueString, val, PR_FALSE));
01203     }
01204 
01205     else if (aHTMLAttribute &&
01206              ( (nsEditProperty::font == aHTMLProperty && 
01207                 aHTMLAttribute->EqualsLiteral("color")) ||
01208                aHTMLAttribute->EqualsLiteral("bgcolor"))) {
01209       if (htmlValueString.IsEmpty())
01210         aIsSet = PR_TRUE;
01211       else {
01212         nscolor rgba;
01213         nsAutoString subStr;
01214         htmlValueString.Right(subStr, htmlValueString.Length()-1);
01215         if (NS_ColorNameToRGB(htmlValueString, &rgba) ||
01216             NS_HexToRGB(subStr, &rgba)) {
01217           nsAutoString htmlColor, tmpStr;
01218           htmlColor.AppendLiteral("rgb(");
01219 
01220           NS_NAMED_LITERAL_STRING(comma, ", ");
01221 
01222           tmpStr.AppendInt(NS_GET_R(rgba), 10);
01223           htmlColor.Append(tmpStr + comma);
01224 
01225           tmpStr.Truncate();
01226           tmpStr.AppendInt(NS_GET_G(rgba), 10);
01227           htmlColor.Append(tmpStr + comma);
01228 
01229           tmpStr.Truncate();
01230           tmpStr.AppendInt(NS_GET_B(rgba), 10);
01231           htmlColor.Append(tmpStr);
01232 
01233           htmlColor.Append(PRUnichar(')'));
01234           aIsSet = htmlColor.Equals(valueString,
01235                                     nsCaseInsensitiveStringComparator());
01236         }
01237         else
01238           aIsSet = htmlValueString.Equals(valueString,
01239                                     nsCaseInsensitiveStringComparator());
01240       }
01241     }
01242 
01243     else if (nsEditProperty::tt == aHTMLProperty) {
01244       aIsSet = StringBeginsWith(valueString, NS_LITERAL_STRING("monospace"));
01245     }
01246     
01247     else if ((nsEditProperty::font == aHTMLProperty) && aHTMLAttribute
01248              && aHTMLAttribute->EqualsLiteral("face")) {
01249       if (!htmlValueString.IsEmpty()) {
01250         const PRUnichar commaSpace[] = { PRUnichar(','), PRUnichar(' '), 0 };
01251         const PRUnichar comma[] = { PRUnichar(','), 0 };
01252         htmlValueString.ReplaceSubstring(commaSpace, comma);
01253         nsAutoString valueStringNorm(valueString);
01254         valueStringNorm.ReplaceSubstring(commaSpace, comma);
01255         aIsSet = htmlValueString.Equals(valueStringNorm,
01256                                         nsCaseInsensitiveStringComparator());
01257       }
01258       else {
01259         // ignore this, it's TT or our default
01260         nsAutoString valueStringLower;
01261         ToLowerCase(valueString, valueStringLower);
01262         aIsSet = !valueStringLower.EqualsLiteral("monospace") &&
01263                  !valueStringLower.EqualsLiteral("serif");
01264       }
01265       return NS_OK;
01266     }
01267     else if (aHTMLAttribute
01268              && aHTMLAttribute->EqualsLiteral("align")) {
01269       aIsSet = PR_TRUE;
01270     }
01271     else {
01272       aIsSet = PR_FALSE;
01273       return NS_OK;
01274     }
01275 
01276     if (!htmlValueString.IsEmpty()) {
01277       if (htmlValueString.Equals(valueString,
01278                                  nsCaseInsensitiveStringComparator())) {
01279         aIsSet = PR_TRUE;
01280       }
01281     }
01282 
01283     if (nsEditProperty::u == aHTMLProperty || nsEditProperty::strike == aHTMLProperty) {
01284       // unfortunately, the value of the text-decoration property is not inherited.
01285       // that means that we have to look at ancestors of node to see if they are underlined
01286       nsCOMPtr<nsIDOMNode> tmp;
01287       res = node->GetParentNode(getter_AddRefs(tmp));
01288       if (NS_FAILED(res)) return res;
01289       nsCOMPtr<nsIDOMElement> element = do_QueryInterface(tmp);
01290       node = element;  // set to null if it's not a dom element
01291     }
01292   } while ((nsEditProperty::u == aHTMLProperty || nsEditProperty::strike == aHTMLProperty) &&
01293            !aIsSet && node);
01294   return NS_OK;
01295 }
01296 
01297 nsresult
01298 nsHTMLCSSUtils::SetCSSEnabled(PRBool aIsCSSPrefChecked)
01299 {
01300   mIsCSSPrefChecked = aIsCSSPrefChecked;
01301   return NS_OK;
01302 }
01303 
01304 PRBool
01305 nsHTMLCSSUtils::IsCSSPrefChecked()
01306 {
01307   return mIsCSSPrefChecked ;
01308 }
01309 
01310 // ElementsSameStyle compares two elements and checks if they have the same
01311 // specified CSS declarations in the STYLE attribute 
01312 // The answer is always negative if at least one of them carries an ID or a class
01313 PRBool
01314 nsHTMLCSSUtils::ElementsSameStyle(nsIDOMNode *aFirstNode, nsIDOMNode *aSecondNode)
01315 {
01316   nsresult res;
01317   nsCOMPtr<nsIDOMElement> firstElement  = do_QueryInterface(aFirstNode);
01318   nsCOMPtr<nsIDOMElement> secondElement = do_QueryInterface(aSecondNode);
01319 
01320   NS_ASSERTION((firstElement && secondElement), "Non element nodes passed to ElementsSameStyle.");
01321 
01322   nsAutoString firstID, secondID;
01323   PRBool isFirstIDSet, isSecondIDSet;
01324   res = mHTMLEditor->GetAttributeValue(firstElement,  NS_LITERAL_STRING("id"), firstID,  &isFirstIDSet);
01325   res = mHTMLEditor->GetAttributeValue(secondElement, NS_LITERAL_STRING("id"), secondID, &isSecondIDSet);
01326   if (isFirstIDSet || isSecondIDSet) {
01327     // at least one of the spans carries an ID ; suspect a CSS rule applies to it and
01328     // refuse to merge the nodes
01329     return PR_FALSE;
01330   }
01331 
01332   nsAutoString firstClass, secondClass;
01333   PRBool isFirstClassSet, isSecondClassSet;
01334   res = mHTMLEditor->GetAttributeValue(firstElement,  NS_LITERAL_STRING("class"), firstClass,  &isFirstClassSet);
01335   res = mHTMLEditor->GetAttributeValue(secondElement, NS_LITERAL_STRING("class"), secondClass, &isSecondClassSet);
01336   if (isFirstClassSet && isSecondClassSet) {
01337     // both spans carry a class, let's compare them
01338     if (!firstClass.Equals(secondClass)) {
01339       // WARNING : technically, the comparison just above is questionable :
01340       // from a pure HTML/CSS point of view class="a b" is NOT the same than
01341       // class="b a" because a CSS rule could test the exact value of the class
01342       // attribute to be "a b" for instance ; from a user's point of view, a
01343       // wysiwyg editor should probably NOT make any difference. CSS people
01344       // need to discuss this issue before any modification.
01345       return PR_FALSE;
01346     }
01347   }
01348   else if (isFirstClassSet || isSecondClassSet) {
01349     // one span only carries a class, early way out
01350     return PR_FALSE;
01351   }
01352 
01353   nsCOMPtr<nsIDOMCSSStyleDeclaration> firstCSSDecl, secondCSSDecl;
01354   PRUint32 firstLength, secondLength;
01355   res = GetInlineStyles(firstElement,  getter_AddRefs(firstCSSDecl),  &firstLength);
01356   if (NS_FAILED(res) || !firstCSSDecl) return PR_FALSE;
01357   res = GetInlineStyles(secondElement, getter_AddRefs(secondCSSDecl), &secondLength);
01358   if (NS_FAILED(res) || !secondCSSDecl) return PR_FALSE;
01359 
01360   if (firstLength != secondLength) {
01361     // early way out if we can
01362     return PR_FALSE;
01363   }
01364   else if (0 == firstLength) {
01365     // no inline style !
01366     return PR_TRUE;
01367   }
01368 
01369   PRUint32 i;
01370   nsAutoString propertyNameString;
01371   nsAutoString firstValue, secondValue;
01372   for (i=0; i<firstLength; i++) {
01373     firstCSSDecl->Item(i, propertyNameString);
01374     firstCSSDecl->GetPropertyValue(propertyNameString, firstValue);
01375     secondCSSDecl->GetPropertyValue(propertyNameString, secondValue);
01376     if (!firstValue.Equals(secondValue)) {
01377       return PR_FALSE;
01378     }
01379   }
01380   for (i=0; i<secondLength; i++) {
01381     secondCSSDecl->Item(i, propertyNameString);
01382     secondCSSDecl->GetPropertyValue(propertyNameString, secondValue);
01383     firstCSSDecl->GetPropertyValue(propertyNameString, firstValue);
01384     if (!firstValue.Equals(secondValue)) {
01385       return PR_FALSE;
01386     }
01387   }
01388 
01389   return PR_TRUE;
01390 }
01391 
01392 nsresult
01393 nsHTMLCSSUtils::GetInlineStyles(nsIDOMElement *aElement,
01394                                 nsIDOMCSSStyleDeclaration **aCssDecl,
01395                                 PRUint32 *aLength)
01396 {
01397   if (!aElement || !aLength) return NS_ERROR_NULL_POINTER;
01398   *aLength = 0;
01399   nsCOMPtr<nsIDOMElementCSSInlineStyle> inlineStyles = do_QueryInterface(aElement);
01400   if (!inlineStyles) return NS_ERROR_NULL_POINTER;
01401   nsresult res = inlineStyles->GetStyle(aCssDecl);
01402   if (NS_FAILED(res) || !aCssDecl) return NS_ERROR_NULL_POINTER;
01403   (*aCssDecl)->GetLength(aLength);
01404   return NS_OK;
01405 }
01406 
01407 nsresult
01408 nsHTMLCSSUtils::GetElementContainerOrSelf(nsIDOMNode * aNode, nsIDOMElement ** aElement)
01409 {
01410   NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
01411 
01412   nsCOMPtr<nsIDOMNode> node=aNode, parentNode;
01413   PRUint16 type;
01414   nsresult res;
01415   res = node->GetNodeType(&type);
01416   if (NS_FAILED(res)) return res;
01417   // loop until we find an element
01418   while (node && nsIDOMNode::ELEMENT_NODE != type) {
01419     parentNode = node;
01420     res = parentNode->GetParentNode(getter_AddRefs(node));
01421     if (NS_FAILED(res)) return res;
01422     if (node) {
01423       res = node->GetNodeType(&type);
01424       if (NS_FAILED(res)) return res;
01425     }
01426   }
01427   NS_ASSERTION(node, "we reached a null node ancestor !");
01428   NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
01429   nsCOMPtr<nsIDOMElement> element = do_QueryInterface(node);
01430   (*aElement) = element;
01431   NS_IF_ADDREF(*aElement);
01432   return NS_OK;
01433 }
01434 
01435 nsresult
01436 nsHTMLCSSUtils::SetCSSProperty(nsIDOMElement * aElement,
01437                                const nsAString & aProperty,
01438                                const nsAString & aValue)
01439 {
01440   nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
01441   PRUint32 length;
01442   nsresult res = GetInlineStyles(aElement, getter_AddRefs(cssDecl), &length);
01443   if (NS_FAILED(res) || !cssDecl) return res;
01444 
01445   return cssDecl->SetProperty(aProperty,
01446                               aValue,
01447                               EmptyString());
01448 }
01449 
01450 nsresult
01451 nsHTMLCSSUtils::SetCSSPropertyPixels(nsIDOMElement * aElement,
01452                                      const nsAString & aProperty,
01453                                      PRInt32 aIntValue)
01454 {
01455   nsAutoString s;
01456   s.AppendInt(aIntValue);
01457   return SetCSSProperty(aElement, aProperty, s + NS_LITERAL_STRING("px"));
01458 }
01459 
01460 nsresult
01461 nsHTMLCSSUtils::RemoveCSSProperty(nsIDOMElement * aElement,
01462                                   const nsAString & aProperty)
01463 {
01464   nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
01465   PRUint32 length;
01466   nsresult res = GetInlineStyles(aElement, getter_AddRefs(cssDecl), &length);
01467   if (NS_FAILED(res) || !cssDecl) return res;
01468 
01469   nsAutoString returnString;
01470   return cssDecl->RemoveProperty(aProperty, returnString);
01471 }
01472