• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007, 2008, 2009 Apple Computer, Inc.
3  * Copyright (C) 2010, 2011 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 "core/editing/EditingStyle.h"
29 
30 #include "HTMLNames.h"
31 #include "bindings/v8/ExceptionStatePlaceholder.h"
32 #include "core/css/CSSComputedStyleDeclaration.h"
33 #include "core/css/CSSParser.h"
34 #include "core/css/CSSRuleList.h"
35 #include "core/css/CSSStyleRule.h"
36 #include "core/css/CSSValueList.h"
37 #include "core/css/FontSize.h"
38 #include "core/css/RuntimeCSSEnabled.h"
39 #include "core/css/StylePropertySet.h"
40 #include "core/css/StyleRule.h"
41 #include "core/css/resolver/StyleResolver.h"
42 #include "core/dom/Element.h"
43 #include "core/dom/Node.h"
44 #include "core/dom/NodeTraversal.h"
45 #include "core/dom/Position.h"
46 #include "core/dom/QualifiedName.h"
47 #include "core/editing/ApplyStyleCommand.h"
48 #include "core/editing/Editor.h"
49 #include "core/editing/FrameSelection.h"
50 #include "core/editing/HTMLInterchange.h"
51 #include "core/editing/htmlediting.h"
52 #include "core/frame/Frame.h"
53 #include "core/html/HTMLFontElement.h"
54 #include "core/rendering/style/RenderStyle.h"
55 
56 namespace WebCore {
57 
textDecorationPropertyForEditing()58 static const CSSPropertyID& textDecorationPropertyForEditing()
59 {
60     static const CSSPropertyID property = RuntimeEnabledFeatures::css3TextDecorationsEnabled() ? CSSPropertyTextDecorationLine : CSSPropertyTextDecoration;
61     return property;
62 }
63 
64 // Editing style properties must be preserved during editing operation.
65 // e.g. when a user inserts a new paragraph, all properties listed here must be copied to the new paragraph.
66 // NOTE: Use either allEditingProperties() or inheritableEditingProperties() to
67 // respect runtime enabling of properties.
68 static const CSSPropertyID staticEditingProperties[] = {
69     CSSPropertyBackgroundColor,
70     CSSPropertyColor,
71     CSSPropertyFontFamily,
72     CSSPropertyFontSize,
73     CSSPropertyFontStyle,
74     CSSPropertyFontVariant,
75     CSSPropertyFontWeight,
76     CSSPropertyLetterSpacing,
77     CSSPropertyLineHeight,
78     CSSPropertyOrphans,
79     CSSPropertyTextAlign,
80     // FIXME: CSSPropertyTextDecoration needs to be removed when CSS3 Text
81     // Decoration feature is no longer experimental.
82     CSSPropertyTextDecoration,
83     CSSPropertyTextDecorationLine,
84     CSSPropertyTextIndent,
85     CSSPropertyTextTransform,
86     CSSPropertyWhiteSpace,
87     CSSPropertyWidows,
88     CSSPropertyWordSpacing,
89     CSSPropertyWebkitTextDecorationsInEffect,
90     CSSPropertyWebkitTextFillColor,
91     CSSPropertyWebkitTextStrokeColor,
92     CSSPropertyWebkitTextStrokeWidth,
93 };
94 
95 enum EditingPropertiesType { OnlyInheritableEditingProperties, AllEditingProperties };
96 
allEditingProperties()97 static const Vector<CSSPropertyID>& allEditingProperties()
98 {
99     DEFINE_STATIC_LOCAL(Vector<CSSPropertyID>, properties, ());
100     if (properties.isEmpty()) {
101         RuntimeCSSEnabled::filterEnabledCSSPropertiesIntoVector(staticEditingProperties, WTF_ARRAY_LENGTH(staticEditingProperties), properties);
102         if (RuntimeEnabledFeatures::css3TextDecorationsEnabled())
103             properties.remove(properties.find(CSSPropertyTextDecoration));
104     }
105     return properties;
106 }
107 
inheritableEditingProperties()108 static const Vector<CSSPropertyID>& inheritableEditingProperties()
109 {
110     DEFINE_STATIC_LOCAL(Vector<CSSPropertyID>, properties, ());
111     if (properties.isEmpty()) {
112         RuntimeCSSEnabled::filterEnabledCSSPropertiesIntoVector(staticEditingProperties, WTF_ARRAY_LENGTH(staticEditingProperties), properties);
113         for (size_t index = 0; index < properties.size();) {
114             if (!CSSProperty::isInheritedProperty(properties[index])) {
115                 properties.remove(index);
116                 continue;
117             }
118             ++index;
119         }
120     }
121     return properties;
122 }
123 
124 template <class StyleDeclarationType>
copyEditingProperties(StyleDeclarationType * style,EditingPropertiesType type=OnlyInheritableEditingProperties)125 static PassRefPtr<MutableStylePropertySet> copyEditingProperties(StyleDeclarationType* style, EditingPropertiesType type = OnlyInheritableEditingProperties)
126 {
127     if (type == AllEditingProperties)
128         return style->copyPropertiesInSet(allEditingProperties());
129     return style->copyPropertiesInSet(inheritableEditingProperties());
130 }
131 
isEditingProperty(int id)132 static inline bool isEditingProperty(int id)
133 {
134     return allEditingProperties().contains(static_cast<CSSPropertyID>(id));
135 }
136 
editingStyleFromComputedStyle(PassRefPtr<CSSComputedStyleDeclaration> style,EditingPropertiesType type=OnlyInheritableEditingProperties)137 static PassRefPtr<MutableStylePropertySet> editingStyleFromComputedStyle(PassRefPtr<CSSComputedStyleDeclaration> style, EditingPropertiesType type = OnlyInheritableEditingProperties)
138 {
139     if (!style)
140         return MutableStylePropertySet::create();
141     return copyEditingProperties(style.get(), type);
142 }
143 
144 static PassRefPtr<MutableStylePropertySet> getPropertiesNotIn(StylePropertySet* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle);
145 enum LegacyFontSizeMode { AlwaysUseLegacyFontSize, UseLegacyFontSizeOnlyIfPixelValuesMatch };
146 static int legacyFontSizeFromCSSValue(Document*, CSSPrimitiveValue*, bool shouldUseFixedFontDefaultSize, LegacyFontSizeMode);
147 static bool isTransparentColorValue(CSSValue*);
148 static bool hasTransparentBackgroundColor(CSSStyleDeclaration*);
149 static bool hasTransparentBackgroundColor(StylePropertySet*);
150 static PassRefPtr<CSSValue> backgroundColorInEffect(Node*);
151 
152 class HTMLElementEquivalent {
153     WTF_MAKE_FAST_ALLOCATED;
154 public:
create(CSSPropertyID propertyID,CSSValueID primitiveValue,const QualifiedName & tagName)155     static PassOwnPtr<HTMLElementEquivalent> create(CSSPropertyID propertyID, CSSValueID primitiveValue, const QualifiedName& tagName)
156     {
157         return adoptPtr(new HTMLElementEquivalent(propertyID, primitiveValue, tagName));
158     }
159 
~HTMLElementEquivalent()160     virtual ~HTMLElementEquivalent() { }
matches(const Element * element) const161     virtual bool matches(const Element* element) const { return !m_tagName || element->hasTagName(*m_tagName); }
hasAttribute() const162     virtual bool hasAttribute() const { return false; }
propertyExistsInStyle(const StylePropertySet * style) const163     virtual bool propertyExistsInStyle(const StylePropertySet* style) const { return style->getPropertyCSSValue(m_propertyID); }
164     virtual bool valueIsPresentInStyle(Element*, StylePropertySet*) const;
165     virtual void addToStyle(Element*, EditingStyle*) const;
166 
167 protected:
168     HTMLElementEquivalent(CSSPropertyID);
169     HTMLElementEquivalent(CSSPropertyID, const QualifiedName& tagName);
170     HTMLElementEquivalent(CSSPropertyID, CSSValueID primitiveValue, const QualifiedName& tagName);
171     const CSSPropertyID m_propertyID;
172     const RefPtr<CSSPrimitiveValue> m_primitiveValue;
173     const QualifiedName* m_tagName; // We can store a pointer because HTML tag names are const global.
174 };
175 
HTMLElementEquivalent(CSSPropertyID id)176 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id)
177     : m_propertyID(id)
178     , m_tagName(0)
179 {
180 }
181 
HTMLElementEquivalent(CSSPropertyID id,const QualifiedName & tagName)182 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, const QualifiedName& tagName)
183     : m_propertyID(id)
184     , m_tagName(&tagName)
185 {
186 }
187 
HTMLElementEquivalent(CSSPropertyID id,CSSValueID primitiveValue,const QualifiedName & tagName)188 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, CSSValueID primitiveValue, const QualifiedName& tagName)
189     : m_propertyID(id)
190     , m_primitiveValue(CSSPrimitiveValue::createIdentifier(primitiveValue))
191     , m_tagName(&tagName)
192 {
193     ASSERT(primitiveValue != CSSValueInvalid);
194 }
195 
valueIsPresentInStyle(Element * element,StylePropertySet * style) const196 bool HTMLElementEquivalent::valueIsPresentInStyle(Element* element, StylePropertySet* style) const
197 {
198     RefPtr<CSSValue> value = style->getPropertyCSSValue(m_propertyID);
199     return matches(element) && value && value->isPrimitiveValue() && toCSSPrimitiveValue(value.get())->getValueID() == m_primitiveValue->getValueID();
200 }
201 
addToStyle(Element *,EditingStyle * style) const202 void HTMLElementEquivalent::addToStyle(Element*, EditingStyle* style) const
203 {
204     style->setProperty(m_propertyID, m_primitiveValue->cssText());
205 }
206 
207 class HTMLTextDecorationEquivalent : public HTMLElementEquivalent {
208 public:
create(CSSValueID primitiveValue,const QualifiedName & tagName)209     static PassOwnPtr<HTMLElementEquivalent> create(CSSValueID primitiveValue, const QualifiedName& tagName)
210     {
211         return adoptPtr(new HTMLTextDecorationEquivalent(primitiveValue, tagName));
212     }
213     virtual bool propertyExistsInStyle(const StylePropertySet*) const;
214     virtual bool valueIsPresentInStyle(Element*, StylePropertySet*) const;
215 
216 private:
217     HTMLTextDecorationEquivalent(CSSValueID primitiveValue, const QualifiedName& tagName);
218 };
219 
HTMLTextDecorationEquivalent(CSSValueID primitiveValue,const QualifiedName & tagName)220 HTMLTextDecorationEquivalent::HTMLTextDecorationEquivalent(CSSValueID primitiveValue, const QualifiedName& tagName)
221     : HTMLElementEquivalent(textDecorationPropertyForEditing(), primitiveValue, tagName)
222     // m_propertyID is used in HTMLElementEquivalent::addToStyle
223 {
224 }
225 
propertyExistsInStyle(const StylePropertySet * style) const226 bool HTMLTextDecorationEquivalent::propertyExistsInStyle(const StylePropertySet* style) const
227 {
228     return style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect)
229         || style->getPropertyCSSValue(textDecorationPropertyForEditing());
230 }
231 
valueIsPresentInStyle(Element * element,StylePropertySet * style) const232 bool HTMLTextDecorationEquivalent::valueIsPresentInStyle(Element* element, StylePropertySet* style) const
233 {
234     RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
235     if (!styleValue)
236         styleValue = style->getPropertyCSSValue(textDecorationPropertyForEditing());
237     return matches(element) && styleValue && styleValue->isValueList() && toCSSValueList(styleValue.get())->hasValue(m_primitiveValue.get());
238 }
239 
240 class HTMLAttributeEquivalent : public HTMLElementEquivalent {
241 public:
create(CSSPropertyID propertyID,const QualifiedName & tagName,const QualifiedName & attrName)242     static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& tagName, const QualifiedName& attrName)
243     {
244         return adoptPtr(new HTMLAttributeEquivalent(propertyID, tagName, attrName));
245     }
create(CSSPropertyID propertyID,const QualifiedName & attrName)246     static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& attrName)
247     {
248         return adoptPtr(new HTMLAttributeEquivalent(propertyID, attrName));
249     }
250 
matches(const Element * elem) const251     bool matches(const Element* elem) const { return HTMLElementEquivalent::matches(elem) && elem->hasAttribute(m_attrName); }
hasAttribute() const252     virtual bool hasAttribute() const { return true; }
253     virtual bool valueIsPresentInStyle(Element*, StylePropertySet*) const;
254     virtual void addToStyle(Element*, EditingStyle*) const;
255     virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const;
attributeName() const256     inline const QualifiedName& attributeName() const { return m_attrName; }
257 
258 protected:
259     HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& tagName, const QualifiedName& attrName);
260     HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& attrName);
261     const QualifiedName& m_attrName; // We can store a reference because HTML attribute names are const global.
262 };
263 
HTMLAttributeEquivalent(CSSPropertyID id,const QualifiedName & tagName,const QualifiedName & attrName)264 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& tagName, const QualifiedName& attrName)
265     : HTMLElementEquivalent(id, tagName)
266     , m_attrName(attrName)
267 {
268 }
269 
HTMLAttributeEquivalent(CSSPropertyID id,const QualifiedName & attrName)270 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& attrName)
271     : HTMLElementEquivalent(id)
272     , m_attrName(attrName)
273 {
274 }
275 
valueIsPresentInStyle(Element * element,StylePropertySet * style) const276 bool HTMLAttributeEquivalent::valueIsPresentInStyle(Element* element, StylePropertySet* style) const
277 {
278     RefPtr<CSSValue> value = attributeValueAsCSSValue(element);
279     RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID);
280 
281     return compareCSSValuePtr(value, styleValue);
282 }
283 
addToStyle(Element * element,EditingStyle * style) const284 void HTMLAttributeEquivalent::addToStyle(Element* element, EditingStyle* style) const
285 {
286     if (RefPtr<CSSValue> value = attributeValueAsCSSValue(element))
287         style->setProperty(m_propertyID, value->cssText());
288 }
289 
attributeValueAsCSSValue(Element * element) const290 PassRefPtr<CSSValue> HTMLAttributeEquivalent::attributeValueAsCSSValue(Element* element) const
291 {
292     ASSERT(element);
293     if (!element->hasAttribute(m_attrName))
294         return 0;
295 
296     RefPtr<MutableStylePropertySet> dummyStyle;
297     dummyStyle = MutableStylePropertySet::create();
298     dummyStyle->setProperty(m_propertyID, element->getAttribute(m_attrName));
299     return dummyStyle->getPropertyCSSValue(m_propertyID);
300 }
301 
302 class HTMLFontSizeEquivalent : public HTMLAttributeEquivalent {
303 public:
create()304     static PassOwnPtr<HTMLFontSizeEquivalent> create()
305     {
306         return adoptPtr(new HTMLFontSizeEquivalent());
307     }
308     virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const;
309 
310 private:
311     HTMLFontSizeEquivalent();
312 };
313 
HTMLFontSizeEquivalent()314 HTMLFontSizeEquivalent::HTMLFontSizeEquivalent()
315     : HTMLAttributeEquivalent(CSSPropertyFontSize, HTMLNames::fontTag, HTMLNames::sizeAttr)
316 {
317 }
318 
attributeValueAsCSSValue(Element * element) const319 PassRefPtr<CSSValue> HTMLFontSizeEquivalent::attributeValueAsCSSValue(Element* element) const
320 {
321     ASSERT(element);
322     if (!element->hasAttribute(m_attrName))
323         return 0;
324     CSSValueID size;
325     if (!HTMLFontElement::cssValueFromFontSizeNumber(element->getAttribute(m_attrName), size))
326         return 0;
327     return CSSPrimitiveValue::createIdentifier(size);
328 }
329 
330 float EditingStyle::NoFontDelta = 0.0f;
331 
EditingStyle()332 EditingStyle::EditingStyle()
333     : m_shouldUseFixedDefaultFontSize(false)
334     , m_fontSizeDelta(NoFontDelta)
335 {
336 }
337 
EditingStyle(Node * node,PropertiesToInclude propertiesToInclude)338 EditingStyle::EditingStyle(Node* node, PropertiesToInclude propertiesToInclude)
339     : m_shouldUseFixedDefaultFontSize(false)
340     , m_fontSizeDelta(NoFontDelta)
341 {
342     init(node, propertiesToInclude);
343 }
344 
EditingStyle(const Position & position,PropertiesToInclude propertiesToInclude)345 EditingStyle::EditingStyle(const Position& position, PropertiesToInclude propertiesToInclude)
346     : m_shouldUseFixedDefaultFontSize(false)
347     , m_fontSizeDelta(NoFontDelta)
348 {
349     init(position.deprecatedNode(), propertiesToInclude);
350 }
351 
EditingStyle(const StylePropertySet * style)352 EditingStyle::EditingStyle(const StylePropertySet* style)
353     : m_mutableStyle(style ? style->mutableCopy() : 0)
354     , m_shouldUseFixedDefaultFontSize(false)
355     , m_fontSizeDelta(NoFontDelta)
356 {
357     extractFontSizeDelta();
358 }
359 
EditingStyle(const CSSStyleDeclaration * style)360 EditingStyle::EditingStyle(const CSSStyleDeclaration* style)
361     : m_mutableStyle(style ? style->copyProperties() : 0)
362     , m_shouldUseFixedDefaultFontSize(false)
363     , m_fontSizeDelta(NoFontDelta)
364 {
365     extractFontSizeDelta();
366 }
367 
EditingStyle(CSSPropertyID propertyID,const String & value)368 EditingStyle::EditingStyle(CSSPropertyID propertyID, const String& value)
369     : m_mutableStyle(0)
370     , m_shouldUseFixedDefaultFontSize(false)
371     , m_fontSizeDelta(NoFontDelta)
372 {
373     setProperty(propertyID, value);
374 }
375 
~EditingStyle()376 EditingStyle::~EditingStyle()
377 {
378 }
379 
cssValueToRGBA(CSSValue * colorValue)380 static RGBA32 cssValueToRGBA(CSSValue* colorValue)
381 {
382     if (!colorValue || !colorValue->isPrimitiveValue())
383         return Color::transparent;
384 
385     CSSPrimitiveValue* primitiveColor = toCSSPrimitiveValue(colorValue);
386     if (primitiveColor->isRGBColor())
387         return primitiveColor->getRGBA32Value();
388 
389     RGBA32 rgba = 0;
390     CSSParser::parseColor(rgba, colorValue->cssText());
391     return rgba;
392 }
393 
getRGBAFontColor(CSSStyleDeclaration * style)394 static inline RGBA32 getRGBAFontColor(CSSStyleDeclaration* style)
395 {
396     return cssValueToRGBA(style->getPropertyCSSValueInternal(CSSPropertyColor).get());
397 }
398 
getRGBAFontColor(StylePropertySet * style)399 static inline RGBA32 getRGBAFontColor(StylePropertySet* style)
400 {
401     return cssValueToRGBA(style->getPropertyCSSValue(CSSPropertyColor).get());
402 }
403 
getRGBABackgroundColor(CSSStyleDeclaration * style)404 static inline RGBA32 getRGBABackgroundColor(CSSStyleDeclaration* style)
405 {
406     return cssValueToRGBA(style->getPropertyCSSValueInternal(CSSPropertyBackgroundColor).get());
407 }
408 
getRGBABackgroundColor(StylePropertySet * style)409 static inline RGBA32 getRGBABackgroundColor(StylePropertySet* style)
410 {
411     return cssValueToRGBA(style->getPropertyCSSValue(CSSPropertyBackgroundColor).get());
412 }
413 
rgbaBackgroundColorInEffect(Node * node)414 static inline RGBA32 rgbaBackgroundColorInEffect(Node* node)
415 {
416     return cssValueToRGBA(backgroundColorInEffect(node).get());
417 }
418 
textAlignResolvingStartAndEnd(int textAlign,int direction)419 static int textAlignResolvingStartAndEnd(int textAlign, int direction)
420 {
421     switch (textAlign) {
422     case CSSValueCenter:
423     case CSSValueWebkitCenter:
424         return CSSValueCenter;
425     case CSSValueJustify:
426         return CSSValueJustify;
427     case CSSValueLeft:
428     case CSSValueWebkitLeft:
429         return CSSValueLeft;
430     case CSSValueRight:
431     case CSSValueWebkitRight:
432         return CSSValueRight;
433     case CSSValueStart:
434         return direction != CSSValueRtl ? CSSValueLeft : CSSValueRight;
435     case CSSValueEnd:
436         return direction == CSSValueRtl ? CSSValueRight : CSSValueLeft;
437     }
438     return CSSValueInvalid;
439 }
440 
441 template<typename T>
textAlignResolvingStartAndEnd(T * style)442 static int textAlignResolvingStartAndEnd(T* style)
443 {
444     return textAlignResolvingStartAndEnd(getIdentifierValue(style, CSSPropertyTextAlign), getIdentifierValue(style, CSSPropertyDirection));
445 }
446 
init(Node * node,PropertiesToInclude propertiesToInclude)447 void EditingStyle::init(Node* node, PropertiesToInclude propertiesToInclude)
448 {
449     if (isTabSpanTextNode(node))
450         node = tabSpanNode(node)->parentNode();
451     else if (isTabSpanNode(node))
452         node = node->parentNode();
453 
454     RefPtr<CSSComputedStyleDeclaration> computedStyleAtPosition = CSSComputedStyleDeclaration::create(node);
455     m_mutableStyle = propertiesToInclude == AllProperties && computedStyleAtPosition ? computedStyleAtPosition->copyProperties() : editingStyleFromComputedStyle(computedStyleAtPosition);
456 
457     if (propertiesToInclude == EditingPropertiesInEffect) {
458         if (RefPtr<CSSValue> value = backgroundColorInEffect(node))
459             m_mutableStyle->setProperty(CSSPropertyBackgroundColor, value->cssText());
460         if (RefPtr<CSSValue> value = computedStyleAtPosition->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect))
461             m_mutableStyle->setProperty(CSSPropertyTextDecoration, value->cssText());
462     }
463 
464     if (node && node->computedStyle()) {
465         RenderStyle* renderStyle = node->computedStyle();
466         removeTextFillAndStrokeColorsIfNeeded(renderStyle);
467         replaceFontSizeByKeywordIfPossible(renderStyle, computedStyleAtPosition.get());
468     }
469 
470     m_shouldUseFixedDefaultFontSize = computedStyleAtPosition->useFixedFontDefaultSize();
471     extractFontSizeDelta();
472 }
473 
removeTextFillAndStrokeColorsIfNeeded(RenderStyle * renderStyle)474 void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyle)
475 {
476     // If a node's text fill color is invalid, then its children use
477     // their font-color as their text fill color (they don't
478     // inherit it).  Likewise for stroke color.
479     if (!renderStyle->textFillColor().isValid())
480         m_mutableStyle->removeProperty(CSSPropertyWebkitTextFillColor);
481     if (!renderStyle->textStrokeColor().isValid())
482         m_mutableStyle->removeProperty(CSSPropertyWebkitTextStrokeColor);
483 }
484 
setProperty(CSSPropertyID propertyID,const String & value,bool important)485 void EditingStyle::setProperty(CSSPropertyID propertyID, const String& value, bool important)
486 {
487     if (!m_mutableStyle)
488         m_mutableStyle = MutableStylePropertySet::create();
489 
490     m_mutableStyle->setProperty(propertyID, value, important);
491 }
492 
replaceFontSizeByKeywordIfPossible(RenderStyle * renderStyle,CSSComputedStyleDeclaration * computedStyle)493 void EditingStyle::replaceFontSizeByKeywordIfPossible(RenderStyle* renderStyle, CSSComputedStyleDeclaration* computedStyle)
494 {
495     ASSERT(renderStyle);
496     if (renderStyle->fontDescription().keywordSize())
497         m_mutableStyle->setProperty(CSSPropertyFontSize, computedStyle->getFontSizeCSSValuePreferringKeyword()->cssText());
498 }
499 
extractFontSizeDelta()500 void EditingStyle::extractFontSizeDelta()
501 {
502     if (!m_mutableStyle)
503         return;
504 
505     if (m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize)) {
506         // Explicit font size overrides any delta.
507         m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
508         return;
509     }
510 
511     // Get the adjustment amount out of the style.
512     RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta);
513     if (!value || !value->isPrimitiveValue())
514         return;
515 
516     CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value.get());
517 
518     // Only PX handled now. If we handle more types in the future, perhaps
519     // a switch statement here would be more appropriate.
520     if (!primitiveValue->isPx())
521         return;
522 
523     m_fontSizeDelta = primitiveValue->getFloatValue();
524     m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
525 }
526 
isEmpty() const527 bool EditingStyle::isEmpty() const
528 {
529     return (!m_mutableStyle || m_mutableStyle->isEmpty()) && m_fontSizeDelta == NoFontDelta;
530 }
531 
textDirection(WritingDirection & writingDirection) const532 bool EditingStyle::textDirection(WritingDirection& writingDirection) const
533 {
534     if (!m_mutableStyle)
535         return false;
536 
537     RefPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
538     if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
539         return false;
540 
541     CSSValueID unicodeBidiValue = toCSSPrimitiveValue(unicodeBidi.get())->getValueID();
542     if (unicodeBidiValue == CSSValueEmbed) {
543         RefPtr<CSSValue> direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
544         if (!direction || !direction->isPrimitiveValue())
545             return false;
546 
547         writingDirection = toCSSPrimitiveValue(direction.get())->getValueID() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
548 
549         return true;
550     }
551 
552     if (unicodeBidiValue == CSSValueNormal) {
553         writingDirection = NaturalWritingDirection;
554         return true;
555     }
556 
557     return false;
558 }
559 
setStyle(PassRefPtr<MutableStylePropertySet> style)560 void EditingStyle::setStyle(PassRefPtr<MutableStylePropertySet> style)
561 {
562     m_mutableStyle = style;
563     // FIXME: We should be able to figure out whether or not font is fixed width for mutable style.
564     // We need to check font-family is monospace as in FontDescription but we don't want to duplicate code here.
565     m_shouldUseFixedDefaultFontSize = false;
566     extractFontSizeDelta();
567 }
568 
overrideWithStyle(const StylePropertySet * style)569 void EditingStyle::overrideWithStyle(const StylePropertySet* style)
570 {
571     if (!style || style->isEmpty())
572         return;
573     if (!m_mutableStyle)
574         m_mutableStyle = MutableStylePropertySet::create();
575     m_mutableStyle->mergeAndOverrideOnConflict(style);
576     extractFontSizeDelta();
577 }
578 
clear()579 void EditingStyle::clear()
580 {
581     m_mutableStyle.clear();
582     m_shouldUseFixedDefaultFontSize = false;
583     m_fontSizeDelta = NoFontDelta;
584 }
585 
copy() const586 PassRefPtr<EditingStyle> EditingStyle::copy() const
587 {
588     RefPtr<EditingStyle> copy = EditingStyle::create();
589     if (m_mutableStyle)
590         copy->m_mutableStyle = m_mutableStyle->mutableCopy();
591     copy->m_shouldUseFixedDefaultFontSize = m_shouldUseFixedDefaultFontSize;
592     copy->m_fontSizeDelta = m_fontSizeDelta;
593     return copy;
594 }
595 
extractAndRemoveBlockProperties()596 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveBlockProperties()
597 {
598     RefPtr<EditingStyle> blockProperties = EditingStyle::create();
599     if (!m_mutableStyle)
600         return blockProperties;
601 
602     blockProperties->m_mutableStyle = m_mutableStyle->copyBlockProperties();
603     m_mutableStyle->removeBlockProperties();
604 
605     return blockProperties;
606 }
607 
extractAndRemoveTextDirection()608 PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveTextDirection()
609 {
610     RefPtr<EditingStyle> textDirection = EditingStyle::create();
611     textDirection->m_mutableStyle = MutableStylePropertySet::create();
612     textDirection->m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed, m_mutableStyle->propertyIsImportant(CSSPropertyUnicodeBidi));
613     textDirection->m_mutableStyle->setProperty(CSSPropertyDirection, m_mutableStyle->getPropertyValue(CSSPropertyDirection),
614         m_mutableStyle->propertyIsImportant(CSSPropertyDirection));
615 
616     m_mutableStyle->removeProperty(CSSPropertyUnicodeBidi);
617     m_mutableStyle->removeProperty(CSSPropertyDirection);
618 
619     return textDirection;
620 }
621 
removeBlockProperties()622 void EditingStyle::removeBlockProperties()
623 {
624     if (!m_mutableStyle)
625         return;
626 
627     m_mutableStyle->removeBlockProperties();
628 }
629 
removeStyleAddedByNode(Node * node)630 void EditingStyle::removeStyleAddedByNode(Node* node)
631 {
632     if (!node || !node->parentNode())
633         return;
634     RefPtr<MutableStylePropertySet> parentStyle = editingStyleFromComputedStyle(CSSComputedStyleDeclaration::create(node->parentNode()), AllEditingProperties);
635     RefPtr<MutableStylePropertySet> nodeStyle = editingStyleFromComputedStyle(CSSComputedStyleDeclaration::create(node), AllEditingProperties);
636     nodeStyle->removeEquivalentProperties(parentStyle->ensureCSSStyleDeclaration());
637     m_mutableStyle->removeEquivalentProperties(nodeStyle->ensureCSSStyleDeclaration());
638 }
639 
removeStyleConflictingWithStyleOfNode(Node * node)640 void EditingStyle::removeStyleConflictingWithStyleOfNode(Node* node)
641 {
642     if (!node || !node->parentNode() || !m_mutableStyle)
643         return;
644 
645     RefPtr<MutableStylePropertySet> parentStyle = editingStyleFromComputedStyle(CSSComputedStyleDeclaration::create(node->parentNode()), AllEditingProperties);
646     RefPtr<MutableStylePropertySet> nodeStyle = editingStyleFromComputedStyle(CSSComputedStyleDeclaration::create(node), AllEditingProperties);
647     nodeStyle->removeEquivalentProperties(parentStyle->ensureCSSStyleDeclaration());
648 
649     unsigned propertyCount = nodeStyle->propertyCount();
650     for (unsigned i = 0; i < propertyCount; ++i)
651         m_mutableStyle->removeProperty(nodeStyle->propertyAt(i).id());
652 }
653 
collapseTextDecorationProperties()654 void EditingStyle::collapseTextDecorationProperties()
655 {
656     if (!m_mutableStyle)
657         return;
658 
659     RefPtr<CSSValue> textDecorationsInEffect = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
660     if (!textDecorationsInEffect)
661         return;
662 
663     if (textDecorationsInEffect->isValueList())
664         m_mutableStyle->setProperty(textDecorationPropertyForEditing(), textDecorationsInEffect->cssText(), m_mutableStyle->propertyIsImportant(textDecorationPropertyForEditing()));
665     else
666         m_mutableStyle->removeProperty(textDecorationPropertyForEditing());
667     m_mutableStyle->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
668 }
669 
670 // CSS properties that create a visual difference only when applied to text.
671 static const CSSPropertyID textOnlyProperties[] = {
672     // FIXME: CSSPropertyTextDecoration needs to be removed when CSS3 Text
673     // Decoration feature is no longer experimental.
674     CSSPropertyTextDecoration,
675     CSSPropertyTextDecorationLine,
676     CSSPropertyWebkitTextDecorationsInEffect,
677     CSSPropertyFontStyle,
678     CSSPropertyFontWeight,
679     CSSPropertyColor,
680 };
681 
triStateOfStyle(EditingStyle * style) const682 TriState EditingStyle::triStateOfStyle(EditingStyle* style) const
683 {
684     if (!style || !style->m_mutableStyle)
685         return FalseTriState;
686     return triStateOfStyle(style->m_mutableStyle->ensureCSSStyleDeclaration(), DoNotIgnoreTextOnlyProperties);
687 }
688 
triStateOfStyle(CSSStyleDeclaration * styleToCompare,ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const689 TriState EditingStyle::triStateOfStyle(CSSStyleDeclaration* styleToCompare, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const
690 {
691     RefPtr<MutableStylePropertySet> difference = getPropertiesNotIn(m_mutableStyle.get(), styleToCompare);
692 
693     if (shouldIgnoreTextOnlyProperties == IgnoreTextOnlyProperties)
694         difference->removePropertiesInSet(textOnlyProperties, WTF_ARRAY_LENGTH(textOnlyProperties));
695 
696     if (difference->isEmpty())
697         return TrueTriState;
698     if (difference->propertyCount() == m_mutableStyle->propertyCount())
699         return FalseTriState;
700 
701     return MixedTriState;
702 }
703 
triStateOfStyle(const VisibleSelection & selection) const704 TriState EditingStyle::triStateOfStyle(const VisibleSelection& selection) const
705 {
706     if (!selection.isCaretOrRange())
707         return FalseTriState;
708 
709     if (selection.isCaret())
710         return triStateOfStyle(EditingStyle::styleAtSelectionStart(selection).get());
711 
712     TriState state = FalseTriState;
713     bool nodeIsStart = true;
714     for (Node* node = selection.start().deprecatedNode(); node; node = NodeTraversal::next(*node)) {
715         if (node->renderer() && node->rendererIsEditable()) {
716             RefPtr<CSSComputedStyleDeclaration> nodeStyle = CSSComputedStyleDeclaration::create(node);
717             if (nodeStyle) {
718                 TriState nodeState = triStateOfStyle(nodeStyle.get(), node->isTextNode() ? EditingStyle::DoNotIgnoreTextOnlyProperties : EditingStyle::IgnoreTextOnlyProperties);
719                 if (nodeIsStart) {
720                     state = nodeState;
721                     nodeIsStart = false;
722                 } else if (state != nodeState && node->isTextNode()) {
723                     state = MixedTriState;
724                     break;
725                 }
726             }
727         }
728         if (node == selection.end().deprecatedNode())
729             break;
730     }
731 
732     return state;
733 }
734 
conflictsWithInlineStyleOfElement(Element * element,EditingStyle * extractedStyle,Vector<CSSPropertyID> * conflictingProperties) const735 bool EditingStyle::conflictsWithInlineStyleOfElement(Element* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const
736 {
737     ASSERT(element);
738     ASSERT(!conflictingProperties || conflictingProperties->isEmpty());
739 
740     const StylePropertySet* inlineStyle = element->inlineStyle();
741     if (!m_mutableStyle || !inlineStyle)
742         return false;
743 
744     unsigned propertyCount = m_mutableStyle->propertyCount();
745     for (unsigned i = 0; i < propertyCount; ++i) {
746         CSSPropertyID propertyID = m_mutableStyle->propertyAt(i).id();
747 
748         // We don't override whitespace property of a tab span because that would collapse the tab into a space.
749         if (propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element))
750             continue;
751 
752         if (propertyID == CSSPropertyWebkitTextDecorationsInEffect && inlineStyle->getPropertyCSSValue(textDecorationPropertyForEditing())) {
753             if (!conflictingProperties)
754                 return true;
755             conflictingProperties->append(CSSPropertyTextDecoration);
756             // Because text-decoration expands to text-decoration-line when CSS3
757             // Text Decoration is enabled, we also state it as conflicting.
758             if (RuntimeEnabledFeatures::css3TextDecorationsEnabled())
759                 conflictingProperties->append(CSSPropertyTextDecorationLine);
760             if (extractedStyle)
761                 extractedStyle->setProperty(textDecorationPropertyForEditing(), inlineStyle->getPropertyValue(textDecorationPropertyForEditing()), inlineStyle->propertyIsImportant(textDecorationPropertyForEditing()));
762             continue;
763         }
764 
765         if (!inlineStyle->getPropertyCSSValue(propertyID))
766             continue;
767 
768         if (propertyID == CSSPropertyUnicodeBidi && inlineStyle->getPropertyCSSValue(CSSPropertyDirection)) {
769             if (!conflictingProperties)
770                 return true;
771             conflictingProperties->append(CSSPropertyDirection);
772             if (extractedStyle)
773                 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->propertyIsImportant(propertyID));
774         }
775 
776         if (!conflictingProperties)
777             return true;
778 
779         conflictingProperties->append(propertyID);
780 
781         if (extractedStyle)
782             extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->propertyIsImportant(propertyID));
783     }
784 
785     return conflictingProperties && !conflictingProperties->isEmpty();
786 }
787 
htmlElementEquivalents()788 static const Vector<OwnPtr<HTMLElementEquivalent> >& htmlElementEquivalents()
789 {
790     DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLElementEquivalent> >, HTMLElementEquivalents, ());
791 
792     if (!HTMLElementEquivalents.size()) {
793         HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::bTag));
794         HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::strongTag));
795         HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSub, HTMLNames::subTag));
796         HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSuper, HTMLNames::supTag));
797         HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::iTag));
798         HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::emTag));
799 
800         HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueUnderline, HTMLNames::uTag));
801         HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::sTag));
802         HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::strikeTag));
803     }
804 
805     return HTMLElementEquivalents;
806 }
807 
808 
conflictsWithImplicitStyleOfElement(HTMLElement * element,EditingStyle * extractedStyle,ShouldExtractMatchingStyle shouldExtractMatchingStyle) const809 bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
810 {
811     if (!m_mutableStyle)
812         return false;
813 
814     const Vector<OwnPtr<HTMLElementEquivalent> >& HTMLElementEquivalents = htmlElementEquivalents();
815     for (size_t i = 0; i < HTMLElementEquivalents.size(); ++i) {
816         const HTMLElementEquivalent* equivalent = HTMLElementEquivalents[i].get();
817         if (equivalent->matches(element) && equivalent->propertyExistsInStyle(m_mutableStyle.get())
818             && (shouldExtractMatchingStyle == ExtractMatchingStyle || !equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) {
819             if (extractedStyle)
820                 equivalent->addToStyle(element, extractedStyle);
821             return true;
822         }
823     }
824     return false;
825 }
826 
htmlAttributeEquivalents()827 static const Vector<OwnPtr<HTMLAttributeEquivalent> >& htmlAttributeEquivalents()
828 {
829     DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLAttributeEquivalent> >, HTMLAttributeEquivalents, ());
830 
831     if (!HTMLAttributeEquivalents.size()) {
832         // elementIsStyledSpanOrHTMLEquivalent depends on the fact each HTMLAttriuteEquivalent matches exactly one attribute
833         // of exactly one element except dirAttr.
834         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyColor, HTMLNames::fontTag, HTMLNames::colorAttr));
835         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyFontFamily, HTMLNames::fontTag, HTMLNames::faceAttr));
836         HTMLAttributeEquivalents.append(HTMLFontSizeEquivalent::create());
837 
838         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyDirection, HTMLNames::dirAttr));
839         HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyUnicodeBidi, HTMLNames::dirAttr));
840     }
841 
842     return HTMLAttributeEquivalents;
843 }
844 
conflictsWithImplicitStyleOfAttributes(HTMLElement * element) const845 bool EditingStyle::conflictsWithImplicitStyleOfAttributes(HTMLElement* element) const
846 {
847     ASSERT(element);
848     if (!m_mutableStyle)
849         return false;
850 
851     const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
852     for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
853         if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->propertyExistsInStyle(m_mutableStyle.get())
854             && !HTMLAttributeEquivalents[i]->valueIsPresentInStyle(element, m_mutableStyle.get()))
855             return true;
856     }
857 
858     return false;
859 }
860 
extractConflictingImplicitStyleOfAttributes(HTMLElement * element,ShouldPreserveWritingDirection shouldPreserveWritingDirection,EditingStyle * extractedStyle,Vector<QualifiedName> & conflictingAttributes,ShouldExtractMatchingStyle shouldExtractMatchingStyle) const861 bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* element, ShouldPreserveWritingDirection shouldPreserveWritingDirection,
862     EditingStyle* extractedStyle, Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
863 {
864     ASSERT(element);
865     // HTMLAttributeEquivalent::addToStyle doesn't support unicode-bidi and direction properties
866     ASSERT(!extractedStyle || shouldPreserveWritingDirection == PreserveWritingDirection);
867     if (!m_mutableStyle)
868         return false;
869 
870     const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
871     bool removed = false;
872     for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
873         const HTMLAttributeEquivalent* equivalent = HTMLAttributeEquivalents[i].get();
874 
875         // unicode-bidi and direction are pushed down separately so don't push down with other styles.
876         if (shouldPreserveWritingDirection == PreserveWritingDirection && equivalent->attributeName() == HTMLNames::dirAttr)
877             continue;
878 
879         if (!equivalent->matches(element) || !equivalent->propertyExistsInStyle(m_mutableStyle.get())
880             || (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(element, m_mutableStyle.get())))
881             continue;
882 
883         if (extractedStyle)
884             equivalent->addToStyle(element, extractedStyle);
885         conflictingAttributes.append(equivalent->attributeName());
886         removed = true;
887     }
888 
889     return removed;
890 }
891 
styleIsPresentInComputedStyleOfNode(Node * node) const892 bool EditingStyle::styleIsPresentInComputedStyleOfNode(Node* node) const
893 {
894     return !m_mutableStyle || getPropertiesNotIn(m_mutableStyle.get(), CSSComputedStyleDeclaration::create(node).get())->isEmpty();
895 }
896 
elementIsStyledSpanOrHTMLEquivalent(const HTMLElement * element)897 bool EditingStyle::elementIsStyledSpanOrHTMLEquivalent(const HTMLElement* element)
898 {
899     bool elementIsSpanOrElementEquivalent = false;
900     if (element->hasTagName(HTMLNames::spanTag))
901         elementIsSpanOrElementEquivalent = true;
902     else {
903         const Vector<OwnPtr<HTMLElementEquivalent> >& HTMLElementEquivalents = htmlElementEquivalents();
904         size_t i;
905         for (i = 0; i < HTMLElementEquivalents.size(); ++i) {
906             if (HTMLElementEquivalents[i]->matches(element)) {
907                 elementIsSpanOrElementEquivalent = true;
908                 break;
909             }
910         }
911     }
912 
913     if (!element->hasAttributes())
914         return elementIsSpanOrElementEquivalent; // span, b, etc... without any attributes
915 
916     unsigned matchedAttributes = 0;
917     const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
918     for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
919         if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->attributeName() != HTMLNames::dirAttr)
920             matchedAttributes++;
921     }
922 
923     if (!elementIsSpanOrElementEquivalent && !matchedAttributes)
924         return false; // element is not a span, a html element equivalent, or font element.
925 
926     if (element->getAttribute(HTMLNames::classAttr) == AppleStyleSpanClass)
927         matchedAttributes++;
928 
929     if (element->hasAttribute(HTMLNames::styleAttr)) {
930         if (const StylePropertySet* style = element->inlineStyle()) {
931             unsigned propertyCount = style->propertyCount();
932             for (unsigned i = 0; i < propertyCount; ++i) {
933                 if (!isEditingProperty(style->propertyAt(i).id()))
934                     return false;
935             }
936         }
937         matchedAttributes++;
938     }
939 
940     // font with color attribute, span with style attribute, etc...
941     ASSERT(matchedAttributes <= element->attributeCount());
942     return matchedAttributes >= element->attributeCount();
943 }
944 
prepareToApplyAt(const Position & position,ShouldPreserveWritingDirection shouldPreserveWritingDirection)945 void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWritingDirection shouldPreserveWritingDirection)
946 {
947     if (!m_mutableStyle)
948         return;
949 
950     // ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style.
951     // If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate
952     // which one of editingStyleAtPosition or computedStyle is called.
953     RefPtr<EditingStyle> editingStyleAtPosition = EditingStyle::create(position, EditingPropertiesInEffect);
954     StylePropertySet* styleAtPosition = editingStyleAtPosition->m_mutableStyle.get();
955 
956     RefPtr<CSSValue> unicodeBidi;
957     RefPtr<CSSValue> direction;
958     if (shouldPreserveWritingDirection == PreserveWritingDirection) {
959         unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
960         direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
961     }
962 
963     m_mutableStyle->removeEquivalentProperties(styleAtPosition);
964 
965     if (textAlignResolvingStartAndEnd(m_mutableStyle.get()) == textAlignResolvingStartAndEnd(styleAtPosition))
966         m_mutableStyle->removeProperty(CSSPropertyTextAlign);
967 
968     if (getRGBAFontColor(m_mutableStyle.get()) == getRGBAFontColor(styleAtPosition))
969         m_mutableStyle->removeProperty(CSSPropertyColor);
970 
971     if (hasTransparentBackgroundColor(m_mutableStyle.get())
972         || cssValueToRGBA(m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor).get()) == rgbaBackgroundColorInEffect(position.containerNode()))
973         m_mutableStyle->removeProperty(CSSPropertyBackgroundColor);
974 
975     if (unicodeBidi && unicodeBidi->isPrimitiveValue()) {
976         m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, toCSSPrimitiveValue(unicodeBidi.get())->getValueID());
977         if (direction && direction->isPrimitiveValue())
978             m_mutableStyle->setProperty(CSSPropertyDirection, toCSSPrimitiveValue(direction.get())->getValueID());
979     }
980 }
981 
mergeTypingStyle(Document * document)982 void EditingStyle::mergeTypingStyle(Document* document)
983 {
984     ASSERT(document);
985 
986     RefPtr<EditingStyle> typingStyle = document->frame()->selection().typingStyle();
987     if (!typingStyle || typingStyle == this)
988         return;
989 
990     mergeStyle(typingStyle->style(), OverrideValues);
991 }
992 
mergeInlineStyleOfElement(Element * element,CSSPropertyOverrideMode mode,PropertiesToInclude propertiesToInclude)993 void EditingStyle::mergeInlineStyleOfElement(Element* element, CSSPropertyOverrideMode mode, PropertiesToInclude propertiesToInclude)
994 {
995     ASSERT(element);
996     if (!element->inlineStyle())
997         return;
998 
999     switch (propertiesToInclude) {
1000     case AllProperties:
1001         mergeStyle(element->inlineStyle(), mode);
1002         return;
1003     case OnlyEditingInheritableProperties:
1004         mergeStyle(copyEditingProperties(element->inlineStyle(), OnlyInheritableEditingProperties).get(), mode);
1005         return;
1006     case EditingPropertiesInEffect:
1007         mergeStyle(copyEditingProperties(element->inlineStyle(), AllEditingProperties).get(), mode);
1008         return;
1009     }
1010 }
1011 
elementMatchesAndPropertyIsNotInInlineStyleDecl(const HTMLElementEquivalent * equivalent,const Element * element,EditingStyle::CSSPropertyOverrideMode mode,StylePropertySet * style)1012 static inline bool elementMatchesAndPropertyIsNotInInlineStyleDecl(const HTMLElementEquivalent* equivalent, const Element* element,
1013     EditingStyle::CSSPropertyOverrideMode mode, StylePropertySet* style)
1014 {
1015     return equivalent->matches(element) && (!element->inlineStyle() || !equivalent->propertyExistsInStyle(element->inlineStyle()))
1016         && (mode == EditingStyle::OverrideValues || !equivalent->propertyExistsInStyle(style));
1017 }
1018 
extractEditingProperties(const StylePropertySet * style,EditingStyle::PropertiesToInclude propertiesToInclude)1019 static PassRefPtr<MutableStylePropertySet> extractEditingProperties(const StylePropertySet* style, EditingStyle::PropertiesToInclude propertiesToInclude)
1020 {
1021     if (!style)
1022         return 0;
1023 
1024     switch (propertiesToInclude) {
1025     case EditingStyle::AllProperties:
1026     case EditingStyle::EditingPropertiesInEffect:
1027         return copyEditingProperties(style, AllEditingProperties);
1028     case EditingStyle::OnlyEditingInheritableProperties:
1029         return copyEditingProperties(style, OnlyInheritableEditingProperties);
1030     }
1031 
1032     ASSERT_NOT_REACHED();
1033     return 0;
1034 }
1035 
mergeInlineAndImplicitStyleOfElement(Element * element,CSSPropertyOverrideMode mode,PropertiesToInclude propertiesToInclude)1036 void EditingStyle::mergeInlineAndImplicitStyleOfElement(Element* element, CSSPropertyOverrideMode mode, PropertiesToInclude propertiesToInclude)
1037 {
1038     RefPtr<EditingStyle> styleFromRules = EditingStyle::create();
1039     styleFromRules->mergeStyleFromRulesForSerialization(element);
1040     styleFromRules->m_mutableStyle = extractEditingProperties(styleFromRules->m_mutableStyle.get(), propertiesToInclude);
1041     mergeStyle(styleFromRules->m_mutableStyle.get(), mode);
1042 
1043     mergeInlineStyleOfElement(element, mode, propertiesToInclude);
1044 
1045     const Vector<OwnPtr<HTMLElementEquivalent> >& elementEquivalents = htmlElementEquivalents();
1046     for (size_t i = 0; i < elementEquivalents.size(); ++i) {
1047         if (elementMatchesAndPropertyIsNotInInlineStyleDecl(elementEquivalents[i].get(), element, mode, m_mutableStyle.get()))
1048             elementEquivalents[i]->addToStyle(element, this);
1049     }
1050 
1051     const Vector<OwnPtr<HTMLAttributeEquivalent> >& attributeEquivalents = htmlAttributeEquivalents();
1052     for (size_t i = 0; i < attributeEquivalents.size(); ++i) {
1053         if (attributeEquivalents[i]->attributeName() == HTMLNames::dirAttr)
1054             continue; // We don't want to include directionality
1055         if (elementMatchesAndPropertyIsNotInInlineStyleDecl(attributeEquivalents[i].get(), element, mode, m_mutableStyle.get()))
1056             attributeEquivalents[i]->addToStyle(element, this);
1057     }
1058 }
1059 
wrappingStyleForSerialization(Node * context,bool shouldAnnotate)1060 PassRefPtr<EditingStyle> EditingStyle::wrappingStyleForSerialization(Node* context, bool shouldAnnotate)
1061 {
1062     RefPtr<EditingStyle> wrappingStyle;
1063     if (shouldAnnotate) {
1064         wrappingStyle = EditingStyle::create(context, EditingStyle::EditingPropertiesInEffect);
1065 
1066         // Styles that Mail blockquotes contribute should only be placed on the Mail blockquote,
1067         // to help us differentiate those styles from ones that the user has applied.
1068         // This helps us get the color of content pasted into blockquotes right.
1069         wrappingStyle->removeStyleAddedByNode(enclosingNodeOfType(firstPositionInOrBeforeNode(context), isMailBlockquote, CanCrossEditingBoundary));
1070 
1071         // Call collapseTextDecorationProperties first or otherwise it'll copy the value over from in-effect to text-decorations.
1072         wrappingStyle->collapseTextDecorationProperties();
1073 
1074         return wrappingStyle.release();
1075     }
1076 
1077     wrappingStyle = EditingStyle::create();
1078 
1079     // When not annotating for interchange, we only preserve inline style declarations.
1080     for (Node* node = context; node && !node->isDocumentNode(); node = node->parentNode()) {
1081         if (node->isStyledElement() && !isMailBlockquote(node)) {
1082             wrappingStyle->mergeInlineAndImplicitStyleOfElement(toElement(node), EditingStyle::DoNotOverrideValues,
1083                 EditingStyle::EditingPropertiesInEffect);
1084         }
1085     }
1086 
1087     return wrappingStyle.release();
1088 }
1089 
1090 
mergeTextDecorationValues(CSSValueList * mergedValue,const CSSValueList * valueToMerge)1091 static void mergeTextDecorationValues(CSSValueList* mergedValue, const CSSValueList* valueToMerge)
1092 {
1093     DEFINE_STATIC_REF(CSSPrimitiveValue, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
1094     DEFINE_STATIC_REF(CSSPrimitiveValue, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
1095 
1096     if (valueToMerge->hasValue(underline) && !mergedValue->hasValue(underline))
1097         mergedValue->append(underline);
1098 
1099     if (valueToMerge->hasValue(lineThrough) && !mergedValue->hasValue(lineThrough))
1100         mergedValue->append(lineThrough);
1101 }
1102 
mergeStyle(const StylePropertySet * style,CSSPropertyOverrideMode mode)1103 void EditingStyle::mergeStyle(const StylePropertySet* style, CSSPropertyOverrideMode mode)
1104 {
1105     if (!style)
1106         return;
1107 
1108     if (!m_mutableStyle) {
1109         m_mutableStyle = style->mutableCopy();
1110         return;
1111     }
1112 
1113     unsigned propertyCount = style->propertyCount();
1114     for (unsigned i = 0; i < propertyCount; ++i) {
1115         StylePropertySet::PropertyReference property = style->propertyAt(i);
1116         RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(property.id());
1117 
1118         // text decorations never override values
1119         if ((property.id() == textDecorationPropertyForEditing() || property.id() == CSSPropertyWebkitTextDecorationsInEffect) && property.value()->isValueList() && value) {
1120             if (value->isValueList()) {
1121                 mergeTextDecorationValues(toCSSValueList(value.get()), toCSSValueList(property.value()));
1122                 continue;
1123             }
1124             value = 0; // text-decoration: none is equivalent to not having the property
1125         }
1126 
1127         if (mode == OverrideValues || (mode == DoNotOverrideValues && !value))
1128             m_mutableStyle->setProperty(property.id(), property.value()->cssText(), property.isImportant());
1129     }
1130 }
1131 
styleFromMatchedRulesForElement(Element * element,unsigned rulesToInclude)1132 static PassRefPtr<MutableStylePropertySet> styleFromMatchedRulesForElement(Element* element, unsigned rulesToInclude)
1133 {
1134     RefPtr<MutableStylePropertySet> style = MutableStylePropertySet::create();
1135     RefPtr<StyleRuleList> matchedRules = element->document().ensureStyleResolver().styleRulesForElement(element, rulesToInclude);
1136     if (matchedRules) {
1137         for (unsigned i = 0; i < matchedRules->m_list.size(); ++i)
1138             style->mergeAndOverrideOnConflict(matchedRules->m_list[i]->properties());
1139     }
1140     return style.release();
1141 }
1142 
mergeStyleFromRules(Element * element)1143 void EditingStyle::mergeStyleFromRules(Element* element)
1144 {
1145     RefPtr<MutableStylePropertySet> styleFromMatchedRules = styleFromMatchedRulesForElement(element,
1146         StyleResolver::AuthorCSSRules | StyleResolver::CrossOriginCSSRules);
1147     // Styles from the inline style declaration, held in the variable "style", take precedence
1148     // over those from matched rules.
1149     if (m_mutableStyle)
1150         styleFromMatchedRules->mergeAndOverrideOnConflict(m_mutableStyle.get());
1151 
1152     clear();
1153     m_mutableStyle = styleFromMatchedRules;
1154 }
1155 
mergeStyleFromRulesForSerialization(Element * element)1156 void EditingStyle::mergeStyleFromRulesForSerialization(Element* element)
1157 {
1158     mergeStyleFromRules(element);
1159 
1160     // The property value, if it's a percentage, may not reflect the actual computed value.
1161     // For example: style="height: 1%; overflow: visible;" in quirksmode
1162     // FIXME: There are others like this, see <rdar://problem/5195123> Slashdot copy/paste fidelity problem
1163     RefPtr<CSSComputedStyleDeclaration> computedStyleForElement = CSSComputedStyleDeclaration::create(element);
1164     RefPtr<MutableStylePropertySet> fromComputedStyle = MutableStylePropertySet::create();
1165     {
1166         unsigned propertyCount = m_mutableStyle->propertyCount();
1167         for (unsigned i = 0; i < propertyCount; ++i) {
1168             StylePropertySet::PropertyReference property = m_mutableStyle->propertyAt(i);
1169             CSSValue* value = property.value();
1170             if (!value->isPrimitiveValue())
1171                 continue;
1172             if (toCSSPrimitiveValue(value)->isPercentage()) {
1173                 if (RefPtr<CSSValue> computedPropertyValue = computedStyleForElement->getPropertyCSSValue(property.id()))
1174                     fromComputedStyle->addParsedProperty(CSSProperty(property.id(), computedPropertyValue));
1175             }
1176         }
1177     }
1178     m_mutableStyle->mergeAndOverrideOnConflict(fromComputedStyle.get());
1179 }
1180 
removePropertiesInStyle(MutableStylePropertySet * styleToRemovePropertiesFrom,StylePropertySet * style)1181 static void removePropertiesInStyle(MutableStylePropertySet* styleToRemovePropertiesFrom, StylePropertySet* style)
1182 {
1183     unsigned propertyCount = style->propertyCount();
1184     Vector<CSSPropertyID> propertiesToRemove(propertyCount);
1185     for (unsigned i = 0; i < propertyCount; ++i)
1186         propertiesToRemove[i] = style->propertyAt(i).id();
1187 
1188     styleToRemovePropertiesFrom->removePropertiesInSet(propertiesToRemove.data(), propertiesToRemove.size());
1189 }
1190 
removeStyleFromRulesAndContext(Element * element,Node * context)1191 void EditingStyle::removeStyleFromRulesAndContext(Element* element, Node* context)
1192 {
1193     ASSERT(element);
1194     if (!m_mutableStyle)
1195         return;
1196 
1197     // 1. Remove style from matched rules because style remain without repeating it in inline style declaration
1198     RefPtr<MutableStylePropertySet> styleFromMatchedRules = styleFromMatchedRulesForElement(element, StyleResolver::AllButEmptyCSSRules);
1199     if (styleFromMatchedRules && !styleFromMatchedRules->isEmpty())
1200         m_mutableStyle = getPropertiesNotIn(m_mutableStyle.get(), styleFromMatchedRules->ensureCSSStyleDeclaration());
1201 
1202     // 2. Remove style present in context and not overriden by matched rules.
1203     RefPtr<EditingStyle> computedStyle = EditingStyle::create(context, EditingPropertiesInEffect);
1204     if (computedStyle->m_mutableStyle) {
1205         if (!computedStyle->m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor))
1206             computedStyle->m_mutableStyle->setProperty(CSSPropertyBackgroundColor, CSSValueTransparent);
1207 
1208         removePropertiesInStyle(computedStyle->m_mutableStyle.get(), styleFromMatchedRules.get());
1209         m_mutableStyle = getPropertiesNotIn(m_mutableStyle.get(), computedStyle->m_mutableStyle->ensureCSSStyleDeclaration());
1210     }
1211 
1212     // 3. If this element is a span and has display: inline or float: none, remove them unless they are overriden by rules.
1213     // These rules are added by serialization code to wrap text nodes.
1214     if (isStyleSpanOrSpanWithOnlyStyleAttribute(element)) {
1215         if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyDisplay) && getIdentifierValue(m_mutableStyle.get(), CSSPropertyDisplay) == CSSValueInline)
1216             m_mutableStyle->removeProperty(CSSPropertyDisplay);
1217         if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyFloat) && getIdentifierValue(m_mutableStyle.get(), CSSPropertyFloat) == CSSValueNone)
1218             m_mutableStyle->removeProperty(CSSPropertyFloat);
1219     }
1220 }
1221 
removePropertiesInElementDefaultStyle(Element * element)1222 void EditingStyle::removePropertiesInElementDefaultStyle(Element* element)
1223 {
1224     if (!m_mutableStyle || m_mutableStyle->isEmpty())
1225         return;
1226 
1227     RefPtr<StylePropertySet> defaultStyle = styleFromMatchedRulesForElement(element, StyleResolver::UAAndUserCSSRules);
1228 
1229     removePropertiesInStyle(m_mutableStyle.get(), defaultStyle.get());
1230 }
1231 
forceInline()1232 void EditingStyle::forceInline()
1233 {
1234     if (!m_mutableStyle)
1235         m_mutableStyle = MutableStylePropertySet::create();
1236     const bool propertyIsImportant = true;
1237     m_mutableStyle->setProperty(CSSPropertyDisplay, CSSValueInline, propertyIsImportant);
1238 }
1239 
legacyFontSize(Document * document) const1240 int EditingStyle::legacyFontSize(Document* document) const
1241 {
1242     RefPtr<CSSValue> cssValue = m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize);
1243     if (!cssValue || !cssValue->isPrimitiveValue())
1244         return 0;
1245     return legacyFontSizeFromCSSValue(document, toCSSPrimitiveValue(cssValue.get()),
1246         m_shouldUseFixedDefaultFontSize, AlwaysUseLegacyFontSize);
1247 }
1248 
styleAtSelectionStart(const VisibleSelection & selection,bool shouldUseBackgroundColorInEffect)1249 PassRefPtr<EditingStyle> EditingStyle::styleAtSelectionStart(const VisibleSelection& selection, bool shouldUseBackgroundColorInEffect)
1250 {
1251     if (selection.isNone())
1252         return 0;
1253 
1254     Position position = adjustedSelectionStartForStyleComputation(selection);
1255 
1256     // If the pos is at the end of a text node, then this node is not fully selected.
1257     // Move it to the next deep equivalent position to avoid removing the style from this node.
1258     // e.g. if pos was at Position("hello", 5) in <b>hello<div>world</div></b>, we want Position("world", 0) instead.
1259     // We only do this for range because caret at Position("hello", 5) in <b>hello</b>world should give you font-weight: bold.
1260     Node* positionNode = position.containerNode();
1261     if (selection.isRange() && positionNode && positionNode->isTextNode() && position.computeOffsetInContainerNode() == positionNode->maxCharacterOffset())
1262         position = nextVisuallyDistinctCandidate(position);
1263 
1264     Element* element = position.element();
1265     if (!element)
1266         return 0;
1267 
1268     RefPtr<EditingStyle> style = EditingStyle::create(element, EditingStyle::AllProperties);
1269     style->mergeTypingStyle(&element->document());
1270 
1271     // If background color is transparent, traverse parent nodes until we hit a different value or document root
1272     // Also, if the selection is a range, ignore the background color at the start of selection,
1273     // and find the background color of the common ancestor.
1274     if (shouldUseBackgroundColorInEffect && (selection.isRange() || hasTransparentBackgroundColor(style->m_mutableStyle.get()))) {
1275         RefPtr<Range> range(selection.toNormalizedRange());
1276         if (PassRefPtr<CSSValue> value = backgroundColorInEffect(range->commonAncestorContainer(IGNORE_EXCEPTION)))
1277             style->setProperty(CSSPropertyBackgroundColor, value->cssText());
1278     }
1279 
1280     return style;
1281 }
1282 
textDirectionForSelection(const VisibleSelection & selection,EditingStyle * typingStyle,bool & hasNestedOrMultipleEmbeddings)1283 WritingDirection EditingStyle::textDirectionForSelection(const VisibleSelection& selection, EditingStyle* typingStyle, bool& hasNestedOrMultipleEmbeddings)
1284 {
1285     hasNestedOrMultipleEmbeddings = true;
1286 
1287     if (selection.isNone())
1288         return NaturalWritingDirection;
1289 
1290     Position position = selection.start().downstream();
1291 
1292     Node* node = position.deprecatedNode();
1293     if (!node)
1294         return NaturalWritingDirection;
1295 
1296     Position end;
1297     if (selection.isRange()) {
1298         end = selection.end().upstream();
1299 
1300         ASSERT(end.document());
1301         Node* pastLast = Range::create(*end.document(), position.parentAnchoredEquivalent(), end.parentAnchoredEquivalent())->pastLastNode();
1302         for (Node* n = node; n && n != pastLast; n = NodeTraversal::next(*n)) {
1303             if (!n->isStyledElement())
1304                 continue;
1305 
1306             RefPtr<CSSComputedStyleDeclaration> style = CSSComputedStyleDeclaration::create(n);
1307             RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
1308             if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
1309                 continue;
1310 
1311             CSSValueID unicodeBidiValue = toCSSPrimitiveValue(unicodeBidi.get())->getValueID();
1312             if (unicodeBidiValue == CSSValueEmbed || unicodeBidiValue == CSSValueBidiOverride)
1313                 return NaturalWritingDirection;
1314         }
1315     }
1316 
1317     if (selection.isCaret()) {
1318         WritingDirection direction;
1319         if (typingStyle && typingStyle->textDirection(direction)) {
1320             hasNestedOrMultipleEmbeddings = false;
1321             return direction;
1322         }
1323         node = selection.visibleStart().deepEquivalent().deprecatedNode();
1324     }
1325 
1326     // The selection is either a caret with no typing attributes or a range in which no embedding is added, so just use the start position
1327     // to decide.
1328     Node* block = enclosingBlock(node);
1329     WritingDirection foundDirection = NaturalWritingDirection;
1330 
1331     for (; node != block; node = node->parentNode()) {
1332         if (!node->isStyledElement())
1333             continue;
1334 
1335         RefPtr<CSSComputedStyleDeclaration> style = CSSComputedStyleDeclaration::create(node);
1336         RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
1337         if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
1338             continue;
1339 
1340         CSSValueID unicodeBidiValue = toCSSPrimitiveValue(unicodeBidi.get())->getValueID();
1341         if (unicodeBidiValue == CSSValueNormal)
1342             continue;
1343 
1344         if (unicodeBidiValue == CSSValueBidiOverride)
1345             return NaturalWritingDirection;
1346 
1347         ASSERT(unicodeBidiValue == CSSValueEmbed);
1348         RefPtr<CSSValue> direction = style->getPropertyCSSValue(CSSPropertyDirection);
1349         if (!direction || !direction->isPrimitiveValue())
1350             continue;
1351 
1352         int directionValue = toCSSPrimitiveValue(direction.get())->getValueID();
1353         if (directionValue != CSSValueLtr && directionValue != CSSValueRtl)
1354             continue;
1355 
1356         if (foundDirection != NaturalWritingDirection)
1357             return NaturalWritingDirection;
1358 
1359         // In the range case, make sure that the embedding element persists until the end of the range.
1360         if (selection.isRange() && !end.deprecatedNode()->isDescendantOf(node))
1361             return NaturalWritingDirection;
1362 
1363         foundDirection = directionValue == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
1364     }
1365     hasNestedOrMultipleEmbeddings = false;
1366     return foundDirection;
1367 }
1368 
reconcileTextDecorationProperties(MutableStylePropertySet * style)1369 static void reconcileTextDecorationProperties(MutableStylePropertySet* style)
1370 {
1371     RefPtr<CSSValue> textDecorationsInEffect = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
1372     RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(textDecorationPropertyForEditing());
1373     // We shouldn't have both text-decoration and -webkit-text-decorations-in-effect because that wouldn't make sense.
1374     ASSERT(!textDecorationsInEffect || !textDecoration);
1375     if (textDecorationsInEffect) {
1376         style->setProperty(textDecorationPropertyForEditing(), textDecorationsInEffect->cssText());
1377         style->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
1378         textDecoration = textDecorationsInEffect;
1379     }
1380 
1381     // If text-decoration is set to "none", remove the property because we don't want to add redundant "text-decoration: none".
1382     if (textDecoration && !textDecoration->isValueList())
1383         style->removeProperty(textDecorationPropertyForEditing());
1384 }
1385 
StyleChange(EditingStyle * style,const Position & position)1386 StyleChange::StyleChange(EditingStyle* style, const Position& position)
1387     : m_applyBold(false)
1388     , m_applyItalic(false)
1389     , m_applyUnderline(false)
1390     , m_applyLineThrough(false)
1391     , m_applySubscript(false)
1392     , m_applySuperscript(false)
1393 {
1394     Document* document = position.document();
1395     if (!style || !style->style() || !document || !document->frame())
1396         return;
1397 
1398     RefPtr<CSSComputedStyleDeclaration> computedStyle = position.computedStyle();
1399     // FIXME: take care of background-color in effect
1400     RefPtr<MutableStylePropertySet> mutableStyle = getPropertiesNotIn(style->style(), computedStyle.get());
1401 
1402     reconcileTextDecorationProperties(mutableStyle.get());
1403     if (!document->frame()->editor().shouldStyleWithCSS())
1404         extractTextStyles(document, mutableStyle.get(), computedStyle->useFixedFontDefaultSize());
1405 
1406     // Changing the whitespace style in a tab span would collapse the tab into a space.
1407     if (isTabSpanTextNode(position.deprecatedNode()) || isTabSpanNode((position.deprecatedNode())))
1408         mutableStyle->removeProperty(CSSPropertyWhiteSpace);
1409 
1410     // If unicode-bidi is present in mutableStyle and direction is not, then add direction to mutableStyle.
1411     // FIXME: Shouldn't this be done in getPropertiesNotIn?
1412     if (mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi) && !style->style()->getPropertyCSSValue(CSSPropertyDirection))
1413         mutableStyle->setProperty(CSSPropertyDirection, style->style()->getPropertyValue(CSSPropertyDirection));
1414 
1415     // Save the result for later
1416     m_cssStyle = mutableStyle->asText().stripWhiteSpace();
1417 }
1418 
setTextDecorationProperty(MutableStylePropertySet * style,const CSSValueList * newTextDecoration,CSSPropertyID propertyID)1419 static void setTextDecorationProperty(MutableStylePropertySet* style, const CSSValueList* newTextDecoration, CSSPropertyID propertyID)
1420 {
1421     if (newTextDecoration->length())
1422         style->setProperty(propertyID, newTextDecoration->cssText(), style->propertyIsImportant(propertyID));
1423     else {
1424         // text-decoration: none is redundant since it does not remove any text decorations.
1425         ASSERT(!style->propertyIsImportant(propertyID));
1426         style->removeProperty(propertyID);
1427     }
1428 }
1429 
extractTextStyles(Document * document,MutableStylePropertySet * style,bool shouldUseFixedFontDefaultSize)1430 void StyleChange::extractTextStyles(Document* document, MutableStylePropertySet* style, bool shouldUseFixedFontDefaultSize)
1431 {
1432     ASSERT(style);
1433 
1434     if (getIdentifierValue(style, CSSPropertyFontWeight) == CSSValueBold) {
1435         style->removeProperty(CSSPropertyFontWeight);
1436         m_applyBold = true;
1437     }
1438 
1439     int fontStyle = getIdentifierValue(style, CSSPropertyFontStyle);
1440     if (fontStyle == CSSValueItalic || fontStyle == CSSValueOblique) {
1441         style->removeProperty(CSSPropertyFontStyle);
1442         m_applyItalic = true;
1443     }
1444 
1445     // Assuming reconcileTextDecorationProperties has been called, there should not be -webkit-text-decorations-in-effect
1446     // Furthermore, text-decoration: none has been trimmed so that text-decoration property is always a CSSValueList.
1447     RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(textDecorationPropertyForEditing());
1448     if (textDecoration && textDecoration->isValueList()) {
1449         DEFINE_STATIC_REF(CSSPrimitiveValue, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
1450         DEFINE_STATIC_REF(CSSPrimitiveValue, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
1451 
1452         RefPtr<CSSValueList> newTextDecoration = toCSSValueList(textDecoration.get())->copy();
1453         if (newTextDecoration->removeAll(underline))
1454             m_applyUnderline = true;
1455         if (newTextDecoration->removeAll(lineThrough))
1456             m_applyLineThrough = true;
1457 
1458         // If trimTextDecorations, delete underline and line-through
1459         setTextDecorationProperty(style, newTextDecoration.get(), textDecorationPropertyForEditing());
1460     }
1461 
1462     int verticalAlign = getIdentifierValue(style, CSSPropertyVerticalAlign);
1463     switch (verticalAlign) {
1464     case CSSValueSub:
1465         style->removeProperty(CSSPropertyVerticalAlign);
1466         m_applySubscript = true;
1467         break;
1468     case CSSValueSuper:
1469         style->removeProperty(CSSPropertyVerticalAlign);
1470         m_applySuperscript = true;
1471         break;
1472     }
1473 
1474     if (style->getPropertyCSSValue(CSSPropertyColor)) {
1475         m_applyFontColor = Color(getRGBAFontColor(style)).serialized();
1476         style->removeProperty(CSSPropertyColor);
1477     }
1478 
1479     m_applyFontFace = style->getPropertyValue(CSSPropertyFontFamily);
1480     // Remove single quotes for Outlook 2007 compatibility. See https://bugs.webkit.org/show_bug.cgi?id=79448
1481     m_applyFontFace.replaceWithLiteral('\'', "");
1482     style->removeProperty(CSSPropertyFontFamily);
1483 
1484     if (RefPtr<CSSValue> fontSize = style->getPropertyCSSValue(CSSPropertyFontSize)) {
1485         if (!fontSize->isPrimitiveValue())
1486             style->removeProperty(CSSPropertyFontSize); // Can't make sense of the number. Put no font size.
1487         else if (int legacyFontSize = legacyFontSizeFromCSSValue(document, toCSSPrimitiveValue(fontSize.get()),
1488                 shouldUseFixedFontDefaultSize, UseLegacyFontSizeOnlyIfPixelValuesMatch)) {
1489             m_applyFontSize = String::number(legacyFontSize);
1490             style->removeProperty(CSSPropertyFontSize);
1491         }
1492     }
1493 }
1494 
diffTextDecorations(MutableStylePropertySet * style,CSSPropertyID propertID,CSSValue * refTextDecoration)1495 static void diffTextDecorations(MutableStylePropertySet* style, CSSPropertyID propertID, CSSValue* refTextDecoration)
1496 {
1497     RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(propertID);
1498     if (!textDecoration || !textDecoration->isValueList() || !refTextDecoration || !refTextDecoration->isValueList())
1499         return;
1500 
1501     RefPtr<CSSValueList> newTextDecoration = toCSSValueList(textDecoration.get())->copy();
1502     CSSValueList* valuesInRefTextDecoration = toCSSValueList(refTextDecoration);
1503 
1504     for (size_t i = 0; i < valuesInRefTextDecoration->length(); i++)
1505         newTextDecoration->removeAll(valuesInRefTextDecoration->item(i));
1506 
1507     setTextDecorationProperty(style, newTextDecoration.get(), propertID);
1508 }
1509 
fontWeightIsBold(CSSValue * fontWeight)1510 static bool fontWeightIsBold(CSSValue* fontWeight)
1511 {
1512     if (!fontWeight->isPrimitiveValue())
1513         return false;
1514 
1515     // Because b tag can only bold text, there are only two states in plain html: bold and not bold.
1516     // Collapse all other values to either one of these two states for editing purposes.
1517     switch (toCSSPrimitiveValue(fontWeight)->getValueID()) {
1518         case CSSValue100:
1519         case CSSValue200:
1520         case CSSValue300:
1521         case CSSValue400:
1522         case CSSValue500:
1523         case CSSValueNormal:
1524             return false;
1525         case CSSValueBold:
1526         case CSSValue600:
1527         case CSSValue700:
1528         case CSSValue800:
1529         case CSSValue900:
1530             return true;
1531         default:
1532             break;
1533     }
1534 
1535     ASSERT_NOT_REACHED(); // For CSSValueBolder and CSSValueLighter
1536     return false;
1537 }
1538 
fontWeightNeedsResolving(CSSValue * fontWeight)1539 static bool fontWeightNeedsResolving(CSSValue* fontWeight)
1540 {
1541     if (!fontWeight->isPrimitiveValue())
1542         return true;
1543 
1544     CSSValueID value = toCSSPrimitiveValue(fontWeight)->getValueID();
1545     return value == CSSValueLighter || value == CSSValueBolder;
1546 }
1547 
getPropertiesNotIn(StylePropertySet * styleWithRedundantProperties,CSSStyleDeclaration * baseStyle)1548 PassRefPtr<MutableStylePropertySet> getPropertiesNotIn(StylePropertySet* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle)
1549 {
1550     ASSERT(styleWithRedundantProperties);
1551     ASSERT(baseStyle);
1552     RefPtr<MutableStylePropertySet> result = styleWithRedundantProperties->mutableCopy();
1553 
1554     result->removeEquivalentProperties(baseStyle);
1555 
1556     RefPtr<CSSValue> baseTextDecorationsInEffect = baseStyle->getPropertyCSSValueInternal(CSSPropertyWebkitTextDecorationsInEffect);
1557     diffTextDecorations(result.get(), textDecorationPropertyForEditing(), baseTextDecorationsInEffect.get());
1558     diffTextDecorations(result.get(), CSSPropertyWebkitTextDecorationsInEffect, baseTextDecorationsInEffect.get());
1559 
1560     if (RefPtr<CSSValue> baseFontWeight = baseStyle->getPropertyCSSValueInternal(CSSPropertyFontWeight)) {
1561         if (RefPtr<CSSValue> fontWeight = result->getPropertyCSSValue(CSSPropertyFontWeight)) {
1562             if (!fontWeightNeedsResolving(fontWeight.get()) && (fontWeightIsBold(fontWeight.get()) == fontWeightIsBold(baseFontWeight.get())))
1563                 result->removeProperty(CSSPropertyFontWeight);
1564         }
1565     }
1566 
1567     if (baseStyle->getPropertyCSSValueInternal(CSSPropertyColor) && getRGBAFontColor(result.get()) == getRGBAFontColor(baseStyle))
1568         result->removeProperty(CSSPropertyColor);
1569 
1570     if (baseStyle->getPropertyCSSValueInternal(CSSPropertyTextAlign)
1571         && textAlignResolvingStartAndEnd(result.get()) == textAlignResolvingStartAndEnd(baseStyle))
1572         result->removeProperty(CSSPropertyTextAlign);
1573 
1574     if (baseStyle->getPropertyCSSValueInternal(CSSPropertyBackgroundColor) && getRGBABackgroundColor(result.get()) == getRGBABackgroundColor(baseStyle))
1575         result->removeProperty(CSSPropertyBackgroundColor);
1576 
1577     return result.release();
1578 }
1579 
getIdentifierValue(StylePropertySet * style,CSSPropertyID propertyID)1580 CSSValueID getIdentifierValue(StylePropertySet* style, CSSPropertyID propertyID)
1581 {
1582     if (!style)
1583         return CSSValueInvalid;
1584     RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID);
1585     if (!value || !value->isPrimitiveValue())
1586         return CSSValueInvalid;
1587     return toCSSPrimitiveValue(value.get())->getValueID();
1588 }
1589 
getIdentifierValue(CSSStyleDeclaration * style,CSSPropertyID propertyID)1590 CSSValueID getIdentifierValue(CSSStyleDeclaration* style, CSSPropertyID propertyID)
1591 {
1592     if (!style)
1593         return CSSValueInvalid;
1594     RefPtr<CSSValue> value = style->getPropertyCSSValueInternal(propertyID);
1595     if (!value || !value->isPrimitiveValue())
1596         return CSSValueInvalid;
1597     return toCSSPrimitiveValue(value.get())->getValueID();
1598 }
1599 
isCSSValueLength(CSSPrimitiveValue * value)1600 static bool isCSSValueLength(CSSPrimitiveValue* value)
1601 {
1602     return value->isFontIndependentLength();
1603 }
1604 
legacyFontSizeFromCSSValue(Document * document,CSSPrimitiveValue * value,bool shouldUseFixedFontDefaultSize,LegacyFontSizeMode mode)1605 int legacyFontSizeFromCSSValue(Document* document, CSSPrimitiveValue* value, bool shouldUseFixedFontDefaultSize, LegacyFontSizeMode mode)
1606 {
1607     if (isCSSValueLength(value)) {
1608         int pixelFontSize = value->getIntValue(CSSPrimitiveValue::CSS_PX);
1609         int legacyFontSize = FontSize::legacyFontSize(document, pixelFontSize, shouldUseFixedFontDefaultSize);
1610         // Use legacy font size only if pixel value matches exactly to that of legacy font size.
1611         int cssPrimitiveEquivalent = legacyFontSize - 1 + CSSValueXSmall;
1612         if (mode == AlwaysUseLegacyFontSize || FontSize::fontSizeForKeyword(document, cssPrimitiveEquivalent, shouldUseFixedFontDefaultSize) == pixelFontSize)
1613             return legacyFontSize;
1614 
1615         return 0;
1616     }
1617 
1618     if (CSSValueXSmall <= value->getValueID() && value->getValueID() <= CSSValueWebkitXxxLarge)
1619         return value->getValueID() - CSSValueXSmall + 1;
1620 
1621     return 0;
1622 }
1623 
isTransparentColorValue(CSSValue * cssValue)1624 bool isTransparentColorValue(CSSValue* cssValue)
1625 {
1626     if (!cssValue)
1627         return true;
1628     if (!cssValue->isPrimitiveValue())
1629         return false;
1630     CSSPrimitiveValue* value = toCSSPrimitiveValue(cssValue);
1631     if (value->isRGBColor())
1632         return !alphaChannel(value->getRGBA32Value());
1633     return value->getValueID() == CSSValueTransparent;
1634 }
1635 
hasTransparentBackgroundColor(CSSStyleDeclaration * style)1636 bool hasTransparentBackgroundColor(CSSStyleDeclaration* style)
1637 {
1638     RefPtr<CSSValue> cssValue = style->getPropertyCSSValueInternal(CSSPropertyBackgroundColor);
1639     return isTransparentColorValue(cssValue.get());
1640 }
1641 
hasTransparentBackgroundColor(StylePropertySet * style)1642 bool hasTransparentBackgroundColor(StylePropertySet* style)
1643 {
1644     RefPtr<CSSValue> cssValue = style->getPropertyCSSValue(CSSPropertyBackgroundColor);
1645     return isTransparentColorValue(cssValue.get());
1646 }
1647 
backgroundColorInEffect(Node * node)1648 PassRefPtr<CSSValue> backgroundColorInEffect(Node* node)
1649 {
1650     for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) {
1651         RefPtr<CSSComputedStyleDeclaration> ancestorStyle = CSSComputedStyleDeclaration::create(ancestor);
1652         if (!hasTransparentBackgroundColor(ancestorStyle.get()))
1653             return ancestorStyle->getPropertyCSSValue(CSSPropertyBackgroundColor);
1654     }
1655     return 0;
1656 }
1657 
1658 }
1659