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
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.
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"
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
,
55 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mElement
)
56 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
58 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ChangeCSSInlineStyleTxn
,
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
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
86 while ((kNullCh
!= *end
) && (PR_FALSE
== nsCRT::IsAsciiSpace(*end
))) { // look for space or end
89 *end
= kNullCh
; // end string here
93 if (!nsCRT::strcmp(value
, start
)) {
99 if (nsDependentString(value
).Equals(nsDependentString(start
),
100 nsCaseInsensitiveStringComparator())) {
112 // removes the value aRemoveValue from the string list of white-space separated values aValueList
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
129 while ((kNullCh
!= *end
) && (PR_FALSE
== nsCRT::IsAsciiSpace(*end
))) { // look for space or end
132 *end
= kNullCh
; // end string here
135 if (!aRemoveValue
.Equals(start
)) {
136 outString
.Append(start
);
137 outString
.Append(PRUnichar(' '));
143 aValues
.Assign(outString
);
146 ChangeCSSInlineStyleTxn::ChangeCSSInlineStyleTxn()
151 NS_IMETHODIMP
ChangeCSSInlineStyleTxn::Init(nsIEditor
*aEditor
,
152 nsIDOMElement
*aElement
,
154 const nsAString
& aValue
,
155 PRBool aRemoveProperty
)
157 NS_ASSERTION(aEditor
&& aElement
, "bad arg");
158 if (!aEditor
|| !aElement
) { return NS_ERROR_NULL_POINTER
; }
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();
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
;
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
;
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
;
217 nsAutoString priority
;
218 result
= cssDecl
->GetPropertyPriority(propertyNameString
, priority
);
219 if (NS_FAILED(result
)) return result
;
220 result
= cssDecl
->SetProperty(propertyNameString
, values
,
222 if (NS_FAILED(result
)) return result
;
226 result
= cssDecl
->RemoveProperty(propertyNameString
, returnString
);
227 if (NS_FAILED(result
)) return result
;
231 nsAutoString priority
;
232 result
= cssDecl
->GetPropertyPriority(propertyNameString
, priority
);
233 if (NS_FAILED(result
)) return result
;
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
);
243 values
.Assign(mValue
);
244 result
= cssDecl
->SetProperty(propertyNameString
, values
,
246 if (NS_FAILED(result
)) return result
;
249 // let's be sure we don't keep an empty style attribute
251 result
= cssDecl
->GetLength(&length
);
252 if (NS_FAILED(result
)) return result
;
254 result
= mElement
->RemoveAttribute(styleAttr
);
255 if (NS_FAILED(result
)) return result
;
258 mRedoAttributeWasSet
= PR_TRUE
;
260 return cssDecl
->GetPropertyValue(propertyNameString
, mRedoValue
);
263 nsresult
ChangeCSSInlineStyleTxn::SetStyle(PRBool aAttributeWasSet
,
266 NS_ASSERTION(mEditor
&& mElement
, "bad state");
267 if (!mEditor
|| !mElement
) { return NS_ERROR_NOT_INITIALIZED
; }
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
);
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
);
296 result
= mElement
->RemoveAttribute(NS_LITERAL_STRING("style"));
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] ");
318 aString
.AppendLiteral("true] ");
319 nsAutoString tempString
;
320 mProperty
->ToString(tempString
);
321 aString
+= tempString
;
325 // answers true if the CSS property accepts more than one value
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
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
);