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