| OLD | NEW |
| (Empty) | |
| 1 /* |
| 2 * Copyright (C) 2007, 2008, 2009 Apple Computer, Inc. |
| 3 * Copyright (C) 2010, 2011 Google Inc. All rights reserved. |
| 4 * |
| 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions |
| 7 * are met: |
| 8 * 1. Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright |
| 11 * notice, this list of conditions and the following disclaimer in the |
| 12 * documentation and/or other materials provided with the distribution. |
| 13 * |
| 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 25 */ |
| 26 |
| 27 #include "core/editing/EditingStyle.h" |
| 28 |
| 29 #include "bindings/core/v8/ExceptionState.h" |
| 30 #include "core/HTMLNames.h" |
| 31 #include "core/css/CSSColorValue.h" |
| 32 #include "core/css/CSSComputedStyleDeclaration.h" |
| 33 #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" |
| 42 #include "core/css/StyleRule.h" |
| 43 #include "core/css/parser/CSSParser.h" |
| 44 #include "core/css/resolver/StyleResolver.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" |
| 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 |
| 65 namespace blink { |
| 66 |
| 67 static const CSSPropertyID& textDecorationPropertyForEditing() { |
| 68 static const CSSPropertyID property = |
| 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)) { |
| 783 CSSComputedStyleDeclaration* ancestorStyle = |
| 784 CSSComputedStyleDeclaration::create(&runner); |
| 785 if (getIdentifierValue(ancestorStyle, CSSPropertyVerticalAlign) == value) |
| 786 return true; |
| 787 } |
| 788 return false; |
| 789 } |
| 790 |
| 791 TriState EditingStyle::triStateOfStyle( |
| 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) { |
| 1262 EditingStyle* wrappingStyle = |
| 1263 EditingStyle::create(context, EditingStyle::EditingPropertiesInEffect); |
| 1264 |
| 1265 // 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 |
| 1267 // has applied. This helps us get the color of content pasted into |
| 1268 // blockquotes right. |
| 1269 wrappingStyle->removeStyleAddedByElement(toHTMLElement(enclosingNodeOfType( |
| 1270 firstPositionInOrBeforeNode(context), isMailHTMLBlockquoteElement, |
| 1271 CanCrossEditingBoundary))); |
| 1272 |
| 1273 // Call collapseTextDecorationProperties first or otherwise it'll copy the |
| 1274 // value over from in-effect to text-decorations. |
| 1275 wrappingStyle->collapseTextDecorationProperties(); |
| 1276 |
| 1277 return wrappingStyle; |
| 1278 } |
| 1279 |
| 1280 EditingStyle* EditingStyle::wrappingStyleForSerialization( |
| 1281 ContainerNode* context) { |
| 1282 DCHECK(context); |
| 1283 EditingStyle* wrappingStyle = EditingStyle::create(); |
| 1284 |
| 1285 // When not annotating for interchange, we only preserve inline style |
| 1286 // declarations. |
| 1287 for (Node& node : NodeTraversal::inclusiveAncestorsOf(*context)) { |
| 1288 if (node.isDocumentNode()) |
| 1289 break; |
| 1290 if (node.isStyledElement() && !isMailHTMLBlockquoteElement(&node)) { |
| 1291 wrappingStyle->mergeInlineAndImplicitStyleOfElement( |
| 1292 toElement(&node), EditingStyle::DoNotOverrideValues, |
| 1293 EditingStyle::EditingPropertiesInEffect); |
| 1294 } |
| 1295 } |
| 1296 |
| 1297 return wrappingStyle; |
| 1298 } |
| 1299 |
| 1300 static const CSSValueList& mergeTextDecorationValues( |
| 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, |
| 1541 bool shouldUseBackgroundColorInEffect, |
| 1542 MutableStylePropertySet* styleToCheck) { |
| 1543 if (selection.isNone()) |
| 1544 return nullptr; |
| 1545 |
| 1546 Document& document = *selection.start().document(); |
| 1547 |
| 1548 DCHECK(!document.needsLayoutTreeUpdate()); |
| 1549 DocumentLifecycle::DisallowTransitionScope disallowTransition( |
| 1550 document.lifecycle()); |
| 1551 |
| 1552 Position position = adjustedSelectionStartForStyleComputation(selection); |
| 1553 |
| 1554 // If the pos is at the end of a text node, then this node is not fully |
| 1555 // selected. Move it to the next deep equivalent position to avoid removing |
| 1556 // the style from this node. |
| 1557 // e.g. if pos was at Position("hello", 5) in <b>hello<div>world</div></b>, we |
| 1558 // want Position("world", 0) instead. |
| 1559 // We only do this for range because caret at Position("hello", 5) in |
| 1560 // <b>hello</b>world should give you font-weight: bold. |
| 1561 Node* positionNode = position.computeContainerNode(); |
| 1562 if (selection.isRange() && positionNode && positionNode->isTextNode() && |
| 1563 position.computeOffsetInContainerNode() == |
| 1564 positionNode->maxCharacterOffset()) |
| 1565 position = nextVisuallyDistinctCandidate(position); |
| 1566 |
| 1567 Element* element = associatedElementOf(position); |
| 1568 if (!element) |
| 1569 return nullptr; |
| 1570 |
| 1571 EditingStyle* style = |
| 1572 EditingStyle::create(element, EditingStyle::AllProperties); |
| 1573 style->mergeTypingStyle(&element->document()); |
| 1574 |
| 1575 // If |element| has <sub> or <sup> ancestor element, apply the corresponding |
| 1576 // style(vertical-align) to it so that document.queryCommandState() works with |
| 1577 // the style. See bug http://crbug.com/582225. |
| 1578 CSSValueID valueID = |
| 1579 getIdentifierValue(styleToCheck, CSSPropertyVerticalAlign); |
| 1580 if (valueID == CSSValueSub || valueID == CSSValueSuper) { |
| 1581 CSSComputedStyleDeclaration* elementStyle = |
| 1582 CSSComputedStyleDeclaration::create(element); |
| 1583 // Find the ancestor that has CSSValueSub or CSSValueSuper as the value of |
| 1584 // CSS vertical-align property. |
| 1585 if (getIdentifierValue(elementStyle, CSSPropertyVerticalAlign) == |
| 1586 CSSValueBaseline && |
| 1587 hasAncestorVerticalAlignStyle(*element, valueID)) |
| 1588 style->m_mutableStyle->setProperty(CSSPropertyVerticalAlign, valueID); |
| 1589 } |
| 1590 |
| 1591 // 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 |
| 1593 // the background color at the start of selection, and find the background |
| 1594 // color of the common ancestor. |
| 1595 if (shouldUseBackgroundColorInEffect && |
| 1596 (selection.isRange() || |
| 1597 hasTransparentBackgroundColor(style->m_mutableStyle.get()))) { |
| 1598 const EphemeralRange range(selection.toNormalizedEphemeralRange()); |
| 1599 if (const CSSValue* value = |
| 1600 backgroundColorValueInEffect(Range::commonAncestorContainer( |
| 1601 range.startPosition().computeContainerNode(), |
| 1602 range.endPosition().computeContainerNode()))) |
| 1603 style->setProperty(CSSPropertyBackgroundColor, value->cssText()); |
| 1604 } |
| 1605 |
| 1606 return style; |
| 1607 } |
| 1608 |
| 1609 static bool isUnicodeBidiNestedOrMultipleEmbeddings(CSSValueID valueID) { |
| 1610 return valueID == CSSValueEmbed || valueID == CSSValueBidiOverride || |
| 1611 valueID == CSSValueWebkitIsolate || |
| 1612 valueID == CSSValueWebkitIsolateOverride || |
| 1613 valueID == CSSValueWebkitPlaintext || valueID == CSSValueIsolate || |
| 1614 valueID == CSSValueIsolateOverride || valueID == CSSValuePlaintext; |
| 1615 } |
| 1616 |
| 1617 WritingDirection EditingStyle::textDirectionForSelection( |
| 1618 const VisibleSelection& selection, |
| 1619 EditingStyle* typingStyle, |
| 1620 bool& hasNestedOrMultipleEmbeddings) { |
| 1621 hasNestedOrMultipleEmbeddings = true; |
| 1622 |
| 1623 if (selection.isNone()) |
| 1624 return NaturalWritingDirection; |
| 1625 |
| 1626 Position position = mostForwardCaretPosition(selection.start()); |
| 1627 |
| 1628 Node* node = position.anchorNode(); |
| 1629 if (!node) |
| 1630 return NaturalWritingDirection; |
| 1631 |
| 1632 Position end; |
| 1633 if (selection.isRange()) { |
| 1634 end = mostBackwardCaretPosition(selection.end()); |
| 1635 |
| 1636 DCHECK(end.document()); |
| 1637 const EphemeralRange caretRange(position.parentAnchoredEquivalent(), |
| 1638 end.parentAnchoredEquivalent()); |
| 1639 for (Node& n : caretRange.nodes()) { |
| 1640 if (!n.isStyledElement()) |
| 1641 continue; |
| 1642 |
| 1643 CSSComputedStyleDeclaration* style = |
| 1644 CSSComputedStyleDeclaration::create(&n); |
| 1645 const CSSValue* unicodeBidi = |
| 1646 style->getPropertyCSSValue(CSSPropertyUnicodeBidi); |
| 1647 if (!unicodeBidi || !unicodeBidi->isIdentifierValue()) |
| 1648 continue; |
| 1649 |
| 1650 CSSValueID unicodeBidiValue = |
| 1651 toCSSIdentifierValue(unicodeBidi)->getValueID(); |
| 1652 if (isUnicodeBidiNestedOrMultipleEmbeddings(unicodeBidiValue)) |
| 1653 return NaturalWritingDirection; |
| 1654 } |
| 1655 } |
| 1656 |
| 1657 if (selection.isCaret()) { |
| 1658 WritingDirection direction; |
| 1659 if (typingStyle && typingStyle->textDirection(direction)) { |
| 1660 hasNestedOrMultipleEmbeddings = false; |
| 1661 return direction; |
| 1662 } |
| 1663 node = selection.visibleStart().deepEquivalent().anchorNode(); |
| 1664 } |
| 1665 DCHECK(node); |
| 1666 |
| 1667 // The selection is either a caret with no typing attributes or a range in |
| 1668 // which no embedding is added, so just use the start position to decide. |
| 1669 Node* block = enclosingBlock(node); |
| 1670 WritingDirection foundDirection = NaturalWritingDirection; |
| 1671 |
| 1672 for (Node& runner : NodeTraversal::inclusiveAncestorsOf(*node)) { |
| 1673 if (runner == block) |
| 1674 break; |
| 1675 if (!runner.isStyledElement()) |
| 1676 continue; |
| 1677 |
| 1678 Element* element = &toElement(runner); |
| 1679 CSSComputedStyleDeclaration* style = |
| 1680 CSSComputedStyleDeclaration::create(element); |
| 1681 const CSSValue* unicodeBidi = |
| 1682 style->getPropertyCSSValue(CSSPropertyUnicodeBidi); |
| 1683 if (!unicodeBidi || !unicodeBidi->isIdentifierValue()) |
| 1684 continue; |
| 1685 |
| 1686 CSSValueID unicodeBidiValue = |
| 1687 toCSSIdentifierValue(unicodeBidi)->getValueID(); |
| 1688 if (unicodeBidiValue == CSSValueNormal) |
| 1689 continue; |
| 1690 |
| 1691 if (unicodeBidiValue == CSSValueBidiOverride) |
| 1692 return NaturalWritingDirection; |
| 1693 |
| 1694 DCHECK(isEmbedOrIsolate(unicodeBidiValue)) << unicodeBidiValue; |
| 1695 const CSSValue* direction = |
| 1696 style->getPropertyCSSValue(CSSPropertyDirection); |
| 1697 if (!direction || !direction->isIdentifierValue()) |
| 1698 continue; |
| 1699 |
| 1700 int directionValue = toCSSIdentifierValue(direction)->getValueID(); |
| 1701 if (directionValue != CSSValueLtr && directionValue != CSSValueRtl) |
| 1702 continue; |
| 1703 |
| 1704 if (foundDirection != NaturalWritingDirection) |
| 1705 return NaturalWritingDirection; |
| 1706 |
| 1707 // In the range case, make sure that the embedding element persists until |
| 1708 // the end of the range. |
| 1709 if (selection.isRange() && !end.anchorNode()->isDescendantOf(element)) |
| 1710 return NaturalWritingDirection; |
| 1711 |
| 1712 foundDirection = directionValue == CSSValueLtr |
| 1713 ? LeftToRightWritingDirection |
| 1714 : RightToLeftWritingDirection; |
| 1715 } |
| 1716 hasNestedOrMultipleEmbeddings = false; |
| 1717 return foundDirection; |
| 1718 } |
| 1719 |
| 1720 DEFINE_TRACE(EditingStyle) { |
| 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) |
| 2033 return true; |
| 2034 if (cssValue->isColorValue()) |
| 2035 return !toCSSColorValue(cssValue)->value().alpha(); |
| 2036 if (!cssValue->isIdentifierValue()) |
| 2037 return false; |
| 2038 return toCSSIdentifierValue(cssValue)->getValueID() == CSSValueTransparent; |
| 2039 } |
| 2040 |
| 2041 bool hasTransparentBackgroundColor(CSSStyleDeclaration* style) { |
| 2042 const CSSValue* cssValue = |
| 2043 style->getPropertyCSSValueInternal(CSSPropertyBackgroundColor); |
| 2044 return isTransparentColorValue(cssValue); |
| 2045 } |
| 2046 |
| 2047 bool hasTransparentBackgroundColor(StylePropertySet* style) { |
| 2048 const CSSValue* cssValue = |
| 2049 style->getPropertyCSSValue(CSSPropertyBackgroundColor); |
| 2050 return isTransparentColorValue(cssValue); |
| 2051 } |
| 2052 |
| 2053 const CSSValue* backgroundColorValueInEffect(Node* node) { |
| 2054 for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) { |
| 2055 CSSComputedStyleDeclaration* ancestorStyle = |
| 2056 CSSComputedStyleDeclaration::create(ancestor); |
| 2057 if (!hasTransparentBackgroundColor(ancestorStyle)) |
| 2058 return ancestorStyle->getPropertyCSSValue(CSSPropertyBackgroundColor); |
| 2059 } |
| 2060 return nullptr; |
| 2061 } |
| 2062 |
| 2063 } // namespace blink |
| OLD | NEW |