1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/editor/libeditor/base/ChangeCSSInlineStyleTxn.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,315 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "ChangeCSSInlineStyleTxn.h" 1.10 +#include "nsAString.h" // for nsAString_internal::Append, etc 1.11 +#include "nsCRT.h" // for nsCRT 1.12 +#include "nsDebug.h" // for NS_ENSURE_SUCCESS, etc 1.13 +#include "nsError.h" // for NS_ERROR_NULL_POINTER, etc 1.14 +#include "nsGkAtoms.h" // for nsGkAtoms, etc 1.15 +#include "nsIAtom.h" // for nsIAtom 1.16 +#include "nsIDOMCSSStyleDeclaration.h" // for nsIDOMCSSStyleDeclaration 1.17 +#include "nsIDOMElement.h" // for nsIDOMElement 1.18 +#include "nsIDOMElementCSSInlineStyle.h" 1.19 +#include "nsISupportsImpl.h" // for EditTxn::QueryInterface, etc 1.20 +#include "nsISupportsUtils.h" // for NS_ADDREF 1.21 +#include "nsLiteralString.h" // for NS_LITERAL_STRING, etc 1.22 +#include "nsReadableUtils.h" // for ToNewUnicode 1.23 +#include "nsString.h" // for nsAutoString, nsString, etc 1.24 +#include "nsUnicharUtils.h" 1.25 +#include "nsXPCOM.h" // for NS_Free 1.26 + 1.27 +class nsIEditor; 1.28 + 1.29 +#define kNullCh (char16_t('\0')) 1.30 + 1.31 +NS_IMPL_CYCLE_COLLECTION_INHERITED(ChangeCSSInlineStyleTxn, EditTxn, 1.32 + mElement) 1.33 + 1.34 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ChangeCSSInlineStyleTxn) 1.35 +NS_INTERFACE_MAP_END_INHERITING(EditTxn) 1.36 + 1.37 +// answers true if aValue is in the string list of white-space separated values aValueList 1.38 +// a case-sensitive search is performed if aCaseSensitive is true 1.39 +bool 1.40 +ChangeCSSInlineStyleTxn::ValueIncludes(const nsAString &aValueList, const nsAString &aValue, bool aCaseSensitive) 1.41 +{ 1.42 + nsAutoString valueList(aValueList); 1.43 + bool result = false; 1.44 + 1.45 + valueList.Append(kNullCh); // put an extra null at the end 1.46 + 1.47 + char16_t *value = ToNewUnicode(aValue); 1.48 + char16_t *start = valueList.BeginWriting(); 1.49 + char16_t *end = start; 1.50 + 1.51 + while (kNullCh != *start) { 1.52 + while ((kNullCh != *start) && nsCRT::IsAsciiSpace(*start)) { // skip leading space 1.53 + start++; 1.54 + } 1.55 + end = start; 1.56 + 1.57 + while ((kNullCh != *end) && (false == nsCRT::IsAsciiSpace(*end))) { // look for space or end 1.58 + end++; 1.59 + } 1.60 + *end = kNullCh; // end string here 1.61 + 1.62 + if (start < end) { 1.63 + if (aCaseSensitive) { 1.64 + if (!nsCRT::strcmp(value, start)) { 1.65 + result = true; 1.66 + break; 1.67 + } 1.68 + } 1.69 + else { 1.70 + if (nsDependentString(value).Equals(nsDependentString(start), 1.71 + nsCaseInsensitiveStringComparator())) { 1.72 + result = true; 1.73 + break; 1.74 + } 1.75 + } 1.76 + } 1.77 + start = ++end; 1.78 + } 1.79 + NS_Free(value); 1.80 + return result; 1.81 +} 1.82 + 1.83 +// removes the value aRemoveValue from the string list of white-space separated values aValueList 1.84 +void 1.85 +ChangeCSSInlineStyleTxn::RemoveValueFromListOfValues(nsAString & aValues, const nsAString & aRemoveValue) 1.86 +{ 1.87 + nsAutoString classStr(aValues); // copy to work buffer nsAutoString rv(aRemoveValue); 1.88 + nsAutoString outString; 1.89 + classStr.Append(kNullCh); // put an extra null at the end 1.90 + 1.91 + char16_t *start = classStr.BeginWriting(); 1.92 + char16_t *end = start; 1.93 + 1.94 + while (kNullCh != *start) { 1.95 + while ((kNullCh != *start) && nsCRT::IsAsciiSpace(*start)) { // skip leading space 1.96 + start++; 1.97 + } 1.98 + end = start; 1.99 + 1.100 + while ((kNullCh != *end) && (false == nsCRT::IsAsciiSpace(*end))) { // look for space or end 1.101 + end++; 1.102 + } 1.103 + *end = kNullCh; // end string here 1.104 + 1.105 + if (start < end) { 1.106 + if (!aRemoveValue.Equals(start)) { 1.107 + outString.Append(start); 1.108 + outString.Append(char16_t(' ')); 1.109 + } 1.110 + } 1.111 + 1.112 + start = ++end; 1.113 + } 1.114 + aValues.Assign(outString); 1.115 +} 1.116 + 1.117 +ChangeCSSInlineStyleTxn::ChangeCSSInlineStyleTxn() 1.118 + : EditTxn() 1.119 +{ 1.120 +} 1.121 + 1.122 +NS_IMETHODIMP ChangeCSSInlineStyleTxn::Init(nsIEditor *aEditor, 1.123 + nsIDOMElement *aElement, 1.124 + nsIAtom *aProperty, 1.125 + const nsAString& aValue, 1.126 + bool aRemoveProperty) 1.127 +{ 1.128 + NS_ASSERTION(aEditor && aElement, "bad arg"); 1.129 + if (!aEditor || !aElement) { return NS_ERROR_NULL_POINTER; } 1.130 + 1.131 + mEditor = aEditor; 1.132 + mElement = do_QueryInterface(aElement); 1.133 + mProperty = aProperty; 1.134 + NS_ADDREF(mProperty); 1.135 + mValue.Assign(aValue); 1.136 + mRemoveProperty = aRemoveProperty; 1.137 + mUndoAttributeWasSet = false; 1.138 + mRedoAttributeWasSet = false; 1.139 + mUndoValue.Truncate(); 1.140 + mRedoValue.Truncate(); 1.141 + return NS_OK; 1.142 +} 1.143 + 1.144 +NS_IMETHODIMP ChangeCSSInlineStyleTxn::DoTransaction(void) 1.145 +{ 1.146 + NS_ASSERTION(mEditor && mElement, "bad state"); 1.147 + if (!mEditor || !mElement) { return NS_ERROR_NOT_INITIALIZED; } 1.148 + 1.149 + nsCOMPtr<nsIDOMElementCSSInlineStyle> inlineStyles = do_QueryInterface(mElement); 1.150 + NS_ENSURE_TRUE(inlineStyles, NS_ERROR_NULL_POINTER); 1.151 + 1.152 + nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl; 1.153 + nsresult result = inlineStyles->GetStyle(getter_AddRefs(cssDecl)); 1.154 + NS_ENSURE_SUCCESS(result, result); 1.155 + NS_ENSURE_TRUE(cssDecl, NS_ERROR_NULL_POINTER); 1.156 + 1.157 + nsAutoString propertyNameString; 1.158 + mProperty->ToString(propertyNameString); 1.159 + 1.160 + NS_NAMED_LITERAL_STRING(styleAttr, "style"); 1.161 + result = mElement->HasAttribute(styleAttr, &mUndoAttributeWasSet); 1.162 + NS_ENSURE_SUCCESS(result, result); 1.163 + 1.164 + nsAutoString values; 1.165 + result = cssDecl->GetPropertyValue(propertyNameString, values); 1.166 + NS_ENSURE_SUCCESS(result, result); 1.167 + mUndoValue.Assign(values); 1.168 + 1.169 + // does this property accept more than 1 value ? 1.170 + // we need to know that because of bug 62682 1.171 + bool multiple = AcceptsMoreThanOneValue(mProperty); 1.172 + 1.173 + if (mRemoveProperty) { 1.174 + nsAutoString returnString; 1.175 + if (multiple) { 1.176 + // the property can have more than one value, let's remove only 1.177 + // the value we have to remove and not the others 1.178 + 1.179 + // the 2 lines below are a workaround because nsDOMCSSDeclaration::GetPropertyCSSValue 1.180 + // is not yet implemented (bug 62682) 1.181 + RemoveValueFromListOfValues(values, NS_LITERAL_STRING("none")); 1.182 + RemoveValueFromListOfValues(values, mValue); 1.183 + if (values.IsEmpty()) { 1.184 + result = cssDecl->RemoveProperty(propertyNameString, returnString); 1.185 + NS_ENSURE_SUCCESS(result, result); 1.186 + } 1.187 + else { 1.188 + nsAutoString priority; 1.189 + result = cssDecl->GetPropertyPriority(propertyNameString, priority); 1.190 + NS_ENSURE_SUCCESS(result, result); 1.191 + result = cssDecl->SetProperty(propertyNameString, values, 1.192 + priority); 1.193 + NS_ENSURE_SUCCESS(result, result); 1.194 + } 1.195 + } 1.196 + else { 1.197 + result = cssDecl->RemoveProperty(propertyNameString, returnString); 1.198 + NS_ENSURE_SUCCESS(result, result); 1.199 + } 1.200 + } 1.201 + else { 1.202 + nsAutoString priority; 1.203 + result = cssDecl->GetPropertyPriority(propertyNameString, priority); 1.204 + NS_ENSURE_SUCCESS(result, result); 1.205 + if (multiple) { 1.206 + // the property can have more than one value, let's add 1.207 + // the value we have to add to the others 1.208 + 1.209 + // the line below is a workaround because nsDOMCSSDeclaration::GetPropertyCSSValue 1.210 + // is not yet implemented (bug 62682) 1.211 + AddValueToMultivalueProperty(values, mValue); 1.212 + } 1.213 + else 1.214 + values.Assign(mValue); 1.215 + result = cssDecl->SetProperty(propertyNameString, values, 1.216 + priority); 1.217 + NS_ENSURE_SUCCESS(result, result); 1.218 + } 1.219 + 1.220 + // let's be sure we don't keep an empty style attribute 1.221 + uint32_t length; 1.222 + result = cssDecl->GetLength(&length); 1.223 + NS_ENSURE_SUCCESS(result, result); 1.224 + if (!length) { 1.225 + result = mElement->RemoveAttribute(styleAttr); 1.226 + NS_ENSURE_SUCCESS(result, result); 1.227 + } 1.228 + else 1.229 + mRedoAttributeWasSet = true; 1.230 + 1.231 + return cssDecl->GetPropertyValue(propertyNameString, mRedoValue); 1.232 +} 1.233 + 1.234 +nsresult ChangeCSSInlineStyleTxn::SetStyle(bool aAttributeWasSet, 1.235 + nsAString & aValue) 1.236 +{ 1.237 + NS_ASSERTION(mEditor && mElement, "bad state"); 1.238 + if (!mEditor || !mElement) { return NS_ERROR_NOT_INITIALIZED; } 1.239 + 1.240 + nsresult result = NS_OK; 1.241 + if (aAttributeWasSet) { 1.242 + // the style attribute was set and not empty, let's recreate the declaration 1.243 + nsAutoString propertyNameString; 1.244 + mProperty->ToString(propertyNameString); 1.245 + 1.246 + nsCOMPtr<nsIDOMElementCSSInlineStyle> inlineStyles = do_QueryInterface(mElement); 1.247 + NS_ENSURE_TRUE(inlineStyles, NS_ERROR_NULL_POINTER); 1.248 + nsCOMPtr<nsIDOMCSSStyleDeclaration> cssDecl; 1.249 + result = inlineStyles->GetStyle(getter_AddRefs(cssDecl)); 1.250 + NS_ENSURE_SUCCESS(result, result); 1.251 + NS_ENSURE_TRUE(cssDecl, NS_ERROR_NULL_POINTER); 1.252 + 1.253 + if (aValue.IsEmpty()) { 1.254 + // an empty value means we have to remove the property 1.255 + nsAutoString returnString; 1.256 + result = cssDecl->RemoveProperty(propertyNameString, returnString); 1.257 + } 1.258 + else { 1.259 + // let's recreate the declaration as it was 1.260 + nsAutoString priority; 1.261 + result = cssDecl->GetPropertyPriority(propertyNameString, priority); 1.262 + NS_ENSURE_SUCCESS(result, result); 1.263 + result = cssDecl->SetProperty(propertyNameString, aValue, priority); 1.264 + } 1.265 + } 1.266 + else 1.267 + result = mElement->RemoveAttribute(NS_LITERAL_STRING("style")); 1.268 + 1.269 + return result; 1.270 +} 1.271 + 1.272 +NS_IMETHODIMP ChangeCSSInlineStyleTxn::UndoTransaction(void) 1.273 +{ 1.274 + return SetStyle(mUndoAttributeWasSet, mUndoValue); 1.275 +} 1.276 + 1.277 +NS_IMETHODIMP ChangeCSSInlineStyleTxn::RedoTransaction(void) 1.278 +{ 1.279 + return SetStyle(mRedoAttributeWasSet, mRedoValue); 1.280 +} 1.281 + 1.282 +NS_IMETHODIMP ChangeCSSInlineStyleTxn::GetTxnDescription(nsAString& aString) 1.283 +{ 1.284 + aString.AssignLiteral("ChangeCSSInlineStyleTxn: [mRemoveProperty == "); 1.285 + 1.286 + if (!mRemoveProperty) 1.287 + aString.AppendLiteral("false] "); 1.288 + else 1.289 + aString.AppendLiteral("true] "); 1.290 + nsAutoString tempString; 1.291 + mProperty->ToString(tempString); 1.292 + aString += tempString; 1.293 + return NS_OK; 1.294 +} 1.295 + 1.296 +// answers true if the CSS property accepts more than one value 1.297 +bool 1.298 +ChangeCSSInlineStyleTxn::AcceptsMoreThanOneValue(nsIAtom *aCSSProperty) 1.299 +{ 1.300 + return aCSSProperty == nsGkAtoms::text_decoration; 1.301 +} 1.302 + 1.303 +// adds the value aNewValue to the list of white-space separated values aValues 1.304 +NS_IMETHODIMP 1.305 +ChangeCSSInlineStyleTxn::AddValueToMultivalueProperty(nsAString & aValues, const nsAString & aNewValue) 1.306 +{ 1.307 + if (aValues.IsEmpty() 1.308 + || aValues.LowerCaseEqualsLiteral("none")) { 1.309 + // the list of values is empty of the value is 'none' 1.310 + aValues.Assign(aNewValue); 1.311 + } 1.312 + else if (!ValueIncludes(aValues, aNewValue, false)) { 1.313 + // we already have another value but not this one; add it 1.314 + aValues.Append(char16_t(' ')); 1.315 + aValues.Append(aNewValue); 1.316 + } 1.317 + return NS_OK; 1.318 +}