| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. | 2 * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
| 6 * are met: | 6 * are met: |
| 7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
| 8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
| 9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
| 10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 149 // the new location of endOfSelection and use it as the end of t
he new selection. | 149 // the new location of endOfSelection and use it as the end of t
he new selection. |
| 150 if (!startOfLastParagraph.deepEquivalent().inDocument()) | 150 if (!startOfLastParagraph.deepEquivalent().inDocument()) |
| 151 return; | 151 return; |
| 152 setEndingSelection(startOfCurrentParagraph); | 152 setEndingSelection(startOfCurrentParagraph); |
| 153 | 153 |
| 154 // Save and restore endOfSelection and startOfLastParagraph when
necessary | 154 // Save and restore endOfSelection and startOfLastParagraph when
necessary |
| 155 // since moveParagraph and movePragraphWithClones can remove nod
es. | 155 // since moveParagraph and movePragraphWithClones can remove nod
es. |
| 156 // FIXME: This is an inefficient way to keep selection alive bec
ause indexForVisiblePosition walks from | 156 // FIXME: This is an inefficient way to keep selection alive bec
ause indexForVisiblePosition walks from |
| 157 // the beginning of the document to the endOfSelection everytime
this code is executed. | 157 // the beginning of the document to the endOfSelection everytime
this code is executed. |
| 158 // But not using index is hard because there are so many ways we
can lose selection inside doApplyForSingleParagraph. | 158 // But not using index is hard because there are so many ways we
can lose selection inside doApplyForSingleParagraph. |
| 159 doApplyForSingleParagraph(forceCreateList, listTag, currentSelec
tion.get()); | 159 doApplyForSingleParagraph(forceCreateList, listTag, *currentSele
ction); |
| 160 if (endOfSelection.isNull() || endOfSelection.isOrphan() || star
tOfLastParagraph.isNull() || startOfLastParagraph.isOrphan()) { | 160 if (endOfSelection.isNull() || endOfSelection.isOrphan() || star
tOfLastParagraph.isNull() || startOfLastParagraph.isOrphan()) { |
| 161 endOfSelection = visiblePositionForIndex(indexForEndOfSelect
ion, scope.get()); | 161 endOfSelection = visiblePositionForIndex(indexForEndOfSelect
ion, scope.get()); |
| 162 // If endOfSelection is null, then some contents have been d
eleted from the document. | 162 // If endOfSelection is null, then some contents have been d
eleted from the document. |
| 163 // This should never happen and if it did, exit early immedi
ately because we've lost the loop invariant. | 163 // This should never happen and if it did, exit early immedi
ately because we've lost the loop invariant. |
| 164 ASSERT(endOfSelection.isNotNull()); | 164 ASSERT(endOfSelection.isNotNull()); |
| 165 if (endOfSelection.isNull()) | 165 if (endOfSelection.isNull()) |
| 166 return; | 166 return; |
| 167 startOfLastParagraph = startOfParagraph(endOfSelection, CanS
kipOverEditingBoundary); | 167 startOfLastParagraph = startOfParagraph(endOfSelection, CanS
kipOverEditingBoundary); |
| 168 } | 168 } |
| 169 | 169 |
| 170 // Fetch the start of the selection after moving the first parag
raph, | 170 // Fetch the start of the selection after moving the first parag
raph, |
| 171 // because moving the paragraph will invalidate the original sta
rt. | 171 // because moving the paragraph will invalidate the original sta
rt. |
| 172 // We'll use the new start to restore the original selection aft
er | 172 // We'll use the new start to restore the original selection aft
er |
| 173 // we modified all selected paragraphs. | 173 // we modified all selected paragraphs. |
| 174 if (startOfCurrentParagraph == startOfSelection) | 174 if (startOfCurrentParagraph == startOfSelection) |
| 175 startOfSelection = endingSelection().visibleStart(); | 175 startOfSelection = endingSelection().visibleStart(); |
| 176 | 176 |
| 177 startOfCurrentParagraph = startOfNextParagraph(endingSelection()
.visibleStart()); | 177 startOfCurrentParagraph = startOfNextParagraph(endingSelection()
.visibleStart()); |
| 178 } | 178 } |
| 179 setEndingSelection(endOfSelection); | 179 setEndingSelection(endOfSelection); |
| 180 doApplyForSingleParagraph(forceCreateList, listTag, currentSelection
.get()); | 180 doApplyForSingleParagraph(forceCreateList, listTag, *currentSelectio
n); |
| 181 // Fetch the end of the selection, for the reason mentioned above. | 181 // Fetch the end of the selection, for the reason mentioned above. |
| 182 if (endOfSelection.isNull() || endOfSelection.isOrphan()) { | 182 if (endOfSelection.isNull() || endOfSelection.isOrphan()) { |
| 183 endOfSelection = visiblePositionForIndex(indexForEndOfSelection,
scope.get()); | 183 endOfSelection = visiblePositionForIndex(indexForEndOfSelection,
scope.get()); |
| 184 if (endOfSelection.isNull()) | 184 if (endOfSelection.isNull()) |
| 185 return; | 185 return; |
| 186 } | 186 } |
| 187 setEndingSelection(VisibleSelection(startOfSelection, endOfSelection
, endingSelection().isDirectional())); | 187 setEndingSelection(VisibleSelection(startOfSelection, endOfSelection
, endingSelection().isDirectional())); |
| 188 return; | 188 return; |
| 189 } | 189 } |
| 190 } | 190 } |
| 191 | 191 |
| 192 doApplyForSingleParagraph(false, listTag, endingSelection().firstRange().get
()); | 192 ASSERT(endingSelection().firstRange()); |
| 193 doApplyForSingleParagraph(false, listTag, *endingSelection().firstRange()); |
| 193 } | 194 } |
| 194 | 195 |
| 195 void InsertListCommand::doApplyForSingleParagraph(bool forceCreateList, const Qu
alifiedName& listTag, Range* currentSelection) | 196 void InsertListCommand::doApplyForSingleParagraph(bool forceCreateList, const Qu
alifiedName& listTag, Range& currentSelection) |
| 196 { | 197 { |
| 197 // FIXME: This will produce unexpected results for a selection that starts j
ust before a | 198 // FIXME: This will produce unexpected results for a selection that starts j
ust before a |
| 198 // table and ends inside the first cell, selectionForParagraphIteration shou
ld probably | 199 // table and ends inside the first cell, selectionForParagraphIteration shou
ld probably |
| 199 // be renamed and deployed inside setEndingSelection(). | 200 // be renamed and deployed inside setEndingSelection(). |
| 200 Node* selectionNode = endingSelection().start().deprecatedNode(); | 201 Node* selectionNode = endingSelection().start().deprecatedNode(); |
| 201 Node* listChildNode = enclosingListChild(selectionNode); | 202 Node* listChildNode = enclosingListChild(selectionNode); |
| 202 bool switchListType = false; | 203 bool switchListType = false; |
| 203 if (listChildNode) { | 204 if (listChildNode) { |
| 204 // Remove the list chlild. | 205 // Remove the list chlild. |
| 205 RefPtr<HTMLElement> listNode = enclosingList(listChildNode); | 206 RefPtr<HTMLElement> listNode = enclosingList(listChildNode); |
| 206 if (!listNode) { | 207 if (!listNode) { |
| 207 listNode = fixOrphanedListChild(listChildNode); | 208 listNode = fixOrphanedListChild(listChildNode); |
| 208 listNode = mergeWithNeighboringLists(listNode); | 209 listNode = mergeWithNeighboringLists(listNode); |
| 209 } | 210 } |
| 210 if (!listNode->hasTagName(listTag)) | 211 if (!listNode->hasTagName(listTag)) |
| 211 // listChildNode will be removed from the list and a list of type m_
type will be created. | 212 // listChildNode will be removed from the list and a list of type m_
type will be created. |
| 212 switchListType = true; | 213 switchListType = true; |
| 213 | 214 |
| 214 // If the list is of the desired type, and we are not removing the list,
then exit early. | 215 // If the list is of the desired type, and we are not removing the list,
then exit early. |
| 215 if (!switchListType && forceCreateList) | 216 if (!switchListType && forceCreateList) |
| 216 return; | 217 return; |
| 217 | 218 |
| 218 // If the entire list is selected, then convert the whole list. | 219 // If the entire list is selected, then convert the whole list. |
| 219 if (switchListType && isNodeVisiblyContainedWithin(listNode.get(), curre
ntSelection)) { | 220 if (switchListType && isNodeVisiblyContainedWithin(*listNode, currentSel
ection)) { |
| 220 bool rangeStartIsInList = visiblePositionBeforeNode(listNode.get())
== VisiblePosition(currentSelection->startPosition()); | 221 bool rangeStartIsInList = visiblePositionBeforeNode(*listNode) == Vi
siblePosition(currentSelection.startPosition()); |
| 221 bool rangeEndIsInList = visiblePositionAfterNode(listNode.get()) ==
VisiblePosition(currentSelection->endPosition()); | 222 bool rangeEndIsInList = visiblePositionAfterNode(*listNode) == Visib
lePosition(currentSelection.endPosition()); |
| 222 | 223 |
| 223 RefPtr<HTMLElement> newList = createHTMLElement(document(), listTag)
; | 224 RefPtr<HTMLElement> newList = createHTMLElement(document(), listTag)
; |
| 224 insertNodeBefore(newList, listNode); | 225 insertNodeBefore(newList, listNode); |
| 225 | 226 |
| 226 Node* firstChildInList = enclosingListChild(VisiblePosition(firstPos
itionInNode(listNode.get())).deepEquivalent().deprecatedNode(), listNode.get()); | 227 Node* firstChildInList = enclosingListChild(VisiblePosition(firstPos
itionInNode(listNode.get())).deepEquivalent().deprecatedNode(), listNode.get()); |
| 227 Node* outerBlock = firstChildInList && firstChildInList->isBlockFlow
Element() ? firstChildInList : listNode.get(); | 228 Node* outerBlock = firstChildInList && firstChildInList->isBlockFlow
Element() ? firstChildInList : listNode.get(); |
| 228 | 229 |
| 229 moveParagraphWithClones(VisiblePosition(firstPositionInNode(listNode
.get())), VisiblePosition(lastPositionInNode(listNode.get())), newList.get(), ou
terBlock); | 230 moveParagraphWithClones(VisiblePosition(firstPositionInNode(listNode
.get())), VisiblePosition(lastPositionInNode(listNode.get())), newList.get(), ou
terBlock); |
| 230 | 231 |
| 231 // Manually remove listNode because moveParagraphWithClones sometime
s leaves it behind in the document. | 232 // Manually remove listNode because moveParagraphWithClones sometime
s leaves it behind in the document. |
| 232 // See the bug 33668 and editing/execCommand/insert-list-orphaned-it
em-with-nested-lists.html. | 233 // See the bug 33668 and editing/execCommand/insert-list-orphaned-it
em-with-nested-lists.html. |
| 233 // FIXME: This might be a bug in moveParagraphWithClones or deleteSe
lection. | 234 // FIXME: This might be a bug in moveParagraphWithClones or deleteSe
lection. |
| 234 if (listNode && listNode->inDocument()) | 235 if (listNode && listNode->inDocument()) |
| 235 removeNode(listNode); | 236 removeNode(listNode); |
| 236 | 237 |
| 237 newList = mergeWithNeighboringLists(newList); | 238 newList = mergeWithNeighboringLists(newList); |
| 238 | 239 |
| 239 // Restore the start and the end of current selection if they starte
d inside listNode | 240 // Restore the start and the end of current selection if they starte
d inside listNode |
| 240 // because moveParagraphWithClones could have removed them. | 241 // because moveParagraphWithClones could have removed them. |
| 241 if (rangeStartIsInList && newList) | 242 if (rangeStartIsInList && newList) |
| 242 currentSelection->setStart(newList, 0, IGNORE_EXCEPTION); | 243 currentSelection.setStart(newList, 0, IGNORE_EXCEPTION); |
| 243 if (rangeEndIsInList && newList) | 244 if (rangeEndIsInList && newList) |
| 244 currentSelection->setEnd(newList, lastOffsetInNode(newList.get()
), IGNORE_EXCEPTION); | 245 currentSelection.setEnd(newList, lastOffsetInNode(newList.get())
, IGNORE_EXCEPTION); |
| 245 | 246 |
| 246 setEndingSelection(VisiblePosition(firstPositionInNode(newList.get()
))); | 247 setEndingSelection(VisiblePosition(firstPositionInNode(newList.get()
))); |
| 247 | 248 |
| 248 return; | 249 return; |
| 249 } | 250 } |
| 250 | 251 |
| 251 unlistifyParagraph(endingSelection().visibleStart(), listNode.get(), lis
tChildNode); | 252 unlistifyParagraph(endingSelection().visibleStart(), listNode.get(), lis
tChildNode); |
| 252 } | 253 } |
| 253 | 254 |
| 254 if (!listChildNode || switchListType || forceCreateList) | 255 if (!listChildNode || switchListType || forceCreateList) |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 367 | 368 |
| 368 // Insert the list at a position visually equivalent to start of the | 369 // Insert the list at a position visually equivalent to start of the |
| 369 // paragraph that is being moved into the list. | 370 // paragraph that is being moved into the list. |
| 370 // Try to avoid inserting it somewhere where it will be surrounded by | 371 // Try to avoid inserting it somewhere where it will be surrounded by |
| 371 // inline ancestors of start, since it is easier for editing to produce | 372 // inline ancestors of start, since it is easier for editing to produce |
| 372 // clean markup when inline elements are pushed down as far as possible. | 373 // clean markup when inline elements are pushed down as far as possible. |
| 373 Position insertionPos(start.deepEquivalent().upstream()); | 374 Position insertionPos(start.deepEquivalent().upstream()); |
| 374 // Also avoid the containing list item. | 375 // Also avoid the containing list item. |
| 375 Node* listChild = enclosingListChild(insertionPos.deprecatedNode()); | 376 Node* listChild = enclosingListChild(insertionPos.deprecatedNode()); |
| 376 if (listChild && listChild->hasTagName(liTag)) | 377 if (listChild && listChild->hasTagName(liTag)) |
| 377 insertionPos = positionInParentBeforeNode(listChild); | 378 insertionPos = positionInParentBeforeNode(*listChild); |
| 378 | 379 |
| 379 insertNodeAt(listElement, insertionPos); | 380 insertNodeAt(listElement, insertionPos); |
| 380 | 381 |
| 381 // We inserted the list at the start of the content we're about to move | 382 // We inserted the list at the start of the content we're about to move |
| 382 // Update the start of content, so we don't try to move the list into it
self. bug 19066 | 383 // Update the start of content, so we don't try to move the list into it
self. bug 19066 |
| 383 // Layout is necessary since start's node's inline renderers may have be
en destroyed by the insertion | 384 // Layout is necessary since start's node's inline renderers may have be
en destroyed by the insertion |
| 384 // The end of the content may have changed after the insertion and layou
t so update it as well. | 385 // The end of the content may have changed after the insertion and layou
t so update it as well. |
| 385 if (insertionPos == start.deepEquivalent()) { | 386 if (insertionPos == start.deepEquivalent()) { |
| 386 listElement->document().updateLayoutIgnorePendingStylesheets(); | 387 listElement->document().updateLayoutIgnorePendingStylesheets(); |
| 387 start = startOfParagraph(originalStart, CanSkipOverEditingBoundary); | 388 start = startOfParagraph(originalStart, CanSkipOverEditingBoundary); |
| 388 end = endOfParagraph(start, CanSkipOverEditingBoundary); | 389 end = endOfParagraph(start, CanSkipOverEditingBoundary); |
| 389 } | 390 } |
| 390 } | 391 } |
| 391 | 392 |
| 392 moveParagraph(start, end, VisiblePosition(positionBeforeNode(placeholder.get
())), true); | 393 moveParagraph(start, end, VisiblePosition(positionBeforeNode(placeholder.get
())), true); |
| 393 | 394 |
| 394 if (listElement) | 395 if (listElement) |
| 395 return mergeWithNeighboringLists(listElement); | 396 return mergeWithNeighboringLists(listElement); |
| 396 | 397 |
| 397 if (canMergeLists(previousList, nextList)) | 398 if (canMergeLists(previousList, nextList)) |
| 398 mergeIdenticalElements(previousList, nextList); | 399 mergeIdenticalElements(previousList, nextList); |
| 399 | 400 |
| 400 return listElement; | 401 return listElement; |
| 401 } | 402 } |
| 402 | 403 |
| 403 } | 404 } |
| OLD | NEW |