OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2004, 2008, 2009, 2010 Apple Inc. All rights reserved. | 2 * Copyright (C) 2004, 2008, 2009, 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 325 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
336 SetSelectionOptions options, | 336 SetSelectionOptions options, |
337 CursorAlignOnScroll align, | 337 CursorAlignOnScroll align, |
338 TextGranularity granularity) { | 338 TextGranularity granularity) { |
339 setSelection( | 339 setSelection( |
340 SelectionInFlatTree::Builder(newSelection.asSelection()) | 340 SelectionInFlatTree::Builder(newSelection.asSelection()) |
341 .setIsHandleVisible(handleVisibility == HandleVisibility::Visible) | 341 .setIsHandleVisible(handleVisibility == HandleVisibility::Visible) |
342 .build(), | 342 .build(), |
343 options, align, granularity); | 343 options, align, granularity); |
344 } | 344 } |
345 | 345 |
346 // TODO(yosin): We should move |computePositionForChildrenRemoval()| to | |
347 // "SelectionEditor.cpp" since it used only in | |
348 // |SelectionEditor::nodeChildrenWillBeRemoved()|. | |
349 static Position computePositionForChildrenRemoval(const Position& position, | |
350 ContainerNode& container) { | |
351 Node* node = position.computeContainerNode(); | |
352 if (container.containsIncludingHostElements(*node)) | |
353 return Position::firstPositionInNode(&container); | |
354 return position; | |
355 } | |
356 | |
357 void FrameSelection::nodeChildrenWillBeRemoved(ContainerNode& container) { | 346 void FrameSelection::nodeChildrenWillBeRemoved(ContainerNode& container) { |
358 if (!container.inActiveDocument()) | 347 if (!container.inActiveDocument()) |
359 return; | 348 return; |
360 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to | 349 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to |
361 // |Editor| class. | 350 // |Editor| class. |
362 if (!document().isRunningExecCommand()) | 351 if (!document().isRunningExecCommand()) |
363 TypingCommand::closeTyping(m_frame); | 352 TypingCommand::closeTyping(m_frame); |
364 } | 353 } |
365 | 354 |
366 // TODO(yosin): We should move |SelectionEditor::nodeChildrenWillBeRemoved()| | |
367 // to "SelectionEditor.cpp". | |
368 void SelectionEditor::nodeChildrenWillBeRemoved(ContainerNode& container) { | |
369 if (m_selection.isNone()) | |
370 return; | |
371 const Position oldBase = m_selection.m_base; | |
372 const Position oldExtent = m_selection.m_extent; | |
373 const Position& newBase = | |
374 computePositionForChildrenRemoval(oldBase, container); | |
375 const Position& newExtent = | |
376 computePositionForChildrenRemoval(oldExtent, container); | |
377 if (newBase == oldBase && newExtent == oldExtent) | |
378 return; | |
379 m_selection = SelectionInDOMTree::Builder() | |
380 .setBaseAndExtent(newBase, newExtent) | |
381 .build(); | |
382 markCacheDirty(); | |
383 } | |
384 | |
385 // TODO(yosin): We should move |computePositionForChildrenRemoval()| with | |
386 // |nodeWillBeRemoved()| to "SelectionEditor.cpp". | |
387 static Position computePositionForNodeRemoval(const Position& position, | |
388 Node& nodeToBeRemoved) { | |
389 Position result = position; | |
390 // TODO(yosin): We should rename |updatePositionForNodeRemoval()| | |
391 // to |computePositionForNodeRemoval()| to avoid using output parameter. | |
392 updatePositionForNodeRemoval(result, nodeToBeRemoved); | |
393 return result; | |
394 } | |
395 | |
396 // TODO(yosin): We should move |nodeWillBeRemoved()| to | |
397 // "SelectionEditor.cpp". | |
398 void SelectionEditor::nodeWillBeRemoved(Node& nodeToBeRemoved) { | |
399 if (m_selection.isNone()) | |
400 return; | |
401 const Position oldBase = m_selection.m_base; | |
402 const Position oldExtent = m_selection.m_extent; | |
403 const Position& newBase = | |
404 computePositionForNodeRemoval(oldBase, nodeToBeRemoved); | |
405 const Position& newExtent = | |
406 computePositionForNodeRemoval(oldExtent, nodeToBeRemoved); | |
407 if (newBase == oldBase && newExtent == oldExtent) | |
408 return; | |
409 m_selection = SelectionInDOMTree::Builder() | |
410 .setBaseAndExtent(newBase, newExtent) | |
411 .build(); | |
412 markCacheDirty(); | |
413 } | |
414 | |
415 void FrameSelection::nodeWillBeRemoved(Node& node) { | 355 void FrameSelection::nodeWillBeRemoved(Node& node) { |
416 // There can't be a selection inside a fragment, so if a fragment's node is | 356 // There can't be a selection inside a fragment, so if a fragment's node is |
417 // being removed, the selection in the document that created the fragment | 357 // being removed, the selection in the document that created the fragment |
418 // needs no adjustment. | 358 // needs no adjustment. |
419 if (!node.inActiveDocument()) | 359 if (!node.inActiveDocument()) |
420 return; | 360 return; |
421 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to | 361 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to |
422 // |Editor| class. | 362 // |Editor| class. |
423 if (!document().isRunningExecCommand()) | 363 if (!document().isRunningExecCommand()) |
424 TypingCommand::closeTyping(m_frame); | 364 TypingCommand::closeTyping(m_frame); |
425 } | 365 } |
426 | 366 |
427 // TODO(yosin): We should move |updatePositionAfterAdoptingTextReplacement()| | |
428 // to "SelectionEditor.cpp" since it used only in | |
429 // |SelectionEditor::didUpdateCharacterData()|. | |
430 static Position updatePositionAfterAdoptingTextReplacement( | |
431 const Position& position, | |
432 CharacterData* node, | |
433 unsigned offset, | |
434 unsigned oldLength, | |
435 unsigned newLength) { | |
436 if (position.anchorNode() != node) | |
437 return position; | |
438 | |
439 if (position.isBeforeAnchor()) { | |
440 return updatePositionAfterAdoptingTextReplacement( | |
441 Position(node, 0), node, offset, oldLength, newLength); | |
442 } | |
443 if (position.isAfterAnchor()) { | |
444 return updatePositionAfterAdoptingTextReplacement( | |
445 Position(node, oldLength), node, offset, oldLength, newLength); | |
446 } | |
447 | |
448 // See: | |
449 // http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-
Mutation | |
450 DCHECK_GE(position.offsetInContainerNode(), 0); | |
451 unsigned positionOffset = | |
452 static_cast<unsigned>(position.offsetInContainerNode()); | |
453 // Replacing text can be viewed as a deletion followed by insertion. | |
454 if (positionOffset >= offset && positionOffset <= offset + oldLength) | |
455 positionOffset = offset; | |
456 | |
457 // Adjust the offset if the position is after the end of the deleted contents | |
458 // (positionOffset > offset + oldLength) to avoid having a stale offset. | |
459 if (positionOffset > offset + oldLength) | |
460 positionOffset = positionOffset - oldLength + newLength; | |
461 | |
462 // Due to case folding | |
463 // (http://unicode.org/Public/UCD/latest/ucd/CaseFolding.txt), LayoutText | |
464 // length may be different from Text length. A correct implementation would | |
465 // translate the LayoutText offset to a Text offset; this is just a safety | |
466 // precaution to avoid offset values that run off the end of the Text. | |
467 if (positionOffset > node->length()) | |
468 positionOffset = node->length(); | |
469 | |
470 return Position(node, positionOffset); | |
471 } | |
472 | |
473 // TODO(yosin): We should move |didUpdateCharacterData()| to | |
474 // "SelectionEditor.cpp". | |
475 void SelectionEditor::didUpdateCharacterData(CharacterData* node, | |
476 unsigned offset, | |
477 unsigned oldLength, | |
478 unsigned newLength) { | |
479 // The fragment check is a performance optimization. See | |
480 // http://trac.webkit.org/changeset/30062. | |
481 if (m_selection.isNone() || !node || !node->isConnected()) { | |
482 didFinishDOMMutation(); | |
483 return; | |
484 } | |
485 const Position& newBase = updatePositionAfterAdoptingTextReplacement( | |
486 m_selection.m_base, node, offset, oldLength, newLength); | |
487 const Position& newExtent = updatePositionAfterAdoptingTextReplacement( | |
488 m_selection.m_extent, node, offset, oldLength, newLength); | |
489 didFinishTextChange(newBase, newExtent); | |
490 } | |
491 | |
492 // TODO(yosin): We should move |updatePostionAfterAdoptingTextNodesMerged()| | |
493 // to "SelectionEditor.cpp" since it used only in | |
494 // |SelectionEditor::didMergeTextNodes()|. | |
495 // TODO(yosin): We should introduce |Position(const Text&, int)| to avoid | |
496 // |const_cast<Text*>|. | |
497 static Position updatePostionAfterAdoptingTextNodesMerged( | |
498 const Position& position, | |
499 const Text& mergedNode, | |
500 const NodeWithIndex& nodeToBeRemovedWithIndex, | |
501 unsigned oldLength) { | |
502 Node* const anchorNode = position.anchorNode(); | |
503 const Node& nodeToBeRemoved = nodeToBeRemovedWithIndex.node(); | |
504 switch (position.anchorType()) { | |
505 case PositionAnchorType::BeforeChildren: | |
506 case PositionAnchorType::AfterChildren: | |
507 return position; | |
508 case PositionAnchorType::BeforeAnchor: | |
509 if (anchorNode == nodeToBeRemoved) | |
510 return Position(const_cast<Text*>(&mergedNode), mergedNode.length()); | |
511 return position; | |
512 case PositionAnchorType::AfterAnchor: | |
513 if (anchorNode == nodeToBeRemoved) | |
514 return Position(const_cast<Text*>(&mergedNode), mergedNode.length()); | |
515 if (anchorNode == mergedNode) | |
516 return Position(const_cast<Text*>(&mergedNode), oldLength); | |
517 return position; | |
518 case PositionAnchorType::OffsetInAnchor: { | |
519 const int offset = position.offsetInContainerNode(); | |
520 if (anchorNode == nodeToBeRemoved) | |
521 return Position(const_cast<Text*>(&mergedNode), oldLength + offset); | |
522 if (anchorNode == nodeToBeRemoved.parentNode() && | |
523 offset == nodeToBeRemovedWithIndex.index()) { | |
524 return Position(const_cast<Text*>(&mergedNode), oldLength); | |
525 } | |
526 return position; | |
527 } | |
528 } | |
529 NOTREACHED() << position; | |
530 return position; | |
531 } | |
532 | |
533 // TODO(yosin): We should move |SelectionEditor::didMergeTextNodes()| to | |
534 // "SelectionEditor.cpp". | |
535 void SelectionEditor::didMergeTextNodes( | |
536 const Text& mergedNode, | |
537 const NodeWithIndex& nodeToBeRemovedWithIndex, | |
538 unsigned oldLength) { | |
539 if (m_selection.isNone()) { | |
540 didFinishDOMMutation(); | |
541 return; | |
542 } | |
543 const Position& newBase = updatePostionAfterAdoptingTextNodesMerged( | |
544 m_selection.m_base, mergedNode, nodeToBeRemovedWithIndex, oldLength); | |
545 const Position& newExtent = updatePostionAfterAdoptingTextNodesMerged( | |
546 m_selection.m_extent, mergedNode, nodeToBeRemovedWithIndex, oldLength); | |
547 didFinishTextChange(newBase, newExtent); | |
548 } | |
549 | |
550 // TODO(yosin): We should move |updatePostionAfterAdoptingTextNodeSplit()| | |
551 // to "SelectionEditor.cpp" since it used only in | |
552 // |SelectionEditor::didSplitTextNode()|. | |
553 static Position updatePostionAfterAdoptingTextNodeSplit( | |
554 const Position& position, | |
555 const Text& oldNode) { | |
556 if (!position.anchorNode() || position.anchorNode() != &oldNode || | |
557 !position.isOffsetInAnchor()) | |
558 return position; | |
559 // See: | |
560 // http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-
Mutation | |
561 DCHECK_GE(position.offsetInContainerNode(), 0); | |
562 unsigned positionOffset = | |
563 static_cast<unsigned>(position.offsetInContainerNode()); | |
564 unsigned oldLength = oldNode.length(); | |
565 if (positionOffset <= oldLength) | |
566 return position; | |
567 return Position(toText(oldNode.nextSibling()), positionOffset - oldLength); | |
568 } | |
569 | |
570 // TODO(yosin): We should move |SelectionEditor::didSplitTextNode()| to | |
571 // "SelectionEditor.cpp". | |
572 void SelectionEditor::didSplitTextNode(const Text& oldNode) { | |
573 if (m_selection.isNone() || !oldNode.isConnected()) { | |
574 didFinishDOMMutation(); | |
575 return; | |
576 } | |
577 const Position& newBase = | |
578 updatePostionAfterAdoptingTextNodeSplit(m_selection.m_base, oldNode); | |
579 const Position& newExtent = | |
580 updatePostionAfterAdoptingTextNodeSplit(m_selection.m_extent, oldNode); | |
581 didFinishTextChange(newBase, newExtent); | |
582 } | |
583 | |
584 void FrameSelection::didChangeFocus() { | 367 void FrameSelection::didChangeFocus() { |
585 // Hits in | 368 // Hits in |
586 // virtual/gpu/compositedscrolling/scrollbars/scrollbar-miss-mousemove-disable
d.html | 369 // virtual/gpu/compositedscrolling/scrollbars/scrollbar-miss-mousemove-disable
d.html |
587 DisableCompositingQueryAsserts disabler; | 370 DisableCompositingQueryAsserts disabler; |
588 updateAppearance(); | 371 updateAppearance(); |
589 } | 372 } |
590 | 373 |
591 static DispatchEventResult dispatchSelectStart( | 374 static DispatchEventResult dispatchSelectStart( |
592 const VisibleSelection& selection) { | 375 const VisibleSelection& selection) { |
593 Node* selectStartTarget = selection.extent().computeContainerNode(); | 376 Node* selectStartTarget = selection.extent().computeContainerNode(); |
(...skipping 810 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1404 } | 1187 } |
1405 | 1188 |
1406 void showTree(const blink::FrameSelection* sel) { | 1189 void showTree(const blink::FrameSelection* sel) { |
1407 if (sel) | 1190 if (sel) |
1408 sel->showTreeForThis(); | 1191 sel->showTreeForThis(); |
1409 else | 1192 else |
1410 LOG(INFO) << "Cannot showTree for <null> FrameSelection."; | 1193 LOG(INFO) << "Cannot showTree for <null> FrameSelection."; |
1411 } | 1194 } |
1412 | 1195 |
1413 #endif | 1196 #endif |
OLD | NEW |