Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "mozilla/ChangeStyleTransaction.h"
7 :
8 : #include "mozilla/dom/Element.h" // for Element
9 : #include "nsAString.h" // for nsAString::Append, etc.
10 : #include "nsCRT.h" // for nsCRT::IsAsciiSpace
11 : #include "nsDebug.h" // for NS_ENSURE_SUCCESS, etc.
12 : #include "nsError.h" // for NS_ERROR_NULL_POINTER, etc.
13 : #include "nsGkAtoms.h" // for nsGkAtoms, etc.
14 : #include "nsICSSDeclaration.h" // for nsICSSDeclaration.
15 : #include "nsLiteralString.h" // for NS_LITERAL_STRING, etc.
16 : #include "nsReadableUtils.h" // for ToNewUnicode
17 : #include "nsString.h" // for nsAutoString, nsString, etc.
18 : #include "nsStyledElement.h" // for nsStyledElement.
19 : #include "nsUnicharUtils.h" // for nsCaseInsensitiveStringComparator
20 :
21 : namespace mozilla {
22 :
23 : using namespace dom;
24 :
25 : #define kNullCh (char16_t('\0'))
26 :
27 0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(ChangeStyleTransaction, EditTransactionBase,
28 : mElement)
29 :
30 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ChangeStyleTransaction)
31 0 : NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
32 :
33 0 : NS_IMPL_ADDREF_INHERITED(ChangeStyleTransaction, EditTransactionBase)
34 0 : NS_IMPL_RELEASE_INHERITED(ChangeStyleTransaction, EditTransactionBase)
35 :
36 0 : ChangeStyleTransaction::~ChangeStyleTransaction()
37 : {
38 0 : }
39 :
40 : // Answers true if aValue is in the string list of white-space separated values
41 : // aValueList.
42 : bool
43 0 : ChangeStyleTransaction::ValueIncludes(const nsAString& aValueList,
44 : const nsAString& aValue)
45 : {
46 0 : nsAutoString valueList(aValueList);
47 0 : bool result = false;
48 :
49 : // put an extra null at the end
50 0 : valueList.Append(kNullCh);
51 :
52 0 : char16_t* value = ToNewUnicode(aValue);
53 0 : char16_t* start = valueList.BeginWriting();
54 0 : char16_t* end = start;
55 :
56 0 : while (kNullCh != *start) {
57 0 : while (kNullCh != *start && nsCRT::IsAsciiSpace(*start)) {
58 : // skip leading space
59 0 : start++;
60 : }
61 0 : end = start;
62 :
63 0 : while (kNullCh != *end && !nsCRT::IsAsciiSpace(*end)) {
64 : // look for space or end
65 0 : end++;
66 : }
67 : // end string here
68 0 : *end = kNullCh;
69 :
70 0 : if (start < end) {
71 0 : if (nsDependentString(value).Equals(nsDependentString(start),
72 0 : nsCaseInsensitiveStringComparator())) {
73 0 : result = true;
74 0 : break;
75 : }
76 : }
77 0 : start = ++end;
78 : }
79 0 : free(value);
80 0 : return result;
81 : }
82 :
83 : // Removes the value aRemoveValue from the string list of white-space separated
84 : // values aValueList
85 : void
86 0 : ChangeStyleTransaction::RemoveValueFromListOfValues(
87 : nsAString& aValues,
88 : const nsAString& aRemoveValue)
89 : {
90 0 : nsAutoString classStr(aValues);
91 0 : nsAutoString outString;
92 : // put an extra null at the end
93 0 : classStr.Append(kNullCh);
94 :
95 0 : char16_t* start = classStr.BeginWriting();
96 0 : char16_t* end = start;
97 :
98 0 : while (kNullCh != *start) {
99 0 : while (kNullCh != *start && nsCRT::IsAsciiSpace(*start)) {
100 : // skip leading space
101 0 : start++;
102 : }
103 0 : end = start;
104 :
105 0 : while (kNullCh != *end && !nsCRT::IsAsciiSpace(*end)) {
106 : // look for space or end
107 0 : end++;
108 : }
109 : // end string here
110 0 : *end = kNullCh;
111 :
112 0 : if (start < end && !aRemoveValue.Equals(start)) {
113 0 : outString.Append(start);
114 0 : outString.Append(char16_t(' '));
115 : }
116 :
117 0 : start = ++end;
118 : }
119 0 : aValues.Assign(outString);
120 0 : }
121 :
122 0 : ChangeStyleTransaction::ChangeStyleTransaction(Element& aElement,
123 : nsIAtom& aProperty,
124 : const nsAString& aValue,
125 0 : EChangeType aChangeType)
126 : : EditTransactionBase()
127 : , mElement(&aElement)
128 : , mProperty(&aProperty)
129 : , mValue(aValue)
130 0 : , mRemoveProperty(aChangeType == eRemove)
131 : , mUndoValue()
132 : , mRedoValue()
133 : , mUndoAttributeWasSet(false)
134 0 : , mRedoAttributeWasSet(false)
135 : {
136 0 : }
137 :
138 : NS_IMETHODIMP
139 0 : ChangeStyleTransaction::DoTransaction()
140 : {
141 0 : nsCOMPtr<nsStyledElement> inlineStyles = do_QueryInterface(mElement);
142 0 : NS_ENSURE_TRUE(inlineStyles, NS_ERROR_NULL_POINTER);
143 :
144 0 : nsCOMPtr<nsICSSDeclaration> cssDecl = inlineStyles->Style();
145 :
146 0 : nsAutoString propertyNameString;
147 0 : mProperty->ToString(propertyNameString);
148 :
149 0 : mUndoAttributeWasSet = mElement->HasAttr(kNameSpaceID_None,
150 : nsGkAtoms::style);
151 :
152 0 : nsAutoString values;
153 0 : nsresult rv = cssDecl->GetPropertyValue(propertyNameString, values);
154 0 : NS_ENSURE_SUCCESS(rv, rv);
155 0 : mUndoValue.Assign(values);
156 :
157 : // Does this property accept more than one value? (bug 62682)
158 0 : bool multiple = AcceptsMoreThanOneValue(*mProperty);
159 :
160 0 : if (mRemoveProperty) {
161 0 : nsAutoString returnString;
162 0 : if (multiple) {
163 : // Let's remove only the value we have to remove and not the others
164 :
165 : // The two lines below are a workaround because
166 : // nsDOMCSSDeclaration::GetPropertyCSSValue is not yet implemented (bug
167 : // 62682)
168 0 : RemoveValueFromListOfValues(values, NS_LITERAL_STRING("none"));
169 0 : RemoveValueFromListOfValues(values, mValue);
170 0 : if (values.IsEmpty()) {
171 0 : rv = cssDecl->RemoveProperty(propertyNameString, returnString);
172 0 : NS_ENSURE_SUCCESS(rv, rv);
173 : } else {
174 0 : nsAutoString priority;
175 0 : cssDecl->GetPropertyPriority(propertyNameString, priority);
176 0 : rv = cssDecl->SetProperty(propertyNameString, values, priority);
177 0 : NS_ENSURE_SUCCESS(rv, rv);
178 : }
179 : } else {
180 0 : rv = cssDecl->RemoveProperty(propertyNameString, returnString);
181 0 : NS_ENSURE_SUCCESS(rv, rv);
182 : }
183 : } else {
184 0 : nsAutoString priority;
185 0 : cssDecl->GetPropertyPriority(propertyNameString, priority);
186 0 : if (multiple) {
187 : // Let's add the value we have to add to the others
188 :
189 : // The line below is a workaround because
190 : // nsDOMCSSDeclaration::GetPropertyCSSValue is not yet implemented (bug
191 : // 62682)
192 0 : AddValueToMultivalueProperty(values, mValue);
193 : } else {
194 0 : values.Assign(mValue);
195 : }
196 0 : rv = cssDecl->SetProperty(propertyNameString, values, priority);
197 0 : NS_ENSURE_SUCCESS(rv, rv);
198 : }
199 :
200 : // Let's be sure we don't keep an empty style attribute
201 : uint32_t length;
202 0 : rv = cssDecl->GetLength(&length);
203 0 : NS_ENSURE_SUCCESS(rv, rv);
204 0 : if (!length) {
205 0 : rv = mElement->UnsetAttr(kNameSpaceID_None, nsGkAtoms::style, true);
206 0 : NS_ENSURE_SUCCESS(rv, rv);
207 : } else {
208 0 : mRedoAttributeWasSet = true;
209 : }
210 :
211 0 : return cssDecl->GetPropertyValue(propertyNameString, mRedoValue);
212 : }
213 :
214 : nsresult
215 0 : ChangeStyleTransaction::SetStyle(bool aAttributeWasSet,
216 : nsAString& aValue)
217 : {
218 0 : if (aAttributeWasSet) {
219 : // The style attribute was not empty, let's recreate the declaration
220 0 : nsAutoString propertyNameString;
221 0 : mProperty->ToString(propertyNameString);
222 :
223 0 : nsCOMPtr<nsStyledElement> inlineStyles = do_QueryInterface(mElement);
224 0 : NS_ENSURE_TRUE(inlineStyles, NS_ERROR_NULL_POINTER);
225 0 : nsCOMPtr<nsICSSDeclaration> cssDecl = inlineStyles->Style();
226 :
227 0 : if (aValue.IsEmpty()) {
228 : // An empty value means we have to remove the property
229 0 : nsAutoString returnString;
230 0 : return cssDecl->RemoveProperty(propertyNameString, returnString);
231 : }
232 : // Let's recreate the declaration as it was
233 0 : nsAutoString priority;
234 0 : cssDecl->GetPropertyPriority(propertyNameString, priority);
235 0 : return cssDecl->SetProperty(propertyNameString, aValue, priority);
236 : }
237 0 : return mElement->UnsetAttr(kNameSpaceID_None, nsGkAtoms::style, true);
238 : }
239 :
240 : NS_IMETHODIMP
241 0 : ChangeStyleTransaction::UndoTransaction()
242 : {
243 0 : return SetStyle(mUndoAttributeWasSet, mUndoValue);
244 : }
245 :
246 : NS_IMETHODIMP
247 0 : ChangeStyleTransaction::RedoTransaction()
248 : {
249 0 : return SetStyle(mRedoAttributeWasSet, mRedoValue);
250 : }
251 :
252 : NS_IMETHODIMP
253 0 : ChangeStyleTransaction::GetTxnDescription(nsAString& aString)
254 : {
255 0 : aString.AssignLiteral("ChangeStyleTransaction: [mRemoveProperty == ");
256 :
257 0 : if (mRemoveProperty) {
258 0 : aString.AppendLiteral("true] ");
259 : } else {
260 0 : aString.AppendLiteral("false] ");
261 : }
262 0 : aString += nsDependentAtomString(mProperty);
263 0 : return NS_OK;
264 : }
265 :
266 : // True if the CSS property accepts more than one value
267 : bool
268 0 : ChangeStyleTransaction::AcceptsMoreThanOneValue(nsIAtom& aCSSProperty)
269 : {
270 0 : return &aCSSProperty == nsGkAtoms::text_decoration;
271 : }
272 :
273 : // Adds the value aNewValue to the list of white-space separated values aValues
274 : void
275 0 : ChangeStyleTransaction::AddValueToMultivalueProperty(nsAString& aValues,
276 : const nsAString& aNewValue)
277 : {
278 0 : if (aValues.IsEmpty() || aValues.LowerCaseEqualsLiteral("none")) {
279 0 : aValues.Assign(aNewValue);
280 0 : } else if (!ValueIncludes(aValues, aNewValue)) {
281 : // We already have another value but not this one; add it
282 0 : aValues.Append(char16_t(' '));
283 0 : aValues.Append(aNewValue);
284 : }
285 0 : }
286 :
287 : } // namespace mozilla
|