OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2005, 2006, 2008, 2009 Apple Inc. All rights reserved. | 2 * Copyright (C) 2005, 2006, 2008, 2009 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 335 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
346 end = endPosition(); | 346 end = endPosition(); |
347 } | 347 } |
348 | 348 |
349 // Calculate loop end point. | 349 // Calculate loop end point. |
350 // If the end node is before the start node (can only happen if the end node
is | 350 // If the end node is before the start node (can only happen if the end node
is |
351 // an ancestor of the start node), we gather nodes up to the next sibling of
the end node | 351 // an ancestor of the start node), we gather nodes up to the next sibling of
the end node |
352 Node *beyondEnd; | 352 Node *beyondEnd; |
353 if (start.deprecatedNode()->isDescendantOf(end.deprecatedNode())) | 353 if (start.deprecatedNode()->isDescendantOf(end.deprecatedNode())) |
354 beyondEnd = NodeTraversal::nextSkippingChildren(end.deprecatedNode()); | 354 beyondEnd = NodeTraversal::nextSkippingChildren(end.deprecatedNode()); |
355 else | 355 else |
356 beyondEnd = NodeTraversal::next(end.deprecatedNode()); | 356 beyondEnd = NodeTraversal::next(*end.deprecatedNode()); |
357 | 357 |
358 start = start.upstream(); // Move upstream to ensure we do not add redundant
spans. | 358 start = start.upstream(); // Move upstream to ensure we do not add redundant
spans. |
359 Node* startNode = start.deprecatedNode(); | 359 Node* startNode = start.deprecatedNode(); |
| 360 ASSERT(startNode); |
360 if (startNode->isTextNode() && start.deprecatedEditingOffset() >= caretMaxOf
fset(startNode)) // Move out of text node if range does not include its characte
rs. | 361 if (startNode->isTextNode() && start.deprecatedEditingOffset() >= caretMaxOf
fset(startNode)) // Move out of text node if range does not include its characte
rs. |
361 startNode = NodeTraversal::next(startNode); | 362 startNode = NodeTraversal::next(*startNode); |
362 | 363 |
363 // Store away font size before making any changes to the document. | 364 // Store away font size before making any changes to the document. |
364 // This ensures that changes to one node won't effect another. | 365 // This ensures that changes to one node won't effect another. |
365 HashMap<Node*, float> startingFontSizes; | 366 HashMap<Node*, float> startingFontSizes; |
366 for (Node *node = startNode; node != beyondEnd; node = NodeTraversal::next(n
ode)) | 367 for (Node* node = startNode; node != beyondEnd; node = NodeTraversal::next(*
node)) |
367 startingFontSizes.set(node, computedFontSize(node)); | 368 startingFontSizes.set(node, computedFontSize(node)); |
368 | 369 |
369 // These spans were added by us. If empty after font size changes, they can
be removed. | 370 // These spans were added by us. If empty after font size changes, they can
be removed. |
370 Vector<RefPtr<HTMLElement> > unstyledSpans; | 371 Vector<RefPtr<HTMLElement> > unstyledSpans; |
371 | 372 |
372 Node* lastStyledNode = 0; | 373 Node* lastStyledNode = 0; |
373 for (Node* node = startNode; node != beyondEnd; node = NodeTraversal::next(n
ode)) { | 374 for (Node* node = startNode; node != beyondEnd; node = NodeTraversal::next(*
node)) { |
374 RefPtr<HTMLElement> element; | 375 RefPtr<HTMLElement> element; |
375 if (node->isHTMLElement()) { | 376 if (node->isHTMLElement()) { |
376 // Only work on fully selected nodes. | 377 // Only work on fully selected nodes. |
377 if (!nodeFullySelected(node, start, end)) | 378 if (!nodeFullySelected(node, start, end)) |
378 continue; | 379 continue; |
379 element = toHTMLElement(node); | 380 element = toHTMLElement(node); |
380 } else if (node->isTextNode() && node->renderer() && node->parentNode()
!= lastStyledNode) { | 381 } else if (node->isTextNode() && node->renderer() && node->parentNode()
!= lastStyledNode) { |
381 // Last styled node was not parent node of this text node, but we wi
sh to style this | 382 // Last styled node was not parent node of this text node, but we wi
sh to style this |
382 // text node. To make this possible, add a style span to surround th
is text node. | 383 // text node. To make this possible, add a style span to surround th
is text node. |
383 RefPtr<HTMLElement> span = createStyleSpanElement(document()); | 384 RefPtr<HTMLElement> span = createStyleSpanElement(document()); |
(...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
665 | 666 |
666 // Remove dummy style spans created by splitting text elements. | 667 // Remove dummy style spans created by splitting text elements. |
667 cleanupUnstyledAppleStyleSpans(startDummySpanAncestor.get()); | 668 cleanupUnstyledAppleStyleSpans(startDummySpanAncestor.get()); |
668 if (endDummySpanAncestor != startDummySpanAncestor) | 669 if (endDummySpanAncestor != startDummySpanAncestor) |
669 cleanupUnstyledAppleStyleSpans(endDummySpanAncestor.get()); | 670 cleanupUnstyledAppleStyleSpans(endDummySpanAncestor.get()); |
670 } | 671 } |
671 | 672 |
672 void ApplyStyleCommand::fixRangeAndApplyInlineStyle(EditingStyle* style, const P
osition& start, const Position& end) | 673 void ApplyStyleCommand::fixRangeAndApplyInlineStyle(EditingStyle* style, const P
osition& start, const Position& end) |
673 { | 674 { |
674 Node* startNode = start.deprecatedNode(); | 675 Node* startNode = start.deprecatedNode(); |
| 676 ASSERT(startNode); |
675 | 677 |
676 if (start.deprecatedEditingOffset() >= caretMaxOffset(start.deprecatedNode()
)) { | 678 if (start.deprecatedEditingOffset() >= caretMaxOffset(start.deprecatedNode()
)) { |
677 startNode = NodeTraversal::next(startNode); | 679 startNode = NodeTraversal::next(*startNode); |
678 if (!startNode || comparePositions(end, firstPositionInOrBeforeNode(star
tNode)) < 0) | 680 if (!startNode || comparePositions(end, firstPositionInOrBeforeNode(star
tNode)) < 0) |
679 return; | 681 return; |
680 } | 682 } |
681 | 683 |
682 Node* pastEndNode = end.deprecatedNode(); | 684 Node* pastEndNode = end.deprecatedNode(); |
683 if (end.deprecatedEditingOffset() >= caretMaxOffset(end.deprecatedNode())) | 685 if (end.deprecatedEditingOffset() >= caretMaxOffset(end.deprecatedNode())) |
684 pastEndNode = NodeTraversal::nextSkippingChildren(end.deprecatedNode()); | 686 pastEndNode = NodeTraversal::nextSkippingChildren(end.deprecatedNode()); |
685 | 687 |
686 // FIXME: Callers should perform this operation on a Range that includes the
br | 688 // FIXME: Callers should perform this operation on a Range that includes the
br |
687 // if they want style applied to the empty line. | 689 // if they want style applied to the empty line. |
688 if (start == end && start.deprecatedNode()->hasTagName(brTag)) | 690 if (start == end && start.deprecatedNode()->hasTagName(brTag)) |
689 pastEndNode = NodeTraversal::next(start.deprecatedNode()); | 691 pastEndNode = NodeTraversal::next(*start.deprecatedNode()); |
690 | 692 |
691 // Start from the highest fully selected ancestor so that we can modify the
fully selected node. | 693 // Start from the highest fully selected ancestor so that we can modify the
fully selected node. |
692 // e.g. When applying font-size: large on <font color="blue">hello</font>, w
e need to include the font element in our run | 694 // e.g. When applying font-size: large on <font color="blue">hello</font>, w
e need to include the font element in our run |
693 // to generate <font color="blue" size="4">hello</font> instead of <font col
or="blue"><font size="4">hello</font></font> | 695 // to generate <font color="blue" size="4">hello</font> instead of <font col
or="blue"><font size="4">hello</font></font> |
694 RefPtr<Range> range = Range::create(startNode->document(), start, end); | 696 RefPtr<Range> range = Range::create(startNode->document(), start, end); |
695 Element* editableRoot = startNode->rootEditableElement(); | 697 Element* editableRoot = startNode->rootEditableElement(); |
696 if (startNode != editableRoot) { | 698 if (startNode != editableRoot) { |
697 while (editableRoot && startNode->parentNode() != editableRoot && isNode
VisiblyContainedWithin(startNode->parentNode(), range.get())) | 699 while (editableRoot && startNode->parentNode() != editableRoot && isNode
VisiblyContainedWithin(startNode->parentNode(), range.get())) |
698 startNode = startNode->parentNode(); | 700 startNode = startNode->parentNode(); |
699 } | 701 } |
700 | 702 |
701 applyInlineStyleToNodeRange(style, startNode, pastEndNode); | 703 applyInlineStyleToNodeRange(style, startNode, pastEndNode); |
702 } | 704 } |
703 | 705 |
704 static bool containsNonEditableRegion(Node* node) | 706 static bool containsNonEditableRegion(Node* node) |
705 { | 707 { |
706 if (!node->rendererIsEditable()) | 708 if (!node->rendererIsEditable()) |
707 return true; | 709 return true; |
708 | 710 |
709 Node* sibling = NodeTraversal::nextSkippingChildren(node); | 711 Node* sibling = NodeTraversal::nextSkippingChildren(node); |
710 for (Node* descendent = node->firstChild(); descendent && descendent != sibl
ing; descendent = NodeTraversal::next(descendent)) { | 712 for (Node* descendent = node->firstChild(); descendent && descendent != sibl
ing; descendent = NodeTraversal::next(*descendent)) { |
711 if (!descendent->rendererIsEditable()) | 713 if (!descendent->rendererIsEditable()) |
712 return true; | 714 return true; |
713 } | 715 } |
714 | 716 |
715 return false; | 717 return false; |
716 } | 718 } |
717 | 719 |
718 struct InlineRunToApplyStyle { | 720 struct InlineRunToApplyStyle { |
719 InlineRunToApplyStyle(Node* start, Node* end, Node* pastEndNode) | 721 InlineRunToApplyStyle(Node* start, Node* end, Node* pastEndNode) |
720 : start(start) | 722 : start(start) |
(...skipping 19 matching lines...) Expand all Loading... |
740 void ApplyStyleCommand::applyInlineStyleToNodeRange(EditingStyle* style, PassRef
Ptr<Node> startNode, PassRefPtr<Node> pastEndNode) | 742 void ApplyStyleCommand::applyInlineStyleToNodeRange(EditingStyle* style, PassRef
Ptr<Node> startNode, PassRefPtr<Node> pastEndNode) |
741 { | 743 { |
742 if (m_removeOnly) | 744 if (m_removeOnly) |
743 return; | 745 return; |
744 | 746 |
745 document().updateLayoutIgnorePendingStylesheets(); | 747 document().updateLayoutIgnorePendingStylesheets(); |
746 | 748 |
747 Vector<InlineRunToApplyStyle> runs; | 749 Vector<InlineRunToApplyStyle> runs; |
748 RefPtr<Node> node = startNode; | 750 RefPtr<Node> node = startNode; |
749 for (RefPtr<Node> next; node && node != pastEndNode; node = next) { | 751 for (RefPtr<Node> next; node && node != pastEndNode; node = next) { |
750 next = NodeTraversal::next(node.get()); | 752 next = NodeTraversal::next(*node); |
751 | 753 |
752 if (!node->renderer() || !node->rendererIsEditable()) | 754 if (!node->renderer() || !node->rendererIsEditable()) |
753 continue; | 755 continue; |
754 | 756 |
755 if (!node->rendererIsRichlyEditable() && node->isHTMLElement()) { | 757 if (!node->rendererIsRichlyEditable() && node->isHTMLElement()) { |
756 // This is a plaintext-only region. Only proceed if it's fully selec
ted. | 758 // This is a plaintext-only region. Only proceed if it's fully selec
ted. |
757 // pastEndNode is the node after the last fully selected node, so if
it's inside node then | 759 // pastEndNode is the node after the last fully selected node, so if
it's inside node then |
758 // node isn't fully selected. | 760 // node isn't fully selected. |
759 if (pastEndNode && pastEndNode->isDescendantOf(node.get())) | 761 if (pastEndNode && pastEndNode->isDescendantOf(node.get())) |
760 break; | 762 break; |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
821 bool ApplyStyleCommand::isStyledInlineElementToRemove(Element* element) const | 823 bool ApplyStyleCommand::isStyledInlineElementToRemove(Element* element) const |
822 { | 824 { |
823 return (m_styledInlineElement && element->hasTagName(m_styledInlineElement->
tagQName())) | 825 return (m_styledInlineElement && element->hasTagName(m_styledInlineElement->
tagQName())) |
824 || (m_isInlineElementToRemoveFunction && m_isInlineElementToRemoveFuncti
on(element)); | 826 || (m_isInlineElementToRemoveFunction && m_isInlineElementToRemoveFuncti
on(element)); |
825 } | 827 } |
826 | 828 |
827 bool ApplyStyleCommand::shouldApplyInlineStyleToRun(EditingStyle* style, Node* r
unStart, Node* pastEndNode) | 829 bool ApplyStyleCommand::shouldApplyInlineStyleToRun(EditingStyle* style, Node* r
unStart, Node* pastEndNode) |
828 { | 830 { |
829 ASSERT(style && runStart); | 831 ASSERT(style && runStart); |
830 | 832 |
831 for (Node* node = runStart; node && node != pastEndNode; node = NodeTraversa
l::next(node)) { | 833 for (Node* node = runStart; node && node != pastEndNode; node = NodeTraversa
l::next(*node)) { |
832 if (node->childNodeCount()) | 834 if (node->childNodeCount()) |
833 continue; | 835 continue; |
834 // We don't consider m_isInlineElementToRemoveFunction here because we n
ever apply style when m_isInlineElementToRemoveFunction is specified | 836 // We don't consider m_isInlineElementToRemoveFunction here because we n
ever apply style when m_isInlineElementToRemoveFunction is specified |
835 if (!style->styleIsPresentInComputedStyleOfNode(node)) | 837 if (!style->styleIsPresentInComputedStyleOfNode(node)) |
836 return true; | 838 return true; |
837 if (m_styledInlineElement && !enclosingNodeWithTag(positionBeforeNode(no
de), m_styledInlineElement->tagQName())) | 839 if (m_styledInlineElement && !enclosingNodeWithTag(positionBeforeNode(no
de), m_styledInlineElement->tagQName())) |
838 return true; | 840 return true; |
839 } | 841 } |
840 return false; | 842 return false; |
841 } | 843 } |
842 | 844 |
843 void ApplyStyleCommand::removeConflictingInlineStyleFromRun(EditingStyle* style,
RefPtr<Node>& runStart, RefPtr<Node>& runEnd, PassRefPtr<Node> pastEndNode) | 845 void ApplyStyleCommand::removeConflictingInlineStyleFromRun(EditingStyle* style,
RefPtr<Node>& runStart, RefPtr<Node>& runEnd, PassRefPtr<Node> pastEndNode) |
844 { | 846 { |
845 ASSERT(runStart && runEnd); | 847 ASSERT(runStart && runEnd); |
846 RefPtr<Node> next = runStart; | 848 RefPtr<Node> next = runStart; |
847 for (RefPtr<Node> node = next; node && node->inDocument() && node != pastEnd
Node; node = next) { | 849 for (RefPtr<Node> node = next; node && node->inDocument() && node != pastEnd
Node; node = next) { |
848 if (editingIgnoresContent(node.get())) { | 850 if (editingIgnoresContent(node.get())) { |
849 ASSERT(!node->contains(pastEndNode.get())); | 851 ASSERT(!node->contains(pastEndNode.get())); |
850 next = NodeTraversal::nextSkippingChildren(node.get()); | 852 next = NodeTraversal::nextSkippingChildren(node.get()); |
851 } else | 853 } else { |
852 next = NodeTraversal::next(node.get()); | 854 next = NodeTraversal::next(*node); |
| 855 } |
853 if (!node->isHTMLElement()) | 856 if (!node->isHTMLElement()) |
854 continue; | 857 continue; |
855 | 858 |
856 RefPtr<Node> previousSibling = node->previousSibling(); | 859 RefPtr<Node> previousSibling = node->previousSibling(); |
857 RefPtr<Node> nextSibling = node->nextSibling(); | 860 RefPtr<Node> nextSibling = node->nextSibling(); |
858 RefPtr<ContainerNode> parent = node->parentNode(); | 861 RefPtr<ContainerNode> parent = node->parentNode(); |
859 removeInlineStyleFromElement(style, toHTMLElement(node), RemoveAlways); | 862 removeInlineStyleFromElement(style, toHTMLElement(node), RemoveAlways); |
860 if (!node->inDocument()) { | 863 if (!node->inDocument()) { |
861 // FIXME: We might need to update the start and the end of current s
election here but need a test. | 864 // FIXME: We might need to update the start and the end of current s
election here but need a test. |
862 if (runStart == node) | 865 if (runStart == node) |
(...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1101 Position s = start.isNull() || start.isOrphan() ? pushDownStart : start; | 1104 Position s = start.isNull() || start.isOrphan() ? pushDownStart : start; |
1102 Position e = end.isNull() || end.isOrphan() ? pushDownEnd : end; | 1105 Position e = end.isNull() || end.isOrphan() ? pushDownEnd : end; |
1103 | 1106 |
1104 RefPtr<Node> node = start.deprecatedNode(); | 1107 RefPtr<Node> node = start.deprecatedNode(); |
1105 while (node) { | 1108 while (node) { |
1106 RefPtr<Node> next; | 1109 RefPtr<Node> next; |
1107 if (editingIgnoresContent(node.get())) { | 1110 if (editingIgnoresContent(node.get())) { |
1108 ASSERT(node == end.deprecatedNode() || !node->contains(end.deprecate
dNode())); | 1111 ASSERT(node == end.deprecatedNode() || !node->contains(end.deprecate
dNode())); |
1109 next = NodeTraversal::nextSkippingChildren(node.get()); | 1112 next = NodeTraversal::nextSkippingChildren(node.get()); |
1110 } else { | 1113 } else { |
1111 next = NodeTraversal::next(node.get()); | 1114 next = NodeTraversal::next(*node); |
1112 } | 1115 } |
1113 if (node->isHTMLElement() && nodeFullySelected(node.get(), start, end))
{ | 1116 if (node->isHTMLElement() && nodeFullySelected(node.get(), start, end))
{ |
1114 RefPtr<HTMLElement> elem = toHTMLElement(node); | 1117 RefPtr<HTMLElement> elem = toHTMLElement(node); |
1115 RefPtr<Node> prev = NodeTraversal::previousPostOrder(elem.get()); | 1118 RefPtr<Node> prev = NodeTraversal::previousPostOrder(elem.get()); |
1116 RefPtr<Node> next = NodeTraversal::next(elem.get()); | 1119 RefPtr<Node> next = NodeTraversal::next(*elem); |
1117 RefPtr<EditingStyle> styleToPushDown; | 1120 RefPtr<EditingStyle> styleToPushDown; |
1118 RefPtr<Node> childNode; | 1121 RefPtr<Node> childNode; |
1119 if (isStyledInlineElementToRemove(elem.get())) { | 1122 if (isStyledInlineElementToRemove(elem.get())) { |
1120 styleToPushDown = EditingStyle::create(); | 1123 styleToPushDown = EditingStyle::create(); |
1121 childNode = elem->firstChild(); | 1124 childNode = elem->firstChild(); |
1122 } | 1125 } |
1123 | 1126 |
1124 removeInlineStyleFromElement(style, elem.get(), RemoveIfNeeded, styl
eToPushDown.get()); | 1127 removeInlineStyleFromElement(style, elem.get(), RemoveIfNeeded, styl
eToPushDown.get()); |
1125 if (!elem->inDocument()) { | 1128 if (!elem->inDocument()) { |
1126 if (s.deprecatedNode() == elem) { | 1129 if (s.deprecatedNode() == elem) { |
(...skipping 416 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1543 String textToMove = nextText->data(); | 1546 String textToMove = nextText->data(); |
1544 insertTextIntoNode(childText, childText->length(), textToMove); | 1547 insertTextIntoNode(childText, childText->length(), textToMove); |
1545 removeNode(next); | 1548 removeNode(next); |
1546 // don't move child node pointer. it may want to merge with more text no
des. | 1549 // don't move child node pointer. it may want to merge with more text no
des. |
1547 } | 1550 } |
1548 | 1551 |
1549 updateStartEnd(newStart, newEnd); | 1552 updateStartEnd(newStart, newEnd); |
1550 } | 1553 } |
1551 | 1554 |
1552 } | 1555 } |
OLD | NEW |