editor/libeditor/base/ChangeCSSInlineStyleTxn.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial