1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "third_party/blink/renderer/core/editing/commands/composite_edit_command.h"
27 
28 #include <algorithm>
29 
30 #include "third_party/blink/renderer/core/accessibility/blink_ax_event_intent.h"
31 #include "third_party/blink/renderer/core/accessibility/scoped_blink_ax_event_intent.h"
32 #include "third_party/blink/renderer/core/dom/document.h"
33 #include "third_party/blink/renderer/core/dom/document_fragment.h"
34 #include "third_party/blink/renderer/core/dom/element_traversal.h"
35 #include "third_party/blink/renderer/core/dom/events/scoped_event_queue.h"
36 #include "third_party/blink/renderer/core/dom/node_traversal.h"
37 #include "third_party/blink/renderer/core/dom/range.h"
38 #include "third_party/blink/renderer/core/dom/text.h"
39 #include "third_party/blink/renderer/core/editing/commands/append_node_command.h"
40 #include "third_party/blink/renderer/core/editing/commands/apply_style_command.h"
41 #include "third_party/blink/renderer/core/editing/commands/delete_from_text_node_command.h"
42 #include "third_party/blink/renderer/core/editing/commands/delete_selection_command.h"
43 #include "third_party/blink/renderer/core/editing/commands/editing_commands_utilities.h"
44 #include "third_party/blink/renderer/core/editing/commands/insert_into_text_node_command.h"
45 #include "third_party/blink/renderer/core/editing/commands/insert_line_break_command.h"
46 #include "third_party/blink/renderer/core/editing/commands/insert_node_before_command.h"
47 #include "third_party/blink/renderer/core/editing/commands/insert_paragraph_separator_command.h"
48 #include "third_party/blink/renderer/core/editing/commands/merge_identical_elements_command.h"
49 #include "third_party/blink/renderer/core/editing/commands/remove_css_property_command.h"
50 #include "third_party/blink/renderer/core/editing/commands/remove_node_command.h"
51 #include "third_party/blink/renderer/core/editing/commands/remove_node_preserving_children_command.h"
52 #include "third_party/blink/renderer/core/editing/commands/replace_node_with_span_command.h"
53 #include "third_party/blink/renderer/core/editing/commands/replace_selection_command.h"
54 #include "third_party/blink/renderer/core/editing/commands/set_character_data_command.h"
55 #include "third_party/blink/renderer/core/editing/commands/set_node_attribute_command.h"
56 #include "third_party/blink/renderer/core/editing/commands/split_element_command.h"
57 #include "third_party/blink/renderer/core/editing/commands/split_text_node_command.h"
58 #include "third_party/blink/renderer/core/editing/commands/split_text_node_containing_element_command.h"
59 #include "third_party/blink/renderer/core/editing/commands/undo_stack.h"
60 #include "third_party/blink/renderer/core/editing/commands/wrap_contents_in_dummy_span_command.h"
61 #include "third_party/blink/renderer/core/editing/editing_utilities.h"
62 #include "third_party/blink/renderer/core/editing/editor.h"
63 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
64 #include "third_party/blink/renderer/core/editing/frame_selection.h"
65 #include "third_party/blink/renderer/core/editing/iterators/text_iterator.h"
66 #include "third_party/blink/renderer/core/editing/markers/document_marker_controller.h"
67 #include "third_party/blink/renderer/core/editing/plain_text_range.h"
68 #include "third_party/blink/renderer/core/editing/relocatable_position.h"
69 #include "third_party/blink/renderer/core/editing/selection_template.h"
70 #include "third_party/blink/renderer/core/editing/serializers/serialization.h"
71 #include "third_party/blink/renderer/core/editing/visible_position.h"
72 #include "third_party/blink/renderer/core/editing/visible_selection.h"
73 #include "third_party/blink/renderer/core/editing/visible_units.h"
74 #include "third_party/blink/renderer/core/frame/local_frame.h"
75 #include "third_party/blink/renderer/core/html/html_br_element.h"
76 #include "third_party/blink/renderer/core/html/html_div_element.h"
77 #include "third_party/blink/renderer/core/html/html_element.h"
78 #include "third_party/blink/renderer/core/html/html_li_element.h"
79 #include "third_party/blink/renderer/core/html/html_quote_element.h"
80 #include "third_party/blink/renderer/core/html/html_span_element.h"
81 #include "third_party/blink/renderer/core/html_names.h"
82 #include "third_party/blink/renderer/core/layout/layout_block.h"
83 #include "third_party/blink/renderer/core/layout/layout_list_item.h"
84 #include "third_party/blink/renderer/core/layout/layout_text.h"
85 #include "third_party/blink/renderer/core/layout/line/inline_text_box.h"
86 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
87 #include "third_party/blink/renderer/platform/heap/heap.h"
88 
89 namespace blink {
90 
CompositeEditCommand(Document & document)91 CompositeEditCommand::CompositeEditCommand(Document& document)
92     : EditCommand(document) {
93   const VisibleSelection& visible_selection =
94       document.GetFrame()
95           ->Selection()
96           .ComputeVisibleSelectionInDOMTreeDeprecated();
97   SetStartingSelection(
98       SelectionForUndoStep::From(visible_selection.AsSelection()));
99   SetEndingSelection(starting_selection_);
100 }
101 
~CompositeEditCommand()102 CompositeEditCommand::~CompositeEditCommand() {
103   DCHECK(IsTopLevelCommand() || !undo_step_);
104 }
105 
EndingVisibleSelection() const106 VisibleSelection CompositeEditCommand::EndingVisibleSelection() const {
107   // TODO(editing-dev): The use of
108   // |Document::UpdateStyleAndLayout()|
109   // needs to be audited.  See http://crbug.com/590369 for more details.
110   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
111   return CreateVisibleSelection(ending_selection_);
112 }
113 
Apply()114 bool CompositeEditCommand::Apply() {
115   DCHECK(!IsCommandGroupWrapper());
116   if (!IsRichlyEditablePosition(EndingVisibleSelection().Base())) {
117     switch (GetInputType()) {
118       case InputEvent::InputType::kInsertText:
119       case InputEvent::InputType::kInsertLineBreak:
120       case InputEvent::InputType::kInsertParagraph:
121       case InputEvent::InputType::kInsertFromPaste:
122       case InputEvent::InputType::kInsertFromDrop:
123       case InputEvent::InputType::kInsertFromYank:
124       case InputEvent::InputType::kInsertTranspose:
125       case InputEvent::InputType::kInsertReplacementText:
126       case InputEvent::InputType::kInsertCompositionText:
127       case InputEvent::InputType::kDeleteWordBackward:
128       case InputEvent::InputType::kDeleteWordForward:
129       case InputEvent::InputType::kDeleteSoftLineBackward:
130       case InputEvent::InputType::kDeleteSoftLineForward:
131       case InputEvent::InputType::kDeleteHardLineBackward:
132       case InputEvent::InputType::kDeleteHardLineForward:
133       case InputEvent::InputType::kDeleteContentBackward:
134       case InputEvent::InputType::kDeleteContentForward:
135       case InputEvent::InputType::kDeleteByCut:
136       case InputEvent::InputType::kDeleteByDrag:
137       case InputEvent::InputType::kNone:
138         break;
139       default:
140         return false;
141     }
142   }
143   EnsureUndoStep();
144 
145   // Changes to the document may have been made since the last editing operation
146   // that require a layout, as in <rdar://problem/5658603>. Low level
147   // operations, like RemoveNodeCommand, don't require a layout because the high
148   // level operations that use them perform one if one is necessary (like for
149   // the creation of VisiblePositions).
150   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
151 
152   LocalFrame* frame = GetDocument().GetFrame();
153   DCHECK(frame);
154   // directional is stored at the top level command, so that before and after
155   // executing command same directional will be there.
156   SetSelectionIsDirectional(frame->Selection().IsDirectional());
157   GetUndoStep()->SetSelectionIsDirectional(SelectionIsDirectional());
158 
159   // Provides details to accessibility about any text change caused by applying
160   // this command, throughout the current call stack.
161   ScopedBlinkAXEventIntent scoped_blink_ax_event_intent(
162       BlinkAXEventIntent::FromEditCommand(*this), &GetDocument());
163 
164   EditingState editing_state;
165   EventQueueScope event_queue_scope;
166   DoApply(&editing_state);
167 
168   // Only need to call appliedEditing for top-level commands, and TypingCommands
169   // do it on their own (see TypingCommand::typingAddedToOpenCommand).
170   if (!IsTypingCommand())
171     AppliedEditing();
172   return !editing_state.IsAborted();
173 }
174 
EnsureUndoStep()175 UndoStep* CompositeEditCommand::EnsureUndoStep() {
176   CompositeEditCommand* command = this;
177   while (command && command->Parent())
178     command = command->Parent();
179   if (!command->undo_step_) {
180     command->undo_step_ = MakeGarbageCollected<UndoStep>(
181         &GetDocument(), StartingSelection(), EndingSelection(), GetInputType());
182   }
183   return command->undo_step_.Get();
184 }
185 
PreservesTypingStyle() const186 bool CompositeEditCommand::PreservesTypingStyle() const {
187   return false;
188 }
189 
IsTypingCommand() const190 bool CompositeEditCommand::IsTypingCommand() const {
191   return false;
192 }
193 
IsCommandGroupWrapper() const194 bool CompositeEditCommand::IsCommandGroupWrapper() const {
195   return false;
196 }
197 
IsDragAndDropCommand() const198 bool CompositeEditCommand::IsDragAndDropCommand() const {
199   return false;
200 }
201 
IsReplaceSelectionCommand() const202 bool CompositeEditCommand::IsReplaceSelectionCommand() const {
203   return false;
204 }
205 
206 //
207 // sugary-sweet convenience functions to help create and apply edit commands in
208 // composite commands
209 //
ApplyCommandToComposite(EditCommand * command,EditingState * editing_state)210 void CompositeEditCommand::ApplyCommandToComposite(
211     EditCommand* command,
212     EditingState* editing_state) {
213   command->SetParent(this);
214   command->SetSelectionIsDirectional(SelectionIsDirectional());
215   command->DoApply(editing_state);
216   if (editing_state->IsAborted()) {
217     command->SetParent(nullptr);
218     return;
219   }
220   if (auto* simple_edit_command = DynamicTo<SimpleEditCommand>(command)) {
221     command->SetParent(nullptr);
222     EnsureUndoStep()->Append(simple_edit_command);
223   }
224   commands_.push_back(command);
225 }
226 
AppendCommandToUndoStep(CompositeEditCommand * command)227 void CompositeEditCommand::AppendCommandToUndoStep(
228     CompositeEditCommand* command) {
229   EnsureUndoStep()->Append(command->EnsureUndoStep());
230   command->undo_step_ = nullptr;
231   command->SetParent(this);
232   commands_.push_back(command);
233 }
234 
ApplyStyle(const EditingStyle * style,EditingState * editing_state)235 void CompositeEditCommand::ApplyStyle(const EditingStyle* style,
236                                       EditingState* editing_state) {
237   ApplyCommandToComposite(
238       MakeGarbageCollected<ApplyStyleCommand>(GetDocument(), style,
239                                               InputEvent::InputType::kNone),
240       editing_state);
241 }
242 
ApplyStyle(const EditingStyle * style,const Position & start,const Position & end,EditingState * editing_state)243 void CompositeEditCommand::ApplyStyle(const EditingStyle* style,
244                                       const Position& start,
245                                       const Position& end,
246                                       EditingState* editing_state) {
247   ApplyCommandToComposite(
248       MakeGarbageCollected<ApplyStyleCommand>(GetDocument(), style, start, end),
249       editing_state);
250 }
251 
ApplyStyledElement(Element * element,EditingState * editing_state)252 void CompositeEditCommand::ApplyStyledElement(Element* element,
253                                               EditingState* editing_state) {
254   ApplyCommandToComposite(
255       MakeGarbageCollected<ApplyStyleCommand>(element, false), editing_state);
256 }
257 
RemoveStyledElement(Element * element,EditingState * editing_state)258 void CompositeEditCommand::RemoveStyledElement(Element* element,
259                                                EditingState* editing_state) {
260   ApplyCommandToComposite(
261       MakeGarbageCollected<ApplyStyleCommand>(element, true), editing_state);
262 }
263 
InsertParagraphSeparator(EditingState * editing_state,bool use_default_paragraph_element,bool paste_blockqutoe_into_unquoted_area)264 void CompositeEditCommand::InsertParagraphSeparator(
265     EditingState* editing_state,
266     bool use_default_paragraph_element,
267     bool paste_blockqutoe_into_unquoted_area) {
268   ApplyCommandToComposite(MakeGarbageCollected<InsertParagraphSeparatorCommand>(
269                               GetDocument(), use_default_paragraph_element,
270                               paste_blockqutoe_into_unquoted_area),
271                           editing_state);
272 }
273 
IsRemovableBlock(const Node * node)274 bool CompositeEditCommand::IsRemovableBlock(const Node* node) {
275   DCHECK(node);
276   const auto* element = DynamicTo<HTMLDivElement>(node);
277   if (!element)
278     return false;
279 
280   ContainerNode* parent_node = element->parentNode();
281   if (parent_node && parent_node->firstChild() != parent_node->lastChild())
282     return false;
283 
284   if (!element->hasAttributes())
285     return true;
286 
287   return false;
288 }
289 
InsertNodeBefore(Node * insert_child,Node * ref_child,EditingState * editing_state,ShouldAssumeContentIsAlwaysEditable should_assume_content_is_always_editable)290 void CompositeEditCommand::InsertNodeBefore(
291     Node* insert_child,
292     Node* ref_child,
293     EditingState* editing_state,
294     ShouldAssumeContentIsAlwaysEditable
295         should_assume_content_is_always_editable) {
296   ABORT_EDITING_COMMAND_IF(GetDocument().body() == ref_child);
297   ABORT_EDITING_COMMAND_IF(!ref_child->parentNode());
298   // TODO(editing-dev): Use of UpdateStyleAndLayout
299   // needs to be audited.  See http://crbug.com/590369 for more details.
300   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
301   ABORT_EDITING_COMMAND_IF(!HasEditableStyle(*ref_child->parentNode()) &&
302                            ref_child->parentNode()->InActiveDocument());
303   ApplyCommandToComposite(
304       MakeGarbageCollected<InsertNodeBeforeCommand>(
305           insert_child, ref_child, should_assume_content_is_always_editable),
306       editing_state);
307 }
308 
InsertNodeAfter(Node * insert_child,Node * ref_child,EditingState * editing_state)309 void CompositeEditCommand::InsertNodeAfter(Node* insert_child,
310                                            Node* ref_child,
311                                            EditingState* editing_state) {
312   ABORT_EDITING_COMMAND_IF(!ref_child->parentNode());
313   DCHECK(insert_child);
314   DCHECK(ref_child);
315   DCHECK_NE(GetDocument().body(), ref_child);
316   ContainerNode* parent = ref_child->parentNode();
317   DCHECK(parent);
318   DCHECK(!parent->IsShadowRoot()) << parent;
319   if (parent->lastChild() == ref_child) {
320     AppendNode(insert_child, parent, editing_state);
321   } else {
322     DCHECK(ref_child->nextSibling()) << ref_child;
323     InsertNodeBefore(insert_child, ref_child->nextSibling(), editing_state);
324   }
325 }
326 
InsertNodeAt(Node * insert_child,const Position & editing_position,EditingState * editing_state)327 void CompositeEditCommand::InsertNodeAt(Node* insert_child,
328                                         const Position& editing_position,
329                                         EditingState* editing_state) {
330   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
331   ABORT_EDITING_COMMAND_IF(!IsEditablePosition(editing_position));
332   // For editing positions like [table, 0], insert before the table,
333   // likewise for replaced elements, brs, etc.
334   Position p = editing_position.ParentAnchoredEquivalent();
335   Node* ref_child = p.AnchorNode();
336   int offset = p.OffsetInContainerNode();
337 
338   auto* ref_child_text_node = DynamicTo<Text>(ref_child);
339   if (CanHaveChildrenForEditing(ref_child)) {
340     Node* child = ref_child->firstChild();
341     for (int i = 0; child && i < offset; i++)
342       child = child->nextSibling();
343     if (child)
344       InsertNodeBefore(insert_child, child, editing_state);
345     else
346       AppendNode(insert_child, To<ContainerNode>(ref_child), editing_state);
347   } else if (CaretMinOffset(ref_child) >= offset) {
348     InsertNodeBefore(insert_child, ref_child, editing_state);
349   } else if (ref_child_text_node && CaretMaxOffset(ref_child) > offset) {
350     SplitTextNode(ref_child_text_node, offset);
351 
352     // Mutation events (bug 22634) from the text node insertion may have
353     // removed the refChild
354     if (!ref_child->isConnected())
355       return;
356     InsertNodeBefore(insert_child, ref_child, editing_state);
357   } else {
358     InsertNodeAfter(insert_child, ref_child, editing_state);
359   }
360 }
361 
AppendNode(Node * node,ContainerNode * parent,EditingState * editing_state)362 void CompositeEditCommand::AppendNode(Node* node,
363                                       ContainerNode* parent,
364                                       EditingState* editing_state) {
365   // When cloneParagraphUnderNewElement() clones the fallback content
366   // of an OBJECT element, the ASSERT below may fire since the return
367   // value of canHaveChildrenForEditing is not reliable until the layout
368   // object of the OBJECT is created. Hence we ignore this check for OBJECTs.
369   // TODO(yosin): We should move following |ABORT_EDITING_COMMAND_IF|s to
370   // |AppendNodeCommand|.
371   // TODO(yosin): We should get rid of |canHaveChildrenForEditing()|, since
372   // |cloneParagraphUnderNewElement()| attempt to clone non-well-formed HTML,
373   // produced by JavaScript.
374   auto* parent_element = DynamicTo<Element>(parent);
375   ABORT_EDITING_COMMAND_IF(!CanHaveChildrenForEditing(parent) &&
376                            !(parent_element && parent_element->TagQName() ==
377                                                    html_names::kObjectTag));
378   ABORT_EDITING_COMMAND_IF(!HasEditableStyle(*parent) &&
379                            parent->InActiveDocument());
380   ApplyCommandToComposite(MakeGarbageCollected<AppendNodeCommand>(parent, node),
381                           editing_state);
382 }
383 
RemoveAllChildrenIfPossible(ContainerNode * container,EditingState * editing_state,ShouldAssumeContentIsAlwaysEditable should_assume_content_is_always_editable)384 void CompositeEditCommand::RemoveAllChildrenIfPossible(
385     ContainerNode* container,
386     EditingState* editing_state,
387     ShouldAssumeContentIsAlwaysEditable
388         should_assume_content_is_always_editable) {
389   Node* child = container->firstChild();
390   while (child) {
391     Node* const next = child->nextSibling();
392     RemoveNode(child, editing_state, should_assume_content_is_always_editable);
393     if (editing_state->IsAborted())
394       return;
395     if (next && next->parentNode() != container) {
396       // |RemoveNode()| moves |next| outside |node|.
397       return;
398     }
399     child = next;
400   }
401 }
402 
RemoveChildrenInRange(Node * node,unsigned from,unsigned to,EditingState * editing_state)403 void CompositeEditCommand::RemoveChildrenInRange(Node* node,
404                                                  unsigned from,
405                                                  unsigned to,
406                                                  EditingState* editing_state) {
407   HeapVector<Member<Node>> children;
408   Node* child = NodeTraversal::ChildAt(*node, from);
409   for (unsigned i = from; child && i < to; i++, child = child->nextSibling())
410     children.push_back(child);
411 
412   size_t size = children.size();
413   for (wtf_size_t i = 0; i < size; ++i) {
414     RemoveNode(children[i].Release(), editing_state);
415     if (editing_state->IsAborted())
416       return;
417   }
418 }
419 
RemoveNode(Node * node,EditingState * editing_state,ShouldAssumeContentIsAlwaysEditable should_assume_content_is_always_editable)420 void CompositeEditCommand::RemoveNode(
421     Node* node,
422     EditingState* editing_state,
423     ShouldAssumeContentIsAlwaysEditable
424         should_assume_content_is_always_editable) {
425   if (!node || !node->NonShadowBoundaryParentNode())
426     return;
427   ABORT_EDITING_COMMAND_IF(!node->GetDocument().GetFrame());
428   ApplyCommandToComposite(MakeGarbageCollected<RemoveNodeCommand>(
429                               node, should_assume_content_is_always_editable),
430                           editing_state);
431 }
432 
RemoveNodePreservingChildren(Node * node,EditingState * editing_state,ShouldAssumeContentIsAlwaysEditable should_assume_content_is_always_editable)433 void CompositeEditCommand::RemoveNodePreservingChildren(
434     Node* node,
435     EditingState* editing_state,
436     ShouldAssumeContentIsAlwaysEditable
437         should_assume_content_is_always_editable) {
438   ABORT_EDITING_COMMAND_IF(!node->GetDocument().GetFrame());
439   ApplyCommandToComposite(
440       MakeGarbageCollected<RemoveNodePreservingChildrenCommand>(
441           node, should_assume_content_is_always_editable),
442       editing_state);
443 }
444 
RemoveNodeAndPruneAncestors(Node * node,EditingState * editing_state,Node * exclude_node)445 void CompositeEditCommand::RemoveNodeAndPruneAncestors(
446     Node* node,
447     EditingState* editing_state,
448     Node* exclude_node) {
449   DCHECK_NE(node, exclude_node);
450   ContainerNode* parent = node->parentNode();
451   RemoveNode(node, editing_state);
452   if (editing_state->IsAborted())
453     return;
454   Prune(parent, editing_state, exclude_node);
455 }
456 
MoveRemainingSiblingsToNewParent(Node * node,Node * past_last_node_to_move,Element * new_parent,EditingState * editing_state)457 void CompositeEditCommand::MoveRemainingSiblingsToNewParent(
458     Node* node,
459     Node* past_last_node_to_move,
460     Element* new_parent,
461     EditingState* editing_state) {
462   NodeVector nodes_to_remove;
463 
464   for (; node && node != past_last_node_to_move; node = node->nextSibling())
465     nodes_to_remove.push_back(node);
466 
467   for (unsigned i = 0; i < nodes_to_remove.size(); i++) {
468     RemoveNode(nodes_to_remove[i], editing_state);
469     if (editing_state->IsAborted())
470       return;
471     AppendNode(nodes_to_remove[i], new_parent, editing_state);
472     if (editing_state->IsAborted())
473       return;
474   }
475 }
476 
UpdatePositionForNodeRemovalPreservingChildren(Position & position,Node & node)477 void CompositeEditCommand::UpdatePositionForNodeRemovalPreservingChildren(
478     Position& position,
479     Node& node) {
480   int offset =
481       position.IsOffsetInAnchor() ? position.OffsetInContainerNode() : 0;
482   position = ComputePositionForNodeRemoval(position, node);
483   if (offset == 0)
484     return;
485   position = Position::CreateWithoutValidationDeprecated(
486       *position.ComputeContainerNode(), offset);
487 }
488 
489 HTMLSpanElement*
ReplaceElementWithSpanPreservingChildrenAndAttributes(HTMLElement * node)490 CompositeEditCommand::ReplaceElementWithSpanPreservingChildrenAndAttributes(
491     HTMLElement* node) {
492   // It would also be possible to implement all of ReplaceNodeWithSpanCommand
493   // as a series of existing smaller edit commands.  Someone who wanted to
494   // reduce the number of edit commands could do so here.
495   auto* command = MakeGarbageCollected<ReplaceNodeWithSpanCommand>(node);
496   // ReplaceNodeWithSpanCommand is never aborted.
497   ApplyCommandToComposite(command, ASSERT_NO_EDITING_ABORT);
498   // Returning a raw pointer here is OK because the command is retained by
499   // applyCommandToComposite (thus retaining the span), and the span is also
500   // in the DOM tree, and thus alive whie it has a parent.
501   DCHECK(command->SpanElement()->isConnected()) << command->SpanElement();
502   return command->SpanElement();
503 }
504 
Prune(Node * node,EditingState * editing_state,Node * exclude_node)505 void CompositeEditCommand::Prune(Node* node,
506                                  EditingState* editing_state,
507                                  Node* exclude_node) {
508   if (Node* highest_node_to_remove =
509           HighestNodeToRemoveInPruning(node, exclude_node))
510     RemoveNode(highest_node_to_remove, editing_state);
511 }
512 
SplitTextNode(Text * node,unsigned offset)513 void CompositeEditCommand::SplitTextNode(Text* node, unsigned offset) {
514   // SplitTextNodeCommand is never aborted.
515   ApplyCommandToComposite(
516       MakeGarbageCollected<SplitTextNodeCommand>(node, offset),
517       ASSERT_NO_EDITING_ABORT);
518 }
519 
SplitElement(Element * element,Node * at_child)520 void CompositeEditCommand::SplitElement(Element* element, Node* at_child) {
521   // SplitElementCommand is never aborted.
522   ApplyCommandToComposite(
523       MakeGarbageCollected<SplitElementCommand>(element, at_child),
524       ASSERT_NO_EDITING_ABORT);
525 }
526 
MergeIdenticalElements(Element * first,Element * second,EditingState * editing_state)527 void CompositeEditCommand::MergeIdenticalElements(Element* first,
528                                                   Element* second,
529                                                   EditingState* editing_state) {
530   DCHECK(!first->IsDescendantOf(second)) << first << " " << second;
531   DCHECK_NE(second, first);
532   if (first->nextSibling() != second) {
533     RemoveNode(second, editing_state);
534     if (editing_state->IsAborted())
535       return;
536     InsertNodeAfter(second, first, editing_state);
537     if (editing_state->IsAborted())
538       return;
539   }
540   ApplyCommandToComposite(
541       MakeGarbageCollected<MergeIdenticalElementsCommand>(first, second),
542       editing_state);
543 }
544 
WrapContentsInDummySpan(Element * element)545 void CompositeEditCommand::WrapContentsInDummySpan(Element* element) {
546   // WrapContentsInDummySpanCommand is never aborted.
547   ApplyCommandToComposite(
548       MakeGarbageCollected<WrapContentsInDummySpanCommand>(element),
549       ASSERT_NO_EDITING_ABORT);
550 }
551 
SplitTextNodeContainingElement(Text * text,unsigned offset)552 void CompositeEditCommand::SplitTextNodeContainingElement(Text* text,
553                                                           unsigned offset) {
554   // SplitTextNodeContainingElementCommand is never aborted.
555   ApplyCommandToComposite(
556       MakeGarbageCollected<SplitTextNodeContainingElementCommand>(text, offset),
557       ASSERT_NO_EDITING_ABORT);
558 }
559 
InsertTextIntoNode(Text * node,unsigned offset,const String & text)560 void CompositeEditCommand::InsertTextIntoNode(Text* node,
561                                               unsigned offset,
562                                               const String& text) {
563   // InsertIntoTextNodeCommand is never aborted.
564   if (!text.IsEmpty())
565     ApplyCommandToComposite(
566         MakeGarbageCollected<InsertIntoTextNodeCommand>(node, offset, text),
567         ASSERT_NO_EDITING_ABORT);
568 }
569 
DeleteTextFromNode(Text * node,unsigned offset,unsigned count)570 void CompositeEditCommand::DeleteTextFromNode(Text* node,
571                                               unsigned offset,
572                                               unsigned count) {
573   // DeleteFromTextNodeCommand is never aborted.
574   ApplyCommandToComposite(
575       MakeGarbageCollected<DeleteFromTextNodeCommand>(node, offset, count),
576       ASSERT_NO_EDITING_ABORT);
577 }
578 
ReplaceTextInNode(Text * node,unsigned offset,unsigned count,const String & replacement_text)579 void CompositeEditCommand::ReplaceTextInNode(Text* node,
580                                              unsigned offset,
581                                              unsigned count,
582                                              const String& replacement_text) {
583   // SetCharacterDataCommand is never aborted.
584   ApplyCommandToComposite(MakeGarbageCollected<SetCharacterDataCommand>(
585                               node, offset, count, replacement_text),
586                           ASSERT_NO_EDITING_ABORT);
587 }
588 
ReplaceSelectedTextInNode(const String & text)589 Position CompositeEditCommand::ReplaceSelectedTextInNode(const String& text) {
590   const Position& start = EndingSelection().Start();
591   const Position& end = EndingSelection().End();
592   auto* text_node = DynamicTo<Text>(start.ComputeContainerNode());
593   if (!text_node || text_node != end.ComputeContainerNode() ||
594       IsTabHTMLSpanElementTextNode(text_node))
595     return Position();
596 
597   ReplaceTextInNode(text_node, start.OffsetInContainerNode(),
598                     end.OffsetInContainerNode() - start.OffsetInContainerNode(),
599                     text);
600 
601   return Position(text_node, start.OffsetInContainerNode() + text.length());
602 }
603 
PositionOutsideTabSpan(const Position & pos)604 Position CompositeEditCommand::PositionOutsideTabSpan(const Position& pos) {
605   if (!IsTabHTMLSpanElementTextNode(pos.AnchorNode()))
606     return pos;
607 
608   switch (pos.AnchorType()) {
609     case PositionAnchorType::kBeforeChildren:
610     case PositionAnchorType::kAfterChildren:
611       NOTREACHED();
612       return pos;
613     case PositionAnchorType::kOffsetInAnchor:
614       break;
615     case PositionAnchorType::kBeforeAnchor:
616       return Position::InParentBeforeNode(*pos.AnchorNode());
617     case PositionAnchorType::kAfterAnchor:
618       return Position::InParentAfterNode(*pos.AnchorNode());
619   }
620 
621   HTMLSpanElement* tab_span = TabSpanElement(pos.ComputeContainerNode());
622   DCHECK(tab_span);
623 
624   // TODO(editing-dev): Hoist this UpdateStyleAndLayout
625   // to the callers. See crbug.com/590369 for details.
626   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
627 
628   if (pos.OffsetInContainerNode() <= CaretMinOffset(pos.ComputeContainerNode()))
629     return Position::InParentBeforeNode(*tab_span);
630 
631   if (pos.OffsetInContainerNode() >= CaretMaxOffset(pos.ComputeContainerNode()))
632     return Position::InParentAfterNode(*tab_span);
633 
634   SplitTextNodeContainingElement(To<Text>(pos.ComputeContainerNode()),
635                                  pos.OffsetInContainerNode());
636   return Position::InParentBeforeNode(*tab_span);
637 }
638 
InsertNodeAtTabSpanPosition(Node * node,const Position & pos,EditingState * editing_state)639 void CompositeEditCommand::InsertNodeAtTabSpanPosition(
640     Node* node,
641     const Position& pos,
642     EditingState* editing_state) {
643   // insert node before, after, or at split of tab span
644   InsertNodeAt(node, PositionOutsideTabSpan(pos), editing_state);
645 }
646 
DeleteSelection(EditingState * editing_state,const DeleteSelectionOptions & options)647 bool CompositeEditCommand::DeleteSelection(
648     EditingState* editing_state,
649     const DeleteSelectionOptions& options) {
650   if (!EndingSelection().IsRange())
651     return true;
652 
653   ApplyCommandToComposite(
654       MakeGarbageCollected<DeleteSelectionCommand>(GetDocument(), options),
655       editing_state);
656   if (editing_state->IsAborted())
657     return false;
658 
659   if (!EndingSelection().IsValidFor(GetDocument())) {
660     editing_state->Abort();
661     return false;
662   }
663   return true;
664 }
665 
RemoveCSSProperty(Element * element,CSSPropertyID property)666 void CompositeEditCommand::RemoveCSSProperty(Element* element,
667                                              CSSPropertyID property) {
668   // RemoveCSSPropertyCommand is never aborted.
669   ApplyCommandToComposite(MakeGarbageCollected<RemoveCSSPropertyCommand>(
670                               GetDocument(), element, property),
671                           ASSERT_NO_EDITING_ABORT);
672 }
673 
RemoveElementAttribute(Element * element,const QualifiedName & attribute)674 void CompositeEditCommand::RemoveElementAttribute(
675     Element* element,
676     const QualifiedName& attribute) {
677   SetNodeAttribute(element, attribute, AtomicString());
678 }
679 
SetNodeAttribute(Element * element,const QualifiedName & attribute,const AtomicString & value)680 void CompositeEditCommand::SetNodeAttribute(Element* element,
681                                             const QualifiedName& attribute,
682                                             const AtomicString& value) {
683   // SetNodeAttributeCommand is never aborted.
684   ApplyCommandToComposite(
685       MakeGarbageCollected<SetNodeAttributeCommand>(element, attribute, value),
686       ASSERT_NO_EDITING_ABORT);
687 }
688 
CanRebalance(const Position & position) const689 bool CompositeEditCommand::CanRebalance(const Position& position) const {
690   // TODO(editing-dev): Use of UpdateStyleAndLayout()
691   // needs to be audited.  See http://crbug.com/590369 for more details.
692   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
693 
694   auto* text_node = DynamicTo<Text>(position.ComputeContainerNode());
695   if (!position.IsOffsetInAnchor() || !text_node ||
696       !HasRichlyEditableStyle(*text_node))
697     return false;
698 
699   if (text_node->length() == 0)
700     return false;
701 
702   LayoutText* layout_text = text_node->GetLayoutObject();
703   if (layout_text && !layout_text->Style()->CollapseWhiteSpace())
704     return false;
705 
706   return true;
707 }
708 
709 // FIXME: Doesn't go into text nodes that contribute adjacent text (siblings,
710 // cousins, etc).
RebalanceWhitespaceAt(const Position & position)711 void CompositeEditCommand::RebalanceWhitespaceAt(const Position& position) {
712   Node* node = position.ComputeContainerNode();
713   if (!CanRebalance(position))
714     return;
715 
716   // If the rebalance is for the single offset, and neither text[offset] nor
717   // text[offset - 1] are some form of whitespace, do nothing.
718   int offset = position.ComputeOffsetInContainerNode();
719   String text = To<Text>(node)->data();
720   if (!IsWhitespace(text[offset])) {
721     offset--;
722     if (offset < 0 || !IsWhitespace(text[offset]))
723       return;
724   }
725 
726   RebalanceWhitespaceOnTextSubstring(To<Text>(node),
727                                      position.OffsetInContainerNode(),
728                                      position.OffsetInContainerNode());
729 }
730 
RebalanceWhitespaceOnTextSubstring(Text * text_node,int start_offset,int end_offset)731 void CompositeEditCommand::RebalanceWhitespaceOnTextSubstring(Text* text_node,
732                                                               int start_offset,
733                                                               int end_offset) {
734   String text = text_node->data();
735   DCHECK(!text.IsEmpty());
736 
737   // Set upstream and downstream to define the extent of the whitespace
738   // surrounding text[offset].
739   int upstream = start_offset;
740   while (upstream > 0 && IsWhitespace(text[upstream - 1]))
741     upstream--;
742 
743   int downstream = end_offset;
744   while ((unsigned)downstream < text.length() && IsWhitespace(text[downstream]))
745     downstream++;
746 
747   int length = downstream - upstream;
748   if (!length)
749     return;
750 
751   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
752   VisiblePosition visible_upstream_pos =
753       CreateVisiblePosition(Position(text_node, upstream));
754   VisiblePosition visible_downstream_pos =
755       CreateVisiblePosition(Position(text_node, downstream));
756 
757   String string = text.Substring(upstream, length);
758   // FIXME: Because of the problem mentioned at the top of this function, we
759   // must also use nbsps at the start/end of the string because this function
760   // doesn't get all surrounding whitespace, just the whitespace in the
761   // current text node. However, if the next sibling node is a text node
762   // (not empty, see http://crbug.com/632300), we should use a plain space.
763   // See http://crbug.com/310149
764   auto* next_text_node = DynamicTo<Text>(text_node->nextSibling());
765   const bool next_sibling_is_text_node =
766       next_text_node && next_text_node->data().length() &&
767       !IsWhitespace(next_text_node->data()[0]);
768   const bool should_emit_nbs_pbefore_end =
769       (IsEndOfParagraph(visible_downstream_pos) ||
770        (unsigned)downstream == text.length()) &&
771       !next_sibling_is_text_node;
772   String rebalanced_string = StringWithRebalancedWhitespace(
773       string, IsStartOfParagraph(visible_upstream_pos) || !upstream,
774       should_emit_nbs_pbefore_end);
775 
776   if (string != rebalanced_string)
777     ReplaceTextInNode(text_node, upstream, length, rebalanced_string);
778 }
779 
PrepareWhitespaceAtPositionForSplit(Position & position)780 void CompositeEditCommand::PrepareWhitespaceAtPositionForSplit(
781     Position& position) {
782   if (!IsRichlyEditablePosition(position))
783     return;
784 
785   auto* text_node = DynamicTo<Text>(position.AnchorNode());
786   if (!text_node)
787     return;
788 
789   if (text_node->length() == 0)
790     return;
791   LayoutText* layout_text = text_node->GetLayoutObject();
792   if (layout_text && !layout_text->Style()->CollapseWhiteSpace())
793     return;
794 
795   // Delete collapsed whitespace so that inserting nbsps doesn't uncollapse it.
796   Position upstream_pos = MostBackwardCaretPosition(position);
797   DeleteInsignificantText(upstream_pos, MostForwardCaretPosition(position));
798 
799   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
800   position = MostForwardCaretPosition(upstream_pos);
801   VisiblePosition visible_pos = CreateVisiblePosition(position);
802   VisiblePosition previous_visible_pos = PreviousPositionOf(visible_pos);
803   ReplaceCollapsibleWhitespaceWithNonBreakingSpaceIfNeeded(
804       previous_visible_pos);
805 
806   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
807   ReplaceCollapsibleWhitespaceWithNonBreakingSpaceIfNeeded(
808       CreateVisiblePosition(position));
809 }
810 
811 void CompositeEditCommand::
ReplaceCollapsibleWhitespaceWithNonBreakingSpaceIfNeeded(const VisiblePosition & visible_position)812     ReplaceCollapsibleWhitespaceWithNonBreakingSpaceIfNeeded(
813         const VisiblePosition& visible_position) {
814   if (!IsCollapsibleWhitespace(CharacterAfter(visible_position)))
815     return;
816   Position pos = MostForwardCaretPosition(visible_position.DeepEquivalent());
817   auto* container_text_node = DynamicTo<Text>(pos.ComputeContainerNode());
818   if (!container_text_node)
819     return;
820   ReplaceTextInNode(container_text_node, pos.OffsetInContainerNode(), 1,
821                     NonBreakingSpaceString());
822 }
823 
RebalanceWhitespace()824 void CompositeEditCommand::RebalanceWhitespace() {
825   VisibleSelection selection = EndingVisibleSelection();
826   if (selection.IsNone())
827     return;
828 
829   RebalanceWhitespaceAt(selection.Start());
830   if (selection.IsRange())
831     RebalanceWhitespaceAt(selection.End());
832 }
833 
DeleteInsignificantText(Text * text_node,unsigned start,unsigned end)834 void CompositeEditCommand::DeleteInsignificantText(Text* text_node,
835                                                    unsigned start,
836                                                    unsigned end) {
837   if (!text_node || start >= end)
838     return;
839 
840   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
841 
842   LayoutText* text_layout_object = text_node->GetLayoutObject();
843   if (!text_layout_object)
844     return;
845 
846   if (!text_layout_object->HasInlineFragments()) {
847     // whole text node is empty
848     // Removing a Text node won't dispatch synchronous events.
849     RemoveNode(text_node, ASSERT_NO_EDITING_ABORT);
850     return;
851   }
852   unsigned length = text_node->length();
853   if (start >= length || end > length)
854     return;
855 
856   if (text_layout_object->IsInLayoutNGInlineFormattingContext()) {
857     const String string = PlainText(
858         EphemeralRange(Position(*text_node, start), Position(*text_node, end)));
859     if (string.IsEmpty())
860       return DeleteTextFromNode(text_node, start, end - start);
861     // Replace the text between start and end with collapsed version.
862     return ReplaceTextInNode(text_node, start, end - start, string);
863   }
864 
865   Vector<InlineTextBox*> sorted_text_boxes;
866   wtf_size_t sorted_text_boxes_position = 0;
867 
868   for (InlineTextBox* text_box : text_layout_object->TextBoxes())
869     sorted_text_boxes.push_back(text_box);
870 
871   // If there is mixed directionality text, the boxes can be out of order,
872   // (like Arabic with embedded LTR), so sort them first.
873   if (text_layout_object->ContainsReversedText())
874     std::sort(sorted_text_boxes.begin(), sorted_text_boxes.end(),
875               InlineTextBox::CompareByStart);
876   InlineTextBox* box = sorted_text_boxes.IsEmpty()
877                            ? 0
878                            : sorted_text_boxes[sorted_text_boxes_position];
879 
880   unsigned removed = 0;
881   InlineTextBox* prev_box = nullptr;
882   String str;
883 
884   // This loop structure works to process all gaps preceding a box,
885   // and also will look at the gap after the last box.
886   while (prev_box || box) {
887     unsigned gap_start = prev_box ? prev_box->Start() + prev_box->Len() : 0;
888     if (end < gap_start) {
889       // No more chance for any intersections
890       break;
891     }
892 
893     unsigned gap_end = box ? box->Start() : length;
894     bool indices_intersect = start <= gap_end && end >= gap_start;
895     int gap_len = gap_end - gap_start;
896     if (indices_intersect && gap_len > 0) {
897       gap_start = std::max(gap_start, start);
898       if (str.IsNull())
899         str = text_node->data().Substring(start, end - start);
900       // remove text in the gap
901       str.Remove(gap_start - start - removed, gap_len);
902       removed += gap_len;
903     }
904 
905     prev_box = box;
906     if (box) {
907       if (++sorted_text_boxes_position < sorted_text_boxes.size())
908         box = sorted_text_boxes[sorted_text_boxes_position];
909       else
910         box = nullptr;
911     }
912   }
913 
914   if (!str.IsNull()) {
915     // Replace the text between start and end with our pruned version.
916     if (!str.IsEmpty()) {
917       ReplaceTextInNode(text_node, start, end - start, str);
918     } else {
919       // Assert that we are not going to delete all of the text in the node.
920       // If we were, that should have been done above with the call to
921       // removeNode and return.
922       DCHECK(start > 0 || end - start < text_node->length());
923       DeleteTextFromNode(text_node, start, end - start);
924     }
925   }
926 }
927 
DeleteInsignificantText(const Position & start,const Position & end)928 void CompositeEditCommand::DeleteInsignificantText(const Position& start,
929                                                    const Position& end) {
930   if (start.IsNull() || end.IsNull())
931     return;
932 
933   if (ComparePositions(start, end) >= 0)
934     return;
935 
936   HeapVector<Member<Text>> nodes;
937   for (Node& node : NodeTraversal::StartsAt(*start.AnchorNode())) {
938     if (auto* text_node = DynamicTo<Text>(&node))
939       nodes.push_back(text_node);
940     if (&node == end.AnchorNode())
941       break;
942   }
943 
944   for (const auto& node : nodes) {
945     Text* text_node = node;
946     int start_offset = text_node == start.AnchorNode()
947                            ? start.ComputeOffsetInContainerNode()
948                            : 0;
949     int end_offset = text_node == end.AnchorNode()
950                          ? end.ComputeOffsetInContainerNode()
951                          : static_cast<int>(text_node->length());
952     DeleteInsignificantText(text_node, start_offset, end_offset);
953   }
954 }
955 
DeleteInsignificantTextDownstream(const Position & pos)956 void CompositeEditCommand::DeleteInsignificantTextDownstream(
957     const Position& pos) {
958   DCHECK(!GetDocument().NeedsLayoutTreeUpdate());
959   Position end = MostForwardCaretPosition(
960       NextPositionOf(CreateVisiblePosition(pos)).DeepEquivalent());
961   DeleteInsignificantText(pos, end);
962 }
963 
AppendBlockPlaceholder(Element * container,EditingState * editing_state)964 HTMLBRElement* CompositeEditCommand::AppendBlockPlaceholder(
965     Element* container,
966     EditingState* editing_state) {
967   if (!container)
968     return nullptr;
969 
970   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
971 
972   // Should assert isLayoutBlockFlow || isInlineFlow when deletion improves. See
973   // 4244964.
974   DCHECK(container->GetLayoutObject()) << container;
975 
976   auto* placeholder = MakeGarbageCollected<HTMLBRElement>(GetDocument());
977   AppendNode(placeholder, container, editing_state);
978   if (editing_state->IsAborted())
979     return nullptr;
980   return placeholder;
981 }
982 
InsertBlockPlaceholder(const Position & pos,EditingState * editing_state)983 HTMLBRElement* CompositeEditCommand::InsertBlockPlaceholder(
984     const Position& pos,
985     EditingState* editing_state) {
986   if (pos.IsNull())
987     return nullptr;
988 
989   // Should assert isLayoutBlockFlow || isInlineFlow when deletion improves. See
990   // 4244964.
991   DCHECK(pos.AnchorNode()->GetLayoutObject()) << pos;
992 
993   auto* placeholder = MakeGarbageCollected<HTMLBRElement>(GetDocument());
994   InsertNodeAt(placeholder, pos, editing_state);
995   if (editing_state->IsAborted())
996     return nullptr;
997   return placeholder;
998 }
999 
IsEmptyListItem(const LayoutBlockFlow & block_flow)1000 static bool IsEmptyListItem(const LayoutBlockFlow& block_flow) {
1001   if (block_flow.IsLayoutNGListItem())
1002     return !block_flow.FirstChild();
1003   if (block_flow.IsListItem())
1004     return To<LayoutListItem>(block_flow).IsEmpty();
1005   return false;
1006 }
1007 
AddBlockPlaceholderIfNeeded(Element * container,EditingState * editing_state)1008 HTMLBRElement* CompositeEditCommand::AddBlockPlaceholderIfNeeded(
1009     Element* container,
1010     EditingState* editing_state) {
1011   if (!container)
1012     return nullptr;
1013 
1014   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
1015 
1016   auto* block = DynamicTo<LayoutBlockFlow>(container->GetLayoutObject());
1017   if (!block)
1018     return nullptr;
1019 
1020   // append the placeholder to make sure it follows
1021   // any unrendered blocks
1022   if (block->Size().Height() == 0 || IsEmptyListItem(*block))
1023     return AppendBlockPlaceholder(container, editing_state);
1024 
1025   return nullptr;
1026 }
1027 
1028 // Assumes that the position is at a placeholder and does the removal without
1029 // much checking.
RemovePlaceholderAt(const Position & p)1030 void CompositeEditCommand::RemovePlaceholderAt(const Position& p) {
1031   DCHECK(LineBreakExistsAtPosition(p)) << p;
1032 
1033   // We are certain that the position is at a line break, but it may be a br or
1034   // a preserved newline.
1035   if (IsA<HTMLBRElement>(*p.AnchorNode())) {
1036     // Removing a BR element won't dispatch synchronous events.
1037     RemoveNode(p.AnchorNode(), ASSERT_NO_EDITING_ABORT);
1038     return;
1039   }
1040 
1041   DeleteTextFromNode(To<Text>(p.AnchorNode()), p.OffsetInContainerNode(), 1);
1042 }
1043 
InsertNewDefaultParagraphElementAt(const Position & position,EditingState * editing_state)1044 HTMLElement* CompositeEditCommand::InsertNewDefaultParagraphElementAt(
1045     const Position& position,
1046     EditingState* editing_state) {
1047   HTMLElement* paragraph_element = CreateDefaultParagraphElement(GetDocument());
1048   paragraph_element->AppendChild(
1049       MakeGarbageCollected<HTMLBRElement>(GetDocument()));
1050   InsertNodeAt(paragraph_element, position, editing_state);
1051   if (editing_state->IsAborted())
1052     return nullptr;
1053   return paragraph_element;
1054 }
1055 
1056 // If the paragraph is not entirely within it's own block, create one and move
1057 // the paragraph into it, and return that block.  Otherwise return 0.
MoveParagraphContentsToNewBlockIfNecessary(const Position & pos,EditingState * editing_state)1058 HTMLElement* CompositeEditCommand::MoveParagraphContentsToNewBlockIfNecessary(
1059     const Position& pos,
1060     EditingState* editing_state) {
1061   DCHECK(!GetDocument().NeedsLayoutTreeUpdate());
1062   DCHECK(IsEditablePosition(pos)) << pos;
1063 
1064   // It's strange that this function is responsible for verifying that pos has
1065   // not been invalidated by an earlier call to this function.  The caller,
1066   // applyBlockStyle, should do this.
1067   VisiblePosition visible_pos = CreateVisiblePosition(pos);
1068   VisiblePosition visible_paragraph_start = StartOfParagraph(visible_pos);
1069   VisiblePosition visible_paragraph_end = EndOfParagraph(visible_pos);
1070   VisiblePosition next = NextPositionOf(visible_paragraph_end);
1071   VisiblePosition visible_end = next.IsNotNull() ? next : visible_paragraph_end;
1072 
1073   Position upstream_start =
1074       MostBackwardCaretPosition(visible_paragraph_start.DeepEquivalent());
1075   Position upstream_end =
1076       MostBackwardCaretPosition(visible_end.DeepEquivalent());
1077 
1078   // If there are no VisiblePositions in the same block as pos then
1079   // upstreamStart will be outside the paragraph
1080   if (ComparePositions(pos, upstream_start) < 0)
1081     return nullptr;
1082 
1083   // Perform some checks to see if we need to perform work in this function.
1084   if (IsEnclosingBlock(upstream_start.AnchorNode())) {
1085     // If the block is the root editable element, always move content to a new
1086     // block, since it is illegal to modify attributes on the root editable
1087     // element for editing.
1088     if (upstream_start.AnchorNode() == RootEditableElementOf(upstream_start)) {
1089       // If the block is the root editable element and it contains no visible
1090       // content, create a new block but don't try and move content into it,
1091       // since there's nothing for moveParagraphs to move.
1092       if (!HasRenderedNonAnonymousDescendantsWithHeight(
1093               upstream_start.AnchorNode()->GetLayoutObject()))
1094         return InsertNewDefaultParagraphElementAt(upstream_start,
1095                                                   editing_state);
1096     } else if (IsEnclosingBlock(upstream_end.AnchorNode())) {
1097       if (!upstream_end.AnchorNode()->IsDescendantOf(
1098               upstream_start.AnchorNode())) {
1099         // If the paragraph end is a descendant of paragraph start, then we need
1100         // to run the rest of this function. If not, we can bail here.
1101         return nullptr;
1102       }
1103     } else if (EnclosingBlock(upstream_end.AnchorNode()) !=
1104                upstream_start.AnchorNode()) {
1105       // It should be an ancestor of the paragraph start.
1106       // We can bail as we have a full block to work with.
1107       return nullptr;
1108     } else if (IsEndOfEditableOrNonEditableContent(visible_end)) {
1109       // At the end of the editable region. We can bail here as well.
1110       return nullptr;
1111     }
1112   }
1113 
1114   if (visible_paragraph_end.IsNull())
1115     return nullptr;
1116 
1117   HTMLElement* const new_block =
1118       InsertNewDefaultParagraphElementAt(upstream_start, editing_state);
1119   if (editing_state->IsAborted())
1120     return nullptr;
1121   DCHECK(new_block);
1122 
1123   bool end_was_br =
1124       IsA<HTMLBRElement>(*visible_paragraph_end.DeepEquivalent().AnchorNode());
1125 
1126   // Inserting default paragraph element can change visible position. We
1127   // should update visible positions before use them.
1128   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
1129   const VisiblePosition& destination =
1130       VisiblePosition::FirstPositionInNode(*new_block);
1131   if (destination.IsNull()) {
1132     // Reached by CompositeEditingCommandTest
1133     //    .MoveParagraphContentsToNewBlockWithNonEditableStyle.
1134     editing_state->Abort();
1135     return nullptr;
1136   }
1137 
1138   visible_pos = CreateVisiblePosition(pos);
1139   visible_paragraph_start = StartOfParagraph(visible_pos);
1140   visible_paragraph_end = EndOfParagraph(visible_pos);
1141   MoveParagraphs(visible_paragraph_start, visible_paragraph_end, destination,
1142                  editing_state);
1143   if (editing_state->IsAborted())
1144     return nullptr;
1145 
1146   if (new_block->lastChild() && IsA<HTMLBRElement>(*new_block->lastChild()) &&
1147       !end_was_br) {
1148     RemoveNode(new_block->lastChild(), editing_state);
1149     if (editing_state->IsAborted())
1150       return nullptr;
1151   }
1152 
1153   return new_block;
1154 }
1155 
PushAnchorElementDown(Element * anchor_node,EditingState * editing_state)1156 void CompositeEditCommand::PushAnchorElementDown(Element* anchor_node,
1157                                                  EditingState* editing_state) {
1158   if (!anchor_node)
1159     return;
1160 
1161   DCHECK(anchor_node->IsLink()) << anchor_node;
1162 
1163   const VisibleSelection& visible_selection = CreateVisibleSelection(
1164       SelectionInDOMTree::Builder().SelectAllChildren(*anchor_node).Build());
1165   SetEndingSelection(
1166       SelectionForUndoStep::From(visible_selection.AsSelection()));
1167   ApplyStyledElement(anchor_node, editing_state);
1168   if (editing_state->IsAborted())
1169     return;
1170   // Clones of anchorNode have been pushed down, now remove it.
1171   if (anchor_node->isConnected())
1172     RemoveNodePreservingChildren(anchor_node, editing_state);
1173 }
1174 
1175 // Clone the paragraph between start and end under blockElement,
1176 // preserving the hierarchy up to outerNode.
1177 
CloneParagraphUnderNewElement(const Position & start,const Position & end,Node * passed_outer_node,Element * block_element,EditingState * editing_state)1178 void CompositeEditCommand::CloneParagraphUnderNewElement(
1179     const Position& start,
1180     const Position& end,
1181     Node* passed_outer_node,
1182     Element* block_element,
1183     EditingState* editing_state) {
1184   DCHECK_LE(start, end);
1185   DCHECK(passed_outer_node);
1186   DCHECK(block_element);
1187 
1188   // First we clone the outerNode
1189   Node* last_node = nullptr;
1190   Node* outer_node = passed_outer_node;
1191 
1192   if (IsRootEditableElement(*outer_node)) {
1193     last_node = block_element;
1194   } else {
1195     last_node = outer_node->cloneNode(IsDisplayInsideTable(outer_node));
1196     AppendNode(last_node, block_element, editing_state);
1197     if (editing_state->IsAborted())
1198       return;
1199   }
1200 
1201   if (start.AnchorNode() != outer_node && last_node->IsElementNode() &&
1202       start.AnchorNode()->IsDescendantOf(outer_node)) {
1203     HeapVector<Member<Node>> ancestors;
1204 
1205     // Insert each node from innerNode to outerNode (excluded) in a list.
1206     for (Node& runner :
1207          NodeTraversal::InclusiveAncestorsOf(*start.AnchorNode())) {
1208       if (runner == outer_node)
1209         break;
1210       ancestors.push_back(runner);
1211     }
1212 
1213     // Clone every node between start.anchorNode() and outerBlock.
1214 
1215     for (wtf_size_t i = ancestors.size(); i != 0; --i) {
1216       Node* item = ancestors[i - 1].Get();
1217       Node* child = item->cloneNode(IsDisplayInsideTable(item));
1218       AppendNode(child, To<Element>(last_node), editing_state);
1219       if (editing_state->IsAborted())
1220         return;
1221       last_node = child;
1222     }
1223   }
1224 
1225   // Scripts specified in javascript protocol may remove |outerNode|
1226   // during insertion, e.g. <iframe src="javascript:...">
1227   if (!outer_node->isConnected())
1228     return;
1229 
1230   // Handle the case of paragraphs with more than one node,
1231   // cloning all the siblings until end.anchorNode() is reached.
1232 
1233   if (start.AnchorNode() != end.AnchorNode() &&
1234       !start.AnchorNode()->IsDescendantOf(end.AnchorNode())) {
1235     // If end is not a descendant of outerNode we need to
1236     // find the first common ancestor to increase the scope
1237     // of our nextSibling traversal.
1238     while (outer_node && !end.AnchorNode()->IsDescendantOf(outer_node)) {
1239       outer_node = outer_node->parentNode();
1240     }
1241 
1242     if (!outer_node)
1243       return;
1244 
1245     Node* start_node = start.AnchorNode();
1246     for (Node* node =
1247              NodeTraversal::NextSkippingChildren(*start_node, outer_node);
1248          node; node = NodeTraversal::NextSkippingChildren(*node, outer_node)) {
1249       // Move lastNode up in the tree as much as node was moved up in the tree
1250       // by NodeTraversal::nextSkippingChildren, so that the relative depth
1251       // between node and the original start node is maintained in the clone.
1252       while (start_node && last_node &&
1253              start_node->parentNode() != node->parentNode()) {
1254         start_node = start_node->parentNode();
1255         last_node = last_node->parentNode();
1256       }
1257 
1258       if (!last_node || !last_node->parentNode())
1259         return;
1260 
1261       Node* cloned_node = node->cloneNode(true);
1262       InsertNodeAfter(cloned_node, last_node, editing_state);
1263       if (editing_state->IsAborted())
1264         return;
1265       last_node = cloned_node;
1266       if (node == end.AnchorNode() || end.AnchorNode()->IsDescendantOf(node))
1267         break;
1268     }
1269   }
1270 }
1271 
1272 // There are bugs in deletion when it removes a fully selected table/list.
1273 // It expands and removes the entire table/list, but will let content
1274 // before and after the table/list collapse onto one line.
1275 // Deleting a paragraph will leave a placeholder. Remove it (and prune
1276 // empty or unrendered parents).
1277 
CleanupAfterDeletion(EditingState * editing_state)1278 void CompositeEditCommand::CleanupAfterDeletion(EditingState* editing_state) {
1279   CleanupAfterDeletion(editing_state, VisiblePosition());
1280 }
1281 
CleanupAfterDeletion(EditingState * editing_state,VisiblePosition destination)1282 void CompositeEditCommand::CleanupAfterDeletion(EditingState* editing_state,
1283                                                 VisiblePosition destination) {
1284   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
1285 
1286   VisiblePosition caret_after_delete = EndingVisibleSelection().VisibleStart();
1287   Node* destination_node = destination.DeepEquivalent().AnchorNode();
1288   if (caret_after_delete.DeepEquivalent() != destination.DeepEquivalent() &&
1289       IsStartOfParagraph(caret_after_delete) &&
1290       IsEndOfParagraph(caret_after_delete)) {
1291     // Note: We want the rightmost candidate.
1292     Position position =
1293         MostForwardCaretPosition(caret_after_delete.DeepEquivalent());
1294     Node* node = position.AnchorNode();
1295 
1296     // InsertListCommandTest.CleanupNodeSameAsDestinationNode reaches here.
1297     ABORT_EDITING_COMMAND_IF(destination_node == node);
1298     // Bail if we'd remove an ancestor of our destination.
1299     if (destination_node && destination_node->IsDescendantOf(node))
1300       return;
1301 
1302     // Normally deletion will leave a br as a placeholder.
1303     if (IsA<HTMLBRElement>(*node)) {
1304       RemoveNodeAndPruneAncestors(node, editing_state, destination_node);
1305 
1306       // If the selection to move was empty and in an empty block that
1307       // doesn't require a placeholder to prop itself open (like a bordered
1308       // div or an li), remove it during the move (the list removal code
1309       // expects this behavior).
1310     } else if (IsEnclosingBlock(node)) {
1311       // If caret position after deletion and destination position coincides,
1312       // node should not be removed.
1313       if (!RendersInDifferentPosition(position, destination.DeepEquivalent())) {
1314         Prune(node, editing_state, destination_node);
1315         return;
1316       }
1317       RemoveNodeAndPruneAncestors(node, editing_state, destination_node);
1318     } else if (LineBreakExistsAtPosition(position)) {
1319       // There is a preserved '\n' at caretAfterDelete.
1320       // We can safely assume this is a text node.
1321       auto* text_node = To<Text>(node);
1322       if (text_node->length() == 1)
1323         RemoveNodeAndPruneAncestors(node, editing_state, destination_node);
1324       else
1325         DeleteTextFromNode(text_node, position.ComputeOffsetInContainerNode(),
1326                            1);
1327     }
1328   }
1329 }
1330 
1331 // This is a version of moveParagraph that preserves style by keeping the
1332 // original markup. It is currently used only by IndentOutdentCommand but it is
1333 // meant to be used in the future by several other commands such as InsertList
1334 // and the align commands.
1335 // The blockElement parameter is the element to move the paragraph to, outerNode
1336 // is the top element of the paragraph hierarchy.
1337 
MoveParagraphWithClones(const VisiblePosition & start_of_paragraph_to_move,const VisiblePosition & end_of_paragraph_to_move,HTMLElement * block_element,Node * outer_node,EditingState * editing_state)1338 void CompositeEditCommand::MoveParagraphWithClones(
1339     const VisiblePosition& start_of_paragraph_to_move,
1340     const VisiblePosition& end_of_paragraph_to_move,
1341     HTMLElement* block_element,
1342     Node* outer_node,
1343     EditingState* editing_state) {
1344   // InsertListCommandTest.InsertListWithCollapsedVisibility reaches here.
1345   ABORT_EDITING_COMMAND_IF(start_of_paragraph_to_move.IsNull());
1346   ABORT_EDITING_COMMAND_IF(end_of_paragraph_to_move.IsNull());
1347   DCHECK(outer_node);
1348   DCHECK(block_element);
1349 
1350   RelocatablePosition relocatable_before_paragraph(
1351       PreviousPositionOf(start_of_paragraph_to_move).DeepEquivalent());
1352   RelocatablePosition relocatable_after_paragraph(
1353       NextPositionOf(end_of_paragraph_to_move).DeepEquivalent());
1354 
1355   // We upstream() the end and downstream() the start so that we don't include
1356   // collapsed whitespace in the move. When we paste a fragment, spaces after
1357   // the end and before the start are treated as though they were rendered.
1358   Position start =
1359       MostForwardCaretPosition(start_of_paragraph_to_move.DeepEquivalent());
1360   Position end = start_of_paragraph_to_move.DeepEquivalent() ==
1361                          end_of_paragraph_to_move.DeepEquivalent()
1362                      ? start
1363                      : MostBackwardCaretPosition(
1364                            end_of_paragraph_to_move.DeepEquivalent());
1365   if (ComparePositions(start, end) > 0)
1366     end = start;
1367 
1368   CloneParagraphUnderNewElement(start, end, outer_node, block_element,
1369                                 editing_state);
1370 
1371   SetEndingSelection(SelectionForUndoStep::From(
1372       SelectionInDOMTree::Builder().Collapse(start).Extend(end).Build()));
1373   if (!DeleteSelection(
1374           editing_state,
1375           DeleteSelectionOptions::Builder().SetSanitizeMarkup(true).Build()))
1376     return;
1377 
1378   // There are bugs in deletion when it removes a fully selected table/list.
1379   // It expands and removes the entire table/list, but will let content
1380   // before and after the table/list collapse onto one line.
1381 
1382   CleanupAfterDeletion(editing_state);
1383   if (editing_state->IsAborted())
1384     return;
1385 
1386   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
1387 
1388   // Add a br if pruning an empty block level element caused a collapse.  For
1389   // example:
1390   // foo^
1391   // <div>bar</div>
1392   // baz
1393   // Imagine moving 'bar' to ^.  'bar' will be deleted and its div pruned.  That
1394   // would cause 'baz' to collapse onto the line with 'foobar' unless we insert
1395   // a br. Must recononicalize these two VisiblePositions after the pruning
1396   // above.
1397   const VisiblePosition& before_paragraph =
1398       CreateVisiblePosition(relocatable_before_paragraph.GetPosition());
1399   const VisiblePosition& after_paragraph =
1400       CreateVisiblePosition(relocatable_after_paragraph.GetPosition());
1401 
1402   if (before_paragraph.IsNotNull() &&
1403       !IsDisplayInsideTable(before_paragraph.DeepEquivalent().AnchorNode()) &&
1404       ((!IsEndOfParagraph(before_paragraph) &&
1405         !IsStartOfParagraph(before_paragraph)) ||
1406        before_paragraph.DeepEquivalent() == after_paragraph.DeepEquivalent())) {
1407     // FIXME: Trim text between beforeParagraph and afterParagraph if they
1408     // aren't equal.
1409     InsertNodeAt(MakeGarbageCollected<HTMLBRElement>(GetDocument()),
1410                  before_paragraph.DeepEquivalent(), editing_state);
1411   }
1412 }
1413 
MoveParagraph(const VisiblePosition & start_of_paragraph_to_move,const VisiblePosition & end_of_paragraph_to_move,const VisiblePosition & destination,EditingState * editing_state,ShouldPreserveSelection should_preserve_selection,ShouldPreserveStyle should_preserve_style,Node * constraining_ancestor)1414 void CompositeEditCommand::MoveParagraph(
1415     const VisiblePosition& start_of_paragraph_to_move,
1416     const VisiblePosition& end_of_paragraph_to_move,
1417     const VisiblePosition& destination,
1418     EditingState* editing_state,
1419     ShouldPreserveSelection should_preserve_selection,
1420     ShouldPreserveStyle should_preserve_style,
1421     Node* constraining_ancestor) {
1422   DCHECK(!GetDocument().NeedsLayoutTreeUpdate());
1423   DCHECK(IsStartOfParagraph(start_of_paragraph_to_move))
1424       << start_of_paragraph_to_move;
1425   DCHECK(IsEndOfParagraph(end_of_paragraph_to_move))
1426       << end_of_paragraph_to_move;
1427   MoveParagraphs(start_of_paragraph_to_move, end_of_paragraph_to_move,
1428                  destination, editing_state, should_preserve_selection,
1429                  should_preserve_style, constraining_ancestor);
1430 }
1431 
MoveParagraphs(const VisiblePosition & start_of_paragraph_to_move,const VisiblePosition & end_of_paragraph_to_move,const VisiblePosition & destination,EditingState * editing_state,ShouldPreserveSelection should_preserve_selection,ShouldPreserveStyle should_preserve_style,Node * constraining_ancestor)1432 void CompositeEditCommand::MoveParagraphs(
1433     const VisiblePosition& start_of_paragraph_to_move,
1434     const VisiblePosition& end_of_paragraph_to_move,
1435     const VisiblePosition& destination,
1436     EditingState* editing_state,
1437     ShouldPreserveSelection should_preserve_selection,
1438     ShouldPreserveStyle should_preserve_style,
1439     Node* constraining_ancestor) {
1440   DCHECK(!GetDocument().NeedsLayoutTreeUpdate());
1441   DCHECK(start_of_paragraph_to_move.IsNotNull());
1442   DCHECK(end_of_paragraph_to_move.IsNotNull());
1443   DCHECK(destination.IsNotNull());
1444 
1445   if (start_of_paragraph_to_move.DeepEquivalent() ==
1446           destination.DeepEquivalent() ||
1447       start_of_paragraph_to_move.IsNull())
1448     return;
1449 
1450   // Can't move the range to a destination inside itself.
1451   if (destination.DeepEquivalent() >=
1452           start_of_paragraph_to_move.DeepEquivalent() &&
1453       destination.DeepEquivalent() <=
1454           end_of_paragraph_to_move.DeepEquivalent()) {
1455     // Reached by unit test TypingCommandTest.insertLineBreakWithIllFormedHTML
1456     // and ApplyStyleCommandTest.JustifyRightDetachesDestination
1457     editing_state->Abort();
1458     return;
1459   }
1460 
1461   int start_index = -1;
1462   int end_index = -1;
1463   int destination_index = -1;
1464   if (should_preserve_selection == kPreserveSelection &&
1465       !EndingSelection().IsNone()) {
1466     VisiblePosition visible_start = EndingVisibleSelection().VisibleStart();
1467     VisiblePosition visible_end = EndingVisibleSelection().VisibleEnd();
1468 
1469     bool start_after_paragraph =
1470         ComparePositions(visible_start, end_of_paragraph_to_move) > 0;
1471     bool end_before_paragraph =
1472         ComparePositions(visible_end, start_of_paragraph_to_move) < 0;
1473 
1474     if (!start_after_paragraph && !end_before_paragraph) {
1475       bool start_in_paragraph =
1476           ComparePositions(visible_start, start_of_paragraph_to_move) >= 0;
1477       bool end_in_paragraph =
1478           ComparePositions(visible_end, end_of_paragraph_to_move) <= 0;
1479 
1480       const TextIteratorBehavior behavior =
1481           TextIteratorBehavior::AllVisiblePositionsRangeLengthBehavior();
1482 
1483       start_index = 0;
1484       if (start_in_paragraph) {
1485         start_index = TextIterator::RangeLength(
1486             start_of_paragraph_to_move.ToParentAnchoredPosition(),
1487             visible_start.ToParentAnchoredPosition(), behavior);
1488       }
1489 
1490       end_index = 0;
1491       if (end_in_paragraph) {
1492         end_index = TextIterator::RangeLength(
1493             start_of_paragraph_to_move.ToParentAnchoredPosition(),
1494             visible_end.ToParentAnchoredPosition(), behavior);
1495       }
1496     }
1497   }
1498 
1499   RelocatablePosition before_paragraph_position(
1500       PreviousPositionOf(start_of_paragraph_to_move,
1501                          kCannotCrossEditingBoundary)
1502           .DeepEquivalent());
1503   RelocatablePosition after_paragraph_position(
1504       NextPositionOf(end_of_paragraph_to_move, kCannotCrossEditingBoundary)
1505           .DeepEquivalent());
1506 
1507   // We upstream() the end and downstream() the start so that we don't include
1508   // collapsed whitespace in the move. When we paste a fragment, spaces after
1509   // the end and before the start are treated as though they were rendered.
1510   Position start =
1511       MostForwardCaretPosition(start_of_paragraph_to_move.DeepEquivalent());
1512   Position end =
1513       MostBackwardCaretPosition(end_of_paragraph_to_move.DeepEquivalent());
1514 
1515   // FIXME: This is an inefficient way to preserve style on nodes in the
1516   // paragraph to move. It shouldn't matter though, since moved paragraphs will
1517   // usually be quite small.
1518   DocumentFragment* fragment = nullptr;
1519   if (start_of_paragraph_to_move.DeepEquivalent() !=
1520       end_of_paragraph_to_move.DeepEquivalent()) {
1521     const String paragraphs_markup = CreateMarkup(
1522         start.ParentAnchoredEquivalent(), end.ParentAnchoredEquivalent(),
1523         CreateMarkupOptions::Builder()
1524             .SetShouldConvertBlocksToInlines(true)
1525             .SetConstrainingAncestor(constraining_ancestor)
1526             .Build());
1527     fragment = CreateSanitizedFragmentFromMarkupWithContext(
1528         GetDocument(), paragraphs_markup, 0, paragraphs_markup.length(), "");
1529   }
1530 
1531   // A non-empty paragraph's style is moved when we copy and move it.  We don't
1532   // move anything if we're given an empty paragraph, but an empty paragraph can
1533   // have style too, <div><b><br></b></div> for example.  Save it so that we can
1534   // preserve it later.
1535   EditingStyle* style_in_empty_paragraph = nullptr;
1536   if (start_of_paragraph_to_move.DeepEquivalent() ==
1537           end_of_paragraph_to_move.DeepEquivalent() &&
1538       should_preserve_style == kPreserveStyle) {
1539     style_in_empty_paragraph = MakeGarbageCollected<EditingStyle>(
1540         start_of_paragraph_to_move.DeepEquivalent());
1541     style_in_empty_paragraph->MergeTypingStyle(&GetDocument());
1542     // The moved paragraph should assume the block style of the destination.
1543     style_in_empty_paragraph->RemoveBlockProperties(
1544         GetDocument().GetExecutionContext());
1545   }
1546 
1547   // FIXME (5098931): We should add a new insert action
1548   // "WebViewInsertActionMoved" and call shouldInsertFragment here.
1549 
1550   DCHECK(!GetDocument().NeedsLayoutTreeUpdate());
1551 
1552   const VisibleSelection& selection_to_delete = CreateVisibleSelection(
1553       SelectionInDOMTree::Builder().Collapse(start).Extend(end).Build());
1554   SetEndingSelection(
1555       SelectionForUndoStep::From(selection_to_delete.AsSelection()));
1556   if (!DeleteSelection(
1557           editing_state,
1558           DeleteSelectionOptions::Builder().SetSanitizeMarkup(true).Build()))
1559     return;
1560 
1561   DCHECK(destination.DeepEquivalent().IsConnected()) << destination;
1562   CleanupAfterDeletion(editing_state, destination);
1563   if (editing_state->IsAborted())
1564     return;
1565   DCHECK(destination.DeepEquivalent().IsConnected()) << destination;
1566 
1567   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
1568 
1569   // Add a br if pruning an empty block level element caused a collapse. For
1570   // example:
1571   // foo^
1572   // <div>bar</div>
1573   // baz
1574   // Imagine moving 'bar' to ^. 'bar' will be deleted and its div pruned. That
1575   // would cause 'baz' to collapse onto the line with 'foobar' unless we insert
1576   // a br. Must recononicalize these two VisiblePositions after the pruning
1577   // above.
1578   VisiblePosition before_paragraph =
1579       CreateVisiblePosition(before_paragraph_position.GetPosition());
1580   VisiblePosition after_paragraph =
1581       CreateVisiblePosition(after_paragraph_position.GetPosition());
1582   if (before_paragraph.IsNotNull() &&
1583       ((!IsStartOfParagraph(before_paragraph) &&
1584         !IsEndOfParagraph(before_paragraph)) ||
1585        before_paragraph.DeepEquivalent() == after_paragraph.DeepEquivalent())) {
1586     // FIXME: Trim text between beforeParagraph and afterParagraph if they
1587     // aren't equal.
1588     InsertNodeAt(MakeGarbageCollected<HTMLBRElement>(GetDocument()),
1589                  before_paragraph.DeepEquivalent(), editing_state);
1590     if (editing_state->IsAborted())
1591       return;
1592   }
1593 
1594   // TextIterator::rangeLength requires clean layout.
1595   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
1596 
1597   destination_index = TextIterator::RangeLength(
1598       Position::FirstPositionInNode(*GetDocument().documentElement()),
1599       destination.ToParentAnchoredPosition(),
1600       TextIteratorBehavior::AllVisiblePositionsRangeLengthBehavior());
1601 
1602   const VisibleSelection& destination_selection =
1603       CreateVisibleSelection(SelectionInDOMTree::Builder()
1604                                  .Collapse(destination.ToPositionWithAffinity())
1605                                  .Build());
1606   if (EndingSelection().IsNone()) {
1607     // We abort executing command since |destination| becomes invisible.
1608     editing_state->Abort();
1609     return;
1610   }
1611   SetEndingSelection(
1612       SelectionForUndoStep::From(destination_selection.AsSelection()));
1613   ReplaceSelectionCommand::CommandOptions options =
1614       ReplaceSelectionCommand::kSelectReplacement |
1615       ReplaceSelectionCommand::kMovingParagraph;
1616   if (should_preserve_style == kDoNotPreserveStyle)
1617     options |= ReplaceSelectionCommand::kMatchStyle;
1618   ApplyCommandToComposite(MakeGarbageCollected<ReplaceSelectionCommand>(
1619                               GetDocument(), fragment, options),
1620                           editing_state);
1621   if (editing_state->IsAborted())
1622     return;
1623   ABORT_EDITING_COMMAND_IF(!EndingSelection().IsValidFor(GetDocument()));
1624 
1625   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
1626 
1627   // If the selection is in an empty paragraph, restore styles from the old
1628   // empty paragraph to the new empty paragraph.
1629   bool selection_is_empty_paragraph =
1630       EndingSelection().IsCaret() &&
1631       IsStartOfParagraph(EndingVisibleSelection().VisibleStart()) &&
1632       IsEndOfParagraph(EndingVisibleSelection().VisibleStart());
1633   if (style_in_empty_paragraph && selection_is_empty_paragraph) {
1634     ApplyStyle(style_in_empty_paragraph, editing_state);
1635     if (editing_state->IsAborted())
1636       return;
1637   }
1638 
1639   if (should_preserve_selection == kDoNotPreserveSelection || start_index == -1)
1640     return;
1641   Element* document_element = GetDocument().documentElement();
1642   if (!document_element)
1643     return;
1644 
1645   // We need clean layout in order to compute plain-text ranges below.
1646   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
1647 
1648   // Fragment creation (using createMarkup) incorrectly uses regular spaces
1649   // instead of nbsps for some spaces that were rendered (11475), which causes
1650   // spaces to be collapsed during the move operation. This results in a call
1651   // to rangeFromLocationAndLength with a location past the end of the
1652   // document (which will return null).
1653   EphemeralRange start_range = PlainTextRange(destination_index + start_index)
1654                                    .CreateRangeForSelection(*document_element);
1655   if (start_range.IsNull())
1656     return;
1657   EphemeralRange end_range = PlainTextRange(destination_index + end_index)
1658                                  .CreateRangeForSelection(*document_element);
1659   if (end_range.IsNull())
1660     return;
1661   const VisibleSelection& visible_selection =
1662       CreateVisibleSelection(SelectionInDOMTree::Builder()
1663                                  .Collapse(start_range.StartPosition())
1664                                  .Extend(end_range.StartPosition())
1665                                  .Build());
1666   SetEndingSelection(
1667       SelectionForUndoStep::From(visible_selection.AsSelection()));
1668 }
1669 
1670 // FIXME: Send an appropriate shouldDeleteRange call.
BreakOutOfEmptyListItem(EditingState * editing_state)1671 bool CompositeEditCommand::BreakOutOfEmptyListItem(
1672     EditingState* editing_state) {
1673   DCHECK(!GetDocument().NeedsLayoutTreeUpdate());
1674   Node* empty_list_item =
1675       EnclosingEmptyListItem(EndingVisibleSelection().VisibleStart());
1676   if (!empty_list_item)
1677     return false;
1678 
1679   EditingStyle* style =
1680       MakeGarbageCollected<EditingStyle>(EndingSelection().Start());
1681   style->MergeTypingStyle(&GetDocument());
1682 
1683   ContainerNode* list_node = empty_list_item->parentNode();
1684   // FIXME: Can't we do something better when the immediate parent wasn't a list
1685   // node?
1686   if (!list_node ||
1687       (!IsA<HTMLUListElement>(*list_node) &&
1688        !IsA<HTMLOListElement>(*list_node)) ||
1689       !HasEditableStyle(*list_node) ||
1690       list_node == RootEditableElement(*empty_list_item))
1691     return false;
1692 
1693   HTMLElement* new_block = nullptr;
1694   if (ContainerNode* block_enclosing_list = list_node->parentNode()) {
1695     if (block_enclosing_list->HasTagName(
1696             html_names::kLiTag)) {  // listNode is inside another list item
1697       if (CreateVisiblePosition(PositionAfterNode(*block_enclosing_list))
1698               .DeepEquivalent() ==
1699           CreateVisiblePosition(PositionAfterNode(*list_node))
1700               .DeepEquivalent()) {
1701         // If listNode appears at the end of the outer list item, then move
1702         // listNode outside of this list item, e.g.
1703         //   <ul><li>hello <ul><li><br></li></ul> </li></ul>
1704         // should become
1705         //   <ul><li>hello</li> <ul><li><br></li></ul> </ul>
1706         // after this section.
1707         //
1708         // If listNode does NOT appear at the end, then we should consider it as
1709         // a regular paragraph, e.g.
1710         //   <ul><li> <ul><li><br></li></ul> hello</li></ul>
1711         // should become
1712         //   <ul><li> <div><br></div> hello</li></ul>
1713         // at the end
1714         SplitElement(To<Element>(block_enclosing_list), list_node);
1715         RemoveNodePreservingChildren(list_node->parentNode(), editing_state);
1716         if (editing_state->IsAborted())
1717           return false;
1718         new_block = MakeGarbageCollected<HTMLLIElement>(GetDocument());
1719       }
1720       // If listNode does NOT appear at the end of the outer list item, then
1721       // behave as if in a regular paragraph.
1722     } else if (block_enclosing_list->HasTagName(html_names::kOlTag) ||
1723                block_enclosing_list->HasTagName(html_names::kUlTag)) {
1724       new_block = MakeGarbageCollected<HTMLLIElement>(GetDocument());
1725     }
1726   }
1727   if (!new_block)
1728     new_block = CreateDefaultParagraphElement(GetDocument());
1729 
1730   Node* previous_list_node =
1731       empty_list_item->IsElementNode()
1732           ? ElementTraversal::PreviousSibling(*empty_list_item)
1733           : empty_list_item->previousSibling();
1734   Node* next_list_node = empty_list_item->IsElementNode()
1735                              ? ElementTraversal::NextSibling(*empty_list_item)
1736                              : empty_list_item->nextSibling();
1737   if (next_list_node && IsListElementTag(list_node)) {
1738     // If emptyListItem follows another list item or nested list, split the list
1739     // node.
1740     if (IsListItemTag(previous_list_node) ||
1741         IsHTMLListElement(previous_list_node)) {
1742       SplitElement(To<Element>(list_node), empty_list_item);
1743     }
1744 
1745     // If emptyListItem is followed by other list item or nested list, then
1746     // insert newBlock before the list node. Because we have split the
1747     // element, emptyListItem is the first element in the list node.
1748     // i.e. insert newBlock before ul or ol whose first element is emptyListItem
1749     InsertNodeBefore(new_block, list_node, editing_state);
1750     if (editing_state->IsAborted())
1751       return false;
1752     RemoveNode(empty_list_item, editing_state);
1753     if (editing_state->IsAborted())
1754       return false;
1755   } else {
1756     // When emptyListItem does not follow any list item or nested list, insert
1757     // newBlock after the enclosing list node. Remove the enclosing node if
1758     // emptyListItem is the only child; otherwise just remove emptyListItem.
1759     //   <ul>                             <ul>
1760     //     <li>                             <li>
1761     //       abc                              abc
1762     //       <ul>                             <ul>
1763     //         <li>def</li>                     <li>def</li>
1764     //         <li>{}<br></li>    ->          </ul>
1765     //       </ul>                            <div>{}<br></div>
1766     //       ghi                              ghi
1767     //     </li>                            </li>
1768     //   </ul>                            </ul>
1769     InsertNodeAfter(new_block, list_node, editing_state);
1770     if (editing_state->IsAborted())
1771       return false;
1772     RemoveNode(previous_list_node ? empty_list_item : list_node, editing_state);
1773     if (editing_state->IsAborted())
1774       return false;
1775   }
1776 
1777   AppendBlockPlaceholder(new_block, editing_state);
1778   if (editing_state->IsAborted())
1779     return false;
1780 
1781   SetEndingSelection(SelectionForUndoStep::From(
1782       SelectionInDOMTree::Builder()
1783           .Collapse(Position::FirstPositionInNode(*new_block))
1784           .Build()));
1785 
1786   style->PrepareToApplyAt(EndingSelection().Start());
1787   if (!style->IsEmpty()) {
1788     ApplyStyle(style, editing_state);
1789     if (editing_state->IsAborted())
1790       return false;
1791   }
1792 
1793   return true;
1794 }
1795 
1796 // If the caret is in an empty quoted paragraph, and either there is nothing
1797 // before that paragraph, or what is before is unquoted, and the user presses
1798 // delete, unquote that paragraph.
BreakOutOfEmptyMailBlockquotedParagraph(EditingState * editing_state)1799 bool CompositeEditCommand::BreakOutOfEmptyMailBlockquotedParagraph(
1800     EditingState* editing_state) {
1801   if (!EndingSelection().IsCaret())
1802     return false;
1803 
1804   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
1805 
1806   VisiblePosition caret = EndingVisibleSelection().VisibleStart();
1807   auto* highest_blockquote = To<HTMLQuoteElement>(HighestEnclosingNodeOfType(
1808       caret.DeepEquivalent(), &IsMailHTMLBlockquoteElement));
1809   if (!highest_blockquote)
1810     return false;
1811 
1812   if (!IsStartOfParagraph(caret) || !IsEndOfParagraph(caret))
1813     return false;
1814 
1815   VisiblePosition previous =
1816       PreviousPositionOf(caret, kCannotCrossEditingBoundary);
1817   // Only move forward if there's nothing before the caret, or if there's
1818   // unquoted content before it.
1819   if (EnclosingNodeOfType(previous.DeepEquivalent(),
1820                           &IsMailHTMLBlockquoteElement))
1821     return false;
1822 
1823   auto* br = MakeGarbageCollected<HTMLBRElement>(GetDocument());
1824   // We want to replace this quoted paragraph with an unquoted one, so insert a
1825   // br to hold the caret before the highest blockquote.
1826   InsertNodeBefore(br, highest_blockquote, editing_state);
1827   if (editing_state->IsAborted())
1828     return false;
1829 
1830   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
1831 
1832   VisiblePosition at_br = VisiblePosition::BeforeNode(*br);
1833   // If the br we inserted collapsed, for example:
1834   //   foo<br><blockquote>...</blockquote>
1835   // insert a second one.
1836   if (!IsStartOfParagraph(at_br)) {
1837     InsertNodeBefore(MakeGarbageCollected<HTMLBRElement>(GetDocument()), br,
1838                      editing_state);
1839     if (editing_state->IsAborted())
1840       return false;
1841     GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
1842   }
1843   SetEndingSelection(SelectionForUndoStep::From(
1844       SelectionInDOMTree::Builder()
1845           .Collapse(at_br.ToPositionWithAffinity())
1846           .Build()));
1847 
1848   // If this is an empty paragraph there must be a line break here.
1849   if (!LineBreakExistsAtVisiblePosition(caret))
1850     return false;
1851 
1852   Position caret_pos(MostForwardCaretPosition(caret.DeepEquivalent()));
1853   // A line break is either a br or a preserved newline.
1854   DCHECK(
1855       IsA<HTMLBRElement>(caret_pos.AnchorNode()) ||
1856       (caret_pos.AnchorNode()->IsTextNode() &&
1857        caret_pos.AnchorNode()->GetLayoutObject()->Style()->PreserveNewline()))
1858       << caret_pos;
1859 
1860   if (IsA<HTMLBRElement>(*caret_pos.AnchorNode())) {
1861     RemoveNodeAndPruneAncestors(caret_pos.AnchorNode(), editing_state);
1862     if (editing_state->IsAborted())
1863       return false;
1864   } else if (auto* text_node = DynamicTo<Text>(caret_pos.AnchorNode())) {
1865     DCHECK_EQ(caret_pos.ComputeOffsetInContainerNode(), 0);
1866     ContainerNode* parent_node = text_node->parentNode();
1867     // The preserved newline must be the first thing in the node, since
1868     // otherwise the previous paragraph would be quoted, and we verified that it
1869     // wasn't above.
1870     DeleteTextFromNode(text_node, 0, 1);
1871     Prune(parent_node, editing_state);
1872     if (editing_state->IsAborted())
1873       return false;
1874   }
1875 
1876   return true;
1877 }
1878 
1879 // Operations use this function to avoid inserting content into an anchor when
1880 // at the start or the end of that anchor, as in NSTextView.
1881 // FIXME: This is only an approximation of NSTextViews insertion behavior, which
1882 // varies depending on how the caret was made.
PositionAvoidingSpecialElementBoundary(const Position & original,EditingState * editing_state)1883 Position CompositeEditCommand::PositionAvoidingSpecialElementBoundary(
1884     const Position& original,
1885     EditingState* editing_state) {
1886   if (original.IsNull())
1887     return original;
1888 
1889   GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
1890   VisiblePosition visible_pos = CreateVisiblePosition(original);
1891   Element* enclosing_anchor = EnclosingAnchorElement(original);
1892   Position result = original;
1893 
1894   if (!enclosing_anchor)
1895     return result;
1896 
1897   // Don't avoid block level anchors, because that would insert content into the
1898   // wrong paragraph.
1899   if (enclosing_anchor && !IsEnclosingBlock(enclosing_anchor)) {
1900     VisiblePosition first_in_anchor =
1901         VisiblePosition::FirstPositionInNode(*enclosing_anchor);
1902     VisiblePosition last_in_anchor =
1903         VisiblePosition::LastPositionInNode(*enclosing_anchor);
1904     // If visually just after the anchor, insert *inside* the anchor unless it's
1905     // the last VisiblePosition in the document, to match NSTextView.
1906     if (visible_pos.DeepEquivalent() == last_in_anchor.DeepEquivalent()) {
1907       // Make sure anchors are pushed down before avoiding them so that we don't
1908       // also avoid structural elements like lists and blocks (5142012).
1909       if (original.AnchorNode() != enclosing_anchor &&
1910           original.AnchorNode()->parentNode() != enclosing_anchor) {
1911         PushAnchorElementDown(enclosing_anchor, editing_state);
1912         if (editing_state->IsAborted())
1913           return original;
1914         enclosing_anchor = EnclosingAnchorElement(original);
1915         if (!enclosing_anchor)
1916           return original;
1917       }
1918 
1919       GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
1920 
1921       // Don't insert outside an anchor if doing so would skip over a line
1922       // break.  It would probably be safe to move the line break so that we
1923       // could still avoid the anchor here.
1924       Position downstream(
1925           MostForwardCaretPosition(visible_pos.DeepEquivalent()));
1926       if (LineBreakExistsAtVisiblePosition(visible_pos) &&
1927           downstream.AnchorNode()->IsDescendantOf(enclosing_anchor))
1928         return original;
1929 
1930       result = Position::InParentAfterNode(*enclosing_anchor);
1931     }
1932 
1933     // If visually just before an anchor, insert *outside* the anchor unless
1934     // it's the first VisiblePosition in a paragraph, to match NSTextView.
1935     if (visible_pos.DeepEquivalent() == first_in_anchor.DeepEquivalent()) {
1936       // Make sure anchors are pushed down before avoiding them so that we don't
1937       // also avoid structural elements like lists and blocks (5142012).
1938       if (original.AnchorNode() != enclosing_anchor &&
1939           original.AnchorNode()->parentNode() != enclosing_anchor) {
1940         PushAnchorElementDown(enclosing_anchor, editing_state);
1941         if (editing_state->IsAborted())
1942           return original;
1943         enclosing_anchor = EnclosingAnchorElement(original);
1944       }
1945       if (!enclosing_anchor)
1946         return original;
1947 
1948       result = Position::InParentBeforeNode(*enclosing_anchor);
1949     }
1950   }
1951 
1952   if (result.IsNull() || !RootEditableElementOf(result))
1953     result = original;
1954 
1955   return result;
1956 }
1957 
1958 // Splits the tree parent by parent until we reach the specified ancestor. We
1959 // use VisiblePositions to determine if the split is necessary. Returns the last
1960 // split node.
SplitTreeToNode(Node * start,Node * end,bool should_split_ancestor)1961 Node* CompositeEditCommand::SplitTreeToNode(Node* start,
1962                                             Node* end,
1963                                             bool should_split_ancestor) {
1964   DCHECK(start);
1965   DCHECK(end);
1966   DCHECK_NE(start, end);
1967 
1968   if (should_split_ancestor && end->parentNode())
1969     end = end->parentNode();
1970   if (!start->IsDescendantOf(end))
1971     return end;
1972 
1973   Node* end_node = end;
1974   Node* node = nullptr;
1975   for (node = start; node->parentNode() != end_node;
1976        node = node->parentNode()) {
1977     Element* parent_element = node->parentElement();
1978     if (!parent_element)
1979       break;
1980 
1981     GetDocument().UpdateStyleAndLayout(DocumentUpdateReason::kEditing);
1982 
1983     // Do not split a node when doing so introduces an empty node.
1984     VisiblePosition position_in_parent =
1985         VisiblePosition::FirstPositionInNode(*parent_element);
1986     VisiblePosition position_in_node =
1987         CreateVisiblePosition(FirstPositionInOrBeforeNode(*node));
1988     if (position_in_parent.DeepEquivalent() !=
1989         position_in_node.DeepEquivalent())
1990       SplitElement(parent_element, node);
1991   }
1992 
1993   return node;
1994 }
1995 
SetStartingSelection(const SelectionForUndoStep & selection)1996 void CompositeEditCommand::SetStartingSelection(
1997     const SelectionForUndoStep& selection) {
1998   for (CompositeEditCommand* command = this;; command = command->Parent()) {
1999     if (UndoStep* undo_step = command->GetUndoStep()) {
2000       DCHECK(command->IsTopLevelCommand());
2001       undo_step->SetStartingSelection(selection);
2002     }
2003     command->starting_selection_ = selection;
2004     if (!command->Parent() || command->Parent()->IsFirstCommand(command))
2005       break;
2006   }
2007 }
2008 
SetEndingSelection(const SelectionForUndoStep & selection)2009 void CompositeEditCommand::SetEndingSelection(
2010     const SelectionForUndoStep& selection) {
2011   for (CompositeEditCommand* command = this; command;
2012        command = command->Parent()) {
2013     if (UndoStep* undo_step = command->GetUndoStep()) {
2014       DCHECK(command->IsTopLevelCommand());
2015       undo_step->SetEndingSelection(selection);
2016     }
2017     command->ending_selection_ = selection;
2018   }
2019 }
2020 
SetParent(CompositeEditCommand * parent)2021 void CompositeEditCommand::SetParent(CompositeEditCommand* parent) {
2022   EditCommand::SetParent(parent);
2023   if (!parent)
2024     return;
2025   starting_selection_ = parent->ending_selection_;
2026   ending_selection_ = parent->ending_selection_;
2027 }
2028 
2029 // Determines whether a node is inside a range or visibly starts and ends at the
2030 // boundaries of the range. Call this function to determine whether a node is
2031 // visibly fit inside selectedRange
IsNodeVisiblyContainedWithin(Node & node,const EphemeralRange & selected_range)2032 bool CompositeEditCommand::IsNodeVisiblyContainedWithin(
2033     Node& node,
2034     const EphemeralRange& selected_range) {
2035   DCHECK(!NeedsLayoutTreeUpdate(node));
2036   DocumentLifecycle::DisallowTransitionScope disallow_transition(
2037       node.GetDocument().Lifecycle());
2038 
2039   if (IsNodeFullyContained(selected_range, node))
2040     return true;
2041 
2042   bool start_is_visually_same =
2043       CreateVisiblePosition(PositionBeforeNode(node)).DeepEquivalent() ==
2044       CreateVisiblePosition(selected_range.StartPosition()).DeepEquivalent();
2045   if (start_is_visually_same &&
2046       ComparePositions(Position::InParentAfterNode(node),
2047                        selected_range.EndPosition()) < 0)
2048     return true;
2049 
2050   bool end_is_visually_same =
2051       CreateVisiblePosition(PositionAfterNode(node)).DeepEquivalent() ==
2052       CreateVisiblePosition(selected_range.EndPosition()).DeepEquivalent();
2053   if (end_is_visually_same &&
2054       ComparePositions(selected_range.StartPosition(),
2055                        Position::InParentBeforeNode(node)) < 0)
2056     return true;
2057 
2058   return start_is_visually_same && end_is_visually_same;
2059 }
2060 
Trace(Visitor * visitor) const2061 void CompositeEditCommand::Trace(Visitor* visitor) const {
2062   visitor->Trace(commands_);
2063   visitor->Trace(starting_selection_);
2064   visitor->Trace(ending_selection_);
2065   visitor->Trace(undo_step_);
2066   EditCommand::Trace(visitor);
2067 }
2068 
AppliedEditing()2069 void CompositeEditCommand::AppliedEditing() {
2070   DCHECK(!IsCommandGroupWrapper());
2071   EventQueueScope scope;
2072 
2073   const UndoStep& undo_step = *GetUndoStep();
2074   DispatchEditableContentChangedEvents(undo_step.StartingRootEditableElement(),
2075                                        undo_step.EndingRootEditableElement());
2076   LocalFrame* const frame = GetDocument().GetFrame();
2077   Editor& editor = frame->GetEditor();
2078   // TODO(editing-dev): Filter empty InputType after spec is finalized.
2079   DispatchInputEventEditableContentChanged(
2080       undo_step.StartingRootEditableElement(),
2081       undo_step.EndingRootEditableElement(), GetInputType(),
2082       TextDataForInputEvent(), IsComposingFromCommand(this));
2083 
2084   const SelectionInDOMTree& new_selection =
2085       CorrectedSelectionAfterCommand(EndingSelection(), &GetDocument());
2086 
2087   // Don't clear the typing style with this selection change. We do those things
2088   // elsewhere if necessary.
2089   ChangeSelectionAfterCommand(frame, new_selection,
2090                               SetSelectionOptions::Builder()
2091                                   .SetIsDirectional(SelectionIsDirectional())
2092                                   .Build());
2093 
2094   if (!PreservesTypingStyle())
2095     editor.ClearTypingStyle();
2096 
2097   CompositeEditCommand* const last_edit_command = editor.LastEditCommand();
2098   // Command will be equal to last edit command only in the case of typing
2099   if (last_edit_command == this) {
2100     DCHECK(IsTypingCommand());
2101   } else if (last_edit_command && last_edit_command->IsDragAndDropCommand() &&
2102              (GetInputType() == InputEvent::InputType::kDeleteByDrag ||
2103               GetInputType() == InputEvent::InputType::kInsertFromDrop)) {
2104     // Only register undo entry when combined with other commands.
2105     if (!last_edit_command->GetUndoStep()) {
2106       editor.GetUndoStack().RegisterUndoStep(
2107           last_edit_command->EnsureUndoStep());
2108     }
2109     last_edit_command->EnsureUndoStep()->SetEndingSelection(
2110         EnsureUndoStep()->EndingSelection());
2111     last_edit_command->GetUndoStep()->SetSelectionIsDirectional(
2112         GetUndoStep()->SelectionIsDirectional());
2113     last_edit_command->AppendCommandToUndoStep(this);
2114   } else {
2115     // Only register a new undo command if the command passed in is
2116     // different from the last command
2117     editor.SetLastEditCommand(this);
2118     editor.GetUndoStack().RegisterUndoStep(EnsureUndoStep());
2119   }
2120 
2121   editor.RespondToChangedContents(new_selection.Base());
2122 }
2123 
2124 }  // namespace blink
2125