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

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

Issue 2465743002: Trybot test for losing style (Closed)
Patch Set: donnot clear 14:38 Created 4 years 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 (c) 2016 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 "core/editing/commands/InsertIncrementalTextCommand.h"
6
7 #include "core/dom/Document.h"
8 #include "core/dom/Element.h"
9 #include "core/dom/Text.h"
10 #include "core/editing/EditingUtilities.h"
11 #include "core/editing/Editor.h"
12 #include "core/editing/PlainTextRange.h"
13 #include "core/editing/VisibleUnits.h"
14 #include "core/frame/LocalFrame.h"
15 #include "core/html/HTMLSpanElement.h"
16
17 namespace blink {
18
19 InsertIncrementalTextCommand::InsertIncrementalTextCommand(
20 Document& document,
21 const String& text,
22 bool selectInsertedText,
23 RebalanceType rebalanceType)
24 : CompositeEditCommand(document),
25 m_text(text),
26 m_selectInsertedText(selectInsertedText),
27 m_rebalanceType(rebalanceType) {}
28
29 String InsertIncrementalTextCommand::textDataForInputEvent() const {
30 return m_text;
31 }
32
33 Position InsertIncrementalTextCommand::positionInsideTextNode(
34 const Position& p,
35 EditingState* editingState) {
36 Position pos = p;
37 if (isTabHTMLSpanElementTextNode(pos.anchorNode())) {
38 Text* textNode = document().createEditingTextNode("");
39 insertNodeAtTabSpanPosition(textNode, pos, editingState);
40 if (editingState->isAborted())
41 return Position();
42 return Position::firstPositionInNode(textNode);
43 }
44
45 // Prepare for text input by looking at the specified position.
46 // It may be necessary to insert a text node to receive characters.
47 if (!pos.computeContainerNode()->isTextNode()) {
48 Text* textNode = document().createEditingTextNode("");
49 insertNodeAt(textNode, pos, editingState);
50 if (editingState->isAborted())
51 return Position();
52 return Position::firstPositionInNode(textNode);
53 }
54
55 return pos;
56 }
57
58 void InsertIncrementalTextCommand::setEndingSelectionWithoutValidation(
59 const Position& startPosition,
60 const Position& endPosition) {
61 // We could have inserted a part of composed character sequence,
62 // so we are basically treating ending selection as a range to avoid
63 // validation. <http://bugs.webkit.org/show_bug.cgi?id=15781>
64 setEndingSelection(SelectionInDOMTree::Builder()
65 .collapse(startPosition)
66 .extend(endPosition)
67 .setIsDirectional(endingSelection().isDirectional())
68 .build());
69 }
70
71 // This avoids the expense of a full fledged delete operation, and avoids a
72 // layout that typically results from text removal.
73 bool InsertIncrementalTextCommand::performTrivialReplace(
74 const String& text,
75 bool selectInsertedText) {
76 if (!endingSelection().isRange())
77 return false;
78
79 if (text.contains('\t') || text.contains(' ') || text.contains('\n'))
80 return false;
81
82 Position start = endingSelection().start();
83 Position endPosition = replaceSelectedTextInNode(text);
84 if (endPosition.isNull())
85 return false;
86
87 setEndingSelectionWithoutValidation(start, endPosition);
88 if (selectInsertedText)
89 return true;
90 setEndingSelection(SelectionInDOMTree::Builder()
91 .collapse(endingSelection().end())
92 .setIsDirectional(endingSelection().isDirectional())
93 .build());
94 return true;
95 }
96
97 bool InsertIncrementalTextCommand::performOverwrite(const String& text,
98 bool selectInsertedText) {
99 Position start = endingSelection().start();
100 if (start.isNull() || !start.isOffsetInAnchor() ||
101 !start.computeContainerNode()->isTextNode())
102 return false;
103 Text* textNode = toText(start.computeContainerNode());
104 if (!textNode)
105 return false;
106
107 unsigned count = std::min(text.length(),
108 textNode->length() - start.offsetInContainerNode());
109 if (!count)
110 return false;
111
112 replaceTextInNode(textNode, start.offsetInContainerNode(), count, text);
113
114 Position endPosition =
115 Position(textNode, start.offsetInContainerNode() + text.length());
116 setEndingSelectionWithoutValidation(start, endPosition);
117 if (selectInsertedText || endingSelection().isNone())
118 return true;
119 setEndingSelection(SelectionInDOMTree::Builder()
120 .collapse(endingSelection().end())
121 .setIsDirectional(endingSelection().isDirectional())
122 .build());
123 return true;
124 }
125
126 static size_t computeCommonPrefixLength(const String& str1,
127 const String& str2) {
128 const size_t maxCommonPrefixLength = std::min(str1.length(), str2.length());
129 for (size_t index = 0; index < maxCommonPrefixLength; ++index) {
130 if (str1[index] != str2[index])
131 return index;
132 }
133 return maxCommonPrefixLength;
134 }
135
136 static size_t computeCommonSuffixLength(const String& str1,
137 const String& str2) {
138 const size_t length1 = str1.length();
139 const size_t length2 = str2.length();
140 const size_t maxCommonSuffixLength = std::min(length1, length2);
141 for (size_t index = 0; index < maxCommonSuffixLength; ++index) {
142 if (str1[length1 - index - 1] != str2[length2 - index - 1])
143 return index;
144 }
145 return maxCommonSuffixLength;
146 }
147
148 // If current position is at grapheme boundary, return 0; otherwise, return the
149 // distance to its nearest left grapheme boundary.
150 static size_t computeDistanceToLeftGraphemeBoundary(const Position& position) {
151 const Position& adjustedPosition = previousPositionOf(
152 nextPositionOf(position, PositionMoveType::GraphemeCluster),
153 PositionMoveType::GraphemeCluster);
154 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode());
155 DCHECK_GE(position.computeOffsetInContainerNode(),
156 adjustedPosition.computeOffsetInContainerNode());
157 return static_cast<size_t>(position.computeOffsetInContainerNode() -
158 adjustedPosition.computeOffsetInContainerNode());
159 }
160
161 static size_t computeCommonGraphemeClusterPrefixLength(
162 const String& oldText,
163 const String& newText,
164 const Element* rootEditableElement) {
165 const size_t commonPrefixLength = computeCommonPrefixLength(oldText, newText);
166
167 // For grapheme cluster, we should adjust it for grapheme boundary.
168 const EphemeralRange& range =
169 PlainTextRange(0, commonPrefixLength).createRange(*rootEditableElement);
170 if (range.isNull())
171 return 0;
172 const Position& position = range.endPosition();
173 const size_t diff = computeDistanceToLeftGraphemeBoundary(position);
174 DCHECK_GE(commonPrefixLength, diff);
175 return commonPrefixLength - diff;
176 }
177
178 // If current position is at grapheme boundary, return 0; otherwise, return the
179 // distance to its nearest right grapheme boundary.
180 static size_t computeDistanceToRightGraphemeBoundary(const Position& position) {
181 const Position& adjustedPosition = nextPositionOf(
182 previousPositionOf(position, PositionMoveType::GraphemeCluster),
183 PositionMoveType::GraphemeCluster);
184 DCHECK_EQ(position.anchorNode(), adjustedPosition.anchorNode());
185 DCHECK_GE(adjustedPosition.computeOffsetInContainerNode(),
186 position.computeOffsetInContainerNode());
187 return static_cast<size_t>(adjustedPosition.computeOffsetInContainerNode() -
188 position.computeOffsetInContainerNode());
189 }
190
191 static size_t computeCommonGraphemeClusterSuffixLength(
192 const String& oldText,
193 const String& newText,
194 const Element* rootEditableElement) {
195 const size_t commonSuffixLength = computeCommonSuffixLength(oldText, newText);
196
197 // For grapheme cluster, we should adjust it for grapheme boundary.
198 const EphemeralRange& range =
199 PlainTextRange(0, oldText.length() - commonSuffixLength)
200 .createRange(*rootEditableElement);
201 if (range.isNull())
202 return 0;
203 const Position& position = range.endPosition();
204 const size_t diff = computeDistanceToRightGraphemeBoundary(position);
205 DCHECK_GE(commonSuffixLength, diff);
206 return commonSuffixLength - diff;
207 }
208
209 static PlainTextRange getSelectionOffsets(LocalFrame* frame) {
210 EphemeralRange range = firstEphemeralRangeOf(frame->selection().selection());
211 if (range.isNull())
212 return PlainTextRange();
213 ContainerNode* editable =
214 frame->selection().rootEditableElementOrTreeScopeRootNode();
215 DCHECK(editable);
216
217 return PlainTextRange::create(*editable, range);
218 }
219
220 static const VisibleSelection createSelectionForIncrementalInsertion(
221 const size_t start,
222 const size_t end,
223 const bool isDirectional,
224 LocalFrame* frame) {
225 Element* element = frame->selection().selection().rootEditableElement();
226 DCHECK(element);
227
228 const EphemeralRange& startRange =
229 PlainTextRange(0, static_cast<int>(start)).createRange(*element);
230 DCHECK(startRange.isNotNull());
231 const Position& startPosition = startRange.endPosition();
232
233 const EphemeralRange& endRange =
234 PlainTextRange(0, static_cast<int>(end)).createRange(*element);
235 DCHECK(endRange.isNotNull());
236 const Position& endPosition = endRange.endPosition();
237
238 VisibleSelection selection =
239 createVisibleSelection(SelectionInDOMTree::Builder()
240 .setBaseAndExtent(startPosition, endPosition)
241 .build());
242 selection.setIsDirectional(isDirectional);
243
244 return selection;
245 }
246
247 void InsertIncrementalTextCommand::setSelection(const size_t start,
248 const size_t end,
249 LocalFrame* frame) {
250 const VisibleSelection selection = createSelectionForIncrementalInsertion(
251 start, end, endingSelection().isDirectional(), frame);
252 setStartingSelection(selection);
253 setEndingSelectionWithoutValidation(selection.start(), selection.end());
254
255 document().frame()->selection().setSelection(selection);
256 }
257
258 void InsertIncrementalTextCommand::doApply(EditingState* editingState) {
259 // LOG(ERROR) << "hyb:\n";
260 // LOG(ERROR) << "hyb: InsertIncrementalTextCommand::doApply";
261 DCHECK_EQ(m_text.find('\n'), kNotFound);
262 if (!endingSelection().isNonOrphanedCaretOrRange())
263 return;
264 /*
265 LOG(ERROR)<<"hyb:
266 endingSelection().start()"<<endingSelection().start().computeOffsetInContainer Node();
267 LOG(ERROR)<<"hyb:
268 endingSelection().end()"<<endingSelection().end().computeOffsetInContainerNode ();
269 LOG(ERROR)<<"hyb:
270 startingSelection().start()"<<startingSelection().start().computeOffsetInConta inerNode();
271 LOG(ERROR)<<"hyb:
272 startingSelection().end()"<<startingSelection().end().computeOffsetInContainer Node();
273 LOG(ERROR)<<"hyb: frame
274 start:"<<document().frame()->selection().selection().start().computeOffsetInCo ntainerNode();
275 LOG(ERROR)<<"hyb: frame
276 end:"<<document().frame()->selection().selection().end().computeOffsetInContai nerNode();
277 LOG(ERROR)<<"hyb: m_text:"<<m_text.utf8().data();
278
279 LOG(ERROR)<<"hyb: endingSelection().end node()"
280 <<endingSelection().end().computeContainerNode();
281 LOG(ERROR)<<"hyb: frame end node:"<<
282 document().frame()->selection().selection().end().computeContainerNode();
283
284 */
285
286 LocalFrame* frame = document().frame();
287 DCHECK(frame);
288 const Element* element = endingSelection().rootEditableElement();
289 DCHECK(element);
290
291 const String& newText = m_text;
292 String oldText = frame->selectedText();
293 // LOG(ERROR) << "hyb: -------oldText:" << oldText.utf8().data();
294 // LOG(ERROR) << "hyb: -------newText:" << newText.utf8().data();
295
296 //...........!!!!!
297 // need some comment!!!
298 if (element->tagName() == String("INPUT"))
299 oldText = emptyString();
300
301 /*
302 LOG(ERROR) << "hyb: " <<
303 element->getIdAttribute().getString().utf8().data();
304 LOG(ERROR) << "hyb: "
305 << element->getNameAttribute().getString().utf8().data();
306 LOG(ERROR) << "hyb: "
307 << element->getClassAttribute().getString().utf8().data();
308
309 LOG(ERROR) << "hyb: -------oldText:" << oldText.utf8().data();
310 */
311
312 const size_t newTextLength = newText.length();
313 const size_t commonPrefixLength =
314 computeCommonGraphemeClusterPrefixLength(oldText, newText, element);
315 // We should ignore common prefix when finding common suffix.
316 const size_t commonSuffixLength = computeCommonGraphemeClusterSuffixLength(
317 oldText.right(oldText.length() - commonPrefixLength),
318 newText.right(newTextLength - commonPrefixLength), element);
319 // LOG(ERROR)<<"hyb: common: "<<commonPrefixLength<<" "<<commonSuffixLength;
320
321 const String textToInsert =
322 newText.substring(commonPrefixLength, newTextLength - commonPrefixLength -
323 commonSuffixLength);
324 // LOG(ERROR) << "hyb: textToInsert:" << textToInsert.utf8().data();
325
326 PlainTextRange selectionOffsets = getSelectionOffsets(frame);
327 const size_t selecitonStart = selectionOffsets.start();
328 const size_t selectionEnd = selectionOffsets.end();
329 const size_t insertionStart = selecitonStart + commonPrefixLength;
330 const size_t insertionEnd = selectionEnd - commonSuffixLength;
331
332 // LOG(ERROR) << "hyb: ..." << selecitonStart << " " << selectionEnd << " "
333 //<< insertionStart << " " << insertionEnd;
334 DCHECK_LE(insertionStart, insertionEnd);
335
336 const VisibleSelection selectionForInsertion =
337 createSelectionForIncrementalInsertion(insertionStart, insertionEnd,
338 endingSelection().isDirectional(),
339 frame);
340
341 const bool changeSelection = selectionForInsertion != endingSelection();
342 // LOG(ERROR)<<"hyb:
343 // selectionForInsertion().start()"<<selectionForInsertion.start().computeOffs etInContainerNode();
344 // LOG(ERROR)<<"hyb:
345 // selectionForInsertion().end()"<<selectionForInsertion.end().computeOffsetIn ContainerNode();
346
347 setStartingSelection(selectionForInsertion);
348 setEndingSelectionWithoutValidation(selectionForInsertion.start(),
349 selectionForInsertion.end());
350
351 // ...
352 // document().updateStyleAndLayoutIgnorePendingStylesheets(); // crash without
353 // this line???
354
355 // Delete the current selection.
356 // FIXME: This delete operation blows away the typing style.
357 if (endingSelection().isRange()) {
358 if (performTrivialReplace(textToInsert, m_selectInsertedText)) {
359 // LOG(ERROR) << "hyb: 1.1";
360 if (changeSelection)
361 setSelection(selecitonStart, selecitonStart + newTextLength, frame);
362 document().updateStyleAndLayoutIgnorePendingStylesheets();
363
364 // document().updateStyleAndLayoutIgnorePendingStylesheets();//???
365 /*
366 LOG(ERROR)<<"hyb:
367 endingSelection().start()"<<endingSelection().start().computeOffsetInConta inerNode();
368 LOG(ERROR)<<"hyb:
369 endingSelection().end()"<<endingSelection().end().computeOffsetInContainer Node();
370
371 PlainTextRange selectionOffsets = getSelectionOffsets(frame);
372 const size_t selecitonStart = selectionOffsets.start();
373 const size_t selectionEnd = selectionOffsets.end();
374 LOG(ERROR)<<"hyb: ..."<<selecitonStart<<" "<<selectionEnd;
375
376 LOG(ERROR)<<"hyb: frame end node:"<<
377 document().frame()->selection().selection().end().computeContainerNode ();
378 */
379 return;
380 }
381 document().updateStyleAndLayoutIgnorePendingStylesheets();
382 bool endOfSelectionWasAtStartOfBlock =
383 isStartOfBlock(endingSelection().visibleEnd());
384 deleteSelection(editingState, false, true, false, false);
385 if (editingState->isAborted()) {
386 // LOG(ERROR) << "hyb: 1.2";
387 return;
388 }
389 // deleteSelection eventually makes a new endingSelection out of a Position.
390 // If that Position doesn't have a layoutObject (e.g. it is on a <frameset>
391 // in the DOM), the VisibleSelection cannot be canonicalized to anything
392 // other than NoSelection. The rest of this function requires a real
393 // endingSelection, so bail out.
394 if (endingSelection().isNone()) {
395 // LOG(ERROR) << "hyb: 1.3";
396 return;
397 }
398 if (endOfSelectionWasAtStartOfBlock) {
399 if (EditingStyle* typingStyle = frame->selection().typingStyle())
400 typingStyle->removeBlockProperties();
401 }
402 } else if (frame->editor().isOverwriteModeEnabled()) {
403 if (performOverwrite(textToInsert, m_selectInsertedText)) {
404 if (changeSelection)
405 setSelection(selecitonStart, selecitonStart + newTextLength, frame);
406 document().updateStyleAndLayoutIgnorePendingStylesheets();
407 // LOG(ERROR) << "hyb: 1.4";
408 return;
409 }
410 }
411
412 // LOG(ERROR) << "hyb: 222";
413 document().updateStyleAndLayoutIgnorePendingStylesheets();
414
415 Position startPosition(endingSelection().start());
416
417 Position placeholder;
418 // We want to remove preserved newlines and brs that will collapse (and thus
419 // become unnecessary) when content is inserted just before them.
420 // FIXME: We shouldn't really have to do this, but removing placeholders is a
421 // workaround for 9661.
422 // If the caret is just before a placeholder, downstream will normalize the
423 // caret to it.
424 Position downstream(mostForwardCaretPosition(startPosition));
425 if (lineBreakExistsAtPosition(downstream)) {
426 // FIXME: This doesn't handle placeholders at the end of anonymous blocks.
427 VisiblePosition caret = createVisiblePosition(startPosition);
428 if (isEndOfBlock(caret) && isStartOfParagraph(caret))
429 placeholder = downstream;
430 // Don't remove the placeholder yet, otherwise the block we're inserting
431 // into would collapse before we get a chance to insert into it. We check
432 // for a placeholder now, though, because doing so requires the creation of
433 // a VisiblePosition, and if we did that post-insertion it would force a
434 // layout.
435 }
436
437 // Insert the character at the leftmost candidate.
438 startPosition = mostBackwardCaretPosition(startPosition);
439
440 // It is possible for the node that contains startPosition to contain only
441 // unrendered whitespace, and so deleteInsignificantText could remove it.
442 // Save the position before the node in case that happens.
443 DCHECK(startPosition.computeContainerNode()) << startPosition;
444 Position positionBeforeStartNode(
445 Position::inParentBeforeNode(*startPosition.computeContainerNode()));
446 deleteInsignificantText(startPosition,
447 mostForwardCaretPosition(startPosition));
448 if (!startPosition.isConnected())
449 startPosition = positionBeforeStartNode;
450 if (!isVisuallyEquivalentCandidate(startPosition))
451 startPosition = mostForwardCaretPosition(startPosition);
452
453 startPosition =
454 positionAvoidingSpecialElementBoundary(startPosition, editingState);
455 if (editingState->isAborted())
456 return;
457
458 // LOG(ERROR) << "hyb: 333";
459 Position endPosition;
460
461 if (textToInsert == "\t" && isRichlyEditablePosition(startPosition)) {
462 endPosition = insertTab(startPosition, editingState);
463 if (editingState->isAborted())
464 return;
465 startPosition =
466 previousPositionOf(endPosition, PositionMoveType::GraphemeCluster);
467 if (placeholder.isNotNull())
468 removePlaceholderAt(placeholder);
469 } else {
470 // Make sure the document is set up to receive textToInsert
471 startPosition = positionInsideTextNode(startPosition, editingState);
472 if (editingState->isAborted())
473 return;
474 DCHECK(startPosition.isOffsetInAnchor()) << startPosition;
475 DCHECK(startPosition.computeContainerNode()) << startPosition;
476 DCHECK(startPosition.computeContainerNode()->isTextNode()) << startPosition;
477 if (placeholder.isNotNull())
478 removePlaceholderAt(placeholder);
479 Text* textNode = toText(startPosition.computeContainerNode());
480 const unsigned offset = startPosition.offsetInContainerNode();
481
482 insertTextIntoNode(textNode, offset, textToInsert);
483 endPosition = Position(textNode, offset + textToInsert.length());
484
485 if (m_rebalanceType == RebalanceLeadingAndTrailingWhitespaces) {
486 // The insertion may require adjusting adjacent whitespace, if it is
487 // present.
488 rebalanceWhitespaceAt(endPosition);
489 // Rebalancing on both sides isn't necessary if we've inserted only
490 // spaces.
491 if (!shouldRebalanceLeadingWhitespaceFor(textToInsert))
492 rebalanceWhitespaceAt(startPosition);
493 } else {
494 DCHECK_EQ(m_rebalanceType, RebalanceAllWhitespaces);
495 if (canRebalance(startPosition) && canRebalance(endPosition)) {
496 rebalanceWhitespaceOnTextSubstring(
497 textNode, startPosition.offsetInContainerNode(),
498 endPosition.offsetInContainerNode());
499 }
500 }
501 }
502
503 LOG(ERROR) << "hyb: 555";
504 document().updateStyleAndLayoutIgnorePendingStylesheets(); //??
505
506 LOG(ERROR) << "hyb: 666";
507 setEndingSelectionWithoutValidation(startPosition, endPosition);
508
509 // document().updateStyleAndLayoutIgnorePendingStylesheets(); //???
510
511 /*
512 LOG(ERROR) << "hyb: endingSelection().start()"
513 << endingSelection().start().computeOffsetInContainerNode();
514 LOG(ERROR) << "hyb: endingSelection().end()"
515 << endingSelection().end().computeOffsetInContainerNode();
516 */
517
518 // Handle the case where there is a typing style.
519 if (EditingStyle* typingStyle = frame->selection().typingStyle()) {
520 typingStyle->prepareToApplyAt(endPosition,
521 EditingStyle::PreserveWritingDirection);
522 if (!typingStyle->isEmpty()) {
523 applyStyle(typingStyle, editingState);
524 if (editingState->isAborted())
525 return;
526 }
527 }
528
529 LOG(ERROR) << "hyb: 777"; ///
530 // totally test...
531 if (changeSelection)
532 setSelection(selecitonStart, selecitonStart + newTextLength, frame);
533
534 // document().updateStyleAndLayoutIgnorePendingStylesheets(); //???
535
536 if (!m_selectInsertedText) {
537 SelectionInDOMTree::Builder builder;
538 builder.setAffinity(endingSelection().affinity());
539 builder.setIsDirectional(endingSelection().isDirectional());
540 if (endingSelection().end().isNotNull())
541 builder.collapse(endingSelection().end());
542 setEndingSelection(builder.build());
543 }
544
545 LOG(ERROR) << "hyb: 888"; ////
546 }
547
548 Position InsertIncrementalTextCommand::insertTab(const Position& pos,
549 EditingState* editingState) {
550 document().updateStyleAndLayoutIgnorePendingStylesheets();
551
552 Position insertPos = createVisiblePosition(pos).deepEquivalent();
553 if (insertPos.isNull())
554 return pos;
555
556 Node* node = insertPos.computeContainerNode();
557 unsigned offset = node->isTextNode() ? insertPos.offsetInContainerNode() : 0;
558
559 // keep tabs coalesced in tab span
560 if (isTabHTMLSpanElementTextNode(node)) {
561 Text* textNode = toText(node);
562 insertTextIntoNode(textNode, offset, "\t");
563 return Position(textNode, offset + 1);
564 }
565
566 // create new tab span
567 HTMLSpanElement* spanElement = createTabSpanElement(document());
568
569 // place it
570 if (!node->isTextNode()) {
571 insertNodeAt(spanElement, insertPos, editingState);
572 } else {
573 Text* textNode = toText(node);
574 if (offset >= textNode->length()) {
575 insertNodeAfter(spanElement, textNode, editingState);
576 } else {
577 // split node to make room for the span
578 // NOTE: splitTextNode uses textNode for the
579 // second node in the split, so we need to
580 // insert the span before it.
581 if (offset > 0)
582 splitTextNode(textNode, offset);
583 insertNodeBefore(spanElement, textNode, editingState);
584 }
585 }
586 if (editingState->isAborted())
587 return Position();
588
589 // return the position following the new tab
590 return Position::lastPositionInNode(spanElement);
591 }
592
593 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698