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

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

Issue 2628943009: Introduce EditingStyleUtilities.{cpp,h} (Closed)
Patch Set: add STATIC_ONLY Created 3 years, 11 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
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "EditingStyleUtilities.h"
6
7 #include "core/css/CSSColorValue.h"
8 #include "core/css/CSSComputedStyleDeclaration.h"
9 #include "core/css/CSSIdentifierValue.h"
10 #include "core/css/StylePropertySet.h"
11 #include "core/css/parser/CSSParser.h"
12 #include "core/editing/EditingStyle.h"
13 #include "core/editing/EditingUtilities.h"
14
15 namespace blink {
16
17 EditingStyle* EditingStyleUtilities::createWrappingStyleForSerialization(
18 ContainerNode* context) {
19 DCHECK(context);
20 EditingStyle* wrappingStyle = EditingStyle::create();
21
22 // When not annotating for interchange, we only preserve inline style
23 // declarations.
24 for (Node& node : NodeTraversal::inclusiveAncestorsOf(*context)) {
25 if (node.isDocumentNode())
26 break;
27 if (node.isStyledElement() && !isMailHTMLBlockquoteElement(&node)) {
28 wrappingStyle->mergeInlineAndImplicitStyleOfElement(
29 toElement(&node), EditingStyle::DoNotOverrideValues,
30 EditingStyle::EditingPropertiesInEffect);
31 }
32 }
33
34 return wrappingStyle;
35 }
36
37 EditingStyle*
38 EditingStyleUtilities::createWrappingStyleForAnnotatedSerialization(
39 ContainerNode* context) {
40 EditingStyle* wrappingStyle =
41 EditingStyle::create(context, EditingStyle::EditingPropertiesInEffect);
42
43 // Styles that Mail blockquotes contribute should only be placed on the Mail
44 // blockquote, to help us differentiate those styles from ones that the user
45 // has applied. This helps us get the color of content pasted into
46 // blockquotes right.
47 wrappingStyle->removeStyleAddedByElement(toHTMLElement(enclosingNodeOfType(
48 firstPositionInOrBeforeNode(context), isMailHTMLBlockquoteElement,
49 CanCrossEditingBoundary)));
50
51 // Call collapseTextDecorationProperties first or otherwise it'll copy the
52 // value over from in-effect to text-decorations.
53 wrappingStyle->collapseTextDecorationProperties();
54
55 return wrappingStyle;
56 }
57
58 EditingStyle* EditingStyleUtilities::createStyleAtSelectionStart(
59 const VisibleSelection& selection,
60 bool shouldUseBackgroundColorInEffect,
61 MutableStylePropertySet* styleToCheck) {
62 if (selection.isNone())
63 return nullptr;
64
65 Document& document = *selection.start().document();
66
67 DCHECK(!document.needsLayoutTreeUpdate());
68 DocumentLifecycle::DisallowTransitionScope disallowTransition(
69 document.lifecycle());
70
71 Position position = adjustedSelectionStartForStyleComputation(selection);
72
73 // If the pos is at the end of a text node, then this node is not fully
74 // selected. Move it to the next deep equivalent position to avoid removing
75 // the style from this node.
76 // e.g. if pos was at Position("hello", 5) in <b>hello<div>world</div></b>, we
77 // want Position("world", 0) instead.
78 // We only do this for range because caret at Position("hello", 5) in
79 // <b>hello</b>world should give you font-weight: bold.
80 Node* positionNode = position.computeContainerNode();
81 if (selection.isRange() && positionNode && positionNode->isTextNode() &&
82 position.computeOffsetInContainerNode() ==
83 positionNode->maxCharacterOffset())
84 position = nextVisuallyDistinctCandidate(position);
85
86 Element* element = associatedElementOf(position);
87 if (!element)
88 return nullptr;
89
90 EditingStyle* style =
91 EditingStyle::create(element, EditingStyle::AllProperties);
92 style->mergeTypingStyle(&element->document());
93
94 // If |element| has <sub> or <sup> ancestor element, apply the corresponding
95 // style(vertical-align) to it so that document.queryCommandState() works with
96 // the style. See bug http://crbug.com/582225.
97 CSSValueID valueID =
98 getIdentifierValue(styleToCheck, CSSPropertyVerticalAlign);
99 if (valueID == CSSValueSub || valueID == CSSValueSuper) {
100 CSSComputedStyleDeclaration* elementStyle =
101 CSSComputedStyleDeclaration::create(element);
102 // Find the ancestor that has CSSValueSub or CSSValueSuper as the value of
103 // CSS vertical-align property.
104 if (getIdentifierValue(elementStyle, CSSPropertyVerticalAlign) ==
105 CSSValueBaseline &&
106 hasAncestorVerticalAlignStyle(*element, valueID))
107 style->style()->setProperty(CSSPropertyVerticalAlign, valueID);
108 }
109
110 // If background color is transparent, traverse parent nodes until we hit a
111 // different value or document root Also, if the selection is a range, ignore
112 // the background color at the start of selection, and find the background
113 // color of the common ancestor.
114 if (shouldUseBackgroundColorInEffect &&
115 (selection.isRange() || hasTransparentBackgroundColor(style->style()))) {
116 const EphemeralRange range(selection.toNormalizedEphemeralRange());
117 if (const CSSValue* value =
118 backgroundColorValueInEffect(Range::commonAncestorContainer(
119 range.startPosition().computeContainerNode(),
120 range.endPosition().computeContainerNode())))
121 style->setProperty(CSSPropertyBackgroundColor, value->cssText());
122 }
123
124 return style;
125 }
126
127 bool EditingStyleUtilities::hasAncestorVerticalAlignStyle(Node& node,
128 CSSValueID value) {
129 for (Node& runner : NodeTraversal::inclusiveAncestorsOf(node)) {
130 CSSComputedStyleDeclaration* ancestorStyle =
131 CSSComputedStyleDeclaration::create(&runner);
132 if (getIdentifierValue(ancestorStyle, CSSPropertyVerticalAlign) == value)
133 return true;
134 }
135 return false;
136 }
137
138 static bool isUnicodeBidiNestedOrMultipleEmbeddings(CSSValueID valueID) {
139 return valueID == CSSValueEmbed || valueID == CSSValueBidiOverride ||
140 valueID == CSSValueWebkitIsolate ||
141 valueID == CSSValueWebkitIsolateOverride ||
142 valueID == CSSValueWebkitPlaintext || valueID == CSSValueIsolate ||
143 valueID == CSSValueIsolateOverride || valueID == CSSValuePlaintext;
144 }
145
146 WritingDirection EditingStyleUtilities::textDirectionForSelection(
147 const VisibleSelection& selection,
148 EditingStyle* typingStyle,
149 bool& hasNestedOrMultipleEmbeddings) {
150 hasNestedOrMultipleEmbeddings = true;
151
152 if (selection.isNone())
153 return NaturalWritingDirection;
154
155 Position position = mostForwardCaretPosition(selection.start());
156
157 Node* node = position.anchorNode();
158 if (!node)
159 return NaturalWritingDirection;
160
161 Position end;
162 if (selection.isRange()) {
163 end = mostBackwardCaretPosition(selection.end());
164
165 DCHECK(end.document());
166 const EphemeralRange caretRange(position.parentAnchoredEquivalent(),
167 end.parentAnchoredEquivalent());
168 for (Node& n : caretRange.nodes()) {
169 if (!n.isStyledElement())
170 continue;
171
172 CSSComputedStyleDeclaration* style =
173 CSSComputedStyleDeclaration::create(&n);
174 const CSSValue* unicodeBidi =
175 style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
176 if (!unicodeBidi || !unicodeBidi->isIdentifierValue())
177 continue;
178
179 CSSValueID unicodeBidiValue =
180 toCSSIdentifierValue(unicodeBidi)->getValueID();
181 if (isUnicodeBidiNestedOrMultipleEmbeddings(unicodeBidiValue))
182 return NaturalWritingDirection;
183 }
184 }
185
186 if (selection.isCaret()) {
187 WritingDirection direction;
188 if (typingStyle && typingStyle->textDirection(direction)) {
189 hasNestedOrMultipleEmbeddings = false;
190 return direction;
191 }
192 node = selection.visibleStart().deepEquivalent().anchorNode();
193 }
194 DCHECK(node);
195
196 // The selection is either a caret with no typing attributes or a range in
197 // which no embedding is added, so just use the start position to decide.
198 Node* block = enclosingBlock(node);
199 WritingDirection foundDirection = NaturalWritingDirection;
200
201 for (Node& runner : NodeTraversal::inclusiveAncestorsOf(*node)) {
202 if (runner == block)
203 break;
204 if (!runner.isStyledElement())
205 continue;
206
207 Element* element = &toElement(runner);
208 CSSComputedStyleDeclaration* style =
209 CSSComputedStyleDeclaration::create(element);
210 const CSSValue* unicodeBidi =
211 style->getPropertyCSSValue(CSSPropertyUnicodeBidi);
212 if (!unicodeBidi || !unicodeBidi->isIdentifierValue())
213 continue;
214
215 CSSValueID unicodeBidiValue =
216 toCSSIdentifierValue(unicodeBidi)->getValueID();
217 if (unicodeBidiValue == CSSValueNormal)
218 continue;
219
220 if (unicodeBidiValue == CSSValueBidiOverride)
221 return NaturalWritingDirection;
222
223 DCHECK(isEmbedOrIsolate(unicodeBidiValue)) << unicodeBidiValue;
224 const CSSValue* direction =
225 style->getPropertyCSSValue(CSSPropertyDirection);
226 if (!direction || !direction->isIdentifierValue())
227 continue;
228
229 int directionValue = toCSSIdentifierValue(direction)->getValueID();
230 if (directionValue != CSSValueLtr && directionValue != CSSValueRtl)
231 continue;
232
233 if (foundDirection != NaturalWritingDirection)
234 return NaturalWritingDirection;
235
236 // In the range case, make sure that the embedding element persists until
237 // the end of the range.
238 if (selection.isRange() && !end.anchorNode()->isDescendantOf(element))
239 return NaturalWritingDirection;
240
241 foundDirection = directionValue == CSSValueLtr
242 ? LeftToRightWritingDirection
243 : RightToLeftWritingDirection;
244 }
245 hasNestedOrMultipleEmbeddings = false;
246 return foundDirection;
247 }
248
249 bool EditingStyleUtilities::isTransparentColorValue(const CSSValue* cssValue) {
250 if (!cssValue)
251 return true;
252 if (cssValue->isColorValue())
253 return !toCSSColorValue(cssValue)->value().alpha();
254 if (!cssValue->isIdentifierValue())
255 return false;
256 return toCSSIdentifierValue(cssValue)->getValueID() == CSSValueTransparent;
257 }
258
259 bool EditingStyleUtilities::hasTransparentBackgroundColor(
260 CSSStyleDeclaration* style) {
261 const CSSValue* cssValue =
262 style->getPropertyCSSValueInternal(CSSPropertyBackgroundColor);
263 return isTransparentColorValue(cssValue);
264 }
265
266 bool EditingStyleUtilities::hasTransparentBackgroundColor(
267 StylePropertySet* style) {
268 const CSSValue* cssValue =
269 style->getPropertyCSSValue(CSSPropertyBackgroundColor);
270 return isTransparentColorValue(cssValue);
271 }
272
273 const CSSValue* EditingStyleUtilities::backgroundColorValueInEffect(
274 Node* node) {
275 for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) {
276 CSSComputedStyleDeclaration* ancestorStyle =
277 CSSComputedStyleDeclaration::create(ancestor);
278 if (!hasTransparentBackgroundColor(ancestorStyle))
279 return ancestorStyle->getPropertyCSSValue(CSSPropertyBackgroundColor);
280 }
281 return nullptr;
282 }
283
284 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/editing/EditingStyleUtilities.h ('k') | third_party/WebKit/Source/core/editing/Editor.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698