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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
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.
21 : *
22 : * Contributor(s):
23 : * Original Author: Daniel Glazman <glazman@netscape.com>
24 : *
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.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
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"
48 :
49 : #define kNullCh (PRUnichar('\0'))
50 :
51 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(ChangeCSSInlineStyleTxn)
52 :
53 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ChangeCSSInlineStyleTxn,
54 : EditTxn)
55 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mElement)
56 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
57 :
58 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ChangeCSSInlineStyleTxn,
59 : EditTxn)
60 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mElement)
61 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
62 :
63 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ChangeCSSInlineStyleTxn)
64 0 : NS_INTERFACE_MAP_END_INHERITING(EditTxn)
65 :
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 : bool
69 0 : ChangeCSSInlineStyleTxn::ValueIncludes(const nsAString &aValueList, const nsAString &aValue, bool aCaseSensitive)
70 : {
71 0 : nsAutoString valueList(aValueList);
72 0 : bool result = false;
73 :
74 0 : valueList.Append(kNullCh); // put an extra null at the end
75 :
76 0 : PRUnichar *value = ToNewUnicode(aValue);
77 0 : PRUnichar *start = valueList.BeginWriting();
78 0 : PRUnichar *end = start;
79 :
80 0 : while (kNullCh != *start) {
81 0 : while ((kNullCh != *start) && nsCRT::IsAsciiSpace(*start)) { // skip leading space
82 0 : start++;
83 : }
84 0 : end = start;
85 :
86 0 : while ((kNullCh != *end) && (false == nsCRT::IsAsciiSpace(*end))) { // look for space or end
87 0 : end++;
88 : }
89 0 : *end = kNullCh; // end string here
90 :
91 0 : if (start < end) {
92 0 : if (aCaseSensitive) {
93 0 : if (!nsCRT::strcmp(value, start)) {
94 0 : result = true;
95 0 : break;
96 : }
97 : }
98 : else {
99 0 : if (nsDependentString(value).Equals(nsDependentString(start),
100 0 : nsCaseInsensitiveStringComparator())) {
101 0 : result = true;
102 0 : break;
103 : }
104 : }
105 : }
106 0 : start = ++end;
107 : }
108 0 : NS_Free(value);
109 0 : return result;
110 : }
111 :
112 : // removes the value aRemoveValue from the string list of white-space separated values aValueList
113 : void
114 0 : ChangeCSSInlineStyleTxn::RemoveValueFromListOfValues(nsAString & aValues, const nsAString & aRemoveValue)
115 : {
116 0 : nsAutoString classStr(aValues); // copy to work buffer nsAutoString rv(aRemoveValue);
117 0 : nsAutoString outString;
118 0 : classStr.Append(kNullCh); // put an extra null at the end
119 :
120 0 : PRUnichar *start = classStr.BeginWriting();
121 0 : PRUnichar *end = start;
122 :
123 0 : while (kNullCh != *start) {
124 0 : while ((kNullCh != *start) && nsCRT::IsAsciiSpace(*start)) { // skip leading space
125 0 : start++;
126 : }
127 0 : end = start;
128 :
129 0 : while ((kNullCh != *end) && (false == nsCRT::IsAsciiSpace(*end))) { // look for space or end
130 0 : end++;
131 : }
132 0 : *end = kNullCh; // end string here
133 :
134 0 : if (start < end) {
135 0 : if (!aRemoveValue.Equals(start)) {
136 0 : outString.Append(start);
137 0 : outString.Append(PRUnichar(' '));
138 : }
139 : }
140 :
141 0 : start = ++end;
142 : }
143 0 : aValues.Assign(outString);
144 0 : }
145 :
146 0 : ChangeCSSInlineStyleTxn::ChangeCSSInlineStyleTxn()
147 0 : : EditTxn()
148 : {
149 0 : }
150 :
151 0 : NS_IMETHODIMP ChangeCSSInlineStyleTxn::Init(nsIEditor *aEditor,
152 : nsIDOMElement *aElement,
153 : nsIAtom *aProperty,
154 : const nsAString& aValue,
155 : bool aRemoveProperty)
156 : {
157 0 : NS_ASSERTION(aEditor && aElement, "bad arg");
158 0 : if (!aEditor || !aElement) { return NS_ERROR_NULL_POINTER; }
159 :
160 0 : mEditor = aEditor;
161 0 : mElement = do_QueryInterface(aElement);
162 0 : mProperty = aProperty;
163 0 : NS_ADDREF(mProperty);
164 0 : mValue.Assign(aValue);
165 0 : mRemoveProperty = aRemoveProperty;
166 0 : mUndoAttributeWasSet = false;
167 0 : mRedoAttributeWasSet = false;
168 0 : mUndoValue.Truncate();
169 0 : mRedoValue.Truncate();
170 0 : return NS_OK;
171 : }
172 :
173 0 : NS_IMETHODIMP ChangeCSSInlineStyleTxn::DoTransaction(void)
174 : {
175 0 : NS_ASSERTION(mEditor && mElement, "bad state");
176 0 : if (!mEditor || !mElement) { return NS_ERROR_NOT_INITIALIZED; }
177 :
178 0 : nsCOMPtr<nsIDOMElementCSSInlineStyle> inlineStyles = do_QueryInterface(mElement);
179 0 : NS_ENSURE_TRUE(inlineStyles, NS_ERROR_NULL_POINTER);
180 :
181 0 : nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
182 0 : nsresult result = inlineStyles->GetStyle(getter_AddRefs(cssDecl));
183 0 : NS_ENSURE_SUCCESS(result, result);
184 0 : NS_ENSURE_TRUE(cssDecl, NS_ERROR_NULL_POINTER);
185 :
186 0 : nsAutoString propertyNameString;
187 0 : mProperty->ToString(propertyNameString);
188 :
189 0 : NS_NAMED_LITERAL_STRING(styleAttr, "style");
190 0 : result = mElement->HasAttribute(styleAttr, &mUndoAttributeWasSet);
191 0 : NS_ENSURE_SUCCESS(result, result);
192 :
193 0 : nsAutoString values;
194 0 : result = cssDecl->GetPropertyValue(propertyNameString, values);
195 0 : NS_ENSURE_SUCCESS(result, result);
196 0 : mUndoValue.Assign(values);
197 :
198 : // does this property accept more than 1 value ?
199 : // we need to know that because of bug 62682
200 0 : bool multiple = AcceptsMoreThanOneValue(mProperty);
201 :
202 0 : if (mRemoveProperty) {
203 0 : nsAutoString returnString;
204 0 : 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
207 :
208 : // the 2 lines below are a workaround because nsDOMCSSDeclaration::GetPropertyCSSValue
209 : // is not yet implemented (bug 62682)
210 0 : RemoveValueFromListOfValues(values, NS_LITERAL_STRING("none"));
211 0 : RemoveValueFromListOfValues(values, mValue);
212 0 : if (values.IsEmpty()) {
213 0 : result = cssDecl->RemoveProperty(propertyNameString, returnString);
214 0 : NS_ENSURE_SUCCESS(result, result);
215 : }
216 : else {
217 0 : nsAutoString priority;
218 0 : result = cssDecl->GetPropertyPriority(propertyNameString, priority);
219 0 : NS_ENSURE_SUCCESS(result, result);
220 0 : result = cssDecl->SetProperty(propertyNameString, values,
221 0 : priority);
222 0 : NS_ENSURE_SUCCESS(result, result);
223 : }
224 : }
225 : else {
226 0 : result = cssDecl->RemoveProperty(propertyNameString, returnString);
227 0 : NS_ENSURE_SUCCESS(result, result);
228 : }
229 : }
230 : else {
231 0 : nsAutoString priority;
232 0 : result = cssDecl->GetPropertyPriority(propertyNameString, priority);
233 0 : NS_ENSURE_SUCCESS(result, result);
234 0 : if (multiple) {
235 : // the property can have more than one value, let's add
236 : // the value we have to add to the others
237 :
238 : // the line below is a workaround because nsDOMCSSDeclaration::GetPropertyCSSValue
239 : // is not yet implemented (bug 62682)
240 0 : AddValueToMultivalueProperty(values, mValue);
241 : }
242 : else
243 0 : values.Assign(mValue);
244 0 : result = cssDecl->SetProperty(propertyNameString, values,
245 0 : priority);
246 0 : NS_ENSURE_SUCCESS(result, result);
247 : }
248 :
249 : // let's be sure we don't keep an empty style attribute
250 : PRUint32 length;
251 0 : result = cssDecl->GetLength(&length);
252 0 : NS_ENSURE_SUCCESS(result, result);
253 0 : if (!length) {
254 0 : result = mElement->RemoveAttribute(styleAttr);
255 0 : NS_ENSURE_SUCCESS(result, result);
256 : }
257 : else
258 0 : mRedoAttributeWasSet = true;
259 :
260 0 : return cssDecl->GetPropertyValue(propertyNameString, mRedoValue);
261 : }
262 :
263 0 : nsresult ChangeCSSInlineStyleTxn::SetStyle(bool aAttributeWasSet,
264 : nsAString & aValue)
265 : {
266 0 : NS_ASSERTION(mEditor && mElement, "bad state");
267 0 : if (!mEditor || !mElement) { return NS_ERROR_NOT_INITIALIZED; }
268 :
269 0 : nsresult result = NS_OK;
270 0 : if (aAttributeWasSet) {
271 : // the style attribute was set and not empty, let's recreate the declaration
272 0 : nsAutoString propertyNameString;
273 0 : mProperty->ToString(propertyNameString);
274 :
275 0 : nsCOMPtr<nsIDOMElementCSSInlineStyle> inlineStyles = do_QueryInterface(mElement);
276 0 : NS_ENSURE_TRUE(inlineStyles, NS_ERROR_NULL_POINTER);
277 0 : nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl;
278 0 : result = inlineStyles->GetStyle(getter_AddRefs(cssDecl));
279 0 : NS_ENSURE_SUCCESS(result, result);
280 0 : NS_ENSURE_TRUE(cssDecl, NS_ERROR_NULL_POINTER);
281 :
282 0 : if (aValue.IsEmpty()) {
283 : // an empty value means we have to remove the property
284 0 : nsAutoString returnString;
285 0 : result = cssDecl->RemoveProperty(propertyNameString, returnString);
286 : }
287 : else {
288 : // let's recreate the declaration as it was
289 0 : nsAutoString priority;
290 0 : result = cssDecl->GetPropertyPriority(propertyNameString, priority);
291 0 : NS_ENSURE_SUCCESS(result, result);
292 0 : result = cssDecl->SetProperty(propertyNameString, aValue, priority);
293 : }
294 : }
295 : else
296 0 : result = mElement->RemoveAttribute(NS_LITERAL_STRING("style"));
297 :
298 0 : return result;
299 : }
300 :
301 0 : NS_IMETHODIMP ChangeCSSInlineStyleTxn::UndoTransaction(void)
302 : {
303 0 : return SetStyle(mUndoAttributeWasSet, mUndoValue);
304 : }
305 :
306 0 : NS_IMETHODIMP ChangeCSSInlineStyleTxn::RedoTransaction(void)
307 : {
308 0 : return SetStyle(mRedoAttributeWasSet, mRedoValue);
309 : }
310 :
311 0 : NS_IMETHODIMP ChangeCSSInlineStyleTxn::GetTxnDescription(nsAString& aString)
312 : {
313 0 : aString.AssignLiteral("ChangeCSSInlineStyleTxn: [mRemoveProperty == ");
314 :
315 0 : if (!mRemoveProperty)
316 0 : aString.AppendLiteral("false] ");
317 : else
318 0 : aString.AppendLiteral("true] ");
319 0 : nsAutoString tempString;
320 0 : mProperty->ToString(tempString);
321 0 : aString += tempString;
322 0 : return NS_OK;
323 : }
324 :
325 : // answers true if the CSS property accepts more than one value
326 : bool
327 0 : ChangeCSSInlineStyleTxn::AcceptsMoreThanOneValue(nsIAtom *aCSSProperty)
328 : {
329 0 : return aCSSProperty == nsGkAtoms::text_decoration;
330 : }
331 :
332 : // adds the value aNewValue to the list of white-space separated values aValues
333 : NS_IMETHODIMP
334 0 : ChangeCSSInlineStyleTxn::AddValueToMultivalueProperty(nsAString & aValues, const nsAString & aNewValue)
335 : {
336 0 : if (aValues.IsEmpty()
337 0 : || aValues.LowerCaseEqualsLiteral("none")) {
338 : // the list of values is empty of the value is 'none'
339 0 : aValues.Assign(aNewValue);
340 : }
341 0 : else if (!ValueIncludes(aValues, aNewValue, false)) {
342 : // we already have another value but not this one; add it
343 0 : aValues.Append(PRUnichar(' '));
344 0 : aValues.Append(aNewValue);
345 : }
346 0 : return NS_OK;
347 4392 : }
|