Back to index

lightning-sunbird  0.9+nobinonly
ChangeCSSInlineStyleTxn.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 "ChangeCSSInlineStyleTxn.h"
00040 #include "nsIDOMElement.h"
00041 #include "nsIDOMCSSStyleDeclaration.h"
00042 #include "nsIDOMElementCSSInlineStyle.h"
00043 #include "nsReadableUtils.h"
00044 #include "nsUnicharUtils.h"
00045 #include "nsCRT.h"
00046 #include "nsIAtom.h"
00047 
00048 #define kNullCh (PRUnichar('\0'))
00049 
00050 void
00051 ChangeCSSInlineStyleTxn::AppendDeclaration(nsAString & aOutputString,
00052                                            const nsAString & aProperty,
00053                                            const nsAString & aValues)
00054 {
00055   aOutputString.Append(aProperty
00056                        + NS_LITERAL_STRING(": ")
00057                        + aValues
00058                        + NS_LITERAL_STRING("; "));
00059 }
00060 
00061 // answers true if aValue is in the string list of white-space separated values aValueList
00062 // a case-sensitive search is performed if aCaseSensitive is true
00063 PRBool
00064 ChangeCSSInlineStyleTxn::ValueIncludes(const nsAString &aValueList, const nsAString &aValue, PRBool aCaseSensitive)
00065 {
00066   nsAutoString  valueList(aValueList);
00067   PRBool result = PR_FALSE;
00068 
00069   valueList.Append(kNullCh);  // put an extra null at the end
00070 
00071   PRUnichar *value = ToNewUnicode(aValue);
00072   PRUnichar *start = valueList.BeginWriting();
00073   PRUnichar *end   = start;
00074 
00075   while (kNullCh != *start) {
00076     while ((kNullCh != *start) && nsCRT::IsAsciiSpace(*start)) {  // skip leading space
00077       start++;
00078     }
00079     end = start;
00080 
00081     while ((kNullCh != *end) && (PR_FALSE == nsCRT::IsAsciiSpace(*end))) { // look for space or end
00082       end++;
00083     }
00084     *end = kNullCh; // end string here
00085 
00086     if (start < end) {
00087       if (aCaseSensitive) {
00088         if (!nsCRT::strcmp(value, start)) {
00089           result = PR_TRUE;
00090           break;
00091         }
00092       }
00093       else {
00094         if (nsDependentString(value).Equals(nsDependentString(start),
00095                                             nsCaseInsensitiveStringComparator())) {
00096           result = PR_TRUE;
00097           break;
00098         }
00099       }
00100     }
00101     start = ++end;
00102   }
00103   NS_Free(value);
00104   return result;
00105 }
00106 
00107 // removes the value aRemoveValue from the string list of white-space separated values aValueList
00108 void
00109 ChangeCSSInlineStyleTxn::RemoveValueFromListOfValues(nsAString & aValues, const nsAString  & aRemoveValue)
00110 {
00111   nsAutoString  classStr(aValues);  // copy to work buffer   nsAutoString  rv(aRemoveValue);
00112   nsAutoString  outString;
00113   classStr.Append(kNullCh);  // put an extra null at the end
00114 
00115   PRUnichar *start = classStr.BeginWriting();
00116   PRUnichar *end   = start;
00117 
00118   while (kNullCh != *start) {
00119     while ((kNullCh != *start) && nsCRT::IsAsciiSpace(*start)) {  // skip leading space
00120       start++;
00121     }
00122     end = start;
00123 
00124     while ((kNullCh != *end) && (PR_FALSE == nsCRT::IsAsciiSpace(*end))) { // look for space or end
00125       end++;
00126     }
00127     *end = kNullCh; // end string here
00128 
00129     if (start < end) {
00130       if (!aRemoveValue.Equals(start)) {
00131         outString.Append(start);
00132         outString.Append(PRUnichar(' '));
00133       }
00134     }
00135 
00136     start = ++end;
00137   }
00138   aValues.Assign(outString);
00139 }
00140 
00141 ChangeCSSInlineStyleTxn::ChangeCSSInlineStyleTxn()
00142   : EditTxn()
00143 {
00144 }
00145 
00146 ChangeCSSInlineStyleTxn::~ChangeCSSInlineStyleTxn()
00147 {
00148 }
00149 
00150 NS_IMETHODIMP ChangeCSSInlineStyleTxn::Init(nsIEditor      *aEditor,
00151                                             nsIDOMElement  *aElement,
00152                                             nsIAtom        *aProperty,
00153                                             const nsAString& aValue,
00154                                             PRBool aRemoveProperty)
00155 {
00156   NS_ASSERTION(aEditor && aElement, "bad arg");
00157   if (!aEditor || !aElement) { return NS_ERROR_NULL_POINTER; }
00158 
00159   mEditor = aEditor;
00160   mElement = do_QueryInterface(aElement);
00161   mProperty = aProperty;
00162   NS_ADDREF(mProperty);
00163   mValue.Assign(aValue);
00164   mRemoveProperty = aRemoveProperty;
00165   mUndoAttributeWasSet = PR_FALSE;
00166   mRedoAttributeWasSet = PR_FALSE;
00167   mUndoValue.Truncate();
00168   mRedoValue.Truncate();
00169   return NS_OK;
00170 }
00171 
00172 NS_IMETHODIMP ChangeCSSInlineStyleTxn::DoTransaction(void)
00173 {
00174   NS_ASSERTION(mEditor && mElement, "bad state");
00175   if (!mEditor || !mElement) { return NS_ERROR_NOT_INITIALIZED; }
00176 
00177   nsCOMPtr<nsIDOMElementCSSInlineStyle> inlineStyles = do_QueryInterface(mElement);
00178   if (!inlineStyles) return NS_ERROR_NULL_POINTER;
00179 
00180   nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
00181   nsresult result = inlineStyles->GetStyle(getter_AddRefs(cssDecl));
00182   if (NS_FAILED(result)) return result;
00183   if (!cssDecl) return NS_ERROR_NULL_POINTER;
00184 
00185   nsAutoString propertyNameString;
00186   mProperty->ToString(propertyNameString);
00187 
00188   NS_NAMED_LITERAL_STRING(styleAttr, "style");
00189   result = mElement->HasAttribute(styleAttr, &mUndoAttributeWasSet);
00190   if (NS_FAILED(result)) return result;
00191 
00192   nsAutoString values;
00193   result = cssDecl->GetPropertyValue(propertyNameString, values);
00194   if (NS_FAILED(result)) return result;     
00195   mUndoValue.Assign(values);
00196 
00197   // does this property accept more than 1 value ?
00198   // we need to know that because of bug 62682
00199   PRBool multiple = AcceptsMoreThanOneValue(mProperty);
00200   
00201   if (mRemoveProperty) {
00202     nsAutoString returnString;
00203     if (multiple) {
00204       // the property can have more than one value, let's remove only
00205       // the value we have to remove and not the others
00206 
00207       // the 2 lines below are a workaround because nsDOMCSSDeclaration::GetPropertyCSSValue
00208       // is not yet implemented (bug 62682)
00209       RemoveValueFromListOfValues(values, NS_LITERAL_STRING("none"));
00210       RemoveValueFromListOfValues(values, mValue);
00211       if (values.IsEmpty()) {
00212         result = cssDecl->RemoveProperty(propertyNameString, returnString);
00213         if (NS_FAILED(result)) return result;     
00214       }
00215       else {
00216         nsAutoString priority;
00217         result = cssDecl->GetPropertyPriority(propertyNameString, priority);
00218         if (NS_FAILED(result)) return result;     
00219         result = cssDecl->SetProperty(propertyNameString, values,
00220                                       priority);
00221         if (NS_FAILED(result)) return result;     
00222       }
00223     }
00224     else {
00225       result = cssDecl->RemoveProperty(propertyNameString, returnString);
00226       if (NS_FAILED(result)) return result;     
00227     }
00228   }
00229   else {
00230     nsAutoString priority;
00231     result = cssDecl->GetPropertyPriority(propertyNameString, priority);
00232     if (NS_FAILED(result)) return result;
00233     if (multiple) {
00234       // the property can have more than one value, let's add
00235       // the value we have to add to the others
00236 
00237       // the line below is a workaround because nsDOMCSSDeclaration::GetPropertyCSSValue
00238       // is not yet implemented (bug 62682)
00239       AddValueToMultivalueProperty(values, mValue);
00240     }
00241     else
00242       values.Assign(mValue);
00243     result = cssDecl->SetProperty(propertyNameString, values,
00244                                   priority);
00245     if (NS_FAILED(result)) return result;     
00246   }
00247 
00248   // let's be sure we don't keep an empty style attribute
00249   PRUint32 length;
00250   result = cssDecl->GetLength(&length);
00251   if (NS_FAILED(result)) return result;     
00252   if (!length) {
00253     result = mElement->RemoveAttribute(styleAttr);
00254     if (NS_FAILED(result)) return result;     
00255   }
00256   else
00257     mRedoAttributeWasSet = PR_TRUE;
00258 
00259   return cssDecl->GetPropertyValue(propertyNameString, mRedoValue);
00260 }
00261 
00262 nsresult ChangeCSSInlineStyleTxn::SetStyle(PRBool aAttributeWasSet,
00263                                            nsAString & aValue)
00264 {
00265   NS_ASSERTION(mEditor && mElement, "bad state");
00266   if (!mEditor || !mElement) { return NS_ERROR_NOT_INITIALIZED; }
00267 
00268   nsresult result;
00269   if (aAttributeWasSet) {
00270     // the style attribute was set and not empty, let's recreate the declaration
00271     nsAutoString propertyNameString;
00272     mProperty->ToString(propertyNameString);
00273 
00274     nsCOMPtr<nsIDOMElementCSSInlineStyle> inlineStyles = do_QueryInterface(mElement);
00275     if (!inlineStyles) return NS_ERROR_NULL_POINTER;
00276     nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
00277     result = inlineStyles->GetStyle(getter_AddRefs(cssDecl));
00278     if (NS_FAILED(result)) return result;
00279     if (!cssDecl) return NS_ERROR_NULL_POINTER;
00280 
00281     if (aValue.IsEmpty()) {
00282       // an empty value means we have to remove the property
00283       nsAutoString returnString;
00284       result = cssDecl->RemoveProperty(propertyNameString, returnString);
00285     }
00286     else {
00287       // let's recreate the declaration as it was
00288       nsAutoString priority;
00289       result = cssDecl->GetPropertyPriority(propertyNameString, priority);
00290       if (NS_FAILED(result)) return result;
00291       result = cssDecl->SetProperty(propertyNameString, aValue, priority);
00292     }
00293   }
00294   else
00295     result = mElement->RemoveAttribute(NS_LITERAL_STRING("style"));
00296 
00297   return result;
00298 }
00299 
00300 NS_IMETHODIMP ChangeCSSInlineStyleTxn::UndoTransaction(void)
00301 {
00302   return SetStyle(mUndoAttributeWasSet, mUndoValue);
00303 }
00304 
00305 NS_IMETHODIMP ChangeCSSInlineStyleTxn::RedoTransaction(void)
00306 {
00307   return SetStyle(mRedoAttributeWasSet, mRedoValue);
00308 }
00309 
00310 NS_IMETHODIMP ChangeCSSInlineStyleTxn::Merge(nsITransaction *aTransaction, PRBool *aDidMerge)
00311 {
00312   if (aDidMerge)
00313     *aDidMerge = PR_FALSE;
00314   return NS_OK;
00315 }
00316 
00317 NS_IMETHODIMP ChangeCSSInlineStyleTxn::GetTxnDescription(nsAString& aString)
00318 {
00319   aString.AssignLiteral("ChangeCSSInlineStyleTxn: [mRemoveProperty == ");
00320 
00321   if (!mRemoveProperty)
00322     aString.AppendLiteral("false] ");
00323   else
00324     aString.AppendLiteral("true] ");
00325   nsAutoString tempString;
00326   mProperty->ToString(tempString);
00327   aString += tempString;
00328   return NS_OK;
00329 }
00330 
00331 // answers true if the CSS property accepts more than one value
00332 PRBool
00333 ChangeCSSInlineStyleTxn::AcceptsMoreThanOneValue(nsIAtom *aCSSProperty)
00334 {
00335   nsIAtom * textDecorationAtom = NS_NewAtom("text-decoration");
00336   PRBool res = (textDecorationAtom == aCSSProperty);
00337   NS_IF_RELEASE(textDecorationAtom);
00338   return res;
00339 }
00340 
00341 // adds the value aNewValue to the list of white-space separated values aValues
00342 NS_IMETHODIMP
00343 ChangeCSSInlineStyleTxn::AddValueToMultivalueProperty(nsAString & aValues, const nsAString & aNewValue)
00344 {
00345   if (aValues.IsEmpty()
00346       || aValues.LowerCaseEqualsLiteral("none")) {
00347     // the list of values is empty of the value is 'none'
00348     aValues.Assign(aNewValue);
00349   }
00350   else if (!ValueIncludes(aValues, aNewValue, PR_FALSE)) {
00351     // we already have another value but not this one; add it
00352     aValues.Append(PRUnichar(' '));
00353     aValues.Append(aNewValue);
00354   }
00355   return NS_OK;
00356 }