EditingStyle.cpp revision cad810f21b803229eb11403f9209855525a25d57
1/* 2 * Copyright (C) 2007, 2008, 2009 Apple Computer, Inc. 3 * Copyright (C) 2010 Google Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "config.h" 28#include "EditingStyle.h" 29 30#include "ApplyStyleCommand.h" 31#include "CSSComputedStyleDeclaration.h" 32#include "CSSMutableStyleDeclaration.h" 33#include "CSSValueKeywords.h" 34#include "Frame.h" 35#include "Node.h" 36#include "Position.h" 37#include "RenderStyle.h" 38#include "SelectionController.h" 39 40namespace WebCore { 41 42// Editing style properties must be preserved during editing operation. 43// e.g. when a user inserts a new paragraph, all properties listed here must be copied to the new paragraph. 44// FIXME: The current editingStyleProperties contains all inheritableProperties but we may not need to preserve all inheritable properties 45static const int editingStyleProperties[] = { 46 // CSS inheritable properties 47 CSSPropertyBorderCollapse, 48 CSSPropertyColor, 49 CSSPropertyFontFamily, 50 CSSPropertyFontSize, 51 CSSPropertyFontStyle, 52 CSSPropertyFontVariant, 53 CSSPropertyFontWeight, 54 CSSPropertyLetterSpacing, 55 CSSPropertyLineHeight, 56 CSSPropertyOrphans, 57 CSSPropertyTextAlign, 58 CSSPropertyTextIndent, 59 CSSPropertyTextTransform, 60 CSSPropertyWhiteSpace, 61 CSSPropertyWidows, 62 CSSPropertyWordSpacing, 63 CSSPropertyWebkitBorderHorizontalSpacing, 64 CSSPropertyWebkitBorderVerticalSpacing, 65 CSSPropertyWebkitTextDecorationsInEffect, 66 CSSPropertyWebkitTextFillColor, 67 CSSPropertyWebkitTextSizeAdjust, 68 CSSPropertyWebkitTextStrokeColor, 69 CSSPropertyWebkitTextStrokeWidth, 70}; 71size_t numEditingStyleProperties = WTF_ARRAY_LENGTH(editingStyleProperties); 72 73static PassRefPtr<CSSMutableStyleDeclaration> copyEditingProperties(CSSStyleDeclaration* style) 74{ 75 return style->copyPropertiesInSet(editingStyleProperties, numEditingStyleProperties); 76} 77 78static PassRefPtr<CSSMutableStyleDeclaration> editingStyleFromComputedStyle(PassRefPtr<CSSComputedStyleDeclaration> style) 79{ 80 if (!style) 81 return CSSMutableStyleDeclaration::create(); 82 return copyEditingProperties(style.get()); 83} 84 85float EditingStyle::NoFontDelta = 0.0f; 86 87EditingStyle::EditingStyle() 88 : m_shouldUseFixedDefaultFontSize(false) 89 , m_fontSizeDelta(NoFontDelta) 90{ 91} 92 93EditingStyle::EditingStyle(Node* node) 94 : m_shouldUseFixedDefaultFontSize(false) 95 , m_fontSizeDelta(NoFontDelta) 96{ 97 init(node); 98} 99 100EditingStyle::EditingStyle(const Position& position) 101 : m_shouldUseFixedDefaultFontSize(false) 102 , m_fontSizeDelta(NoFontDelta) 103{ 104 init(position.node()); 105} 106 107EditingStyle::EditingStyle(const CSSStyleDeclaration* style) 108 : m_mutableStyle(style->copy()) 109 , m_shouldUseFixedDefaultFontSize(false) 110 , m_fontSizeDelta(NoFontDelta) 111{ 112 extractFontSizeDelta(); 113} 114 115EditingStyle::~EditingStyle() 116{ 117} 118 119void EditingStyle::init(Node* node) 120{ 121 RefPtr<CSSComputedStyleDeclaration> computedStyleAtPosition = computedStyle(node); 122 m_mutableStyle = editingStyleFromComputedStyle(computedStyleAtPosition); 123 124 if (node && node->computedStyle()) { 125 RenderStyle* renderStyle = node->computedStyle(); 126 removeTextFillAndStrokeColorsIfNeeded(renderStyle); 127 replaceFontSizeByKeywordIfPossible(renderStyle, computedStyleAtPosition.get()); 128 } 129 130 m_shouldUseFixedDefaultFontSize = computedStyleAtPosition->useFixedFontDefaultSize(); 131 extractFontSizeDelta(); 132} 133 134void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyle) 135{ 136 // If a node's text fill color is invalid, then its children use 137 // their font-color as their text fill color (they don't 138 // inherit it). Likewise for stroke color. 139 ExceptionCode ec = 0; 140 if (!renderStyle->textFillColor().isValid()) 141 m_mutableStyle->removeProperty(CSSPropertyWebkitTextFillColor, ec); 142 if (!renderStyle->textStrokeColor().isValid()) 143 m_mutableStyle->removeProperty(CSSPropertyWebkitTextStrokeColor, ec); 144 ASSERT(!ec); 145} 146 147void EditingStyle::replaceFontSizeByKeywordIfPossible(RenderStyle* renderStyle, CSSComputedStyleDeclaration* computedStyle) 148{ 149 ASSERT(renderStyle); 150 if (renderStyle->fontDescription().keywordSize()) 151 m_mutableStyle->setProperty(CSSPropertyFontSize, computedStyle->getFontSizeCSSValuePreferringKeyword()->cssText()); 152} 153 154void EditingStyle::extractFontSizeDelta() 155{ 156 if (m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize)) { 157 // Explicit font size overrides any delta. 158 m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta); 159 return; 160 } 161 162 // Get the adjustment amount out of the style. 163 RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta); 164 if (!value || value->cssValueType() != CSSValue::CSS_PRIMITIVE_VALUE) 165 return; 166 167 CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value.get()); 168 169 // Only PX handled now. If we handle more types in the future, perhaps 170 // a switch statement here would be more appropriate. 171 if (primitiveValue->primitiveType() != CSSPrimitiveValue::CSS_PX) 172 return; 173 174 m_fontSizeDelta = primitiveValue->getFloatValue(); 175 m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta); 176} 177 178bool EditingStyle::isEmpty() const 179{ 180 return (!m_mutableStyle || m_mutableStyle->isEmpty()) && m_fontSizeDelta == NoFontDelta; 181} 182 183bool EditingStyle::textDirection(WritingDirection& writingDirection) const 184{ 185 RefPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi); 186 if (!unicodeBidi) 187 return false; 188 189 ASSERT(unicodeBidi->isPrimitiveValue()); 190 int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent(); 191 if (unicodeBidiValue == CSSValueEmbed) { 192 RefPtr<CSSValue> direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection); 193 ASSERT(!direction || direction->isPrimitiveValue()); 194 if (!direction) 195 return false; 196 197 writingDirection = static_cast<CSSPrimitiveValue*>(direction.get())->getIdent() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection; 198 199 return true; 200 } 201 202 if (unicodeBidiValue == CSSValueNormal) { 203 writingDirection = NaturalWritingDirection; 204 return true; 205 } 206 207 return false; 208} 209 210void EditingStyle::setStyle(PassRefPtr<CSSMutableStyleDeclaration> style) 211{ 212 m_mutableStyle = style; 213 // FIXME: We should be able to figure out whether or not font is fixed width for mutable style. 214 // We need to check font-family is monospace as in FontDescription but we don't want to duplicate code here. 215 m_shouldUseFixedDefaultFontSize = false; 216 extractFontSizeDelta(); 217} 218 219void EditingStyle::overrideWithStyle(const CSSMutableStyleDeclaration* style) 220{ 221 if (!style || !style->length()) 222 return; 223 if (!m_mutableStyle) 224 m_mutableStyle = CSSMutableStyleDeclaration::create(); 225 m_mutableStyle->merge(style); 226 extractFontSizeDelta(); 227} 228 229void EditingStyle::clear() 230{ 231 m_mutableStyle.clear(); 232 m_shouldUseFixedDefaultFontSize = false; 233 m_fontSizeDelta = NoFontDelta; 234} 235 236PassRefPtr<EditingStyle> EditingStyle::copy() const 237{ 238 RefPtr<EditingStyle> copy = EditingStyle::create(); 239 if (m_mutableStyle) 240 copy->m_mutableStyle = m_mutableStyle->copy(); 241 copy->m_shouldUseFixedDefaultFontSize = m_shouldUseFixedDefaultFontSize; 242 copy->m_fontSizeDelta = m_fontSizeDelta; 243 return copy; 244} 245 246PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveBlockProperties() 247{ 248 RefPtr<EditingStyle> blockProperties = EditingStyle::create(); 249 if (!m_mutableStyle) 250 return blockProperties; 251 252 blockProperties->m_mutableStyle = m_mutableStyle->copyBlockProperties(); 253 m_mutableStyle->removeBlockProperties(); 254 255 return blockProperties; 256} 257 258void EditingStyle::removeBlockProperties() 259{ 260 if (!m_mutableStyle) 261 return; 262 263 m_mutableStyle->removeBlockProperties(); 264} 265 266void EditingStyle::removeStyleAddedByNode(Node* node) 267{ 268 if (!node || !node->parentNode()) 269 return; 270 RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode())); 271 RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node)); 272 parentStyle->diff(nodeStyle.get()); 273 nodeStyle->diff(m_mutableStyle.get()); 274} 275 276void EditingStyle::removeStyleConflictingWithStyleOfNode(Node* node) 277{ 278 if (!node || !node->parentNode() || !m_mutableStyle) 279 return; 280 RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode())); 281 RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node)); 282 parentStyle->diff(nodeStyle.get()); 283 284 CSSMutableStyleDeclaration::const_iterator end = nodeStyle->end(); 285 for (CSSMutableStyleDeclaration::const_iterator it = nodeStyle->begin(); it != end; ++it) 286 m_mutableStyle->removeProperty(it->id()); 287} 288 289void EditingStyle::removeNonEditingProperties() 290{ 291 if (m_mutableStyle) 292 m_mutableStyle = copyEditingProperties(m_mutableStyle.get()); 293} 294 295void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWritingDirection shouldPreserveWritingDirection) 296{ 297 if (!m_mutableStyle) 298 return; 299 300 // ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style. 301 // If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate 302 // which one of editingStyleAtPosition or computedStyle is called. 303 RefPtr<EditingStyle> style = EditingStyle::create(position); 304 305 RefPtr<CSSValue> unicodeBidi; 306 RefPtr<CSSValue> direction; 307 if (shouldPreserveWritingDirection == PreserveWritingDirection) { 308 unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi); 309 direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection); 310 } 311 312 style->m_mutableStyle->diff(m_mutableStyle.get()); 313 314 // if alpha value is zero, we don't add the background color. 315 RefPtr<CSSValue> backgroundColor = m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor); 316 if (backgroundColor && backgroundColor->isPrimitiveValue() 317 && !alphaChannel(static_cast<CSSPrimitiveValue*>(backgroundColor.get())->getRGBA32Value())) { 318 ExceptionCode ec; 319 m_mutableStyle->removeProperty(CSSPropertyBackgroundColor, ec); 320 } 321 322 if (unicodeBidi) { 323 ASSERT(unicodeBidi->isPrimitiveValue()); 324 m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent()); 325 if (direction) { 326 ASSERT(direction->isPrimitiveValue()); 327 m_mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent()); 328 } 329 } 330} 331 332PassRefPtr<EditingStyle> editingStyleIncludingTypingStyle(const Position& position) 333{ 334 RefPtr<EditingStyle> editingStyle = EditingStyle::create(position); 335 RefPtr<EditingStyle> typingStyle = position.node()->document()->frame()->selection()->typingStyle(); 336 if (typingStyle && typingStyle->style()) 337 editingStyle->style()->merge(copyEditingProperties(typingStyle->style()).get()); 338 return editingStyle; 339} 340 341} 342