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