CSSStyleDeclaration::setProperty should override important style
[chromium-blink-merge.git] / third_party / WebKit / Source / core / editing / EditingStyle.cpp
blobb4b6102ca1e48af6f42c4bf26bb0e7b5754045eb
1 /*
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
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.
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.
27 #include "config.h"
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"
61 namespace blink {
63 static const CSSPropertyID& textDecorationPropertyForEditing()
65 static const CSSPropertyID property = RuntimeEnabledFeatures::css3TextDecorationsEnabled() ? CSSPropertyTextDecorationLine : CSSPropertyTextDecoration;
66 return property;
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,
75 CSSPropertyColor,
76 CSSPropertyFontFamily,
77 CSSPropertyFontSize,
78 CSSPropertyFontStyle,
79 CSSPropertyFontVariant,
80 CSSPropertyFontWeight,
81 CSSPropertyLetterSpacing,
82 CSSPropertyLineHeight,
83 CSSPropertyOrphans,
84 CSSPropertyTextAlign,
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,
92 CSSPropertyWidows,
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));
110 return properties;
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);
121 continue;
123 ++index;
126 return properties;
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)
144 if (!style)
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);
160 public:
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); }
174 protected:
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)
186 : m_propertyID(id)
187 , m_tagName(0)
191 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, const HTMLQualifiedName& tagName)
192 : m_propertyID(id)
193 , m_tagName(&tagName)
197 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, CSSValueID primitiveValue, const HTMLQualifiedName& tagName)
198 : m_propertyID(id)
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 {
217 public:
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); }
227 private:
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);
246 if (!styleValue)
247 styleValue = style->getPropertyCSSValue(textDecorationPropertyForEditing());
248 return matches(element) && styleValue && styleValue->isValueList() && toCSSValueList(styleValue.get())->hasValue(m_primitiveValue.get());
251 class HTMLAttributeEquivalent : public HTMLElementEquivalent {
252 public:
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); }
271 protected:
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
305 ASSERT(element);
306 const AtomicString& value = element->getAttribute(m_attrName);
307 if (value.isNull())
308 return nullptr;
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 {
317 public:
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); }
326 private:
327 HTMLFontSizeEquivalent();
330 HTMLFontSizeEquivalent::HTMLFontSizeEquivalent()
331 : HTMLAttributeEquivalent(CSSPropertyFontSize, HTMLNames::fontTag, HTMLNames::sizeAttr)
335 PassRefPtrWillBeRawPtr<CSSValue> HTMLFontSizeEquivalent::attributeValueAsCSSValue(Element* element) const
337 ASSERT(element);
338 const AtomicString& value = element->getAttribute(m_attrName);
339 if (value.isNull())
340 return nullptr;
341 CSSValueID size;
342 if (!HTMLFontElement::cssValueFromFontSizeNumber(value, size))
343 return nullptr;
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();
398 RGBA32 rgba = 0;
399 // FIXME: Why ignore the return value?
400 CSSParser::parseColor(rgba, colorValue->cssText());
401 return rgba;
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)
431 switch (textAlign) {
432 case CSSValueCenter:
433 case CSSValueWebkitCenter:
434 return CSSValueCenter;
435 case CSSValueJustify:
436 return CSSValueJustify;
437 case CSSValueLeft:
438 case CSSValueWebkitLeft:
439 return CSSValueLeft;
440 case CSSValueRight:
441 case CSSValueWebkitRight:
442 return CSSValueRight;
443 case CSSValueStart:
444 return direction != CSSValueRtl ? CSSValueLeft : CSSValueRight;
445 case CSSValueEnd:
446 return direction == CSSValueRtl ? CSSValueRight : CSSValueLeft;
448 return CSSValueInvalid;
451 template<typename T>
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)
497 if (!m_mutableStyle)
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()
512 if (!m_mutableStyle)
513 return;
515 if (m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize)) {
516 // Explicit font size overrides any delta.
517 m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
518 return;
521 // Get the adjustment amount out of the style.
522 RefPtrWillBeRawPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta);
523 if (!value || !value->isPrimitiveValue())
524 return;
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())
531 return;
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
544 if (!m_mutableStyle)
545 return false;
547 RefPtrWillBeRawPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
548 if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
549 return false;
551 CSSValueID unicodeBidiValue = toCSSPrimitiveValue(unicodeBidi.get())->getValueID();
552 if (unicodeBidiValue == CSSValueEmbed) {
553 RefPtrWillBeRawPtr<CSSValue> direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
554 if (!direction || !direction->isPrimitiveValue())
555 return false;
557 writingDirection = toCSSPrimitiveValue(direction.get())->getValueID() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
559 return true;
562 if (unicodeBidiValue == CSSValueNormal) {
563 writingDirection = NaturalWritingDirection;
564 return true;
567 return false;
570 void EditingStyle::overrideWithStyle(const StylePropertySet* style)
572 if (!style || style->isEmpty())
573 return;
574 if (!m_mutableStyle)
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();
590 if (m_mutableStyle)
591 copy->m_mutableStyle = m_mutableStyle->mutableCopy();
592 copy->m_isMonospaceFont = m_isMonospaceFont;
593 copy->m_fontSizeDelta = m_fontSizeDelta;
594 return copy;
597 PassRefPtrWillBeRawPtr<EditingStyle> EditingStyle::extractAndRemoveBlockProperties()
599 RefPtrWillBeRawPtr<EditingStyle> blockProperties = EditingStyle::create();
600 if (!m_mutableStyle)
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()
625 if (!m_mutableStyle)
626 return;
628 m_mutableStyle->removeBlockProperties();
631 void EditingStyle::removeStyleAddedByElement(Element* element)
633 if (!element || !element->parentNode())
634 return;
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)
644 return;
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()
657 if (!m_mutableStyle)
658 return;
660 RefPtrWillBeRawPtr<CSSValue> textDecorationsInEffect = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
661 if (!textDecorationsInEffect)
662 return;
664 if (textDecorationsInEffect->isValueList())
665 m_mutableStyle->setProperty(textDecorationPropertyForEditing(), textDecorationsInEffect->cssText(), m_mutableStyle->propertyIsImportant(textDecorationPropertyForEditing()));
666 else
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,
680 CSSPropertyColor,
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())
698 return TrueTriState;
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);
718 if (nodeStyle) {
719 TriState nodeState = triStateOfStyle(nodeStyle.get(), node.isTextNode() ? EditingStyle::DoNotIgnoreTextOnlyProperties : EditingStyle::IgnoreTextOnlyProperties);
720 if (nodeIsStart) {
721 state = nodeState;
722 nodeIsStart = false;
723 } else if (state != nodeState && node.isTextNode()) {
724 state = MixedTriState;
725 break;
729 if (&node == selection.end().deprecatedNode())
730 break;
733 return state;
736 bool EditingStyle::conflictsWithInlineStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const
738 ASSERT(element);
739 ASSERT(!conflictingProperties || conflictingProperties->isEmpty());
741 const StylePropertySet* inlineStyle = element->inlineStyle();
742 if (!m_mutableStyle || !inlineStyle)
743 return false;
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))
751 continue;
753 if (propertyID == CSSPropertyWebkitTextDecorationsInEffect && inlineStyle->getPropertyCSSValue(textDecorationPropertyForEditing())) {
754 if (!conflictingProperties)
755 return true;
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);
761 if (extractedStyle)
762 extractedStyle->setProperty(textDecorationPropertyForEditing(), inlineStyle->getPropertyValue(textDecorationPropertyForEditing()), inlineStyle->propertyIsImportant(textDecorationPropertyForEditing()));
763 continue;
766 if (!inlineStyle->getPropertyCSSValue(propertyID))
767 continue;
769 if (propertyID == CSSPropertyUnicodeBidi && inlineStyle->getPropertyCSSValue(CSSPropertyDirection)) {
770 if (!conflictingProperties)
771 return true;
772 conflictingProperties->append(CSSPropertyDirection);
773 if (extractedStyle)
774 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->propertyIsImportant(propertyID));
777 if (!conflictingProperties)
778 return true;
780 conflictingProperties->append(propertyID);
782 if (extractedStyle)
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
811 if (!m_mutableStyle)
812 return false;
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()))) {
819 if (extractedStyle)
820 equivalent->addToStyle(element, extractedStyle);
821 return true;
824 return false;
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
846 ASSERT(element);
847 if (!m_mutableStyle)
848 return false;
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()))
854 return true;
857 return false;
860 bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* element, ShouldPreserveWritingDirection shouldPreserveWritingDirection,
861 EditingStyle* extractedStyle, Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
863 ASSERT(element);
864 // HTMLAttributeEquivalent::addToStyle doesn't support unicode-bidi and direction properties
865 ASSERT(!extractedStyle || shouldPreserveWritingDirection == PreserveWritingDirection);
866 if (!m_mutableStyle)
867 return false;
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)
876 continue;
878 if (!equivalent->matches(element) || !equivalent->propertyExistsInStyle(m_mutableStyle.get())
879 || (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(element, m_mutableStyle.get())))
880 continue;
882 if (extractedStyle)
883 equivalent->addToStyle(element, extractedStyle);
884 conflictingAttributes.append(equivalent->attributeName());
885 removed = true;
888 return removed;
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)
898 ASSERT(element);
899 bool elementIsSpanOrElementEquivalent = false;
900 if (isHTMLSpanElement(*element))
901 elementIsSpanOrElementEquivalent = true;
902 else {
903 const WillBeHeapVector<OwnPtrWillBeMember<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;
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)
921 matchedAttributes++;
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)
928 matchedAttributes++;
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()))
935 return false;
938 matchedAttributes++;
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)
948 if (!m_mutableStyle)
949 return;
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)
985 ASSERT(document);
987 RefPtrWillBeRawPtr<EditingStyle> typingStyle = document->frame()->selection().typingStyle();
988 if (!typingStyle || typingStyle == this)
989 return;
991 mergeStyle(typingStyle->style(), OverrideValues);
994 void EditingStyle::mergeInlineStyleOfElement(HTMLElement* element, CSSPropertyOverrideMode mode, PropertiesToInclude propertiesToInclude)
996 ASSERT(element);
997 if (!element->inlineStyle())
998 return;
1000 switch (propertiesToInclude) {
1001 case AllProperties:
1002 mergeStyle(element->inlineStyle(), mode);
1003 return;
1004 case OnlyEditingInheritableProperties:
1005 mergeStyle(copyEditingProperties(element->inlineStyle(), OnlyInheritableEditingProperties).get(), mode);
1006 return;
1007 case EditingPropertiesInEffect:
1008 mergeStyle(copyEditingProperties(element->inlineStyle(), AllEditingProperties).get(), mode);
1009 return;
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)
1022 if (!style)
1023 return nullptr;
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();
1034 return nullptr;
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)
1107 if (!style)
1108 return;
1110 if (!m_mutableStyle) {
1111 m_mutableStyle = style->mutableCopy();
1112 return;
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()));
1124 continue;
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);
1138 if (matchedRules) {
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.
1151 if (m_mutableStyle)
1152 styleFromMatchedRules->mergeAndOverrideOnConflict(m_mutableStyle.get());
1154 clear();
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())
1173 continue;
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)
1195 ASSERT(element);
1196 if (!m_mutableStyle)
1197 return;
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())
1227 return;
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())
1271 return 0;
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())
1279 return nullptr;
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();
1292 if (!element)
1293 return nullptr;
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());
1307 return style;
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();
1320 if (!node)
1321 return NaturalWritingDirection;
1323 Position end;
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())
1331 continue;
1333 RefPtrWillBeRawPtr<CSSComputedStyleDeclaration> style = CSSComputedStyleDeclaration::create(n);
1334 RefPtrWillBeRawPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
1335 if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
1336 continue;
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;
1348 return direction;
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
1354 // to decide.
1355 Node* block = enclosingBlock(node);
1356 WritingDirection foundDirection = NaturalWritingDirection;
1358 for (; node != block; node = node->parentNode()) {
1359 if (!node->isStyledElement())
1360 continue;
1362 Element* element = toElement(node);
1363 RefPtrWillBeRawPtr<CSSComputedStyleDeclaration> style = CSSComputedStyleDeclaration::create(element);
1364 RefPtrWillBeRawPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
1365 if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
1366 continue;
1368 CSSValueID unicodeBidiValue = toCSSPrimitiveValue(unicodeBidi.get())->getValueID();
1369 if (unicodeBidiValue == CSSValueNormal)
1370 continue;
1372 if (unicodeBidiValue == CSSValueBidiOverride)
1373 return NaturalWritingDirection;
1375 ASSERT(unicodeBidiValue == CSSValueEmbed);
1376 RefPtrWillBeRawPtr<CSSValue> direction = style->getPropertyCSSValue(CSSPropertyDirection);
1377 if (!direction || !direction->isPrimitiveValue())
1378 continue;
1380 int directionValue = toCSSPrimitiveValue(direction.get())->getValueID();
1381 if (directionValue != CSSValueLtr && directionValue != CSSValueRtl)
1382 continue;
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())
1429 return;
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));
1457 else {
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)
1465 ASSERT(style);
1467 if (getIdentifierValue(style, CSSPropertyFontWeight) == CSSValueBold) {
1468 style->removeProperty(CSSPropertyFontWeight);
1469 m_applyBold = true;
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) {
1496 case CSSValueSub:
1497 style->removeProperty(CSSPropertyVerticalAlign);
1498 m_applySubscript = true;
1499 break;
1500 case CSSValueSuper:
1501 style->removeProperty(CSSPropertyVerticalAlign);
1502 m_applySuperscript = true;
1503 break;
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())
1530 return;
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())
1544 return false;
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()) {
1549 case CSSValue100:
1550 case CSSValue200:
1551 case CSSValue300:
1552 case CSSValue400:
1553 case CSSValue500:
1554 case CSSValueNormal:
1555 return false;
1556 case CSSValueBold:
1557 case CSSValue600:
1558 case CSSValue700:
1559 case CSSValue800:
1560 case CSSValue900:
1561 return true;
1562 default:
1563 break;
1566 ASSERT_NOT_REACHED(); // For CSSValueBolder and CSSValueLighter
1567 return false;
1570 static bool fontWeightNeedsResolving(CSSValue* fontWeight)
1572 if (!fontWeight->isPrimitiveValue())
1573 return true;
1575 CSSValueID value = toCSSPrimitiveValue(fontWeight)->getValueID();
1576 return value == CSSValueLighter || value == CSSValueBolder;
1579 PassRefPtrWillBeRawPtr<MutableStylePropertySet> getPropertiesNotIn(StylePropertySet* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle)
1581 ASSERT(styleWithRedundantProperties);
1582 ASSERT(baseStyle);
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)
1613 if (!style)
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)
1623 if (!style)
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;
1645 return 0;
1648 if (CSSValueXSmall <= value->getValueID() && value->getValueID() <= CSSValueWebkitXxxLarge)
1649 return value->getValueID() - CSSValueXSmall + 1;
1651 return 0;
1654 bool isTransparentColorValue(CSSValue* cssValue)
1656 if (!cssValue)
1657 return true;
1658 if (!cssValue->isPrimitiveValue())
1659 return false;
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);
1685 return nullptr;