| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2006, 2010 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 "config.h" | |
| 27 #include "core/editing/InsertListCommand.h" | |
| 28 | |
| 29 #include "bindings/core/v8/ExceptionStatePlaceholder.h" | |
| 30 #include "core/HTMLNames.h" | |
| 31 #include "core/dom/Document.h" | |
| 32 #include "core/dom/Element.h" | |
| 33 #include "core/dom/ElementTraversal.h" | |
| 34 #include "core/editing/EditingUtilities.h" | |
| 35 #include "core/editing/VisibleUnits.h" | |
| 36 #include "core/editing/iterators/TextIterator.h" | |
| 37 #include "core/html/HTMLBRElement.h" | |
| 38 #include "core/html/HTMLElement.h" | |
| 39 #include "core/html/HTMLLIElement.h" | |
| 40 #include "core/html/HTMLUListElement.h" | |
| 41 | |
| 42 namespace blink { | |
| 43 | |
| 44 using namespace HTMLNames; | |
| 45 | |
| 46 static Node* enclosingListChild(Node* node, Node* listNode) | |
| 47 { | |
| 48 Node* listChild = enclosingListChild(node); | |
| 49 while (listChild && enclosingList(listChild) != listNode) | |
| 50 listChild = enclosingListChild(listChild->parentNode()); | |
| 51 return listChild; | |
| 52 } | |
| 53 | |
| 54 HTMLUListElement* InsertListCommand::fixOrphanedListChild(Node* node) | |
| 55 { | |
| 56 RefPtrWillBeRawPtr<HTMLUListElement> listElement = createUnorderedListElemen
t(document()); | |
| 57 insertNodeBefore(listElement, node); | |
| 58 removeNode(node); | |
| 59 appendNode(node, listElement); | |
| 60 m_listElement = listElement; | |
| 61 return listElement.get(); | |
| 62 } | |
| 63 | |
| 64 PassRefPtrWillBeRawPtr<HTMLElement> InsertListCommand::mergeWithNeighboringLists
(PassRefPtrWillBeRawPtr<HTMLElement> passedList) | |
| 65 { | |
| 66 RefPtrWillBeRawPtr<HTMLElement> list = passedList; | |
| 67 Element* previousList = ElementTraversal::previousSibling(*list); | |
| 68 if (canMergeLists(previousList, list.get())) | |
| 69 mergeIdenticalElements(previousList, list); | |
| 70 | |
| 71 if (!list) | |
| 72 return nullptr; | |
| 73 | |
| 74 Element* nextSibling = ElementTraversal::nextSibling(*list); | |
| 75 if (!nextSibling || !nextSibling->isHTMLElement()) | |
| 76 return list.release(); | |
| 77 | |
| 78 RefPtrWillBeRawPtr<HTMLElement> nextList = toHTMLElement(nextSibling); | |
| 79 if (canMergeLists(list.get(), nextList.get())) { | |
| 80 mergeIdenticalElements(list, nextList); | |
| 81 return nextList.release(); | |
| 82 } | |
| 83 return list.release(); | |
| 84 } | |
| 85 | |
| 86 bool InsertListCommand::selectionHasListOfType(const VisibleSelection& selection
, const HTMLQualifiedName& listTag) | |
| 87 { | |
| 88 VisiblePosition start = selection.visibleStart(); | |
| 89 | |
| 90 if (!enclosingList(start.deepEquivalent().anchorNode())) | |
| 91 return false; | |
| 92 | |
| 93 VisiblePosition end = startOfParagraph(selection.visibleEnd()); | |
| 94 while (start.isNotNull() && start.deepEquivalent() != end.deepEquivalent())
{ | |
| 95 HTMLElement* listElement = enclosingList(start.deepEquivalent().anchorNo
de()); | |
| 96 if (!listElement || !listElement->hasTagName(listTag)) | |
| 97 return false; | |
| 98 start = startOfNextParagraph(start); | |
| 99 } | |
| 100 | |
| 101 return true; | |
| 102 } | |
| 103 | |
| 104 InsertListCommand::InsertListCommand(Document& document, Type type) | |
| 105 : CompositeEditCommand(document), m_type(type) | |
| 106 { | |
| 107 } | |
| 108 | |
| 109 void InsertListCommand::doApply() | |
| 110 { | |
| 111 if (!endingSelection().isNonOrphanedCaretOrRange()) | |
| 112 return; | |
| 113 | |
| 114 if (!endingSelection().rootEditableElement()) | |
| 115 return; | |
| 116 | |
| 117 VisiblePosition visibleEnd = endingSelection().visibleEnd(); | |
| 118 VisiblePosition visibleStart = endingSelection().visibleStart(); | |
| 119 // When a selection ends at the start of a paragraph, we rarely paint | |
| 120 // the selection gap before that paragraph, because there often is no gap. | |
| 121 // In a case like this, it's not obvious to the user that the selection | |
| 122 // ends "inside" that paragraph, so it would be confusing if InsertUn{Ordere
d}List | |
| 123 // operated on that paragraph. | |
| 124 // FIXME: We paint the gap before some paragraphs that are indented with lef
t | |
| 125 // margin/padding, but not others. We should make the gap painting more con
sistent and | |
| 126 // then use a left margin/padding rule here. | |
| 127 if (visibleEnd.deepEquivalent() != visibleStart.deepEquivalent() && isStartO
fParagraph(visibleEnd, CanSkipOverEditingBoundary)) { | |
| 128 setEndingSelection(VisibleSelection(visibleStart, visibleEnd.previous(Ca
nnotCrossEditingBoundary), endingSelection().isDirectional())); | |
| 129 if (!endingSelection().rootEditableElement()) | |
| 130 return; | |
| 131 } | |
| 132 | |
| 133 const HTMLQualifiedName& listTag = (m_type == OrderedList) ? olTag : ulTag; | |
| 134 if (endingSelection().isRange()) { | |
| 135 bool forceListCreation = false; | |
| 136 VisibleSelection selection = selectionForParagraphIteration(endingSelect
ion()); | |
| 137 ASSERT(selection.isRange()); | |
| 138 VisiblePosition startOfSelection = selection.visibleStart(); | |
| 139 VisiblePosition endOfSelection = selection.visibleEnd(); | |
| 140 VisiblePosition startOfLastParagraph = startOfParagraph(endOfSelection,
CanSkipOverEditingBoundary); | |
| 141 | |
| 142 RefPtrWillBeRawPtr<Range> currentSelection = endingSelection().firstRang
e(); | |
| 143 RefPtrWillBeRawPtr<ContainerNode> scopeForStartOfSelection = nullptr; | |
| 144 RefPtrWillBeRawPtr<ContainerNode> scopeForEndOfSelection = nullptr; | |
| 145 // FIXME: This is an inefficient way to keep selection alive because | |
| 146 // indexForVisiblePosition walks from the beginning of the document to t
he | |
| 147 // endOfSelection everytime this code is executed. But not using index i
s hard | |
| 148 // because there are so many ways we can los eselection inside doApplyFo
rSingleParagraph. | |
| 149 int indexForStartOfSelection = indexForVisiblePosition(startOfSelection,
scopeForStartOfSelection); | |
| 150 int indexForEndOfSelection = indexForVisiblePosition(endOfSelection, sco
peForEndOfSelection); | |
| 151 | |
| 152 if (startOfParagraph(startOfSelection, CanSkipOverEditingBoundary).deepE
quivalent() != startOfLastParagraph.deepEquivalent()) { | |
| 153 forceListCreation = !selectionHasListOfType(selection, listTag); | |
| 154 | |
| 155 VisiblePosition startOfCurrentParagraph = startOfSelection; | |
| 156 while (startOfCurrentParagraph.isNotNull() && !inSameParagraph(start
OfCurrentParagraph, startOfLastParagraph, CanCrossEditingBoundary)) { | |
| 157 // doApply() may operate on and remove the last paragraph of the
selection from the document | |
| 158 // if it's in the same list item as startOfCurrentParagraph. Re
turn early to avoid an | |
| 159 // infinite loop and because there is no more work to be done. | |
| 160 // FIXME(<rdar://problem/5983974>): The endingSelection() may be
incorrect here. Compute | |
| 161 // the new location of endOfSelection and use it as the end of t
he new selection. | |
| 162 if (!startOfLastParagraph.deepEquivalent().inDocument()) | |
| 163 return; | |
| 164 setEndingSelection(startOfCurrentParagraph); | |
| 165 | |
| 166 // Save and restore endOfSelection and startOfLastParagraph when
necessary | |
| 167 // since moveParagraph and movePragraphWithClones can remove nod
es. | |
| 168 if (!doApplyForSingleParagraph(forceListCreation, listTag, *curr
entSelection)) | |
| 169 break; | |
| 170 if (endOfSelection.isNull() || endOfSelection.isOrphan() || star
tOfLastParagraph.isNull() || startOfLastParagraph.isOrphan()) { | |
| 171 endOfSelection = visiblePositionForIndex(indexForEndOfSelect
ion, scopeForEndOfSelection.get()); | |
| 172 // If endOfSelection is null, then some contents have been d
eleted from the document. | |
| 173 // This should never happen and if it did, exit early immedi
ately because we've lost the loop invariant. | |
| 174 ASSERT(endOfSelection.isNotNull()); | |
| 175 if (endOfSelection.isNull()) | |
| 176 return; | |
| 177 startOfLastParagraph = startOfParagraph(endOfSelection, CanS
kipOverEditingBoundary); | |
| 178 } | |
| 179 | |
| 180 startOfCurrentParagraph = startOfNextParagraph(endingSelection()
.visibleStart()); | |
| 181 } | |
| 182 setEndingSelection(endOfSelection); | |
| 183 } | |
| 184 doApplyForSingleParagraph(forceListCreation, listTag, *currentSelection)
; | |
| 185 // Fetch the end of the selection, for the reason mentioned above. | |
| 186 if (endOfSelection.isNull() || endOfSelection.isOrphan()) { | |
| 187 endOfSelection = visiblePositionForIndex(indexForEndOfSelection, sco
peForEndOfSelection.get()); | |
| 188 if (endOfSelection.isNull()) | |
| 189 return; | |
| 190 } | |
| 191 if (startOfSelection.isNull() || startOfSelection.isOrphan()) { | |
| 192 startOfSelection = visiblePositionForIndex(indexForStartOfSelection,
scopeForStartOfSelection.get()); | |
| 193 if (startOfSelection.isNull()) | |
| 194 return; | |
| 195 } | |
| 196 setEndingSelection(VisibleSelection(startOfSelection, endOfSelection, en
dingSelection().isDirectional())); | |
| 197 return; | |
| 198 } | |
| 199 | |
| 200 ASSERT(endingSelection().firstRange()); | |
| 201 doApplyForSingleParagraph(false, listTag, *endingSelection().firstRange()); | |
| 202 } | |
| 203 | |
| 204 bool InsertListCommand::doApplyForSingleParagraph(bool forceCreateList, const HT
MLQualifiedName& listTag, Range& currentSelection) | |
| 205 { | |
| 206 // FIXME: This will produce unexpected results for a selection that starts j
ust before a | |
| 207 // table and ends inside the first cell, selectionForParagraphIteration shou
ld probably | |
| 208 // be renamed and deployed inside setEndingSelection(). | |
| 209 Node* selectionNode = endingSelection().start().anchorNode(); | |
| 210 Node* listChildNode = enclosingListChild(selectionNode); | |
| 211 bool switchListType = false; | |
| 212 if (listChildNode) { | |
| 213 if (!listChildNode->parentNode()->hasEditableStyle()) | |
| 214 return false; | |
| 215 // Remove the list child. | |
| 216 RefPtrWillBeRawPtr<HTMLElement> listElement = enclosingList(listChildNod
e); | |
| 217 if (!listElement) { | |
| 218 listElement = fixOrphanedListChild(listChildNode); | |
| 219 listElement = mergeWithNeighboringLists(listElement); | |
| 220 } | |
| 221 if (!listElement->hasTagName(listTag)) | |
| 222 // listChildNode will be removed from the list and a list of type m_
type will be created. | |
| 223 switchListType = true; | |
| 224 | |
| 225 // If the list is of the desired type, and we are not removing the list,
then exit early. | |
| 226 if (!switchListType && forceCreateList) | |
| 227 return true; | |
| 228 | |
| 229 // If the entire list is selected, then convert the whole list. | |
| 230 if (switchListType && isNodeVisiblyContainedWithin(*listElement, current
Selection)) { | |
| 231 bool rangeStartIsInList = visiblePositionBeforeNode(*listElement).de
epEquivalent() == VisiblePosition(currentSelection.startPosition()).deepEquivale
nt(); | |
| 232 bool rangeEndIsInList = visiblePositionAfterNode(*listElement).deepE
quivalent() == VisiblePosition(currentSelection.endPosition()).deepEquivalent(); | |
| 233 | |
| 234 RefPtrWillBeRawPtr<HTMLElement> newList = createHTMLElement(document
(), listTag); | |
| 235 insertNodeBefore(newList, listElement); | |
| 236 | |
| 237 Node* firstChildInList = enclosingListChild(VisiblePosition(firstPos
itionInNode(listElement.get())).deepEquivalent().anchorNode(), listElement.get()
); | |
| 238 Element* outerBlock = firstChildInList && isBlockFlowElement(*firstC
hildInList) ? toElement(firstChildInList) : listElement.get(); | |
| 239 | |
| 240 moveParagraphWithClones(VisiblePosition(firstPositionInNode(listElem
ent.get())), VisiblePosition(lastPositionInNode(listElement.get())), newList.get
(), outerBlock); | |
| 241 | |
| 242 // Manually remove listNode because moveParagraphWithClones sometime
s leaves it behind in the document. | |
| 243 // See the bug 33668 and editing/execCommand/insert-list-orphaned-it
em-with-nested-lists.html. | |
| 244 // FIXME: This might be a bug in moveParagraphWithClones or deleteSe
lection. | |
| 245 if (listElement && listElement->inDocument()) | |
| 246 removeNode(listElement); | |
| 247 | |
| 248 newList = mergeWithNeighboringLists(newList); | |
| 249 | |
| 250 // Restore the start and the end of current selection if they starte
d inside listNode | |
| 251 // because moveParagraphWithClones could have removed them. | |
| 252 if (rangeStartIsInList && newList) | |
| 253 currentSelection.setStart(newList, 0, IGNORE_EXCEPTION); | |
| 254 if (rangeEndIsInList && newList) | |
| 255 currentSelection.setEnd(newList, lastOffsetInNode(newList.get())
, IGNORE_EXCEPTION); | |
| 256 | |
| 257 setEndingSelection(VisiblePosition(firstPositionInNode(newList.get()
))); | |
| 258 | |
| 259 return true; | |
| 260 } | |
| 261 | |
| 262 unlistifyParagraph(endingSelection().visibleStart(), listElement.get(),
listChildNode); | |
| 263 } | |
| 264 | |
| 265 if (!listChildNode || switchListType || forceCreateList) | |
| 266 m_listElement = listifyParagraph(endingSelection().visibleStart(), listT
ag); | |
| 267 | |
| 268 return true; | |
| 269 } | |
| 270 | |
| 271 void InsertListCommand::unlistifyParagraph(const VisiblePosition& originalStart,
HTMLElement* listElement, Node* listChildNode) | |
| 272 { | |
| 273 Node* nextListChild; | |
| 274 Node* previousListChild; | |
| 275 VisiblePosition start; | |
| 276 VisiblePosition end; | |
| 277 ASSERT(listChildNode); | |
| 278 if (isHTMLLIElement(*listChildNode)) { | |
| 279 start = VisiblePosition(firstPositionInNode(listChildNode)); | |
| 280 end = VisiblePosition(lastPositionInNode(listChildNode)); | |
| 281 nextListChild = listChildNode->nextSibling(); | |
| 282 previousListChild = listChildNode->previousSibling(); | |
| 283 } else { | |
| 284 // A paragraph is visually a list item minus a list marker. The paragra
ph will be moved. | |
| 285 start = startOfParagraph(originalStart, CanSkipOverEditingBoundary); | |
| 286 end = endOfParagraph(start, CanSkipOverEditingBoundary); | |
| 287 nextListChild = enclosingListChild(end.next().deepEquivalent().anchorNod
e(), listElement); | |
| 288 ASSERT(nextListChild != listChildNode); | |
| 289 previousListChild = enclosingListChild(start.previous().deepEquivalent()
.anchorNode(), listElement); | |
| 290 ASSERT(previousListChild != listChildNode); | |
| 291 } | |
| 292 // When removing a list, we must always create a placeholder to act as a poi
nt of insertion | |
| 293 // for the list content being removed. | |
| 294 RefPtrWillBeRawPtr<HTMLBRElement> placeholder = createBreakElement(document(
)); | |
| 295 RefPtrWillBeRawPtr<HTMLElement> elementToInsert = placeholder; | |
| 296 // If the content of the list item will be moved into another list, put it i
n a list item | |
| 297 // so that we don't create an orphaned list child. | |
| 298 if (enclosingList(listElement)) { | |
| 299 elementToInsert = createListItemElement(document()); | |
| 300 appendNode(placeholder, elementToInsert); | |
| 301 } | |
| 302 | |
| 303 if (nextListChild && previousListChild) { | |
| 304 // We want to pull listChildNode out of listNode, and place it before ne
xtListChild | |
| 305 // and after previousListChild, so we split listNode and insert it betwe
en the two lists. | |
| 306 // But to split listNode, we must first split ancestors of listChildNode
between it and listNode, | |
| 307 // if any exist. | |
| 308 // FIXME: We appear to split at nextListChild as opposed to listChildNod
e so that when we remove | |
| 309 // listChildNode below in moveParagraphs, previousListChild will be remo
ved along with it if it is | |
| 310 // unrendered. But we ought to remove nextListChild too, if it is unrend
ered. | |
| 311 splitElement(listElement, splitTreeToNode(nextListChild, listElement)); | |
| 312 insertNodeBefore(elementToInsert, listElement); | |
| 313 } else if (nextListChild || listChildNode->parentNode() != listElement) { | |
| 314 // Just because listChildNode has no previousListChild doesn't mean ther
e isn't any content | |
| 315 // in listNode that comes before listChildNode, as listChildNode could h
ave ancestors | |
| 316 // between it and listNode. So, we split up to listNode before inserting
the placeholder | |
| 317 // where we're about to move listChildNode to. | |
| 318 if (listChildNode->parentNode() != listElement) | |
| 319 splitElement(listElement, splitTreeToNode(listChildNode, listElement
).get()); | |
| 320 insertNodeBefore(elementToInsert, listElement); | |
| 321 } else { | |
| 322 insertNodeAfter(elementToInsert, listElement); | |
| 323 } | |
| 324 | |
| 325 VisiblePosition insertionPoint = VisiblePosition(positionBeforeNode(placehol
der.get())); | |
| 326 moveParagraphs(start, end, insertionPoint, /* preserveSelection */ true, /*
preserveStyle */ true, listChildNode); | |
| 327 } | |
| 328 | |
| 329 static HTMLElement* adjacentEnclosingList(const VisiblePosition& pos, const Visi
blePosition& adjacentPos, const HTMLQualifiedName& listTag) | |
| 330 { | |
| 331 HTMLElement* listElement = outermostEnclosingList(adjacentPos.deepEquivalent
().anchorNode()); | |
| 332 | |
| 333 if (!listElement) | |
| 334 return 0; | |
| 335 | |
| 336 Element* previousCell = enclosingTableCell(pos.deepEquivalent()); | |
| 337 Element* currentCell = enclosingTableCell(adjacentPos.deepEquivalent()); | |
| 338 | |
| 339 if (!listElement->hasTagName(listTag) | |
| 340 || listElement->contains(pos.deepEquivalent().anchorNode()) | |
| 341 || previousCell != currentCell | |
| 342 || enclosingList(listElement) != enclosingList(pos.deepEquivalent().anch
orNode())) | |
| 343 return 0; | |
| 344 | |
| 345 return listElement; | |
| 346 } | |
| 347 | |
| 348 PassRefPtrWillBeRawPtr<HTMLElement> InsertListCommand::listifyParagraph(const Vi
siblePosition& originalStart, const HTMLQualifiedName& listTag) | |
| 349 { | |
| 350 VisiblePosition start = startOfParagraph(originalStart, CanSkipOverEditingBo
undary); | |
| 351 VisiblePosition end = endOfParagraph(start, CanSkipOverEditingBoundary); | |
| 352 | |
| 353 if (start.isNull() || end.isNull()) | |
| 354 return nullptr; | |
| 355 | |
| 356 // Check for adjoining lists. | |
| 357 RefPtrWillBeRawPtr<HTMLElement> listItemElement = createListItemElement(docu
ment()); | |
| 358 RefPtrWillBeRawPtr<HTMLBRElement> placeholder = createBreakElement(document(
)); | |
| 359 appendNode(placeholder, listItemElement); | |
| 360 | |
| 361 // Place list item into adjoining lists. | |
| 362 HTMLElement* previousList = adjacentEnclosingList(start, start.previous(Cann
otCrossEditingBoundary), listTag); | |
| 363 HTMLElement* nextList = adjacentEnclosingList(start, end.next(CannotCrossEdi
tingBoundary), listTag); | |
| 364 RefPtrWillBeRawPtr<HTMLElement> listElement = nullptr; | |
| 365 if (previousList) | |
| 366 appendNode(listItemElement, previousList); | |
| 367 else if (nextList) | |
| 368 insertNodeAt(listItemElement, positionBeforeNode(nextList)); | |
| 369 else { | |
| 370 // Create the list. | |
| 371 listElement = createHTMLElement(document(), listTag); | |
| 372 appendNode(listItemElement, listElement); | |
| 373 | |
| 374 if (start.deepEquivalent() == end.deepEquivalent() && isBlock(start.deep
Equivalent().anchorNode())) { | |
| 375 // Inserting the list into an empty paragraph that isn't held open | |
| 376 // by a br or a '\n', will invalidate start and end. Insert | |
| 377 // a placeholder and then recompute start and end. | |
| 378 RefPtrWillBeRawPtr<HTMLBRElement> placeholder = insertBlockPlacehold
er(start.deepEquivalent()); | |
| 379 start = VisiblePosition(positionBeforeNode(placeholder.get())); | |
| 380 end = start; | |
| 381 } | |
| 382 | |
| 383 // Insert the list at a position visually equivalent to start of the | |
| 384 // paragraph that is being moved into the list. | |
| 385 // Try to avoid inserting it somewhere where it will be surrounded by | |
| 386 // inline ancestors of start, since it is easier for editing to produce | |
| 387 // clean markup when inline elements are pushed down as far as possible. | |
| 388 Position insertionPos(start.deepEquivalent().upstream()); | |
| 389 // Also avoid the containing list item. | |
| 390 Node* listChild = enclosingListChild(insertionPos.anchorNode()); | |
| 391 if (isHTMLLIElement(listChild)) | |
| 392 insertionPos = positionInParentBeforeNode(*listChild); | |
| 393 | |
| 394 insertNodeAt(listElement, insertionPos); | |
| 395 | |
| 396 // We inserted the list at the start of the content we're about to move | |
| 397 // Update the start of content, so we don't try to move the list into it
self. bug 19066 | |
| 398 // Layout is necessary since start's node's inline layoutObjects may hav
e been destroyed by the insertion | |
| 399 // The end of the content may have changed after the insertion and layou
t so update it as well. | |
| 400 if (insertionPos == start.deepEquivalent()) | |
| 401 start = originalStart; | |
| 402 } | |
| 403 | |
| 404 // Inserting list element and list item list may change start of pargraph | |
| 405 // to move. We calculate start of paragraph again. | |
| 406 document().updateLayoutIgnorePendingStylesheets(); | |
| 407 start = startOfParagraph(start, CanSkipOverEditingBoundary); | |
| 408 end = endOfParagraph(start, CanSkipOverEditingBoundary); | |
| 409 moveParagraph(start, end, VisiblePosition(positionBeforeNode(placeholder.get
())), true); | |
| 410 | |
| 411 if (listElement) | |
| 412 return mergeWithNeighboringLists(listElement); | |
| 413 | |
| 414 if (canMergeLists(previousList, nextList)) | |
| 415 mergeIdenticalElements(previousList, nextList); | |
| 416 | |
| 417 return listElement; | |
| 418 } | |
| 419 | |
| 420 DEFINE_TRACE(InsertListCommand) | |
| 421 { | |
| 422 visitor->trace(m_listElement); | |
| 423 CompositeEditCommand::trace(visitor); | |
| 424 } | |
| 425 | |
| 426 } | |
| OLD | NEW |