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

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

Issue 2920193003: Move "word" granularity related functions to VisibleUnitWord.cpp (Closed)
Patch Set: 2017-06-07T11:28:29 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
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/editing/iterators/BackwardsCharacterIterator.h"
35 #include "core/editing/iterators/BackwardsTextBuffer.h"
36 #include "core/editing/iterators/CharacterIterator.h"
37 #include "core/editing/iterators/ForwardsTextBuffer.h"
38 #include "core/editing/iterators/SimplifiedBackwardsTextIterator.h"
39 #include "platform/instrumentation/tracing/TraceEvent.h"
40 #include "platform/text/TextBoundaries.h"
41 #include "platform/text/TextBreakIterator.h"
42
43 namespace blink {
44
45 namespace {
46
47 template <typename Strategy>
48 ContainerNode* NonShadowBoundaryParentNode(Node* node) {
49 ContainerNode* parent = Strategy::Parent(*node);
50 return parent && !parent->IsShadowRoot() ? parent : nullptr;
51 }
52
53 template <typename Strategy>
54 Node* ParentEditingBoundary(const PositionTemplate<Strategy>& position) {
55 Node* const anchor_node = position.AnchorNode();
56 if (!anchor_node)
57 return nullptr;
58
59 Node* document_element = anchor_node->GetDocument().documentElement();
60 if (!document_element)
61 return nullptr;
62
63 Node* boundary = position.ComputeContainerNode();
64 while (boundary != document_element &&
65 NonShadowBoundaryParentNode<Strategy>(boundary) &&
66 HasEditableStyle(*anchor_node) ==
67 HasEditableStyle(*Strategy::Parent(*boundary)))
68 boundary = NonShadowBoundaryParentNode<Strategy>(boundary);
69
70 return boundary;
71 }
72
73 template <typename Strategy>
74 PositionTemplate<Strategy> PreviousBoundaryAlgorithm(
75 const VisiblePositionTemplate<Strategy>& c,
76 BoundarySearchFunction search_function) {
77 DCHECK(c.IsValid()) << c;
78 const PositionTemplate<Strategy> pos = c.DeepEquivalent();
79 Node* boundary = ParentEditingBoundary(pos);
80 if (!boundary)
81 return PositionTemplate<Strategy>();
82
83 const PositionTemplate<Strategy> start =
84 PositionTemplate<Strategy>::EditingPositionOf(boundary, 0)
85 .ParentAnchoredEquivalent();
86 const PositionTemplate<Strategy> end = pos.ParentAnchoredEquivalent();
87
88 ForwardsTextBuffer suffix_string;
89 if (RequiresContextForWordBoundary(CharacterBefore(c))) {
90 TextIteratorAlgorithm<Strategy> forwards_iterator(
91 end, PositionTemplate<Strategy>::AfterNode(boundary));
92 while (!forwards_iterator.AtEnd()) {
93 forwards_iterator.CopyTextTo(&suffix_string);
94 int context_end_index = EndOfFirstWordBoundaryContext(
95 suffix_string.Data() + suffix_string.Size() -
96 forwards_iterator.length(),
97 forwards_iterator.length());
98 if (context_end_index < forwards_iterator.length()) {
99 suffix_string.Shrink(forwards_iterator.length() - context_end_index);
100 break;
101 }
102 forwards_iterator.Advance();
103 }
104 }
105
106 unsigned suffix_length = suffix_string.Size();
107 BackwardsTextBuffer string;
108 string.PushRange(suffix_string.Data(), suffix_string.Size());
109
110 SimplifiedBackwardsTextIteratorAlgorithm<Strategy> it(start, end);
111 int remaining_length = 0;
112 unsigned next = 0;
113 bool need_more_context = false;
114 while (!it.AtEnd()) {
115 bool in_text_security_mode = it.IsInTextSecurityMode();
116 // iterate to get chunks until the searchFunction returns a non-zero
117 // value.
118 if (!in_text_security_mode) {
119 int run_offset = 0;
120 do {
121 run_offset += it.CopyTextTo(&string, run_offset, string.Capacity());
122 // TODO(xiaochengh): The following line takes O(string.size()) time,
123 // which makes quadratic overall running time in the worst case.
124 // Should improve it in some way.
125 next = search_function(string.Data(), string.Size(),
126 string.Size() - suffix_length,
127 kMayHaveMoreContext, need_more_context);
128 } while (!next && run_offset < it.length());
129 if (next) {
130 remaining_length = it.length() - run_offset;
131 break;
132 }
133 } else {
134 // Treat bullets used in the text security mode as regular
135 // characters when looking for boundaries
136 string.PushCharacters('x', it.length());
137 next = 0;
138 }
139 it.Advance();
140 }
141 if (need_more_context) {
142 // The last search returned the beginning of the buffer and asked for
143 // more context, but there is no earlier text. Force a search with
144 // what's available.
145 // TODO(xiaochengh): Do we have to search the whole string?
146 next = search_function(string.Data(), string.Size(),
147 string.Size() - suffix_length, kDontHaveMoreContext,
148 need_more_context);
149 DCHECK(!need_more_context);
150 }
151
152 if (!next)
153 return it.AtEnd() ? it.StartPosition() : pos;
154
155 Node* node = it.StartContainer();
156 int boundary_offset = remaining_length + next;
157 if (node->IsTextNode() && boundary_offset <= node->MaxCharacterOffset()) {
158 // The next variable contains a usable index into a text node
159 return PositionTemplate<Strategy>(node, boundary_offset);
160 }
161
162 // Use the character iterator to translate the next value into a DOM
163 // position.
164 BackwardsCharacterIteratorAlgorithm<Strategy> char_it(start, end);
165 char_it.Advance(string.Size() - suffix_length - next);
166 // TODO(yosin) charIt can get out of shadow host.
167 return char_it.EndPosition();
168 }
169
170 template <typename Strategy>
171 PositionTemplate<Strategy> NextBoundaryAlgorithm(
172 const VisiblePositionTemplate<Strategy>& c,
173 BoundarySearchFunction search_function) {
174 DCHECK(c.IsValid()) << c;
175 PositionTemplate<Strategy> pos = c.DeepEquivalent();
176 Node* boundary = ParentEditingBoundary(pos);
177 if (!boundary)
178 return PositionTemplate<Strategy>();
179
180 Document& d = boundary->GetDocument();
181 const PositionTemplate<Strategy> start(pos.ParentAnchoredEquivalent());
182
183 BackwardsTextBuffer prefix_string;
184 if (RequiresContextForWordBoundary(CharacterAfter(c))) {
185 SimplifiedBackwardsTextIteratorAlgorithm<Strategy> backwards_iterator(
186 PositionTemplate<Strategy>::FirstPositionInNode(&d), start);
187 while (!backwards_iterator.AtEnd()) {
188 backwards_iterator.CopyTextTo(&prefix_string);
189 int context_start_index = StartOfLastWordBoundaryContext(
190 prefix_string.Data(), backwards_iterator.length());
191 if (context_start_index > 0) {
192 prefix_string.Shrink(context_start_index);
193 break;
194 }
195 backwards_iterator.Advance();
196 }
197 }
198
199 unsigned prefix_length = prefix_string.Size();
200 ForwardsTextBuffer string;
201 string.PushRange(prefix_string.Data(), prefix_string.Size());
202
203 const PositionTemplate<Strategy> search_start =
204 PositionTemplate<Strategy>::EditingPositionOf(
205 start.AnchorNode(), start.OffsetInContainerNode());
206 const PositionTemplate<Strategy> search_end =
207 PositionTemplate<Strategy>::LastPositionInNode(boundary);
208 TextIteratorAlgorithm<Strategy> it(
209 search_start, search_end,
210 TextIteratorBehavior::Builder()
211 .SetEmitsCharactersBetweenAllVisiblePositions(true)
212 .Build());
213 const unsigned kInvalidOffset = static_cast<unsigned>(-1);
214 unsigned next = kInvalidOffset;
215 unsigned offset = prefix_length;
216 bool need_more_context = false;
217 while (!it.AtEnd()) {
218 // Keep asking the iterator for chunks until the search function
219 // returns an end value not equal to the length of the string passed to
220 // it.
221 bool in_text_security_mode = it.IsInTextSecurityMode();
222 if (!in_text_security_mode) {
223 int run_offset = 0;
224 do {
225 run_offset += it.CopyTextTo(&string, run_offset, string.Capacity());
226 next = search_function(string.Data(), string.Size(), offset,
227 kMayHaveMoreContext, need_more_context);
228 if (!need_more_context) {
229 // When the search does not need more context, skip all examined
230 // characters except the last one, in case it is a boundary.
231 offset = string.Size();
232 U16_BACK_1(string.Data(), 0, offset);
233 }
234 } while (next == string.Size() && run_offset < it.length());
235 if (next != string.Size())
236 break;
237 } else {
238 // Treat bullets used in the text security mode as regular
239 // characters when looking for boundaries
240 string.PushCharacters('x', it.length());
241 next = string.Size();
242 }
243 it.Advance();
244 }
245 if (need_more_context) {
246 // The last search returned the end of the buffer and asked for more
247 // context, but there is no further text. Force a search with what's
248 // available.
249 // TODO(xiaochengh): Do we still have to search the whole string?
250 next = search_function(string.Data(), string.Size(), prefix_length,
251 kDontHaveMoreContext, need_more_context);
252 DCHECK(!need_more_context);
253 }
254
255 if (it.AtEnd() && next == string.Size()) {
256 pos = it.StartPositionInCurrentContainer();
257 } else if (next != kInvalidOffset && next != prefix_length) {
258 // Use the character iterator to translate the next value into a DOM
259 // position.
260 CharacterIteratorAlgorithm<Strategy> char_it(
261 search_start, search_end,
262 TextIteratorBehavior::Builder()
263 .SetEmitsCharactersBetweenAllVisiblePositions(true)
264 .Build());
265 char_it.Advance(next - prefix_length - 1);
266 pos = char_it.EndPosition();
267
268 if (char_it.CharacterAt(0) == '\n') {
269 // TODO(yosin) workaround for collapsed range (where only start
270 // position is correct) emitted for some emitted newlines
271 // (see rdar://5192593)
272 const VisiblePositionTemplate<Strategy> vis_pos =
273 CreateVisiblePosition(pos);
274 if (vis_pos.DeepEquivalent() ==
275 CreateVisiblePosition(char_it.StartPosition()).DeepEquivalent()) {
276 char_it.Advance(1);
277 pos = char_it.StartPosition();
278 }
279 }
280 }
281
282 return pos;
283 }
284
285 unsigned StartWordBoundary(
286 const UChar* characters,
287 unsigned length,
288 unsigned offset,
289 BoundarySearchContextAvailability may_have_more_context,
290 bool& need_more_context) {
291 TRACE_EVENT0("blink", "startWordBoundary");
292 DCHECK(offset);
293 if (may_have_more_context &&
294 !StartOfLastWordBoundaryContext(characters, offset)) {
295 need_more_context = true;
296 return 0;
297 }
298 need_more_context = false;
299 int start, end;
300 U16_BACK_1(characters, 0, offset);
301 FindWordBoundary(characters, length, offset, &start, &end);
302 return start;
303 }
304
305 template <typename Strategy>
306 PositionTemplate<Strategy> StartOfWordAlgorithm(
307 const VisiblePositionTemplate<Strategy>& c,
308 EWordSide side) {
309 DCHECK(c.IsValid()) << c;
310 // TODO(yosin) This returns a null VP for c at the start of the document
311 // and |side| == |LeftWordIfOnBoundary|
312 VisiblePositionTemplate<Strategy> p = c;
313 if (side == kRightWordIfOnBoundary) {
314 // at paragraph end, the startofWord is the current position
315 if (IsEndOfParagraph(c))
316 return c.DeepEquivalent();
317
318 p = NextPositionOf(c);
319 if (p.IsNull())
320 return c.DeepEquivalent();
321 }
322 return PreviousBoundary(p, StartWordBoundary);
323 }
324
325 unsigned EndWordBoundary(
326 const UChar* characters,
327 unsigned length,
328 unsigned offset,
329 BoundarySearchContextAvailability may_have_more_context,
330 bool& need_more_context) {
331 DCHECK_LE(offset, length);
332 if (may_have_more_context &&
333 EndOfFirstWordBoundaryContext(characters + offset, length - offset) ==
334 static_cast<int>(length - offset)) {
335 need_more_context = true;
336 return length;
337 }
338 need_more_context = false;
339 return FindWordEndBoundary(characters, length, offset);
340 }
341
342 template <typename Strategy>
343 PositionTemplate<Strategy> EndOfWordAlgorithm(
344 const VisiblePositionTemplate<Strategy>& c,
345 EWordSide side) {
346 DCHECK(c.IsValid()) << c;
347 VisiblePositionTemplate<Strategy> p = c;
348 if (side == kLeftWordIfOnBoundary) {
349 if (IsStartOfParagraph(c))
350 return c.DeepEquivalent();
351
352 p = PreviousPositionOf(c);
353 if (p.IsNull())
354 return c.DeepEquivalent();
355 } else if (IsEndOfParagraph(c)) {
356 return c.DeepEquivalent();
357 }
358
359 return NextBoundary(p, EndWordBoundary);
360 }
361
362 unsigned PreviousWordPositionBoundary(
363 const UChar* characters,
364 unsigned length,
365 unsigned offset,
366 BoundarySearchContextAvailability may_have_more_context,
367 bool& need_more_context) {
368 if (may_have_more_context &&
369 !StartOfLastWordBoundaryContext(characters, offset)) {
370 need_more_context = true;
371 return 0;
372 }
373 need_more_context = false;
374 return FindNextWordFromIndex(characters, length, offset, false);
375 }
376
377 unsigned NextWordPositionBoundary(
378 const UChar* characters,
379 unsigned length,
380 unsigned offset,
381 BoundarySearchContextAvailability may_have_more_context,
382 bool& need_more_context) {
383 if (may_have_more_context &&
384 EndOfFirstWordBoundaryContext(characters + offset, length - offset) ==
385 static_cast<int>(length - offset)) {
386 need_more_context = true;
387 return length;
388 }
389 need_more_context = false;
390 return FindNextWordFromIndex(characters, length, offset, true);
391 }
392
393 } // namespace
394
395 Position EndOfWordPosition(const VisiblePosition& position, EWordSide side) {
396 return EndOfWordAlgorithm<EditingStrategy>(position, side);
397 }
398
399 VisiblePosition EndOfWord(const VisiblePosition& position, EWordSide side) {
400 return CreateVisiblePosition(EndOfWordPosition(position, side),
401 VP_UPSTREAM_IF_POSSIBLE);
402 }
403
404 PositionInFlatTree EndOfWordPosition(const VisiblePositionInFlatTree& position,
405 EWordSide side) {
406 return EndOfWordAlgorithm<EditingInFlatTreeStrategy>(position, side);
407 }
408
409 VisiblePositionInFlatTree EndOfWord(const VisiblePositionInFlatTree& position,
410 EWordSide side) {
411 return CreateVisiblePosition(EndOfWordPosition(position, side),
412 VP_UPSTREAM_IF_POSSIBLE);
413 }
414
415 Position NextBoundary(const VisiblePosition& visible_position,
416 BoundarySearchFunction search_function) {
417 return NextBoundaryAlgorithm(visible_position, search_function);
418 }
419
420 PositionInFlatTree NextBoundary(
421 const VisiblePositionInFlatTree& visible_position,
422 BoundarySearchFunction search_function) {
423 return NextBoundaryAlgorithm(visible_position, search_function);
424 }
425
426 VisiblePosition NextWordPosition(const VisiblePosition& c) {
427 DCHECK(c.IsValid()) << c;
428 VisiblePosition next = CreateVisiblePosition(
429 NextBoundary(c, NextWordPositionBoundary), VP_UPSTREAM_IF_POSSIBLE);
430 return HonorEditingBoundaryAtOrAfter(next, c.DeepEquivalent());
431 }
432
433 Position PreviousBoundary(const VisiblePosition& visible_position,
434 BoundarySearchFunction search_function) {
435 return PreviousBoundaryAlgorithm(visible_position, search_function);
436 }
437
438 PositionInFlatTree PreviousBoundary(
439 const VisiblePositionInFlatTree& visible_position,
440 BoundarySearchFunction search_function) {
441 return PreviousBoundaryAlgorithm(visible_position, search_function);
442 }
443
444 VisiblePosition PreviousWordPosition(const VisiblePosition& c) {
445 DCHECK(c.IsValid()) << c;
446 VisiblePosition prev =
447 CreateVisiblePosition(PreviousBoundary(c, PreviousWordPositionBoundary));
448 return HonorEditingBoundaryAtOrBefore(prev, c.DeepEquivalent());
449 }
450
451 Position StartOfWordPosition(const VisiblePosition& position, EWordSide side) {
452 return StartOfWordAlgorithm<EditingStrategy>(position, side);
453 }
454
455 VisiblePosition StartOfWord(const VisiblePosition& position, EWordSide side) {
456 return CreateVisiblePosition(StartOfWordPosition(position, side));
457 }
458
459 PositionInFlatTree StartOfWordPosition(
460 const VisiblePositionInFlatTree& position,
461 EWordSide side) {
462 return StartOfWordAlgorithm<EditingInFlatTreeStrategy>(position, side);
463 }
464
465 VisiblePositionInFlatTree StartOfWord(const VisiblePositionInFlatTree& position,
466 EWordSide side) {
467 return CreateVisiblePosition(StartOfWordPosition(position, side));
468 }
469
470 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698