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