2 * Copyright (C) 2007, 2008, 2009 Apple Computer, Inc.
3 * Copyright (C) 2010, 2011 Google Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
28 #include "core/editing/EditingStyle.h"
30 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
31 #include "core/HTMLNames.h"
32 #include "core/css/CSSComputedStyleDeclaration.h"
33 #include "core/css/CSSPropertyMetadata.h"
34 #include "core/css/CSSRuleList.h"
35 #include "core/css/CSSStyleRule.h"
36 #include "core/css/CSSValueList.h"
37 #include "core/css/CSSValuePool.h"
38 #include "core/css/FontSize.h"
39 #include "core/css/StylePropertySet.h"
40 #include "core/css/StyleRule.h"
41 #include "core/css/parser/CSSParser.h"
42 #include "core/css/resolver/StyleResolver.h"
43 #include "core/dom/Document.h"
44 #include "core/dom/Element.h"
45 #include "core/dom/Node.h"
46 #include "core/dom/NodeTraversal.h"
47 #include "core/dom/Position.h"
48 #include "core/dom/QualifiedName.h"
49 #include "core/editing/ApplyStyleCommand.h"
50 #include "core/editing/Editor.h"
51 #include "core/editing/FrameSelection.h"
52 #include "core/editing/HTMLInterchange.h"
53 #include "core/editing/htmlediting.h"
54 #include "core/frame/LocalFrame.h"
55 #include "core/html/HTMLFontElement.h"
56 #include "core/html/HTMLSpanElement.h"
57 #include "core/layout/LayoutBox.h"
58 #include "core/layout/LayoutObject.h"
59 #include "core/style/ComputedStyle.h"
63 static const CSSPropertyID
& textDecorationPropertyForEditing()
65 static const CSSPropertyID property
= RuntimeEnabledFeatures::css3TextDecorationsEnabled() ? CSSPropertyTextDecorationLine
: CSSPropertyTextDecoration
;
69 // Editing style properties must be preserved during editing operation.
70 // e.g. when a user inserts a new paragraph, all properties listed here must be copied to the new paragraph.
71 // NOTE: Use either allEditingProperties() or inheritableEditingProperties() to
72 // respect runtime enabling of properties.
73 static const CSSPropertyID staticEditingProperties
[] = {
74 CSSPropertyBackgroundColor
,
76 CSSPropertyFontFamily
,
79 CSSPropertyFontVariant
,
80 CSSPropertyFontWeight
,
81 CSSPropertyLetterSpacing
,
82 CSSPropertyLineHeight
,
85 // FIXME: CSSPropertyTextDecoration needs to be removed when CSS3 Text
86 // Decoration feature is no longer experimental.
87 CSSPropertyTextDecoration
,
88 CSSPropertyTextDecorationLine
,
89 CSSPropertyTextIndent
,
90 CSSPropertyTextTransform
,
91 CSSPropertyWhiteSpace
,
93 CSSPropertyWordSpacing
,
94 CSSPropertyWebkitTextDecorationsInEffect
,
95 CSSPropertyWebkitTextFillColor
,
96 CSSPropertyWebkitTextStrokeColor
,
97 CSSPropertyWebkitTextStrokeWidth
,
100 enum EditingPropertiesType
{ OnlyInheritableEditingProperties
, AllEditingProperties
};
102 static const Vector
<CSSPropertyID
>& allEditingProperties()
104 DEFINE_STATIC_LOCAL(Vector
<CSSPropertyID
>, properties
, ());
105 if (properties
.isEmpty()) {
106 CSSPropertyMetadata::filterEnabledCSSPropertiesIntoVector(staticEditingProperties
, WTF_ARRAY_LENGTH(staticEditingProperties
), properties
);
107 if (RuntimeEnabledFeatures::css3TextDecorationsEnabled())
108 properties
.remove(properties
.find(CSSPropertyTextDecoration
));
113 static const Vector
<CSSPropertyID
>& inheritableEditingProperties()
115 DEFINE_STATIC_LOCAL(Vector
<CSSPropertyID
>, properties
, ());
116 if (properties
.isEmpty()) {
117 CSSPropertyMetadata::filterEnabledCSSPropertiesIntoVector(staticEditingProperties
, WTF_ARRAY_LENGTH(staticEditingProperties
), properties
);
118 for (size_t index
= 0; index
< properties
.size();) {
119 if (!CSSPropertyMetadata::isInheritedProperty(properties
[index
])) {
120 properties
.remove(index
);
129 template <class StyleDeclarationType
>
130 static PassRefPtrWillBeRawPtr
<MutableStylePropertySet
> copyEditingProperties(StyleDeclarationType
* style
, EditingPropertiesType type
= OnlyInheritableEditingProperties
)
132 if (type
== AllEditingProperties
)
133 return style
->copyPropertiesInSet(allEditingProperties());
134 return style
->copyPropertiesInSet(inheritableEditingProperties());
137 static inline bool isEditingProperty(int id
)
139 return allEditingProperties().contains(static_cast<CSSPropertyID
>(id
));
142 static PassRefPtrWillBeRawPtr
<MutableStylePropertySet
> editingStyleFromComputedStyle(PassRefPtrWillBeRawPtr
<CSSComputedStyleDeclaration
> style
, EditingPropertiesType type
= OnlyInheritableEditingProperties
)
145 return MutableStylePropertySet::create();
146 return copyEditingProperties(style
.get(), type
);
149 static PassRefPtrWillBeRawPtr
<MutableStylePropertySet
> getPropertiesNotIn(StylePropertySet
* styleWithRedundantProperties
, CSSStyleDeclaration
* baseStyle
);
150 enum LegacyFontSizeMode
{ AlwaysUseLegacyFontSize
, UseLegacyFontSizeOnlyIfPixelValuesMatch
};
151 static int legacyFontSizeFromCSSValue(Document
*, CSSPrimitiveValue
*, bool, LegacyFontSizeMode
);
152 static bool isTransparentColorValue(CSSValue
*);
153 static bool hasTransparentBackgroundColor(CSSStyleDeclaration
*);
154 static bool hasTransparentBackgroundColor(StylePropertySet
*);
155 static PassRefPtrWillBeRawPtr
<CSSValue
> backgroundColorInEffect(Node
*);
157 class HTMLElementEquivalent
: public NoBaseWillBeGarbageCollected
<HTMLElementEquivalent
> {
158 WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED(HTMLElementEquivalent
);
159 DECLARE_EMPTY_VIRTUAL_DESTRUCTOR_WILL_BE_REMOVED(HTMLElementEquivalent
);
161 static PassOwnPtrWillBeRawPtr
<HTMLElementEquivalent
> create(CSSPropertyID propertyID
, CSSValueID primitiveValue
, const HTMLQualifiedName
& tagName
)
163 return adoptPtrWillBeNoop(new HTMLElementEquivalent(propertyID
, primitiveValue
, tagName
));
166 virtual bool matches(const Element
* element
) const { return !m_tagName
|| element
->hasTagName(*m_tagName
); }
167 virtual bool hasAttribute() const { return false; }
168 virtual bool propertyExistsInStyle(const StylePropertySet
* style
) const { return style
->getPropertyCSSValue(m_propertyID
); }
169 virtual bool valueIsPresentInStyle(HTMLElement
*, StylePropertySet
*) const;
170 virtual void addToStyle(Element
*, EditingStyle
*) const;
172 DEFINE_INLINE_VIRTUAL_TRACE() { visitor
->trace(m_primitiveValue
); }
175 HTMLElementEquivalent(CSSPropertyID
);
176 HTMLElementEquivalent(CSSPropertyID
, const HTMLQualifiedName
& tagName
);
177 HTMLElementEquivalent(CSSPropertyID
, CSSValueID primitiveValue
, const HTMLQualifiedName
& tagName
);
178 const CSSPropertyID m_propertyID
;
179 const RefPtrWillBeMember
<CSSPrimitiveValue
> m_primitiveValue
;
180 const HTMLQualifiedName
* m_tagName
; // We can store a pointer because HTML tag names are const global.
183 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(HTMLElementEquivalent
);
185 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id
)
191 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id
, const HTMLQualifiedName
& tagName
)
193 , m_tagName(&tagName
)
197 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id
, CSSValueID primitiveValue
, const HTMLQualifiedName
& tagName
)
199 , m_primitiveValue(CSSPrimitiveValue::createIdentifier(primitiveValue
))
200 , m_tagName(&tagName
)
202 ASSERT(primitiveValue
!= CSSValueInvalid
);
205 bool HTMLElementEquivalent::valueIsPresentInStyle(HTMLElement
* element
, StylePropertySet
* style
) const
207 RefPtrWillBeRawPtr
<CSSValue
> value
= style
->getPropertyCSSValue(m_propertyID
);
208 return matches(element
) && value
&& value
->isPrimitiveValue() && toCSSPrimitiveValue(value
.get())->getValueID() == m_primitiveValue
->getValueID();
211 void HTMLElementEquivalent::addToStyle(Element
*, EditingStyle
* style
) const
213 style
->setProperty(m_propertyID
, m_primitiveValue
->cssText());
216 class HTMLTextDecorationEquivalent final
: public HTMLElementEquivalent
{
218 static PassOwnPtrWillBeRawPtr
<HTMLElementEquivalent
> create(CSSValueID primitiveValue
, const HTMLQualifiedName
& tagName
)
220 return adoptPtrWillBeNoop(new HTMLTextDecorationEquivalent(primitiveValue
, tagName
));
222 virtual bool propertyExistsInStyle(const StylePropertySet
*) const override
;
223 virtual bool valueIsPresentInStyle(HTMLElement
*, StylePropertySet
*) const override
;
225 DEFINE_INLINE_VIRTUAL_TRACE() { HTMLElementEquivalent::trace(visitor
); }
228 HTMLTextDecorationEquivalent(CSSValueID primitiveValue
, const HTMLQualifiedName
& tagName
);
231 HTMLTextDecorationEquivalent::HTMLTextDecorationEquivalent(CSSValueID primitiveValue
, const HTMLQualifiedName
& tagName
)
232 : HTMLElementEquivalent(textDecorationPropertyForEditing(), primitiveValue
, tagName
)
233 // m_propertyID is used in HTMLElementEquivalent::addToStyle
237 bool HTMLTextDecorationEquivalent::propertyExistsInStyle(const StylePropertySet
* style
) const
239 return style
->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect
)
240 || style
->getPropertyCSSValue(textDecorationPropertyForEditing());
243 bool HTMLTextDecorationEquivalent::valueIsPresentInStyle(HTMLElement
* element
, StylePropertySet
* style
) const
245 RefPtrWillBeRawPtr
<CSSValue
> styleValue
= style
->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect
);
247 styleValue
= style
->getPropertyCSSValue(textDecorationPropertyForEditing());
248 return matches(element
) && styleValue
&& styleValue
->isValueList() && toCSSValueList(styleValue
.get())->hasValue(m_primitiveValue
.get());
251 class HTMLAttributeEquivalent
: public HTMLElementEquivalent
{
253 static PassOwnPtrWillBeRawPtr
<HTMLAttributeEquivalent
> create(CSSPropertyID propertyID
, const HTMLQualifiedName
& tagName
, const QualifiedName
& attrName
)
255 return adoptPtrWillBeNoop(new HTMLAttributeEquivalent(propertyID
, tagName
, attrName
));
257 static PassOwnPtrWillBeRawPtr
<HTMLAttributeEquivalent
> create(CSSPropertyID propertyID
, const QualifiedName
& attrName
)
259 return adoptPtrWillBeNoop(new HTMLAttributeEquivalent(propertyID
, attrName
));
262 virtual bool matches(const Element
* element
) const override
{ return HTMLElementEquivalent::matches(element
) && element
->hasAttribute(m_attrName
); }
263 virtual bool hasAttribute() const override
{ return true; }
264 virtual bool valueIsPresentInStyle(HTMLElement
*, StylePropertySet
*) const override
;
265 virtual void addToStyle(Element
*, EditingStyle
*) const override
;
266 virtual PassRefPtrWillBeRawPtr
<CSSValue
> attributeValueAsCSSValue(Element
*) const;
267 inline const QualifiedName
& attributeName() const { return m_attrName
; }
269 DEFINE_INLINE_VIRTUAL_TRACE() { HTMLElementEquivalent::trace(visitor
); }
272 HTMLAttributeEquivalent(CSSPropertyID
, const HTMLQualifiedName
& tagName
, const QualifiedName
& attrName
);
273 HTMLAttributeEquivalent(CSSPropertyID
, const QualifiedName
& attrName
);
274 const QualifiedName
& m_attrName
; // We can store a reference because HTML attribute names are const global.
277 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id
, const HTMLQualifiedName
& tagName
, const QualifiedName
& attrName
)
278 : HTMLElementEquivalent(id
, tagName
)
279 , m_attrName(attrName
)
283 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id
, const QualifiedName
& attrName
)
284 : HTMLElementEquivalent(id
)
285 , m_attrName(attrName
)
289 bool HTMLAttributeEquivalent::valueIsPresentInStyle(HTMLElement
* element
, StylePropertySet
* style
) const
291 RefPtrWillBeRawPtr
<CSSValue
> value
= attributeValueAsCSSValue(element
);
292 RefPtrWillBeRawPtr
<CSSValue
> styleValue
= style
->getPropertyCSSValue(m_propertyID
);
294 return compareCSSValuePtr(value
, styleValue
);
297 void HTMLAttributeEquivalent::addToStyle(Element
* element
, EditingStyle
* style
) const
299 if (RefPtrWillBeRawPtr
<CSSValue
> value
= attributeValueAsCSSValue(element
))
300 style
->setProperty(m_propertyID
, value
->cssText());
303 PassRefPtrWillBeRawPtr
<CSSValue
> HTMLAttributeEquivalent::attributeValueAsCSSValue(Element
* element
) const
306 const AtomicString
& value
= element
->getAttribute(m_attrName
);
310 RefPtrWillBeRawPtr
<MutableStylePropertySet
> dummyStyle
= nullptr;
311 dummyStyle
= MutableStylePropertySet::create();
312 dummyStyle
->setProperty(m_propertyID
, value
);
313 return dummyStyle
->getPropertyCSSValue(m_propertyID
);
316 class HTMLFontSizeEquivalent final
: public HTMLAttributeEquivalent
{
318 static PassOwnPtrWillBeRawPtr
<HTMLFontSizeEquivalent
> create()
320 return adoptPtrWillBeNoop(new HTMLFontSizeEquivalent());
322 virtual PassRefPtrWillBeRawPtr
<CSSValue
> attributeValueAsCSSValue(Element
*) const override
;
324 DEFINE_INLINE_VIRTUAL_TRACE() { HTMLAttributeEquivalent::trace(visitor
); }
327 HTMLFontSizeEquivalent();
330 HTMLFontSizeEquivalent::HTMLFontSizeEquivalent()
331 : HTMLAttributeEquivalent(CSSPropertyFontSize
, HTMLNames::fontTag
, HTMLNames::sizeAttr
)
335 PassRefPtrWillBeRawPtr
<CSSValue
> HTMLFontSizeEquivalent::attributeValueAsCSSValue(Element
* element
) const
338 const AtomicString
& value
= element
->getAttribute(m_attrName
);
342 if (!HTMLFontElement::cssValueFromFontSizeNumber(value
, size
))
344 return CSSPrimitiveValue::createIdentifier(size
);
347 float EditingStyle::NoFontDelta
= 0.0f
;
349 EditingStyle::EditingStyle()
350 : m_isMonospaceFont(false)
351 , m_fontSizeDelta(NoFontDelta
)
355 EditingStyle::EditingStyle(ContainerNode
* node
, PropertiesToInclude propertiesToInclude
)
356 : m_isMonospaceFont(false)
357 , m_fontSizeDelta(NoFontDelta
)
359 init(node
, propertiesToInclude
);
362 EditingStyle::EditingStyle(const Position
& position
, PropertiesToInclude propertiesToInclude
)
363 : m_isMonospaceFont(false)
364 , m_fontSizeDelta(NoFontDelta
)
366 init(position
.deprecatedNode(), propertiesToInclude
);
369 EditingStyle::EditingStyle(const StylePropertySet
* style
)
370 : m_mutableStyle(style
? style
->mutableCopy() : nullptr)
371 , m_isMonospaceFont(false)
372 , m_fontSizeDelta(NoFontDelta
)
374 extractFontSizeDelta();
377 EditingStyle::EditingStyle(CSSPropertyID propertyID
, const String
& value
)
378 : m_mutableStyle(nullptr)
379 , m_isMonospaceFont(false)
380 , m_fontSizeDelta(NoFontDelta
)
382 setProperty(propertyID
, value
);
385 EditingStyle::~EditingStyle()
389 static RGBA32
cssValueToRGBA(CSSValue
* colorValue
)
391 if (!colorValue
|| !colorValue
->isPrimitiveValue())
392 return Color::transparent
;
394 CSSPrimitiveValue
* primitiveColor
= toCSSPrimitiveValue(colorValue
);
395 if (primitiveColor
->isRGBColor())
396 return primitiveColor
->getRGBA32Value();
399 // FIXME: Why ignore the return value?
400 CSSParser::parseColor(rgba
, colorValue
->cssText());
404 static inline RGBA32
getRGBAFontColor(CSSStyleDeclaration
* style
)
406 return cssValueToRGBA(style
->getPropertyCSSValueInternal(CSSPropertyColor
).get());
409 static inline RGBA32
getRGBAFontColor(StylePropertySet
* style
)
411 return cssValueToRGBA(style
->getPropertyCSSValue(CSSPropertyColor
).get());
414 static inline RGBA32
getRGBABackgroundColor(CSSStyleDeclaration
* style
)
416 return cssValueToRGBA(style
->getPropertyCSSValueInternal(CSSPropertyBackgroundColor
).get());
419 static inline RGBA32
getRGBABackgroundColor(StylePropertySet
* style
)
421 return cssValueToRGBA(style
->getPropertyCSSValue(CSSPropertyBackgroundColor
).get());
424 static inline RGBA32
rgbaBackgroundColorInEffect(Node
* node
)
426 return cssValueToRGBA(backgroundColorInEffect(node
).get());
429 static int textAlignResolvingStartAndEnd(int textAlign
, int direction
)
433 case CSSValueWebkitCenter
:
434 return CSSValueCenter
;
435 case CSSValueJustify
:
436 return CSSValueJustify
;
438 case CSSValueWebkitLeft
:
441 case CSSValueWebkitRight
:
442 return CSSValueRight
;
444 return direction
!= CSSValueRtl
? CSSValueLeft
: CSSValueRight
;
446 return direction
== CSSValueRtl
? CSSValueRight
: CSSValueLeft
;
448 return CSSValueInvalid
;
452 static int textAlignResolvingStartAndEnd(T
* style
)
454 return textAlignResolvingStartAndEnd(getIdentifierValue(style
, CSSPropertyTextAlign
), getIdentifierValue(style
, CSSPropertyDirection
));
457 void EditingStyle::init(Node
* node
, PropertiesToInclude propertiesToInclude
)
459 if (isTabHTMLSpanElementTextNode(node
))
460 node
= tabSpanElement(node
)->parentNode();
461 else if (isTabHTMLSpanElement(node
))
462 node
= node
->parentNode();
464 RefPtrWillBeRawPtr
<CSSComputedStyleDeclaration
> computedStyleAtPosition
= CSSComputedStyleDeclaration::create(node
);
465 m_mutableStyle
= propertiesToInclude
== AllProperties
&& computedStyleAtPosition
? computedStyleAtPosition
->copyProperties() : editingStyleFromComputedStyle(computedStyleAtPosition
);
467 if (propertiesToInclude
== EditingPropertiesInEffect
) {
468 if (RefPtrWillBeRawPtr
<CSSValue
> value
= backgroundColorInEffect(node
))
469 m_mutableStyle
->setProperty(CSSPropertyBackgroundColor
, value
->cssText());
470 if (RefPtrWillBeRawPtr
<CSSValue
> value
= computedStyleAtPosition
->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect
))
471 m_mutableStyle
->setProperty(CSSPropertyTextDecoration
, value
->cssText());
474 if (node
&& node
->ensureComputedStyle()) {
475 const ComputedStyle
* computedStyle
= node
->ensureComputedStyle();
476 removeTextFillAndStrokeColorsIfNeeded(computedStyle
);
477 replaceFontSizeByKeywordIfPossible(computedStyle
, computedStyleAtPosition
.get());
480 m_isMonospaceFont
= computedStyleAtPosition
->isMonospaceFont();
481 extractFontSizeDelta();
484 void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(const ComputedStyle
* computedStyle
)
486 // If a node's text fill color is currentColor, then its children use
487 // their font-color as their text fill color (they don't
488 // inherit it). Likewise for stroke color.
489 if (computedStyle
->textFillColor().isCurrentColor())
490 m_mutableStyle
->removeProperty(CSSPropertyWebkitTextFillColor
);
491 if (computedStyle
->textStrokeColor().isCurrentColor())
492 m_mutableStyle
->removeProperty(CSSPropertyWebkitTextStrokeColor
);
495 void EditingStyle::setProperty(CSSPropertyID propertyID
, const String
& value
, bool important
)
498 m_mutableStyle
= MutableStylePropertySet::create();
500 m_mutableStyle
->setProperty(propertyID
, value
, important
);
503 void EditingStyle::replaceFontSizeByKeywordIfPossible(const ComputedStyle
* computedStyle
, CSSComputedStyleDeclaration
* cssComputedStyle
)
505 ASSERT(computedStyle
);
506 if (computedStyle
->fontDescription().keywordSize())
507 m_mutableStyle
->setProperty(CSSPropertyFontSize
, cssComputedStyle
->getFontSizeCSSValuePreferringKeyword()->cssText());
510 void EditingStyle::extractFontSizeDelta()
515 if (m_mutableStyle
->getPropertyCSSValue(CSSPropertyFontSize
)) {
516 // Explicit font size overrides any delta.
517 m_mutableStyle
->removeProperty(CSSPropertyWebkitFontSizeDelta
);
521 // Get the adjustment amount out of the style.
522 RefPtrWillBeRawPtr
<CSSValue
> value
= m_mutableStyle
->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta
);
523 if (!value
|| !value
->isPrimitiveValue())
526 CSSPrimitiveValue
* primitiveValue
= toCSSPrimitiveValue(value
.get());
528 // Only PX handled now. If we handle more types in the future, perhaps
529 // a switch statement here would be more appropriate.
530 if (!primitiveValue
->isPx())
533 m_fontSizeDelta
= primitiveValue
->getFloatValue();
534 m_mutableStyle
->removeProperty(CSSPropertyWebkitFontSizeDelta
);
537 bool EditingStyle::isEmpty() const
539 return (!m_mutableStyle
|| m_mutableStyle
->isEmpty()) && m_fontSizeDelta
== NoFontDelta
;
542 bool EditingStyle::textDirection(WritingDirection
& writingDirection
) const
547 RefPtrWillBeRawPtr
<CSSValue
> unicodeBidi
= m_mutableStyle
->getPropertyCSSValue(CSSPropertyUnicodeBidi
);
548 if (!unicodeBidi
|| !unicodeBidi
->isPrimitiveValue())
551 CSSValueID unicodeBidiValue
= toCSSPrimitiveValue(unicodeBidi
.get())->getValueID();
552 if (unicodeBidiValue
== CSSValueEmbed
) {
553 RefPtrWillBeRawPtr
<CSSValue
> direction
= m_mutableStyle
->getPropertyCSSValue(CSSPropertyDirection
);
554 if (!direction
|| !direction
->isPrimitiveValue())
557 writingDirection
= toCSSPrimitiveValue(direction
.get())->getValueID() == CSSValueLtr
? LeftToRightWritingDirection
: RightToLeftWritingDirection
;
562 if (unicodeBidiValue
== CSSValueNormal
) {
563 writingDirection
= NaturalWritingDirection
;
570 void EditingStyle::overrideWithStyle(const StylePropertySet
* style
)
572 if (!style
|| style
->isEmpty())
575 m_mutableStyle
= MutableStylePropertySet::create();
576 m_mutableStyle
->mergeAndOverrideOnConflict(style
);
577 extractFontSizeDelta();
580 void EditingStyle::clear()
582 m_mutableStyle
.clear();
583 m_isMonospaceFont
= false;
584 m_fontSizeDelta
= NoFontDelta
;
587 PassRefPtrWillBeRawPtr
<EditingStyle
> EditingStyle::copy() const
589 RefPtrWillBeRawPtr
<EditingStyle
> copy
= EditingStyle::create();
591 copy
->m_mutableStyle
= m_mutableStyle
->mutableCopy();
592 copy
->m_isMonospaceFont
= m_isMonospaceFont
;
593 copy
->m_fontSizeDelta
= m_fontSizeDelta
;
597 PassRefPtrWillBeRawPtr
<EditingStyle
> EditingStyle::extractAndRemoveBlockProperties()
599 RefPtrWillBeRawPtr
<EditingStyle
> blockProperties
= EditingStyle::create();
601 return blockProperties
;
603 blockProperties
->m_mutableStyle
= m_mutableStyle
->copyBlockProperties();
604 m_mutableStyle
->removeBlockProperties();
606 return blockProperties
;
609 PassRefPtrWillBeRawPtr
<EditingStyle
> EditingStyle::extractAndRemoveTextDirection()
611 RefPtrWillBeRawPtr
<EditingStyle
> textDirection
= EditingStyle::create();
612 textDirection
->m_mutableStyle
= MutableStylePropertySet::create();
613 textDirection
->m_mutableStyle
->setProperty(CSSPropertyUnicodeBidi
, CSSValueEmbed
, m_mutableStyle
->propertyIsImportant(CSSPropertyUnicodeBidi
));
614 textDirection
->m_mutableStyle
->setProperty(CSSPropertyDirection
, m_mutableStyle
->getPropertyValue(CSSPropertyDirection
),
615 m_mutableStyle
->propertyIsImportant(CSSPropertyDirection
));
617 m_mutableStyle
->removeProperty(CSSPropertyUnicodeBidi
);
618 m_mutableStyle
->removeProperty(CSSPropertyDirection
);
620 return textDirection
;
623 void EditingStyle::removeBlockProperties()
628 m_mutableStyle
->removeBlockProperties();
631 void EditingStyle::removeStyleAddedByElement(Element
* element
)
633 if (!element
|| !element
->parentNode())
635 RefPtrWillBeRawPtr
<MutableStylePropertySet
> parentStyle
= editingStyleFromComputedStyle(CSSComputedStyleDeclaration::create(element
->parentNode()), AllEditingProperties
);
636 RefPtrWillBeRawPtr
<MutableStylePropertySet
> nodeStyle
= editingStyleFromComputedStyle(CSSComputedStyleDeclaration::create(element
), AllEditingProperties
);
637 nodeStyle
->removeEquivalentProperties(parentStyle
.get());
638 m_mutableStyle
->removeEquivalentProperties(nodeStyle
.get());
641 void EditingStyle::removeStyleConflictingWithStyleOfElement(Element
* element
)
643 if (!element
|| !element
->parentNode() || !m_mutableStyle
)
646 RefPtrWillBeRawPtr
<MutableStylePropertySet
> parentStyle
= editingStyleFromComputedStyle(CSSComputedStyleDeclaration::create(element
->parentNode()), AllEditingProperties
);
647 RefPtrWillBeRawPtr
<MutableStylePropertySet
> nodeStyle
= editingStyleFromComputedStyle(CSSComputedStyleDeclaration::create(element
), AllEditingProperties
);
648 nodeStyle
->removeEquivalentProperties(parentStyle
.get());
650 unsigned propertyCount
= nodeStyle
->propertyCount();
651 for (unsigned i
= 0; i
< propertyCount
; ++i
)
652 m_mutableStyle
->removeProperty(nodeStyle
->propertyAt(i
).id());
655 void EditingStyle::collapseTextDecorationProperties()
660 RefPtrWillBeRawPtr
<CSSValue
> textDecorationsInEffect
= m_mutableStyle
->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect
);
661 if (!textDecorationsInEffect
)
664 if (textDecorationsInEffect
->isValueList())
665 m_mutableStyle
->setProperty(textDecorationPropertyForEditing(), textDecorationsInEffect
->cssText(), m_mutableStyle
->propertyIsImportant(textDecorationPropertyForEditing()));
667 m_mutableStyle
->removeProperty(textDecorationPropertyForEditing());
668 m_mutableStyle
->removeProperty(CSSPropertyWebkitTextDecorationsInEffect
);
671 // CSS properties that create a visual difference only when applied to text.
672 static const CSSPropertyID textOnlyProperties
[] = {
673 // FIXME: CSSPropertyTextDecoration needs to be removed when CSS3 Text
674 // Decoration feature is no longer experimental.
675 CSSPropertyTextDecoration
,
676 CSSPropertyTextDecorationLine
,
677 CSSPropertyWebkitTextDecorationsInEffect
,
678 CSSPropertyFontStyle
,
679 CSSPropertyFontWeight
,
683 TriState
EditingStyle::triStateOfStyle(EditingStyle
* style
) const
685 if (!style
|| !style
->m_mutableStyle
)
686 return FalseTriState
;
687 return triStateOfStyle(style
->m_mutableStyle
->ensureCSSStyleDeclaration(), DoNotIgnoreTextOnlyProperties
);
690 TriState
EditingStyle::triStateOfStyle(CSSStyleDeclaration
* styleToCompare
, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties
) const
692 RefPtrWillBeRawPtr
<MutableStylePropertySet
> difference
= getPropertiesNotIn(m_mutableStyle
.get(), styleToCompare
);
694 if (shouldIgnoreTextOnlyProperties
== IgnoreTextOnlyProperties
)
695 difference
->removePropertiesInSet(textOnlyProperties
, WTF_ARRAY_LENGTH(textOnlyProperties
));
697 if (difference
->isEmpty())
699 if (difference
->propertyCount() == m_mutableStyle
->propertyCount())
700 return FalseTriState
;
702 return MixedTriState
;
705 TriState
EditingStyle::triStateOfStyle(const VisibleSelection
& selection
) const
707 if (!selection
.isCaretOrRange())
708 return FalseTriState
;
710 if (selection
.isCaret())
711 return triStateOfStyle(EditingStyle::styleAtSelectionStart(selection
).get());
713 TriState state
= FalseTriState
;
714 bool nodeIsStart
= true;
715 for (Node
& node
: NodeTraversal::startsAt(selection
.start().deprecatedNode())) {
716 if (node
.layoutObject() && node
.hasEditableStyle()) {
717 RefPtrWillBeRawPtr
<CSSComputedStyleDeclaration
> nodeStyle
= CSSComputedStyleDeclaration::create(&node
);
719 TriState nodeState
= triStateOfStyle(nodeStyle
.get(), node
.isTextNode() ? EditingStyle::DoNotIgnoreTextOnlyProperties
: EditingStyle::IgnoreTextOnlyProperties
);
723 } else if (state
!= nodeState
&& node
.isTextNode()) {
724 state
= MixedTriState
;
729 if (&node
== selection
.end().deprecatedNode())
736 bool EditingStyle::conflictsWithInlineStyleOfElement(HTMLElement
* element
, EditingStyle
* extractedStyle
, Vector
<CSSPropertyID
>* conflictingProperties
) const
739 ASSERT(!conflictingProperties
|| conflictingProperties
->isEmpty());
741 const StylePropertySet
* inlineStyle
= element
->inlineStyle();
742 if (!m_mutableStyle
|| !inlineStyle
)
745 unsigned propertyCount
= m_mutableStyle
->propertyCount();
746 for (unsigned i
= 0; i
< propertyCount
; ++i
) {
747 CSSPropertyID propertyID
= m_mutableStyle
->propertyAt(i
).id();
749 // We don't override whitespace property of a tab span because that would collapse the tab into a space.
750 if (propertyID
== CSSPropertyWhiteSpace
&& isTabHTMLSpanElement(element
))
753 if (propertyID
== CSSPropertyWebkitTextDecorationsInEffect
&& inlineStyle
->getPropertyCSSValue(textDecorationPropertyForEditing())) {
754 if (!conflictingProperties
)
756 conflictingProperties
->append(CSSPropertyTextDecoration
);
757 // Because text-decoration expands to text-decoration-line when CSS3
758 // Text Decoration is enabled, we also state it as conflicting.
759 if (RuntimeEnabledFeatures::css3TextDecorationsEnabled())
760 conflictingProperties
->append(CSSPropertyTextDecorationLine
);
762 extractedStyle
->setProperty(textDecorationPropertyForEditing(), inlineStyle
->getPropertyValue(textDecorationPropertyForEditing()), inlineStyle
->propertyIsImportant(textDecorationPropertyForEditing()));
766 if (!inlineStyle
->getPropertyCSSValue(propertyID
))
769 if (propertyID
== CSSPropertyUnicodeBidi
&& inlineStyle
->getPropertyCSSValue(CSSPropertyDirection
)) {
770 if (!conflictingProperties
)
772 conflictingProperties
->append(CSSPropertyDirection
);
774 extractedStyle
->setProperty(propertyID
, inlineStyle
->getPropertyValue(propertyID
), inlineStyle
->propertyIsImportant(propertyID
));
777 if (!conflictingProperties
)
780 conflictingProperties
->append(propertyID
);
783 extractedStyle
->setProperty(propertyID
, inlineStyle
->getPropertyValue(propertyID
), inlineStyle
->propertyIsImportant(propertyID
));
786 return conflictingProperties
&& !conflictingProperties
->isEmpty();
789 static const WillBeHeapVector
<OwnPtrWillBeMember
<HTMLElementEquivalent
>>& htmlElementEquivalents()
791 DEFINE_STATIC_LOCAL(WillBePersistentHeapVector
<OwnPtrWillBeMember
<HTMLElementEquivalent
>>, HTMLElementEquivalents
, ());
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
));
800 HTMLElementEquivalents
.append(HTMLTextDecorationEquivalent::create(CSSValueUnderline
, HTMLNames::uTag
));
801 HTMLElementEquivalents
.append(HTMLTextDecorationEquivalent::create(CSSValueLineThrough
, HTMLNames::sTag
));
802 HTMLElementEquivalents
.append(HTMLTextDecorationEquivalent::create(CSSValueLineThrough
, HTMLNames::strikeTag
));
805 return HTMLElementEquivalents
;
809 bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement
* element
, EditingStyle
* extractedStyle
, ShouldExtractMatchingStyle shouldExtractMatchingStyle
) const
814 const WillBeHeapVector
<OwnPtrWillBeMember
<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()))) {
820 equivalent
->addToStyle(element
, extractedStyle
);
827 static const WillBeHeapVector
<OwnPtrWillBeMember
<HTMLAttributeEquivalent
>>& htmlAttributeEquivalents()
829 DEFINE_STATIC_LOCAL(WillBePersistentHeapVector
<OwnPtrWillBeMember
<HTMLAttributeEquivalent
>>, HTMLAttributeEquivalents
, ());
830 if (!HTMLAttributeEquivalents
.size()) {
831 // elementIsStyledSpanOrHTMLEquivalent depends on the fact each HTMLAttriuteEquivalent matches exactly one attribute
832 // of exactly one element except dirAttr.
833 HTMLAttributeEquivalents
.append(HTMLAttributeEquivalent::create(CSSPropertyColor
, HTMLNames::fontTag
, HTMLNames::colorAttr
));
834 HTMLAttributeEquivalents
.append(HTMLAttributeEquivalent::create(CSSPropertyFontFamily
, HTMLNames::fontTag
, HTMLNames::faceAttr
));
835 HTMLAttributeEquivalents
.append(HTMLFontSizeEquivalent::create());
837 HTMLAttributeEquivalents
.append(HTMLAttributeEquivalent::create(CSSPropertyDirection
, HTMLNames::dirAttr
));
838 HTMLAttributeEquivalents
.append(HTMLAttributeEquivalent::create(CSSPropertyUnicodeBidi
, HTMLNames::dirAttr
));
841 return HTMLAttributeEquivalents
;
844 bool EditingStyle::conflictsWithImplicitStyleOfAttributes(HTMLElement
* element
) const
850 const WillBeHeapVector
<OwnPtrWillBeMember
<HTMLAttributeEquivalent
>>& HTMLAttributeEquivalents
= htmlAttributeEquivalents();
851 for (const auto& equivalent
: HTMLAttributeEquivalents
) {
852 if (equivalent
->matches(element
) && equivalent
->propertyExistsInStyle(m_mutableStyle
.get())
853 && !equivalent
->valueIsPresentInStyle(element
, m_mutableStyle
.get()))
860 bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement
* element
, ShouldPreserveWritingDirection shouldPreserveWritingDirection
,
861 EditingStyle
* extractedStyle
, Vector
<QualifiedName
>& conflictingAttributes
, ShouldExtractMatchingStyle shouldExtractMatchingStyle
) const
864 // HTMLAttributeEquivalent::addToStyle doesn't support unicode-bidi and direction properties
865 ASSERT(!extractedStyle
|| shouldPreserveWritingDirection
== PreserveWritingDirection
);
869 const WillBeHeapVector
<OwnPtrWillBeMember
<HTMLAttributeEquivalent
>>& HTMLAttributeEquivalents
= htmlAttributeEquivalents();
870 bool removed
= false;
871 for (const auto& attribute
: HTMLAttributeEquivalents
) {
872 const HTMLAttributeEquivalent
* equivalent
= attribute
.get();
874 // unicode-bidi and direction are pushed down separately so don't push down with other styles.
875 if (shouldPreserveWritingDirection
== PreserveWritingDirection
&& equivalent
->attributeName() == HTMLNames::dirAttr
)
878 if (!equivalent
->matches(element
) || !equivalent
->propertyExistsInStyle(m_mutableStyle
.get())
879 || (shouldExtractMatchingStyle
== DoNotExtractMatchingStyle
&& equivalent
->valueIsPresentInStyle(element
, m_mutableStyle
.get())))
883 equivalent
->addToStyle(element
, extractedStyle
);
884 conflictingAttributes
.append(equivalent
->attributeName());
891 bool EditingStyle::styleIsPresentInComputedStyleOfNode(Node
* node
) const
893 return !m_mutableStyle
|| getPropertiesNotIn(m_mutableStyle
.get(), CSSComputedStyleDeclaration::create(node
).get())->isEmpty();
896 bool EditingStyle::elementIsStyledSpanOrHTMLEquivalent(const HTMLElement
* element
)
899 bool elementIsSpanOrElementEquivalent
= false;
900 if (isHTMLSpanElement(*element
))
901 elementIsSpanOrElementEquivalent
= true;
903 const WillBeHeapVector
<OwnPtrWillBeMember
<HTMLElementEquivalent
>>& HTMLElementEquivalents
= htmlElementEquivalents();
905 for (i
= 0; i
< HTMLElementEquivalents
.size(); ++i
) {
906 if (HTMLElementEquivalents
[i
]->matches(element
)) {
907 elementIsSpanOrElementEquivalent
= true;
913 AttributeCollection attributes
= element
->attributes();
914 if (attributes
.isEmpty())
915 return elementIsSpanOrElementEquivalent
; // span, b, etc... without any attributes
917 unsigned matchedAttributes
= 0;
918 const WillBeHeapVector
<OwnPtrWillBeMember
<HTMLAttributeEquivalent
>>& HTMLAttributeEquivalents
= htmlAttributeEquivalents();
919 for (const auto& equivalent
: HTMLAttributeEquivalents
) {
920 if (equivalent
->matches(element
) && equivalent
->attributeName() != HTMLNames::dirAttr
)
924 if (!elementIsSpanOrElementEquivalent
&& !matchedAttributes
)
925 return false; // element is not a span, a html element equivalent, or font element.
927 if (element
->getAttribute(HTMLNames::classAttr
) == AppleStyleSpanClass
)
930 if (element
->hasAttribute(HTMLNames::styleAttr
)) {
931 if (const StylePropertySet
* style
= element
->inlineStyle()) {
932 unsigned propertyCount
= style
->propertyCount();
933 for (unsigned i
= 0; i
< propertyCount
; ++i
) {
934 if (!isEditingProperty(style
->propertyAt(i
).id()))
941 // font with color attribute, span with style attribute, etc...
942 ASSERT(matchedAttributes
<= attributes
.size());
943 return matchedAttributes
>= attributes
.size();
946 void EditingStyle::prepareToApplyAt(const Position
& position
, ShouldPreserveWritingDirection shouldPreserveWritingDirection
)
951 // ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style.
952 // If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate
953 // which one of editingStyleAtPosition or computedStyle is called.
954 RefPtrWillBeRawPtr
<EditingStyle
> editingStyleAtPosition
= EditingStyle::create(position
, EditingPropertiesInEffect
);
955 StylePropertySet
* styleAtPosition
= editingStyleAtPosition
->m_mutableStyle
.get();
957 RefPtrWillBeRawPtr
<CSSValue
> unicodeBidi
= nullptr;
958 RefPtrWillBeRawPtr
<CSSValue
> direction
= nullptr;
959 if (shouldPreserveWritingDirection
== PreserveWritingDirection
) {
960 unicodeBidi
= m_mutableStyle
->getPropertyCSSValue(CSSPropertyUnicodeBidi
);
961 direction
= m_mutableStyle
->getPropertyCSSValue(CSSPropertyDirection
);
964 m_mutableStyle
->removeEquivalentProperties(styleAtPosition
);
966 if (textAlignResolvingStartAndEnd(m_mutableStyle
.get()) == textAlignResolvingStartAndEnd(styleAtPosition
))
967 m_mutableStyle
->removeProperty(CSSPropertyTextAlign
);
969 if (getRGBAFontColor(m_mutableStyle
.get()) == getRGBAFontColor(styleAtPosition
))
970 m_mutableStyle
->removeProperty(CSSPropertyColor
);
972 if (hasTransparentBackgroundColor(m_mutableStyle
.get())
973 || cssValueToRGBA(m_mutableStyle
->getPropertyCSSValue(CSSPropertyBackgroundColor
).get()) == rgbaBackgroundColorInEffect(position
.containerNode()))
974 m_mutableStyle
->removeProperty(CSSPropertyBackgroundColor
);
976 if (unicodeBidi
&& unicodeBidi
->isPrimitiveValue()) {
977 m_mutableStyle
->setProperty(CSSPropertyUnicodeBidi
, toCSSPrimitiveValue(unicodeBidi
.get())->getValueID());
978 if (direction
&& direction
->isPrimitiveValue())
979 m_mutableStyle
->setProperty(CSSPropertyDirection
, toCSSPrimitiveValue(direction
.get())->getValueID());
983 void EditingStyle::mergeTypingStyle(Document
* document
)
987 RefPtrWillBeRawPtr
<EditingStyle
> typingStyle
= document
->frame()->selection().typingStyle();
988 if (!typingStyle
|| typingStyle
== this)
991 mergeStyle(typingStyle
->style(), OverrideValues
);
994 void EditingStyle::mergeInlineStyleOfElement(HTMLElement
* element
, CSSPropertyOverrideMode mode
, PropertiesToInclude propertiesToInclude
)
997 if (!element
->inlineStyle())
1000 switch (propertiesToInclude
) {
1002 mergeStyle(element
->inlineStyle(), mode
);
1004 case OnlyEditingInheritableProperties
:
1005 mergeStyle(copyEditingProperties(element
->inlineStyle(), OnlyInheritableEditingProperties
).get(), mode
);
1007 case EditingPropertiesInEffect
:
1008 mergeStyle(copyEditingProperties(element
->inlineStyle(), AllEditingProperties
).get(), mode
);
1013 static inline bool elementMatchesAndPropertyIsNotInInlineStyleDecl(const HTMLElementEquivalent
* equivalent
, const Element
* element
,
1014 EditingStyle::CSSPropertyOverrideMode mode
, StylePropertySet
* style
)
1016 return equivalent
->matches(element
) && (!element
->inlineStyle() || !equivalent
->propertyExistsInStyle(element
->inlineStyle()))
1017 && (mode
== EditingStyle::OverrideValues
|| !equivalent
->propertyExistsInStyle(style
));
1020 static PassRefPtrWillBeRawPtr
<MutableStylePropertySet
> extractEditingProperties(const StylePropertySet
* style
, EditingStyle::PropertiesToInclude propertiesToInclude
)
1025 switch (propertiesToInclude
) {
1026 case EditingStyle::AllProperties
:
1027 case EditingStyle::EditingPropertiesInEffect
:
1028 return copyEditingProperties(style
, AllEditingProperties
);
1029 case EditingStyle::OnlyEditingInheritableProperties
:
1030 return copyEditingProperties(style
, OnlyInheritableEditingProperties
);
1033 ASSERT_NOT_REACHED();
1037 void EditingStyle::mergeInlineAndImplicitStyleOfElement(Element
* element
, CSSPropertyOverrideMode mode
, PropertiesToInclude propertiesToInclude
)
1039 RefPtrWillBeRawPtr
<EditingStyle
> styleFromRules
= EditingStyle::create();
1040 styleFromRules
->mergeStyleFromRulesForSerialization(element
);
1042 if (element
->inlineStyle())
1043 styleFromRules
->m_mutableStyle
->mergeAndOverrideOnConflict(element
->inlineStyle());
1045 styleFromRules
->m_mutableStyle
= extractEditingProperties(styleFromRules
->m_mutableStyle
.get(), propertiesToInclude
);
1046 mergeStyle(styleFromRules
->m_mutableStyle
.get(), mode
);
1048 const WillBeHeapVector
<OwnPtrWillBeMember
<HTMLElementEquivalent
>>& elementEquivalents
= htmlElementEquivalents();
1049 for (const auto& equivalent
: elementEquivalents
) {
1050 if (elementMatchesAndPropertyIsNotInInlineStyleDecl(equivalent
.get(), element
, mode
, m_mutableStyle
.get()))
1051 equivalent
->addToStyle(element
, this);
1054 const WillBeHeapVector
<OwnPtrWillBeMember
<HTMLAttributeEquivalent
>>& attributeEquivalents
= htmlAttributeEquivalents();
1055 for (const auto& attribute
: attributeEquivalents
) {
1056 if (attribute
->attributeName() == HTMLNames::dirAttr
)
1057 continue; // We don't want to include directionality
1058 if (elementMatchesAndPropertyIsNotInInlineStyleDecl(attribute
.get(), element
, mode
, m_mutableStyle
.get()))
1059 attribute
->addToStyle(element
, this);
1063 PassRefPtrWillBeRawPtr
<EditingStyle
> EditingStyle::wrappingStyleForSerialization(ContainerNode
* context
, bool shouldAnnotate
)
1065 RefPtrWillBeRawPtr
<EditingStyle
> wrappingStyle
= nullptr;
1066 if (shouldAnnotate
) {
1067 wrappingStyle
= EditingStyle::create(context
, EditingStyle::EditingPropertiesInEffect
);
1069 // Styles that Mail blockquotes contribute should only be placed on the Mail blockquote,
1070 // to help us differentiate those styles from ones that the user has applied.
1071 // This helps us get the color of content pasted into blockquotes right.
1072 wrappingStyle
->removeStyleAddedByElement(toHTMLElement(enclosingNodeOfType(firstPositionInOrBeforeNode(context
), isMailHTMLBlockquoteElement
, CanCrossEditingBoundary
)));
1074 // Call collapseTextDecorationProperties first or otherwise it'll copy the value over from in-effect to text-decorations.
1075 wrappingStyle
->collapseTextDecorationProperties();
1077 return wrappingStyle
.release();
1080 wrappingStyle
= EditingStyle::create();
1082 // When not annotating for interchange, we only preserve inline style declarations.
1083 for (ContainerNode
* node
= context
; node
&& !node
->isDocumentNode(); node
= node
->parentNode()) {
1084 if (node
->isStyledElement() && !isMailHTMLBlockquoteElement(node
)) {
1085 wrappingStyle
->mergeInlineAndImplicitStyleOfElement(toElement(node
), EditingStyle::DoNotOverrideValues
,
1086 EditingStyle::EditingPropertiesInEffect
);
1090 return wrappingStyle
.release();
1094 static void mergeTextDecorationValues(CSSValueList
* mergedValue
, const CSSValueList
* valueToMerge
)
1096 DEFINE_STATIC_REF_WILL_BE_PERSISTENT(CSSPrimitiveValue
, underline
, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline
)));
1097 DEFINE_STATIC_REF_WILL_BE_PERSISTENT(CSSPrimitiveValue
, lineThrough
, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough
)));
1098 if (valueToMerge
->hasValue(underline
) && !mergedValue
->hasValue(underline
))
1099 mergedValue
->append(underline
);
1101 if (valueToMerge
->hasValue(lineThrough
) && !mergedValue
->hasValue(lineThrough
))
1102 mergedValue
->append(lineThrough
);
1105 void EditingStyle::mergeStyle(const StylePropertySet
* style
, CSSPropertyOverrideMode mode
)
1110 if (!m_mutableStyle
) {
1111 m_mutableStyle
= style
->mutableCopy();
1115 unsigned propertyCount
= style
->propertyCount();
1116 for (unsigned i
= 0; i
< propertyCount
; ++i
) {
1117 StylePropertySet::PropertyReference property
= style
->propertyAt(i
);
1118 RefPtrWillBeRawPtr
<CSSValue
> value
= m_mutableStyle
->getPropertyCSSValue(property
.id());
1120 // text decorations never override values
1121 if ((property
.id() == textDecorationPropertyForEditing() || property
.id() == CSSPropertyWebkitTextDecorationsInEffect
) && property
.value()->isValueList() && value
) {
1122 if (value
->isValueList()) {
1123 mergeTextDecorationValues(toCSSValueList(value
.get()), toCSSValueList(property
.value()));
1126 value
= nullptr; // text-decoration: none is equivalent to not having the property
1129 if (mode
== OverrideValues
|| (mode
== DoNotOverrideValues
&& !value
))
1130 m_mutableStyle
->setProperty(property
.id(), property
.value()->cssText(), property
.isImportant());
1134 static PassRefPtrWillBeRawPtr
<MutableStylePropertySet
> styleFromMatchedRulesForElement(Element
* element
, unsigned rulesToInclude
)
1136 RefPtrWillBeRawPtr
<MutableStylePropertySet
> style
= MutableStylePropertySet::create();
1137 RefPtrWillBeRawPtr
<StyleRuleList
> matchedRules
= element
->document().ensureStyleResolver().styleRulesForElement(element
, rulesToInclude
);
1139 for (unsigned i
= 0; i
< matchedRules
->m_list
.size(); ++i
)
1140 style
->mergeAndOverrideOnConflict(&matchedRules
->m_list
[i
]->properties());
1142 return style
.release();
1145 void EditingStyle::mergeStyleFromRules(Element
* element
)
1147 RefPtrWillBeRawPtr
<MutableStylePropertySet
> styleFromMatchedRules
= styleFromMatchedRulesForElement(element
,
1148 StyleResolver::AuthorCSSRules
| StyleResolver::CrossOriginCSSRules
);
1149 // Styles from the inline style declaration, held in the variable "style", take precedence
1150 // over those from matched rules.
1152 styleFromMatchedRules
->mergeAndOverrideOnConflict(m_mutableStyle
.get());
1155 m_mutableStyle
= styleFromMatchedRules
;
1158 void EditingStyle::mergeStyleFromRulesForSerialization(Element
* element
)
1160 mergeStyleFromRules(element
);
1162 // The property value, if it's a percentage, may not reflect the actual computed value.
1163 // For example: style="height: 1%; overflow: visible;" in quirksmode
1164 // FIXME: There are others like this, see <rdar://problem/5195123> Slashdot copy/paste fidelity problem
1165 RefPtrWillBeRawPtr
<CSSComputedStyleDeclaration
> computedStyleForElement
= CSSComputedStyleDeclaration::create(element
);
1166 RefPtrWillBeRawPtr
<MutableStylePropertySet
> fromComputedStyle
= MutableStylePropertySet::create();
1168 unsigned propertyCount
= m_mutableStyle
->propertyCount();
1169 for (unsigned i
= 0; i
< propertyCount
; ++i
) {
1170 StylePropertySet::PropertyReference property
= m_mutableStyle
->propertyAt(i
);
1171 CSSValue
* value
= property
.value();
1172 if (!value
->isPrimitiveValue())
1174 if (toCSSPrimitiveValue(value
)->isPercentage()) {
1175 if (RefPtrWillBeRawPtr
<CSSValue
> computedPropertyValue
= computedStyleForElement
->getPropertyCSSValue(property
.id()))
1176 fromComputedStyle
->addRespectingCascade(CSSProperty(property
.id(), computedPropertyValue
));
1180 m_mutableStyle
->mergeAndOverrideOnConflict(fromComputedStyle
.get());
1183 static void removePropertiesInStyle(MutableStylePropertySet
* styleToRemovePropertiesFrom
, StylePropertySet
* style
)
1185 unsigned propertyCount
= style
->propertyCount();
1186 Vector
<CSSPropertyID
> propertiesToRemove(propertyCount
);
1187 for (unsigned i
= 0; i
< propertyCount
; ++i
)
1188 propertiesToRemove
[i
] = style
->propertyAt(i
).id();
1190 styleToRemovePropertiesFrom
->removePropertiesInSet(propertiesToRemove
.data(), propertiesToRemove
.size());
1193 void EditingStyle::removeStyleFromRulesAndContext(Element
* element
, ContainerNode
* context
)
1196 if (!m_mutableStyle
)
1199 // 1. Remove style from matched rules because style remain without repeating it in inline style declaration
1200 RefPtrWillBeRawPtr
<MutableStylePropertySet
> styleFromMatchedRules
= styleFromMatchedRulesForElement(element
, StyleResolver::AllButEmptyCSSRules
);
1201 if (styleFromMatchedRules
&& !styleFromMatchedRules
->isEmpty())
1202 m_mutableStyle
= getPropertiesNotIn(m_mutableStyle
.get(), styleFromMatchedRules
->ensureCSSStyleDeclaration());
1204 // 2. Remove style present in context and not overriden by matched rules.
1205 RefPtrWillBeRawPtr
<EditingStyle
> computedStyle
= EditingStyle::create(context
, EditingPropertiesInEffect
);
1206 if (computedStyle
->m_mutableStyle
) {
1207 if (!computedStyle
->m_mutableStyle
->getPropertyCSSValue(CSSPropertyBackgroundColor
))
1208 computedStyle
->m_mutableStyle
->setProperty(CSSPropertyBackgroundColor
, CSSValueTransparent
);
1210 removePropertiesInStyle(computedStyle
->m_mutableStyle
.get(), styleFromMatchedRules
.get());
1211 m_mutableStyle
= getPropertiesNotIn(m_mutableStyle
.get(), computedStyle
->m_mutableStyle
->ensureCSSStyleDeclaration());
1214 // 3. If this element is a span and has display: inline or float: none, remove them unless they are overriden by rules.
1215 // These rules are added by serialization code to wrap text nodes.
1216 if (isStyleSpanOrSpanWithOnlyStyleAttribute(element
)) {
1217 if (!styleFromMatchedRules
->getPropertyCSSValue(CSSPropertyDisplay
) && getIdentifierValue(m_mutableStyle
.get(), CSSPropertyDisplay
) == CSSValueInline
)
1218 m_mutableStyle
->removeProperty(CSSPropertyDisplay
);
1219 if (!styleFromMatchedRules
->getPropertyCSSValue(CSSPropertyFloat
) && getIdentifierValue(m_mutableStyle
.get(), CSSPropertyFloat
) == CSSValueNone
)
1220 m_mutableStyle
->removeProperty(CSSPropertyFloat
);
1224 void EditingStyle::removePropertiesInElementDefaultStyle(Element
* element
)
1226 if (!m_mutableStyle
|| m_mutableStyle
->isEmpty())
1229 RefPtrWillBeRawPtr
<StylePropertySet
> defaultStyle
= styleFromMatchedRulesForElement(element
, StyleResolver::UAAndUserCSSRules
);
1231 removePropertiesInStyle(m_mutableStyle
.get(), defaultStyle
.get());
1234 void EditingStyle::addAbsolutePositioningFromElement(const Element
& element
)
1236 LayoutRect rect
= element
.boundingBox();
1237 LayoutObject
* renderer
= element
.layoutObject();
1239 LayoutUnit x
= rect
.x();
1240 LayoutUnit y
= rect
.y();
1241 LayoutUnit width
= rect
.width();
1242 LayoutUnit height
= rect
.height();
1243 if (renderer
&& renderer
->isBox()) {
1244 LayoutBox
* layoutBox
= toLayoutBox(renderer
);
1246 x
-= layoutBox
->marginLeft();
1247 y
-= layoutBox
->marginTop();
1249 m_mutableStyle
->setProperty(CSSPropertyBoxSizing
, CSSValueBorderBox
);
1252 m_mutableStyle
->setProperty(CSSPropertyPosition
, CSSValueAbsolute
);
1253 m_mutableStyle
->setProperty(CSSPropertyLeft
, cssValuePool().createValue(x
, CSSPrimitiveValue::CSS_PX
));
1254 m_mutableStyle
->setProperty(CSSPropertyTop
, cssValuePool().createValue(y
, CSSPrimitiveValue::CSS_PX
));
1255 m_mutableStyle
->setProperty(CSSPropertyWidth
, cssValuePool().createValue(width
, CSSPrimitiveValue::CSS_PX
));
1256 m_mutableStyle
->setProperty(CSSPropertyHeight
, cssValuePool().createValue(height
, CSSPrimitiveValue::CSS_PX
));
1259 void EditingStyle::forceInline()
1261 if (!m_mutableStyle
)
1262 m_mutableStyle
= MutableStylePropertySet::create();
1263 const bool propertyIsImportant
= true;
1264 m_mutableStyle
->setProperty(CSSPropertyDisplay
, CSSValueInline
, propertyIsImportant
);
1267 int EditingStyle::legacyFontSize(Document
* document
) const
1269 RefPtrWillBeRawPtr
<CSSValue
> cssValue
= m_mutableStyle
->getPropertyCSSValue(CSSPropertyFontSize
);
1270 if (!cssValue
|| !cssValue
->isPrimitiveValue())
1272 return legacyFontSizeFromCSSValue(document
, toCSSPrimitiveValue(cssValue
.get()),
1273 m_isMonospaceFont
, AlwaysUseLegacyFontSize
);
1276 PassRefPtrWillBeRawPtr
<EditingStyle
> EditingStyle::styleAtSelectionStart(const VisibleSelection
& selection
, bool shouldUseBackgroundColorInEffect
)
1278 if (selection
.isNone())
1281 Position position
= adjustedSelectionStartForStyleComputation(selection
);
1283 // If the pos is at the end of a text node, then this node is not fully selected.
1284 // Move it to the next deep equivalent position to avoid removing the style from this node.
1285 // e.g. if pos was at Position("hello", 5) in <b>hello<div>world</div></b>, we want Position("world", 0) instead.
1286 // We only do this for range because caret at Position("hello", 5) in <b>hello</b>world should give you font-weight: bold.
1287 Node
* positionNode
= position
.containerNode();
1288 if (selection
.isRange() && positionNode
&& positionNode
->isTextNode() && position
.computeOffsetInContainerNode() == positionNode
->maxCharacterOffset())
1289 position
= nextVisuallyDistinctCandidate(position
);
1291 Element
* element
= position
.element();
1295 RefPtrWillBeRawPtr
<EditingStyle
> style
= EditingStyle::create(element
, EditingStyle::AllProperties
);
1296 style
->mergeTypingStyle(&element
->document());
1298 // If background color is transparent, traverse parent nodes until we hit a different value or document root
1299 // Also, if the selection is a range, ignore the background color at the start of selection,
1300 // and find the background color of the common ancestor.
1301 if (shouldUseBackgroundColorInEffect
&& (selection
.isRange() || hasTransparentBackgroundColor(style
->m_mutableStyle
.get()))) {
1302 RefPtrWillBeRawPtr
<Range
> range(selection
.toNormalizedRange());
1303 if (PassRefPtrWillBeRawPtr
<CSSValue
> value
= backgroundColorInEffect(range
->commonAncestorContainer()))
1304 style
->setProperty(CSSPropertyBackgroundColor
, value
->cssText());
1310 WritingDirection
EditingStyle::textDirectionForSelection(const VisibleSelection
& selection
, EditingStyle
* typingStyle
, bool& hasNestedOrMultipleEmbeddings
)
1312 hasNestedOrMultipleEmbeddings
= true;
1314 if (selection
.isNone())
1315 return NaturalWritingDirection
;
1317 Position position
= selection
.start().downstream();
1319 Node
* node
= position
.deprecatedNode();
1321 return NaturalWritingDirection
;
1324 if (selection
.isRange()) {
1325 end
= selection
.end().upstream();
1327 ASSERT(end
.document());
1328 Node
* pastLast
= Range::create(*end
.document(), position
.parentAnchoredEquivalent(), end
.parentAnchoredEquivalent())->pastLastNode();
1329 for (Node
* n
= node
; n
&& n
!= pastLast
; n
= NodeTraversal::next(*n
)) {
1330 if (!n
->isStyledElement())
1333 RefPtrWillBeRawPtr
<CSSComputedStyleDeclaration
> style
= CSSComputedStyleDeclaration::create(n
);
1334 RefPtrWillBeRawPtr
<CSSValue
> unicodeBidi
= style
->getPropertyCSSValue(CSSPropertyUnicodeBidi
);
1335 if (!unicodeBidi
|| !unicodeBidi
->isPrimitiveValue())
1338 CSSValueID unicodeBidiValue
= toCSSPrimitiveValue(unicodeBidi
.get())->getValueID();
1339 if (unicodeBidiValue
== CSSValueEmbed
|| unicodeBidiValue
== CSSValueBidiOverride
)
1340 return NaturalWritingDirection
;
1344 if (selection
.isCaret()) {
1345 WritingDirection direction
;
1346 if (typingStyle
&& typingStyle
->textDirection(direction
)) {
1347 hasNestedOrMultipleEmbeddings
= false;
1350 node
= selection
.visibleStart().deepEquivalent().deprecatedNode();
1353 // 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
1355 Node
* block
= enclosingBlock(node
);
1356 WritingDirection foundDirection
= NaturalWritingDirection
;
1358 for (; node
!= block
; node
= node
->parentNode()) {
1359 if (!node
->isStyledElement())
1362 Element
* element
= toElement(node
);
1363 RefPtrWillBeRawPtr
<CSSComputedStyleDeclaration
> style
= CSSComputedStyleDeclaration::create(element
);
1364 RefPtrWillBeRawPtr
<CSSValue
> unicodeBidi
= style
->getPropertyCSSValue(CSSPropertyUnicodeBidi
);
1365 if (!unicodeBidi
|| !unicodeBidi
->isPrimitiveValue())
1368 CSSValueID unicodeBidiValue
= toCSSPrimitiveValue(unicodeBidi
.get())->getValueID();
1369 if (unicodeBidiValue
== CSSValueNormal
)
1372 if (unicodeBidiValue
== CSSValueBidiOverride
)
1373 return NaturalWritingDirection
;
1375 ASSERT(unicodeBidiValue
== CSSValueEmbed
);
1376 RefPtrWillBeRawPtr
<CSSValue
> direction
= style
->getPropertyCSSValue(CSSPropertyDirection
);
1377 if (!direction
|| !direction
->isPrimitiveValue())
1380 int directionValue
= toCSSPrimitiveValue(direction
.get())->getValueID();
1381 if (directionValue
!= CSSValueLtr
&& directionValue
!= CSSValueRtl
)
1384 if (foundDirection
!= NaturalWritingDirection
)
1385 return NaturalWritingDirection
;
1387 // In the range case, make sure that the embedding element persists until the end of the range.
1388 if (selection
.isRange() && !end
.deprecatedNode()->isDescendantOf(element
))
1389 return NaturalWritingDirection
;
1391 foundDirection
= directionValue
== CSSValueLtr
? LeftToRightWritingDirection
: RightToLeftWritingDirection
;
1393 hasNestedOrMultipleEmbeddings
= false;
1394 return foundDirection
;
1397 DEFINE_TRACE(EditingStyle
)
1399 visitor
->trace(m_mutableStyle
);
1402 static void reconcileTextDecorationProperties(MutableStylePropertySet
* style
)
1404 RefPtrWillBeRawPtr
<CSSValue
> textDecorationsInEffect
= style
->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect
);
1405 RefPtrWillBeRawPtr
<CSSValue
> textDecoration
= style
->getPropertyCSSValue(textDecorationPropertyForEditing());
1406 // We shouldn't have both text-decoration and -webkit-text-decorations-in-effect because that wouldn't make sense.
1407 ASSERT(!textDecorationsInEffect
|| !textDecoration
);
1408 if (textDecorationsInEffect
) {
1409 style
->setProperty(textDecorationPropertyForEditing(), textDecorationsInEffect
->cssText());
1410 style
->removeProperty(CSSPropertyWebkitTextDecorationsInEffect
);
1411 textDecoration
= textDecorationsInEffect
;
1414 // If text-decoration is set to "none", remove the property because we don't want to add redundant "text-decoration: none".
1415 if (textDecoration
&& !textDecoration
->isValueList())
1416 style
->removeProperty(textDecorationPropertyForEditing());
1419 StyleChange::StyleChange(EditingStyle
* style
, const Position
& position
)
1420 : m_applyBold(false)
1421 , m_applyItalic(false)
1422 , m_applyUnderline(false)
1423 , m_applyLineThrough(false)
1424 , m_applySubscript(false)
1425 , m_applySuperscript(false)
1427 Document
* document
= position
.document();
1428 if (!style
|| !style
->style() || !document
|| !document
->frame() || !position
.element())
1431 RefPtrWillBeRawPtr
<CSSComputedStyleDeclaration
> computedStyle
= position
.ensureComputedStyle();
1432 // FIXME: take care of background-color in effect
1433 RefPtrWillBeRawPtr
<MutableStylePropertySet
> mutableStyle
= getPropertiesNotIn(style
->style(), computedStyle
.get());
1434 ASSERT(mutableStyle
);
1436 reconcileTextDecorationProperties(mutableStyle
.get());
1437 if (!document
->frame()->editor().shouldStyleWithCSS())
1438 extractTextStyles(document
, mutableStyle
.get(), computedStyle
->isMonospaceFont());
1440 // Changing the whitespace style in a tab span would collapse the tab into a space.
1441 if (isTabHTMLSpanElementTextNode(position
.deprecatedNode()) || isTabHTMLSpanElement((position
.deprecatedNode())))
1442 mutableStyle
->removeProperty(CSSPropertyWhiteSpace
);
1444 // If unicode-bidi is present in mutableStyle and direction is not, then add direction to mutableStyle.
1445 // FIXME: Shouldn't this be done in getPropertiesNotIn?
1446 if (mutableStyle
->getPropertyCSSValue(CSSPropertyUnicodeBidi
) && !style
->style()->getPropertyCSSValue(CSSPropertyDirection
))
1447 mutableStyle
->setProperty(CSSPropertyDirection
, style
->style()->getPropertyValue(CSSPropertyDirection
));
1449 // Save the result for later
1450 m_cssStyle
= mutableStyle
->asText().stripWhiteSpace();
1453 static void setTextDecorationProperty(MutableStylePropertySet
* style
, const CSSValueList
* newTextDecoration
, CSSPropertyID propertyID
)
1455 if (newTextDecoration
->length())
1456 style
->setProperty(propertyID
, newTextDecoration
->cssText(), style
->propertyIsImportant(propertyID
));
1458 // text-decoration: none is redundant since it does not remove any text decorations.
1459 style
->removeProperty(propertyID
);
1463 void StyleChange::extractTextStyles(Document
* document
, MutableStylePropertySet
* style
, bool isMonospaceFont
)
1467 if (getIdentifierValue(style
, CSSPropertyFontWeight
) == CSSValueBold
) {
1468 style
->removeProperty(CSSPropertyFontWeight
);
1472 int fontStyle
= getIdentifierValue(style
, CSSPropertyFontStyle
);
1473 if (fontStyle
== CSSValueItalic
|| fontStyle
== CSSValueOblique
) {
1474 style
->removeProperty(CSSPropertyFontStyle
);
1475 m_applyItalic
= true;
1478 // Assuming reconcileTextDecorationProperties has been called, there should not be -webkit-text-decorations-in-effect
1479 // Furthermore, text-decoration: none has been trimmed so that text-decoration property is always a CSSValueList.
1480 RefPtrWillBeRawPtr
<CSSValue
> textDecoration
= style
->getPropertyCSSValue(textDecorationPropertyForEditing());
1481 if (textDecoration
&& textDecoration
->isValueList()) {
1482 DEFINE_STATIC_REF_WILL_BE_PERSISTENT(CSSPrimitiveValue
, underline
, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline
)));
1483 DEFINE_STATIC_REF_WILL_BE_PERSISTENT(CSSPrimitiveValue
, lineThrough
, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough
)));
1484 RefPtrWillBeRawPtr
<CSSValueList
> newTextDecoration
= toCSSValueList(textDecoration
.get())->copy();
1485 if (newTextDecoration
->removeAll(underline
))
1486 m_applyUnderline
= true;
1487 if (newTextDecoration
->removeAll(lineThrough
))
1488 m_applyLineThrough
= true;
1490 // If trimTextDecorations, delete underline and line-through
1491 setTextDecorationProperty(style
, newTextDecoration
.get(), textDecorationPropertyForEditing());
1494 int verticalAlign
= getIdentifierValue(style
, CSSPropertyVerticalAlign
);
1495 switch (verticalAlign
) {
1497 style
->removeProperty(CSSPropertyVerticalAlign
);
1498 m_applySubscript
= true;
1501 style
->removeProperty(CSSPropertyVerticalAlign
);
1502 m_applySuperscript
= true;
1506 if (style
->getPropertyCSSValue(CSSPropertyColor
)) {
1507 m_applyFontColor
= Color(getRGBAFontColor(style
)).serialized();
1508 style
->removeProperty(CSSPropertyColor
);
1511 m_applyFontFace
= style
->getPropertyValue(CSSPropertyFontFamily
);
1512 // Remove single quotes for Outlook 2007 compatibility. See https://bugs.webkit.org/show_bug.cgi?id=79448
1513 m_applyFontFace
.replaceWithLiteral('\'', "");
1514 style
->removeProperty(CSSPropertyFontFamily
);
1516 if (RefPtrWillBeRawPtr
<CSSValue
> fontSize
= style
->getPropertyCSSValue(CSSPropertyFontSize
)) {
1517 if (!fontSize
->isPrimitiveValue()) {
1518 style
->removeProperty(CSSPropertyFontSize
); // Can't make sense of the number. Put no font size.
1519 } else if (int legacyFontSize
= legacyFontSizeFromCSSValue(document
, toCSSPrimitiveValue(fontSize
.get()), isMonospaceFont
, UseLegacyFontSizeOnlyIfPixelValuesMatch
)) {
1520 m_applyFontSize
= String::number(legacyFontSize
);
1521 style
->removeProperty(CSSPropertyFontSize
);
1526 static void diffTextDecorations(MutableStylePropertySet
* style
, CSSPropertyID propertID
, CSSValue
* refTextDecoration
)
1528 RefPtrWillBeRawPtr
<CSSValue
> textDecoration
= style
->getPropertyCSSValue(propertID
);
1529 if (!textDecoration
|| !textDecoration
->isValueList() || !refTextDecoration
|| !refTextDecoration
->isValueList())
1532 RefPtrWillBeRawPtr
<CSSValueList
> newTextDecoration
= toCSSValueList(textDecoration
.get())->copy();
1533 CSSValueList
* valuesInRefTextDecoration
= toCSSValueList(refTextDecoration
);
1535 for (size_t i
= 0; i
< valuesInRefTextDecoration
->length(); i
++)
1536 newTextDecoration
->removeAll(valuesInRefTextDecoration
->item(i
));
1538 setTextDecorationProperty(style
, newTextDecoration
.get(), propertID
);
1541 static bool fontWeightIsBold(CSSValue
* fontWeight
)
1543 if (!fontWeight
->isPrimitiveValue())
1546 // Because b tag can only bold text, there are only two states in plain html: bold and not bold.
1547 // Collapse all other values to either one of these two states for editing purposes.
1548 switch (toCSSPrimitiveValue(fontWeight
)->getValueID()) {
1554 case CSSValueNormal
:
1566 ASSERT_NOT_REACHED(); // For CSSValueBolder and CSSValueLighter
1570 static bool fontWeightNeedsResolving(CSSValue
* fontWeight
)
1572 if (!fontWeight
->isPrimitiveValue())
1575 CSSValueID value
= toCSSPrimitiveValue(fontWeight
)->getValueID();
1576 return value
== CSSValueLighter
|| value
== CSSValueBolder
;
1579 PassRefPtrWillBeRawPtr
<MutableStylePropertySet
> getPropertiesNotIn(StylePropertySet
* styleWithRedundantProperties
, CSSStyleDeclaration
* baseStyle
)
1581 ASSERT(styleWithRedundantProperties
);
1583 RefPtrWillBeRawPtr
<MutableStylePropertySet
> result
= styleWithRedundantProperties
->mutableCopy();
1585 result
->removeEquivalentProperties(baseStyle
);
1587 RefPtrWillBeRawPtr
<CSSValue
> baseTextDecorationsInEffect
= baseStyle
->getPropertyCSSValueInternal(CSSPropertyWebkitTextDecorationsInEffect
);
1588 diffTextDecorations(result
.get(), textDecorationPropertyForEditing(), baseTextDecorationsInEffect
.get());
1589 diffTextDecorations(result
.get(), CSSPropertyWebkitTextDecorationsInEffect
, baseTextDecorationsInEffect
.get());
1591 if (RefPtrWillBeRawPtr
<CSSValue
> baseFontWeight
= baseStyle
->getPropertyCSSValueInternal(CSSPropertyFontWeight
)) {
1592 if (RefPtrWillBeRawPtr
<CSSValue
> fontWeight
= result
->getPropertyCSSValue(CSSPropertyFontWeight
)) {
1593 if (!fontWeightNeedsResolving(fontWeight
.get()) && (fontWeightIsBold(fontWeight
.get()) == fontWeightIsBold(baseFontWeight
.get())))
1594 result
->removeProperty(CSSPropertyFontWeight
);
1598 if (baseStyle
->getPropertyCSSValueInternal(CSSPropertyColor
) && getRGBAFontColor(result
.get()) == getRGBAFontColor(baseStyle
))
1599 result
->removeProperty(CSSPropertyColor
);
1601 if (baseStyle
->getPropertyCSSValueInternal(CSSPropertyTextAlign
)
1602 && textAlignResolvingStartAndEnd(result
.get()) == textAlignResolvingStartAndEnd(baseStyle
))
1603 result
->removeProperty(CSSPropertyTextAlign
);
1605 if (baseStyle
->getPropertyCSSValueInternal(CSSPropertyBackgroundColor
) && getRGBABackgroundColor(result
.get()) == getRGBABackgroundColor(baseStyle
))
1606 result
->removeProperty(CSSPropertyBackgroundColor
);
1608 return result
.release();
1611 CSSValueID
getIdentifierValue(StylePropertySet
* style
, CSSPropertyID propertyID
)
1614 return CSSValueInvalid
;
1615 RefPtrWillBeRawPtr
<CSSValue
> value
= style
->getPropertyCSSValue(propertyID
);
1616 if (!value
|| !value
->isPrimitiveValue())
1617 return CSSValueInvalid
;
1618 return toCSSPrimitiveValue(value
.get())->getValueID();
1621 CSSValueID
getIdentifierValue(CSSStyleDeclaration
* style
, CSSPropertyID propertyID
)
1624 return CSSValueInvalid
;
1625 RefPtrWillBeRawPtr
<CSSValue
> value
= style
->getPropertyCSSValueInternal(propertyID
);
1626 if (!value
|| !value
->isPrimitiveValue())
1627 return CSSValueInvalid
;
1628 return toCSSPrimitiveValue(value
.get())->getValueID();
1631 static bool isCSSValueLength(CSSPrimitiveValue
* value
)
1633 return value
->isFontIndependentLength();
1636 int legacyFontSizeFromCSSValue(Document
* document
, CSSPrimitiveValue
* value
, bool isMonospaceFont
, LegacyFontSizeMode mode
)
1638 if (isCSSValueLength(value
)) {
1639 int pixelFontSize
= clampTo
<int>(value
->deprecatedGetDoubleValue());
1640 int legacyFontSize
= FontSize::legacyFontSize(document
, pixelFontSize
, isMonospaceFont
);
1641 // Use legacy font size only if pixel value matches exactly to that of legacy font size.
1642 if (mode
== AlwaysUseLegacyFontSize
|| FontSize::fontSizeForKeyword(document
, legacyFontSize
, isMonospaceFont
) == pixelFontSize
)
1643 return legacyFontSize
;
1648 if (CSSValueXSmall
<= value
->getValueID() && value
->getValueID() <= CSSValueWebkitXxxLarge
)
1649 return value
->getValueID() - CSSValueXSmall
+ 1;
1654 bool isTransparentColorValue(CSSValue
* cssValue
)
1658 if (!cssValue
->isPrimitiveValue())
1660 CSSPrimitiveValue
* value
= toCSSPrimitiveValue(cssValue
);
1661 if (value
->isRGBColor())
1662 return !alphaChannel(value
->getRGBA32Value());
1663 return value
->getValueID() == CSSValueTransparent
;
1666 bool hasTransparentBackgroundColor(CSSStyleDeclaration
* style
)
1668 RefPtrWillBeRawPtr
<CSSValue
> cssValue
= style
->getPropertyCSSValueInternal(CSSPropertyBackgroundColor
);
1669 return isTransparentColorValue(cssValue
.get());
1672 bool hasTransparentBackgroundColor(StylePropertySet
* style
)
1674 RefPtrWillBeRawPtr
<CSSValue
> cssValue
= style
->getPropertyCSSValue(CSSPropertyBackgroundColor
);
1675 return isTransparentColorValue(cssValue
.get());
1678 PassRefPtrWillBeRawPtr
<CSSValue
> backgroundColorInEffect(Node
* node
)
1680 for (Node
* ancestor
= node
; ancestor
; ancestor
= ancestor
->parentNode()) {
1681 RefPtrWillBeRawPtr
<CSSComputedStyleDeclaration
> ancestorStyle
= CSSComputedStyleDeclaration::create(ancestor
);
1682 if (!hasTransparentBackgroundColor(ancestorStyle
.get()))
1683 return ancestorStyle
->getPropertyCSSValue(CSSPropertyBackgroundColor
);