2
* Copyright (C) 2007, 2008, 2009 Apple Computer, Inc.
3
* Copyright (C) 2010, 2011 Google Inc. All rights reserved.
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
14
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
#include "EditingStyle.h"
30
#include "ApplyStyleCommand.h"
31
#include "CSSComputedStyleDeclaration.h"
32
#include "CSSParser.h"
33
#include "CSSRuleList.h"
34
#include "CSSStyleRule.h"
35
#include "CSSValueKeywords.h"
36
#include "CSSValueList.h"
38
#include "FrameSelection.h"
39
#include "HTMLFontElement.h"
40
#include "HTMLInterchange.h"
41
#include "HTMLNames.h"
44
#include "QualifiedName.h"
45
#include "RenderStyle.h"
46
#include "StylePropertySet.h"
47
#include "StyleResolver.h"
48
#include "StyleRule.h"
49
#include "StyledElement.h"
50
#include "htmlediting.h"
51
#include "visible_units.h"
52
#include <wtf/HashSet.h>
56
// Editing style properties must be preserved during editing operation.
57
// e.g. when a user inserts a new paragraph, all properties listed here must be copied to the new paragraph.
58
static const CSSPropertyID editingProperties[] = {
59
CSSPropertyBackgroundColor,
60
CSSPropertyTextDecoration,
62
// CSS inheritable properties
64
CSSPropertyFontFamily,
67
CSSPropertyFontVariant,
68
CSSPropertyFontWeight,
69
CSSPropertyLetterSpacing,
70
CSSPropertyLineHeight,
73
CSSPropertyTextIndent,
74
CSSPropertyTextTransform,
75
CSSPropertyWhiteSpace,
77
CSSPropertyWordSpacing,
78
CSSPropertyWebkitTextDecorationsInEffect,
79
CSSPropertyWebkitTextFillColor,
80
CSSPropertyWebkitTextSizeAdjust,
81
CSSPropertyWebkitTextStrokeColor,
82
CSSPropertyWebkitTextStrokeWidth,
85
enum EditingPropertiesType { OnlyInheritableEditingProperties, AllEditingProperties };
87
template <class StyleDeclarationType>
88
static PassRefPtr<StylePropertySet> copyEditingProperties(StyleDeclarationType* style, EditingPropertiesType type = OnlyInheritableEditingProperties)
90
if (type == AllEditingProperties)
91
return style->copyPropertiesInSet(editingProperties, WTF_ARRAY_LENGTH(editingProperties));
92
return style->copyPropertiesInSet(editingProperties + 2, WTF_ARRAY_LENGTH(editingProperties) - 2);
95
static inline bool isEditingProperty(int id)
97
for (size_t i = 0; i < WTF_ARRAY_LENGTH(editingProperties); ++i) {
98
if (editingProperties[i] == id)
104
static PassRefPtr<StylePropertySet> editingStyleFromComputedStyle(PassRefPtr<CSSComputedStyleDeclaration> style, EditingPropertiesType type = OnlyInheritableEditingProperties)
107
return StylePropertySet::create();
108
return copyEditingProperties(style.get(), type);
111
static PassRefPtr<StylePropertySet> getPropertiesNotIn(StylePropertySet* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle);
112
enum LegacyFontSizeMode { AlwaysUseLegacyFontSize, UseLegacyFontSizeOnlyIfPixelValuesMatch };
113
static int legacyFontSizeFromCSSValue(Document*, CSSPrimitiveValue*, bool shouldUseFixedFontDefaultSize, LegacyFontSizeMode);
114
static bool isTransparentColorValue(CSSValue*);
115
static bool hasTransparentBackgroundColor(CSSStyleDeclaration*);
116
static bool hasTransparentBackgroundColor(StylePropertySet*);
117
static PassRefPtr<CSSValue> backgroundColorInEffect(Node*);
119
class HTMLElementEquivalent {
120
WTF_MAKE_FAST_ALLOCATED;
122
static PassOwnPtr<HTMLElementEquivalent> create(CSSPropertyID propertyID, int primitiveValue, const QualifiedName& tagName)
124
return adoptPtr(new HTMLElementEquivalent(propertyID, primitiveValue, tagName));
127
virtual ~HTMLElementEquivalent() { }
128
virtual bool matches(const Element* element) const { return !m_tagName || element->hasTagName(*m_tagName); }
129
virtual bool hasAttribute() const { return false; }
130
virtual bool propertyExistsInStyle(const StylePropertySet* style) const { return style && style->getPropertyCSSValue(m_propertyID); }
131
virtual bool valueIsPresentInStyle(Element*, StylePropertySet*) const;
132
virtual void addToStyle(Element*, EditingStyle*) const;
135
HTMLElementEquivalent(CSSPropertyID);
136
HTMLElementEquivalent(CSSPropertyID, const QualifiedName& tagName);
137
HTMLElementEquivalent(CSSPropertyID, int primitiveValue, const QualifiedName& tagName);
138
const CSSPropertyID m_propertyID;
139
const RefPtr<CSSPrimitiveValue> m_primitiveValue;
140
const QualifiedName* m_tagName; // We can store a pointer because HTML tag names are const global.
143
HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id)
149
HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, const QualifiedName& tagName)
151
, m_tagName(&tagName)
155
HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, int primitiveValue, const QualifiedName& tagName)
157
, m_primitiveValue(CSSPrimitiveValue::createIdentifier(primitiveValue))
158
, m_tagName(&tagName)
160
ASSERT(primitiveValue != CSSValueInvalid);
163
bool HTMLElementEquivalent::valueIsPresentInStyle(Element* element, StylePropertySet* style) const
165
RefPtr<CSSValue> value = style->getPropertyCSSValue(m_propertyID);
166
return matches(element) && value && value->isPrimitiveValue() && static_cast<CSSPrimitiveValue*>(value.get())->getIdent() == m_primitiveValue->getIdent();
169
void HTMLElementEquivalent::addToStyle(Element*, EditingStyle* style) const
171
style->setProperty(m_propertyID, m_primitiveValue->cssText());
174
class HTMLTextDecorationEquivalent : public HTMLElementEquivalent {
176
static PassOwnPtr<HTMLElementEquivalent> create(int primitiveValue, const QualifiedName& tagName)
178
return adoptPtr(new HTMLTextDecorationEquivalent(primitiveValue, tagName));
180
virtual bool propertyExistsInStyle(const StylePropertySet*) const;
181
virtual bool valueIsPresentInStyle(Element*, StylePropertySet*) const;
184
HTMLTextDecorationEquivalent(int primitiveValue, const QualifiedName& tagName);
187
HTMLTextDecorationEquivalent::HTMLTextDecorationEquivalent(int primitiveValue, const QualifiedName& tagName)
188
: HTMLElementEquivalent(CSSPropertyTextDecoration, primitiveValue, tagName)
189
// m_propertyID is used in HTMLElementEquivalent::addToStyle
193
bool HTMLTextDecorationEquivalent::propertyExistsInStyle(const StylePropertySet* style) const
195
return style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect) || style->getPropertyCSSValue(CSSPropertyTextDecoration);
198
bool HTMLTextDecorationEquivalent::valueIsPresentInStyle(Element* element, StylePropertySet* style) const
200
RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
202
styleValue = style->getPropertyCSSValue(CSSPropertyTextDecoration);
203
return matches(element) && styleValue && styleValue->isValueList() && static_cast<CSSValueList*>(styleValue.get())->hasValue(m_primitiveValue.get());
206
class HTMLAttributeEquivalent : public HTMLElementEquivalent {
208
static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& tagName, const QualifiedName& attrName)
210
return adoptPtr(new HTMLAttributeEquivalent(propertyID, tagName, attrName));
212
static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& attrName)
214
return adoptPtr(new HTMLAttributeEquivalent(propertyID, attrName));
217
bool matches(const Element* elem) const { return HTMLElementEquivalent::matches(elem) && elem->hasAttribute(m_attrName); }
218
virtual bool hasAttribute() const { return true; }
219
virtual bool valueIsPresentInStyle(Element*, StylePropertySet*) const;
220
virtual void addToStyle(Element*, EditingStyle*) const;
221
virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const;
222
inline const QualifiedName& attributeName() const { return m_attrName; }
225
HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& tagName, const QualifiedName& attrName);
226
HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& attrName);
227
const QualifiedName& m_attrName; // We can store a reference because HTML attribute names are const global.
230
HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& tagName, const QualifiedName& attrName)
231
: HTMLElementEquivalent(id, tagName)
232
, m_attrName(attrName)
236
HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const QualifiedName& attrName)
237
: HTMLElementEquivalent(id)
238
, m_attrName(attrName)
242
bool HTMLAttributeEquivalent::valueIsPresentInStyle(Element* element, StylePropertySet* style) const
244
RefPtr<CSSValue> value = attributeValueAsCSSValue(element);
245
RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID);
247
// FIXME: This is very inefficient way of comparing values
248
// but we can't string compare attribute value and CSS property value.
249
return value && styleValue && value->cssText() == styleValue->cssText();
252
void HTMLAttributeEquivalent::addToStyle(Element* element, EditingStyle* style) const
254
if (RefPtr<CSSValue> value = attributeValueAsCSSValue(element))
255
style->setProperty(m_propertyID, value->cssText());
258
PassRefPtr<CSSValue> HTMLAttributeEquivalent::attributeValueAsCSSValue(Element* element) const
261
if (!element->hasAttribute(m_attrName))
264
RefPtr<StylePropertySet> dummyStyle;
265
dummyStyle = StylePropertySet::create();
266
dummyStyle->setProperty(m_propertyID, element->getAttribute(m_attrName));
267
return dummyStyle->getPropertyCSSValue(m_propertyID);
270
class HTMLFontSizeEquivalent : public HTMLAttributeEquivalent {
272
static PassOwnPtr<HTMLFontSizeEquivalent> create()
274
return adoptPtr(new HTMLFontSizeEquivalent());
276
virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const;
279
HTMLFontSizeEquivalent();
282
HTMLFontSizeEquivalent::HTMLFontSizeEquivalent()
283
: HTMLAttributeEquivalent(CSSPropertyFontSize, HTMLNames::fontTag, HTMLNames::sizeAttr)
287
PassRefPtr<CSSValue> HTMLFontSizeEquivalent::attributeValueAsCSSValue(Element* element) const
290
if (!element->hasAttribute(m_attrName))
293
if (!HTMLFontElement::cssValueFromFontSizeNumber(element->getAttribute(m_attrName), size))
295
return CSSPrimitiveValue::createIdentifier(size);
298
float EditingStyle::NoFontDelta = 0.0f;
300
EditingStyle::EditingStyle()
301
: m_shouldUseFixedDefaultFontSize(false)
302
, m_fontSizeDelta(NoFontDelta)
306
EditingStyle::EditingStyle(Node* node, PropertiesToInclude propertiesToInclude)
307
: m_shouldUseFixedDefaultFontSize(false)
308
, m_fontSizeDelta(NoFontDelta)
310
init(node, propertiesToInclude);
313
EditingStyle::EditingStyle(const Position& position, PropertiesToInclude propertiesToInclude)
314
: m_shouldUseFixedDefaultFontSize(false)
315
, m_fontSizeDelta(NoFontDelta)
317
init(position.deprecatedNode(), propertiesToInclude);
320
EditingStyle::EditingStyle(const StylePropertySet* style)
321
: m_mutableStyle(style ? style->copy() : 0)
322
, m_shouldUseFixedDefaultFontSize(false)
323
, m_fontSizeDelta(NoFontDelta)
325
extractFontSizeDelta();
328
EditingStyle::EditingStyle(const CSSStyleDeclaration* style)
329
: m_mutableStyle(style ? style->copy() : 0)
330
, m_shouldUseFixedDefaultFontSize(false)
331
, m_fontSizeDelta(NoFontDelta)
333
extractFontSizeDelta();
336
EditingStyle::EditingStyle(CSSPropertyID propertyID, const String& value)
338
, m_shouldUseFixedDefaultFontSize(false)
339
, m_fontSizeDelta(NoFontDelta)
341
setProperty(propertyID, value);
344
EditingStyle::~EditingStyle()
348
static RGBA32 cssValueToRGBA(CSSValue* colorValue)
350
if (!colorValue || !colorValue->isPrimitiveValue())
351
return Color::transparent;
353
CSSPrimitiveValue* primitiveColor = static_cast<CSSPrimitiveValue*>(colorValue);
354
if (primitiveColor->isRGBColor())
355
return primitiveColor->getRGBA32Value();
358
CSSParser::parseColor(rgba, colorValue->cssText());
362
static inline RGBA32 getRGBAFontColor(CSSStyleDeclaration* style)
364
return cssValueToRGBA(style->getPropertyCSSValueInternal(CSSPropertyColor).get());
367
static inline RGBA32 getRGBAFontColor(StylePropertySet* style)
369
return cssValueToRGBA(style->getPropertyCSSValue(CSSPropertyColor).get());
372
static inline RGBA32 getRGBABackgroundColor(CSSStyleDeclaration* style)
374
return cssValueToRGBA(style->getPropertyCSSValueInternal(CSSPropertyBackgroundColor).get());
377
static inline RGBA32 getRGBABackgroundColor(StylePropertySet* style)
379
return cssValueToRGBA(style->getPropertyCSSValue(CSSPropertyBackgroundColor).get());
382
static inline RGBA32 rgbaBackgroundColorInEffect(Node* node)
384
return cssValueToRGBA(backgroundColorInEffect(node).get());
387
static int textAlignResolvingStartAndEnd(int textAlign, int direction)
391
case CSSValueWebkitCenter:
392
return CSSValueCenter;
393
case CSSValueJustify:
394
return CSSValueJustify;
396
case CSSValueWebkitLeft:
399
case CSSValueWebkitRight:
400
return CSSValueRight;
402
return direction != CSSValueRtl ? CSSValueLeft : CSSValueRight;
404
return direction == CSSValueRtl ? CSSValueRight : CSSValueLeft;
406
return CSSValueInvalid;
410
static int textAlignResolvingStartAndEnd(T* style)
412
return textAlignResolvingStartAndEnd(getIdentifierValue(style, CSSPropertyTextAlign), getIdentifierValue(style, CSSPropertyDirection));
415
void EditingStyle::init(Node* node, PropertiesToInclude propertiesToInclude)
417
if (isTabSpanTextNode(node))
418
node = tabSpanNode(node)->parentNode();
419
else if (isTabSpanNode(node))
420
node = node->parentNode();
422
RefPtr<CSSComputedStyleDeclaration> computedStyleAtPosition = CSSComputedStyleDeclaration::create(node);
423
m_mutableStyle = propertiesToInclude == AllProperties && computedStyleAtPosition ? computedStyleAtPosition->copy() : editingStyleFromComputedStyle(computedStyleAtPosition);
425
if (propertiesToInclude == EditingPropertiesInEffect) {
426
if (RefPtr<CSSValue> value = backgroundColorInEffect(node))
427
m_mutableStyle->setProperty(CSSPropertyBackgroundColor, value->cssText());
428
if (RefPtr<CSSValue> value = computedStyleAtPosition->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect))
429
m_mutableStyle->setProperty(CSSPropertyTextDecoration, value->cssText());
432
if (node && node->computedStyle()) {
433
RenderStyle* renderStyle = node->computedStyle();
434
removeTextFillAndStrokeColorsIfNeeded(renderStyle);
435
replaceFontSizeByKeywordIfPossible(renderStyle, computedStyleAtPosition.get());
438
m_shouldUseFixedDefaultFontSize = computedStyleAtPosition->useFixedFontDefaultSize();
439
extractFontSizeDelta();
442
void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyle)
444
// If a node's text fill color is invalid, then its children use
445
// their font-color as their text fill color (they don't
446
// inherit it). Likewise for stroke color.
447
if (!renderStyle->textFillColor().isValid())
448
m_mutableStyle->removeProperty(CSSPropertyWebkitTextFillColor);
449
if (!renderStyle->textStrokeColor().isValid())
450
m_mutableStyle->removeProperty(CSSPropertyWebkitTextStrokeColor);
453
void EditingStyle::setProperty(CSSPropertyID propertyID, const String& value, bool important)
456
m_mutableStyle = StylePropertySet::create();
458
m_mutableStyle->setProperty(propertyID, value, important);
461
void EditingStyle::replaceFontSizeByKeywordIfPossible(RenderStyle* renderStyle, CSSComputedStyleDeclaration* computedStyle)
464
if (renderStyle->fontDescription().keywordSize())
465
m_mutableStyle->setProperty(CSSPropertyFontSize, computedStyle->getFontSizeCSSValuePreferringKeyword()->cssText());
468
void EditingStyle::extractFontSizeDelta()
473
if (m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize)) {
474
// Explicit font size overrides any delta.
475
m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
479
// Get the adjustment amount out of the style.
480
RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta);
481
if (!value || !value->isPrimitiveValue())
484
CSSPrimitiveValue* primitiveValue = static_cast<CSSPrimitiveValue*>(value.get());
486
// Only PX handled now. If we handle more types in the future, perhaps
487
// a switch statement here would be more appropriate.
488
if (!primitiveValue->isPx())
491
m_fontSizeDelta = primitiveValue->getFloatValue();
492
m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
495
bool EditingStyle::isEmpty() const
497
return (!m_mutableStyle || m_mutableStyle->isEmpty()) && m_fontSizeDelta == NoFontDelta;
500
bool EditingStyle::textDirection(WritingDirection& writingDirection) const
505
RefPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
506
if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
509
int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
510
if (unicodeBidiValue == CSSValueEmbed) {
511
RefPtr<CSSValue> direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
512
if (!direction || !direction->isPrimitiveValue())
515
writingDirection = static_cast<CSSPrimitiveValue*>(direction.get())->getIdent() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
520
if (unicodeBidiValue == CSSValueNormal) {
521
writingDirection = NaturalWritingDirection;
528
void EditingStyle::setStyle(PassRefPtr<StylePropertySet> style)
530
m_mutableStyle = style;
531
// FIXME: We should be able to figure out whether or not font is fixed width for mutable style.
532
// We need to check font-family is monospace as in FontDescription but we don't want to duplicate code here.
533
m_shouldUseFixedDefaultFontSize = false;
534
extractFontSizeDelta();
537
void EditingStyle::overrideWithStyle(const StylePropertySet* style)
539
if (!style || style->isEmpty())
542
m_mutableStyle = StylePropertySet::create();
543
m_mutableStyle->mergeAndOverrideOnConflict(style);
544
extractFontSizeDelta();
547
void EditingStyle::clear()
549
m_mutableStyle.clear();
550
m_shouldUseFixedDefaultFontSize = false;
551
m_fontSizeDelta = NoFontDelta;
554
PassRefPtr<EditingStyle> EditingStyle::copy() const
556
RefPtr<EditingStyle> copy = EditingStyle::create();
558
copy->m_mutableStyle = m_mutableStyle->copy();
559
copy->m_shouldUseFixedDefaultFontSize = m_shouldUseFixedDefaultFontSize;
560
copy->m_fontSizeDelta = m_fontSizeDelta;
564
PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveBlockProperties()
566
RefPtr<EditingStyle> blockProperties = EditingStyle::create();
568
return blockProperties;
570
blockProperties->m_mutableStyle = m_mutableStyle->copyBlockProperties();
571
m_mutableStyle->removeBlockProperties();
573
return blockProperties;
576
PassRefPtr<EditingStyle> EditingStyle::extractAndRemoveTextDirection()
578
RefPtr<EditingStyle> textDirection = EditingStyle::create();
579
textDirection->m_mutableStyle = StylePropertySet::create();
580
textDirection->m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueEmbed, m_mutableStyle->propertyIsImportant(CSSPropertyUnicodeBidi));
581
textDirection->m_mutableStyle->setProperty(CSSPropertyDirection, m_mutableStyle->getPropertyValue(CSSPropertyDirection),
582
m_mutableStyle->propertyIsImportant(CSSPropertyDirection));
584
m_mutableStyle->removeProperty(CSSPropertyUnicodeBidi);
585
m_mutableStyle->removeProperty(CSSPropertyDirection);
587
return textDirection;
590
void EditingStyle::removeBlockProperties()
595
m_mutableStyle->removeBlockProperties();
598
void EditingStyle::removeStyleAddedByNode(Node* node)
600
if (!node || !node->parentNode())
602
RefPtr<StylePropertySet> parentStyle = editingStyleFromComputedStyle(CSSComputedStyleDeclaration::create(node->parentNode()), AllEditingProperties);
603
RefPtr<StylePropertySet> nodeStyle = editingStyleFromComputedStyle(CSSComputedStyleDeclaration::create(node), AllEditingProperties);
604
nodeStyle->removeEquivalentProperties(parentStyle->ensureCSSStyleDeclaration());
605
m_mutableStyle->removeEquivalentProperties(nodeStyle->ensureCSSStyleDeclaration());
608
void EditingStyle::removeStyleConflictingWithStyleOfNode(Node* node)
610
if (!node || !node->parentNode() || !m_mutableStyle)
613
RefPtr<StylePropertySet> parentStyle = editingStyleFromComputedStyle(CSSComputedStyleDeclaration::create(node->parentNode()), AllEditingProperties);
614
RefPtr<StylePropertySet> nodeStyle = editingStyleFromComputedStyle(CSSComputedStyleDeclaration::create(node), AllEditingProperties);
615
nodeStyle->removeEquivalentProperties(parentStyle->ensureCSSStyleDeclaration());
617
unsigned propertyCount = nodeStyle->propertyCount();
618
for (unsigned i = 0; i < propertyCount; ++i)
619
m_mutableStyle->removeProperty(nodeStyle->propertyAt(i).id());
622
void EditingStyle::removeNonEditingProperties()
625
m_mutableStyle = copyEditingProperties(m_mutableStyle.get());
628
void EditingStyle::collapseTextDecorationProperties()
633
RefPtr<CSSValue> textDecorationsInEffect = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
634
if (!textDecorationsInEffect)
637
if (textDecorationsInEffect->isValueList())
638
m_mutableStyle->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText(), m_mutableStyle->propertyIsImportant(CSSPropertyTextDecoration));
640
m_mutableStyle->removeProperty(CSSPropertyTextDecoration);
641
m_mutableStyle->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
644
// CSS properties that create a visual difference only when applied to text.
645
static const CSSPropertyID textOnlyProperties[] = {
646
CSSPropertyTextDecoration,
647
CSSPropertyWebkitTextDecorationsInEffect,
648
CSSPropertyFontStyle,
649
CSSPropertyFontWeight,
653
TriState EditingStyle::triStateOfStyle(EditingStyle* style) const
655
if (!style || !style->m_mutableStyle)
656
return FalseTriState;
657
return triStateOfStyle(style->m_mutableStyle->ensureCSSStyleDeclaration(), DoNotIgnoreTextOnlyProperties);
660
TriState EditingStyle::triStateOfStyle(CSSStyleDeclaration* styleToCompare, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const
662
RefPtr<StylePropertySet> difference = getPropertiesNotIn(m_mutableStyle.get(), styleToCompare);
664
if (shouldIgnoreTextOnlyProperties == IgnoreTextOnlyProperties)
665
difference->removePropertiesInSet(textOnlyProperties, WTF_ARRAY_LENGTH(textOnlyProperties));
667
if (difference->isEmpty())
669
if (difference->propertyCount() == m_mutableStyle->propertyCount())
670
return FalseTriState;
672
return MixedTriState;
675
TriState EditingStyle::triStateOfStyle(const VisibleSelection& selection) const
677
if (!selection.isCaretOrRange())
678
return FalseTriState;
680
if (selection.isCaret())
681
return triStateOfStyle(EditingStyle::styleAtSelectionStart(selection).get());
683
TriState state = FalseTriState;
684
for (Node* node = selection.start().deprecatedNode(); node; node = node->traverseNextNode()) {
685
RefPtr<CSSComputedStyleDeclaration> nodeStyle = CSSComputedStyleDeclaration::create(node);
687
TriState nodeState = triStateOfStyle(nodeStyle.get(), node->isTextNode() ? EditingStyle::DoNotIgnoreTextOnlyProperties : EditingStyle::IgnoreTextOnlyProperties);
688
if (node == selection.start().deprecatedNode())
690
else if (state != nodeState && node->isTextNode()) {
691
state = MixedTriState;
695
if (node == selection.end().deprecatedNode())
702
bool EditingStyle::conflictsWithInlineStyleOfElement(StyledElement* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const
705
ASSERT(!conflictingProperties || conflictingProperties->isEmpty());
707
const StylePropertySet* inlineStyle = element->inlineStyle();
708
if (!m_mutableStyle || !inlineStyle)
711
unsigned propertyCount = m_mutableStyle->propertyCount();
712
for (unsigned i = 0; i < propertyCount; ++i) {
713
CSSPropertyID propertyID = m_mutableStyle->propertyAt(i).id();
715
// We don't override whitespace property of a tab span because that would collapse the tab into a space.
716
if (propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element))
719
if (propertyID == CSSPropertyWebkitTextDecorationsInEffect && inlineStyle->getPropertyCSSValue(CSSPropertyTextDecoration)) {
720
if (!conflictingProperties)
722
conflictingProperties->append(CSSPropertyTextDecoration);
724
extractedStyle->setProperty(CSSPropertyTextDecoration, inlineStyle->getPropertyValue(CSSPropertyTextDecoration), inlineStyle->propertyIsImportant(CSSPropertyTextDecoration));
728
if (!inlineStyle->getPropertyCSSValue(propertyID))
731
if (propertyID == CSSPropertyUnicodeBidi && inlineStyle->getPropertyCSSValue(CSSPropertyDirection)) {
732
if (!conflictingProperties)
734
conflictingProperties->append(CSSPropertyDirection);
736
extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->propertyIsImportant(propertyID));
739
if (!conflictingProperties)
742
conflictingProperties->append(propertyID);
745
extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->propertyIsImportant(propertyID));
748
return conflictingProperties && !conflictingProperties->isEmpty();
751
static const Vector<OwnPtr<HTMLElementEquivalent> >& htmlElementEquivalents()
753
DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLElementEquivalent> >, HTMLElementEquivalents, ());
755
if (!HTMLElementEquivalents.size()) {
756
HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::bTag));
757
HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::strongTag));
758
HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSub, HTMLNames::subTag));
759
HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSuper, HTMLNames::supTag));
760
HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::iTag));
761
HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::emTag));
763
HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueUnderline, HTMLNames::uTag));
764
HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::sTag));
765
HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::strikeTag));
768
return HTMLElementEquivalents;
772
bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
777
const Vector<OwnPtr<HTMLElementEquivalent> >& HTMLElementEquivalents = htmlElementEquivalents();
778
for (size_t i = 0; i < HTMLElementEquivalents.size(); ++i) {
779
const HTMLElementEquivalent* equivalent = HTMLElementEquivalents[i].get();
780
if (equivalent->matches(element) && equivalent->propertyExistsInStyle(m_mutableStyle.get())
781
&& (shouldExtractMatchingStyle == ExtractMatchingStyle || !equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) {
783
equivalent->addToStyle(element, extractedStyle);
790
static const Vector<OwnPtr<HTMLAttributeEquivalent> >& htmlAttributeEquivalents()
792
DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLAttributeEquivalent> >, HTMLAttributeEquivalents, ());
794
if (!HTMLAttributeEquivalents.size()) {
795
// elementIsStyledSpanOrHTMLEquivalent depends on the fact each HTMLAttriuteEquivalent matches exactly one attribute
796
// of exactly one element except dirAttr.
797
HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyColor, HTMLNames::fontTag, HTMLNames::colorAttr));
798
HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyFontFamily, HTMLNames::fontTag, HTMLNames::faceAttr));
799
HTMLAttributeEquivalents.append(HTMLFontSizeEquivalent::create());
801
HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyDirection, HTMLNames::dirAttr));
802
HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyUnicodeBidi, HTMLNames::dirAttr));
805
return HTMLAttributeEquivalents;
808
bool EditingStyle::conflictsWithImplicitStyleOfAttributes(HTMLElement* element) const
814
const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
815
for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
816
if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->propertyExistsInStyle(m_mutableStyle.get())
817
&& !HTMLAttributeEquivalents[i]->valueIsPresentInStyle(element, m_mutableStyle.get()))
824
bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* element, ShouldPreserveWritingDirection shouldPreserveWritingDirection,
825
EditingStyle* extractedStyle, Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
828
// HTMLAttributeEquivalent::addToStyle doesn't support unicode-bidi and direction properties
829
ASSERT(!extractedStyle || shouldPreserveWritingDirection == PreserveWritingDirection);
833
const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
834
bool removed = false;
835
for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
836
const HTMLAttributeEquivalent* equivalent = HTMLAttributeEquivalents[i].get();
838
// unicode-bidi and direction are pushed down separately so don't push down with other styles.
839
if (shouldPreserveWritingDirection == PreserveWritingDirection && equivalent->attributeName() == HTMLNames::dirAttr)
842
if (!equivalent->matches(element) || !equivalent->propertyExistsInStyle(m_mutableStyle.get())
843
|| (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(element, m_mutableStyle.get())))
847
equivalent->addToStyle(element, extractedStyle);
848
conflictingAttributes.append(equivalent->attributeName());
855
bool EditingStyle::styleIsPresentInComputedStyleOfNode(Node* node) const
857
return !m_mutableStyle || getPropertiesNotIn(m_mutableStyle.get(), CSSComputedStyleDeclaration::create(node).get())->isEmpty();
860
bool EditingStyle::elementIsStyledSpanOrHTMLEquivalent(const HTMLElement* element)
862
bool elementIsSpanOrElementEquivalent = false;
863
if (element->hasTagName(HTMLNames::spanTag))
864
elementIsSpanOrElementEquivalent = true;
866
const Vector<OwnPtr<HTMLElementEquivalent> >& HTMLElementEquivalents = htmlElementEquivalents();
868
for (i = 0; i < HTMLElementEquivalents.size(); ++i) {
869
if (HTMLElementEquivalents[i]->matches(element)) {
870
elementIsSpanOrElementEquivalent = true;
876
if (!element->hasAttributes())
877
return elementIsSpanOrElementEquivalent; // span, b, etc... without any attributes
879
unsigned matchedAttributes = 0;
880
const Vector<OwnPtr<HTMLAttributeEquivalent> >& HTMLAttributeEquivalents = htmlAttributeEquivalents();
881
for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
882
if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->attributeName() != HTMLNames::dirAttr)
886
if (!elementIsSpanOrElementEquivalent && !matchedAttributes)
887
return false; // element is not a span, a html element equivalent, or font element.
889
if (element->getAttribute(HTMLNames::classAttr) == AppleStyleSpanClass)
892
if (element->hasAttribute(HTMLNames::styleAttr)) {
893
if (const StylePropertySet* style = element->inlineStyle()) {
894
unsigned propertyCount = style->propertyCount();
895
for (unsigned i = 0; i < propertyCount; ++i) {
896
if (!isEditingProperty(style->propertyAt(i).id()))
903
// font with color attribute, span with style attribute, etc...
904
ASSERT(matchedAttributes <= element->attributeCount());
905
return matchedAttributes >= element->attributeCount();
908
void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWritingDirection shouldPreserveWritingDirection)
913
// ReplaceSelectionCommand::handleStyleSpans() requires that this function only removes the editing style.
914
// If this function was modified in the future to delete all redundant properties, then add a boolean value to indicate
915
// which one of editingStyleAtPosition or computedStyle is called.
916
RefPtr<EditingStyle> editingStyleAtPosition = EditingStyle::create(position, EditingPropertiesInEffect);
917
StylePropertySet* styleAtPosition = editingStyleAtPosition->m_mutableStyle.get();
919
RefPtr<CSSValue> unicodeBidi;
920
RefPtr<CSSValue> direction;
921
if (shouldPreserveWritingDirection == PreserveWritingDirection) {
922
unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
923
direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
926
m_mutableStyle->removeEquivalentProperties(styleAtPosition);
928
if (textAlignResolvingStartAndEnd(m_mutableStyle.get()) == textAlignResolvingStartAndEnd(styleAtPosition))
929
m_mutableStyle->removeProperty(CSSPropertyTextAlign);
931
if (getRGBAFontColor(m_mutableStyle.get()) == getRGBAFontColor(styleAtPosition))
932
m_mutableStyle->removeProperty(CSSPropertyColor);
934
if (hasTransparentBackgroundColor(m_mutableStyle.get())
935
|| cssValueToRGBA(m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor).get()) == rgbaBackgroundColorInEffect(position.containerNode()))
936
m_mutableStyle->removeProperty(CSSPropertyBackgroundColor);
938
if (unicodeBidi && unicodeBidi->isPrimitiveValue()) {
939
m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent());
940
if (direction && direction->isPrimitiveValue())
941
m_mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSPrimitiveValue*>(direction.get())->getIdent());
945
void EditingStyle::mergeTypingStyle(Document* document)
949
RefPtr<EditingStyle> typingStyle = document->frame()->selection()->typingStyle();
950
if (!typingStyle || typingStyle == this)
953
mergeStyle(typingStyle->style(), OverrideValues);
956
void EditingStyle::mergeInlineStyleOfElement(StyledElement* element, CSSPropertyOverrideMode mode, PropertiesToInclude propertiesToInclude)
959
if (!element->inlineStyle())
962
switch (propertiesToInclude) {
964
mergeStyle(element->inlineStyle(), mode);
966
case OnlyEditingInheritableProperties:
967
mergeStyle(copyEditingProperties(element->inlineStyle(), OnlyInheritableEditingProperties).get(), mode);
969
case EditingPropertiesInEffect:
970
mergeStyle(copyEditingProperties(element->inlineStyle(), AllEditingProperties).get(), mode);
975
static inline bool elementMatchesAndPropertyIsNotInInlineStyleDecl(const HTMLElementEquivalent* equivalent, const StyledElement* element,
976
EditingStyle::CSSPropertyOverrideMode mode, StylePropertySet* style)
978
return equivalent->matches(element) && !equivalent->propertyExistsInStyle(element->inlineStyle())
979
&& (mode == EditingStyle::OverrideValues || !equivalent->propertyExistsInStyle(style));
982
void EditingStyle::mergeInlineAndImplicitStyleOfElement(StyledElement* element, CSSPropertyOverrideMode mode, PropertiesToInclude propertiesToInclude)
984
RefPtr<EditingStyle> styleFromRules = EditingStyle::create();
985
styleFromRules->mergeStyleFromRulesForSerialization(element);
986
styleFromRules->removeNonEditingProperties();
987
mergeStyle(styleFromRules->m_mutableStyle.get(), mode);
989
mergeInlineStyleOfElement(element, mode, propertiesToInclude);
991
const Vector<OwnPtr<HTMLElementEquivalent> >& elementEquivalents = htmlElementEquivalents();
992
for (size_t i = 0; i < elementEquivalents.size(); ++i) {
993
if (elementMatchesAndPropertyIsNotInInlineStyleDecl(elementEquivalents[i].get(), element, mode, m_mutableStyle.get()))
994
elementEquivalents[i]->addToStyle(element, this);
997
const Vector<OwnPtr<HTMLAttributeEquivalent> >& attributeEquivalents = htmlAttributeEquivalents();
998
for (size_t i = 0; i < attributeEquivalents.size(); ++i) {
999
if (attributeEquivalents[i]->attributeName() == HTMLNames::dirAttr)
1000
continue; // We don't want to include directionality
1001
if (elementMatchesAndPropertyIsNotInInlineStyleDecl(attributeEquivalents[i].get(), element, mode, m_mutableStyle.get()))
1002
attributeEquivalents[i]->addToStyle(element, this);
1006
PassRefPtr<EditingStyle> EditingStyle::wrappingStyleForSerialization(Node* context, bool shouldAnnotate)
1008
RefPtr<EditingStyle> wrappingStyle;
1009
if (shouldAnnotate) {
1010
wrappingStyle = EditingStyle::create(context, EditingStyle::EditingPropertiesInEffect);
1012
// Styles that Mail blockquotes contribute should only be placed on the Mail blockquote,
1013
// to help us differentiate those styles from ones that the user has applied.
1014
// This helps us get the color of content pasted into blockquotes right.
1015
wrappingStyle->removeStyleAddedByNode(enclosingNodeOfType(firstPositionInOrBeforeNode(context), isMailBlockquote, CanCrossEditingBoundary));
1017
// Call collapseTextDecorationProperties first or otherwise it'll copy the value over from in-effect to text-decorations.
1018
wrappingStyle->collapseTextDecorationProperties();
1020
return wrappingStyle.release();
1023
wrappingStyle = EditingStyle::create();
1025
// When not annotating for interchange, we only preserve inline style declarations.
1026
for (Node* node = context; node && !node->isDocumentNode(); node = node->parentNode()) {
1027
if (node->isStyledElement() && !isMailBlockquote(node)) {
1028
wrappingStyle->mergeInlineAndImplicitStyleOfElement(static_cast<StyledElement*>(node), EditingStyle::DoNotOverrideValues,
1029
EditingStyle::EditingPropertiesInEffect);
1033
return wrappingStyle.release();
1037
static void mergeTextDecorationValues(CSSValueList* mergedValue, const CSSValueList* valueToMerge)
1039
DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
1040
DEFINE_STATIC_LOCAL(const RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
1042
if (valueToMerge->hasValue(underline.get()) && !mergedValue->hasValue(underline.get()))
1043
mergedValue->append(underline.get());
1045
if (valueToMerge->hasValue(lineThrough.get()) && !mergedValue->hasValue(lineThrough.get()))
1046
mergedValue->append(lineThrough.get());
1049
void EditingStyle::mergeStyle(const StylePropertySet* style, CSSPropertyOverrideMode mode)
1054
if (!m_mutableStyle) {
1055
m_mutableStyle = style->copy();
1059
unsigned propertyCount = style->propertyCount();
1060
for (unsigned i = 0; i < propertyCount; ++i) {
1061
StylePropertySet::PropertyReference property = style->propertyAt(i);
1062
RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(property.id());
1064
// text decorations never override values
1065
if ((property.id() == CSSPropertyTextDecoration || property.id() == CSSPropertyWebkitTextDecorationsInEffect) && property.value()->isValueList() && value) {
1066
if (value->isValueList()) {
1067
mergeTextDecorationValues(static_cast<CSSValueList*>(value.get()), static_cast<CSSValueList*>(property.value()));
1070
value = 0; // text-decoration: none is equivalent to not having the property
1073
if (mode == OverrideValues || (mode == DoNotOverrideValues && !value))
1074
m_mutableStyle->setProperty(property.id(), property.value()->cssText(), property.isImportant());
1078
static PassRefPtr<StylePropertySet> styleFromMatchedRulesForElement(Element* element, unsigned rulesToInclude)
1080
RefPtr<StylePropertySet> style = StylePropertySet::create();
1081
RefPtr<CSSRuleList> matchedRules = element->document()->styleResolver()->styleRulesForElement(element, rulesToInclude);
1083
for (unsigned i = 0; i < matchedRules->length(); i++) {
1084
if (matchedRules->item(i)->type() == CSSRule::STYLE_RULE)
1085
style->mergeAndOverrideOnConflict(static_cast<CSSStyleRule*>(matchedRules->item(i))->styleRule()->properties());
1089
return style.release();
1092
void EditingStyle::mergeStyleFromRules(StyledElement* element)
1094
RefPtr<StylePropertySet> styleFromMatchedRules = styleFromMatchedRulesForElement(element,
1095
StyleResolver::AuthorCSSRules | StyleResolver::CrossOriginCSSRules);
1096
// Styles from the inline style declaration, held in the variable "style", take precedence
1097
// over those from matched rules.
1099
styleFromMatchedRules->mergeAndOverrideOnConflict(m_mutableStyle.get());
1102
m_mutableStyle = styleFromMatchedRules;
1105
void EditingStyle::mergeStyleFromRulesForSerialization(StyledElement* element)
1107
mergeStyleFromRules(element);
1109
// The property value, if it's a percentage, may not reflect the actual computed value.
1110
// For example: style="height: 1%; overflow: visible;" in quirksmode
1111
// FIXME: There are others like this, see <rdar://problem/5195123> Slashdot copy/paste fidelity problem
1112
RefPtr<CSSComputedStyleDeclaration> computedStyleForElement = CSSComputedStyleDeclaration::create(element);
1113
RefPtr<StylePropertySet> fromComputedStyle = StylePropertySet::create();
1115
unsigned propertyCount = m_mutableStyle->propertyCount();
1116
for (unsigned i = 0; i < propertyCount; ++i) {
1117
StylePropertySet::PropertyReference property = m_mutableStyle->propertyAt(i);
1118
CSSValue* value = property.value();
1119
if (!value->isPrimitiveValue())
1121
if (static_cast<CSSPrimitiveValue*>(value)->isPercentage()) {
1122
if (RefPtr<CSSValue> computedPropertyValue = computedStyleForElement->getPropertyCSSValue(property.id()))
1123
fromComputedStyle->addParsedProperty(CSSProperty(property.id(), computedPropertyValue));
1127
m_mutableStyle->mergeAndOverrideOnConflict(fromComputedStyle.get());
1130
static void removePropertiesInStyle(StylePropertySet* styleToRemovePropertiesFrom, StylePropertySet* style)
1132
unsigned propertyCount = style->propertyCount();
1133
Vector<CSSPropertyID> propertiesToRemove(propertyCount);
1134
for (unsigned i = 0; i < propertyCount; ++i)
1135
propertiesToRemove[i] = style->propertyAt(i).id();
1137
styleToRemovePropertiesFrom->removePropertiesInSet(propertiesToRemove.data(), propertiesToRemove.size());
1140
void EditingStyle::removeStyleFromRulesAndContext(StyledElement* element, Node* context)
1143
if (!m_mutableStyle)
1146
// 1. Remove style from matched rules because style remain without repeating it in inline style declaration
1147
RefPtr<StylePropertySet> styleFromMatchedRules = styleFromMatchedRulesForElement(element, StyleResolver::AllButEmptyCSSRules);
1148
if (styleFromMatchedRules && !styleFromMatchedRules->isEmpty())
1149
m_mutableStyle = getPropertiesNotIn(m_mutableStyle.get(), styleFromMatchedRules->ensureCSSStyleDeclaration());
1151
// 2. Remove style present in context and not overriden by matched rules.
1152
RefPtr<EditingStyle> computedStyle = EditingStyle::create(context, EditingPropertiesInEffect);
1153
if (computedStyle->m_mutableStyle) {
1154
if (!computedStyle->m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor))
1155
computedStyle->m_mutableStyle->setProperty(CSSPropertyBackgroundColor, CSSValueTransparent);
1157
removePropertiesInStyle(computedStyle->m_mutableStyle.get(), styleFromMatchedRules.get());
1158
m_mutableStyle = getPropertiesNotIn(m_mutableStyle.get(), computedStyle->m_mutableStyle->ensureCSSStyleDeclaration());
1161
// 3. If this element is a span and has display: inline or float: none, remove them unless they are overriden by rules.
1162
// These rules are added by serialization code to wrap text nodes.
1163
if (isStyleSpanOrSpanWithOnlyStyleAttribute(element)) {
1164
if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyDisplay) && getIdentifierValue(m_mutableStyle.get(), CSSPropertyDisplay) == CSSValueInline)
1165
m_mutableStyle->removeProperty(CSSPropertyDisplay);
1166
if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyFloat) && getIdentifierValue(m_mutableStyle.get(), CSSPropertyFloat) == CSSValueNone)
1167
m_mutableStyle->removeProperty(CSSPropertyFloat);
1171
void EditingStyle::removePropertiesInElementDefaultStyle(Element* element)
1173
if (!m_mutableStyle || m_mutableStyle->isEmpty())
1176
RefPtr<StylePropertySet> defaultStyle = styleFromMatchedRulesForElement(element, StyleResolver::UAAndUserCSSRules);
1178
removePropertiesInStyle(m_mutableStyle.get(), defaultStyle.get());
1181
void EditingStyle::forceInline()
1183
if (!m_mutableStyle)
1184
m_mutableStyle = StylePropertySet::create();
1185
const bool propertyIsImportant = true;
1186
m_mutableStyle->setProperty(CSSPropertyDisplay, CSSValueInline, propertyIsImportant);
1189
int EditingStyle::legacyFontSize(Document* document) const
1191
RefPtr<CSSValue> cssValue = m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize);
1192
if (!cssValue || !cssValue->isPrimitiveValue())
1194
return legacyFontSizeFromCSSValue(document, static_cast<CSSPrimitiveValue*>(cssValue.get()),
1195
m_shouldUseFixedDefaultFontSize, AlwaysUseLegacyFontSize);
1198
PassRefPtr<EditingStyle> EditingStyle::styleAtSelectionStart(const VisibleSelection& selection, bool shouldUseBackgroundColorInEffect)
1200
if (selection.isNone())
1203
Position position = adjustedSelectionStartForStyleComputation(selection);
1205
// If the pos is at the end of a text node, then this node is not fully selected.
1206
// Move it to the next deep equivalent position to avoid removing the style from this node.
1207
// e.g. if pos was at Position("hello", 5) in <b>hello<div>world</div></b>, we want Position("world", 0) instead.
1208
// We only do this for range because caret at Position("hello", 5) in <b>hello</b>world should give you font-weight: bold.
1209
Node* positionNode = position.containerNode();
1210
if (selection.isRange() && positionNode && positionNode->isTextNode() && position.computeOffsetInContainerNode() == positionNode->maxCharacterOffset())
1211
position = nextVisuallyDistinctCandidate(position);
1213
Element* element = position.element();
1217
RefPtr<EditingStyle> style = EditingStyle::create(element, EditingStyle::AllProperties);
1218
style->mergeTypingStyle(element->document());
1220
// If background color is transparent, traverse parent nodes until we hit a different value or document root
1221
// Also, if the selection is a range, ignore the background color at the start of selection,
1222
// and find the background color of the common ancestor.
1223
if (shouldUseBackgroundColorInEffect && (selection.isRange() || hasTransparentBackgroundColor(style->m_mutableStyle.get()))) {
1224
RefPtr<Range> range(selection.toNormalizedRange());
1225
ExceptionCode ec = 0;
1226
if (PassRefPtr<CSSValue> value = backgroundColorInEffect(range->commonAncestorContainer(ec)))
1227
style->setProperty(CSSPropertyBackgroundColor, value->cssText());
1233
WritingDirection EditingStyle::textDirectionForSelection(const VisibleSelection& selection, EditingStyle* typingStyle, bool& hasNestedOrMultipleEmbeddings)
1235
hasNestedOrMultipleEmbeddings = true;
1237
if (selection.isNone())
1238
return NaturalWritingDirection;
1240
Position position = selection.start().downstream();
1242
Node* node = position.deprecatedNode();
1244
return NaturalWritingDirection;
1247
if (selection.isRange()) {
1248
end = selection.end().upstream();
1250
Node* pastLast = Range::create(end.document(), position.parentAnchoredEquivalent(), end.parentAnchoredEquivalent())->pastLastNode();
1251
for (Node* n = node; n && n != pastLast; n = n->traverseNextNode()) {
1252
if (!n->isStyledElement())
1255
RefPtr<CSSComputedStyleDeclaration> style = CSSComputedStyleDeclaration::create(n);
1256
RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
1257
if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
1260
int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
1261
if (unicodeBidiValue == CSSValueEmbed || unicodeBidiValue == CSSValueBidiOverride)
1262
return NaturalWritingDirection;
1266
if (selection.isCaret()) {
1267
WritingDirection direction;
1268
if (typingStyle && typingStyle->textDirection(direction)) {
1269
hasNestedOrMultipleEmbeddings = false;
1272
node = selection.visibleStart().deepEquivalent().deprecatedNode();
1275
// 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
1277
Node* block = enclosingBlock(node);
1278
WritingDirection foundDirection = NaturalWritingDirection;
1280
for (; node != block; node = node->parentNode()) {
1281
if (!node->isStyledElement())
1284
RefPtr<CSSComputedStyleDeclaration> style = CSSComputedStyleDeclaration::create(node);
1285
RefPtr<CSSValue> unicodeBidi = style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
1286
if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
1289
int unicodeBidiValue = static_cast<CSSPrimitiveValue*>(unicodeBidi.get())->getIdent();
1290
if (unicodeBidiValue == CSSValueNormal)
1293
if (unicodeBidiValue == CSSValueBidiOverride)
1294
return NaturalWritingDirection;
1296
ASSERT(unicodeBidiValue == CSSValueEmbed);
1297
RefPtr<CSSValue> direction = style->getPropertyCSSValue(CSSPropertyDirection);
1298
if (!direction || !direction->isPrimitiveValue())
1301
int directionValue = static_cast<CSSPrimitiveValue*>(direction.get())->getIdent();
1302
if (directionValue != CSSValueLtr && directionValue != CSSValueRtl)
1305
if (foundDirection != NaturalWritingDirection)
1306
return NaturalWritingDirection;
1308
// In the range case, make sure that the embedding element persists until the end of the range.
1309
if (selection.isRange() && !end.deprecatedNode()->isDescendantOf(node))
1310
return NaturalWritingDirection;
1312
foundDirection = directionValue == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
1314
hasNestedOrMultipleEmbeddings = false;
1315
return foundDirection;
1318
static void reconcileTextDecorationProperties(StylePropertySet* style)
1320
RefPtr<CSSValue> textDecorationsInEffect = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
1321
RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration);
1322
// We shouldn't have both text-decoration and -webkit-text-decorations-in-effect because that wouldn't make sense.
1323
ASSERT(!textDecorationsInEffect || !textDecoration);
1324
if (textDecorationsInEffect) {
1325
style->setProperty(CSSPropertyTextDecoration, textDecorationsInEffect->cssText());
1326
style->removeProperty(CSSPropertyWebkitTextDecorationsInEffect);
1327
textDecoration = textDecorationsInEffect;
1330
// If text-decoration is set to "none", remove the property because we don't want to add redundant "text-decoration: none".
1331
if (textDecoration && !textDecoration->isValueList())
1332
style->removeProperty(CSSPropertyTextDecoration);
1335
StyleChange::StyleChange(EditingStyle* style, const Position& position)
1336
: m_applyBold(false)
1337
, m_applyItalic(false)
1338
, m_applyUnderline(false)
1339
, m_applyLineThrough(false)
1340
, m_applySubscript(false)
1341
, m_applySuperscript(false)
1343
Document* document = position.anchorNode() ? position.anchorNode()->document() : 0;
1344
if (!style || !style->style() || !document || !document->frame())
1347
RefPtr<CSSComputedStyleDeclaration> computedStyle = position.computedStyle();
1348
// FIXME: take care of background-color in effect
1349
RefPtr<StylePropertySet> mutableStyle = getPropertiesNotIn(style->style(), computedStyle.get());
1351
reconcileTextDecorationProperties(mutableStyle.get());
1352
if (!document->frame()->editor()->shouldStyleWithCSS())
1353
extractTextStyles(document, mutableStyle.get(), computedStyle->useFixedFontDefaultSize());
1355
// Changing the whitespace style in a tab span would collapse the tab into a space.
1356
if (isTabSpanTextNode(position.deprecatedNode()) || isTabSpanNode((position.deprecatedNode())))
1357
mutableStyle->removeProperty(CSSPropertyWhiteSpace);
1359
// If unicode-bidi is present in mutableStyle and direction is not, then add direction to mutableStyle.
1360
// FIXME: Shouldn't this be done in getPropertiesNotIn?
1361
if (mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi) && !style->style()->getPropertyCSSValue(CSSPropertyDirection))
1362
mutableStyle->setProperty(CSSPropertyDirection, style->style()->getPropertyValue(CSSPropertyDirection));
1364
// Save the result for later
1365
m_cssStyle = mutableStyle->asText().stripWhiteSpace();
1368
static void setTextDecorationProperty(StylePropertySet* style, const CSSValueList* newTextDecoration, CSSPropertyID propertyID)
1370
if (newTextDecoration->length())
1371
style->setProperty(propertyID, newTextDecoration->cssText(), style->propertyIsImportant(propertyID));
1373
// text-decoration: none is redundant since it does not remove any text decorations.
1374
ASSERT(!style->propertyIsImportant(propertyID));
1375
style->removeProperty(propertyID);
1379
void StyleChange::extractTextStyles(Document* document, StylePropertySet* style, bool shouldUseFixedFontDefaultSize)
1383
if (getIdentifierValue(style, CSSPropertyFontWeight) == CSSValueBold) {
1384
style->removeProperty(CSSPropertyFontWeight);
1388
int fontStyle = getIdentifierValue(style, CSSPropertyFontStyle);
1389
if (fontStyle == CSSValueItalic || fontStyle == CSSValueOblique) {
1390
style->removeProperty(CSSPropertyFontStyle);
1391
m_applyItalic = true;
1394
// Assuming reconcileTextDecorationProperties has been called, there should not be -webkit-text-decorations-in-effect
1395
// Furthermore, text-decoration: none has been trimmed so that text-decoration property is always a CSSValueList.
1396
RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration);
1397
if (textDecoration && textDecoration->isValueList()) {
1398
DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, underline, (CSSPrimitiveValue::createIdentifier(CSSValueUnderline)));
1399
DEFINE_STATIC_LOCAL(RefPtr<CSSPrimitiveValue>, lineThrough, (CSSPrimitiveValue::createIdentifier(CSSValueLineThrough)));
1401
RefPtr<CSSValueList> newTextDecoration = static_cast<CSSValueList*>(textDecoration.get())->copy();
1402
if (newTextDecoration->removeAll(underline.get()))
1403
m_applyUnderline = true;
1404
if (newTextDecoration->removeAll(lineThrough.get()))
1405
m_applyLineThrough = true;
1407
// If trimTextDecorations, delete underline and line-through
1408
setTextDecorationProperty(style, newTextDecoration.get(), CSSPropertyTextDecoration);
1411
int verticalAlign = getIdentifierValue(style, CSSPropertyVerticalAlign);
1412
switch (verticalAlign) {
1414
style->removeProperty(CSSPropertyVerticalAlign);
1415
m_applySubscript = true;
1418
style->removeProperty(CSSPropertyVerticalAlign);
1419
m_applySuperscript = true;
1423
if (style->getPropertyCSSValue(CSSPropertyColor)) {
1424
m_applyFontColor = Color(getRGBAFontColor(style)).serialized();
1425
style->removeProperty(CSSPropertyColor);
1428
m_applyFontFace = style->getPropertyValue(CSSPropertyFontFamily);
1429
// Remove single quotes for Outlook 2007 compatibility. See https://bugs.webkit.org/show_bug.cgi?id=79448
1430
m_applyFontFace.replaceWithLiteral('\'', "");
1431
style->removeProperty(CSSPropertyFontFamily);
1433
if (RefPtr<CSSValue> fontSize = style->getPropertyCSSValue(CSSPropertyFontSize)) {
1434
if (!fontSize->isPrimitiveValue())
1435
style->removeProperty(CSSPropertyFontSize); // Can't make sense of the number. Put no font size.
1436
else if (int legacyFontSize = legacyFontSizeFromCSSValue(document, static_cast<CSSPrimitiveValue*>(fontSize.get()),
1437
shouldUseFixedFontDefaultSize, UseLegacyFontSizeOnlyIfPixelValuesMatch)) {
1438
m_applyFontSize = String::number(legacyFontSize);
1439
style->removeProperty(CSSPropertyFontSize);
1444
static void diffTextDecorations(StylePropertySet* style, CSSPropertyID propertID, CSSValue* refTextDecoration)
1446
RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(propertID);
1447
if (!textDecoration || !textDecoration->isValueList() || !refTextDecoration || !refTextDecoration->isValueList())
1450
RefPtr<CSSValueList> newTextDecoration = static_cast<CSSValueList*>(textDecoration.get())->copy();
1451
CSSValueList* valuesInRefTextDecoration = static_cast<CSSValueList*>(refTextDecoration);
1453
for (size_t i = 0; i < valuesInRefTextDecoration->length(); i++)
1454
newTextDecoration->removeAll(valuesInRefTextDecoration->item(i));
1456
setTextDecorationProperty(style, newTextDecoration.get(), propertID);
1459
static bool fontWeightIsBold(CSSValue* fontWeight)
1463
if (!fontWeight->isPrimitiveValue())
1466
// Because b tag can only bold text, there are only two states in plain html: bold and not bold.
1467
// Collapse all other values to either one of these two states for editing purposes.
1468
switch (static_cast<CSSPrimitiveValue*>(fontWeight)->getIdent()) {
1474
case CSSValueNormal:
1484
ASSERT_NOT_REACHED(); // For CSSValueBolder and CSSValueLighter
1488
static bool fontWeightIsBold(CSSStyleDeclaration* style)
1491
RefPtr<CSSValue> fontWeight = style->getPropertyCSSValueInternal(CSSPropertyFontWeight);
1492
return fontWeightIsBold(fontWeight.get());
1495
static bool fontWeightIsBold(StylePropertySet* style)
1498
RefPtr<CSSValue> fontWeight = style->getPropertyCSSValue(CSSPropertyFontWeight);
1499
return fontWeightIsBold(fontWeight.get());
1502
PassRefPtr<StylePropertySet> getPropertiesNotIn(StylePropertySet* styleWithRedundantProperties, CSSStyleDeclaration* baseStyle)
1504
ASSERT(styleWithRedundantProperties);
1506
RefPtr<StylePropertySet> result = styleWithRedundantProperties->copy();
1508
result->removeEquivalentProperties(baseStyle);
1510
RefPtr<CSSValue> baseTextDecorationsInEffect = baseStyle->getPropertyCSSValueInternal(CSSPropertyWebkitTextDecorationsInEffect);
1511
diffTextDecorations(result.get(), CSSPropertyTextDecoration, baseTextDecorationsInEffect.get());
1512
diffTextDecorations(result.get(), CSSPropertyWebkitTextDecorationsInEffect, baseTextDecorationsInEffect.get());
1514
if (baseStyle->getPropertyCSSValueInternal(CSSPropertyFontWeight) && fontWeightIsBold(result.get()) == fontWeightIsBold(baseStyle))
1515
result->removeProperty(CSSPropertyFontWeight);
1517
if (baseStyle->getPropertyCSSValueInternal(CSSPropertyColor) && getRGBAFontColor(result.get()) == getRGBAFontColor(baseStyle))
1518
result->removeProperty(CSSPropertyColor);
1520
if (baseStyle->getPropertyCSSValueInternal(CSSPropertyTextAlign)
1521
&& textAlignResolvingStartAndEnd(result.get()) == textAlignResolvingStartAndEnd(baseStyle))
1522
result->removeProperty(CSSPropertyTextAlign);
1524
if (baseStyle->getPropertyCSSValueInternal(CSSPropertyBackgroundColor) && getRGBABackgroundColor(result.get()) == getRGBABackgroundColor(baseStyle))
1525
result->removeProperty(CSSPropertyBackgroundColor);
1530
int getIdentifierValue(StylePropertySet* style, CSSPropertyID propertyID)
1534
RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID);
1535
if (!value || !value->isPrimitiveValue())
1537
return static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
1540
int getIdentifierValue(CSSStyleDeclaration* style, CSSPropertyID propertyID)
1544
RefPtr<CSSValue> value = style->getPropertyCSSValueInternal(propertyID);
1545
if (!value || !value->isPrimitiveValue())
1547
return static_cast<CSSPrimitiveValue*>(value.get())->getIdent();
1550
static bool isCSSValueLength(CSSPrimitiveValue* value)
1552
return value->isFontIndependentLength();
1555
int legacyFontSizeFromCSSValue(Document* document, CSSPrimitiveValue* value, bool shouldUseFixedFontDefaultSize, LegacyFontSizeMode mode)
1557
if (isCSSValueLength(value)) {
1558
int pixelFontSize = value->getIntValue(CSSPrimitiveValue::CSS_PX);
1559
int legacyFontSize = StyleResolver::legacyFontSize(document, pixelFontSize, shouldUseFixedFontDefaultSize);
1560
// Use legacy font size only if pixel value matches exactly to that of legacy font size.
1561
int cssPrimitiveEquivalent = legacyFontSize - 1 + CSSValueXSmall;
1562
if (mode == AlwaysUseLegacyFontSize || StyleResolver::fontSizeForKeyword(document, cssPrimitiveEquivalent, shouldUseFixedFontDefaultSize) == pixelFontSize)
1563
return legacyFontSize;
1568
if (CSSValueXSmall <= value->getIdent() && value->getIdent() <= CSSValueWebkitXxxLarge)
1569
return value->getIdent() - CSSValueXSmall + 1;
1574
bool isTransparentColorValue(CSSValue* cssValue)
1578
if (!cssValue->isPrimitiveValue())
1580
CSSPrimitiveValue* value = static_cast<CSSPrimitiveValue*>(cssValue);
1581
if (value->isRGBColor())
1582
return !alphaChannel(value->getRGBA32Value());
1583
return value->getIdent() == CSSValueTransparent;
1586
bool hasTransparentBackgroundColor(CSSStyleDeclaration* style)
1588
RefPtr<CSSValue> cssValue = style->getPropertyCSSValueInternal(CSSPropertyBackgroundColor);
1589
return isTransparentColorValue(cssValue.get());
1592
bool hasTransparentBackgroundColor(StylePropertySet* style)
1594
RefPtr<CSSValue> cssValue = style->getPropertyCSSValue(CSSPropertyBackgroundColor);
1595
return isTransparentColorValue(cssValue.get());
1598
PassRefPtr<CSSValue> backgroundColorInEffect(Node* node)
1600
for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) {
1601
RefPtr<CSSComputedStyleDeclaration> ancestorStyle = CSSComputedStyleDeclaration::create(ancestor);
1602
if (!hasTransparentBackgroundColor(ancestorStyle.get()))
1603
return ancestorStyle->getPropertyCSSValue(CSSPropertyBackgroundColor);