Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
|
yosin_UTC9
2017/02/09 01:21:33
Please keep original copyright comment since this
joone
2017/02/09 04:18:27
Done.
| |
| 2 * Copyright (C) 2007, 2008, 2009 Apple Computer, Inc. | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 * Copyright (C) 2010, 2011 Google Inc. All rights reserved. | 3 // found in the LICENSE file. |
| 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 | 4 |
| 27 #include "core/editing/EditingStyle.h" | 5 #include "EditingStyleUtilities.h" |
| 28 | 6 |
| 29 #include "bindings/core/v8/ExceptionState.h" | |
| 30 #include "core/HTMLNames.h" | |
| 31 #include "core/css/CSSColorValue.h" | 7 #include "core/css/CSSColorValue.h" |
| 32 #include "core/css/CSSComputedStyleDeclaration.h" | 8 #include "core/css/CSSComputedStyleDeclaration.h" |
| 33 #include "core/css/CSSIdentifierValue.h" | 9 #include "core/css/CSSIdentifierValue.h" |
| 34 #include "core/css/CSSPrimitiveValue.h" | |
| 35 #include "core/css/CSSPrimitiveValueMappings.h" | |
| 36 #include "core/css/CSSPropertyMetadata.h" | |
| 37 #include "core/css/CSSRuleList.h" | |
| 38 #include "core/css/CSSStyleRule.h" | |
| 39 #include "core/css/CSSValueList.h" | |
| 40 #include "core/css/FontSize.h" | |
| 41 #include "core/css/StylePropertySet.h" | 10 #include "core/css/StylePropertySet.h" |
| 42 #include "core/css/StyleRule.h" | |
| 43 #include "core/css/parser/CSSParser.h" | 11 #include "core/css/parser/CSSParser.h" |
| 44 #include "core/css/resolver/StyleResolver.h" | 12 #include "core/editing/EditingStyle.h" |
| 45 #include "core/dom/Document.h" | |
| 46 #include "core/dom/Element.h" | |
| 47 #include "core/dom/Node.h" | |
| 48 #include "core/dom/NodeComputedStyle.h" | |
| 49 #include "core/dom/NodeTraversal.h" | |
| 50 #include "core/dom/QualifiedName.h" | |
| 51 #include "core/editing/EditingUtilities.h" | 13 #include "core/editing/EditingUtilities.h" |
| 52 #include "core/editing/Editor.h" | |
| 53 #include "core/editing/FrameSelection.h" | |
| 54 #include "core/editing/Position.h" | |
| 55 #include "core/editing/commands/ApplyStyleCommand.h" | |
| 56 #include "core/editing/serializers/HTMLInterchange.h" | |
| 57 #include "core/frame/LocalFrame.h" | |
| 58 #include "core/html/HTMLFontElement.h" | |
| 59 #include "core/html/HTMLSpanElement.h" | |
| 60 #include "core/layout/LayoutBox.h" | |
| 61 #include "core/layout/LayoutObject.h" | |
| 62 #include "core/style/ComputedStyle.h" | |
| 63 #include "wtf/StdLibExtras.h" | |
| 64 | 14 |
| 65 namespace blink { | 15 namespace blink { |
| 66 | 16 |
| 67 static const CSSPropertyID& textDecorationPropertyForEditing() { | 17 bool EditingStyleUtilities::hasAncestorVerticalAlignStyle(Node& node, |
| 68 static const CSSPropertyID property = | 18 CSSValueID value) { |
| 69 RuntimeEnabledFeatures::css3TextDecorationsEnabled() | |
| 70 ? CSSPropertyTextDecorationLine | |
| 71 : CSSPropertyTextDecoration; | |
| 72 return property; | |
| 73 } | |
| 74 | |
| 75 // Editing style properties must be preserved during editing operation. | |
| 76 // e.g. when a user inserts a new paragraph, all properties listed here must be | |
| 77 // copied to the new paragraph. | |
| 78 // NOTE: Use either allEditingProperties() or inheritableEditingProperties() to | |
| 79 // respect runtime enabling of properties. | |
| 80 static const CSSPropertyID staticEditingProperties[] = { | |
| 81 CSSPropertyBackgroundColor, CSSPropertyColor, CSSPropertyFontFamily, | |
| 82 CSSPropertyFontSize, CSSPropertyFontStyle, CSSPropertyFontVariantLigatures, | |
| 83 CSSPropertyFontVariantCaps, CSSPropertyFontWeight, CSSPropertyLetterSpacing, | |
| 84 CSSPropertyOrphans, CSSPropertyTextAlign, | |
| 85 // FIXME: CSSPropertyTextDecoration needs to be removed when CSS3 Text | |
| 86 // Decoration feature is no longer experimental. | |
| 87 CSSPropertyTextDecoration, CSSPropertyTextDecorationLine, | |
| 88 CSSPropertyTextIndent, CSSPropertyTextTransform, CSSPropertyWhiteSpace, | |
| 89 CSSPropertyWidows, CSSPropertyWordSpacing, | |
| 90 CSSPropertyWebkitTextDecorationsInEffect, CSSPropertyWebkitTextFillColor, | |
| 91 CSSPropertyWebkitTextStrokeColor, CSSPropertyWebkitTextStrokeWidth, | |
| 92 CSSPropertyCaretColor}; | |
| 93 | |
| 94 enum EditingPropertiesType { | |
| 95 OnlyInheritableEditingProperties, | |
| 96 AllEditingProperties | |
| 97 }; | |
| 98 | |
| 99 static const Vector<CSSPropertyID>& allEditingProperties() { | |
| 100 DEFINE_STATIC_LOCAL(Vector<CSSPropertyID>, properties, ()); | |
| 101 if (properties.isEmpty()) { | |
| 102 CSSPropertyMetadata::filterEnabledCSSPropertiesIntoVector( | |
| 103 staticEditingProperties, WTF_ARRAY_LENGTH(staticEditingProperties), | |
| 104 properties); | |
| 105 if (RuntimeEnabledFeatures::css3TextDecorationsEnabled()) | |
| 106 properties.remove(properties.find(CSSPropertyTextDecoration)); | |
| 107 } | |
| 108 return properties; | |
| 109 } | |
| 110 | |
| 111 static const Vector<CSSPropertyID>& inheritableEditingProperties() { | |
| 112 DEFINE_STATIC_LOCAL(Vector<CSSPropertyID>, properties, ()); | |
| 113 if (properties.isEmpty()) { | |
| 114 CSSPropertyMetadata::filterEnabledCSSPropertiesIntoVector( | |
| 115 staticEditingProperties, WTF_ARRAY_LENGTH(staticEditingProperties), | |
| 116 properties); | |
| 117 for (size_t index = 0; index < properties.size();) { | |
| 118 if (!CSSPropertyMetadata::isInheritedProperty(properties[index])) { | |
| 119 properties.remove(index); | |
| 120 continue; | |
| 121 } | |
| 122 ++index; | |
| 123 } | |
| 124 } | |
| 125 return properties; | |
| 126 } | |
| 127 | |
| 128 template <class StyleDeclarationType> | |
| 129 static MutableStylePropertySet* copyEditingProperties( | |
| 130 StyleDeclarationType* style, | |
| 131 EditingPropertiesType type = OnlyInheritableEditingProperties) { | |
| 132 if (type == AllEditingProperties) | |
| 133 return style->copyPropertiesInSet(allEditingProperties()); | |
| 134 return style->copyPropertiesInSet(inheritableEditingProperties()); | |
| 135 } | |
| 136 | |
| 137 static inline bool isEditingProperty(int id) { | |
| 138 return allEditingProperties().contains(static_cast<CSSPropertyID>(id)); | |
| 139 } | |
| 140 | |
| 141 static MutableStylePropertySet* editingStyleFromComputedStyle( | |
| 142 CSSComputedStyleDeclaration* style, | |
| 143 EditingPropertiesType type = OnlyInheritableEditingProperties) { | |
| 144 if (!style) | |
| 145 return MutableStylePropertySet::create(HTMLQuirksMode); | |
| 146 return copyEditingProperties(style, type); | |
| 147 } | |
| 148 | |
| 149 static CSSComputedStyleDeclaration* ensureComputedStyle( | |
| 150 const Position& position) { | |
| 151 Element* elem = associatedElementOf(position); | |
| 152 if (!elem) | |
| 153 return nullptr; | |
| 154 return CSSComputedStyleDeclaration::create(elem); | |
| 155 } | |
| 156 | |
| 157 static MutableStylePropertySet* getPropertiesNotIn( | |
| 158 StylePropertySet* styleWithRedundantProperties, | |
| 159 CSSStyleDeclaration* baseStyle); | |
| 160 enum LegacyFontSizeMode { | |
| 161 AlwaysUseLegacyFontSize, | |
| 162 UseLegacyFontSizeOnlyIfPixelValuesMatch | |
| 163 }; | |
| 164 static int legacyFontSizeFromCSSValue(Document*, | |
| 165 const CSSValue*, | |
| 166 bool, | |
| 167 LegacyFontSizeMode); | |
| 168 static bool isTransparentColorValue(const CSSValue*); | |
| 169 static bool hasTransparentBackgroundColor(CSSStyleDeclaration*); | |
| 170 static bool hasTransparentBackgroundColor(StylePropertySet*); | |
| 171 static const CSSValue* backgroundColorValueInEffect(Node*); | |
| 172 static bool hasAncestorVerticalAlignStyle(Node&, CSSValueID); | |
| 173 | |
| 174 class HTMLElementEquivalent : public GarbageCollected<HTMLElementEquivalent> { | |
| 175 public: | |
| 176 static HTMLElementEquivalent* create(CSSPropertyID propertyID, | |
| 177 CSSValueID primitiveValue, | |
| 178 const HTMLQualifiedName& tagName) { | |
| 179 return new HTMLElementEquivalent(propertyID, primitiveValue, tagName); | |
| 180 } | |
| 181 | |
| 182 virtual bool matches(const Element* element) const { | |
| 183 return !m_tagName || element->hasTagName(*m_tagName); | |
| 184 } | |
| 185 virtual bool hasAttribute() const { return false; } | |
| 186 virtual bool propertyExistsInStyle(const StylePropertySet* style) const { | |
| 187 return style->getPropertyCSSValue(m_propertyID); | |
| 188 } | |
| 189 virtual bool valueIsPresentInStyle(HTMLElement*, StylePropertySet*) const; | |
| 190 virtual void addToStyle(Element*, EditingStyle*) const; | |
| 191 | |
| 192 DEFINE_INLINE_VIRTUAL_TRACE() { visitor->trace(m_identifierValue); } | |
| 193 | |
| 194 protected: | |
| 195 HTMLElementEquivalent(CSSPropertyID); | |
| 196 HTMLElementEquivalent(CSSPropertyID, const HTMLQualifiedName& tagName); | |
| 197 HTMLElementEquivalent(CSSPropertyID, | |
| 198 CSSValueID primitiveValue, | |
| 199 const HTMLQualifiedName& tagName); | |
| 200 const CSSPropertyID m_propertyID; | |
| 201 const Member<CSSIdentifierValue> m_identifierValue; | |
| 202 // We can store a pointer because HTML tag names are const global. | |
| 203 const HTMLQualifiedName* m_tagName; | |
| 204 }; | |
| 205 | |
| 206 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id) | |
| 207 : m_propertyID(id), m_tagName(0) {} | |
| 208 | |
| 209 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, | |
| 210 const HTMLQualifiedName& tagName) | |
| 211 : m_propertyID(id), m_tagName(&tagName) {} | |
| 212 | |
| 213 HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, | |
| 214 CSSValueID valueID, | |
| 215 const HTMLQualifiedName& tagName) | |
| 216 : m_propertyID(id), | |
| 217 m_identifierValue(CSSIdentifierValue::create(valueID)), | |
| 218 m_tagName(&tagName) { | |
| 219 DCHECK_NE(valueID, CSSValueInvalid); | |
| 220 } | |
| 221 | |
| 222 bool HTMLElementEquivalent::valueIsPresentInStyle( | |
| 223 HTMLElement* element, | |
| 224 StylePropertySet* style) const { | |
| 225 const CSSValue* value = style->getPropertyCSSValue(m_propertyID); | |
| 226 return matches(element) && value && value->isIdentifierValue() && | |
| 227 toCSSIdentifierValue(value)->getValueID() == | |
| 228 m_identifierValue->getValueID(); | |
| 229 } | |
| 230 | |
| 231 void HTMLElementEquivalent::addToStyle(Element*, EditingStyle* style) const { | |
| 232 style->setProperty(m_propertyID, m_identifierValue->cssText()); | |
| 233 } | |
| 234 | |
| 235 class HTMLTextDecorationEquivalent final : public HTMLElementEquivalent { | |
| 236 public: | |
| 237 static HTMLElementEquivalent* create(CSSValueID primitiveValue, | |
| 238 const HTMLQualifiedName& tagName) { | |
| 239 return new HTMLTextDecorationEquivalent(primitiveValue, tagName); | |
| 240 } | |
| 241 bool propertyExistsInStyle(const StylePropertySet*) const override; | |
| 242 bool valueIsPresentInStyle(HTMLElement*, StylePropertySet*) const override; | |
| 243 | |
| 244 DEFINE_INLINE_VIRTUAL_TRACE() { HTMLElementEquivalent::trace(visitor); } | |
| 245 | |
| 246 private: | |
| 247 HTMLTextDecorationEquivalent(CSSValueID primitiveValue, | |
| 248 const HTMLQualifiedName& tagName); | |
| 249 }; | |
| 250 | |
| 251 HTMLTextDecorationEquivalent::HTMLTextDecorationEquivalent( | |
| 252 CSSValueID primitiveValue, | |
| 253 const HTMLQualifiedName& tagName) | |
| 254 : HTMLElementEquivalent(textDecorationPropertyForEditing(), | |
| 255 primitiveValue, | |
| 256 tagName) | |
| 257 // m_propertyID is used in HTMLElementEquivalent::addToStyle | |
| 258 {} | |
| 259 | |
| 260 bool HTMLTextDecorationEquivalent::propertyExistsInStyle( | |
| 261 const StylePropertySet* style) const { | |
| 262 return style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect) || | |
| 263 style->getPropertyCSSValue(textDecorationPropertyForEditing()); | |
| 264 } | |
| 265 | |
| 266 bool HTMLTextDecorationEquivalent::valueIsPresentInStyle( | |
| 267 HTMLElement* element, | |
| 268 StylePropertySet* style) const { | |
| 269 const CSSValue* styleValue = | |
| 270 style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect); | |
| 271 if (!styleValue) | |
| 272 styleValue = style->getPropertyCSSValue(textDecorationPropertyForEditing()); | |
| 273 return matches(element) && styleValue && styleValue->isValueList() && | |
| 274 toCSSValueList(styleValue)->hasValue(*m_identifierValue); | |
| 275 } | |
| 276 | |
| 277 class HTMLAttributeEquivalent : public HTMLElementEquivalent { | |
| 278 public: | |
| 279 static HTMLAttributeEquivalent* create(CSSPropertyID propertyID, | |
| 280 const HTMLQualifiedName& tagName, | |
| 281 const QualifiedName& attrName) { | |
| 282 return new HTMLAttributeEquivalent(propertyID, tagName, attrName); | |
| 283 } | |
| 284 static HTMLAttributeEquivalent* create(CSSPropertyID propertyID, | |
| 285 const QualifiedName& attrName) { | |
| 286 return new HTMLAttributeEquivalent(propertyID, attrName); | |
| 287 } | |
| 288 | |
| 289 bool matches(const Element* element) const override { | |
| 290 return HTMLElementEquivalent::matches(element) && | |
| 291 element->hasAttribute(m_attrName); | |
| 292 } | |
| 293 bool hasAttribute() const override { return true; } | |
| 294 bool valueIsPresentInStyle(HTMLElement*, StylePropertySet*) const override; | |
| 295 void addToStyle(Element*, EditingStyle*) const override; | |
| 296 virtual const CSSValue* attributeValueAsCSSValue(Element*) const; | |
| 297 inline const QualifiedName& attributeName() const { return m_attrName; } | |
| 298 | |
| 299 DEFINE_INLINE_VIRTUAL_TRACE() { HTMLElementEquivalent::trace(visitor); } | |
| 300 | |
| 301 protected: | |
| 302 HTMLAttributeEquivalent(CSSPropertyID, | |
| 303 const HTMLQualifiedName& tagName, | |
| 304 const QualifiedName& attrName); | |
| 305 HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& attrName); | |
| 306 // We can store a reference because HTML attribute names are const global. | |
| 307 const QualifiedName& m_attrName; | |
| 308 }; | |
| 309 | |
| 310 HTMLAttributeEquivalent::HTMLAttributeEquivalent( | |
| 311 CSSPropertyID id, | |
| 312 const HTMLQualifiedName& tagName, | |
| 313 const QualifiedName& attrName) | |
| 314 : HTMLElementEquivalent(id, tagName), m_attrName(attrName) {} | |
| 315 | |
| 316 HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, | |
| 317 const QualifiedName& attrName) | |
| 318 : HTMLElementEquivalent(id), m_attrName(attrName) {} | |
| 319 | |
| 320 bool HTMLAttributeEquivalent::valueIsPresentInStyle( | |
| 321 HTMLElement* element, | |
| 322 StylePropertySet* style) const { | |
| 323 const CSSValue* value = attributeValueAsCSSValue(element); | |
| 324 const CSSValue* styleValue = style->getPropertyCSSValue(m_propertyID); | |
| 325 | |
| 326 return compareCSSValuePtr(value, styleValue); | |
| 327 } | |
| 328 | |
| 329 void HTMLAttributeEquivalent::addToStyle(Element* element, | |
| 330 EditingStyle* style) const { | |
| 331 if (const CSSValue* value = attributeValueAsCSSValue(element)) | |
| 332 style->setProperty(m_propertyID, value->cssText()); | |
| 333 } | |
| 334 | |
| 335 const CSSValue* HTMLAttributeEquivalent::attributeValueAsCSSValue( | |
| 336 Element* element) const { | |
| 337 DCHECK(element); | |
| 338 const AtomicString& value = element->getAttribute(m_attrName); | |
| 339 if (value.isNull()) | |
| 340 return nullptr; | |
| 341 | |
| 342 MutableStylePropertySet* dummyStyle = nullptr; | |
| 343 dummyStyle = MutableStylePropertySet::create(HTMLQuirksMode); | |
| 344 dummyStyle->setProperty(m_propertyID, value); | |
| 345 return dummyStyle->getPropertyCSSValue(m_propertyID); | |
| 346 } | |
| 347 | |
| 348 class HTMLFontSizeEquivalent final : public HTMLAttributeEquivalent { | |
| 349 public: | |
| 350 static HTMLFontSizeEquivalent* create() { | |
| 351 return new HTMLFontSizeEquivalent(); | |
| 352 } | |
| 353 const CSSValue* attributeValueAsCSSValue(Element*) const override; | |
| 354 | |
| 355 DEFINE_INLINE_VIRTUAL_TRACE() { HTMLAttributeEquivalent::trace(visitor); } | |
| 356 | |
| 357 private: | |
| 358 HTMLFontSizeEquivalent(); | |
| 359 }; | |
| 360 | |
| 361 HTMLFontSizeEquivalent::HTMLFontSizeEquivalent() | |
| 362 : HTMLAttributeEquivalent(CSSPropertyFontSize, | |
| 363 HTMLNames::fontTag, | |
| 364 HTMLNames::sizeAttr) {} | |
| 365 | |
| 366 const CSSValue* HTMLFontSizeEquivalent::attributeValueAsCSSValue( | |
| 367 Element* element) const { | |
| 368 DCHECK(element); | |
| 369 const AtomicString& value = element->getAttribute(m_attrName); | |
| 370 if (value.isNull()) | |
| 371 return nullptr; | |
| 372 CSSValueID size; | |
| 373 if (!HTMLFontElement::cssValueFromFontSizeNumber(value, size)) | |
| 374 return nullptr; | |
| 375 return CSSIdentifierValue::create(size); | |
| 376 } | |
| 377 | |
| 378 float EditingStyle::NoFontDelta = 0.0f; | |
| 379 | |
| 380 EditingStyle::EditingStyle(ContainerNode* node, | |
| 381 PropertiesToInclude propertiesToInclude) { | |
| 382 init(node, propertiesToInclude); | |
| 383 } | |
| 384 | |
| 385 EditingStyle::EditingStyle(const Position& position, | |
| 386 PropertiesToInclude propertiesToInclude) { | |
| 387 init(position.anchorNode(), propertiesToInclude); | |
| 388 } | |
| 389 | |
| 390 EditingStyle::EditingStyle(const StylePropertySet* style) | |
| 391 : m_mutableStyle(style ? style->mutableCopy() : nullptr) { | |
| 392 extractFontSizeDelta(); | |
| 393 } | |
| 394 | |
| 395 EditingStyle::EditingStyle(CSSPropertyID propertyID, const String& value) | |
| 396 : m_mutableStyle(nullptr) { | |
| 397 setProperty(propertyID, value); | |
| 398 m_isVerticalAlign = propertyID == CSSPropertyVerticalAlign && | |
| 399 (value == "sub" || value == "super"); | |
| 400 } | |
| 401 | |
| 402 static Color cssValueToColor(const CSSValue* colorValue) { | |
| 403 if (!colorValue || | |
| 404 (!colorValue->isColorValue() && !colorValue->isPrimitiveValue() && | |
| 405 !colorValue->isIdentifierValue())) | |
| 406 return Color::transparent; | |
| 407 | |
| 408 if (colorValue->isColorValue()) | |
| 409 return toCSSColorValue(colorValue)->value(); | |
| 410 | |
| 411 Color color = 0; | |
| 412 // FIXME: Why ignore the return value? | |
| 413 CSSParser::parseColor(color, colorValue->cssText()); | |
| 414 return color; | |
| 415 } | |
| 416 | |
| 417 static inline Color getFontColor(CSSStyleDeclaration* style) { | |
| 418 return cssValueToColor(style->getPropertyCSSValueInternal(CSSPropertyColor)); | |
| 419 } | |
| 420 | |
| 421 static inline Color getFontColor(StylePropertySet* style) { | |
| 422 return cssValueToColor(style->getPropertyCSSValue(CSSPropertyColor)); | |
| 423 } | |
| 424 | |
| 425 static inline Color getBackgroundColor(CSSStyleDeclaration* style) { | |
| 426 return cssValueToColor( | |
| 427 style->getPropertyCSSValueInternal(CSSPropertyBackgroundColor)); | |
| 428 } | |
| 429 | |
| 430 static inline Color getBackgroundColor(StylePropertySet* style) { | |
| 431 return cssValueToColor( | |
| 432 style->getPropertyCSSValue(CSSPropertyBackgroundColor)); | |
| 433 } | |
| 434 | |
| 435 static inline Color backgroundColorInEffect(Node* node) { | |
| 436 return cssValueToColor(backgroundColorValueInEffect(node)); | |
| 437 } | |
| 438 | |
| 439 static int textAlignResolvingStartAndEnd(int textAlign, int direction) { | |
| 440 switch (textAlign) { | |
| 441 case CSSValueCenter: | |
| 442 case CSSValueWebkitCenter: | |
| 443 return CSSValueCenter; | |
| 444 case CSSValueJustify: | |
| 445 return CSSValueJustify; | |
| 446 case CSSValueLeft: | |
| 447 case CSSValueWebkitLeft: | |
| 448 return CSSValueLeft; | |
| 449 case CSSValueRight: | |
| 450 case CSSValueWebkitRight: | |
| 451 return CSSValueRight; | |
| 452 case CSSValueStart: | |
| 453 return direction != CSSValueRtl ? CSSValueLeft : CSSValueRight; | |
| 454 case CSSValueEnd: | |
| 455 return direction == CSSValueRtl ? CSSValueRight : CSSValueLeft; | |
| 456 } | |
| 457 return CSSValueInvalid; | |
| 458 } | |
| 459 | |
| 460 template <typename T> | |
| 461 static int textAlignResolvingStartAndEnd(T* style) { | |
| 462 return textAlignResolvingStartAndEnd( | |
| 463 getIdentifierValue(style, CSSPropertyTextAlign), | |
| 464 getIdentifierValue(style, CSSPropertyDirection)); | |
| 465 } | |
| 466 | |
| 467 void EditingStyle::init(Node* node, PropertiesToInclude propertiesToInclude) { | |
| 468 if (isTabHTMLSpanElementTextNode(node)) | |
| 469 node = tabSpanElement(node)->parentNode(); | |
| 470 else if (isTabHTMLSpanElement(node)) | |
| 471 node = node->parentNode(); | |
| 472 | |
| 473 CSSComputedStyleDeclaration* computedStyleAtPosition = | |
| 474 CSSComputedStyleDeclaration::create(node); | |
| 475 m_mutableStyle = | |
| 476 propertiesToInclude == AllProperties && computedStyleAtPosition | |
| 477 ? computedStyleAtPosition->copyProperties() | |
| 478 : editingStyleFromComputedStyle(computedStyleAtPosition); | |
| 479 | |
| 480 if (propertiesToInclude == EditingPropertiesInEffect) { | |
| 481 if (const CSSValue* value = backgroundColorValueInEffect(node)) | |
| 482 m_mutableStyle->setProperty(CSSPropertyBackgroundColor, value->cssText()); | |
| 483 if (const CSSValue* value = computedStyleAtPosition->getPropertyCSSValue( | |
| 484 CSSPropertyWebkitTextDecorationsInEffect)) | |
| 485 m_mutableStyle->setProperty(CSSPropertyTextDecoration, value->cssText()); | |
| 486 } | |
| 487 | |
| 488 if (node && node->ensureComputedStyle()) { | |
| 489 const ComputedStyle* computedStyle = node->ensureComputedStyle(); | |
| 490 removeInheritedColorsIfNeeded(computedStyle); | |
| 491 replaceFontSizeByKeywordIfPossible(computedStyle, computedStyleAtPosition); | |
| 492 } | |
| 493 | |
| 494 m_isMonospaceFont = computedStyleAtPosition->isMonospaceFont(); | |
| 495 extractFontSizeDelta(); | |
| 496 } | |
| 497 | |
| 498 void EditingStyle::removeInheritedColorsIfNeeded( | |
| 499 const ComputedStyle* computedStyle) { | |
| 500 // If a node's text fill color is currentColor, then its children use | |
| 501 // their font-color as their text fill color (they don't | |
| 502 // inherit it). Likewise for stroke color. | |
| 503 // Similar thing happens for caret-color if it's auto or currentColor. | |
| 504 if (computedStyle->textFillColor().isCurrentColor()) | |
| 505 m_mutableStyle->removeProperty(CSSPropertyWebkitTextFillColor); | |
| 506 if (computedStyle->textStrokeColor().isCurrentColor()) | |
| 507 m_mutableStyle->removeProperty(CSSPropertyWebkitTextStrokeColor); | |
| 508 if (computedStyle->caretColor().isAutoColor() || | |
| 509 computedStyle->caretColor().isCurrentColor()) | |
| 510 m_mutableStyle->removeProperty(CSSPropertyCaretColor); | |
| 511 } | |
| 512 | |
| 513 void EditingStyle::setProperty(CSSPropertyID propertyID, | |
| 514 const String& value, | |
| 515 bool important) { | |
| 516 if (!m_mutableStyle) | |
| 517 m_mutableStyle = MutableStylePropertySet::create(HTMLQuirksMode); | |
| 518 | |
| 519 m_mutableStyle->setProperty(propertyID, value, important); | |
| 520 } | |
| 521 | |
| 522 void EditingStyle::replaceFontSizeByKeywordIfPossible( | |
| 523 const ComputedStyle* computedStyle, | |
| 524 CSSComputedStyleDeclaration* cssComputedStyle) { | |
| 525 DCHECK(computedStyle); | |
| 526 if (computedStyle->getFontDescription().keywordSize()) { | |
| 527 m_mutableStyle->setProperty( | |
| 528 CSSPropertyFontSize, | |
| 529 cssComputedStyle->getFontSizeCSSValuePreferringKeyword()->cssText()); | |
| 530 } | |
| 531 } | |
| 532 | |
| 533 void EditingStyle::extractFontSizeDelta() { | |
| 534 if (!m_mutableStyle) | |
| 535 return; | |
| 536 | |
| 537 if (m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize)) { | |
| 538 // Explicit font size overrides any delta. | |
| 539 m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta); | |
| 540 return; | |
| 541 } | |
| 542 | |
| 543 // Get the adjustment amount out of the style. | |
| 544 const CSSValue* value = | |
| 545 m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta); | |
| 546 if (!value || !value->isPrimitiveValue()) | |
| 547 return; | |
| 548 | |
| 549 const CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value); | |
| 550 | |
| 551 // Only PX handled now. If we handle more types in the future, perhaps | |
| 552 // a switch statement here would be more appropriate. | |
| 553 if (!primitiveValue->isPx()) | |
| 554 return; | |
| 555 | |
| 556 m_fontSizeDelta = primitiveValue->getFloatValue(); | |
| 557 m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta); | |
| 558 } | |
| 559 | |
| 560 bool EditingStyle::isEmpty() const { | |
| 561 return (!m_mutableStyle || m_mutableStyle->isEmpty()) && | |
| 562 m_fontSizeDelta == NoFontDelta; | |
| 563 } | |
| 564 | |
| 565 bool EditingStyle::textDirection(WritingDirection& writingDirection) const { | |
| 566 if (!m_mutableStyle) | |
| 567 return false; | |
| 568 | |
| 569 const CSSValue* unicodeBidi = | |
| 570 m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi); | |
| 571 if (!unicodeBidi || !unicodeBidi->isIdentifierValue()) | |
| 572 return false; | |
| 573 | |
| 574 CSSValueID unicodeBidiValue = toCSSIdentifierValue(unicodeBidi)->getValueID(); | |
| 575 if (isEmbedOrIsolate(unicodeBidiValue)) { | |
| 576 const CSSValue* direction = | |
| 577 m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection); | |
| 578 if (!direction || !direction->isIdentifierValue()) | |
| 579 return false; | |
| 580 | |
| 581 writingDirection = | |
| 582 toCSSIdentifierValue(direction)->getValueID() == CSSValueLtr | |
| 583 ? LeftToRightWritingDirection | |
| 584 : RightToLeftWritingDirection; | |
| 585 | |
| 586 return true; | |
| 587 } | |
| 588 | |
| 589 if (unicodeBidiValue == CSSValueNormal) { | |
| 590 writingDirection = NaturalWritingDirection; | |
| 591 return true; | |
| 592 } | |
| 593 | |
| 594 return false; | |
| 595 } | |
| 596 | |
| 597 void EditingStyle::overrideWithStyle(const StylePropertySet* style) { | |
| 598 if (!style || style->isEmpty()) | |
| 599 return; | |
| 600 if (!m_mutableStyle) | |
| 601 m_mutableStyle = MutableStylePropertySet::create(HTMLQuirksMode); | |
| 602 m_mutableStyle->mergeAndOverrideOnConflict(style); | |
| 603 extractFontSizeDelta(); | |
| 604 } | |
| 605 | |
| 606 void EditingStyle::clear() { | |
| 607 m_mutableStyle.clear(); | |
| 608 m_isMonospaceFont = false; | |
| 609 m_fontSizeDelta = NoFontDelta; | |
| 610 } | |
| 611 | |
| 612 EditingStyle* EditingStyle::copy() const { | |
| 613 EditingStyle* copy = EditingStyle::create(); | |
| 614 if (m_mutableStyle) | |
| 615 copy->m_mutableStyle = m_mutableStyle->mutableCopy(); | |
| 616 copy->m_isMonospaceFont = m_isMonospaceFont; | |
| 617 copy->m_fontSizeDelta = m_fontSizeDelta; | |
| 618 return copy; | |
| 619 } | |
| 620 | |
| 621 // This is the list of CSS properties that apply specially to block-level | |
| 622 // elements. | |
| 623 static const CSSPropertyID staticBlockProperties[] = { | |
| 624 CSSPropertyBreakAfter, | |
| 625 CSSPropertyBreakBefore, | |
| 626 CSSPropertyBreakInside, | |
| 627 CSSPropertyOrphans, | |
| 628 CSSPropertyOverflow, // This can be also be applied to replaced elements | |
| 629 CSSPropertyColumnCount, | |
| 630 CSSPropertyColumnGap, | |
| 631 CSSPropertyColumnRuleColor, | |
| 632 CSSPropertyColumnRuleStyle, | |
| 633 CSSPropertyColumnRuleWidth, | |
| 634 CSSPropertyWebkitColumnBreakBefore, | |
| 635 CSSPropertyWebkitColumnBreakAfter, | |
| 636 CSSPropertyWebkitColumnBreakInside, | |
| 637 CSSPropertyColumnWidth, | |
| 638 CSSPropertyPageBreakAfter, | |
| 639 CSSPropertyPageBreakBefore, | |
| 640 CSSPropertyPageBreakInside, | |
| 641 CSSPropertyTextAlign, | |
| 642 CSSPropertyTextAlignLast, | |
| 643 CSSPropertyTextIndent, | |
| 644 CSSPropertyTextJustify, | |
| 645 CSSPropertyWidows}; | |
| 646 | |
| 647 static const Vector<CSSPropertyID>& blockPropertiesVector() { | |
| 648 DEFINE_STATIC_LOCAL(Vector<CSSPropertyID>, properties, ()); | |
| 649 if (properties.isEmpty()) { | |
| 650 CSSPropertyMetadata::filterEnabledCSSPropertiesIntoVector( | |
| 651 staticBlockProperties, WTF_ARRAY_LENGTH(staticBlockProperties), | |
| 652 properties); | |
| 653 } | |
| 654 return properties; | |
| 655 } | |
| 656 | |
| 657 EditingStyle* EditingStyle::extractAndRemoveBlockProperties() { | |
| 658 EditingStyle* blockProperties = EditingStyle::create(); | |
| 659 if (!m_mutableStyle) | |
| 660 return blockProperties; | |
| 661 | |
| 662 blockProperties->m_mutableStyle = | |
| 663 m_mutableStyle->copyPropertiesInSet(blockPropertiesVector()); | |
| 664 removeBlockProperties(); | |
| 665 | |
| 666 return blockProperties; | |
| 667 } | |
| 668 | |
| 669 EditingStyle* EditingStyle::extractAndRemoveTextDirection() { | |
| 670 EditingStyle* textDirection = EditingStyle::create(); | |
| 671 textDirection->m_mutableStyle = | |
| 672 MutableStylePropertySet::create(HTMLQuirksMode); | |
| 673 textDirection->m_mutableStyle->setProperty( | |
| 674 CSSPropertyUnicodeBidi, CSSValueIsolate, | |
| 675 m_mutableStyle->propertyIsImportant(CSSPropertyUnicodeBidi)); | |
| 676 textDirection->m_mutableStyle->setProperty( | |
| 677 CSSPropertyDirection, | |
| 678 m_mutableStyle->getPropertyValue(CSSPropertyDirection), | |
| 679 m_mutableStyle->propertyIsImportant(CSSPropertyDirection)); | |
| 680 | |
| 681 m_mutableStyle->removeProperty(CSSPropertyUnicodeBidi); | |
| 682 m_mutableStyle->removeProperty(CSSPropertyDirection); | |
| 683 | |
| 684 return textDirection; | |
| 685 } | |
| 686 | |
| 687 void EditingStyle::removeBlockProperties() { | |
| 688 if (!m_mutableStyle) | |
| 689 return; | |
| 690 | |
| 691 m_mutableStyle->removePropertiesInSet(blockPropertiesVector().data(), | |
| 692 blockPropertiesVector().size()); | |
| 693 } | |
| 694 | |
| 695 void EditingStyle::removeStyleAddedByElement(Element* element) { | |
| 696 if (!element || !element->parentNode()) | |
| 697 return; | |
| 698 MutableStylePropertySet* parentStyle = editingStyleFromComputedStyle( | |
| 699 CSSComputedStyleDeclaration::create(element->parentNode()), | |
| 700 AllEditingProperties); | |
| 701 MutableStylePropertySet* nodeStyle = editingStyleFromComputedStyle( | |
| 702 CSSComputedStyleDeclaration::create(element), AllEditingProperties); | |
| 703 nodeStyle->removeEquivalentProperties(parentStyle); | |
| 704 m_mutableStyle->removeEquivalentProperties(nodeStyle); | |
| 705 } | |
| 706 | |
| 707 void EditingStyle::removeStyleConflictingWithStyleOfElement(Element* element) { | |
| 708 if (!element || !element->parentNode() || !m_mutableStyle) | |
| 709 return; | |
| 710 | |
| 711 MutableStylePropertySet* parentStyle = editingStyleFromComputedStyle( | |
| 712 CSSComputedStyleDeclaration::create(element->parentNode()), | |
| 713 AllEditingProperties); | |
| 714 MutableStylePropertySet* nodeStyle = editingStyleFromComputedStyle( | |
| 715 CSSComputedStyleDeclaration::create(element), AllEditingProperties); | |
| 716 nodeStyle->removeEquivalentProperties(parentStyle); | |
| 717 | |
| 718 unsigned propertyCount = nodeStyle->propertyCount(); | |
| 719 for (unsigned i = 0; i < propertyCount; ++i) | |
| 720 m_mutableStyle->removeProperty(nodeStyle->propertyAt(i).id()); | |
| 721 } | |
| 722 | |
| 723 void EditingStyle::collapseTextDecorationProperties() { | |
| 724 if (!m_mutableStyle) | |
| 725 return; | |
| 726 | |
| 727 const CSSValue* textDecorationsInEffect = m_mutableStyle->getPropertyCSSValue( | |
| 728 CSSPropertyWebkitTextDecorationsInEffect); | |
| 729 if (!textDecorationsInEffect) | |
| 730 return; | |
| 731 | |
| 732 if (textDecorationsInEffect->isValueList()) { | |
| 733 m_mutableStyle->setProperty(textDecorationPropertyForEditing(), | |
| 734 textDecorationsInEffect->cssText(), | |
| 735 m_mutableStyle->propertyIsImportant( | |
| 736 textDecorationPropertyForEditing())); | |
| 737 } else { | |
| 738 m_mutableStyle->removeProperty(textDecorationPropertyForEditing()); | |
| 739 } | |
| 740 m_mutableStyle->removeProperty(CSSPropertyWebkitTextDecorationsInEffect); | |
| 741 } | |
| 742 | |
| 743 // CSS properties that create a visual difference only when applied to text. | |
| 744 static const CSSPropertyID textOnlyProperties[] = { | |
| 745 // FIXME: CSSPropertyTextDecoration needs to be removed when CSS3 Text | |
| 746 // Decoration feature is no longer experimental. | |
| 747 CSSPropertyTextDecoration, | |
| 748 CSSPropertyTextDecorationLine, | |
| 749 CSSPropertyWebkitTextDecorationsInEffect, | |
| 750 CSSPropertyFontStyle, | |
| 751 CSSPropertyFontWeight, | |
| 752 CSSPropertyColor, | |
| 753 }; | |
| 754 | |
| 755 TriState EditingStyle::triStateOfStyle(EditingStyle* style) const { | |
| 756 if (!style || !style->m_mutableStyle) | |
| 757 return FalseTriState; | |
| 758 return triStateOfStyle(style->m_mutableStyle->ensureCSSStyleDeclaration(), | |
| 759 DoNotIgnoreTextOnlyProperties); | |
| 760 } | |
| 761 | |
| 762 TriState EditingStyle::triStateOfStyle( | |
| 763 CSSStyleDeclaration* styleToCompare, | |
| 764 ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const { | |
| 765 MutableStylePropertySet* difference = | |
| 766 getPropertiesNotIn(m_mutableStyle.get(), styleToCompare); | |
| 767 | |
| 768 if (shouldIgnoreTextOnlyProperties == IgnoreTextOnlyProperties) { | |
| 769 difference->removePropertiesInSet(textOnlyProperties, | |
| 770 WTF_ARRAY_LENGTH(textOnlyProperties)); | |
| 771 } | |
| 772 | |
| 773 if (difference->isEmpty()) | |
| 774 return TrueTriState; | |
| 775 if (difference->propertyCount() == m_mutableStyle->propertyCount()) | |
| 776 return FalseTriState; | |
| 777 | |
| 778 return MixedTriState; | |
| 779 } | |
| 780 | |
| 781 static bool hasAncestorVerticalAlignStyle(Node& node, CSSValueID value) { | |
| 782 for (Node& runner : NodeTraversal::inclusiveAncestorsOf(node)) { | 19 for (Node& runner : NodeTraversal::inclusiveAncestorsOf(node)) { |
| 783 CSSComputedStyleDeclaration* ancestorStyle = | 20 CSSComputedStyleDeclaration* ancestorStyle = |
| 784 CSSComputedStyleDeclaration::create(&runner); | 21 CSSComputedStyleDeclaration::create(&runner); |
| 785 if (getIdentifierValue(ancestorStyle, CSSPropertyVerticalAlign) == value) | 22 if (getIdentifierValue(ancestorStyle, CSSPropertyVerticalAlign) == value) |
| 786 return true; | 23 return true; |
| 787 } | 24 } |
| 788 return false; | 25 return false; |
| 789 } | 26 } |
| 790 | 27 |
| 791 TriState EditingStyle::triStateOfStyle( | 28 EditingStyle* EditingStyleUtilities::wrappingStyleForAnnotatedSerialization( |
| 792 const VisibleSelection& selection) const { | |
| 793 if (selection.isNone()) | |
| 794 return FalseTriState; | |
| 795 | |
| 796 if (selection.isCaret()) | |
| 797 return triStateOfStyle(EditingStyle::styleAtSelectionStart(selection)); | |
| 798 | |
| 799 TriState state = FalseTriState; | |
| 800 bool nodeIsStart = true; | |
| 801 for (Node& node : NodeTraversal::startsAt(*selection.start().anchorNode())) { | |
| 802 if (node.layoutObject() && hasEditableStyle(node)) { | |
| 803 CSSComputedStyleDeclaration* nodeStyle = | |
| 804 CSSComputedStyleDeclaration::create(&node); | |
| 805 if (nodeStyle) { | |
| 806 // If the selected element has <sub> or <sup> ancestor element, apply | |
| 807 // the corresponding style(vertical-align) to it so that | |
| 808 // document.queryCommandState() works with the style. See bug | |
| 809 // http://crbug.com/582225. | |
| 810 if (m_isVerticalAlign && | |
| 811 getIdentifierValue(nodeStyle, CSSPropertyVerticalAlign) == | |
| 812 CSSValueBaseline) { | |
| 813 const CSSIdentifierValue* verticalAlign = toCSSIdentifierValue( | |
| 814 m_mutableStyle->getPropertyCSSValue(CSSPropertyVerticalAlign)); | |
| 815 if (hasAncestorVerticalAlignStyle(node, | |
| 816 verticalAlign->getValueID())) { | |
| 817 node.mutableComputedStyle()->setVerticalAlign( | |
| 818 verticalAlign->convertTo<EVerticalAlign>()); | |
| 819 } | |
| 820 } | |
| 821 | |
| 822 // Pass EditingStyle::DoNotIgnoreTextOnlyProperties without checking if | |
| 823 // node.isTextNode() because the node can be an element node. See bug | |
| 824 // http://crbug.com/584939. | |
| 825 TriState nodeState = triStateOfStyle( | |
| 826 nodeStyle, EditingStyle::DoNotIgnoreTextOnlyProperties); | |
| 827 if (nodeIsStart) { | |
| 828 state = nodeState; | |
| 829 nodeIsStart = false; | |
| 830 } else if (state != nodeState && node.isTextNode()) { | |
| 831 state = MixedTriState; | |
| 832 break; | |
| 833 } | |
| 834 } | |
| 835 } | |
| 836 if (&node == selection.end().anchorNode()) | |
| 837 break; | |
| 838 } | |
| 839 | |
| 840 return state; | |
| 841 } | |
| 842 | |
| 843 bool EditingStyle::conflictsWithInlineStyleOfElement( | |
| 844 HTMLElement* element, | |
| 845 EditingStyle* extractedStyle, | |
| 846 Vector<CSSPropertyID>* conflictingProperties) const { | |
| 847 DCHECK(element); | |
| 848 DCHECK(!conflictingProperties || conflictingProperties->isEmpty()); | |
| 849 | |
| 850 const StylePropertySet* inlineStyle = element->inlineStyle(); | |
| 851 if (!m_mutableStyle || !inlineStyle) | |
| 852 return false; | |
| 853 | |
| 854 unsigned propertyCount = m_mutableStyle->propertyCount(); | |
| 855 for (unsigned i = 0; i < propertyCount; ++i) { | |
| 856 CSSPropertyID propertyID = m_mutableStyle->propertyAt(i).id(); | |
| 857 | |
| 858 // We don't override whitespace property of a tab span because that would | |
| 859 // collapse the tab into a space. | |
| 860 if (propertyID == CSSPropertyWhiteSpace && isTabHTMLSpanElement(element)) | |
| 861 continue; | |
| 862 | |
| 863 if (propertyID == CSSPropertyWebkitTextDecorationsInEffect && | |
| 864 inlineStyle->getPropertyCSSValue(textDecorationPropertyForEditing())) { | |
| 865 if (!conflictingProperties) | |
| 866 return true; | |
| 867 conflictingProperties->push_back(CSSPropertyTextDecoration); | |
| 868 // Because text-decoration expands to text-decoration-line when CSS3 | |
| 869 // Text Decoration is enabled, we also state it as conflicting. | |
| 870 if (RuntimeEnabledFeatures::css3TextDecorationsEnabled()) | |
| 871 conflictingProperties->push_back(CSSPropertyTextDecorationLine); | |
| 872 if (extractedStyle) { | |
| 873 extractedStyle->setProperty( | |
| 874 textDecorationPropertyForEditing(), | |
| 875 inlineStyle->getPropertyValue(textDecorationPropertyForEditing()), | |
| 876 inlineStyle->propertyIsImportant( | |
| 877 textDecorationPropertyForEditing())); | |
| 878 } | |
| 879 continue; | |
| 880 } | |
| 881 | |
| 882 if (!inlineStyle->getPropertyCSSValue(propertyID)) | |
| 883 continue; | |
| 884 | |
| 885 if (propertyID == CSSPropertyUnicodeBidi && | |
| 886 inlineStyle->getPropertyCSSValue(CSSPropertyDirection)) { | |
| 887 if (!conflictingProperties) | |
| 888 return true; | |
| 889 conflictingProperties->push_back(CSSPropertyDirection); | |
| 890 if (extractedStyle) { | |
| 891 extractedStyle->setProperty( | |
| 892 propertyID, inlineStyle->getPropertyValue(propertyID), | |
| 893 inlineStyle->propertyIsImportant(propertyID)); | |
| 894 } | |
| 895 } | |
| 896 | |
| 897 if (!conflictingProperties) | |
| 898 return true; | |
| 899 | |
| 900 conflictingProperties->push_back(propertyID); | |
| 901 | |
| 902 if (extractedStyle) { | |
| 903 extractedStyle->setProperty(propertyID, | |
| 904 inlineStyle->getPropertyValue(propertyID), | |
| 905 inlineStyle->propertyIsImportant(propertyID)); | |
| 906 } | |
| 907 } | |
| 908 | |
| 909 return conflictingProperties && !conflictingProperties->isEmpty(); | |
| 910 } | |
| 911 | |
| 912 static const HeapVector<Member<HTMLElementEquivalent>>& | |
| 913 htmlElementEquivalents() { | |
| 914 DEFINE_STATIC_LOCAL(HeapVector<Member<HTMLElementEquivalent>>, | |
| 915 HTMLElementEquivalents, | |
| 916 (new HeapVector<Member<HTMLElementEquivalent>>)); | |
| 917 if (!HTMLElementEquivalents.size()) { | |
| 918 HTMLElementEquivalents.push_back(HTMLElementEquivalent::create( | |
| 919 CSSPropertyFontWeight, CSSValueBold, HTMLNames::bTag)); | |
| 920 HTMLElementEquivalents.push_back(HTMLElementEquivalent::create( | |
| 921 CSSPropertyFontWeight, CSSValueBold, HTMLNames::strongTag)); | |
| 922 HTMLElementEquivalents.push_back(HTMLElementEquivalent::create( | |
| 923 CSSPropertyVerticalAlign, CSSValueSub, HTMLNames::subTag)); | |
| 924 HTMLElementEquivalents.push_back(HTMLElementEquivalent::create( | |
| 925 CSSPropertyVerticalAlign, CSSValueSuper, HTMLNames::supTag)); | |
| 926 HTMLElementEquivalents.push_back(HTMLElementEquivalent::create( | |
| 927 CSSPropertyFontStyle, CSSValueItalic, HTMLNames::iTag)); | |
| 928 HTMLElementEquivalents.push_back(HTMLElementEquivalent::create( | |
| 929 CSSPropertyFontStyle, CSSValueItalic, HTMLNames::emTag)); | |
| 930 | |
| 931 HTMLElementEquivalents.push_back(HTMLTextDecorationEquivalent::create( | |
| 932 CSSValueUnderline, HTMLNames::uTag)); | |
| 933 HTMLElementEquivalents.push_back(HTMLTextDecorationEquivalent::create( | |
| 934 CSSValueLineThrough, HTMLNames::sTag)); | |
| 935 HTMLElementEquivalents.push_back(HTMLTextDecorationEquivalent::create( | |
| 936 CSSValueLineThrough, HTMLNames::strikeTag)); | |
| 937 } | |
| 938 | |
| 939 return HTMLElementEquivalents; | |
| 940 } | |
| 941 | |
| 942 bool EditingStyle::conflictsWithImplicitStyleOfElement( | |
| 943 HTMLElement* element, | |
| 944 EditingStyle* extractedStyle, | |
| 945 ShouldExtractMatchingStyle shouldExtractMatchingStyle) const { | |
| 946 if (!m_mutableStyle) | |
| 947 return false; | |
| 948 | |
| 949 const HeapVector<Member<HTMLElementEquivalent>>& HTMLElementEquivalents = | |
| 950 htmlElementEquivalents(); | |
| 951 for (size_t i = 0; i < HTMLElementEquivalents.size(); ++i) { | |
| 952 const HTMLElementEquivalent* equivalent = HTMLElementEquivalents[i].get(); | |
| 953 if (equivalent->matches(element) && | |
| 954 equivalent->propertyExistsInStyle(m_mutableStyle.get()) && | |
| 955 (shouldExtractMatchingStyle == ExtractMatchingStyle || | |
| 956 !equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) { | |
| 957 if (extractedStyle) | |
| 958 equivalent->addToStyle(element, extractedStyle); | |
| 959 return true; | |
| 960 } | |
| 961 } | |
| 962 return false; | |
| 963 } | |
| 964 | |
| 965 static const HeapVector<Member<HTMLAttributeEquivalent>>& | |
| 966 htmlAttributeEquivalents() { | |
| 967 DEFINE_STATIC_LOCAL(HeapVector<Member<HTMLAttributeEquivalent>>, | |
| 968 HTMLAttributeEquivalents, | |
| 969 (new HeapVector<Member<HTMLAttributeEquivalent>>)); | |
| 970 if (!HTMLAttributeEquivalents.size()) { | |
| 971 // elementIsStyledSpanOrHTMLEquivalent depends on the fact each | |
| 972 // HTMLAttriuteEquivalent matches exactly one attribute of exactly one | |
| 973 // element except dirAttr. | |
| 974 HTMLAttributeEquivalents.push_back(HTMLAttributeEquivalent::create( | |
| 975 CSSPropertyColor, HTMLNames::fontTag, HTMLNames::colorAttr)); | |
| 976 HTMLAttributeEquivalents.push_back(HTMLAttributeEquivalent::create( | |
| 977 CSSPropertyFontFamily, HTMLNames::fontTag, HTMLNames::faceAttr)); | |
| 978 HTMLAttributeEquivalents.push_back(HTMLFontSizeEquivalent::create()); | |
| 979 | |
| 980 HTMLAttributeEquivalents.push_back(HTMLAttributeEquivalent::create( | |
| 981 CSSPropertyDirection, HTMLNames::dirAttr)); | |
| 982 HTMLAttributeEquivalents.push_back(HTMLAttributeEquivalent::create( | |
| 983 CSSPropertyUnicodeBidi, HTMLNames::dirAttr)); | |
| 984 } | |
| 985 | |
| 986 return HTMLAttributeEquivalents; | |
| 987 } | |
| 988 | |
| 989 bool EditingStyle::conflictsWithImplicitStyleOfAttributes( | |
| 990 HTMLElement* element) const { | |
| 991 DCHECK(element); | |
| 992 if (!m_mutableStyle) | |
| 993 return false; | |
| 994 | |
| 995 const HeapVector<Member<HTMLAttributeEquivalent>>& HTMLAttributeEquivalents = | |
| 996 htmlAttributeEquivalents(); | |
| 997 for (const auto& equivalent : HTMLAttributeEquivalents) { | |
| 998 if (equivalent->matches(element) && | |
| 999 equivalent->propertyExistsInStyle(m_mutableStyle.get()) && | |
| 1000 !equivalent->valueIsPresentInStyle(element, m_mutableStyle.get())) | |
| 1001 return true; | |
| 1002 } | |
| 1003 | |
| 1004 return false; | |
| 1005 } | |
| 1006 | |
| 1007 bool EditingStyle::extractConflictingImplicitStyleOfAttributes( | |
| 1008 HTMLElement* element, | |
| 1009 ShouldPreserveWritingDirection shouldPreserveWritingDirection, | |
| 1010 EditingStyle* extractedStyle, | |
| 1011 Vector<QualifiedName>& conflictingAttributes, | |
| 1012 ShouldExtractMatchingStyle shouldExtractMatchingStyle) const { | |
| 1013 DCHECK(element); | |
| 1014 // HTMLAttributeEquivalent::addToStyle doesn't support unicode-bidi and | |
| 1015 // direction properties | |
| 1016 if (extractedStyle) | |
| 1017 DCHECK_EQ(shouldPreserveWritingDirection, PreserveWritingDirection); | |
| 1018 if (!m_mutableStyle) | |
| 1019 return false; | |
| 1020 | |
| 1021 const HeapVector<Member<HTMLAttributeEquivalent>>& HTMLAttributeEquivalents = | |
| 1022 htmlAttributeEquivalents(); | |
| 1023 bool removed = false; | |
| 1024 for (const auto& attribute : HTMLAttributeEquivalents) { | |
| 1025 const HTMLAttributeEquivalent* equivalent = attribute.get(); | |
| 1026 | |
| 1027 // unicode-bidi and direction are pushed down separately so don't push down | |
| 1028 // with other styles. | |
| 1029 if (shouldPreserveWritingDirection == PreserveWritingDirection && | |
| 1030 equivalent->attributeName() == HTMLNames::dirAttr) | |
| 1031 continue; | |
| 1032 | |
| 1033 if (!equivalent->matches(element) || | |
| 1034 !equivalent->propertyExistsInStyle(m_mutableStyle.get()) || | |
| 1035 (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && | |
| 1036 equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) | |
| 1037 continue; | |
| 1038 | |
| 1039 if (extractedStyle) | |
| 1040 equivalent->addToStyle(element, extractedStyle); | |
| 1041 conflictingAttributes.push_back(equivalent->attributeName()); | |
| 1042 removed = true; | |
| 1043 } | |
| 1044 | |
| 1045 return removed; | |
| 1046 } | |
| 1047 | |
| 1048 bool EditingStyle::styleIsPresentInComputedStyleOfNode(Node* node) const { | |
| 1049 return !m_mutableStyle || | |
| 1050 getPropertiesNotIn(m_mutableStyle.get(), | |
| 1051 CSSComputedStyleDeclaration::create(node)) | |
| 1052 ->isEmpty(); | |
| 1053 } | |
| 1054 | |
| 1055 bool EditingStyle::elementIsStyledSpanOrHTMLEquivalent( | |
| 1056 const HTMLElement* element) { | |
| 1057 DCHECK(element); | |
| 1058 bool elementIsSpanOrElementEquivalent = false; | |
| 1059 if (isHTMLSpanElement(*element)) { | |
| 1060 elementIsSpanOrElementEquivalent = true; | |
| 1061 } else { | |
| 1062 const HeapVector<Member<HTMLElementEquivalent>>& HTMLElementEquivalents = | |
| 1063 htmlElementEquivalents(); | |
| 1064 size_t i; | |
| 1065 for (i = 0; i < HTMLElementEquivalents.size(); ++i) { | |
| 1066 if (HTMLElementEquivalents[i]->matches(element)) { | |
| 1067 elementIsSpanOrElementEquivalent = true; | |
| 1068 break; | |
| 1069 } | |
| 1070 } | |
| 1071 } | |
| 1072 | |
| 1073 AttributeCollection attributes = element->attributes(); | |
| 1074 if (attributes.isEmpty()) { | |
| 1075 // span, b, etc... without any attributes | |
| 1076 return elementIsSpanOrElementEquivalent; | |
| 1077 } | |
| 1078 | |
| 1079 unsigned matchedAttributes = 0; | |
| 1080 const HeapVector<Member<HTMLAttributeEquivalent>>& HTMLAttributeEquivalents = | |
| 1081 htmlAttributeEquivalents(); | |
| 1082 for (const auto& equivalent : HTMLAttributeEquivalents) { | |
| 1083 if (equivalent->matches(element) && | |
| 1084 equivalent->attributeName() != HTMLNames::dirAttr) | |
| 1085 matchedAttributes++; | |
| 1086 } | |
| 1087 | |
| 1088 if (!elementIsSpanOrElementEquivalent && !matchedAttributes) { | |
| 1089 // element is not a span, a html element equivalent, or font element. | |
| 1090 return false; | |
| 1091 } | |
| 1092 | |
| 1093 if (element->getAttribute(HTMLNames::classAttr) == AppleStyleSpanClass) | |
| 1094 matchedAttributes++; | |
| 1095 | |
| 1096 if (element->hasAttribute(HTMLNames::styleAttr)) { | |
| 1097 if (const StylePropertySet* style = element->inlineStyle()) { | |
| 1098 unsigned propertyCount = style->propertyCount(); | |
| 1099 for (unsigned i = 0; i < propertyCount; ++i) { | |
| 1100 if (!isEditingProperty(style->propertyAt(i).id())) | |
| 1101 return false; | |
| 1102 } | |
| 1103 } | |
| 1104 matchedAttributes++; | |
| 1105 } | |
| 1106 | |
| 1107 // font with color attribute, span with style attribute, etc... | |
| 1108 DCHECK_LE(matchedAttributes, attributes.size()); | |
| 1109 return matchedAttributes >= attributes.size(); | |
| 1110 } | |
| 1111 | |
| 1112 void EditingStyle::prepareToApplyAt( | |
| 1113 const Position& position, | |
| 1114 ShouldPreserveWritingDirection shouldPreserveWritingDirection) { | |
| 1115 if (!m_mutableStyle) | |
| 1116 return; | |
| 1117 | |
| 1118 // ReplaceSelectionCommand::handleStyleSpans() requires that this function | |
| 1119 // only removes the editing style. If this function was modified in the future | |
| 1120 // to delete all redundant properties, then add a boolean value to indicate | |
| 1121 // which one of editingStyleAtPosition or computedStyle is called. | |
| 1122 EditingStyle* editingStyleAtPosition = | |
| 1123 EditingStyle::create(position, EditingPropertiesInEffect); | |
| 1124 StylePropertySet* styleAtPosition = | |
| 1125 editingStyleAtPosition->m_mutableStyle.get(); | |
| 1126 | |
| 1127 const CSSValue* unicodeBidi = nullptr; | |
| 1128 const CSSValue* direction = nullptr; | |
| 1129 if (shouldPreserveWritingDirection == PreserveWritingDirection) { | |
| 1130 unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi); | |
| 1131 direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection); | |
| 1132 } | |
| 1133 | |
| 1134 m_mutableStyle->removeEquivalentProperties(styleAtPosition); | |
| 1135 | |
| 1136 if (textAlignResolvingStartAndEnd(m_mutableStyle.get()) == | |
| 1137 textAlignResolvingStartAndEnd(styleAtPosition)) | |
| 1138 m_mutableStyle->removeProperty(CSSPropertyTextAlign); | |
| 1139 | |
| 1140 if (getFontColor(m_mutableStyle.get()) == getFontColor(styleAtPosition)) | |
| 1141 m_mutableStyle->removeProperty(CSSPropertyColor); | |
| 1142 | |
| 1143 if (hasTransparentBackgroundColor(m_mutableStyle.get()) || | |
| 1144 cssValueToColor( | |
| 1145 m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor)) == | |
| 1146 backgroundColorInEffect(position.computeContainerNode())) | |
| 1147 m_mutableStyle->removeProperty(CSSPropertyBackgroundColor); | |
| 1148 | |
| 1149 if (unicodeBidi && unicodeBidi->isIdentifierValue()) { | |
| 1150 m_mutableStyle->setProperty( | |
| 1151 CSSPropertyUnicodeBidi, | |
| 1152 toCSSIdentifierValue(unicodeBidi)->getValueID()); | |
| 1153 if (direction && direction->isIdentifierValue()) { | |
| 1154 m_mutableStyle->setProperty( | |
| 1155 CSSPropertyDirection, toCSSIdentifierValue(direction)->getValueID()); | |
| 1156 } | |
| 1157 } | |
| 1158 } | |
| 1159 | |
| 1160 void EditingStyle::mergeTypingStyle(Document* document) { | |
| 1161 DCHECK(document); | |
| 1162 | |
| 1163 EditingStyle* typingStyle = document->frame()->selection().typingStyle(); | |
| 1164 if (!typingStyle || typingStyle == this) | |
| 1165 return; | |
| 1166 | |
| 1167 mergeStyle(typingStyle->style(), OverrideValues); | |
| 1168 } | |
| 1169 | |
| 1170 void EditingStyle::mergeInlineStyleOfElement( | |
| 1171 HTMLElement* element, | |
| 1172 CSSPropertyOverrideMode mode, | |
| 1173 PropertiesToInclude propertiesToInclude) { | |
| 1174 DCHECK(element); | |
| 1175 if (!element->inlineStyle()) | |
| 1176 return; | |
| 1177 | |
| 1178 switch (propertiesToInclude) { | |
| 1179 case AllProperties: | |
| 1180 mergeStyle(element->inlineStyle(), mode); | |
| 1181 return; | |
| 1182 case OnlyEditingInheritableProperties: | |
| 1183 mergeStyle(copyEditingProperties(element->inlineStyle(), | |
| 1184 OnlyInheritableEditingProperties), | |
| 1185 mode); | |
| 1186 return; | |
| 1187 case EditingPropertiesInEffect: | |
| 1188 mergeStyle( | |
| 1189 copyEditingProperties(element->inlineStyle(), AllEditingProperties), | |
| 1190 mode); | |
| 1191 return; | |
| 1192 } | |
| 1193 } | |
| 1194 | |
| 1195 static inline bool elementMatchesAndPropertyIsNotInInlineStyleDecl( | |
| 1196 const HTMLElementEquivalent* equivalent, | |
| 1197 const Element* element, | |
| 1198 EditingStyle::CSSPropertyOverrideMode mode, | |
| 1199 StylePropertySet* style) { | |
| 1200 return equivalent->matches(element) && | |
| 1201 (!element->inlineStyle() || | |
| 1202 !equivalent->propertyExistsInStyle(element->inlineStyle())) && | |
| 1203 (mode == EditingStyle::OverrideValues || | |
| 1204 !equivalent->propertyExistsInStyle(style)); | |
| 1205 } | |
| 1206 | |
| 1207 static MutableStylePropertySet* extractEditingProperties( | |
| 1208 const StylePropertySet* style, | |
| 1209 EditingStyle::PropertiesToInclude propertiesToInclude) { | |
| 1210 if (!style) | |
| 1211 return nullptr; | |
| 1212 | |
| 1213 switch (propertiesToInclude) { | |
| 1214 case EditingStyle::AllProperties: | |
| 1215 case EditingStyle::EditingPropertiesInEffect: | |
| 1216 return copyEditingProperties(style, AllEditingProperties); | |
| 1217 case EditingStyle::OnlyEditingInheritableProperties: | |
| 1218 return copyEditingProperties(style, OnlyInheritableEditingProperties); | |
| 1219 } | |
| 1220 | |
| 1221 NOTREACHED(); | |
| 1222 return nullptr; | |
| 1223 } | |
| 1224 | |
| 1225 void EditingStyle::mergeInlineAndImplicitStyleOfElement( | |
| 1226 Element* element, | |
| 1227 CSSPropertyOverrideMode mode, | |
| 1228 PropertiesToInclude propertiesToInclude) { | |
| 1229 EditingStyle* styleFromRules = EditingStyle::create(); | |
| 1230 styleFromRules->mergeStyleFromRulesForSerialization(element); | |
| 1231 | |
| 1232 if (element->inlineStyle()) { | |
| 1233 styleFromRules->m_mutableStyle->mergeAndOverrideOnConflict( | |
| 1234 element->inlineStyle()); | |
| 1235 } | |
| 1236 | |
| 1237 styleFromRules->m_mutableStyle = extractEditingProperties( | |
| 1238 styleFromRules->m_mutableStyle.get(), propertiesToInclude); | |
| 1239 mergeStyle(styleFromRules->m_mutableStyle.get(), mode); | |
| 1240 | |
| 1241 const HeapVector<Member<HTMLElementEquivalent>>& elementEquivalents = | |
| 1242 htmlElementEquivalents(); | |
| 1243 for (const auto& equivalent : elementEquivalents) { | |
| 1244 if (elementMatchesAndPropertyIsNotInInlineStyleDecl( | |
| 1245 equivalent.get(), element, mode, m_mutableStyle.get())) | |
| 1246 equivalent->addToStyle(element, this); | |
| 1247 } | |
| 1248 | |
| 1249 const HeapVector<Member<HTMLAttributeEquivalent>>& attributeEquivalents = | |
| 1250 htmlAttributeEquivalents(); | |
| 1251 for (const auto& attribute : attributeEquivalents) { | |
| 1252 if (attribute->attributeName() == HTMLNames::dirAttr) | |
| 1253 continue; // We don't want to include directionality | |
| 1254 if (elementMatchesAndPropertyIsNotInInlineStyleDecl( | |
| 1255 attribute.get(), element, mode, m_mutableStyle.get())) | |
| 1256 attribute->addToStyle(element, this); | |
| 1257 } | |
| 1258 } | |
| 1259 | |
| 1260 EditingStyle* EditingStyle::wrappingStyleForAnnotatedSerialization( | |
| 1261 ContainerNode* context) { | 29 ContainerNode* context) { |
| 1262 EditingStyle* wrappingStyle = | 30 EditingStyle* wrappingStyle = |
| 1263 EditingStyle::create(context, EditingStyle::EditingPropertiesInEffect); | 31 EditingStyle::create(context, EditingStyle::EditingPropertiesInEffect); |
| 1264 | 32 |
| 1265 // Styles that Mail blockquotes contribute should only be placed on the Mail | 33 // Styles that Mail blockquotes contribute should only be placed on the Mail |
| 1266 // blockquote, to help us differentiate those styles from ones that the user | 34 // blockquote, to help us differentiate those styles from ones that the user |
| 1267 // has applied. This helps us get the color of content pasted into | 35 // has applied. This helps us get the color of content pasted into |
| 1268 // blockquotes right. | 36 // blockquotes right. |
| 1269 wrappingStyle->removeStyleAddedByElement(toHTMLElement(enclosingNodeOfType( | 37 wrappingStyle->removeStyleAddedByElement(toHTMLElement(enclosingNodeOfType( |
| 1270 firstPositionInOrBeforeNode(context), isMailHTMLBlockquoteElement, | 38 firstPositionInOrBeforeNode(context), isMailHTMLBlockquoteElement, |
| 1271 CanCrossEditingBoundary))); | 39 CanCrossEditingBoundary))); |
| 1272 | 40 |
| 1273 // Call collapseTextDecorationProperties first or otherwise it'll copy the | 41 // Call collapseTextDecorationProperties first or otherwise it'll copy the |
| 1274 // value over from in-effect to text-decorations. | 42 // value over from in-effect to text-decorations. |
| 1275 wrappingStyle->collapseTextDecorationProperties(); | 43 wrappingStyle->collapseTextDecorationProperties(); |
| 1276 | 44 |
| 1277 return wrappingStyle; | 45 return wrappingStyle; |
| 1278 } | 46 } |
| 1279 | 47 |
| 1280 EditingStyle* EditingStyle::wrappingStyleForSerialization( | 48 EditingStyle* EditingStyleUtilities::wrappingStyleForSerialization( |
| 1281 ContainerNode* context) { | 49 ContainerNode* context) { |
| 1282 DCHECK(context); | 50 DCHECK(context); |
| 1283 EditingStyle* wrappingStyle = EditingStyle::create(); | 51 EditingStyle* wrappingStyle = EditingStyle::create(); |
| 1284 | 52 |
| 1285 // When not annotating for interchange, we only preserve inline style | 53 // When not annotating for interchange, we only preserve inline style |
| 1286 // declarations. | 54 // declarations. |
| 1287 for (Node& node : NodeTraversal::inclusiveAncestorsOf(*context)) { | 55 for (Node& node : NodeTraversal::inclusiveAncestorsOf(*context)) { |
| 1288 if (node.isDocumentNode()) | 56 if (node.isDocumentNode()) |
| 1289 break; | 57 break; |
| 1290 if (node.isStyledElement() && !isMailHTMLBlockquoteElement(&node)) { | 58 if (node.isStyledElement() && !isMailHTMLBlockquoteElement(&node)) { |
| 1291 wrappingStyle->mergeInlineAndImplicitStyleOfElement( | 59 wrappingStyle->mergeInlineAndImplicitStyleOfElement( |
| 1292 toElement(&node), EditingStyle::DoNotOverrideValues, | 60 toElement(&node), EditingStyle::DoNotOverrideValues, |
| 1293 EditingStyle::EditingPropertiesInEffect); | 61 EditingStyle::EditingPropertiesInEffect); |
| 1294 } | 62 } |
| 1295 } | 63 } |
| 1296 | 64 |
| 1297 return wrappingStyle; | 65 return wrappingStyle; |
| 1298 } | 66 } |
| 1299 | 67 |
| 1300 static const CSSValueList& mergeTextDecorationValues( | 68 EditingStyle* EditingStyleUtilities::styleAtSelectionStart( |
| 1301 const CSSValueList& mergedValue, | |
| 1302 const CSSValueList& valueToMerge) { | |
| 1303 DEFINE_STATIC_LOCAL(CSSIdentifierValue, underline, | |
| 1304 (CSSIdentifierValue::create(CSSValueUnderline))); | |
| 1305 DEFINE_STATIC_LOCAL(CSSIdentifierValue, lineThrough, | |
| 1306 (CSSIdentifierValue::create(CSSValueLineThrough))); | |
| 1307 CSSValueList& result = *mergedValue.copy(); | |
| 1308 if (valueToMerge.hasValue(underline) && !mergedValue.hasValue(underline)) | |
| 1309 result.append(underline); | |
| 1310 | |
| 1311 if (valueToMerge.hasValue(lineThrough) && !mergedValue.hasValue(lineThrough)) | |
| 1312 result.append(lineThrough); | |
| 1313 | |
| 1314 return result; | |
| 1315 } | |
| 1316 | |
| 1317 void EditingStyle::mergeStyle(const StylePropertySet* style, | |
| 1318 CSSPropertyOverrideMode mode) { | |
| 1319 if (!style) | |
| 1320 return; | |
| 1321 | |
| 1322 if (!m_mutableStyle) { | |
| 1323 m_mutableStyle = style->mutableCopy(); | |
| 1324 return; | |
| 1325 } | |
| 1326 | |
| 1327 unsigned propertyCount = style->propertyCount(); | |
| 1328 for (unsigned i = 0; i < propertyCount; ++i) { | |
| 1329 StylePropertySet::PropertyReference property = style->propertyAt(i); | |
| 1330 const CSSValue* value = m_mutableStyle->getPropertyCSSValue(property.id()); | |
| 1331 | |
| 1332 // text decorations never override values | |
| 1333 if ((property.id() == textDecorationPropertyForEditing() || | |
| 1334 property.id() == CSSPropertyWebkitTextDecorationsInEffect) && | |
| 1335 property.value().isValueList() && value) { | |
| 1336 if (value->isValueList()) { | |
| 1337 const CSSValueList& result = mergeTextDecorationValues( | |
| 1338 *toCSSValueList(value), toCSSValueList(property.value())); | |
| 1339 m_mutableStyle->setProperty(property.id(), result, | |
| 1340 property.isImportant()); | |
| 1341 continue; | |
| 1342 } | |
| 1343 // text-decoration: none is equivalent to not having the property | |
| 1344 value = nullptr; | |
| 1345 } | |
| 1346 | |
| 1347 if (mode == OverrideValues || (mode == DoNotOverrideValues && !value)) | |
| 1348 m_mutableStyle->setProperty(property.toCSSProperty()); | |
| 1349 } | |
| 1350 } | |
| 1351 | |
| 1352 static MutableStylePropertySet* styleFromMatchedRulesForElement( | |
| 1353 Element* element, | |
| 1354 unsigned rulesToInclude) { | |
| 1355 MutableStylePropertySet* style = | |
| 1356 MutableStylePropertySet::create(HTMLQuirksMode); | |
| 1357 StyleRuleList* matchedRules = | |
| 1358 element->document().ensureStyleResolver().styleRulesForElement( | |
| 1359 element, rulesToInclude); | |
| 1360 if (matchedRules) { | |
| 1361 for (unsigned i = 0; i < matchedRules->size(); ++i) | |
| 1362 style->mergeAndOverrideOnConflict(&matchedRules->at(i)->properties()); | |
| 1363 } | |
| 1364 return style; | |
| 1365 } | |
| 1366 | |
| 1367 void EditingStyle::mergeStyleFromRules(Element* element) { | |
| 1368 MutableStylePropertySet* styleFromMatchedRules = | |
| 1369 styleFromMatchedRulesForElement( | |
| 1370 element, | |
| 1371 StyleResolver::AuthorCSSRules | StyleResolver::CrossOriginCSSRules); | |
| 1372 // Styles from the inline style declaration, held in the variable "style", | |
| 1373 // take precedence over those from matched rules. | |
| 1374 if (m_mutableStyle) | |
| 1375 styleFromMatchedRules->mergeAndOverrideOnConflict(m_mutableStyle.get()); | |
| 1376 | |
| 1377 clear(); | |
| 1378 m_mutableStyle = styleFromMatchedRules; | |
| 1379 } | |
| 1380 | |
| 1381 void EditingStyle::mergeStyleFromRulesForSerialization(Element* element) { | |
| 1382 mergeStyleFromRules(element); | |
| 1383 | |
| 1384 // The property value, if it's a percentage, may not reflect the actual | |
| 1385 // computed value. | |
| 1386 // For example: style="height: 1%; overflow: visible;" in quirksmode | |
| 1387 // FIXME: There are others like this, see <rdar://problem/5195123> Slashdot | |
| 1388 // copy/paste fidelity problem | |
| 1389 CSSComputedStyleDeclaration* computedStyleForElement = | |
| 1390 CSSComputedStyleDeclaration::create(element); | |
| 1391 MutableStylePropertySet* fromComputedStyle = | |
| 1392 MutableStylePropertySet::create(HTMLQuirksMode); | |
| 1393 { | |
| 1394 unsigned propertyCount = m_mutableStyle->propertyCount(); | |
| 1395 for (unsigned i = 0; i < propertyCount; ++i) { | |
| 1396 StylePropertySet::PropertyReference property = | |
| 1397 m_mutableStyle->propertyAt(i); | |
| 1398 const CSSValue& value = property.value(); | |
| 1399 if (!value.isPrimitiveValue()) | |
| 1400 continue; | |
| 1401 if (toCSSPrimitiveValue(value).isPercentage()) { | |
| 1402 if (const CSSValue* computedPropertyValue = | |
| 1403 computedStyleForElement->getPropertyCSSValue(property.id())) { | |
| 1404 fromComputedStyle->addRespectingCascade( | |
| 1405 CSSProperty(property.id(), *computedPropertyValue)); | |
| 1406 } | |
| 1407 } | |
| 1408 } | |
| 1409 } | |
| 1410 m_mutableStyle->mergeAndOverrideOnConflict(fromComputedStyle); | |
| 1411 } | |
| 1412 | |
| 1413 static void removePropertiesInStyle( | |
| 1414 MutableStylePropertySet* styleToRemovePropertiesFrom, | |
| 1415 StylePropertySet* style) { | |
| 1416 unsigned propertyCount = style->propertyCount(); | |
| 1417 Vector<CSSPropertyID> propertiesToRemove(propertyCount); | |
| 1418 for (unsigned i = 0; i < propertyCount; ++i) | |
| 1419 propertiesToRemove[i] = style->propertyAt(i).id(); | |
| 1420 | |
| 1421 styleToRemovePropertiesFrom->removePropertiesInSet(propertiesToRemove.data(), | |
| 1422 propertiesToRemove.size()); | |
| 1423 } | |
| 1424 | |
| 1425 void EditingStyle::removeStyleFromRulesAndContext(Element* element, | |
| 1426 ContainerNode* context) { | |
| 1427 DCHECK(element); | |
| 1428 if (!m_mutableStyle) | |
| 1429 return; | |
| 1430 | |
| 1431 // StyleResolver requires clean style. | |
| 1432 DCHECK_GE(element->document().lifecycle().state(), | |
| 1433 DocumentLifecycle::StyleClean); | |
| 1434 DCHECK(element->document().isActive()); | |
| 1435 | |
| 1436 // 1. Remove style from matched rules because style remain without repeating | |
| 1437 // it in inline style declaration | |
| 1438 MutableStylePropertySet* styleFromMatchedRules = | |
| 1439 styleFromMatchedRulesForElement(element, | |
| 1440 StyleResolver::AllButEmptyCSSRules); | |
| 1441 if (styleFromMatchedRules && !styleFromMatchedRules->isEmpty()) { | |
| 1442 m_mutableStyle = | |
| 1443 getPropertiesNotIn(m_mutableStyle.get(), | |
| 1444 styleFromMatchedRules->ensureCSSStyleDeclaration()); | |
| 1445 } | |
| 1446 | |
| 1447 // 2. Remove style present in context and not overriden by matched rules. | |
| 1448 EditingStyle* computedStyle = | |
| 1449 EditingStyle::create(context, EditingPropertiesInEffect); | |
| 1450 if (computedStyle->m_mutableStyle) { | |
| 1451 if (!computedStyle->m_mutableStyle->getPropertyCSSValue( | |
| 1452 CSSPropertyBackgroundColor)) { | |
| 1453 computedStyle->m_mutableStyle->setProperty(CSSPropertyBackgroundColor, | |
| 1454 CSSValueTransparent); | |
| 1455 } | |
| 1456 | |
| 1457 removePropertiesInStyle(computedStyle->m_mutableStyle.get(), | |
| 1458 styleFromMatchedRules); | |
| 1459 m_mutableStyle = getPropertiesNotIn( | |
| 1460 m_mutableStyle.get(), | |
| 1461 computedStyle->m_mutableStyle->ensureCSSStyleDeclaration()); | |
| 1462 } | |
| 1463 | |
| 1464 // 3. If this element is a span and has display: inline or float: none, remove | |
| 1465 // them unless they are overriden by rules. These rules are added by | |
| 1466 // serialization code to wrap text nodes. | |
| 1467 if (isStyleSpanOrSpanWithOnlyStyleAttribute(element)) { | |
| 1468 if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyDisplay) && | |
| 1469 getIdentifierValue(m_mutableStyle.get(), CSSPropertyDisplay) == | |
| 1470 CSSValueInline) | |
| 1471 m_mutableStyle->removeProperty(CSSPropertyDisplay); | |
| 1472 if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyFloat) && | |
| 1473 getIdentifierValue(m_mutableStyle.get(), CSSPropertyFloat) == | |
| 1474 CSSValueNone) | |
| 1475 m_mutableStyle->removeProperty(CSSPropertyFloat); | |
| 1476 } | |
| 1477 } | |
| 1478 | |
| 1479 void EditingStyle::removePropertiesInElementDefaultStyle(Element* element) { | |
| 1480 if (!m_mutableStyle || m_mutableStyle->isEmpty()) | |
| 1481 return; | |
| 1482 | |
| 1483 StylePropertySet* defaultStyle = styleFromMatchedRulesForElement( | |
| 1484 element, StyleResolver::UAAndUserCSSRules); | |
| 1485 | |
| 1486 removePropertiesInStyle(m_mutableStyle.get(), defaultStyle); | |
| 1487 } | |
| 1488 | |
| 1489 void EditingStyle::addAbsolutePositioningFromElement(const Element& element) { | |
| 1490 LayoutRect rect = element.boundingBox(); | |
| 1491 LayoutObject* layoutObject = element.layoutObject(); | |
| 1492 | |
| 1493 LayoutUnit x = rect.x(); | |
| 1494 LayoutUnit y = rect.y(); | |
| 1495 LayoutUnit width = rect.width(); | |
| 1496 LayoutUnit height = rect.height(); | |
| 1497 if (layoutObject && layoutObject->isBox()) { | |
| 1498 LayoutBox* layoutBox = toLayoutBox(layoutObject); | |
| 1499 | |
| 1500 x -= layoutBox->marginLeft(); | |
| 1501 y -= layoutBox->marginTop(); | |
| 1502 | |
| 1503 m_mutableStyle->setProperty(CSSPropertyBoxSizing, CSSValueBorderBox); | |
| 1504 } | |
| 1505 | |
| 1506 m_mutableStyle->setProperty(CSSPropertyPosition, CSSValueAbsolute); | |
| 1507 m_mutableStyle->setProperty( | |
| 1508 CSSPropertyLeft, | |
| 1509 *CSSPrimitiveValue::create(x, CSSPrimitiveValue::UnitType::Pixels)); | |
| 1510 m_mutableStyle->setProperty( | |
| 1511 CSSPropertyTop, | |
| 1512 *CSSPrimitiveValue::create(y, CSSPrimitiveValue::UnitType::Pixels)); | |
| 1513 m_mutableStyle->setProperty( | |
| 1514 CSSPropertyWidth, | |
| 1515 *CSSPrimitiveValue::create(width, CSSPrimitiveValue::UnitType::Pixels)); | |
| 1516 m_mutableStyle->setProperty( | |
| 1517 CSSPropertyHeight, | |
| 1518 *CSSPrimitiveValue::create(height, CSSPrimitiveValue::UnitType::Pixels)); | |
| 1519 } | |
| 1520 | |
| 1521 void EditingStyle::forceInline() { | |
| 1522 if (!m_mutableStyle) | |
| 1523 m_mutableStyle = MutableStylePropertySet::create(HTMLQuirksMode); | |
| 1524 const bool propertyIsImportant = true; | |
| 1525 m_mutableStyle->setProperty(CSSPropertyDisplay, CSSValueInline, | |
| 1526 propertyIsImportant); | |
| 1527 } | |
| 1528 | |
| 1529 int EditingStyle::legacyFontSize(Document* document) const { | |
| 1530 const CSSValue* cssValue = | |
| 1531 m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize); | |
| 1532 if (!cssValue || | |
| 1533 !(cssValue->isPrimitiveValue() || cssValue->isIdentifierValue())) | |
| 1534 return 0; | |
| 1535 return legacyFontSizeFromCSSValue(document, cssValue, m_isMonospaceFont, | |
| 1536 AlwaysUseLegacyFontSize); | |
| 1537 } | |
| 1538 | |
| 1539 EditingStyle* EditingStyle::styleAtSelectionStart( | |
| 1540 const VisibleSelection& selection, | 69 const VisibleSelection& selection, |
| 1541 bool shouldUseBackgroundColorInEffect, | 70 bool shouldUseBackgroundColorInEffect, |
| 1542 MutableStylePropertySet* styleToCheck) { | 71 MutableStylePropertySet* styleToCheck) { |
| 1543 if (selection.isNone()) | 72 if (selection.isNone()) |
| 1544 return nullptr; | 73 return nullptr; |
| 1545 | 74 |
| 1546 Document& document = *selection.start().document(); | 75 Document& document = *selection.start().document(); |
| 1547 | 76 |
| 1548 DCHECK(!document.needsLayoutTreeUpdate()); | 77 DCHECK(!document.needsLayoutTreeUpdate()); |
| 1549 DocumentLifecycle::DisallowTransitionScope disallowTransition( | 78 DocumentLifecycle::DisallowTransitionScope disallowTransition( |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 1578 CSSValueID valueID = | 107 CSSValueID valueID = |
| 1579 getIdentifierValue(styleToCheck, CSSPropertyVerticalAlign); | 108 getIdentifierValue(styleToCheck, CSSPropertyVerticalAlign); |
| 1580 if (valueID == CSSValueSub || valueID == CSSValueSuper) { | 109 if (valueID == CSSValueSub || valueID == CSSValueSuper) { |
| 1581 CSSComputedStyleDeclaration* elementStyle = | 110 CSSComputedStyleDeclaration* elementStyle = |
| 1582 CSSComputedStyleDeclaration::create(element); | 111 CSSComputedStyleDeclaration::create(element); |
| 1583 // Find the ancestor that has CSSValueSub or CSSValueSuper as the value of | 112 // Find the ancestor that has CSSValueSub or CSSValueSuper as the value of |
| 1584 // CSS vertical-align property. | 113 // CSS vertical-align property. |
| 1585 if (getIdentifierValue(elementStyle, CSSPropertyVerticalAlign) == | 114 if (getIdentifierValue(elementStyle, CSSPropertyVerticalAlign) == |
| 1586 CSSValueBaseline && | 115 CSSValueBaseline && |
| 1587 hasAncestorVerticalAlignStyle(*element, valueID)) | 116 hasAncestorVerticalAlignStyle(*element, valueID)) |
| 1588 style->m_mutableStyle->setProperty(CSSPropertyVerticalAlign, valueID); | 117 style->style()->setProperty(CSSPropertyVerticalAlign, valueID); |
| 1589 } | 118 } |
| 1590 | 119 |
| 1591 // If background color is transparent, traverse parent nodes until we hit a | 120 // If background color is transparent, traverse parent nodes until we hit a |
| 1592 // different value or document root Also, if the selection is a range, ignore | 121 // different value or document root Also, if the selection is a range, ignore |
| 1593 // the background color at the start of selection, and find the background | 122 // the background color at the start of selection, and find the background |
| 1594 // color of the common ancestor. | 123 // color of the common ancestor. |
| 1595 if (shouldUseBackgroundColorInEffect && | 124 if (shouldUseBackgroundColorInEffect && |
| 1596 (selection.isRange() || | 125 (selection.isRange() || hasTransparentBackgroundColor(style->style()))) { |
| 1597 hasTransparentBackgroundColor(style->m_mutableStyle.get()))) { | |
| 1598 const EphemeralRange range(selection.toNormalizedEphemeralRange()); | 126 const EphemeralRange range(selection.toNormalizedEphemeralRange()); |
| 1599 if (const CSSValue* value = | 127 if (const CSSValue* value = |
| 1600 backgroundColorValueInEffect(Range::commonAncestorContainer( | 128 backgroundColorValueInEffect(Range::commonAncestorContainer( |
| 1601 range.startPosition().computeContainerNode(), | 129 range.startPosition().computeContainerNode(), |
| 1602 range.endPosition().computeContainerNode()))) | 130 range.endPosition().computeContainerNode()))) |
| 1603 style->setProperty(CSSPropertyBackgroundColor, value->cssText()); | 131 style->setProperty(CSSPropertyBackgroundColor, value->cssText()); |
| 1604 } | 132 } |
| 1605 | 133 |
| 1606 return style; | 134 return style; |
| 1607 } | 135 } |
| 1608 | 136 |
| 1609 static bool isUnicodeBidiNestedOrMultipleEmbeddings(CSSValueID valueID) { | 137 static bool isUnicodeBidiNestedOrMultipleEmbeddings(CSSValueID valueID) { |
| 1610 return valueID == CSSValueEmbed || valueID == CSSValueBidiOverride || | 138 return valueID == CSSValueEmbed || valueID == CSSValueBidiOverride || |
| 1611 valueID == CSSValueWebkitIsolate || | 139 valueID == CSSValueWebkitIsolate || |
| 1612 valueID == CSSValueWebkitIsolateOverride || | 140 valueID == CSSValueWebkitIsolateOverride || |
| 1613 valueID == CSSValueWebkitPlaintext || valueID == CSSValueIsolate || | 141 valueID == CSSValueWebkitPlaintext || valueID == CSSValueIsolate || |
| 1614 valueID == CSSValueIsolateOverride || valueID == CSSValuePlaintext; | 142 valueID == CSSValueIsolateOverride || valueID == CSSValuePlaintext; |
| 1615 } | 143 } |
| 1616 | 144 |
| 1617 WritingDirection EditingStyle::textDirectionForSelection( | 145 WritingDirection EditingStyleUtilities::textDirectionForSelection( |
| 1618 const VisibleSelection& selection, | 146 const VisibleSelection& selection, |
| 1619 EditingStyle* typingStyle, | 147 EditingStyle* typingStyle, |
| 1620 bool& hasNestedOrMultipleEmbeddings) { | 148 bool& hasNestedOrMultipleEmbeddings) { |
| 1621 hasNestedOrMultipleEmbeddings = true; | 149 hasNestedOrMultipleEmbeddings = true; |
| 1622 | 150 |
| 1623 if (selection.isNone()) | 151 if (selection.isNone()) |
| 1624 return NaturalWritingDirection; | 152 return NaturalWritingDirection; |
| 1625 | 153 |
| 1626 Position position = mostForwardCaretPosition(selection.start()); | 154 Position position = mostForwardCaretPosition(selection.start()); |
| 1627 | 155 |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1710 return NaturalWritingDirection; | 238 return NaturalWritingDirection; |
| 1711 | 239 |
| 1712 foundDirection = directionValue == CSSValueLtr | 240 foundDirection = directionValue == CSSValueLtr |
| 1713 ? LeftToRightWritingDirection | 241 ? LeftToRightWritingDirection |
| 1714 : RightToLeftWritingDirection; | 242 : RightToLeftWritingDirection; |
| 1715 } | 243 } |
| 1716 hasNestedOrMultipleEmbeddings = false; | 244 hasNestedOrMultipleEmbeddings = false; |
| 1717 return foundDirection; | 245 return foundDirection; |
| 1718 } | 246 } |
| 1719 | 247 |
| 1720 DEFINE_TRACE(EditingStyle) { | 248 bool EditingStyleUtilities::isTransparentColorValue(const CSSValue* cssValue) { |
| 1721 visitor->trace(m_mutableStyle); | |
| 1722 } | |
| 1723 | |
| 1724 static void reconcileTextDecorationProperties(MutableStylePropertySet* style) { | |
| 1725 const CSSValue* textDecorationsInEffect = | |
| 1726 style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect); | |
| 1727 const CSSValue* textDecoration = | |
| 1728 style->getPropertyCSSValue(textDecorationPropertyForEditing()); | |
| 1729 // "LayoutTests/editing/execCommand/insert-list-and-strikethrough.html" makes | |
| 1730 // both |textDecorationsInEffect| and |textDecoration| non-null. | |
| 1731 if (textDecorationsInEffect) { | |
| 1732 style->setProperty(textDecorationPropertyForEditing(), | |
| 1733 textDecorationsInEffect->cssText()); | |
| 1734 style->removeProperty(CSSPropertyWebkitTextDecorationsInEffect); | |
| 1735 textDecoration = textDecorationsInEffect; | |
| 1736 } | |
| 1737 | |
| 1738 // If text-decoration is set to "none", remove the property because we don't | |
| 1739 // want to add redundant "text-decoration: none". | |
| 1740 if (textDecoration && !textDecoration->isValueList()) | |
| 1741 style->removeProperty(textDecorationPropertyForEditing()); | |
| 1742 } | |
| 1743 | |
| 1744 StyleChange::StyleChange(EditingStyle* style, const Position& position) | |
| 1745 : m_applyBold(false), | |
| 1746 m_applyItalic(false), | |
| 1747 m_applyUnderline(false), | |
| 1748 m_applyLineThrough(false), | |
| 1749 m_applySubscript(false), | |
| 1750 m_applySuperscript(false) { | |
| 1751 Document* document = position.document(); | |
| 1752 if (!style || !style->style() || !document || !document->frame() || | |
| 1753 !associatedElementOf(position)) | |
| 1754 return; | |
| 1755 | |
| 1756 CSSComputedStyleDeclaration* computedStyle = ensureComputedStyle(position); | |
| 1757 // FIXME: take care of background-color in effect | |
| 1758 MutableStylePropertySet* mutableStyle = | |
| 1759 getPropertiesNotIn(style->style(), computedStyle); | |
| 1760 DCHECK(mutableStyle); | |
| 1761 | |
| 1762 reconcileTextDecorationProperties(mutableStyle); | |
| 1763 if (!document->frame()->editor().shouldStyleWithCSS()) | |
| 1764 extractTextStyles(document, mutableStyle, computedStyle->isMonospaceFont()); | |
| 1765 | |
| 1766 // Changing the whitespace style in a tab span would collapse the tab into a | |
| 1767 // space. | |
| 1768 if (isTabHTMLSpanElementTextNode(position.anchorNode()) || | |
| 1769 isTabHTMLSpanElement((position.anchorNode()))) | |
| 1770 mutableStyle->removeProperty(CSSPropertyWhiteSpace); | |
| 1771 | |
| 1772 // If unicode-bidi is present in mutableStyle and direction is not, then add | |
| 1773 // direction to mutableStyle. | |
| 1774 // FIXME: Shouldn't this be done in getPropertiesNotIn? | |
| 1775 if (mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi) && | |
| 1776 !style->style()->getPropertyCSSValue(CSSPropertyDirection)) { | |
| 1777 mutableStyle->setProperty( | |
| 1778 CSSPropertyDirection, | |
| 1779 style->style()->getPropertyValue(CSSPropertyDirection)); | |
| 1780 } | |
| 1781 | |
| 1782 // Save the result for later | |
| 1783 m_cssStyle = mutableStyle->asText().stripWhiteSpace(); | |
| 1784 } | |
| 1785 | |
| 1786 static void setTextDecorationProperty(MutableStylePropertySet* style, | |
| 1787 const CSSValueList* newTextDecoration, | |
| 1788 CSSPropertyID propertyID) { | |
| 1789 if (newTextDecoration->length()) { | |
| 1790 style->setProperty(propertyID, newTextDecoration->cssText(), | |
| 1791 style->propertyIsImportant(propertyID)); | |
| 1792 } else { | |
| 1793 // text-decoration: none is redundant since it does not remove any text | |
| 1794 // decorations. | |
| 1795 style->removeProperty(propertyID); | |
| 1796 } | |
| 1797 } | |
| 1798 | |
| 1799 void StyleChange::extractTextStyles(Document* document, | |
| 1800 MutableStylePropertySet* style, | |
| 1801 bool isMonospaceFont) { | |
| 1802 DCHECK(style); | |
| 1803 | |
| 1804 if (getIdentifierValue(style, CSSPropertyFontWeight) == CSSValueBold) { | |
| 1805 style->removeProperty(CSSPropertyFontWeight); | |
| 1806 m_applyBold = true; | |
| 1807 } | |
| 1808 | |
| 1809 int fontStyle = getIdentifierValue(style, CSSPropertyFontStyle); | |
| 1810 if (fontStyle == CSSValueItalic || fontStyle == CSSValueOblique) { | |
| 1811 style->removeProperty(CSSPropertyFontStyle); | |
| 1812 m_applyItalic = true; | |
| 1813 } | |
| 1814 | |
| 1815 // Assuming reconcileTextDecorationProperties has been called, there should | |
| 1816 // not be -webkit-text-decorations-in-effect | |
| 1817 // Furthermore, text-decoration: none has been trimmed so that text-decoration | |
| 1818 // property is always a CSSValueList. | |
| 1819 const CSSValue* textDecoration = | |
| 1820 style->getPropertyCSSValue(textDecorationPropertyForEditing()); | |
| 1821 if (textDecoration && textDecoration->isValueList()) { | |
| 1822 DEFINE_STATIC_LOCAL(CSSIdentifierValue, underline, | |
| 1823 (CSSIdentifierValue::create(CSSValueUnderline))); | |
| 1824 DEFINE_STATIC_LOCAL(CSSIdentifierValue, lineThrough, | |
| 1825 (CSSIdentifierValue::create(CSSValueLineThrough))); | |
| 1826 CSSValueList* newTextDecoration = toCSSValueList(textDecoration)->copy(); | |
| 1827 if (newTextDecoration->removeAll(underline)) | |
| 1828 m_applyUnderline = true; | |
| 1829 if (newTextDecoration->removeAll(lineThrough)) | |
| 1830 m_applyLineThrough = true; | |
| 1831 | |
| 1832 // If trimTextDecorations, delete underline and line-through | |
| 1833 setTextDecorationProperty(style, newTextDecoration, | |
| 1834 textDecorationPropertyForEditing()); | |
| 1835 } | |
| 1836 | |
| 1837 int verticalAlign = getIdentifierValue(style, CSSPropertyVerticalAlign); | |
| 1838 switch (verticalAlign) { | |
| 1839 case CSSValueSub: | |
| 1840 style->removeProperty(CSSPropertyVerticalAlign); | |
| 1841 m_applySubscript = true; | |
| 1842 break; | |
| 1843 case CSSValueSuper: | |
| 1844 style->removeProperty(CSSPropertyVerticalAlign); | |
| 1845 m_applySuperscript = true; | |
| 1846 break; | |
| 1847 } | |
| 1848 | |
| 1849 if (style->getPropertyCSSValue(CSSPropertyColor)) { | |
| 1850 m_applyFontColor = getFontColor(style).serialized(); | |
| 1851 style->removeProperty(CSSPropertyColor); | |
| 1852 } | |
| 1853 | |
| 1854 m_applyFontFace = style->getPropertyValue(CSSPropertyFontFamily); | |
| 1855 // Remove double quotes for Outlook 2007 compatibility. See | |
| 1856 // https://bugs.webkit.org/show_bug.cgi?id=79448 | |
| 1857 m_applyFontFace.replace('"', ""); | |
| 1858 style->removeProperty(CSSPropertyFontFamily); | |
| 1859 | |
| 1860 if (const CSSValue* fontSize = | |
| 1861 style->getPropertyCSSValue(CSSPropertyFontSize)) { | |
| 1862 if (!fontSize->isPrimitiveValue() && !fontSize->isIdentifierValue()) { | |
| 1863 // Can't make sense of the number. Put no font size. | |
| 1864 style->removeProperty(CSSPropertyFontSize); | |
| 1865 } else if (int legacyFontSize = legacyFontSizeFromCSSValue( | |
| 1866 document, fontSize, isMonospaceFont, | |
| 1867 UseLegacyFontSizeOnlyIfPixelValuesMatch)) { | |
| 1868 m_applyFontSize = String::number(legacyFontSize); | |
| 1869 style->removeProperty(CSSPropertyFontSize); | |
| 1870 } | |
| 1871 } | |
| 1872 } | |
| 1873 | |
| 1874 static void diffTextDecorations(MutableStylePropertySet* style, | |
| 1875 CSSPropertyID propertyID, | |
| 1876 const CSSValue* refTextDecoration) { | |
| 1877 const CSSValue* textDecoration = style->getPropertyCSSValue(propertyID); | |
| 1878 if (!textDecoration || !textDecoration->isValueList() || !refTextDecoration || | |
| 1879 !refTextDecoration->isValueList()) | |
| 1880 return; | |
| 1881 | |
| 1882 CSSValueList* newTextDecoration = toCSSValueList(textDecoration)->copy(); | |
| 1883 const CSSValueList* valuesInRefTextDecoration = | |
| 1884 toCSSValueList(refTextDecoration); | |
| 1885 | |
| 1886 for (size_t i = 0; i < valuesInRefTextDecoration->length(); i++) | |
| 1887 newTextDecoration->removeAll(valuesInRefTextDecoration->item(i)); | |
| 1888 | |
| 1889 setTextDecorationProperty(style, newTextDecoration, propertyID); | |
| 1890 } | |
| 1891 | |
| 1892 static bool fontWeightIsBold(const CSSValue* fontWeight) { | |
| 1893 if (!fontWeight->isIdentifierValue()) | |
| 1894 return false; | |
| 1895 | |
| 1896 // Because b tag can only bold text, there are only two states in plain html: | |
| 1897 // bold and not bold. Collapse all other values to either one of these two | |
| 1898 // states for editing purposes. | |
| 1899 switch (toCSSIdentifierValue(fontWeight)->getValueID()) { | |
| 1900 case CSSValue100: | |
| 1901 case CSSValue200: | |
| 1902 case CSSValue300: | |
| 1903 case CSSValue400: | |
| 1904 case CSSValue500: | |
| 1905 case CSSValueNormal: | |
| 1906 return false; | |
| 1907 case CSSValueBold: | |
| 1908 case CSSValue600: | |
| 1909 case CSSValue700: | |
| 1910 case CSSValue800: | |
| 1911 case CSSValue900: | |
| 1912 return true; | |
| 1913 default: | |
| 1914 break; | |
| 1915 } | |
| 1916 | |
| 1917 NOTREACHED(); // For CSSValueBolder and CSSValueLighter | |
| 1918 return false; | |
| 1919 } | |
| 1920 | |
| 1921 static bool fontWeightNeedsResolving(const CSSValue* fontWeight) { | |
| 1922 if (!fontWeight->isIdentifierValue()) | |
| 1923 return true; | |
| 1924 | |
| 1925 const CSSValueID value = toCSSIdentifierValue(fontWeight)->getValueID(); | |
| 1926 return value == CSSValueLighter || value == CSSValueBolder; | |
| 1927 } | |
| 1928 | |
| 1929 MutableStylePropertySet* getPropertiesNotIn( | |
| 1930 StylePropertySet* styleWithRedundantProperties, | |
| 1931 CSSStyleDeclaration* baseStyle) { | |
| 1932 DCHECK(styleWithRedundantProperties); | |
| 1933 DCHECK(baseStyle); | |
| 1934 MutableStylePropertySet* result = styleWithRedundantProperties->mutableCopy(); | |
| 1935 | |
| 1936 result->removeEquivalentProperties(baseStyle); | |
| 1937 | |
| 1938 const CSSValue* baseTextDecorationsInEffect = | |
| 1939 baseStyle->getPropertyCSSValueInternal( | |
| 1940 CSSPropertyWebkitTextDecorationsInEffect); | |
| 1941 diffTextDecorations(result, textDecorationPropertyForEditing(), | |
| 1942 baseTextDecorationsInEffect); | |
| 1943 diffTextDecorations(result, CSSPropertyWebkitTextDecorationsInEffect, | |
| 1944 baseTextDecorationsInEffect); | |
| 1945 | |
| 1946 if (const CSSValue* baseFontWeight = | |
| 1947 baseStyle->getPropertyCSSValueInternal(CSSPropertyFontWeight)) { | |
| 1948 if (const CSSValue* fontWeight = | |
| 1949 result->getPropertyCSSValue(CSSPropertyFontWeight)) { | |
| 1950 if (!fontWeightNeedsResolving(fontWeight) && | |
| 1951 !fontWeightNeedsResolving(baseFontWeight) && | |
| 1952 (fontWeightIsBold(fontWeight) == fontWeightIsBold(baseFontWeight))) | |
| 1953 result->removeProperty(CSSPropertyFontWeight); | |
| 1954 } | |
| 1955 } | |
| 1956 | |
| 1957 if (baseStyle->getPropertyCSSValueInternal(CSSPropertyColor) && | |
| 1958 getFontColor(result) == getFontColor(baseStyle)) | |
| 1959 result->removeProperty(CSSPropertyColor); | |
| 1960 | |
| 1961 if (baseStyle->getPropertyCSSValueInternal(CSSPropertyTextAlign) && | |
| 1962 textAlignResolvingStartAndEnd(result) == | |
| 1963 textAlignResolvingStartAndEnd(baseStyle)) | |
| 1964 result->removeProperty(CSSPropertyTextAlign); | |
| 1965 | |
| 1966 if (baseStyle->getPropertyCSSValueInternal(CSSPropertyBackgroundColor) && | |
| 1967 getBackgroundColor(result) == getBackgroundColor(baseStyle)) | |
| 1968 result->removeProperty(CSSPropertyBackgroundColor); | |
| 1969 | |
| 1970 return result; | |
| 1971 } | |
| 1972 | |
| 1973 CSSValueID getIdentifierValue(StylePropertySet* style, | |
| 1974 CSSPropertyID propertyID) { | |
| 1975 if (!style) | |
| 1976 return CSSValueInvalid; | |
| 1977 const CSSValue* value = style->getPropertyCSSValue(propertyID); | |
| 1978 if (!value || !value->isIdentifierValue()) | |
| 1979 return CSSValueInvalid; | |
| 1980 return toCSSIdentifierValue(value)->getValueID(); | |
| 1981 } | |
| 1982 | |
| 1983 CSSValueID getIdentifierValue(CSSStyleDeclaration* style, | |
| 1984 CSSPropertyID propertyID) { | |
| 1985 if (!style) | |
| 1986 return CSSValueInvalid; | |
| 1987 const CSSValue* value = style->getPropertyCSSValueInternal(propertyID); | |
| 1988 if (!value || !value->isIdentifierValue()) | |
| 1989 return CSSValueInvalid; | |
| 1990 return toCSSIdentifierValue(value)->getValueID(); | |
| 1991 } | |
| 1992 | |
| 1993 int legacyFontSizeFromCSSValue(Document* document, | |
| 1994 const CSSValue* value, | |
| 1995 bool isMonospaceFont, | |
| 1996 LegacyFontSizeMode mode) { | |
| 1997 if (value->isPrimitiveValue()) { | |
| 1998 const CSSPrimitiveValue& primitiveValue = toCSSPrimitiveValue(*value); | |
| 1999 CSSPrimitiveValue::LengthUnitType lengthType; | |
| 2000 if (CSSPrimitiveValue::unitTypeToLengthUnitType( | |
| 2001 primitiveValue.typeWithCalcResolved(), lengthType) && | |
| 2002 lengthType == CSSPrimitiveValue::UnitTypePixels) { | |
| 2003 double conversion = | |
| 2004 CSSPrimitiveValue::conversionToCanonicalUnitsScaleFactor( | |
| 2005 primitiveValue.typeWithCalcResolved()); | |
| 2006 int pixelFontSize = | |
| 2007 clampTo<int>(primitiveValue.getDoubleValue() * conversion); | |
| 2008 int legacyFontSize = | |
| 2009 FontSize::legacyFontSize(document, pixelFontSize, isMonospaceFont); | |
| 2010 // Use legacy font size only if pixel value matches exactly to that of | |
| 2011 // legacy font size. | |
| 2012 if (mode == AlwaysUseLegacyFontSize || | |
| 2013 FontSize::fontSizeForKeyword(document, legacyFontSize, | |
| 2014 isMonospaceFont) == pixelFontSize) | |
| 2015 return legacyFontSize; | |
| 2016 | |
| 2017 return 0; | |
| 2018 } | |
| 2019 } | |
| 2020 | |
| 2021 if (value->isIdentifierValue()) { | |
| 2022 const CSSIdentifierValue& identifierValue = toCSSIdentifierValue(*value); | |
| 2023 if (CSSValueXSmall <= identifierValue.getValueID() && | |
| 2024 identifierValue.getValueID() <= CSSValueWebkitXxxLarge) | |
| 2025 return identifierValue.getValueID() - CSSValueXSmall + 1; | |
| 2026 } | |
| 2027 | |
| 2028 return 0; | |
| 2029 } | |
| 2030 | |
| 2031 bool isTransparentColorValue(const CSSValue* cssValue) { | |
| 2032 if (!cssValue) | 249 if (!cssValue) |
| 2033 return true; | 250 return true; |
| 2034 if (cssValue->isColorValue()) | 251 if (cssValue->isColorValue()) |
| 2035 return !toCSSColorValue(cssValue)->value().alpha(); | 252 return !toCSSColorValue(cssValue)->value().alpha(); |
| 2036 if (!cssValue->isIdentifierValue()) | 253 if (!cssValue->isIdentifierValue()) |
| 2037 return false; | 254 return false; |
| 2038 return toCSSIdentifierValue(cssValue)->getValueID() == CSSValueTransparent; | 255 return toCSSIdentifierValue(cssValue)->getValueID() == CSSValueTransparent; |
| 2039 } | 256 } |
| 2040 | 257 |
| 2041 bool hasTransparentBackgroundColor(CSSStyleDeclaration* style) { | 258 bool EditingStyleUtilities::hasTransparentBackgroundColor( |
| 259 CSSStyleDeclaration* style) { | |
| 2042 const CSSValue* cssValue = | 260 const CSSValue* cssValue = |
| 2043 style->getPropertyCSSValueInternal(CSSPropertyBackgroundColor); | 261 style->getPropertyCSSValueInternal(CSSPropertyBackgroundColor); |
| 2044 return isTransparentColorValue(cssValue); | 262 return isTransparentColorValue(cssValue); |
| 2045 } | 263 } |
| 2046 | 264 |
| 2047 bool hasTransparentBackgroundColor(StylePropertySet* style) { | 265 bool EditingStyleUtilities::hasTransparentBackgroundColor( |
| 266 StylePropertySet* style) { | |
| 2048 const CSSValue* cssValue = | 267 const CSSValue* cssValue = |
| 2049 style->getPropertyCSSValue(CSSPropertyBackgroundColor); | 268 style->getPropertyCSSValue(CSSPropertyBackgroundColor); |
| 2050 return isTransparentColorValue(cssValue); | 269 return isTransparentColorValue(cssValue); |
| 2051 } | 270 } |
| 2052 | 271 |
| 2053 const CSSValue* backgroundColorValueInEffect(Node* node) { | 272 const CSSValue* EditingStyleUtilities::backgroundColorValueInEffect( |
| 273 Node* node) { | |
| 2054 for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) { | 274 for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) { |
| 2055 CSSComputedStyleDeclaration* ancestorStyle = | 275 CSSComputedStyleDeclaration* ancestorStyle = |
| 2056 CSSComputedStyleDeclaration::create(ancestor); | 276 CSSComputedStyleDeclaration::create(ancestor); |
| 2057 if (!hasTransparentBackgroundColor(ancestorStyle)) | 277 if (!hasTransparentBackgroundColor(ancestorStyle)) |
| 2058 return ancestorStyle->getPropertyCSSValue(CSSPropertyBackgroundColor); | 278 return ancestorStyle->getPropertyCSSValue(CSSPropertyBackgroundColor); |
| 2059 } | 279 } |
| 2060 return nullptr; | 280 return nullptr; |
| 2061 } | 281 } |
| 2062 | 282 |
| 2063 } // namespace blink | 283 } // namespace blink |
| OLD | NEW |