Bug 570258: Some more atom usage cleanup. r=jst
[mozilla-central.git] / editor / libeditor / base / ChangeCSSInlineStyleTxn.cpp
blob91ae1c9a5322d476f1ae9f488e9acb69d39c0295
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Original Author: Daniel Glazman <glazman@netscape.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "ChangeCSSInlineStyleTxn.h"
40 #include "nsIDOMElement.h"
41 #include "nsIDOMCSSStyleDeclaration.h"
42 #include "nsIDOMElementCSSInlineStyle.h"
43 #include "nsReadableUtils.h"
44 #include "nsUnicharUtils.h"
45 #include "nsCRT.h"
46 #include "nsIAtom.h"
47 #include "nsGkAtoms.h"
49 #define kNullCh (PRUnichar('\0'))
51 NS_IMPL_CYCLE_COLLECTION_CLASS(ChangeCSSInlineStyleTxn)
53 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ChangeCSSInlineStyleTxn,
54 EditTxn)
55 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mElement)
56 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
58 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ChangeCSSInlineStyleTxn,
59 EditTxn)
60 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mElement)
61 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
63 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ChangeCSSInlineStyleTxn)
64 NS_INTERFACE_MAP_END_INHERITING(EditTxn)
66 // answers true if aValue is in the string list of white-space separated values aValueList
67 // a case-sensitive search is performed if aCaseSensitive is true
68 PRBool
69 ChangeCSSInlineStyleTxn::ValueIncludes(const nsAString &aValueList, const nsAString &aValue, PRBool aCaseSensitive)
71 nsAutoString valueList(aValueList);
72 PRBool result = PR_FALSE;
74 valueList.Append(kNullCh); // put an extra null at the end
76 PRUnichar *value = ToNewUnicode(aValue);
77 PRUnichar *start = valueList.BeginWriting();
78 PRUnichar *end = start;
80 while (kNullCh != *start) {
81 while ((kNullCh != *start) && nsCRT::IsAsciiSpace(*start)) { // skip leading space
82 start++;
84 end = start;
86 while ((kNullCh != *end) && (PR_FALSE == nsCRT::IsAsciiSpace(*end))) { // look for space or end
87 end++;
89 *end = kNullCh; // end string here
91 if (start < end) {
92 if (aCaseSensitive) {
93 if (!nsCRT::strcmp(value, start)) {
94 result = PR_TRUE;
95 break;
98 else {
99 if (nsDependentString(value).Equals(nsDependentString(start),
100 nsCaseInsensitiveStringComparator())) {
101 result = PR_TRUE;
102 break;
106 start = ++end;
108 NS_Free(value);
109 return result;
112 // removes the value aRemoveValue from the string list of white-space separated values aValueList
113 void
114 ChangeCSSInlineStyleTxn::RemoveValueFromListOfValues(nsAString & aValues, const nsAString & aRemoveValue)
116 nsAutoString classStr(aValues); // copy to work buffer nsAutoString rv(aRemoveValue);
117 nsAutoString outString;
118 classStr.Append(kNullCh); // put an extra null at the end
120 PRUnichar *start = classStr.BeginWriting();
121 PRUnichar *end = start;
123 while (kNullCh != *start) {
124 while ((kNullCh != *start) && nsCRT::IsAsciiSpace(*start)) { // skip leading space
125 start++;
127 end = start;
129 while ((kNullCh != *end) && (PR_FALSE == nsCRT::IsAsciiSpace(*end))) { // look for space or end
130 end++;
132 *end = kNullCh; // end string here
134 if (start < end) {
135 if (!aRemoveValue.Equals(start)) {
136 outString.Append(start);
137 outString.Append(PRUnichar(' '));
141 start = ++end;
143 aValues.Assign(outString);
146 ChangeCSSInlineStyleTxn::ChangeCSSInlineStyleTxn()
147 : EditTxn()
151 NS_IMETHODIMP ChangeCSSInlineStyleTxn::Init(nsIEditor *aEditor,
152 nsIDOMElement *aElement,
153 nsIAtom *aProperty,
154 const nsAString& aValue,
155 PRBool aRemoveProperty)
157 NS_ASSERTION(aEditor && aElement, "bad arg");
158 if (!aEditor || !aElement) { return NS_ERROR_NULL_POINTER; }
160 mEditor = aEditor;
161 mElement = do_QueryInterface(aElement);
162 mProperty = aProperty;
163 NS_ADDREF(mProperty);
164 mValue.Assign(aValue);
165 mRemoveProperty = aRemoveProperty;
166 mUndoAttributeWasSet = PR_FALSE;
167 mRedoAttributeWasSet = PR_FALSE;
168 mUndoValue.Truncate();
169 mRedoValue.Truncate();
170 return NS_OK;
173 NS_IMETHODIMP ChangeCSSInlineStyleTxn::DoTransaction(void)
175 NS_ASSERTION(mEditor && mElement, "bad state");
176 if (!mEditor || !mElement) { return NS_ERROR_NOT_INITIALIZED; }
178 nsCOMPtr<nsIDOMElementCSSInlineStyle> inlineStyles = do_QueryInterface(mElement);
179 if (!inlineStyles) return NS_ERROR_NULL_POINTER;
181 nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
182 nsresult result = inlineStyles->GetStyle(getter_AddRefs(cssDecl));
183 if (NS_FAILED(result)) return result;
184 if (!cssDecl) return NS_ERROR_NULL_POINTER;
186 nsAutoString propertyNameString;
187 mProperty->ToString(propertyNameString);
189 NS_NAMED_LITERAL_STRING(styleAttr, "style");
190 result = mElement->HasAttribute(styleAttr, &mUndoAttributeWasSet);
191 if (NS_FAILED(result)) return result;
193 nsAutoString values;
194 result = cssDecl->GetPropertyValue(propertyNameString, values);
195 if (NS_FAILED(result)) return result;
196 mUndoValue.Assign(values);
198 // does this property accept more than 1 value ?
199 // we need to know that because of bug 62682
200 PRBool multiple = AcceptsMoreThanOneValue(mProperty);
202 if (mRemoveProperty) {
203 nsAutoString returnString;
204 if (multiple) {
205 // the property can have more than one value, let's remove only
206 // the value we have to remove and not the others
208 // the 2 lines below are a workaround because nsDOMCSSDeclaration::GetPropertyCSSValue
209 // is not yet implemented (bug 62682)
210 RemoveValueFromListOfValues(values, NS_LITERAL_STRING("none"));
211 RemoveValueFromListOfValues(values, mValue);
212 if (values.IsEmpty()) {
213 result = cssDecl->RemoveProperty(propertyNameString, returnString);
214 if (NS_FAILED(result)) return result;
216 else {
217 nsAutoString priority;
218 result = cssDecl->GetPropertyPriority(propertyNameString, priority);
219 if (NS_FAILED(result)) return result;
220 result = cssDecl->SetProperty(propertyNameString, values,
221 priority);
222 if (NS_FAILED(result)) return result;
225 else {
226 result = cssDecl->RemoveProperty(propertyNameString, returnString);
227 if (NS_FAILED(result)) return result;
230 else {
231 nsAutoString priority;
232 result = cssDecl->GetPropertyPriority(propertyNameString, priority);
233 if (NS_FAILED(result)) return result;
234 if (multiple) {
235 // the property can have more than one value, let's add
236 // the value we have to add to the others
238 // the line below is a workaround because nsDOMCSSDeclaration::GetPropertyCSSValue
239 // is not yet implemented (bug 62682)
240 AddValueToMultivalueProperty(values, mValue);
242 else
243 values.Assign(mValue);
244 result = cssDecl->SetProperty(propertyNameString, values,
245 priority);
246 if (NS_FAILED(result)) return result;
249 // let's be sure we don't keep an empty style attribute
250 PRUint32 length;
251 result = cssDecl->GetLength(&length);
252 if (NS_FAILED(result)) return result;
253 if (!length) {
254 result = mElement->RemoveAttribute(styleAttr);
255 if (NS_FAILED(result)) return result;
257 else
258 mRedoAttributeWasSet = PR_TRUE;
260 return cssDecl->GetPropertyValue(propertyNameString, mRedoValue);
263 nsresult ChangeCSSInlineStyleTxn::SetStyle(PRBool aAttributeWasSet,
264 nsAString & aValue)
266 NS_ASSERTION(mEditor && mElement, "bad state");
267 if (!mEditor || !mElement) { return NS_ERROR_NOT_INITIALIZED; }
269 nsresult result;
270 if (aAttributeWasSet) {
271 // the style attribute was set and not empty, let's recreate the declaration
272 nsAutoString propertyNameString;
273 mProperty->ToString(propertyNameString);
275 nsCOMPtr<nsIDOMElementCSSInlineStyle> inlineStyles = do_QueryInterface(mElement);
276 if (!inlineStyles) return NS_ERROR_NULL_POINTER;
277 nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
278 result = inlineStyles->GetStyle(getter_AddRefs(cssDecl));
279 if (NS_FAILED(result)) return result;
280 if (!cssDecl) return NS_ERROR_NULL_POINTER;
282 if (aValue.IsEmpty()) {
283 // an empty value means we have to remove the property
284 nsAutoString returnString;
285 result = cssDecl->RemoveProperty(propertyNameString, returnString);
287 else {
288 // let's recreate the declaration as it was
289 nsAutoString priority;
290 result = cssDecl->GetPropertyPriority(propertyNameString, priority);
291 if (NS_FAILED(result)) return result;
292 result = cssDecl->SetProperty(propertyNameString, aValue, priority);
295 else
296 result = mElement->RemoveAttribute(NS_LITERAL_STRING("style"));
298 return result;
301 NS_IMETHODIMP ChangeCSSInlineStyleTxn::UndoTransaction(void)
303 return SetStyle(mUndoAttributeWasSet, mUndoValue);
306 NS_IMETHODIMP ChangeCSSInlineStyleTxn::RedoTransaction(void)
308 return SetStyle(mRedoAttributeWasSet, mRedoValue);
311 NS_IMETHODIMP ChangeCSSInlineStyleTxn::GetTxnDescription(nsAString& aString)
313 aString.AssignLiteral("ChangeCSSInlineStyleTxn: [mRemoveProperty == ");
315 if (!mRemoveProperty)
316 aString.AppendLiteral("false] ");
317 else
318 aString.AppendLiteral("true] ");
319 nsAutoString tempString;
320 mProperty->ToString(tempString);
321 aString += tempString;
322 return NS_OK;
325 // answers true if the CSS property accepts more than one value
326 PRBool
327 ChangeCSSInlineStyleTxn::AcceptsMoreThanOneValue(nsIAtom *aCSSProperty)
329 return aCSSProperty == nsGkAtoms::text_decoration;
332 // adds the value aNewValue to the list of white-space separated values aValues
333 NS_IMETHODIMP
334 ChangeCSSInlineStyleTxn::AddValueToMultivalueProperty(nsAString & aValues, const nsAString & aNewValue)
336 if (aValues.IsEmpty()
337 || aValues.LowerCaseEqualsLiteral("none")) {
338 // the list of values is empty of the value is 'none'
339 aValues.Assign(aNewValue);
341 else if (!ValueIncludes(aValues, aNewValue, PR_FALSE)) {
342 // we already have another value but not this one; add it
343 aValues.Append(PRUnichar(' '));
344 aValues.Append(aNewValue);
346 return NS_OK;