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

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

Issue 2923153002: Move "paragraph" granularity related functions to VisibleUnitParagraph.cpp (Closed)
Patch Set: 2017-06-06T15:30:01 Created 3 years, 6 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/VisibleUnits.cpp ('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
(Empty)
1 /*
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 // Copyright 2017 The Chromium Authors. All rights reserved.
28 // Use of this source code is governed by a BSD-style license that can be
29 // found in the LICENSE file.
30
31 #include "core/editing/VisibleUnits.h"
32
33 #include "core/editing/EditingUtilities.h"
34 #include "core/layout/LayoutText.h"
35 #include "core/layout/api/LayoutItem.h"
36
37 namespace blink {
38
39 namespace {
40
41 bool NodeIsUserSelectAll(const Node* node) {
42 return node && node->GetLayoutObject() &&
43 node->GetLayoutObject()->Style()->UserSelect() == EUserSelect::kAll;
44 }
45
46 template <typename Strategy>
47 PositionTemplate<Strategy> StartOfParagraphAlgorithm(
48 const PositionTemplate<Strategy>& position,
49 EditingBoundaryCrossingRule boundary_crossing_rule) {
50 Node* const start_node = position.AnchorNode();
51
52 if (!start_node)
53 return PositionTemplate<Strategy>();
54
55 if (IsRenderedAsNonInlineTableImageOrHR(start_node))
56 return PositionTemplate<Strategy>::BeforeNode(start_node);
57
58 Element* const start_block = EnclosingBlock(
59 PositionTemplate<Strategy>::FirstPositionInOrBeforeNode(start_node),
60 kCannotCrossEditingBoundary);
61 ContainerNode* const highest_root = HighestEditableRoot(position);
62 const bool start_node_is_editable = HasEditableStyle(*start_node);
63
64 Node* candidate_node = start_node;
65 PositionAnchorType candidate_type = position.AnchorType();
66 int candidate_offset = position.ComputeEditingOffset();
67
68 Node* previous_node_iterator = start_node;
69 while (previous_node_iterator) {
70 if (boundary_crossing_rule == kCannotCrossEditingBoundary &&
71 !NodeIsUserSelectAll(previous_node_iterator) &&
72 HasEditableStyle(*previous_node_iterator) != start_node_is_editable)
73 break;
74 if (boundary_crossing_rule == kCanSkipOverEditingBoundary) {
75 while (previous_node_iterator &&
76 HasEditableStyle(*previous_node_iterator) !=
77 start_node_is_editable) {
78 previous_node_iterator =
79 Strategy::PreviousPostOrder(*previous_node_iterator, start_block);
80 }
81 if (!previous_node_iterator ||
82 !previous_node_iterator->IsDescendantOf(highest_root))
83 break;
84 }
85
86 const LayoutItem layout_item =
87 LayoutItem(previous_node_iterator->GetLayoutObject());
88 if (layout_item.IsNull()) {
89 previous_node_iterator =
90 Strategy::PreviousPostOrder(*previous_node_iterator, start_block);
91 continue;
92 }
93 const ComputedStyle& style = layout_item.StyleRef();
94 if (style.Visibility() != EVisibility::kVisible) {
95 previous_node_iterator =
96 Strategy::PreviousPostOrder(*previous_node_iterator, start_block);
97 continue;
98 }
99
100 if (layout_item.IsBR() || IsEnclosingBlock(previous_node_iterator))
101 break;
102
103 if (layout_item.IsText() &&
104 ToLayoutText(previous_node_iterator->GetLayoutObject())
105 ->ResolvedTextLength()) {
106 SECURITY_DCHECK(previous_node_iterator->IsTextNode());
107 if (style.PreserveNewline()) {
108 LayoutText* text =
109 ToLayoutText(previous_node_iterator->GetLayoutObject());
110 int index = text->TextLength();
111 if (previous_node_iterator == start_node && candidate_offset < index)
112 index = max(0, candidate_offset);
113 while (--index >= 0) {
114 if ((*text)[index] == '\n') {
115 return PositionTemplate<Strategy>(ToText(previous_node_iterator),
116 index + 1);
117 }
118 }
119 }
120 candidate_node = previous_node_iterator;
121 candidate_type = PositionAnchorType::kOffsetInAnchor;
122 candidate_offset = 0;
123 previous_node_iterator =
124 Strategy::PreviousPostOrder(*previous_node_iterator, start_block);
125 } else if (EditingIgnoresContent(*previous_node_iterator) ||
126 IsDisplayInsideTable(previous_node_iterator)) {
127 candidate_node = previous_node_iterator;
128 candidate_type = PositionAnchorType::kBeforeAnchor;
129 previous_node_iterator = previous_node_iterator->previousSibling()
130 ? previous_node_iterator->previousSibling()
131 : Strategy::PreviousPostOrder(
132 *previous_node_iterator, start_block);
133 } else {
134 previous_node_iterator =
135 Strategy::PreviousPostOrder(*previous_node_iterator, start_block);
136 }
137 }
138
139 if (candidate_type == PositionAnchorType::kOffsetInAnchor)
140 return PositionTemplate<Strategy>(candidate_node, candidate_offset);
141
142 return PositionTemplate<Strategy>(candidate_node, candidate_type);
143 }
144
145 template <typename Strategy>
146 VisiblePositionTemplate<Strategy> StartOfParagraphAlgorithm(
147 const VisiblePositionTemplate<Strategy>& visible_position,
148 EditingBoundaryCrossingRule boundary_crossing_rule) {
149 DCHECK(visible_position.IsValid()) << visible_position;
150 return CreateVisiblePosition(StartOfParagraphAlgorithm(
151 visible_position.DeepEquivalent(), boundary_crossing_rule));
152 }
153
154 template <typename Strategy>
155 PositionTemplate<Strategy> EndOfParagraphAlgorithm(
156 const PositionTemplate<Strategy>& position,
157 EditingBoundaryCrossingRule boundary_crossing_rule) {
158 Node* const start_node = position.AnchorNode();
159
160 if (!start_node)
161 return PositionTemplate<Strategy>();
162
163 if (IsRenderedAsNonInlineTableImageOrHR(start_node))
164 return PositionTemplate<Strategy>::AfterNode(start_node);
165
166 Element* const start_block = EnclosingBlock(
167 PositionTemplate<Strategy>::FirstPositionInOrBeforeNode(start_node),
168 kCannotCrossEditingBoundary);
169 ContainerNode* const highest_root = HighestEditableRoot(position);
170 const bool start_node_is_editable = HasEditableStyle(*start_node);
171
172 Node* candidate_node = start_node;
173 PositionAnchorType candidate_type = position.AnchorType();
174 int candidate_offset = position.ComputeEditingOffset();
175
176 Node* next_node_iterator = start_node;
177 while (next_node_iterator) {
178 if (boundary_crossing_rule == kCannotCrossEditingBoundary &&
179 !NodeIsUserSelectAll(next_node_iterator) &&
180 HasEditableStyle(*next_node_iterator) != start_node_is_editable)
181 break;
182 if (boundary_crossing_rule == kCanSkipOverEditingBoundary) {
183 while (next_node_iterator &&
184 HasEditableStyle(*next_node_iterator) != start_node_is_editable)
185 next_node_iterator = Strategy::Next(*next_node_iterator, start_block);
186 if (!next_node_iterator ||
187 !next_node_iterator->IsDescendantOf(highest_root))
188 break;
189 }
190
191 LayoutObject* const layout_object = next_node_iterator->GetLayoutObject();
192 if (!layout_object) {
193 next_node_iterator = Strategy::Next(*next_node_iterator, start_block);
194 continue;
195 }
196 const ComputedStyle& style = layout_object->StyleRef();
197 if (style.Visibility() != EVisibility::kVisible) {
198 next_node_iterator = Strategy::Next(*next_node_iterator, start_block);
199 continue;
200 }
201
202 if (layout_object->IsBR() || IsEnclosingBlock(next_node_iterator))
203 break;
204
205 // TODO(editing-dev): We avoid returning a position where the layoutObject
206 // can't accept the caret.
207 if (layout_object->IsText() &&
208 ToLayoutText(layout_object)->ResolvedTextLength()) {
209 SECURITY_DCHECK(next_node_iterator->IsTextNode());
210 LayoutText* const text = ToLayoutText(layout_object);
211 if (style.PreserveNewline()) {
212 const int length = ToLayoutText(layout_object)->TextLength();
213 for (int i = (next_node_iterator == start_node ? candidate_offset : 0);
214 i < length; ++i) {
215 if ((*text)[i] == '\n') {
216 return PositionTemplate<Strategy>(ToText(next_node_iterator),
217 i + text->TextStartOffset());
218 }
219 }
220 }
221
222 candidate_node = next_node_iterator;
223 candidate_type = PositionAnchorType::kOffsetInAnchor;
224 candidate_offset =
225 layout_object->CaretMaxOffset() + text->TextStartOffset();
226 next_node_iterator = Strategy::Next(*next_node_iterator, start_block);
227 } else if (EditingIgnoresContent(*next_node_iterator) ||
228 IsDisplayInsideTable(next_node_iterator)) {
229 candidate_node = next_node_iterator;
230 candidate_type = PositionAnchorType::kAfterAnchor;
231 next_node_iterator =
232 Strategy::NextSkippingChildren(*next_node_iterator, start_block);
233 } else {
234 next_node_iterator = Strategy::Next(*next_node_iterator, start_block);
235 }
236 }
237
238 if (candidate_type == PositionAnchorType::kOffsetInAnchor)
239 return PositionTemplate<Strategy>(candidate_node, candidate_offset);
240
241 return PositionTemplate<Strategy>(candidate_node, candidate_type);
242 }
243
244 template <typename Strategy>
245 VisiblePositionTemplate<Strategy> EndOfParagraphAlgorithm(
246 const VisiblePositionTemplate<Strategy>& visible_position,
247 EditingBoundaryCrossingRule boundary_crossing_rule) {
248 DCHECK(visible_position.IsValid()) << visible_position;
249 return CreateVisiblePosition(EndOfParagraphAlgorithm(
250 visible_position.DeepEquivalent(), boundary_crossing_rule));
251 }
252
253 template <typename Strategy>
254 bool IsStartOfParagraphAlgorithm(
255 const VisiblePositionTemplate<Strategy>& pos,
256 EditingBoundaryCrossingRule boundary_crossing_rule) {
257 DCHECK(pos.IsValid()) << pos;
258 return pos.IsNotNull() &&
259 pos.DeepEquivalent() ==
260 StartOfParagraph(pos, boundary_crossing_rule).DeepEquivalent();
261 }
262
263 template <typename Strategy>
264 bool IsEndOfParagraphAlgorithm(
265 const VisiblePositionTemplate<Strategy>& pos,
266 EditingBoundaryCrossingRule boundary_crossing_rule) {
267 DCHECK(pos.IsValid()) << pos;
268 return pos.IsNotNull() &&
269 pos.DeepEquivalent() ==
270 EndOfParagraph(pos, boundary_crossing_rule).DeepEquivalent();
271 }
272
273 } // namespace
274
275 VisiblePosition StartOfParagraph(
276 const VisiblePosition& c,
277 EditingBoundaryCrossingRule boundary_crossing_rule) {
278 return StartOfParagraphAlgorithm<EditingStrategy>(c, boundary_crossing_rule);
279 }
280
281 VisiblePositionInFlatTree StartOfParagraph(
282 const VisiblePositionInFlatTree& c,
283 EditingBoundaryCrossingRule boundary_crossing_rule) {
284 return StartOfParagraphAlgorithm<EditingInFlatTreeStrategy>(
285 c, boundary_crossing_rule);
286 }
287
288 VisiblePosition EndOfParagraph(
289 const VisiblePosition& c,
290 EditingBoundaryCrossingRule boundary_crossing_rule) {
291 return EndOfParagraphAlgorithm<EditingStrategy>(c, boundary_crossing_rule);
292 }
293
294 VisiblePositionInFlatTree EndOfParagraph(
295 const VisiblePositionInFlatTree& c,
296 EditingBoundaryCrossingRule boundary_crossing_rule) {
297 return EndOfParagraphAlgorithm<EditingInFlatTreeStrategy>(
298 c, boundary_crossing_rule);
299 }
300
301 // TODO(editing-dev): isStartOfParagraph(startOfNextParagraph(pos)) is not
302 // always true
303 VisiblePosition StartOfNextParagraph(const VisiblePosition& visible_position) {
304 DCHECK(visible_position.IsValid()) << visible_position;
305 VisiblePosition paragraph_end(
306 EndOfParagraph(visible_position, kCanSkipOverEditingBoundary));
307 VisiblePosition after_paragraph_end(
308 NextPositionOf(paragraph_end, kCannotCrossEditingBoundary));
309 // The position after the last position in the last cell of a table
310 // is not the start of the next paragraph.
311 if (TableElementJustBefore(after_paragraph_end))
312 return NextPositionOf(after_paragraph_end, kCannotCrossEditingBoundary);
313 return after_paragraph_end;
314 }
315
316 // TODO(editing-dev): isStartOfParagraph(startOfNextParagraph(pos)) is not
317 // always true
318 bool InSameParagraph(const VisiblePosition& a,
319 const VisiblePosition& b,
320 EditingBoundaryCrossingRule boundary_crossing_rule) {
321 DCHECK(a.IsValid()) << a;
322 DCHECK(b.IsValid()) << b;
323 return a.IsNotNull() &&
324 StartOfParagraph(a, boundary_crossing_rule).DeepEquivalent() ==
325 StartOfParagraph(b, boundary_crossing_rule).DeepEquivalent();
326 }
327
328 bool IsStartOfParagraph(const VisiblePosition& pos,
329 EditingBoundaryCrossingRule boundary_crossing_rule) {
330 return IsStartOfParagraphAlgorithm<EditingStrategy>(pos,
331 boundary_crossing_rule);
332 }
333
334 bool IsStartOfParagraph(const VisiblePositionInFlatTree& pos,
335 EditingBoundaryCrossingRule boundary_crossing_rule) {
336 return IsStartOfParagraphAlgorithm<EditingInFlatTreeStrategy>(
337 pos, boundary_crossing_rule);
338 }
339
340 bool IsEndOfParagraph(const VisiblePosition& pos,
341 EditingBoundaryCrossingRule boundary_crossing_rule) {
342 return IsEndOfParagraphAlgorithm<EditingStrategy>(pos,
343 boundary_crossing_rule);
344 }
345
346 bool IsEndOfParagraph(const VisiblePositionInFlatTree& pos,
347 EditingBoundaryCrossingRule boundary_crossing_rule) {
348 return IsEndOfParagraphAlgorithm<EditingInFlatTreeStrategy>(
349 pos, boundary_crossing_rule);
350 }
351
352 // TODO(editing-dev): We should move |PreviousParagraphPosition()| to
353 // "SelectionModifier.cpp"
354 VisiblePosition PreviousParagraphPosition(const VisiblePosition& p,
355 LayoutUnit x) {
356 DCHECK(p.IsValid()) << p;
357 VisiblePosition pos = p;
358 do {
359 VisiblePosition n = PreviousLinePosition(pos, x);
360 if (n.IsNull() || n.DeepEquivalent() == pos.DeepEquivalent())
361 break;
362 pos = n;
363 } while (InSameParagraph(p, pos));
364 return pos;
365 }
366
367 // TODO(editing-dev): We should move |NextParagraphPosition()| to
368 // "SelectionModifier.cpp"
369 VisiblePosition NextParagraphPosition(const VisiblePosition& p, LayoutUnit x) {
370 DCHECK(p.IsValid()) << p;
371 VisiblePosition pos = p;
372 do {
373 VisiblePosition n = NextLinePosition(pos, x);
374 if (n.IsNull() || n.DeepEquivalent() == pos.DeepEquivalent())
375 break;
376 pos = n;
377 } while (InSameParagraph(p, pos));
378 return pos;
379 }
380
381 EphemeralRange ExpandToParagraphBoundary(const EphemeralRange& range) {
382 const VisiblePosition& start = CreateVisiblePosition(range.StartPosition());
383 DCHECK(start.IsNotNull()) << range.StartPosition();
384 const Position& paragraph_start = StartOfParagraph(start).DeepEquivalent();
385 DCHECK(paragraph_start.IsNotNull()) << range.StartPosition();
386
387 const VisiblePosition& end = CreateVisiblePosition(range.EndPosition());
388 DCHECK(end.IsNotNull()) << range.EndPosition();
389 const Position& paragraph_end = EndOfParagraph(end).DeepEquivalent();
390 DCHECK(paragraph_end.IsNotNull()) << range.EndPosition();
391
392 // TODO(xiaochengh): There are some cases (crbug.com/640112) where we get
393 // |paragraphStart > paragraphEnd|, which is the reason we cannot directly
394 // return |EphemeralRange(paragraphStart, paragraphEnd)|. This is not
395 // desired, though. We should do more investigation to ensure that why
396 // |paragraphStart <= paragraphEnd| is violated.
397 const Position& result_start =
398 paragraph_start.IsNotNull() && paragraph_start <= range.StartPosition()
399 ? paragraph_start
400 : range.StartPosition();
401 const Position& result_end =
402 paragraph_end.IsNotNull() && paragraph_end >= range.EndPosition()
403 ? paragraph_end
404 : range.EndPosition();
405 return EphemeralRange(result_start, result_end);
406 }
407
408 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/editing/VisibleUnits.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698