OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved. | 2 * Copyright (C) 2005, 2006 Apple Computer, 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 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
157 | 157 |
158 // Delete the current selection. | 158 // Delete the current selection. |
159 if (endingSelection().isRange()) { | 159 if (endingSelection().isRange()) { |
160 calculateStyleBeforeInsertion(insertionPosition); | 160 calculateStyleBeforeInsertion(insertionPosition); |
161 deleteSelection(false, true); | 161 deleteSelection(false, true); |
162 insertionPosition = endingSelection().start(); | 162 insertionPosition = endingSelection().start(); |
163 affinity = endingSelection().affinity(); | 163 affinity = endingSelection().affinity(); |
164 } | 164 } |
165 | 165 |
166 // FIXME: The parentAnchoredEquivalent conversion needs to be moved into enc
losingBlock. | 166 // FIXME: The parentAnchoredEquivalent conversion needs to be moved into enc
losingBlock. |
167 Node* startBlockNode = enclosingBlock(insertionPosition.parentAnchoredEquiva
lent().containerNode()); | 167 RefPtr<Element> startBlock = enclosingBlock(insertionPosition.parentAnchored
Equivalent().containerNode()); |
168 Position canonicalPos = VisiblePosition(insertionPosition).deepEquivalent(); | 168 Position canonicalPos = VisiblePosition(insertionPosition).deepEquivalent(); |
169 Element* startBlock = static_cast<Element*>(startBlockNode); | 169 if (!startBlock |
170 if (!startBlockNode | 170 || !startBlock->nonShadowBoundaryParentNode() |
171 || !startBlockNode->isElementNode() | 171 || isTableCell(startBlock.get()) |
172 || !startBlock->nonShadowBoundaryParentNode() | 172 || startBlock->hasTagName(formTag) |
173 || isTableCell(startBlock) | 173 // FIXME: If the node is hidden, we don't have a canonical position so w
e will do the wrong thing for tables and <hr>. https://bugs.webkit.org/show_bug.
cgi?id=40342 |
174 || startBlock->hasTagName(formTag) | 174 || (!canonicalPos.isNull() && canonicalPos.deprecatedNode()->renderer()
&& canonicalPos.deprecatedNode()->renderer()->isTable()) |
175 // FIXME: If the node is hidden, we don't have a canonical position
so we will do the wrong thing for tables and <hr>. https://bugs.webkit.org/show_
bug.cgi?id=40342 | 175 || (!canonicalPos.isNull() && canonicalPos.deprecatedNode()->hasTagName(
hrTag))) { |
176 || (!canonicalPos.isNull() && canonicalPos.deprecatedNode()->rendere
r() && canonicalPos.deprecatedNode()->renderer()->isTable()) | |
177 || (!canonicalPos.isNull() && canonicalPos.deprecatedNode()->hasTagN
ame(hrTag))) { | |
178 applyCommandToComposite(InsertLineBreakCommand::create(document())); | 176 applyCommandToComposite(InsertLineBreakCommand::create(document())); |
179 return; | 177 return; |
180 } | 178 } |
181 | 179 |
182 // Use the leftmost candidate. | 180 // Use the leftmost candidate. |
183 insertionPosition = insertionPosition.upstream(); | 181 insertionPosition = insertionPosition.upstream(); |
184 if (!insertionPosition.isCandidate()) | 182 if (!insertionPosition.isCandidate()) |
185 insertionPosition = insertionPosition.downstream(); | 183 insertionPosition = insertionPosition.downstream(); |
186 | 184 |
187 // Adjust the insertion position after the delete | 185 // Adjust the insertion position after the delete |
(...skipping 11 matching lines...) Expand all Loading... |
199 | 197 |
200 bool isFirstInBlock = isStartOfBlock(visiblePos); | 198 bool isFirstInBlock = isStartOfBlock(visiblePos); |
201 bool isLastInBlock = isEndOfBlock(visiblePos); | 199 bool isLastInBlock = isEndOfBlock(visiblePos); |
202 bool nestNewBlock = false; | 200 bool nestNewBlock = false; |
203 | 201 |
204 // Create block to be inserted. | 202 // Create block to be inserted. |
205 RefPtr<Element> blockToInsert; | 203 RefPtr<Element> blockToInsert; |
206 if (startBlock == startBlock->rootEditableElement()) { | 204 if (startBlock == startBlock->rootEditableElement()) { |
207 blockToInsert = createDefaultParagraphElement(document()); | 205 blockToInsert = createDefaultParagraphElement(document()); |
208 nestNewBlock = true; | 206 nestNewBlock = true; |
209 } else if (shouldUseDefaultParagraphElement(startBlock)) | 207 } else if (shouldUseDefaultParagraphElement(startBlock.get())) |
210 blockToInsert = createDefaultParagraphElement(document()); | 208 blockToInsert = createDefaultParagraphElement(document()); |
211 else | 209 else |
212 blockToInsert = startBlock->cloneElementWithoutChildren(); | 210 blockToInsert = startBlock->cloneElementWithoutChildren(); |
213 | 211 |
214 //--------------------------------------------------------------------- | 212 //--------------------------------------------------------------------- |
215 // Handle case when position is in the last visible position in its block, | 213 // Handle case when position is in the last visible position in its block, |
216 // including when the block is empty. | 214 // including when the block is empty. |
217 if (isLastInBlock) { | 215 if (isLastInBlock) { |
218 if (nestNewBlock) { | 216 if (nestNewBlock) { |
219 if (isFirstInBlock && !lineBreakExistsAtVisiblePosition(visiblePos))
{ | 217 if (isFirstInBlock && !lineBreakExistsAtVisiblePosition(visiblePos))
{ |
220 // The block is empty. Create an empty block to | 218 // The block is empty. Create an empty block to |
221 // represent the paragraph that we're leaving. | 219 // represent the paragraph that we're leaving. |
222 RefPtr<Element> extraBlock = createDefaultParagraphElement(docum
ent()); | 220 RefPtr<Element> extraBlock = createDefaultParagraphElement(docum
ent()); |
223 appendNode(extraBlock, startBlock); | 221 appendNode(extraBlock, startBlock); |
224 appendBlockPlaceholder(extraBlock); | 222 appendBlockPlaceholder(extraBlock); |
225 } | 223 } |
226 appendNode(blockToInsert, startBlock); | 224 appendNode(blockToInsert, startBlock); |
227 } else { | 225 } else { |
228 // We can get here if we pasted a copied portion of a blockquote wit
h a newline at the end and are trying to paste it | 226 // We can get here if we pasted a copied portion of a blockquote wit
h a newline at the end and are trying to paste it |
229 // into an unquoted area. We then don't want the newline within the
blockquote or else it will also be quoted. | 227 // into an unquoted area. We then don't want the newline within the
blockquote or else it will also be quoted. |
230 if (m_pasteBlockqutoeIntoUnquotedArea) { | 228 if (m_pasteBlockqutoeIntoUnquotedArea) { |
231 if (Node* highestBlockquote = highestEnclosingNodeOfType(canonic
alPos, &isMailBlockquote)) | 229 if (Node* highestBlockquote = highestEnclosingNodeOfType(canonic
alPos, &isMailBlockquote)) |
232 startBlock = static_cast<Element*>(highestBlockquote); | 230 startBlock = static_cast<Element*>(highestBlockquote); |
233 } | 231 } |
234 | 232 |
235 // Most of the time we want to stay at the nesting level of the star
tBlock (e.g., when nesting within lists). However, | 233 // Most of the time we want to stay at the nesting level of the star
tBlock (e.g., when nesting within lists). However, |
236 // for div nodes, this can result in nested div tags that are hard t
o break out of. | 234 // for div nodes, this can result in nested div tags that are hard t
o break out of. |
237 Element* siblingNode = startBlock; | 235 Element* siblingNode = startBlock.get(); |
238 if (blockToInsert->hasTagName(divTag)) | 236 if (blockToInsert->hasTagName(divTag)) |
239 siblingNode = highestVisuallyEquivalentDivBelowRoot(startBlock); | 237 siblingNode = highestVisuallyEquivalentDivBelowRoot(startBlock.g
et()); |
240 insertNodeAfter(blockToInsert, siblingNode); | 238 insertNodeAfter(blockToInsert, siblingNode); |
241 } | 239 } |
242 | 240 |
243 // Recreate the same structure in the new paragraph. | 241 // Recreate the same structure in the new paragraph. |
244 | 242 |
245 Vector<Element*> ancestors; | 243 Vector<Element*> ancestors; |
246 getAncestorsInsideBlock(positionOutsideTabSpan(insertionPosition).deprec
atedNode(), startBlock, ancestors); | 244 getAncestorsInsideBlock(positionOutsideTabSpan(insertionPosition).deprec
atedNode(), startBlock.get(), ancestors); |
247 RefPtr<Element> parent = cloneHierarchyUnderNewBlock(ancestors, blockToI
nsert); | 245 RefPtr<Element> parent = cloneHierarchyUnderNewBlock(ancestors, blockToI
nsert); |
248 | 246 |
249 appendBlockPlaceholder(parent); | 247 appendBlockPlaceholder(parent); |
250 | 248 |
251 setEndingSelection(VisibleSelection(firstPositionInNode(parent.get()), D
OWNSTREAM, endingSelection().isDirectional())); | 249 setEndingSelection(VisibleSelection(firstPositionInNode(parent.get()), D
OWNSTREAM, endingSelection().isDirectional())); |
252 return; | 250 return; |
253 } | 251 } |
254 | 252 |
255 | 253 |
256 //--------------------------------------------------------------------- | 254 //--------------------------------------------------------------------- |
257 // Handle case when position is in the first visible position in its block,
and | 255 // Handle case when position is in the first visible position in its block,
and |
258 // similar case where previous position is in another, presumeably nested, b
lock. | 256 // similar case where previous position is in another, presumeably nested, b
lock. |
259 if (isFirstInBlock || !inSameBlock(visiblePos, visiblePos.previous())) { | 257 if (isFirstInBlock || !inSameBlock(visiblePos, visiblePos.previous())) { |
260 Node *refNode; | 258 Node *refNode; |
261 | 259 |
262 insertionPosition = positionOutsideTabSpan(insertionPosition); | 260 insertionPosition = positionOutsideTabSpan(insertionPosition); |
263 | 261 |
264 if (isFirstInBlock && !nestNewBlock) | 262 if (isFirstInBlock && !nestNewBlock) |
265 refNode = startBlock; | 263 refNode = startBlock.get(); |
266 else if (isFirstInBlock && nestNewBlock) { | 264 else if (isFirstInBlock && nestNewBlock) { |
267 // startBlock should always have children, otherwise isLastInBlock w
ould be true and it's handled above. | 265 // startBlock should always have children, otherwise isLastInBlock w
ould be true and it's handled above. |
268 ASSERT(startBlock->firstChild()); | 266 ASSERT(startBlock->firstChild()); |
269 refNode = startBlock->firstChild(); | 267 refNode = startBlock->firstChild(); |
270 } | 268 } |
271 else if (insertionPosition.deprecatedNode() == startBlock && nestNewBloc
k) { | 269 else if (insertionPosition.deprecatedNode() == startBlock && nestNewBloc
k) { |
272 refNode = startBlock->childNode(insertionPosition.deprecatedEditingO
ffset()); | 270 refNode = startBlock->childNode(insertionPosition.deprecatedEditingO
ffset()); |
273 ASSERT(refNode); // must be true or we'd be in the end of block case | 271 ASSERT(refNode); // must be true or we'd be in the end of block case |
274 } else | 272 } else |
275 refNode = insertionPosition.deprecatedNode(); | 273 refNode = insertionPosition.deprecatedNode(); |
276 | 274 |
277 // find ending selection position easily before inserting the paragraph | 275 // find ending selection position easily before inserting the paragraph |
278 insertionPosition = insertionPosition.downstream(); | 276 insertionPosition = insertionPosition.downstream(); |
279 | 277 |
280 insertNodeBefore(blockToInsert, refNode); | 278 insertNodeBefore(blockToInsert, refNode); |
281 | 279 |
282 // Recreate the same structure in the new paragraph. | 280 // Recreate the same structure in the new paragraph. |
283 | 281 |
284 Vector<Element*> ancestors; | 282 Vector<Element*> ancestors; |
285 getAncestorsInsideBlock(positionAvoidingSpecialElementBoundary(positionO
utsideTabSpan(insertionPosition)).deprecatedNode(), startBlock, ancestors); | 283 getAncestorsInsideBlock(positionAvoidingSpecialElementBoundary(positionO
utsideTabSpan(insertionPosition)).deprecatedNode(), startBlock.get(), ancestors)
; |
286 | 284 |
287 appendBlockPlaceholder(cloneHierarchyUnderNewBlock(ancestors, blockToIns
ert)); | 285 appendBlockPlaceholder(cloneHierarchyUnderNewBlock(ancestors, blockToIns
ert)); |
288 | 286 |
289 // In this case, we need to set the new ending selection. | 287 // In this case, we need to set the new ending selection. |
290 setEndingSelection(VisibleSelection(insertionPosition, DOWNSTREAM, endin
gSelection().isDirectional())); | 288 setEndingSelection(VisibleSelection(insertionPosition, DOWNSTREAM, endin
gSelection().isDirectional())); |
291 return; | 289 return; |
292 } | 290 } |
293 | 291 |
294 //--------------------------------------------------------------------- | 292 //--------------------------------------------------------------------- |
295 // Handle the (more complicated) general case, | 293 // Handle the (more complicated) general case, |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
335 Text* textNode = toText(insertionPosition.deprecatedNode()); | 333 Text* textNode = toText(insertionPosition.deprecatedNode()); |
336 bool atEnd = (unsigned)insertionPosition.deprecatedEditingOffset() >= te
xtNode->length(); | 334 bool atEnd = (unsigned)insertionPosition.deprecatedEditingOffset() >= te
xtNode->length(); |
337 if (insertionPosition.deprecatedEditingOffset() > 0 && !atEnd) { | 335 if (insertionPosition.deprecatedEditingOffset() > 0 && !atEnd) { |
338 splitTextNode(textNode, insertionPosition.deprecatedEditingOffset())
; | 336 splitTextNode(textNode, insertionPosition.deprecatedEditingOffset())
; |
339 insertionPosition.moveToOffset(0); | 337 insertionPosition.moveToOffset(0); |
340 visiblePos = VisiblePosition(insertionPosition); | 338 visiblePos = VisiblePosition(insertionPosition); |
341 splitText = true; | 339 splitText = true; |
342 } | 340 } |
343 } | 341 } |
344 | 342 |
| 343 // If we got detached due to mutation events, just bail out. |
| 344 if (!startBlock->parentNode()) |
| 345 return; |
| 346 |
345 // Put the added block in the tree. | 347 // Put the added block in the tree. |
346 if (nestNewBlock) | 348 if (nestNewBlock) |
347 appendNode(blockToInsert.get(), startBlock); | 349 appendNode(blockToInsert.get(), startBlock); |
348 else | 350 else |
349 insertNodeAfter(blockToInsert.get(), startBlock); | 351 insertNodeAfter(blockToInsert.get(), startBlock); |
350 | 352 |
351 document()->updateLayoutIgnorePendingStylesheets(); | 353 document()->updateLayoutIgnorePendingStylesheets(); |
352 | 354 |
353 // If the paragraph separator was inserted at the end of a paragraph, an emp
ty line must be | 355 // If the paragraph separator was inserted at the end of a paragraph, an emp
ty line must be |
354 // created. All of the nodes, starting at visiblePos, are about to be added
to the new paragraph | 356 // created. All of the nodes, starting at visiblePos, are about to be added
to the new paragraph |
355 // element. If the first node to be inserted won't be one that will hold an
empty line open, add a br. | 357 // element. If the first node to be inserted won't be one that will hold an
empty line open, add a br. |
356 if (isEndOfParagraph(visiblePos) && !lineBreakExistsAtVisiblePosition(visibl
ePos)) | 358 if (isEndOfParagraph(visiblePos) && !lineBreakExistsAtVisiblePosition(visibl
ePos)) |
357 appendNode(createBreakElement(document()).get(), blockToInsert.get()); | 359 appendNode(createBreakElement(document()).get(), blockToInsert.get()); |
358 | 360 |
359 // Move the start node and the siblings of the start node. | 361 // Move the start node and the siblings of the start node. |
360 if (VisiblePosition(insertionPosition) != VisiblePosition(positionBeforeNode
(blockToInsert.get()))) { | 362 if (VisiblePosition(insertionPosition) != VisiblePosition(positionBeforeNode
(blockToInsert.get()))) { |
361 Node* n; | 363 Node* n; |
362 if (insertionPosition.containerNode() == startBlock) | 364 if (insertionPosition.containerNode() == startBlock) |
363 n = insertionPosition.computeNodeAfterPosition(); | 365 n = insertionPosition.computeNodeAfterPosition(); |
364 else { | 366 else { |
365 Node* splitTo = insertionPosition.containerNode(); | 367 Node* splitTo = insertionPosition.containerNode(); |
366 if (splitTo->isTextNode() && insertionPosition.offsetInContainerNode
() >= caretMaxOffset(splitTo)) | 368 if (splitTo->isTextNode() && insertionPosition.offsetInContainerNode
() >= caretMaxOffset(splitTo)) |
367 splitTo = splitTo->traverseNextNode(startBlock); | 369 splitTo = splitTo->traverseNextNode(startBlock.get()); |
368 ASSERT(splitTo); | 370 ASSERT(splitTo); |
369 splitTreeToNode(splitTo, startBlock); | 371 splitTreeToNode(splitTo, startBlock.get()); |
370 | 372 |
371 for (n = startBlock->firstChild(); n; n = n->nextSibling()) { | 373 for (n = startBlock->firstChild(); n; n = n->nextSibling()) { |
372 if (comparePositions(VisiblePosition(insertionPosition), positio
nBeforeNode(n)) <= 0) | 374 if (comparePositions(VisiblePosition(insertionPosition), positio
nBeforeNode(n)) <= 0) |
373 break; | 375 break; |
374 } | 376 } |
375 } | 377 } |
376 | 378 |
377 while (n && n != blockToInsert) { | 379 while (n && n != blockToInsert) { |
378 Node *next = n->nextSibling(); | 380 Node *next = n->nextSibling(); |
379 removeNode(n); | 381 removeNode(n); |
(...skipping 10 matching lines...) Expand all Loading... |
390 if (!insertionPosition.isRenderedCharacter()) { | 392 if (!insertionPosition.isRenderedCharacter()) { |
391 // Clear out all whitespace and insert one non-breaking space | 393 // Clear out all whitespace and insert one non-breaking space |
392 ASSERT(!insertionPosition.deprecatedNode()->renderer() || insertionP
osition.deprecatedNode()->renderer()->style()->collapseWhiteSpace()); | 394 ASSERT(!insertionPosition.deprecatedNode()->renderer() || insertionP
osition.deprecatedNode()->renderer()->style()->collapseWhiteSpace()); |
393 deleteInsignificantTextDownstream(insertionPosition); | 395 deleteInsignificantTextDownstream(insertionPosition); |
394 if (insertionPosition.deprecatedNode()->isTextNode()) | 396 if (insertionPosition.deprecatedNode()->isTextNode()) |
395 insertTextIntoNode(toText(insertionPosition.deprecatedNode()), 0
, nonBreakingSpaceString()); | 397 insertTextIntoNode(toText(insertionPosition.deprecatedNode()), 0
, nonBreakingSpaceString()); |
396 } | 398 } |
397 } | 399 } |
398 | 400 |
399 setEndingSelection(VisibleSelection(firstPositionInNode(blockToInsert.get())
, DOWNSTREAM, endingSelection().isDirectional())); | 401 setEndingSelection(VisibleSelection(firstPositionInNode(blockToInsert.get())
, DOWNSTREAM, endingSelection().isDirectional())); |
400 applyStyleAfterInsertion(startBlock); | 402 applyStyleAfterInsertion(startBlock.get()); |
401 } | 403 } |
402 | 404 |
403 } // namespace WebCore | 405 } // namespace WebCore |
OLD | NEW |