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 |