18e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project/* 206ea8e899e48f1f2f396b70e63fae369f2f23232Kristian Monsen * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. 38e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * 48e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * Redistribution and use in source and binary forms, with or without 58e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * modification, are permitted provided that the following conditions 68e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * are met: 78e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * 1. Redistributions of source code must retain the above copyright 88e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * notice, this list of conditions and the following disclaimer. 98e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * 2. Redistributions in binary form must reproduce the above copyright 108e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * notice, this list of conditions and the following disclaimer in the 118e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * documentation and/or other materials provided with the distribution. 128e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * 138e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 158e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 168e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 178e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 188e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 198e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 208e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 218e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 228e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 238e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 248e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project */ 258e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project 268e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "config.h" 278e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "Element.h" 288e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "InsertListCommand.h" 298e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "DocumentFragment.h" 308e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "htmlediting.h" 318e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "HTMLElement.h" 328e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "HTMLNames.h" 338e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "TextIterator.h" 348e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "visible_units.h" 358e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project 368e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectnamespace WebCore { 378e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project 388e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectusing namespace HTMLNames; 398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project 40e8b154fd68f9b33be40a3590e58347f353835f5cSteve Blockstatic Node* enclosingListChild(Node* node, Node* listNode) 41e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block{ 42e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block Node* listChild = enclosingListChild(node); 43e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block while (listChild && enclosingList(listChild) != listNode) 44e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block listChild = enclosingListChild(listChild->parentNode()); 45e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block return listChild; 46e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block} 47e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block 48635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source ProjectPassRefPtr<HTMLElement> InsertListCommand::insertList(Document* document, Type type) 498e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{ 5006ea8e899e48f1f2f396b70e63fae369f2f23232Kristian Monsen RefPtr<InsertListCommand> insertCommand = create(document, type); 518e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project insertCommand->apply(); 528e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project return insertCommand->m_listElement; 538e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project} 548e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project 55635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source ProjectHTMLElement* InsertListCommand::fixOrphanedListChild(Node* node) 568e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{ 57635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project RefPtr<HTMLElement> listElement = createUnorderedListElement(document()); 58635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project insertNodeBefore(listElement, node); 598e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project removeNode(node); 60635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project appendNode(node, listElement); 618e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project m_listElement = listElement; 628e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project return listElement.get(); 638e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project} 648e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project 65db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve BlockPassRefPtr<HTMLElement> InsertListCommand::mergeWithNeighboringLists(PassRefPtr<HTMLElement> passedList) 66db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block{ 67db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block RefPtr<HTMLElement> list = passedList; 68db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block Element* previousList = list->previousElementSibling(); 69db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block if (canMergeLists(previousList, list.get())) 70db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block mergeIdenticalElements(previousList, list); 71db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block 72db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block if (!list || !list->nextElementSibling() || !list->nextElementSibling()->isHTMLElement()) 73db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block return list.release(); 74db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block 752fc2651226baac27029e38c9d6ef883fa32084dbSteve Block RefPtr<HTMLElement> nextList = toHTMLElement(list->nextElementSibling()); 76db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block if (canMergeLists(list.get(), nextList.get())) { 77db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block mergeIdenticalElements(list, nextList); 78db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block return nextList.release(); 79db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block } 80db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block return list.release(); 81db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block} 82db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block 83db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Blockbool InsertListCommand::selectionHasListOfType(const VisibleSelection& selection, const QualifiedName& listTag) 84db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block{ 85db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block VisiblePosition start = selection.visibleStart(); 86db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block 8781bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch if (!enclosingList(start.deepEquivalent().deprecatedNode())) 88db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block return false; 89db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block 90db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block VisiblePosition end = selection.visibleEnd(); 91db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block while (start.isNotNull() && start != end) { 9281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch Element* listNode = enclosingList(start.deepEquivalent().deprecatedNode()); 93db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block if (!listNode || !listNode->hasTagName(listTag)) 94db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block return false; 95db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block start = startOfNextParagraph(start); 96db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block } 97db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block 98db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block return true; 99db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block} 100db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block 1018f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng QianInsertListCommand::InsertListCommand(Document* document, Type type) 102967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch : CompositeEditCommand(document), m_type(type) 1038e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{ 1048e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project} 1058e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project 1068e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectvoid InsertListCommand::doApply() 1078e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{ 108e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block if (!endingSelection().isNonOrphanedCaretOrRange()) 1098e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project return; 110e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block 1118e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project if (!endingSelection().rootEditableElement()) 1128e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project return; 1138e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project 1148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project VisiblePosition visibleEnd = endingSelection().visibleEnd(); 1158e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project VisiblePosition visibleStart = endingSelection().visibleStart(); 1168e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project // When a selection ends at the start of a paragraph, we rarely paint 1178e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project // the selection gap before that paragraph, because there often is no gap. 1188e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project // In a case like this, it's not obvious to the user that the selection 1198e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project // ends "inside" that paragraph, so it would be confusing if InsertUn{Ordered}List 1208e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project // operated on that paragraph. 1218e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project // FIXME: We paint the gap before some paragraphs that are indented with left 1228e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project // margin/padding, but not others. We should make the gap painting more consistent and 1238e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project // then use a left margin/padding rule here. 1242bde8e466a4451c7319e3a072d118917957d6554Steve Block if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd, CanSkipOverEditingBoundary)) 1252bde8e466a4451c7319e3a072d118917957d6554Steve Block setEndingSelection(VisibleSelection(visibleStart, visibleEnd.previous(CannotCrossEditingBoundary))); 1268e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project 127db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block const QualifiedName& listTag = (m_type == OrderedList) ? olTag : ulTag; 128967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch if (endingSelection().isRange()) { 129967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch VisibleSelection selection = selectionForParagraphIteration(endingSelection()); 130967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch ASSERT(selection.isRange()); 131967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch VisiblePosition startOfSelection = selection.visibleStart(); 132967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch VisiblePosition endOfSelection = selection.visibleEnd(); 1332bde8e466a4451c7319e3a072d118917957d6554Steve Block VisiblePosition startOfLastParagraph = startOfParagraph(endOfSelection, CanSkipOverEditingBoundary); 134967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch 1352bde8e466a4451c7319e3a072d118917957d6554Steve Block if (startOfParagraph(startOfSelection, CanSkipOverEditingBoundary) != startOfLastParagraph) { 136db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block bool forceCreateList = !selectionHasListOfType(selection, listTag); 137967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch 138db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block RefPtr<Range> currentSelection = endingSelection().firstRange(); 139967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch VisiblePosition startOfCurrentParagraph = startOfSelection; 1402bde8e466a4451c7319e3a072d118917957d6554Steve Block while (!inSameParagraph(startOfCurrentParagraph, startOfLastParagraph, CanCrossEditingBoundary)) { 141967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch // doApply() may operate on and remove the last paragraph of the selection from the document 142967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch // if it's in the same list item as startOfCurrentParagraph. Return early to avoid an 143967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch // infinite loop and because there is no more work to be done. 144967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch // FIXME(<rdar://problem/5983974>): The endingSelection() may be incorrect here. Compute 145967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch // the new location of endOfSelection and use it as the end of the new selection. 14681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch if (!startOfLastParagraph.deepEquivalent().anchorNode()->inDocument()) 147967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch return; 148967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch setEndingSelection(startOfCurrentParagraph); 149e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block 150e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block // Save and restore endOfSelection and startOfLastParagraph when necessary 151e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block // since moveParagraph and movePragraphWithClones can remove nodes. 152e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block // FIXME: This is an inefficient way to keep selection alive because indexForVisiblePosition walks from 153e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block // the beginning of the document to the endOfSelection everytime this code is executed. 154e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block // But not using index is hard because there are so many ways we can lose selection inside doApplyForSingleParagraph. 155e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block int indexForEndOfSelection = indexForVisiblePosition(endOfSelection); 156db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block doApplyForSingleParagraph(forceCreateList, listTag, currentSelection.get()); 157e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block if (endOfSelection.isNull() || endOfSelection.isOrphan() || startOfLastParagraph.isNull() || startOfLastParagraph.isOrphan()) { 158e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block RefPtr<Range> lastSelectionRange = TextIterator::rangeFromLocationAndLength(document()->documentElement(), indexForEndOfSelection, 0, true); 1594a156157940f51b91eadd76f6c86f862ec0a1da0Ben Murdoch // If lastSelectionRange is null, then some contents have been deleted from the document. 1604a156157940f51b91eadd76f6c86f862ec0a1da0Ben Murdoch // This should never happen and if it did, exit early immediately because we've lost the loop invariant. 1614a156157940f51b91eadd76f6c86f862ec0a1da0Ben Murdoch ASSERT(lastSelectionRange); 1624a156157940f51b91eadd76f6c86f862ec0a1da0Ben Murdoch if (!lastSelectionRange) 1634a156157940f51b91eadd76f6c86f862ec0a1da0Ben Murdoch return; 164e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block endOfSelection = lastSelectionRange->startPosition(); 1652bde8e466a4451c7319e3a072d118917957d6554Steve Block startOfLastParagraph = startOfParagraph(endOfSelection, CanSkipOverEditingBoundary); 166e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block } 167967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch 168967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch // Fetch the start of the selection after moving the first paragraph, 169967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch // because moving the paragraph will invalidate the original start. 170967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch // We'll use the new start to restore the original selection after 171967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch // we modified all selected paragraphs. 172967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch if (startOfCurrentParagraph == startOfSelection) 173967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch startOfSelection = endingSelection().visibleStart(); 174967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch 175967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch startOfCurrentParagraph = startOfNextParagraph(endingSelection().visibleStart()); 176967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch } 177967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch setEndingSelection(endOfSelection); 178db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block doApplyForSingleParagraph(forceCreateList, listTag, currentSelection.get()); 179967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch // Fetch the end of the selection, for the reason mentioned above. 180967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch endOfSelection = endingSelection().visibleEnd(); 181967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch setEndingSelection(VisibleSelection(startOfSelection, endOfSelection)); 182967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch return; 183967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch } 184967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch } 185967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch 186db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block doApplyForSingleParagraph(false, listTag, endingSelection().firstRange().get()); 187967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch} 188967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch 189db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Blockvoid InsertListCommand::doApplyForSingleParagraph(bool forceCreateList, const QualifiedName& listTag, Range* currentSelection) 190967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch{ 1918e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project // FIXME: This will produce unexpected results for a selection that starts just before a 1928e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project // table and ends inside the first cell, selectionForParagraphIteration should probably 1938e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project // be renamed and deployed inside setEndingSelection(). 19481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch Node* selectionNode = endingSelection().start().deprecatedNode(); 1958e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project Node* listChildNode = enclosingListChild(selectionNode); 1968e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project bool switchListType = false; 1978e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project if (listChildNode) { 1988e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project // Remove the list chlild. 199db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block RefPtr<HTMLElement> listNode = enclosingList(listChildNode); 200db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block if (!listNode) { 2018e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project listNode = fixOrphanedListChild(listChildNode); 202db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block listNode = mergeWithNeighboringLists(listNode); 203db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block } 2048e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project if (!listNode->hasTagName(listTag)) 2058e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project // listChildNode will be removed from the list and a list of type m_type will be created. 2068e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project switchListType = true; 207545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch 208db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block // If the list is of the desired type, and we are not removing the list, then exit early. 209db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block if (!switchListType && forceCreateList) 210db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block return; 211db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block 212db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block // If the entire list is selected, then convert the whole list. 213db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block if (switchListType && isNodeVisiblyContainedWithin(listNode.get(), currentSelection)) { 214e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block bool rangeStartIsInList = visiblePositionBeforeNode(listNode.get()) == currentSelection->startPosition(); 215e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block bool rangeEndIsInList = visiblePositionAfterNode(listNode.get()) == currentSelection->endPosition(); 216e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block 217db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block RefPtr<HTMLElement> newList = createHTMLElement(document(), listTag); 218db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block insertNodeBefore(newList, listNode); 219e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block 22081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch Node* firstChildInList = enclosingListChild(VisiblePosition(firstPositionInNode(listNode.get())).deepEquivalent().deprecatedNode(), listNode.get()); 221e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block Node* outerBlock = firstChildInList->isBlockFlow() ? firstChildInList : listNode.get(); 222e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block 223db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block moveParagraphWithClones(firstPositionInNode(listNode.get()), lastPositionInNode(listNode.get()), newList.get(), outerBlock); 224e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block 225e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block // Manually remove listNode because moveParagraphWithClones sometimes leaves it behind in the document. 226e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block // See the bug 33668 and editing/execCommand/insert-list-orphaned-item-with-nested-lists.html. 227e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block // FIXME: This might be a bug in moveParagraphWithClones or deleteSelection. 228e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block if (listNode && listNode->inDocument()) 229e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block removeNode(listNode); 230e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block 231db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block newList = mergeWithNeighboringLists(newList); 232e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block 233e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block // Restore the start and the end of current selection if they started inside listNode 234e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block // because moveParagraphWithClones could have removed them. 235e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block ExceptionCode ec; 236e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block if (rangeStartIsInList && newList) 237e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block currentSelection->setStart(newList, 0, ec); 238e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block if (rangeEndIsInList && newList) 239e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block currentSelection->setEnd(newList, lastOffsetInNode(newList.get()), ec); 240e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block 241db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block setEndingSelection(VisiblePosition(firstPositionInNode(newList.get()))); 242e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block 243db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block return; 244db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block } 245db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block 246db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block unlistifyParagraph(endingSelection().visibleStart(), listNode.get(), listChildNode); 2478e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project } 2485f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian 249967717af5423377c967781471ee106e2bb4e11c8Ben Murdoch if (!listChildNode || switchListType || forceCreateList) 250545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch m_listElement = listifyParagraph(endingSelection().visibleStart(), listTag); 251545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch} 252545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch 253545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdochvoid InsertListCommand::unlistifyParagraph(const VisiblePosition& originalStart, HTMLElement* listNode, Node* listChildNode) 254545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch{ 255545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch Node* nextListChild; 256545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch Node* previousListChild; 257545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch VisiblePosition start; 258545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch VisiblePosition end; 259545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch if (listChildNode->hasTagName(liTag)) { 2602bde8e466a4451c7319e3a072d118917957d6554Steve Block start = firstPositionInNode(listChildNode); 2612bde8e466a4451c7319e3a072d118917957d6554Steve Block end = lastPositionInNode(listChildNode); 262545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch nextListChild = listChildNode->nextSibling(); 263545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch previousListChild = listChildNode->previousSibling(); 264545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch } else { 265545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // A paragraph is visually a list item minus a list marker. The paragraph will be moved. 2662bde8e466a4451c7319e3a072d118917957d6554Steve Block start = startOfParagraph(originalStart, CanSkipOverEditingBoundary); 2672bde8e466a4451c7319e3a072d118917957d6554Steve Block end = endOfParagraph(start, CanSkipOverEditingBoundary); 26881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch nextListChild = enclosingListChild(end.next().deepEquivalent().deprecatedNode(), listNode); 269545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch ASSERT(nextListChild != listChildNode); 27081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch previousListChild = enclosingListChild(start.previous().deepEquivalent().deprecatedNode(), listNode); 271545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch ASSERT(previousListChild != listChildNode); 272545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch } 273545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // When removing a list, we must always create a placeholder to act as a point of insertion 274545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // for the list content being removed. 275545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch RefPtr<Element> placeholder = createBreakElement(document()); 276545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch RefPtr<Element> nodeToInsert = placeholder; 277545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // If the content of the list item will be moved into another list, put it in a list item 278545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // so that we don't create an orphaned list child. 279545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch if (enclosingList(listNode)) { 280545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch nodeToInsert = createListItemElement(document()); 281545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch appendNode(placeholder, nodeToInsert); 282545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch } 283545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch 284545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch if (nextListChild && previousListChild) { 285545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // We want to pull listChildNode out of listNode, and place it before nextListChild 286545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // and after previousListChild, so we split listNode and insert it between the two lists. 287545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // But to split listNode, we must first split ancestors of listChildNode between it and listNode, 288545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // if any exist. 289545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // FIXME: We appear to split at nextListChild as opposed to listChildNode so that when we remove 290545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // listChildNode below in moveParagraphs, previousListChild will be removed along with it if it is 291545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // unrendered. But we ought to remove nextListChild too, if it is unrendered. 292545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch splitElement(listNode, splitTreeToNode(nextListChild, listNode)); 293545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch insertNodeBefore(nodeToInsert, listNode); 294545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch } else if (nextListChild || listChildNode->parentNode() != listNode) { 295545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // Just because listChildNode has no previousListChild doesn't mean there isn't any content 296545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // in listNode that comes before listChildNode, as listChildNode could have ancestors 297545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // between it and listNode. So, we split up to listNode before inserting the placeholder 298545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // where we're about to move listChildNode to. 299545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch if (listChildNode->parentNode() != listNode) 300545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch splitElement(listNode, splitTreeToNode(listChildNode, listNode).get()); 301545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch insertNodeBefore(nodeToInsert, listNode); 302545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch } else 303545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch insertNodeAfter(nodeToInsert, listNode); 304545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch 305ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch VisiblePosition insertionPoint = VisiblePosition(positionBeforeNode(placeholder.get())); 306545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch moveParagraphs(start, end, insertionPoint, true); 307545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch} 3085f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian 3090617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsenstatic Element* adjacentEnclosingList(const VisiblePosition& pos, const VisiblePosition& adjacentPos, const QualifiedName& listTag) 3100617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen{ 31181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch Element* listNode = outermostEnclosingList(adjacentPos.deepEquivalent().deprecatedNode()); 3120617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen 3130617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen if (!listNode) 3140617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen return 0; 3150617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen 3160617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen Node* previousCell = enclosingTableCell(pos.deepEquivalent()); 3170617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen Node* currentCell = enclosingTableCell(adjacentPos.deepEquivalent()); 3180617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen 3190617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen if (!listNode->hasTagName(listTag) 32081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch || listNode->contains(pos.deepEquivalent().deprecatedNode()) 321e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block || previousCell != currentCell 32281bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch || enclosingList(listNode) != enclosingList(pos.deepEquivalent().deprecatedNode())) 3230617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen return 0; 3240617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen 3250617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen return listNode; 3260617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen} 3270617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen 328545e470e52f0ac6a3a072bf559c796b42c6066b6Ben MurdochPassRefPtr<HTMLElement> InsertListCommand::listifyParagraph(const VisiblePosition& originalStart, const QualifiedName& listTag) 329545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch{ 3302bde8e466a4451c7319e3a072d118917957d6554Steve Block VisiblePosition start = startOfParagraph(originalStart, CanSkipOverEditingBoundary); 3312bde8e466a4451c7319e3a072d118917957d6554Steve Block VisiblePosition end = endOfParagraph(start, CanSkipOverEditingBoundary); 332e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block 333e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block if (start.isNull() || end.isNull()) 334e8b154fd68f9b33be40a3590e58347f353835f5cSteve Block return 0; 335545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch 336545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // Check for adjoining lists. 337545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch RefPtr<HTMLElement> listItemElement = createListItemElement(document()); 338545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch RefPtr<HTMLElement> placeholder = createBreakElement(document()); 339545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch appendNode(placeholder, listItemElement); 3400617145a89917ae7735fe1c9538688ab9a577df5Kristian Monsen 341545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // Place list item into adjoining lists. 3422bde8e466a4451c7319e3a072d118917957d6554Steve Block Element* previousList = adjacentEnclosingList(start.deepEquivalent(), start.previous(CannotCrossEditingBoundary), listTag); 3432bde8e466a4451c7319e3a072d118917957d6554Steve Block Element* nextList = adjacentEnclosingList(start.deepEquivalent(), end.next(CannotCrossEditingBoundary), listTag); 344545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch RefPtr<HTMLElement> listElement; 345545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch if (previousList) 346545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch appendNode(listItemElement, previousList); 347545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch else if (nextList) 348ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch insertNodeAt(listItemElement, positionBeforeNode(nextList)); 349545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch else { 350545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // Create the list. 351545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch listElement = createHTMLElement(document(), listTag); 352545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch appendNode(listItemElement, listElement); 353545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch 35481bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch if (start == end && isBlock(start.deepEquivalent().deprecatedNode())) { 355545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // Inserting the list into an empty paragraph that isn't held open 356545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // by a br or a '\n', will invalidate start and end. Insert 357545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // a placeholder and then recompute start and end. 358545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch RefPtr<Node> placeholder = insertBlockPlaceholder(start.deepEquivalent()); 359ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch start = positionBeforeNode(placeholder.get()); 360545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch end = start; 3618e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project } 362545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch 363545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // Insert the list at a position visually equivalent to start of the 364545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // paragraph that is being moved into the list. 365545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // Try to avoid inserting it somewhere where it will be surrounded by 366545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // inline ancestors of start, since it is easier for editing to produce 367545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // clean markup when inline elements are pushed down as far as possible. 368545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch Position insertionPos(start.deepEquivalent().upstream()); 369545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // Also avoid the containing list item. 37081bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch Node* listChild = enclosingListChild(insertionPos.deprecatedNode()); 371545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch if (listChild && listChild->hasTagName(liTag)) 372545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch insertionPos = positionInParentBeforeNode(listChild); 373545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch 374545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch insertNodeAt(listElement, insertionPos); 375545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch 376545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // We inserted the list at the start of the content we're about to move 377545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch // Update the start of content, so we don't try to move the list into itself. bug 19066 3782bde8e466a4451c7319e3a072d118917957d6554Steve Block // Layout is necessary since start's node's inline renderers may have been destroyed by the insertion 3792bde8e466a4451c7319e3a072d118917957d6554Steve Block if (insertionPos == start.deepEquivalent()) { 3802bde8e466a4451c7319e3a072d118917957d6554Steve Block listElement->document()->updateLayoutIgnorePendingStylesheets(); 3812bde8e466a4451c7319e3a072d118917957d6554Steve Block start = startOfParagraph(originalStart, CanSkipOverEditingBoundary); 3822bde8e466a4451c7319e3a072d118917957d6554Steve Block } 3838e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project } 384545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch 385ab9e7a118cf1ea2e3a93dce683b2ded3e7291ddbBen Murdoch moveParagraph(start, end, positionBeforeNode(placeholder.get()), true); 386545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch 387db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block if (listElement) 388db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block return mergeWithNeighboringLists(listElement); 389db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block 390db14019a23d96bc8a444b6576a5da8bd1cfbc8b0Steve Block if (canMergeLists(previousList, nextList)) 391545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch mergeIdenticalElements(previousList, nextList); 392545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch 393545e470e52f0ac6a3a072bf559c796b42c6066b6Ben Murdoch return listElement; 3948e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project} 3958e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project 3968e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project} 397