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 "CSSParser.h"
34 #include "CSSStyleSelector.h"
35 #include "CSSValueKeywords.h"
36 #include "CSSValueList.h"
37 #include "Frame.h"
38 #include "HTMLFontElement.h"
39 #include "HTMLNames.h"
40 #include "Node.h"
41 #include "Position.h"
42 #include "RenderStyle.h"
43 #include "SelectionController.h"
44 #include "StyledElement.h"
45 #include "htmlediting.h"
46
47 namespace WebCore {
48
49 // Editing style properties must be preserved during editing operation.
50 // e.g. when a user inserts a new paragraph, all properties listed here must be copied to the new paragraph.
51 // FIXME: The current editingStyleProperties contains all inheritableProperties but we may not need to preserve all inheritable properties
52 static const int editingStyleProperties[] = {
53 // CSS inheritable properties
54 CSSPropertyBorderCollapse,
55 CSSPropertyColor,
56 CSSPropertyFontFamily,
57 CSSPropertyFontSize,
58 CSSPropertyFontStyle,
59 CSSPropertyFontVariant,
60 CSSPropertyFontWeight,
61 CSSPropertyLetterSpacing,
62 CSSPropertyLineHeight,
63 CSSPropertyOrphans,
64 CSSPropertyTextAlign,
65 CSSPropertyTextIndent,
66 CSSPropertyTextTransform,
67 CSSPropertyWhiteSpace,
68 CSSPropertyWidows,
69 CSSPropertyWordSpacing,
70 CSSPropertyWebkitBorderHorizontalSpacing,
71 CSSPropertyWebkitBorderVerticalSpacing,
72 CSSPropertyWebkitTextDecorationsInEffect,
73 CSSPropertyWebkitTextFillColor,
74 CSSPropertyWebkitTextSizeAdjust,
75 CSSPropertyWebkitTextStrokeColor,
76 CSSPropertyWebkitTextStrokeWidth,
77 };
78 size_t numEditingStyleProperties = WTF_ARRAY_LENGTH(editingStyleProperties);
79
copyEditingProperties(CSSStyleDeclaration * style)80 static PassRefPtr<CSSMutableStyleDeclaration> copyEditingProperties(CSSStyleDeclaration* style)
81 {
82 return style->copyPropertiesInSet(editingStyleProperties, numEditingStyleProperties);
83 }
84
editingStyleFromComputedStyle(PassRefPtr<CSSComputedStyleDeclaration> style)85 static PassRefPtr<CSSMutableStyleDeclaration> editingStyleFromComputedStyle(PassRefPtr<CSSComputedStyleDeclaration> style)
86 {
87 if (!style)
88 return CSSMutableStyleDeclaration::create();
89 return copyEditingProperties(style.get());
90 }
91
92 static RefPtr<CSSMutableStyleDeclaration> getPropertiesNotIn(CSSStyleDeclaration* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle);
93 static RGBA32 getRGBAFontColor(CSSStyleDeclaration*);
94
95 class HTMLElementEquivalent {
96 public:
create(CSSPropertyID propertyID,int primitiveValue,const QualifiedName & tagName)97 static PassOwnPtr<HTMLElementEquivalent> create(CSSPropertyID propertyID, int primitiveValue, const QualifiedName& tagName)
98 {
99 return adoptPtr(new HTMLElementEquivalent(propertyID, primitiveValue, tagName));
100 }
101
~HTMLElementEquivalent()102 virtual ~HTMLElementEquivalent() { }
matches(Element * element) const103 virtual bool matches(Element* element) const { return !m_tagName || element->hasTagName(*m_tagName); }
hasAttribute() const104 virtual bool hasAttribute() const { return false; }
propertyExistsInStyle(CSSStyleDeclaration * style) const105 bool propertyExistsInStyle(CSSStyleDeclaration* style) const { return style->getPropertyCSSValue(m_propertyID); }
106 virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const;
107 virtual void addToStyle(Element*, EditingStyle*) const;
108
109 protected:
110 HTMLElementEquivalent(CSSPropertyID);
111 HTMLElementEquivalent(CSSPropertyID, const QualifiedName& tagName);
112 HTMLElementEquivalent(CSSPropertyID, int primitiveValue, const QualifiedName& tagName);
113 const int m_propertyID;
114 const RefPtr<CSSPrimitiveValue> m_primitiveValue;
115 const QualifiedName* m_tagName; // We can store a pointer because HTML tag names are const global.
116 };
117
HTMLElementEquivalent(CSSPropertyID id)118 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id)
119 : m_propertyID(id)
120 , m_tagName(0)
121 {
122 }
123
HTMLElementEquivalent(CSSPropertyID id,const QualifiedName & tagName)124 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, const QualifiedName& tagName)
125 : m_propertyID(id)
126 , m_tagName(&tagName)
127 {
128 }
129
HTMLElementEquivalent(CSSPropertyID id,int primitiveValue,const QualifiedName & tagName)130 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, int primitiveValue, const QualifiedName& tagName)
131 : m_propertyID(id)
132 , m_primitiveValue(CSSPrimitiveValue::createIdentifier(primitiveValue))
133 , m_tagName(&tagName)
134 {
135 ASSERT(primitiveValue != CSSValueInvalid);
136 }
137
valueIsPresentInStyle(Element * element,CSSStyleDeclaration * style) const138 bool HTMLElementEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const
139 {
140 RefPtr<CSSValue> value = style->getPropertyCSSValue(m_propertyID);
141 return matches(element) && value && value->isPrimitiveValue() && static_cast<CSSPrimitiveValue*>(value.get())->getIdent() == m_primitiveValue->getIdent();
142 }
143
addToStyle(Element *,EditingStyle * style) const144 void HTMLElementEquivalent::addToStyle(Element*, EditingStyle* style) const
145 {
146 style->setProperty(m_propertyID, m_primitiveValue->cssText());
147 }
148
149 class HTMLTextDecorationEquivalent : public HTMLElementEquivalent {
150 public:
create(int primitiveValue,const QualifiedName & tagName)151 static PassOwnPtr<HTMLElementEquivalent> create(int primitiveValue, const QualifiedName& tagName)
152 {
153 return adoptPtr(new HTMLTextDecorationEquivalent(primitiveValue, tagName));
154 }
155 virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const;
156
157 private:
158 HTMLTextDecorationEquivalent(int primitiveValue, const QualifiedName& tagName);
159 };
160
HTMLTextDecorationEquivalent(int primitiveValue,const QualifiedName & tagName)161 HTMLTextDecorationEquivalent::HTMLTextDecorationEquivalent(int primitiveValue, const QualifiedName& tagName)
162 : HTMLElementEquivalent(CSSPropertyTextDecoration, primitiveValue, tagName)
163 {
164 }
165
valueIsPresentInStyle(Element * element,CSSStyleDeclaration * style) const166 bool HTMLTextDecorationEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const
167 {
168 RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID);
169 return matches(element) && styleValue && styleValue->isValueList() && static_cast<CSSValueList*>(styleValue.get())->hasValue(m_primitiveValue.get());
170 }
171
172 class HTMLAttributeEquivalent : public HTMLElementEquivalent {
173 public:
create(CSSPropertyID propertyID,const QualifiedName & tagName,const QualifiedName & attrName)174 static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& tagName, const QualifiedName& attrName)
175 {
176 return adoptPtr(new HTMLAttributeEquivalent(propertyID, tagName, attrName));
177 }
create(CSSPropertyID propertyID,const QualifiedName & attrName)178 static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& attrName)
179 {
180 return adoptPtr(new HTMLAttributeEquivalent(propertyID, attrName));
181 }
182
matches(Element * elem) const183 bool matches(Element* elem) const { return HTMLElementEquivalent::matches(elem) && elem->hasAttribute(m_attrName); }
hasAttribute() const184 virtual bool hasAttribute() const { return true; }
185 virtual bool valueIsPresentInStyle(Element*, CSSStyleDeclaration*) const;
186 virtual void addToStyle(Element*, EditingStyle*) const;
187 virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const;
attributeName() const188 inline const QualifiedName& attributeName() const { return m_attrName; }
189
190 protected:
191 HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& tagName, const QualifiedName& attrName);
192 HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& attrName);
193 const QualifiedName& m_attrName; // We can store a reference because HTML attribute names are const global.
194 };
195
HTMLAttributeEquivalent(CSSPropertyID id,const QualifiedName & tagName,const QualifiedName & attrName)196 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& tagName, const QualifiedName& attrName)
197 : HTMLElementEquivalent(id, tagName)
198 , m_attrName(attrName)
199 {
200 }
201
HTMLAttributeEquivalent(CSSPropertyID id,const QualifiedName & attrName)202 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& attrName)
203 : HTMLElementEquivalent(id)
204 , m_attrName(attrName)
205 {
206 }
207
valueIsPresentInStyle(Element * element,CSSStyleDeclaration * style) const208 bool HTMLAttributeEquivalent::valueIsPresentInStyle(Element* element, CSSStyleDeclaration* style) const
209 {
210 RefPtr<CSSValue> value = attributeValueAsCSSValue(element);
211 RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID);
212
213 // FIXME: This is very inefficient way of comparing values
214 // but we can't string compare attribute value and CSS property value.
215 return value && styleValue && value->cssText() == styleValue->cssText();
216 }
217
addToStyle(Element * element,EditingStyle * style) const218 void HTMLAttributeEquivalent::addToStyle(Element* element, EditingStyle* style) const
219 {
220 if (RefPtr<CSSValue> value = attributeValueAsCSSValue(element))
221 style->setProperty(m_propertyID, value->cssText());
222 }
223
attributeValueAsCSSValue(Element * element) const224 PassRefPtr<CSSValue> HTMLAttributeEquivalent::attributeValueAsCSSValue(Element* element) const
225 {
226 ASSERT(element);
227 if (!element->hasAttribute(m_attrName))
228 return 0;
229
230 RefPtr<CSSMutableStyleDeclaration> dummyStyle;
231 dummyStyle = CSSMutableStyleDeclaration::create();
232 dummyStyle->setProperty(m_propertyID, element->getAttribute(m_attrName));
233 return dummyStyle->getPropertyCSSValue(m_propertyID);
234 }
235
236 class HTMLFontSizeEquivalent : public HTMLAttributeEquivalent {
237 public:
create()238 static PassOwnPtr<HTMLFontSizeEquivalent> create()
239 {
240 return adoptPtr(new HTMLFontSizeEquivalent());
241 }
242 virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const;
243
244 private:
245 HTMLFontSizeEquivalent();
246 };
247
HTMLFontSizeEquivalent()248 HTMLFontSizeEquivalent::HTMLFontSizeEquivalent()
249 : HTMLAttributeEquivalent(CSSPropertyFontSize, HTMLNames::fontTag, HTMLNames::sizeAttr)
250 {
251 }
252
attributeValueAsCSSValue(Element * element) const253 PassRefPtr<CSSValue> HTMLFontSizeEquivalent::attributeValueAsCSSValue(Element* element) const
254 {
255 ASSERT(element);
256 if (!element->hasAttribute(m_attrName))
257 return 0;
258 int size;
259 if (!HTMLFontElement::cssValueFromFontSizeNumber(element->getAttribute(m_attrName), size))
260 return 0;
261 return CSSPrimitiveValue::createIdentifier(size);
262 }
263
264 float EditingStyle::NoFontDelta = 0.0f;
265
EditingStyle()266 EditingStyle::EditingStyle()
267 : m_shouldUseFixedDefaultFontSize(false)
268 , m_fontSizeDelta(NoFontDelta)
269 {
270 }
271
EditingStyle(Node * node,PropertiesToInclude propertiesToInclude)272 EditingStyle::EditingStyle(Node* node, PropertiesToInclude propertiesToInclude)
273 : m_shouldUseFixedDefaultFontSize(false)
274 , m_fontSizeDelta(NoFontDelta)
275 {
276 init(node, propertiesToInclude);
277 }
278
EditingStyle(const Position & position,PropertiesToInclude propertiesToInclude)279 EditingStyle::EditingStyle(const Position& position, PropertiesToInclude propertiesToInclude)
280 : m_shouldUseFixedDefaultFontSize(false)
281 , m_fontSizeDelta(NoFontDelta)
282 {
283 init(position.deprecatedNode(), propertiesToInclude);
284 }
285
EditingStyle(const CSSStyleDeclaration * style)286 EditingStyle::EditingStyle(const CSSStyleDeclaration* style)
287 : m_mutableStyle(style->copy())
288 , m_shouldUseFixedDefaultFontSize(false)
289 , m_fontSizeDelta(NoFontDelta)
290 {
291 extractFontSizeDelta();
292 }
293
EditingStyle(int propertyID,const String & value)294 EditingStyle::EditingStyle(int propertyID, const String& value)
295 : m_mutableStyle(0)
296 , m_shouldUseFixedDefaultFontSize(false)
297 , m_fontSizeDelta(NoFontDelta)
298 {
299 setProperty(propertyID, value);
300 }
301
~EditingStyle()302 EditingStyle::~EditingStyle()
303 {
304 }
305
cssValueToRGBA(CSSValue * colorValue)306 static RGBA32 cssValueToRGBA(CSSValue* colorValue)
307 {
308 if (!colorValue || !colorValue->isPrimitiveValue())
309 return Color::transparent;
310
311 CSSPrimitiveValue* primitiveColor = static_cast<CSSPrimitiveValue*>(colorValue);
312 if (primitiveColor->primitiveType() == CSSPrimitiveValue::CSS_RGBCOLOR)
313 return primitiveColor->getRGBA32Value();
314
315 RGBA32 rgba = 0;
316 CSSParser::parseColor(rgba, colorValue->cssText());
317 return rgba;
318 }
319
getRGBAFontColor(CSSStyleDeclaration * style)320 static inline RGBA32 getRGBAFontColor(CSSStyleDeclaration* style)
321 {
322 return cssValueToRGBA(style->getPropertyCSSValue(CSSPropertyColor).get());
323 }
324
rgbaBackgroundColorInEffect(Node * node)325 static inline RGBA32 rgbaBackgroundColorInEffect(Node* node)
326 {
327 return cssValueToRGBA(backgroundColorInEffect(node).get());
328 }
329
init(Node * node,PropertiesToInclude propertiesToInclude)330 void EditingStyle::init(Node* node, PropertiesToInclude propertiesToInclude)
331 {
332 if (isTabSpanTextNode(node))
333 node = tabSpanNode(node)->parentNode();
334 else if (isTabSpanNode(node))
335 node = node->parentNode();
336
337 RefPtr<CSSComputedStyleDeclaration> computedStyleAtPosition = computedStyle(node);
338 m_mutableStyle = propertiesToInclude == AllProperties && computedStyleAtPosition ? computedStyleAtPosition->copy() : editingStyleFromComputedStyle(computedStyleAtPosition);
339
340 if (propertiesToInclude == InheritablePropertiesAndBackgroundColorInEffect) {
341 if (RefPtr<CSSValue> value = backgroundColorInEffect(node))
342 m_mutableStyle->setProperty(CSSPropertyBackgroundColor, value->cssText());
343 }
344
345 if (node && node->computedStyle()) {
346 RenderStyle* renderStyle = node->computedStyle();
347 removeTextFillAndStrokeColorsIfNeeded(renderStyle);
348 replaceFontSizeByKeywordIfPossible(renderStyle, computedStyleAtPosition.get());
349 }
350
351 m_shouldUseFixedDefaultFontSize = computedStyleAtPosition->useFixedFontDefaultSize();
352 extractFontSizeDelta();
353 }
354
removeTextFillAndStrokeColorsIfNeeded(RenderStyle * renderStyle)355 void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyle)
356 {
357 // If a node's text fill color is invalid, then its children use
358 // their font-color as their text fill color (they don't
359 // inherit it). Likewise for stroke color.
360 ExceptionCode ec = 0;
361 if (!renderStyle->textFillColor().isValid())
362 m_mutableStyle->removeProperty(CSSPropertyWebkitTextFillColor, ec);
363 if (!renderStyle->textStrokeColor().isValid())
364 m_mutableStyle->removeProperty(CSSPropertyWebkitTextStrokeColor, ec);
365 ASSERT(!ec);
366 }
367
setProperty(int propertyID,const String & value,bool important)368 void EditingStyle::setProperty(int propertyID, const String& value, bool important)
369 {
370 if (!m_mutableStyle)
371 m_mutableStyle = CSSMutableStyleDeclaration::create();
372
373 ExceptionCode ec;
374 m_mutableStyle->setProperty(propertyID, value, important, ec);
375 }
376
replaceFontSizeByKeywordIfPossible(RenderStyle * renderStyle,CSSComputedStyleDeclaration * computedStyle)377 void EditingStyle::replaceFontSizeByKeywordIfPossible(RenderStyle* renderStyle, CSSComputedStyleDeclaration* computedStyle)
378 {
379 ASSERT(renderStyle);
380 if (renderStyle->fontDescription().keywordSize())
381 m_mutableStyle->setProperty(CSSPropertyFontSize, computedStyle->getFontSizeCSSValuePreferringKeyword()->cssText());
382 }
383
extractFontSizeDelta()384 void EditingStyle::extractFontSizeDelta()
385 {
386 if (m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize)) {
387 // Explicit font size overrides any delta.
388 m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
389 return;
390 }
391
392 // Get the adjustment amount out of the style.
393 RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta);
394 if (!value || value->cssValueType() != CSSValue::CSS_PRIMITIVE_VALUE)
395 return;
396
397 CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value.get());
398
399 // Only PX handled now. If we handle more types in the future, perhaps
400 // a switch statement here would be more appropriate.
401 if (primitiveValue->primitiveType() != CSSPrimitiveValue::CSS_PX)
402 return;
403
404 m_fontSizeDelta = primitiveValue->getFloatValue();
405 m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
406 }
407
isEmpty() const408 bool EditingStyle::isEmpty() const
409 {
410 return (!m_mutableStyle || m_mutableStyle->isEmpty()) && m_fontSizeDelta == NoFontDelta;
411 }
412
textDirection(WritingDirection & writingDirection) const413 bool EditingStyle::textDirection(WritingDirection& writingDirection) const
414 {
415 if (!m_mutableStyle)
416 return false;
417
418 RefPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
419 if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
420 return false;
421
422 int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
423 if (unicodeBidiValue == CSSValueEmbed) {
424 RefPtr<CSSValue> direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
425 if (!direction || !direction->isPrimitiveValue())
426 return false;
427
428 writingDirection = static_cast<CSSPrimitiveValue*>(direction.get())->getIdent() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
429
430 return true;
431 }
432
433 if (unicodeBidiValue == CSSValueNormal) {
434 writingDirection = NaturalWritingDirection;
435 return true;
436 }
437
438 return false;
439 }
440
setStyle(PassRefPtr<CSSMutableStyleDeclaration> style)441 void EditingStyle::setStyle(PassRefPtr<CSSMutableStyleDeclaration> style)
442 {
443 m_mutableStyle = style;
444 // FIXME: We should be able to figure out whether or not font is fixed width for mutable style.
445 // We need to check font-family is monospace as in FontDescription but we don't want to duplicate code here.
446 m_shouldUseFixedDefaultFontSize = false;
447 extractFontSizeDelta();
448 }
449
overrideWithStyle(const CSSMutableStyleDeclaration * style)450 void EditingStyle::overrideWithStyle(const CSSMutableStyleDeclaration* style)
451 {
452 if (!style || !style->length())
453 return;
454 if (!m_mutableStyle)
455 m_mutableStyle = CSSMutableStyleDeclaration::create();
456 m_mutableStyle->merge(style);
457 extractFontSizeDelta();
458 }
459
clear()460 void EditingStyle::clear()
461 {
462 m_mutableStyle.clear();
463 m_shouldUseFixedDefaultFontSize = false;
464 m_fontSizeDelta = NoFontDelta;
465 }
466
copy() const467 PassRefPtr<EditingStyle> EditingStyle::copy() const
468 {
469 RefPtr<EditingStyle> copy = EditingStyle::create();
470 if (m_mutableStyle)
471 copy->m_mutableStyle = m_mutableStyle->copy();
472 copy->m_shouldUseFixedDefaultFontSize = m_shouldUseFixedDefaultFontSize;
473 copy->m_fontSizeDelta = m_fontSizeDelta;
474 return copy;
475 }
476
extractAndRemoveBlockProperties()477 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveBlockProperties()
478 {
479 RefPtr<EditingStyle> blockProperties = EditingStyle::create();
480 if (!m_mutableStyle)
481 return blockProperties;
482
483 blockProperties->m_mutableStyle = m_mutableStyle->copyBlockProperties();
484 m_mutableStyle->removeBlockProperties();
485
486 return blockProperties;
487 }
488
extractAndRemoveTextDirection()489 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveTextDirection()
490 {
491 RefPtr<EditingStyle> textDirection = EditingStyle::create();
492 textDirection->m_mutableStyle = CSSMutableStyleDeclaration::create();
493 textDirection->m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed, m_mutableStyle->getPropertyPriority(CSSPropertyUnicodeBidi));
494 textDirection->m_mutableStyle->setProperty(CSSPropertyDirection, m_mutableStyle->getPropertyValue(CSSPropertyDirection),
495 m_mutableStyle->getPropertyPriority(CSSPropertyDirection));
496
497 m_mutableStyle->removeProperty(CSSPropertyUnicodeBidi);
498 m_mutableStyle->removeProperty(CSSPropertyDirection);
499
500 return textDirection;
501 }
502
removeBlockProperties()503 void EditingStyle::removeBlockProperties()
504 {
505 if (!m_mutableStyle)
506 return;
507
508 m_mutableStyle->removeBlockProperties();
509 }
510
removeStyleAddedByNode(Node * node)511 void EditingStyle::removeStyleAddedByNode(Node* node)
512 {
513 if (!node || !node->parentNode())
514 return;
515 RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode()));
516 RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node));
517 parentStyle->diff(nodeStyle.get());
518 nodeStyle->diff(m_mutableStyle.get());
519 }
520
removeStyleConflictingWithStyleOfNode(Node * node)521 void EditingStyle::removeStyleConflictingWithStyleOfNode(Node* node)
522 {
523 if (!node || !node->parentNode() || !m_mutableStyle)
524 return;
525 RefPtr<CSSMutableStyleDeclaration> parentStyle = editingStyleFromComputedStyle(computedStyle(node->parentNode()));
526 RefPtr<CSSMutableStyleDeclaration> nodeStyle = editingStyleFromComputedStyle(computedStyle(node));
527 parentStyle->diff(nodeStyle.get());
528
529 CSSMutableStyleDeclaration::const_iterator end = nodeStyle->end();
530 for (CSSMutableStyleDeclaration::const_iterator it = nodeStyle->begin(); it != end; ++it)
531 m_mutableStyle->removeProperty(it->id());
532 }
533
removeNonEditingProperties()534 void EditingStyle::removeNonEditingProperties()
535 {
536 if (m_mutableStyle)
537 m_mutableStyle = copyEditingProperties(m_mutableStyle.get());
538 }
539
collapseTextDecorationProperties()540 void EditingStyle::collapseTextDecorationProperties()
541 {
542 if (!m_mutableStyle)
543 return;
544
545 RefPtr<CSSValue> textDecorationsInEffect = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
546 if (!textDecorationsInEffect)
547 return;
548
549 m_mutableStyle->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText(), m_mutableStyle->getPropertyPriority(CSSPropertyTextDecoration));
550 m_mutableStyle->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
551 }
552
553 // CSS properties that create a visual difference only when applied to text.
554 static const int textOnlyProperties[] = {
555 CSSPropertyTextDecoration,
556 CSSPropertyWebkitTextDecorationsInEffect,
557 CSSPropertyFontStyle,
558 CSSPropertyFontWeight,
559 CSSPropertyColor,
560 };
561
triStateOfStyle(CSSStyleDeclaration * styleToCompare,ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const562 TriState EditingStyle::triStateOfStyle(CSSStyleDeclaration* styleToCompare, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const
563 {
564 // FIXME: take care of background-color in effect
565 RefPtr<CSSMutableStyleDeclaration> difference = getPropertiesNotIn(m_mutableStyle.get(), styleToCompare);
566
567 if (shouldIgnoreTextOnlyProperties == IgnoreTextOnlyProperties)
568 difference->removePropertiesInSet(textOnlyProperties, WTF_ARRAY_LENGTH(textOnlyProperties));
569
570 if (!difference->length())
571 return TrueTriState;
572 if (difference->length() == m_mutableStyle->length())
573 return FalseTriState;
574
575 return MixedTriState;
576 }
577
conflictsWithInlineStyleOfElement(StyledElement * element,EditingStyle * extractedStyle,Vector<CSSPropertyID> * conflictingProperties) const578 bool EditingStyle::conflictsWithInlineStyleOfElement(StyledElement* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const
579 {
580 ASSERT(element);
581 ASSERT(!conflictingProperties || conflictingProperties->isEmpty());
582
583 CSSMutableStyleDeclaration* inlineStyle = element->inlineStyleDecl();
584 if (!m_mutableStyle || !inlineStyle)
585 return false;
586
587 if (!conflictingProperties) {
588 CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end();
589 for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) {
590 CSSPropertyID propertyID = static_cast<CSSPropertyID>(it->id());
591
592 // We don't override whitespace property of a tab span because that would collapse the tab into a space.
593 if (propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element))
594 continue;
595
596 if (inlineStyle->getPropertyCSSValue(propertyID))
597 return true;
598 }
599
600 return false;
601 }
602
603 CSSMutableStyleDeclaration::const_iterator end = m_mutableStyle->end();
604 for (CSSMutableStyleDeclaration::const_iterator it = m_mutableStyle->begin(); it != end; ++it) {
605 CSSPropertyID propertyID = static_cast<CSSPropertyID>(it->id());
606 if ((propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element)) || !inlineStyle->getPropertyCSSValue(propertyID))
607 continue;
608
609 if (propertyID == CSSPropertyUnicodeBidi && inlineStyle->getPropertyCSSValue(CSSPropertyDirection)) {
610 if (extractedStyle)
611 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID));
612 conflictingProperties->append(CSSPropertyDirection);
613 }
614
615 conflictingProperties->append(propertyID);
616 if (extractedStyle)
617 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->getPropertyPriority(propertyID));
618 }
619
620 return !conflictingProperties->isEmpty();
621 }
622
conflictsWithImplicitStyleOfElement(HTMLElement * element,EditingStyle * extractedStyle,ShouldExtractMatchingStyle shouldExtractMatchingStyle) const623 bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
624 {
625 if (!m_mutableStyle)
626 return false;
627
628 static const HTMLElementEquivalent* HTMLEquivalents[] = {
629 HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::bTag).leakPtr(),
630 HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::strongTag).leakPtr(),
631 HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSub, HTMLNames::subTag).leakPtr(),
632 HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSuper, HTMLNames::supTag).leakPtr(),
633 HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::iTag).leakPtr(),
634 HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::emTag).leakPtr(),
635
636 HTMLTextDecorationEquivalent::create(CSSValueUnderline, HTMLNames::uTag).leakPtr(),
637 HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::sTag).leakPtr(),
638 HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::strikeTag).leakPtr(),
639 };
640
641 for (size_t i = 0; i < WTF_ARRAY_LENGTH(HTMLEquivalents); ++i) {
642 const HTMLElementEquivalent* equivalent = HTMLEquivalents[i];
643 if (equivalent->matches(element) && equivalent->propertyExistsInStyle(m_mutableStyle.get())
644 && (shouldExtractMatchingStyle == ExtractMatchingStyle || !equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) {
645 if (extractedStyle)
646 equivalent->addToStyle(element, extractedStyle);
647 return true;
648 }
649 }
650 return false;
651 }
652
htmlAttributeEquivalents()653 static const Vector<OwnPtr<HTMLAttributeEquivalent> >& htmlAttributeEquivalents()
654 {
655 DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLAttributeEquivalent> >, HTMLAttributeEquivalents, ());
656
657 if (!HTMLAttributeEquivalents.size()) {
658 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyColor, HTMLNames::fontTag, HTMLNames::colorAttr));
659 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyFontFamily, HTMLNames::fontTag, HTMLNames::faceAttr));
660 HTMLAttributeEquivalents.append(HTMLFontSizeEquivalent::create());
661
662 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyDirection, HTMLNames::dirAttr));
663 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyUnicodeBidi, HTMLNames::dirAttr));
664 }
665
666 return HTMLAttributeEquivalents;
667 }
668
conflictsWithImplicitStyleOfAttributes(HTMLElement * element) const669 bool EditingStyle::conflictsWithImplicitStyleOfAttributes(HTMLElement* element) const
670 {
671 ASSERT(element);
672 if (!m_mutableStyle)
673 return false;
674
675 const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
676 for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
677 if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->propertyExistsInStyle(m_mutableStyle.get())
678 && !HTMLAttributeEquivalents[i]->valueIsPresentInStyle(element, m_mutableStyle.get()))
679 return true;
680 }
681
682 return false;
683 }
684
extractConflictingImplicitStyleOfAttributes(HTMLElement * element,ShouldPreserveWritingDirection shouldPreserveWritingDirection,EditingStyle * extractedStyle,Vector<QualifiedName> & conflictingAttributes,ShouldExtractMatchingStyle shouldExtractMatchingStyle) const685 bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* element, ShouldPreserveWritingDirection shouldPreserveWritingDirection,
686 EditingStyle* extractedStyle, Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
687 {
688 ASSERT(element);
689 // HTMLAttributeEquivalent::addToStyle doesn't support unicode-bidi and direction properties
690 ASSERT(!extractedStyle || shouldPreserveWritingDirection == PreserveWritingDirection);
691 if (!m_mutableStyle)
692 return false;
693
694 const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
695 bool removed = false;
696 for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
697 const HTMLAttributeEquivalent* equivalent = HTMLAttributeEquivalents[i].get();
698
699 // unicode-bidi and direction are pushed down separately so don't push down with other styles.
700 if (shouldPreserveWritingDirection == PreserveWritingDirection && equivalent->attributeName() == HTMLNames::dirAttr)
701 continue;
702
703 if (!equivalent->matches(element) || !equivalent->propertyExistsInStyle(m_mutableStyle.get())
704 || (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(element, m_mutableStyle.get())))
705 continue;
706
707 if (extractedStyle)
708 equivalent->addToStyle(element, extractedStyle);
709 conflictingAttributes.append(equivalent->attributeName());
710 removed = true;
711 }
712
713 return removed;
714 }
715
styleIsPresentInComputedStyleOfNode(Node * node) const716 bool EditingStyle::styleIsPresentInComputedStyleOfNode(Node* node) const
717 {
718 return !m_mutableStyle || !getPropertiesNotIn(m_mutableStyle.get(), computedStyle(node).get())->length();
719 }
720
prepareToApplyAt(const Position & position,ShouldPreserveWritingDirection shouldPreserveWritingDirection)721 void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWritingDirection shouldPreserveWritingDirection)
722 {
723 if (!m_mutableStyle)
724 return;
725
726 // ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style.
727 // If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate
728 // which one of editingStyleAtPosition or computedStyle is called.
729 RefPtr<EditingStyle> style = EditingStyle::create(position, InheritablePropertiesAndBackgroundColorInEffect);
730
731 RefPtr<CSSValue> unicodeBidi;
732 RefPtr<CSSValue> direction;
733 if (shouldPreserveWritingDirection == PreserveWritingDirection) {
734 unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
735 direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
736 }
737
738 style->m_mutableStyle->diff(m_mutableStyle.get());
739 if (getRGBAFontColor(m_mutableStyle.get()) == getRGBAFontColor(style->m_mutableStyle.get()))
740 m_mutableStyle->removeProperty(CSSPropertyColor);
741
742 if (hasTransparentBackgroundColor(m_mutableStyle.get())
743 || cssValueToRGBA(m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor).get()) == rgbaBackgroundColorInEffect(position.containerNode()))
744 m_mutableStyle->removeProperty(CSSPropertyBackgroundColor);
745
746 if (unicodeBidi && unicodeBidi->isPrimitiveValue()) {
747 m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent());
748 if (direction && direction->isPrimitiveValue())
749 m_mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent());
750 }
751 }
752
mergeTypingStyle(Document * document)753 void EditingStyle::mergeTypingStyle(Document* document)
754 {
755 ASSERT(document);
756
757 RefPtr<EditingStyle> typingStyle = document->frame()->selection()->typingStyle();
758 if (!typingStyle || typingStyle == this)
759 return;
760
761 mergeStyle(typingStyle->style());
762 }
763
mergeInlineStyleOfElement(StyledElement * element)764 void EditingStyle::mergeInlineStyleOfElement(StyledElement* element)
765 {
766 ASSERT(element);
767 mergeStyle(element->inlineStyleDecl());
768 }
769
mergeStyle(CSSMutableStyleDeclaration * style)770 void EditingStyle::mergeStyle(CSSMutableStyleDeclaration* style)
771 {
772 if (!style)
773 return;
774
775 if (!m_mutableStyle) {
776 m_mutableStyle = style->copy();
777 return;
778 }
779
780 CSSMutableStyleDeclaration::const_iterator end = style->end();
781 for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
782 RefPtr<CSSValue> value;
783 if ((it->id() == CSSPropertyTextDecoration || it->id() == CSSPropertyWebkitTextDecorationsInEffect) && it->value()->isValueList()) {
784 value = m_mutableStyle->getPropertyCSSValue(it->id());
785 if (value && !value->isValueList())
786 value = 0;
787 }
788
789 if (!value) {
790 ExceptionCode ec;
791 m_mutableStyle->setProperty(it->id(), it->value()->cssText(), it->isImportant(), ec);
792 continue;
793 }
794
795 CSSValueList* newTextDecorations = static_cast<CSSValueList*>(it->value());
796 CSSValueList* textDecorations = static_cast<CSSValueList*>(value.get());
797
798 DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
799 DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
800
801 if (newTextDecorations->hasValue(underline.get()) && !textDecorations->hasValue(underline.get()))
802 textDecorations->append(underline.get());
803
804 if (newTextDecorations->hasValue(lineThrough.get()) && !textDecorations->hasValue(lineThrough.get()))
805 textDecorations->append(lineThrough.get());
806 }
807 }
808
reconcileTextDecorationProperties(CSSMutableStyleDeclaration * style)809 static void reconcileTextDecorationProperties(CSSMutableStyleDeclaration* style)
810 {
811 RefPtr<CSSValue> textDecorationsInEffect = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
812 RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration);
813 // We shouldn't have both text-decoration and -webkit-text-decorations-in-effect because that wouldn't make sense.
814 ASSERT(!textDecorationsInEffect || !textDecoration);
815 if (textDecorationsInEffect) {
816 style->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText());
817 style->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
818 textDecoration = textDecorationsInEffect;
819 }
820
821 // If text-decoration is set to "none", remove the property because we don't want to add redundant "text-decoration: none".
822 if (textDecoration && !textDecoration->isValueList())
823 style->removeProperty(CSSPropertyTextDecoration);
824 }
825
StyleChange(EditingStyle * style,const Position & position)826 StyleChange::StyleChange(EditingStyle* style, const Position& position)
827 : m_applyBold(false)
828 , m_applyItalic(false)
829 , m_applyUnderline(false)
830 , m_applyLineThrough(false)
831 , m_applySubscript(false)
832 , m_applySuperscript(false)
833 {
834 Document* document = position.anchorNode() ? position.anchorNode()->document() : 0;
835 if (!style || !style->style() || !document || !document->frame())
836 return;
837
838 RefPtr<CSSComputedStyleDeclaration> computedStyle = position.computedStyle();
839 // FIXME: take care of background-color in effect
840 RefPtr<CSSMutableStyleDeclaration> mutableStyle = getPropertiesNotIn(style->style(), computedStyle.get());
841
842 reconcileTextDecorationProperties(mutableStyle.get());
843 if (!document->frame()->editor()->shouldStyleWithCSS())
844 extractTextStyles(document, mutableStyle.get(), computedStyle->useFixedFontDefaultSize());
845
846 // Changing the whitespace style in a tab span would collapse the tab into a space.
847 if (isTabSpanTextNode(position.deprecatedNode()) || isTabSpanNode((position.deprecatedNode())))
848 mutableStyle->removeProperty(CSSPropertyWhiteSpace);
849
850 // If unicode-bidi is present in mutableStyle and direction is not, then add direction to mutableStyle.
851 // FIXME: Shouldn't this be done in getPropertiesNotIn?
852 if (mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi) && !style->style()->getPropertyCSSValue(CSSPropertyDirection))
853 mutableStyle->setProperty(CSSPropertyDirection, style->style()->getPropertyValue(CSSPropertyDirection));
854
855 // Save the result for later
856 m_cssStyle = mutableStyle->cssText().stripWhiteSpace();
857 }
858
setTextDecorationProperty(CSSMutableStyleDeclaration * style,const CSSValueList * newTextDecoration,int propertyID)859 static void setTextDecorationProperty(CSSMutableStyleDeclaration* style, const CSSValueList* newTextDecoration, int propertyID)
860 {
861 if (newTextDecoration->length())
862 style->setProperty(propertyID, newTextDecoration->cssText(), style->getPropertyPriority(propertyID));
863 else {
864 // text-decoration: none is redundant since it does not remove any text decorations.
865 ASSERT(!style->getPropertyPriority(propertyID));
866 style->removeProperty(propertyID);
867 }
868 }
869
extractTextStyles(Document * document,CSSMutableStyleDeclaration * style,bool shouldUseFixedFontDefaultSize)870 void StyleChange::extractTextStyles(Document* document, CSSMutableStyleDeclaration* style, bool shouldUseFixedFontDefaultSize)
871 {
872 ASSERT(style);
873
874 if (getIdentifierValue(style, CSSPropertyFontWeight) == CSSValueBold) {
875 style->removeProperty(CSSPropertyFontWeight);
876 m_applyBold = true;
877 }
878
879 int fontStyle = getIdentifierValue(style, CSSPropertyFontStyle);
880 if (fontStyle == CSSValueItalic || fontStyle == CSSValueOblique) {
881 style->removeProperty(CSSPropertyFontStyle);
882 m_applyItalic = true;
883 }
884
885 // Assuming reconcileTextDecorationProperties has been called, there should not be -webkit-text-decorations-in-effect
886 // Furthermore, text-decoration: none has been trimmed so that text-decoration property is always a CSSValueList.
887 RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration);
888 if (textDecoration && textDecoration->isValueList()) {
889 DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
890 DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
891
892 RefPtr<CSSValueList> newTextDecoration = static_cast<CSSValueList*>(textDecoration.get())->copy();
893 if (newTextDecoration->removeAll(underline.get()))
894 m_applyUnderline = true;
895 if (newTextDecoration->removeAll(lineThrough.get()))
896 m_applyLineThrough = true;
897
898 // If trimTextDecorations, delete underline and line-through
899 setTextDecorationProperty(style, newTextDecoration.get(), CSSPropertyTextDecoration);
900 }
901
902 int verticalAlign = getIdentifierValue(style, CSSPropertyVerticalAlign);
903 switch (verticalAlign) {
904 case CSSValueSub:
905 style->removeProperty(CSSPropertyVerticalAlign);
906 m_applySubscript = true;
907 break;
908 case CSSValueSuper:
909 style->removeProperty(CSSPropertyVerticalAlign);
910 m_applySuperscript = true;
911 break;
912 }
913
914 if (style->getPropertyCSSValue(CSSPropertyColor)) {
915 m_applyFontColor = Color(getRGBAFontColor(style)).serialized();
916 style->removeProperty(CSSPropertyColor);
917 }
918
919 m_applyFontFace = style->getPropertyValue(CSSPropertyFontFamily);
920 style->removeProperty(CSSPropertyFontFamily);
921
922 if (RefPtr<CSSValue> fontSize = style->getPropertyCSSValue(CSSPropertyFontSize)) {
923 if (!fontSize->isPrimitiveValue())
924 style->removeProperty(CSSPropertyFontSize); // Can't make sense of the number. Put no font size.
925 else if (int legacyFontSize = legacyFontSizeFromCSSValue(document, static_cast<CSSPrimitiveValue*>(fontSize.get()),
926 shouldUseFixedFontDefaultSize, UseLegacyFontSizeOnlyIfPixelValuesMatch)) {
927 m_applyFontSize = String::number(legacyFontSize);
928 style->removeProperty(CSSPropertyFontSize);
929 }
930 }
931 }
932
diffTextDecorations(CSSMutableStyleDeclaration * style,int propertID,CSSValue * refTextDecoration)933 static void diffTextDecorations(CSSMutableStyleDeclaration* style, int propertID, CSSValue* refTextDecoration)
934 {
935 RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(propertID);
936 if (!textDecoration || !textDecoration->isValueList() || !refTextDecoration || !refTextDecoration->isValueList())
937 return;
938
939 RefPtr<CSSValueList> newTextDecoration = static_cast<CSSValueList*>(textDecoration.get())->copy();
940 CSSValueList* valuesInRefTextDecoration = static_cast<CSSValueList*>(refTextDecoration);
941
942 for (size_t i = 0; i < valuesInRefTextDecoration->length(); i++)
943 newTextDecoration->removeAll(valuesInRefTextDecoration->item(i));
944
945 setTextDecorationProperty(style, newTextDecoration.get(), propertID);
946 }
947
fontWeightIsBold(CSSStyleDeclaration * style)948 static bool fontWeightIsBold(CSSStyleDeclaration* style)
949 {
950 ASSERT(style);
951 RefPtr<CSSValue> fontWeight = style->getPropertyCSSValue(CSSPropertyFontWeight);
952
953 if (!fontWeight)
954 return false;
955 if (!fontWeight->isPrimitiveValue())
956 return false;
957
958 // Because b tag can only bold text, there are only two states in plain html: bold and not bold.
959 // Collapse all other values to either one of these two states for editing purposes.
960 switch (static_cast<CSSPrimitiveValue*>(fontWeight.get())->getIdent()) {
961 case CSSValue100:
962 case CSSValue200:
963 case CSSValue300:
964 case CSSValue400:
965 case CSSValue500:
966 case CSSValueNormal:
967 return false;
968 case CSSValueBold:
969 case CSSValue600:
970 case CSSValue700:
971 case CSSValue800:
972 case CSSValue900:
973 return true;
974 }
975
976 ASSERT_NOT_REACHED(); // For CSSValueBolder and CSSValueLighter
977 return false; // Make compiler happy
978 }
979
getTextAlignment(CSSStyleDeclaration * style)980 static int getTextAlignment(CSSStyleDeclaration* style)
981 {
982 int textAlign = getIdentifierValue(style, CSSPropertyTextAlign);
983 switch (textAlign) {
984 case CSSValueCenter:
985 case CSSValueWebkitCenter:
986 return CSSValueCenter;
987 case CSSValueJustify:
988 return CSSValueJustify;
989 case CSSValueLeft:
990 case CSSValueWebkitLeft:
991 return CSSValueLeft;
992 case CSSValueRight:
993 case CSSValueWebkitRight:
994 return CSSValueRight;
995 }
996 return CSSValueInvalid;
997 }
998
getPropertiesNotIn(CSSStyleDeclaration * styleWithRedundantProperties,CSSStyleDeclaration * baseStyle)999 RefPtr<CSSMutableStyleDeclaration> getPropertiesNotIn(CSSStyleDeclaration* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle)
1000 {
1001 ASSERT(styleWithRedundantProperties);
1002 ASSERT(baseStyle);
1003 RefPtr<CSSMutableStyleDeclaration> result = styleWithRedundantProperties->copy();
1004
1005 baseStyle->diff(result.get());
1006
1007 RefPtr<CSSValue> baseTextDecorationsInEffect = baseStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
1008 diffTextDecorations(result.get(), CSSPropertyTextDecoration, baseTextDecorationsInEffect.get());
1009 diffTextDecorations(result.get(), CSSPropertyWebkitTextDecorationsInEffect, baseTextDecorationsInEffect.get());
1010
1011 if (fontWeightIsBold(result.get()) == fontWeightIsBold(baseStyle))
1012 result->removeProperty(CSSPropertyFontWeight);
1013
1014 if (getRGBAFontColor(result.get()) == getRGBAFontColor(baseStyle))
1015 result->removeProperty(CSSPropertyColor);
1016
1017 if (getTextAlignment(result.get()) == getTextAlignment(baseStyle))
1018 result->removeProperty(CSSPropertyTextAlign);
1019
1020 return result;
1021 }
1022
getIdentifierValue(CSSStyleDeclaration * style,int propertyID)1023 int getIdentifierValue(CSSStyleDeclaration* style, int propertyID)
1024 {
1025 if (!style)
1026 return 0;
1027
1028 RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID);
1029 if (!value || !value->isPrimitiveValue())
1030 return 0;
1031
1032 return static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
1033 }
1034
isCSSValueLength(CSSPrimitiveValue * value)1035 static bool isCSSValueLength(CSSPrimitiveValue* value)
1036 {
1037 return value->primitiveType() >= CSSPrimitiveValue::CSS_PX && value->primitiveType() <= CSSPrimitiveValue::CSS_PC;
1038 }
1039
legacyFontSizeFromCSSValue(Document * document,CSSPrimitiveValue * value,bool shouldUseFixedFontDefaultSize,LegacyFontSizeMode mode)1040 int legacyFontSizeFromCSSValue(Document* document, CSSPrimitiveValue* value, bool shouldUseFixedFontDefaultSize, LegacyFontSizeMode mode)
1041 {
1042 if (isCSSValueLength(value)) {
1043 int pixelFontSize = value->getIntValue(CSSPrimitiveValue::CSS_PX);
1044 int legacyFontSize = CSSStyleSelector::legacyFontSize(document, pixelFontSize, shouldUseFixedFontDefaultSize);
1045 // Use legacy font size only if pixel value matches exactly to that of legacy font size.
1046 int cssPrimitiveEquivalent = legacyFontSize - 1 + CSSValueXSmall;
1047 if (mode == AlwaysUseLegacyFontSize || CSSStyleSelector::fontSizeForKeyword(document, cssPrimitiveEquivalent, shouldUseFixedFontDefaultSize) == pixelFontSize)
1048 return legacyFontSize;
1049
1050 return 0;
1051 }
1052
1053 if (CSSValueXSmall <= value->getIdent() && value->getIdent() <= CSSValueWebkitXxxLarge)
1054 return value->getIdent() - CSSValueXSmall + 1;
1055
1056 return 0;
1057 }
1058
hasTransparentBackgroundColor(CSSStyleDeclaration * style)1059 bool hasTransparentBackgroundColor(CSSStyleDeclaration* style)
1060 {
1061 RefPtr<CSSValue> cssValue = style->getPropertyCSSValue(CSSPropertyBackgroundColor);
1062 if (!cssValue)
1063 return true;
1064
1065 if (!cssValue->isPrimitiveValue())
1066 return false;
1067 CSSPrimitiveValue* value = static_cast<CSSPrimitiveValue*>(cssValue.get());
1068
1069 if (value->primitiveType() == CSSPrimitiveValue::CSS_RGBCOLOR)
1070 return !alphaChannel(value->getRGBA32Value());
1071
1072 return value->getIdent() == CSSValueTransparent;
1073 }
1074
backgroundColorInEffect(Node * node)1075 PassRefPtr<CSSValue> backgroundColorInEffect(Node* node)
1076 {
1077 for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) {
1078 RefPtr<CSSComputedStyleDeclaration> ancestorStyle = computedStyle(ancestor);
1079 if (!hasTransparentBackgroundColor(ancestorStyle.get()))
1080 return ancestorStyle->getPropertyCSSValue(CSSPropertyBackgroundColor);
1081 }
1082 return 0;
1083 }
1084
1085 }
1086