Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(664)

Side by Side Diff: third_party/WebKit/Source/core/editing/EditingStyleUtilities.cpp

Issue 2685783004: Add EditingStyleUtitilies.{cpp,h} to BUILD.gn (Closed)
Patch Set: keep the original copyright comment Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/WebKit/Source/core/editing/EditingStyleUtilities.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2007, 2008, 2009 Apple Computer, Inc. 2 * Copyright (C) 2007, 2008, 2009 Apple Computer, Inc.
3 * Copyright (C) 2010, 2011 Google Inc. All rights reserved. 3 * Copyright (C) 2010, 2011 Google Inc. All rights reserved.
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions 6 * modification, are permitted provided that the following conditions
7 * are met: 7 * are met:
8 * 1. Redistributions of source code must retain the above copyright 8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright 10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the 11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution. 12 * documentation and/or other materials provided with the distribution.
13 * 13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 15 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 16 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 17 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 19 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 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. 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */ 25 */
26 26
27 #include "core/editing/EditingStyle.h" 27 #include "EditingStyleUtilities.h"
28 28
29 #include "bindings/core/v8/ExceptionState.h"
30 #include "core/HTMLNames.h"
31 #include "core/css/CSSColorValue.h" 29 #include "core/css/CSSColorValue.h"
32 #include "core/css/CSSComputedStyleDeclaration.h" 30 #include "core/css/CSSComputedStyleDeclaration.h"
33 #include "core/css/CSSIdentifierValue.h" 31 #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" 32 #include "core/css/StylePropertySet.h"
42 #include "core/css/StyleRule.h"
43 #include "core/css/parser/CSSParser.h" 33 #include "core/css/parser/CSSParser.h"
44 #include "core/css/resolver/StyleResolver.h" 34 #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" 35 #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 36
65 namespace blink { 37 namespace blink {
66 38
67 static const CSSPropertyID& textDecorationPropertyForEditing() { 39 bool EditingStyleUtilities::hasAncestorVerticalAlignStyle(Node& node,
68 static const CSSPropertyID property = 40 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)) { 41 for (Node& runner : NodeTraversal::inclusiveAncestorsOf(node)) {
783 CSSComputedStyleDeclaration* ancestorStyle = 42 CSSComputedStyleDeclaration* ancestorStyle =
784 CSSComputedStyleDeclaration::create(&runner); 43 CSSComputedStyleDeclaration::create(&runner);
785 if (getIdentifierValue(ancestorStyle, CSSPropertyVerticalAlign) == value) 44 if (getIdentifierValue(ancestorStyle, CSSPropertyVerticalAlign) == value)
786 return true; 45 return true;
787 } 46 }
788 return false; 47 return false;
789 } 48 }
790 49
791 TriState EditingStyle::triStateOfStyle( 50 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) { 51 ContainerNode* context) {
1262 EditingStyle* wrappingStyle = 52 EditingStyle* wrappingStyle =
1263 EditingStyle::create(context, EditingStyle::EditingPropertiesInEffect); 53 EditingStyle::create(context, EditingStyle::EditingPropertiesInEffect);
1264 54
1265 // Styles that Mail blockquotes contribute should only be placed on the Mail 55 // 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 56 // 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 57 // has applied. This helps us get the color of content pasted into
1268 // blockquotes right. 58 // blockquotes right.
1269 wrappingStyle->removeStyleAddedByElement(toHTMLElement(enclosingNodeOfType( 59 wrappingStyle->removeStyleAddedByElement(toHTMLElement(enclosingNodeOfType(
1270 firstPositionInOrBeforeNode(context), isMailHTMLBlockquoteElement, 60 firstPositionInOrBeforeNode(context), isMailHTMLBlockquoteElement,
1271 CanCrossEditingBoundary))); 61 CanCrossEditingBoundary)));
1272 62
1273 // Call collapseTextDecorationProperties first or otherwise it'll copy the 63 // Call collapseTextDecorationProperties first or otherwise it'll copy the
1274 // value over from in-effect to text-decorations. 64 // value over from in-effect to text-decorations.
1275 wrappingStyle->collapseTextDecorationProperties(); 65 wrappingStyle->collapseTextDecorationProperties();
1276 66
1277 return wrappingStyle; 67 return wrappingStyle;
1278 } 68 }
1279 69
1280 EditingStyle* EditingStyle::wrappingStyleForSerialization( 70 EditingStyle* EditingStyleUtilities::wrappingStyleForSerialization(
1281 ContainerNode* context) { 71 ContainerNode* context) {
1282 DCHECK(context); 72 DCHECK(context);
1283 EditingStyle* wrappingStyle = EditingStyle::create(); 73 EditingStyle* wrappingStyle = EditingStyle::create();
1284 74
1285 // When not annotating for interchange, we only preserve inline style 75 // When not annotating for interchange, we only preserve inline style
1286 // declarations. 76 // declarations.
1287 for (Node& node : NodeTraversal::inclusiveAncestorsOf(*context)) { 77 for (Node& node : NodeTraversal::inclusiveAncestorsOf(*context)) {
1288 if (node.isDocumentNode()) 78 if (node.isDocumentNode())
1289 break; 79 break;
1290 if (node.isStyledElement() && !isMailHTMLBlockquoteElement(&node)) { 80 if (node.isStyledElement() && !isMailHTMLBlockquoteElement(&node)) {
1291 wrappingStyle->mergeInlineAndImplicitStyleOfElement( 81 wrappingStyle->mergeInlineAndImplicitStyleOfElement(
1292 toElement(&node), EditingStyle::DoNotOverrideValues, 82 toElement(&node), EditingStyle::DoNotOverrideValues,
1293 EditingStyle::EditingPropertiesInEffect); 83 EditingStyle::EditingPropertiesInEffect);
1294 } 84 }
1295 } 85 }
1296 86
1297 return wrappingStyle; 87 return wrappingStyle;
1298 } 88 }
1299 89
1300 static const CSSValueList& mergeTextDecorationValues( 90 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, 91 const VisibleSelection& selection,
1541 bool shouldUseBackgroundColorInEffect, 92 bool shouldUseBackgroundColorInEffect,
1542 MutableStylePropertySet* styleToCheck) { 93 MutableStylePropertySet* styleToCheck) {
1543 if (selection.isNone()) 94 if (selection.isNone())
1544 return nullptr; 95 return nullptr;
1545 96
1546 Document& document = *selection.start().document(); 97 Document& document = *selection.start().document();
1547 98
1548 DCHECK(!document.needsLayoutTreeUpdate()); 99 DCHECK(!document.needsLayoutTreeUpdate());
1549 DocumentLifecycle::DisallowTransitionScope disallowTransition( 100 DocumentLifecycle::DisallowTransitionScope disallowTransition(
(...skipping 28 matching lines...) Expand all
1578 CSSValueID valueID = 129 CSSValueID valueID =
1579 getIdentifierValue(styleToCheck, CSSPropertyVerticalAlign); 130 getIdentifierValue(styleToCheck, CSSPropertyVerticalAlign);
1580 if (valueID == CSSValueSub || valueID == CSSValueSuper) { 131 if (valueID == CSSValueSub || valueID == CSSValueSuper) {
1581 CSSComputedStyleDeclaration* elementStyle = 132 CSSComputedStyleDeclaration* elementStyle =
1582 CSSComputedStyleDeclaration::create(element); 133 CSSComputedStyleDeclaration::create(element);
1583 // Find the ancestor that has CSSValueSub or CSSValueSuper as the value of 134 // Find the ancestor that has CSSValueSub or CSSValueSuper as the value of
1584 // CSS vertical-align property. 135 // CSS vertical-align property.
1585 if (getIdentifierValue(elementStyle, CSSPropertyVerticalAlign) == 136 if (getIdentifierValue(elementStyle, CSSPropertyVerticalAlign) ==
1586 CSSValueBaseline && 137 CSSValueBaseline &&
1587 hasAncestorVerticalAlignStyle(*element, valueID)) 138 hasAncestorVerticalAlignStyle(*element, valueID))
1588 style->m_mutableStyle->setProperty(CSSPropertyVerticalAlign, valueID); 139 style->style()->setProperty(CSSPropertyVerticalAlign, valueID);
1589 } 140 }
1590 141
1591 // If background color is transparent, traverse parent nodes until we hit a 142 // 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 143 // 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 144 // the background color at the start of selection, and find the background
1594 // color of the common ancestor. 145 // color of the common ancestor.
1595 if (shouldUseBackgroundColorInEffect && 146 if (shouldUseBackgroundColorInEffect &&
1596 (selection.isRange() || 147 (selection.isRange() || hasTransparentBackgroundColor(style->style()))) {
1597 hasTransparentBackgroundColor(style->m_mutableStyle.get()))) {
1598 const EphemeralRange range(selection.toNormalizedEphemeralRange()); 148 const EphemeralRange range(selection.toNormalizedEphemeralRange());
1599 if (const CSSValue* value = 149 if (const CSSValue* value =
1600 backgroundColorValueInEffect(Range::commonAncestorContainer( 150 backgroundColorValueInEffect(Range::commonAncestorContainer(
1601 range.startPosition().computeContainerNode(), 151 range.startPosition().computeContainerNode(),
1602 range.endPosition().computeContainerNode()))) 152 range.endPosition().computeContainerNode())))
1603 style->setProperty(CSSPropertyBackgroundColor, value->cssText()); 153 style->setProperty(CSSPropertyBackgroundColor, value->cssText());
1604 } 154 }
1605 155
1606 return style; 156 return style;
1607 } 157 }
1608 158
1609 static bool isUnicodeBidiNestedOrMultipleEmbeddings(CSSValueID valueID) { 159 static bool isUnicodeBidiNestedOrMultipleEmbeddings(CSSValueID valueID) {
1610 return valueID == CSSValueEmbed || valueID == CSSValueBidiOverride || 160 return valueID == CSSValueEmbed || valueID == CSSValueBidiOverride ||
1611 valueID == CSSValueWebkitIsolate || 161 valueID == CSSValueWebkitIsolate ||
1612 valueID == CSSValueWebkitIsolateOverride || 162 valueID == CSSValueWebkitIsolateOverride ||
1613 valueID == CSSValueWebkitPlaintext || valueID == CSSValueIsolate || 163 valueID == CSSValueWebkitPlaintext || valueID == CSSValueIsolate ||
1614 valueID == CSSValueIsolateOverride || valueID == CSSValuePlaintext; 164 valueID == CSSValueIsolateOverride || valueID == CSSValuePlaintext;
1615 } 165 }
1616 166
1617 WritingDirection EditingStyle::textDirectionForSelection( 167 WritingDirection EditingStyleUtilities::textDirectionForSelection(
1618 const VisibleSelection& selection, 168 const VisibleSelection& selection,
1619 EditingStyle* typingStyle, 169 EditingStyle* typingStyle,
1620 bool& hasNestedOrMultipleEmbeddings) { 170 bool& hasNestedOrMultipleEmbeddings) {
1621 hasNestedOrMultipleEmbeddings = true; 171 hasNestedOrMultipleEmbeddings = true;
1622 172
1623 if (selection.isNone()) 173 if (selection.isNone())
1624 return NaturalWritingDirection; 174 return NaturalWritingDirection;
1625 175
1626 Position position = mostForwardCaretPosition(selection.start()); 176 Position position = mostForwardCaretPosition(selection.start());
1627 177
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
1710 return NaturalWritingDirection; 260 return NaturalWritingDirection;
1711 261
1712 foundDirection = directionValue == CSSValueLtr 262 foundDirection = directionValue == CSSValueLtr
1713 ? LeftToRightWritingDirection 263 ? LeftToRightWritingDirection
1714 : RightToLeftWritingDirection; 264 : RightToLeftWritingDirection;
1715 } 265 }
1716 hasNestedOrMultipleEmbeddings = false; 266 hasNestedOrMultipleEmbeddings = false;
1717 return foundDirection; 267 return foundDirection;
1718 } 268 }
1719 269
1720 DEFINE_TRACE(EditingStyle) { 270 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) 271 if (!cssValue)
2033 return true; 272 return true;
2034 if (cssValue->isColorValue()) 273 if (cssValue->isColorValue())
2035 return !toCSSColorValue(cssValue)->value().alpha(); 274 return !toCSSColorValue(cssValue)->value().alpha();
2036 if (!cssValue->isIdentifierValue()) 275 if (!cssValue->isIdentifierValue())
2037 return false; 276 return false;
2038 return toCSSIdentifierValue(cssValue)->getValueID() == CSSValueTransparent; 277 return toCSSIdentifierValue(cssValue)->getValueID() == CSSValueTransparent;
2039 } 278 }
2040 279
2041 bool hasTransparentBackgroundColor(CSSStyleDeclaration* style) { 280 bool EditingStyleUtilities::hasTransparentBackgroundColor(
281 CSSStyleDeclaration* style) {
2042 const CSSValue* cssValue = 282 const CSSValue* cssValue =
2043 style->getPropertyCSSValueInternal(CSSPropertyBackgroundColor); 283 style->getPropertyCSSValueInternal(CSSPropertyBackgroundColor);
2044 return isTransparentColorValue(cssValue); 284 return isTransparentColorValue(cssValue);
2045 } 285 }
2046 286
2047 bool hasTransparentBackgroundColor(StylePropertySet* style) { 287 bool EditingStyleUtilities::hasTransparentBackgroundColor(
288 StylePropertySet* style) {
2048 const CSSValue* cssValue = 289 const CSSValue* cssValue =
2049 style->getPropertyCSSValue(CSSPropertyBackgroundColor); 290 style->getPropertyCSSValue(CSSPropertyBackgroundColor);
2050 return isTransparentColorValue(cssValue); 291 return isTransparentColorValue(cssValue);
2051 } 292 }
2052 293
2053 const CSSValue* backgroundColorValueInEffect(Node* node) { 294 const CSSValue* EditingStyleUtilities::backgroundColorValueInEffect(
295 Node* node) {
2054 for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) { 296 for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) {
2055 CSSComputedStyleDeclaration* ancestorStyle = 297 CSSComputedStyleDeclaration* ancestorStyle =
2056 CSSComputedStyleDeclaration::create(ancestor); 298 CSSComputedStyleDeclaration::create(ancestor);
2057 if (!hasTransparentBackgroundColor(ancestorStyle)) 299 if (!hasTransparentBackgroundColor(ancestorStyle))
2058 return ancestorStyle->getPropertyCSSValue(CSSPropertyBackgroundColor); 300 return ancestorStyle->getPropertyCSSValue(CSSPropertyBackgroundColor);
2059 } 301 }
2060 return nullptr; 302 return nullptr;
2061 } 303 }
2062 304
2063 } // namespace blink 305 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/editing/EditingStyleUtilities.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698