1 /*
2 * Copyright (C) 2007, 2008, 2009 Apple Computer, Inc.
3 * Copyright (C) 2010, 2011 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "config.h"
28 #include "core/editing/EditingStyle.h"
29
30 #include "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/rendering/RenderBox.h"
58 #include "core/rendering/RenderObject.h"
59 #include "core/rendering/style/RenderStyle.h"
60
61 namespace blink {
62
textDecorationPropertyForEditing()63 static const CSSPropertyID& textDecorationPropertyForEditing()
64 {
65 static const CSSPropertyID property = RuntimeEnabledFeatures::css3TextDecorationsEnabled() ? CSSPropertyTextDecorationLine : CSSPropertyTextDecoration;
66 return property;
67 }
68
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,
98 };
99
100 enum EditingPropertiesType { OnlyInheritableEditingProperties, AllEditingProperties };
101
allEditingProperties()102 static const Vector<CSSPropertyID>& allEditingProperties()
103 {
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));
109 }
110 return properties;
111 }
112
inheritableEditingProperties()113 static const Vector<CSSPropertyID>& inheritableEditingProperties()
114 {
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;
122 }
123 ++index;
124 }
125 }
126 return properties;
127 }
128
129 template <class StyleDeclarationType>
copyEditingProperties(StyleDeclarationType * style,EditingPropertiesType type=OnlyInheritableEditingProperties)130 static PassRefPtrWillBeRawPtr<MutableStylePropertySet> copyEditingProperties(StyleDeclarationType* style, EditingPropertiesType type = OnlyInheritableEditingProperties)
131 {
132 if (type == AllEditingProperties)
133 return style->copyPropertiesInSet(allEditingProperties());
134 return style->copyPropertiesInSet(inheritableEditingProperties());
135 }
136
isEditingProperty(int id)137 static inline bool isEditingProperty(int id)
138 {
139 return allEditingProperties().contains(static_cast<CSSPropertyID>(id));
140 }
141
editingStyleFromComputedStyle(PassRefPtrWillBeRawPtr<CSSComputedStyleDeclaration> style,EditingPropertiesType type=OnlyInheritableEditingProperties)142 static PassRefPtrWillBeRawPtr<MutableStylePropertySet> editingStyleFromComputedStyle(PassRefPtrWillBeRawPtr<CSSComputedStyleDeclaration> style, EditingPropertiesType type = OnlyInheritableEditingProperties)
143 {
144 if (!style)
145 return MutableStylePropertySet::create();
146 return copyEditingProperties(style.get(), type);
147 }
148
149 static PassRefPtrWillBeRawPtr<MutableStylePropertySet> getPropertiesNotIn(StylePropertySet* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle);
150 enum LegacyFontSizeMode { AlwaysUseLegacyFontSize, UseLegacyFontSizeOnlyIfPixelValuesMatch };
151 static int legacyFontSizeFromCSSValue(Document*, CSSPrimitiveValue*, FixedPitchFontType, LegacyFontSizeMode);
152 static bool isTransparentColorValue(CSSValue*);
153 static bool hasTransparentBackgroundColor(CSSStyleDeclaration*);
154 static bool hasTransparentBackgroundColor(StylePropertySet*);
155 static PassRefPtrWillBeRawPtr<CSSValue> backgroundColorInEffect(Node*);
156
157 class HTMLElementEquivalent : public NoBaseWillBeGarbageCollected<HTMLElementEquivalent> {
158 WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED;
159 DECLARE_EMPTY_VIRTUAL_DESTRUCTOR_WILL_BE_REMOVED(HTMLElementEquivalent);
160 public:
create(CSSPropertyID propertyID,CSSValueID primitiveValue,const HTMLQualifiedName & tagName)161 static PassOwnPtrWillBeRawPtr<HTMLElementEquivalent> create(CSSPropertyID propertyID, CSSValueID primitiveValue, const HTMLQualifiedName& tagName)
162 {
163 return adoptPtrWillBeNoop(new HTMLElementEquivalent(propertyID, primitiveValue, tagName));
164 }
165
matches(const Element * element) const166 virtual bool matches(const Element* element) const { return !m_tagName || element->hasTagName(*m_tagName); }
hasAttribute() const167 virtual bool hasAttribute() const { return false; }
propertyExistsInStyle(const StylePropertySet * style) const168 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;
171
trace(Visitor * visitor)172 virtual void trace(Visitor* visitor) { visitor->trace(m_primitiveValue); }
173
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.
181 };
182
183 DEFINE_EMPTY_DESTRUCTOR_WILL_BE_REMOVED(HTMLElementEquivalent);
184
HTMLElementEquivalent(CSSPropertyID id)185 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id)
186 : m_propertyID(id)
187 , m_tagName(0)
188 {
189 }
190
HTMLElementEquivalent(CSSPropertyID id,const HTMLQualifiedName & tagName)191 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, const HTMLQualifiedName& tagName)
192 : m_propertyID(id)
193 , m_tagName(&tagName)
194 {
195 }
196
HTMLElementEquivalent(CSSPropertyID id,CSSValueID primitiveValue,const HTMLQualifiedName & 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)
201 {
202 ASSERT(primitiveValue != CSSValueInvalid);
203 }
204
valueIsPresentInStyle(HTMLElement * element,StylePropertySet * style) const205 bool HTMLElementEquivalent::valueIsPresentInStyle(HTMLElement* element, StylePropertySet* style) const
206 {
207 RefPtrWillBeRawPtr<CSSValue> value = style->getPropertyCSSValue(m_propertyID);
208 return matches(element) && value && value->isPrimitiveValue() && toCSSPrimitiveValue(value.get())->getValueID() == m_primitiveValue->getValueID();
209 }
210
addToStyle(Element *,EditingStyle * style) const211 void HTMLElementEquivalent::addToStyle(Element*, EditingStyle* style) const
212 {
213 style->setProperty(m_propertyID, m_primitiveValue->cssText());
214 }
215
216 class HTMLTextDecorationEquivalent FINAL : public HTMLElementEquivalent {
217 public:
create(CSSValueID primitiveValue,const HTMLQualifiedName & tagName)218 static PassOwnPtrWillBeRawPtr<HTMLElementEquivalent> create(CSSValueID primitiveValue, const HTMLQualifiedName& tagName)
219 {
220 return adoptPtrWillBeNoop(new HTMLTextDecorationEquivalent(primitiveValue, tagName));
221 }
222 virtual bool propertyExistsInStyle(const StylePropertySet*) const OVERRIDE;
223 virtual bool valueIsPresentInStyle(HTMLElement*, StylePropertySet*) const OVERRIDE;
224
trace(Visitor * visitor)225 virtual void trace(Visitor* visitor) OVERRIDE { HTMLElementEquivalent::trace(visitor); }
226
227 private:
228 HTMLTextDecorationEquivalent(CSSValueID primitiveValue, const HTMLQualifiedName& tagName);
229 };
230
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
234 {
235 }
236
propertyExistsInStyle(const StylePropertySet * style) const237 bool HTMLTextDecorationEquivalent::propertyExistsInStyle(const StylePropertySet* style) const
238 {
239 return style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect)
240 || style->getPropertyCSSValue(textDecorationPropertyForEditing());
241 }
242
valueIsPresentInStyle(HTMLElement * element,StylePropertySet * style) const243 bool HTMLTextDecorationEquivalent::valueIsPresentInStyle(HTMLElement* element, StylePropertySet* style) const
244 {
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());
249 }
250
251 class HTMLAttributeEquivalent : public HTMLElementEquivalent {
252 public:
create(CSSPropertyID propertyID,const HTMLQualifiedName & tagName,const QualifiedName & attrName)253 static PassOwnPtrWillBeRawPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const HTMLQualifiedName& tagName, const QualifiedName& attrName)
254 {
255 return adoptPtrWillBeNoop(new HTMLAttributeEquivalent(propertyID, tagName, attrName));
256 }
create(CSSPropertyID propertyID,const QualifiedName & attrName)257 static PassOwnPtrWillBeRawPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& attrName)
258 {
259 return adoptPtrWillBeNoop(new HTMLAttributeEquivalent(propertyID, attrName));
260 }
261
matches(const Element * element) const262 virtual bool matches(const Element* element) const OVERRIDE { return HTMLElementEquivalent::matches(element) && element->hasAttribute(m_attrName); }
hasAttribute() const263 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;
attributeName() const267 inline const QualifiedName& attributeName() const { return m_attrName; }
268
trace(Visitor * visitor)269 virtual void trace(Visitor* visitor) OVERRIDE { HTMLElementEquivalent::trace(visitor); }
270
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.
275 };
276
HTMLAttributeEquivalent(CSSPropertyID id,const HTMLQualifiedName & tagName,const QualifiedName & attrName)277 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const HTMLQualifiedName& tagName, const QualifiedName& attrName)
278 : HTMLElementEquivalent(id, tagName)
279 , m_attrName(attrName)
280 {
281 }
282
HTMLAttributeEquivalent(CSSPropertyID id,const QualifiedName & attrName)283 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& attrName)
284 : HTMLElementEquivalent(id)
285 , m_attrName(attrName)
286 {
287 }
288
valueIsPresentInStyle(HTMLElement * element,StylePropertySet * style) const289 bool HTMLAttributeEquivalent::valueIsPresentInStyle(HTMLElement* element, StylePropertySet* style) const
290 {
291 RefPtrWillBeRawPtr<CSSValue> value = attributeValueAsCSSValue(element);
292 RefPtrWillBeRawPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID);
293
294 return compareCSSValuePtr(value, styleValue);
295 }
296
addToStyle(Element * element,EditingStyle * style) const297 void HTMLAttributeEquivalent::addToStyle(Element* element, EditingStyle* style) const
298 {
299 if (RefPtrWillBeRawPtr<CSSValue> value = attributeValueAsCSSValue(element))
300 style->setProperty(m_propertyID, value->cssText());
301 }
302
attributeValueAsCSSValue(Element * element) const303 PassRefPtrWillBeRawPtr<CSSValue> HTMLAttributeEquivalent::attributeValueAsCSSValue(Element* element) const
304 {
305 ASSERT(element);
306 const AtomicString& value = element->getAttribute(m_attrName);
307 if (value.isNull())
308 return nullptr;
309
310 RefPtrWillBeRawPtr<MutableStylePropertySet> dummyStyle = nullptr;
311 dummyStyle = MutableStylePropertySet::create();
312 dummyStyle->setProperty(m_propertyID, value);
313 return dummyStyle->getPropertyCSSValue(m_propertyID);
314 }
315
316 class HTMLFontSizeEquivalent FINAL : public HTMLAttributeEquivalent {
317 public:
create()318 static PassOwnPtrWillBeRawPtr<HTMLFontSizeEquivalent> create()
319 {
320 return adoptPtrWillBeNoop(new HTMLFontSizeEquivalent());
321 }
322 virtual PassRefPtrWillBeRawPtr<CSSValue> attributeValueAsCSSValue(Element*) const OVERRIDE;
323
trace(Visitor * visitor)324 virtual void trace(Visitor* visitor) OVERRIDE { HTMLAttributeEquivalent::trace(visitor); }
325
326 private:
327 HTMLFontSizeEquivalent();
328 };
329
HTMLFontSizeEquivalent()330 HTMLFontSizeEquivalent::HTMLFontSizeEquivalent()
331 : HTMLAttributeEquivalent(CSSPropertyFontSize, HTMLNames::fontTag, HTMLNames::sizeAttr)
332 {
333 }
334
attributeValueAsCSSValue(Element * element) const335 PassRefPtrWillBeRawPtr<CSSValue> HTMLFontSizeEquivalent::attributeValueAsCSSValue(Element* element) const
336 {
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);
345 }
346
347 float EditingStyle::NoFontDelta = 0.0f;
348
EditingStyle()349 EditingStyle::EditingStyle()
350 : m_fixedPitchFontType(NonFixedPitchFont)
351 , m_fontSizeDelta(NoFontDelta)
352 {
353 }
354
EditingStyle(ContainerNode * node,PropertiesToInclude propertiesToInclude)355 EditingStyle::EditingStyle(ContainerNode* node, PropertiesToInclude propertiesToInclude)
356 : m_fixedPitchFontType(NonFixedPitchFont)
357 , m_fontSizeDelta(NoFontDelta)
358 {
359 init(node, propertiesToInclude);
360 }
361
EditingStyle(const Position & position,PropertiesToInclude propertiesToInclude)362 EditingStyle::EditingStyle(const Position& position, PropertiesToInclude propertiesToInclude)
363 : m_fixedPitchFontType(NonFixedPitchFont)
364 , m_fontSizeDelta(NoFontDelta)
365 {
366 init(position.deprecatedNode(), propertiesToInclude);
367 }
368
EditingStyle(const StylePropertySet * style)369 EditingStyle::EditingStyle(const StylePropertySet* style)
370 : m_mutableStyle(style ? style->mutableCopy() : nullptr)
371 , m_fixedPitchFontType(NonFixedPitchFont)
372 , m_fontSizeDelta(NoFontDelta)
373 {
374 extractFontSizeDelta();
375 }
376
EditingStyle(CSSPropertyID propertyID,const String & value)377 EditingStyle::EditingStyle(CSSPropertyID propertyID, const String& value)
378 : m_mutableStyle(nullptr)
379 , m_fixedPitchFontType(NonFixedPitchFont)
380 , m_fontSizeDelta(NoFontDelta)
381 {
382 setProperty(propertyID, value);
383 }
384
~EditingStyle()385 EditingStyle::~EditingStyle()
386 {
387 }
388
cssValueToRGBA(CSSValue * colorValue)389 static RGBA32 cssValueToRGBA(CSSValue* colorValue)
390 {
391 if (!colorValue || !colorValue->isPrimitiveValue())
392 return Color::transparent;
393
394 CSSPrimitiveValue* primitiveColor = toCSSPrimitiveValue(colorValue);
395 if (primitiveColor->isRGBColor())
396 return primitiveColor->getRGBA32Value();
397
398 RGBA32 rgba = 0;
399 // FIXME: Why ignore the return value?
400 CSSParser::parseColor(rgba, colorValue->cssText());
401 return rgba;
402 }
403
getRGBAFontColor(CSSStyleDeclaration * style)404 static inline RGBA32 getRGBAFontColor(CSSStyleDeclaration* style)
405 {
406 return cssValueToRGBA(style->getPropertyCSSValueInternal(CSSPropertyColor).get());
407 }
408
getRGBAFontColor(StylePropertySet * style)409 static inline RGBA32 getRGBAFontColor(StylePropertySet* style)
410 {
411 return cssValueToRGBA(style->getPropertyCSSValue(CSSPropertyColor).get());
412 }
413
getRGBABackgroundColor(CSSStyleDeclaration * style)414 static inline RGBA32 getRGBABackgroundColor(CSSStyleDeclaration* style)
415 {
416 return cssValueToRGBA(style->getPropertyCSSValueInternal(CSSPropertyBackgroundColor).get());
417 }
418
getRGBABackgroundColor(StylePropertySet * style)419 static inline RGBA32 getRGBABackgroundColor(StylePropertySet* style)
420 {
421 return cssValueToRGBA(style->getPropertyCSSValue(CSSPropertyBackgroundColor).get());
422 }
423
rgbaBackgroundColorInEffect(Node * node)424 static inline RGBA32 rgbaBackgroundColorInEffect(Node* node)
425 {
426 return cssValueToRGBA(backgroundColorInEffect(node).get());
427 }
428
textAlignResolvingStartAndEnd(int textAlign,int direction)429 static int textAlignResolvingStartAndEnd(int textAlign, int direction)
430 {
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;
447 }
448 return CSSValueInvalid;
449 }
450
451 template<typename T>
textAlignResolvingStartAndEnd(T * style)452 static int textAlignResolvingStartAndEnd(T* style)
453 {
454 return textAlignResolvingStartAndEnd(getIdentifierValue(style, CSSPropertyTextAlign), getIdentifierValue(style, CSSPropertyDirection));
455 }
456
init(Node * node,PropertiesToInclude propertiesToInclude)457 void EditingStyle::init(Node* node, PropertiesToInclude propertiesToInclude)
458 {
459 if (isTabHTMLSpanElementTextNode(node))
460 node = tabSpanElement(node)->parentNode();
461 else if (isTabHTMLSpanElement(node))
462 node = node->parentNode();
463
464 RefPtrWillBeRawPtr<CSSComputedStyleDeclaration> computedStyleAtPosition = CSSComputedStyleDeclaration::create(node);
465 m_mutableStyle = propertiesToInclude == AllProperties && computedStyleAtPosition ? computedStyleAtPosition->copyProperties() : editingStyleFromComputedStyle(computedStyleAtPosition);
466
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());
472 }
473
474 if (node && node->computedStyle()) {
475 RenderStyle* renderStyle = node->computedStyle();
476 removeTextFillAndStrokeColorsIfNeeded(renderStyle);
477 replaceFontSizeByKeywordIfPossible(renderStyle, computedStyleAtPosition.get());
478 }
479
480 m_fixedPitchFontType = computedStyleAtPosition->fixedPitchFontType();
481 extractFontSizeDelta();
482 }
483
removeTextFillAndStrokeColorsIfNeeded(RenderStyle * renderStyle)484 void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyle)
485 {
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 (renderStyle->textFillColor().isCurrentColor())
490 m_mutableStyle->removeProperty(CSSPropertyWebkitTextFillColor);
491 if (renderStyle->textStrokeColor().isCurrentColor())
492 m_mutableStyle->removeProperty(CSSPropertyWebkitTextStrokeColor);
493 }
494
setProperty(CSSPropertyID propertyID,const String & value,bool important)495 void EditingStyle::setProperty(CSSPropertyID propertyID, const String& value, bool important)
496 {
497 if (!m_mutableStyle)
498 m_mutableStyle = MutableStylePropertySet::create();
499
500 m_mutableStyle->setProperty(propertyID, value, important);
501 }
502
replaceFontSizeByKeywordIfPossible(RenderStyle * renderStyle,CSSComputedStyleDeclaration * computedStyle)503 void EditingStyle::replaceFontSizeByKeywordIfPossible(RenderStyle* renderStyle, CSSComputedStyleDeclaration* computedStyle)
504 {
505 ASSERT(renderStyle);
506 if (renderStyle->fontDescription().keywordSize())
507 m_mutableStyle->setProperty(CSSPropertyFontSize, computedStyle->getFontSizeCSSValuePreferringKeyword()->cssText());
508 }
509
extractFontSizeDelta()510 void EditingStyle::extractFontSizeDelta()
511 {
512 if (!m_mutableStyle)
513 return;
514
515 if (m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize)) {
516 // Explicit font size overrides any delta.
517 m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
518 return;
519 }
520
521 // Get the adjustment amount out of the style.
522 RefPtrWillBeRawPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta);
523 if (!value || !value->isPrimitiveValue())
524 return;
525
526 CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value.get());
527
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;
532
533 m_fontSizeDelta = primitiveValue->getFloatValue();
534 m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
535 }
536
isEmpty() const537 bool EditingStyle::isEmpty() const
538 {
539 return (!m_mutableStyle || m_mutableStyle->isEmpty()) && m_fontSizeDelta == NoFontDelta;
540 }
541
textDirection(WritingDirection & writingDirection) const542 bool EditingStyle::textDirection(WritingDirection& writingDirection) const
543 {
544 if (!m_mutableStyle)
545 return false;
546
547 RefPtrWillBeRawPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
548 if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
549 return false;
550
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;
556
557 writingDirection = toCSSPrimitiveValue(direction.get())->getValueID() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
558
559 return true;
560 }
561
562 if (unicodeBidiValue == CSSValueNormal) {
563 writingDirection = NaturalWritingDirection;
564 return true;
565 }
566
567 return false;
568 }
569
overrideWithStyle(const StylePropertySet * style)570 void EditingStyle::overrideWithStyle(const StylePropertySet* style)
571 {
572 if (!style || style->isEmpty())
573 return;
574 if (!m_mutableStyle)
575 m_mutableStyle = MutableStylePropertySet::create();
576 m_mutableStyle->mergeAndOverrideOnConflict(style);
577 extractFontSizeDelta();
578 }
579
clear()580 void EditingStyle::clear()
581 {
582 m_mutableStyle.clear();
583 m_fixedPitchFontType = NonFixedPitchFont;
584 m_fontSizeDelta = NoFontDelta;
585 }
586
copy() const587 PassRefPtrWillBeRawPtr<EditingStyle> EditingStyle::copy() const
588 {
589 RefPtrWillBeRawPtr<EditingStyle> copy = EditingStyle::create();
590 if (m_mutableStyle)
591 copy->m_mutableStyle = m_mutableStyle->mutableCopy();
592 copy->m_fixedPitchFontType = m_fixedPitchFontType;
593 copy->m_fontSizeDelta = m_fontSizeDelta;
594 return copy;
595 }
596
extractAndRemoveBlockProperties()597 PassRefPtrWillBeRawPtr<EditingStyle> EditingStyle::extractAndRemoveBlockProperties()
598 {
599 RefPtrWillBeRawPtr<EditingStyle> blockProperties = EditingStyle::create();
600 if (!m_mutableStyle)
601 return blockProperties;
602
603 blockProperties->m_mutableStyle = m_mutableStyle->copyBlockProperties();
604 m_mutableStyle->removeBlockProperties();
605
606 return blockProperties;
607 }
608
extractAndRemoveTextDirection()609 PassRefPtrWillBeRawPtr<EditingStyle> EditingStyle::extractAndRemoveTextDirection()
610 {
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));
616
617 m_mutableStyle->removeProperty(CSSPropertyUnicodeBidi);
618 m_mutableStyle->removeProperty(CSSPropertyDirection);
619
620 return textDirection;
621 }
622
removeBlockProperties()623 void EditingStyle::removeBlockProperties()
624 {
625 if (!m_mutableStyle)
626 return;
627
628 m_mutableStyle->removeBlockProperties();
629 }
630
removeStyleAddedByElement(Element * element)631 void EditingStyle::removeStyleAddedByElement(Element* element)
632 {
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());
639 }
640
removeStyleConflictingWithStyleOfElement(Element * element)641 void EditingStyle::removeStyleConflictingWithStyleOfElement(Element* element)
642 {
643 if (!element || !element->parentNode() || !m_mutableStyle)
644 return;
645
646 RefPtrWillBeRawPtr<MutableStylePropertySet> parentStyle = editingStyleFromComputedStyle(CSSComputedStyleDeclaration::create(element->parentNode()), AllEditingProperties);
647 RefPtrWillBeRawPtr<MutableStylePropertySet> nodeStyle = editingStyleFromComputedStyle(CSSComputedStyleDeclaration::create(element), AllEditingProperties);
648 nodeStyle->removeEquivalentProperties(parentStyle.get());
649
650 unsigned propertyCount = nodeStyle->propertyCount();
651 for (unsigned i = 0; i < propertyCount; ++i)
652 m_mutableStyle->removeProperty(nodeStyle->propertyAt(i).id());
653 }
654
collapseTextDecorationProperties()655 void EditingStyle::collapseTextDecorationProperties()
656 {
657 if (!m_mutableStyle)
658 return;
659
660 RefPtrWillBeRawPtr<CSSValue> textDecorationsInEffect = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
661 if (!textDecorationsInEffect)
662 return;
663
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);
669 }
670
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,
681 };
682
triStateOfStyle(EditingStyle * style) const683 TriState EditingStyle::triStateOfStyle(EditingStyle* style) const
684 {
685 if (!style || !style->m_mutableStyle)
686 return FalseTriState;
687 return triStateOfStyle(style->m_mutableStyle->ensureCSSStyleDeclaration(), DoNotIgnoreTextOnlyProperties);
688 }
689
triStateOfStyle(CSSStyleDeclaration * styleToCompare,ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const690 TriState EditingStyle::triStateOfStyle(CSSStyleDeclaration* styleToCompare, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const
691 {
692 RefPtrWillBeRawPtr<MutableStylePropertySet> difference = getPropertiesNotIn(m_mutableStyle.get(), styleToCompare);
693
694 if (shouldIgnoreTextOnlyProperties == IgnoreTextOnlyProperties)
695 difference->removePropertiesInSet(textOnlyProperties, WTF_ARRAY_LENGTH(textOnlyProperties));
696
697 if (difference->isEmpty())
698 return TrueTriState;
699 if (difference->propertyCount() == m_mutableStyle->propertyCount())
700 return FalseTriState;
701
702 return MixedTriState;
703 }
704
triStateOfStyle(const VisibleSelection & selection) const705 TriState EditingStyle::triStateOfStyle(const VisibleSelection& selection) const
706 {
707 if (!selection.isCaretOrRange())
708 return FalseTriState;
709
710 if (selection.isCaret())
711 return triStateOfStyle(EditingStyle::styleAtSelectionStart(selection).get());
712
713 TriState state = FalseTriState;
714 bool nodeIsStart = true;
715 for (Node* node = selection.start().deprecatedNode(); node; node = NodeTraversal::next(*node)) {
716 if (node->renderer() && 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;
726 }
727 }
728 }
729 if (node == selection.end().deprecatedNode())
730 break;
731 }
732
733 return state;
734 }
735
conflictsWithInlineStyleOfElement(HTMLElement * element,EditingStyle * extractedStyle,Vector<CSSPropertyID> * conflictingProperties) const736 bool EditingStyle::conflictsWithInlineStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const
737 {
738 ASSERT(element);
739 ASSERT(!conflictingProperties || conflictingProperties->isEmpty());
740
741 const StylePropertySet* inlineStyle = element->inlineStyle();
742 if (!m_mutableStyle || !inlineStyle)
743 return false;
744
745 unsigned propertyCount = m_mutableStyle->propertyCount();
746 for (unsigned i = 0; i < propertyCount; ++i) {
747 CSSPropertyID propertyID = m_mutableStyle->propertyAt(i).id();
748
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;
752
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;
764 }
765
766 if (!inlineStyle->getPropertyCSSValue(propertyID))
767 continue;
768
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));
775 }
776
777 if (!conflictingProperties)
778 return true;
779
780 conflictingProperties->append(propertyID);
781
782 if (extractedStyle)
783 extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->propertyIsImportant(propertyID));
784 }
785
786 return conflictingProperties && !conflictingProperties->isEmpty();
787 }
788
htmlElementEquivalents()789 static const WillBeHeapVector<OwnPtrWillBeMember<HTMLElementEquivalent> >& htmlElementEquivalents()
790 {
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));
799
800 HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueUnderline, HTMLNames::uTag));
801 HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::sTag));
802 HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::strikeTag));
803 }
804
805 return HTMLElementEquivalents;
806 }
807
808
conflictsWithImplicitStyleOfElement(HTMLElement * element,EditingStyle * extractedStyle,ShouldExtractMatchingStyle shouldExtractMatchingStyle) const809 bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
810 {
811 if (!m_mutableStyle)
812 return false;
813
814 const 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;
822 }
823 }
824 return false;
825 }
826
htmlAttributeEquivalents()827 static const WillBeHeapVector<OwnPtrWillBeMember<HTMLAttributeEquivalent> >& htmlAttributeEquivalents()
828 {
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());
836
837 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyDirection, HTMLNames::dirAttr));
838 HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyUnicodeBidi, HTMLNames::dirAttr));
839 }
840
841 return HTMLAttributeEquivalents;
842 }
843
conflictsWithImplicitStyleOfAttributes(HTMLElement * element) const844 bool EditingStyle::conflictsWithImplicitStyleOfAttributes(HTMLElement* element) const
845 {
846 ASSERT(element);
847 if (!m_mutableStyle)
848 return false;
849
850 const WillBeHeapVector<OwnPtrWillBeMember<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
851 for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
852 if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->propertyExistsInStyle(m_mutableStyle.get())
853 && !HTMLAttributeEquivalents[i]->valueIsPresentInStyle(element, m_mutableStyle.get()))
854 return true;
855 }
856
857 return false;
858 }
859
extractConflictingImplicitStyleOfAttributes(HTMLElement * element,ShouldPreserveWritingDirection shouldPreserveWritingDirection,EditingStyle * extractedStyle,Vector<QualifiedName> & conflictingAttributes,ShouldExtractMatchingStyle shouldExtractMatchingStyle) const860 bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* element, ShouldPreserveWritingDirection shouldPreserveWritingDirection,
861 EditingStyle* extractedStyle, Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
862 {
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;
868
869 const WillBeHeapVector<OwnPtrWillBeMember<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
870 bool removed = false;
871 for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
872 const HTMLAttributeEquivalent* equivalent = HTMLAttributeEquivalents[i].get();
873
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;
877
878 if (!equivalent->matches(element) || !equivalent->propertyExistsInStyle(m_mutableStyle.get())
879 || (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(element, m_mutableStyle.get())))
880 continue;
881
882 if (extractedStyle)
883 equivalent->addToStyle(element, extractedStyle);
884 conflictingAttributes.append(equivalent->attributeName());
885 removed = true;
886 }
887
888 return removed;
889 }
890
styleIsPresentInComputedStyleOfNode(Node * node) const891 bool EditingStyle::styleIsPresentInComputedStyleOfNode(Node* node) const
892 {
893 return !m_mutableStyle || getPropertiesNotIn(m_mutableStyle.get(), CSSComputedStyleDeclaration::create(node).get())->isEmpty();
894 }
895
elementIsStyledSpanOrHTMLEquivalent(const HTMLElement * element)896 bool EditingStyle::elementIsStyledSpanOrHTMLEquivalent(const HTMLElement* element)
897 {
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;
909 }
910 }
911 }
912
913 AttributeCollection attributes = element->attributes();
914 if (attributes.isEmpty())
915 return elementIsSpanOrElementEquivalent; // span, b, etc... without any attributes
916
917 unsigned matchedAttributes = 0;
918 const WillBeHeapVector<OwnPtrWillBeMember<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
919 for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
920 if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->attributeName() != HTMLNames::dirAttr)
921 matchedAttributes++;
922 }
923
924 if (!elementIsSpanOrElementEquivalent && !matchedAttributes)
925 return false; // element is not a span, a html element equivalent, or font element.
926
927 if (element->getAttribute(HTMLNames::classAttr) == AppleStyleSpanClass)
928 matchedAttributes++;
929
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;
936 }
937 }
938 matchedAttributes++;
939 }
940
941 // font with color attribute, span with style attribute, etc...
942 ASSERT(matchedAttributes <= attributes.size());
943 return matchedAttributes >= attributes.size();
944 }
945
prepareToApplyAt(const Position & position,ShouldPreserveWritingDirection shouldPreserveWritingDirection)946 void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWritingDirection shouldPreserveWritingDirection)
947 {
948 if (!m_mutableStyle)
949 return;
950
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();
956
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);
962 }
963
964 m_mutableStyle->removeEquivalentProperties(styleAtPosition);
965
966 if (textAlignResolvingStartAndEnd(m_mutableStyle.get()) == textAlignResolvingStartAndEnd(styleAtPosition))
967 m_mutableStyle->removeProperty(CSSPropertyTextAlign);
968
969 if (getRGBAFontColor(m_mutableStyle.get()) == getRGBAFontColor(styleAtPosition))
970 m_mutableStyle->removeProperty(CSSPropertyColor);
971
972 if (hasTransparentBackgroundColor(m_mutableStyle.get())
973 || cssValueToRGBA(m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor).get()) == rgbaBackgroundColorInEffect(position.containerNode()))
974 m_mutableStyle->removeProperty(CSSPropertyBackgroundColor);
975
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());
980 }
981 }
982
mergeTypingStyle(Document * document)983 void EditingStyle::mergeTypingStyle(Document* document)
984 {
985 ASSERT(document);
986
987 RefPtrWillBeRawPtr<EditingStyle> typingStyle = document->frame()->selection().typingStyle();
988 if (!typingStyle || typingStyle == this)
989 return;
990
991 mergeStyle(typingStyle->style(), OverrideValues);
992 }
993
mergeInlineStyleOfElement(HTMLElement * element,CSSPropertyOverrideMode mode,PropertiesToInclude propertiesToInclude)994 void EditingStyle::mergeInlineStyleOfElement(HTMLElement* element, CSSPropertyOverrideMode mode, PropertiesToInclude propertiesToInclude)
995 {
996 ASSERT(element);
997 if (!element->inlineStyle())
998 return;
999
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;
1010 }
1011 }
1012
elementMatchesAndPropertyIsNotInInlineStyleDecl(const HTMLElementEquivalent * equivalent,const Element * element,EditingStyle::CSSPropertyOverrideMode mode,StylePropertySet * style)1013 static inline bool elementMatchesAndPropertyIsNotInInlineStyleDecl(const HTMLElementEquivalent* equivalent, const Element* element,
1014 EditingStyle::CSSPropertyOverrideMode mode, StylePropertySet* style)
1015 {
1016 return equivalent->matches(element) && (!element->inlineStyle() || !equivalent->propertyExistsInStyle(element->inlineStyle()))
1017 && (mode == EditingStyle::OverrideValues || !equivalent->propertyExistsInStyle(style));
1018 }
1019
extractEditingProperties(const StylePropertySet * style,EditingStyle::PropertiesToInclude propertiesToInclude)1020 static PassRefPtrWillBeRawPtr<MutableStylePropertySet> extractEditingProperties(const StylePropertySet* style, EditingStyle::PropertiesToInclude propertiesToInclude)
1021 {
1022 if (!style)
1023 return nullptr;
1024
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);
1031 }
1032
1033 ASSERT_NOT_REACHED();
1034 return nullptr;
1035 }
1036
mergeInlineAndImplicitStyleOfElement(Element * element,CSSPropertyOverrideMode mode,PropertiesToInclude propertiesToInclude)1037 void EditingStyle::mergeInlineAndImplicitStyleOfElement(Element* element, CSSPropertyOverrideMode mode, PropertiesToInclude propertiesToInclude)
1038 {
1039 RefPtrWillBeRawPtr<EditingStyle> styleFromRules = EditingStyle::create();
1040 styleFromRules->mergeStyleFromRulesForSerialization(element);
1041
1042 if (element->inlineStyle())
1043 styleFromRules->m_mutableStyle->mergeAndOverrideOnConflict(element->inlineStyle());
1044
1045 styleFromRules->m_mutableStyle = extractEditingProperties(styleFromRules->m_mutableStyle.get(), propertiesToInclude);
1046 mergeStyle(styleFromRules->m_mutableStyle.get(), mode);
1047
1048 const WillBeHeapVector<OwnPtrWillBeMember<HTMLElementEquivalent> >& elementEquivalents = htmlElementEquivalents();
1049 for (size_t i = 0; i < elementEquivalents.size(); ++i) {
1050 if (elementMatchesAndPropertyIsNotInInlineStyleDecl(elementEquivalents[i].get(), element, mode, m_mutableStyle.get()))
1051 elementEquivalents[i]->addToStyle(element, this);
1052 }
1053
1054 const WillBeHeapVector<OwnPtrWillBeMember<HTMLAttributeEquivalent> >& attributeEquivalents = htmlAttributeEquivalents();
1055 for (size_t i = 0; i < attributeEquivalents.size(); ++i) {
1056 if (attributeEquivalents[i]->attributeName() == HTMLNames::dirAttr)
1057 continue; // We don't want to include directionality
1058 if (elementMatchesAndPropertyIsNotInInlineStyleDecl(attributeEquivalents[i].get(), element, mode, m_mutableStyle.get()))
1059 attributeEquivalents[i]->addToStyle(element, this);
1060 }
1061 }
1062
wrappingStyleForSerialization(ContainerNode * context,bool shouldAnnotate)1063 PassRefPtrWillBeRawPtr<EditingStyle> EditingStyle::wrappingStyleForSerialization(ContainerNode* context, bool shouldAnnotate)
1064 {
1065 RefPtrWillBeRawPtr<EditingStyle> wrappingStyle = nullptr;
1066 if (shouldAnnotate) {
1067 wrappingStyle = EditingStyle::create(context, EditingStyle::EditingPropertiesInEffect);
1068
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)));
1073
1074 // Call collapseTextDecorationProperties first or otherwise it'll copy the value over from in-effect to text-decorations.
1075 wrappingStyle->collapseTextDecorationProperties();
1076
1077 return wrappingStyle.release();
1078 }
1079
1080 wrappingStyle = EditingStyle::create();
1081
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);
1087 }
1088 }
1089
1090 return wrappingStyle.release();
1091 }
1092
1093
mergeTextDecorationValues(CSSValueList * mergedValue,const CSSValueList * valueToMerge)1094 static void mergeTextDecorationValues(CSSValueList* mergedValue, const CSSValueList* valueToMerge)
1095 {
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);
1100
1101 if (valueToMerge->hasValue(lineThrough) && !mergedValue->hasValue(lineThrough))
1102 mergedValue->append(lineThrough);
1103 }
1104
mergeStyle(const StylePropertySet * style,CSSPropertyOverrideMode mode)1105 void EditingStyle::mergeStyle(const StylePropertySet* style, CSSPropertyOverrideMode mode)
1106 {
1107 if (!style)
1108 return;
1109
1110 if (!m_mutableStyle) {
1111 m_mutableStyle = style->mutableCopy();
1112 return;
1113 }
1114
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());
1119
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;
1125 }
1126 value = nullptr; // text-decoration: none is equivalent to not having the property
1127 }
1128
1129 if (mode == OverrideValues || (mode == DoNotOverrideValues && !value))
1130 m_mutableStyle->setProperty(property.id(), property.value()->cssText(), property.isImportant());
1131 }
1132 }
1133
styleFromMatchedRulesForElement(Element * element,unsigned rulesToInclude)1134 static PassRefPtrWillBeRawPtr<MutableStylePropertySet> styleFromMatchedRulesForElement(Element* element, unsigned rulesToInclude)
1135 {
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());
1141 }
1142 return style.release();
1143 }
1144
mergeStyleFromRules(Element * element)1145 void EditingStyle::mergeStyleFromRules(Element* element)
1146 {
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());
1153
1154 clear();
1155 m_mutableStyle = styleFromMatchedRules;
1156 }
1157
mergeStyleFromRulesForSerialization(Element * element)1158 void EditingStyle::mergeStyleFromRulesForSerialization(Element* element)
1159 {
1160 mergeStyleFromRules(element);
1161
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();
1167 {
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->addParsedProperty(CSSProperty(property.id(), computedPropertyValue));
1177 }
1178 }
1179 }
1180 m_mutableStyle->mergeAndOverrideOnConflict(fromComputedStyle.get());
1181 }
1182
removePropertiesInStyle(MutableStylePropertySet * styleToRemovePropertiesFrom,StylePropertySet * style)1183 static void removePropertiesInStyle(MutableStylePropertySet* styleToRemovePropertiesFrom, StylePropertySet* style)
1184 {
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();
1189
1190 styleToRemovePropertiesFrom->removePropertiesInSet(propertiesToRemove.data(), propertiesToRemove.size());
1191 }
1192
removeStyleFromRulesAndContext(Element * element,ContainerNode * context)1193 void EditingStyle::removeStyleFromRulesAndContext(Element* element, ContainerNode* context)
1194 {
1195 ASSERT(element);
1196 if (!m_mutableStyle)
1197 return;
1198
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());
1203
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);
1209
1210 removePropertiesInStyle(computedStyle->m_mutableStyle.get(), styleFromMatchedRules.get());
1211 m_mutableStyle = getPropertiesNotIn(m_mutableStyle.get(), computedStyle->m_mutableStyle->ensureCSSStyleDeclaration());
1212 }
1213
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);
1221 }
1222 }
1223
removePropertiesInElementDefaultStyle(Element * element)1224 void EditingStyle::removePropertiesInElementDefaultStyle(Element* element)
1225 {
1226 if (!m_mutableStyle || m_mutableStyle->isEmpty())
1227 return;
1228
1229 RefPtrWillBeRawPtr<StylePropertySet> defaultStyle = styleFromMatchedRulesForElement(element, StyleResolver::UAAndUserCSSRules);
1230
1231 removePropertiesInStyle(m_mutableStyle.get(), defaultStyle.get());
1232 }
1233
addAbsolutePositioningFromElement(const Element & element)1234 void EditingStyle::addAbsolutePositioningFromElement(const Element& element)
1235 {
1236 LayoutRect rect = element.boundingBox();
1237 RenderObject* renderer = element.renderer();
1238
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 RenderBox* renderBox = toRenderBox(renderer);
1245
1246 x -= renderBox->marginLeft();
1247 y -= renderBox->marginTop();
1248
1249 m_mutableStyle->setProperty(CSSPropertyBoxSizing, CSSValueBorderBox);
1250 }
1251
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));
1257 }
1258
forceInline()1259 void EditingStyle::forceInline()
1260 {
1261 if (!m_mutableStyle)
1262 m_mutableStyle = MutableStylePropertySet::create();
1263 const bool propertyIsImportant = true;
1264 m_mutableStyle->setProperty(CSSPropertyDisplay, CSSValueInline, propertyIsImportant);
1265 }
1266
legacyFontSize(Document * document) const1267 int EditingStyle::legacyFontSize(Document* document) const
1268 {
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_fixedPitchFontType, AlwaysUseLegacyFontSize);
1274 }
1275
styleAtSelectionStart(const VisibleSelection & selection,bool shouldUseBackgroundColorInEffect)1276 PassRefPtrWillBeRawPtr<EditingStyle> EditingStyle::styleAtSelectionStart(const VisibleSelection& selection, bool shouldUseBackgroundColorInEffect)
1277 {
1278 if (selection.isNone())
1279 return nullptr;
1280
1281 Position position = adjustedSelectionStartForStyleComputation(selection);
1282
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);
1290
1291 Element* element = position.element();
1292 if (!element)
1293 return nullptr;
1294
1295 RefPtrWillBeRawPtr<EditingStyle> style = EditingStyle::create(element, EditingStyle::AllProperties);
1296 style->mergeTypingStyle(&element->document());
1297
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());
1305 }
1306
1307 return style;
1308 }
1309
textDirectionForSelection(const VisibleSelection & selection,EditingStyle * typingStyle,bool & hasNestedOrMultipleEmbeddings)1310 WritingDirection EditingStyle::textDirectionForSelection(const VisibleSelection& selection, EditingStyle* typingStyle, bool& hasNestedOrMultipleEmbeddings)
1311 {
1312 hasNestedOrMultipleEmbeddings = true;
1313
1314 if (selection.isNone())
1315 return NaturalWritingDirection;
1316
1317 Position position = selection.start().downstream();
1318
1319 Node* node = position.deprecatedNode();
1320 if (!node)
1321 return NaturalWritingDirection;
1322
1323 Position end;
1324 if (selection.isRange()) {
1325 end = selection.end().upstream();
1326
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;
1332
1333 RefPtrWillBeRawPtr<CSSComputedStyleDeclaration> style = CSSComputedStyleDeclaration::create(n);
1334 RefPtrWillBeRawPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
1335 if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
1336 continue;
1337
1338 CSSValueID unicodeBidiValue = toCSSPrimitiveValue(unicodeBidi.get())->getValueID();
1339 if (unicodeBidiValue == CSSValueEmbed || unicodeBidiValue == CSSValueBidiOverride)
1340 return NaturalWritingDirection;
1341 }
1342 }
1343
1344 if (selection.isCaret()) {
1345 WritingDirection direction;
1346 if (typingStyle && typingStyle->textDirection(direction)) {
1347 hasNestedOrMultipleEmbeddings = false;
1348 return direction;
1349 }
1350 node = selection.visibleStart().deepEquivalent().deprecatedNode();
1351 }
1352
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;
1357
1358 for (; node != block; node = node->parentNode()) {
1359 if (!node->isStyledElement())
1360 continue;
1361
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;
1367
1368 CSSValueID unicodeBidiValue = toCSSPrimitiveValue(unicodeBidi.get())->getValueID();
1369 if (unicodeBidiValue == CSSValueNormal)
1370 continue;
1371
1372 if (unicodeBidiValue == CSSValueBidiOverride)
1373 return NaturalWritingDirection;
1374
1375 ASSERT(unicodeBidiValue == CSSValueEmbed);
1376 RefPtrWillBeRawPtr<CSSValue> direction = style->getPropertyCSSValue(CSSPropertyDirection);
1377 if (!direction || !direction->isPrimitiveValue())
1378 continue;
1379
1380 int directionValue = toCSSPrimitiveValue(direction.get())->getValueID();
1381 if (directionValue != CSSValueLtr && directionValue != CSSValueRtl)
1382 continue;
1383
1384 if (foundDirection != NaturalWritingDirection)
1385 return NaturalWritingDirection;
1386
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;
1390
1391 foundDirection = directionValue == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
1392 }
1393 hasNestedOrMultipleEmbeddings = false;
1394 return foundDirection;
1395 }
1396
trace(Visitor * visitor)1397 void EditingStyle::trace(Visitor* visitor)
1398 {
1399 visitor->trace(m_mutableStyle);
1400 }
1401
reconcileTextDecorationProperties(MutableStylePropertySet * style)1402 static void reconcileTextDecorationProperties(MutableStylePropertySet* style)
1403 {
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;
1412 }
1413
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());
1417 }
1418
StyleChange(EditingStyle * style,const Position & position)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)
1426 {
1427 Document* document = position.document();
1428 if (!style || !style->style() || !document || !document->frame())
1429 return;
1430
1431 RefPtrWillBeRawPtr<CSSComputedStyleDeclaration> computedStyle = position.computedStyle();
1432 // FIXME: take care of background-color in effect
1433 RefPtrWillBeRawPtr<MutableStylePropertySet> mutableStyle = getPropertiesNotIn(style->style(), computedStyle.get());
1434
1435 reconcileTextDecorationProperties(mutableStyle.get());
1436 if (!document->frame()->editor().shouldStyleWithCSS())
1437 extractTextStyles(document, mutableStyle.get(), computedStyle->fixedPitchFontType());
1438
1439 // Changing the whitespace style in a tab span would collapse the tab into a space.
1440 if (isTabHTMLSpanElementTextNode(position.deprecatedNode()) || isTabHTMLSpanElement((position.deprecatedNode())))
1441 mutableStyle->removeProperty(CSSPropertyWhiteSpace);
1442
1443 // If unicode-bidi is present in mutableStyle and direction is not, then add direction to mutableStyle.
1444 // FIXME: Shouldn't this be done in getPropertiesNotIn?
1445 if (mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi) && !style->style()->getPropertyCSSValue(CSSPropertyDirection))
1446 mutableStyle->setProperty(CSSPropertyDirection, style->style()->getPropertyValue(CSSPropertyDirection));
1447
1448 // Save the result for later
1449 m_cssStyle = mutableStyle->asText().stripWhiteSpace();
1450 }
1451
setTextDecorationProperty(MutableStylePropertySet * style,const CSSValueList * newTextDecoration,CSSPropertyID propertyID)1452 static void setTextDecorationProperty(MutableStylePropertySet* style, const CSSValueList* newTextDecoration, CSSPropertyID propertyID)
1453 {
1454 if (newTextDecoration->length())
1455 style->setProperty(propertyID, newTextDecoration->cssText(), style->propertyIsImportant(propertyID));
1456 else {
1457 // text-decoration: none is redundant since it does not remove any text decorations.
1458 style->removeProperty(propertyID);
1459 }
1460 }
1461
extractTextStyles(Document * document,MutableStylePropertySet * style,FixedPitchFontType fixedPitchFontType)1462 void StyleChange::extractTextStyles(Document* document, MutableStylePropertySet* style, FixedPitchFontType fixedPitchFontType)
1463 {
1464 ASSERT(style);
1465
1466 if (getIdentifierValue(style, CSSPropertyFontWeight) == CSSValueBold) {
1467 style->removeProperty(CSSPropertyFontWeight);
1468 m_applyBold = true;
1469 }
1470
1471 int fontStyle = getIdentifierValue(style, CSSPropertyFontStyle);
1472 if (fontStyle == CSSValueItalic || fontStyle == CSSValueOblique) {
1473 style->removeProperty(CSSPropertyFontStyle);
1474 m_applyItalic = true;
1475 }
1476
1477 // Assuming reconcileTextDecorationProperties has been called, there should not be -webkit-text-decorations-in-effect
1478 // Furthermore, text-decoration: none has been trimmed so that text-decoration property is always a CSSValueList.
1479 RefPtrWillBeRawPtr<CSSValue> textDecoration = style->getPropertyCSSValue(textDecorationPropertyForEditing());
1480 if (textDecoration && textDecoration->isValueList()) {
1481 DEFINE_STATIC_REF_WILL_BE_PERSISTENT(CSSPrimitiveValue, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
1482 DEFINE_STATIC_REF_WILL_BE_PERSISTENT(CSSPrimitiveValue, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
1483 RefPtrWillBeRawPtr<CSSValueList> newTextDecoration = toCSSValueList(textDecoration.get())->copy();
1484 if (newTextDecoration->removeAll(underline))
1485 m_applyUnderline = true;
1486 if (newTextDecoration->removeAll(lineThrough))
1487 m_applyLineThrough = true;
1488
1489 // If trimTextDecorations, delete underline and line-through
1490 setTextDecorationProperty(style, newTextDecoration.get(), textDecorationPropertyForEditing());
1491 }
1492
1493 int verticalAlign = getIdentifierValue(style, CSSPropertyVerticalAlign);
1494 switch (verticalAlign) {
1495 case CSSValueSub:
1496 style->removeProperty(CSSPropertyVerticalAlign);
1497 m_applySubscript = true;
1498 break;
1499 case CSSValueSuper:
1500 style->removeProperty(CSSPropertyVerticalAlign);
1501 m_applySuperscript = true;
1502 break;
1503 }
1504
1505 if (style->getPropertyCSSValue(CSSPropertyColor)) {
1506 m_applyFontColor = Color(getRGBAFontColor(style)).serialized();
1507 style->removeProperty(CSSPropertyColor);
1508 }
1509
1510 m_applyFontFace = style->getPropertyValue(CSSPropertyFontFamily);
1511 // Remove single quotes for Outlook 2007 compatibility. See https://bugs.webkit.org/show_bug.cgi?id=79448
1512 m_applyFontFace.replaceWithLiteral('\'', "");
1513 style->removeProperty(CSSPropertyFontFamily);
1514
1515 if (RefPtrWillBeRawPtr<CSSValue> fontSize = style->getPropertyCSSValue(CSSPropertyFontSize)) {
1516 if (!fontSize->isPrimitiveValue()) {
1517 style->removeProperty(CSSPropertyFontSize); // Can't make sense of the number. Put no font size.
1518 } else if (int legacyFontSize = legacyFontSizeFromCSSValue(document, toCSSPrimitiveValue(fontSize.get()), fixedPitchFontType, UseLegacyFontSizeOnlyIfPixelValuesMatch)) {
1519 m_applyFontSize = String::number(legacyFontSize);
1520 style->removeProperty(CSSPropertyFontSize);
1521 }
1522 }
1523 }
1524
diffTextDecorations(MutableStylePropertySet * style,CSSPropertyID propertID,CSSValue * refTextDecoration)1525 static void diffTextDecorations(MutableStylePropertySet* style, CSSPropertyID propertID, CSSValue* refTextDecoration)
1526 {
1527 RefPtrWillBeRawPtr<CSSValue> textDecoration = style->getPropertyCSSValue(propertID);
1528 if (!textDecoration || !textDecoration->isValueList() || !refTextDecoration || !refTextDecoration->isValueList())
1529 return;
1530
1531 RefPtrWillBeRawPtr<CSSValueList> newTextDecoration = toCSSValueList(textDecoration.get())->copy();
1532 CSSValueList* valuesInRefTextDecoration = toCSSValueList(refTextDecoration);
1533
1534 for (size_t i = 0; i < valuesInRefTextDecoration->length(); i++)
1535 newTextDecoration->removeAll(valuesInRefTextDecoration->item(i));
1536
1537 setTextDecorationProperty(style, newTextDecoration.get(), propertID);
1538 }
1539
fontWeightIsBold(CSSValue * fontWeight)1540 static bool fontWeightIsBold(CSSValue* fontWeight)
1541 {
1542 if (!fontWeight->isPrimitiveValue())
1543 return false;
1544
1545 // Because b tag can only bold text, there are only two states in plain html: bold and not bold.
1546 // Collapse all other values to either one of these two states for editing purposes.
1547 switch (toCSSPrimitiveValue(fontWeight)->getValueID()) {
1548 case CSSValue100:
1549 case CSSValue200:
1550 case CSSValue300:
1551 case CSSValue400:
1552 case CSSValue500:
1553 case CSSValueNormal:
1554 return false;
1555 case CSSValueBold:
1556 case CSSValue600:
1557 case CSSValue700:
1558 case CSSValue800:
1559 case CSSValue900:
1560 return true;
1561 default:
1562 break;
1563 }
1564
1565 ASSERT_NOT_REACHED(); // For CSSValueBolder and CSSValueLighter
1566 return false;
1567 }
1568
fontWeightNeedsResolving(CSSValue * fontWeight)1569 static bool fontWeightNeedsResolving(CSSValue* fontWeight)
1570 {
1571 if (!fontWeight->isPrimitiveValue())
1572 return true;
1573
1574 CSSValueID value = toCSSPrimitiveValue(fontWeight)->getValueID();
1575 return value == CSSValueLighter || value == CSSValueBolder;
1576 }
1577
getPropertiesNotIn(StylePropertySet * styleWithRedundantProperties,CSSStyleDeclaration * baseStyle)1578 PassRefPtrWillBeRawPtr<MutableStylePropertySet> getPropertiesNotIn(StylePropertySet* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle)
1579 {
1580 ASSERT(styleWithRedundantProperties);
1581 ASSERT(baseStyle);
1582 RefPtrWillBeRawPtr<MutableStylePropertySet> result = styleWithRedundantProperties->mutableCopy();
1583
1584 result->removeEquivalentProperties(baseStyle);
1585
1586 RefPtrWillBeRawPtr<CSSValue> baseTextDecorationsInEffect = baseStyle->getPropertyCSSValueInternal(CSSPropertyWebkitTextDecorationsInEffect);
1587 diffTextDecorations(result.get(), textDecorationPropertyForEditing(), baseTextDecorationsInEffect.get());
1588 diffTextDecorations(result.get(), CSSPropertyWebkitTextDecorationsInEffect, baseTextDecorationsInEffect.get());
1589
1590 if (RefPtrWillBeRawPtr<CSSValue> baseFontWeight = baseStyle->getPropertyCSSValueInternal(CSSPropertyFontWeight)) {
1591 if (RefPtrWillBeRawPtr<CSSValue> fontWeight = result->getPropertyCSSValue(CSSPropertyFontWeight)) {
1592 if (!fontWeightNeedsResolving(fontWeight.get()) && (fontWeightIsBold(fontWeight.get()) == fontWeightIsBold(baseFontWeight.get())))
1593 result->removeProperty(CSSPropertyFontWeight);
1594 }
1595 }
1596
1597 if (baseStyle->getPropertyCSSValueInternal(CSSPropertyColor) && getRGBAFontColor(result.get()) == getRGBAFontColor(baseStyle))
1598 result->removeProperty(CSSPropertyColor);
1599
1600 if (baseStyle->getPropertyCSSValueInternal(CSSPropertyTextAlign)
1601 && textAlignResolvingStartAndEnd(result.get()) == textAlignResolvingStartAndEnd(baseStyle))
1602 result->removeProperty(CSSPropertyTextAlign);
1603
1604 if (baseStyle->getPropertyCSSValueInternal(CSSPropertyBackgroundColor) && getRGBABackgroundColor(result.get()) == getRGBABackgroundColor(baseStyle))
1605 result->removeProperty(CSSPropertyBackgroundColor);
1606
1607 return result.release();
1608 }
1609
getIdentifierValue(StylePropertySet * style,CSSPropertyID propertyID)1610 CSSValueID getIdentifierValue(StylePropertySet* style, CSSPropertyID propertyID)
1611 {
1612 if (!style)
1613 return CSSValueInvalid;
1614 RefPtrWillBeRawPtr<CSSValue> value = style->getPropertyCSSValue(propertyID);
1615 if (!value || !value->isPrimitiveValue())
1616 return CSSValueInvalid;
1617 return toCSSPrimitiveValue(value.get())->getValueID();
1618 }
1619
getIdentifierValue(CSSStyleDeclaration * style,CSSPropertyID propertyID)1620 CSSValueID getIdentifierValue(CSSStyleDeclaration* style, CSSPropertyID propertyID)
1621 {
1622 if (!style)
1623 return CSSValueInvalid;
1624 RefPtrWillBeRawPtr<CSSValue> value = style->getPropertyCSSValueInternal(propertyID);
1625 if (!value || !value->isPrimitiveValue())
1626 return CSSValueInvalid;
1627 return toCSSPrimitiveValue(value.get())->getValueID();
1628 }
1629
isCSSValueLength(CSSPrimitiveValue * value)1630 static bool isCSSValueLength(CSSPrimitiveValue* value)
1631 {
1632 return value->isFontIndependentLength();
1633 }
1634
legacyFontSizeFromCSSValue(Document * document,CSSPrimitiveValue * value,FixedPitchFontType fixedPitchFontType,LegacyFontSizeMode mode)1635 int legacyFontSizeFromCSSValue(Document* document, CSSPrimitiveValue* value, FixedPitchFontType fixedPitchFontType, LegacyFontSizeMode mode)
1636 {
1637 if (isCSSValueLength(value)) {
1638 int pixelFontSize = value->getIntValue(CSSPrimitiveValue::CSS_PX);
1639 int legacyFontSize = FontSize::legacyFontSize(document, pixelFontSize, fixedPitchFontType);
1640 // Use legacy font size only if pixel value matches exactly to that of legacy font size.
1641 if (mode == AlwaysUseLegacyFontSize || FontSize::fontSizeForKeyword(document, legacyFontSize, fixedPitchFontType) == pixelFontSize)
1642 return legacyFontSize;
1643
1644 return 0;
1645 }
1646
1647 if (CSSValueXSmall <= value->getValueID() && value->getValueID() <= CSSValueWebkitXxxLarge)
1648 return value->getValueID() - CSSValueXSmall + 1;
1649
1650 return 0;
1651 }
1652
isTransparentColorValue(CSSValue * cssValue)1653 bool isTransparentColorValue(CSSValue* cssValue)
1654 {
1655 if (!cssValue)
1656 return true;
1657 if (!cssValue->isPrimitiveValue())
1658 return false;
1659 CSSPrimitiveValue* value = toCSSPrimitiveValue(cssValue);
1660 if (value->isRGBColor())
1661 return !alphaChannel(value->getRGBA32Value());
1662 return value->getValueID() == CSSValueTransparent;
1663 }
1664
hasTransparentBackgroundColor(CSSStyleDeclaration * style)1665 bool hasTransparentBackgroundColor(CSSStyleDeclaration* style)
1666 {
1667 RefPtrWillBeRawPtr<CSSValue> cssValue = style->getPropertyCSSValueInternal(CSSPropertyBackgroundColor);
1668 return isTransparentColorValue(cssValue.get());
1669 }
1670
hasTransparentBackgroundColor(StylePropertySet * style)1671 bool hasTransparentBackgroundColor(StylePropertySet* style)
1672 {
1673 RefPtrWillBeRawPtr<CSSValue> cssValue = style->getPropertyCSSValue(CSSPropertyBackgroundColor);
1674 return isTransparentColorValue(cssValue.get());
1675 }
1676
backgroundColorInEffect(Node * node)1677 PassRefPtrWillBeRawPtr<CSSValue> backgroundColorInEffect(Node* node)
1678 {
1679 for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) {
1680 RefPtrWillBeRawPtr<CSSComputedStyleDeclaration> ancestorStyle = CSSComputedStyleDeclaration::create(ancestor);
1681 if (!hasTransparentBackgroundColor(ancestorStyle.get()))
1682 return ancestorStyle->getPropertyCSSValue(CSSPropertyBackgroundColor);
1683 }
1684 return nullptr;
1685 }
1686
1687 }
1688