OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserv
ed. | 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserv
ed. |
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 18 matching lines...) Expand all Loading... |
29 #include "bindings/core/v8/ExceptionState.h" | 29 #include "bindings/core/v8/ExceptionState.h" |
30 #include "bindings/core/v8/ExceptionStatePlaceholder.h" | 30 #include "bindings/core/v8/ExceptionStatePlaceholder.h" |
31 #include "core/HTMLNames.h" | 31 #include "core/HTMLNames.h" |
32 #include "core/dom/Document.h" | 32 #include "core/dom/Document.h" |
33 #include "core/dom/Element.h" | 33 #include "core/dom/Element.h" |
34 #include "core/dom/NodeTraversal.h" | 34 #include "core/dom/NodeTraversal.h" |
35 #include "core/dom/Text.h" | 35 #include "core/dom/Text.h" |
36 #include "core/editing/EditingUtilities.h" | 36 #include "core/editing/EditingUtilities.h" |
37 #include "core/editing/FrameSelection.h" | 37 #include "core/editing/FrameSelection.h" |
38 #include "core/editing/Position.h" | 38 #include "core/editing/Position.h" |
| 39 #include "core/editing/PositionIterator.h" |
39 #include "core/editing/RenderedPosition.h" | 40 #include "core/editing/RenderedPosition.h" |
40 #include "core/editing/TextAffinity.h" | 41 #include "core/editing/TextAffinity.h" |
41 #include "core/editing/VisiblePosition.h" | 42 #include "core/editing/VisiblePosition.h" |
42 #include "core/editing/iterators/BackwardsCharacterIterator.h" | 43 #include "core/editing/iterators/BackwardsCharacterIterator.h" |
43 #include "core/editing/iterators/CharacterIterator.h" | 44 #include "core/editing/iterators/CharacterIterator.h" |
44 #include "core/editing/iterators/SimplifiedBackwardsTextIterator.h" | 45 #include "core/editing/iterators/SimplifiedBackwardsTextIterator.h" |
45 #include "core/editing/iterators/TextIterator.h" | 46 #include "core/editing/iterators/TextIterator.h" |
46 #include "core/html/HTMLBRElement.h" | 47 #include "core/html/HTMLBRElement.h" |
47 #include "core/layout/HitTestRequest.h" | 48 #include "core/layout/HitTestRequest.h" |
48 #include "core/layout/HitTestResult.h" | 49 #include "core/layout/HitTestResult.h" |
(...skipping 1546 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1595 } | 1596 } |
1596 | 1597 |
1597 if (previousRenderedEditable(position1.anchorNode()) == position2.anchorNode
() | 1598 if (previousRenderedEditable(position1.anchorNode()) == position2.anchorNode
() |
1598 && !renderedOffset1 && renderedOffset2 == caretMaxOffset(position2.ancho
rNode())) { | 1599 && !renderedOffset1 && renderedOffset2 == caretMaxOffset(position2.ancho
rNode())) { |
1599 return false; | 1600 return false; |
1600 } | 1601 } |
1601 | 1602 |
1602 return true; | 1603 return true; |
1603 } | 1604 } |
1604 | 1605 |
1605 } | 1606 // Whether or not [node, 0] and [node, lastOffsetForEditing(node)] are their own
VisiblePositions. |
| 1607 // If true, adjacent candidates are visually distinct. |
| 1608 // FIXME: Disregard nodes with layoutObjects that have no height, as we do in is
Candidate. |
| 1609 // FIXME: Share code with isCandidate, if possible. |
| 1610 static bool endsOfNodeAreVisuallyDistinctPositions(Node* node) |
| 1611 { |
| 1612 if (!node || !node->layoutObject()) |
| 1613 return false; |
| 1614 |
| 1615 if (!node->layoutObject()->isInline()) |
| 1616 return true; |
| 1617 |
| 1618 // Don't include inline tables. |
| 1619 if (isHTMLTableElement(*node)) |
| 1620 return false; |
| 1621 |
| 1622 // A Marquee elements are moving so we should assume their ends are always |
| 1623 // visibily distinct. |
| 1624 if (isHTMLMarqueeElement(*node)) |
| 1625 return true; |
| 1626 |
| 1627 // There is a VisiblePosition inside an empty inline-block container. |
| 1628 return node->layoutObject()->isReplaced() && canHaveChildrenForEditing(node)
&& toLayoutBox(node->layoutObject())->size().height() != 0 && !node->hasChildre
n(); |
| 1629 } |
| 1630 |
| 1631 template <typename Strategy> |
| 1632 static Node* enclosingVisualBoundary(Node* node) |
| 1633 { |
| 1634 while (node && !endsOfNodeAreVisuallyDistinctPositions(node)) |
| 1635 node = Strategy::parent(*node); |
| 1636 |
| 1637 return node; |
| 1638 } |
| 1639 |
| 1640 // upstream() and downstream() want to return positions that are either in a |
| 1641 // text node or at just before a non-text node. This method checks for that. |
| 1642 template <typename Strategy> |
| 1643 static bool isStreamer(const PositionIteratorAlgorithm<Strategy>& pos) |
| 1644 { |
| 1645 if (!pos.node()) |
| 1646 return true; |
| 1647 |
| 1648 if (isAtomicNode(pos.node())) |
| 1649 return true; |
| 1650 |
| 1651 return pos.atStartOfNode(); |
| 1652 } |
| 1653 |
| 1654 template <typename Strategy> |
| 1655 static PositionAlgorithm<Strategy> mostForwardCaretPosition(const PositionAlgori
thm<Strategy>& position, EditingBoundaryCrossingRule rule) |
| 1656 { |
| 1657 TRACE_EVENT0("blink", "Position::upstream"); |
| 1658 |
| 1659 Node* startNode = position.anchorNode(); |
| 1660 if (!startNode) |
| 1661 return PositionAlgorithm<Strategy>(); |
| 1662 |
| 1663 // iterate backward from there, looking for a qualified position |
| 1664 Node* boundary = enclosingVisualBoundary<Strategy>(startNode); |
| 1665 // FIXME: PositionIterator should respect Before and After positions. |
| 1666 PositionIteratorAlgorithm<Strategy> lastVisible(position.isAfterAnchor() ? P
ositionAlgorithm<Strategy>::editingPositionOf(position.anchorNode(), Strategy::c
aretMaxOffset(*position.anchorNode())) : position); |
| 1667 PositionIteratorAlgorithm<Strategy> currentPos = lastVisible; |
| 1668 bool startEditable = startNode->hasEditableStyle(); |
| 1669 Node* lastNode = startNode; |
| 1670 bool boundaryCrossed = false; |
| 1671 for (; !currentPos.atStart(); currentPos.decrement()) { |
| 1672 Node* currentNode = currentPos.node(); |
| 1673 // Don't check for an editability change if we haven't moved to a differ
ent node, |
| 1674 // to avoid the expense of computing hasEditableStyle(). |
| 1675 if (currentNode != lastNode) { |
| 1676 // Don't change editability. |
| 1677 bool currentEditable = currentNode->hasEditableStyle(); |
| 1678 if (startEditable != currentEditable) { |
| 1679 if (rule == CannotCrossEditingBoundary) |
| 1680 break; |
| 1681 boundaryCrossed = true; |
| 1682 } |
| 1683 lastNode = currentNode; |
| 1684 } |
| 1685 |
| 1686 // If we've moved to a position that is visually distinct, return the la
st saved position. There |
| 1687 // is code below that terminates early if we're *about* to move to a vis
ually distinct position. |
| 1688 if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode !
= boundary) |
| 1689 return lastVisible.deprecatedComputePosition(); |
| 1690 |
| 1691 // skip position in non-laid out or invisible node |
| 1692 LayoutObject* layoutObject = currentNode->layoutObject(); |
| 1693 if (!layoutObject || layoutObject->style()->visibility() != VISIBLE) |
| 1694 continue; |
| 1695 |
| 1696 if (rule == CanCrossEditingBoundary && boundaryCrossed) { |
| 1697 lastVisible = currentPos; |
| 1698 break; |
| 1699 } |
| 1700 |
| 1701 // track last visible streamer position |
| 1702 if (isStreamer<Strategy>(currentPos)) |
| 1703 lastVisible = currentPos; |
| 1704 |
| 1705 // Don't move past a position that is visually distinct. We could rely
on code above to terminate and |
| 1706 // return lastVisible on the next iteration, but we terminate early to a
void doing a nodeIndex() call. |
| 1707 if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentPos.at
StartOfNode()) |
| 1708 return lastVisible.deprecatedComputePosition(); |
| 1709 |
| 1710 // Return position after tables and nodes which have content that can be
ignored. |
| 1711 if (Strategy::editingIgnoresContent(currentNode) || isRenderedHTMLTableE
lement(currentNode)) { |
| 1712 if (currentPos.atEndOfNode()) |
| 1713 return PositionAlgorithm<Strategy>::afterNode(currentNode); |
| 1714 continue; |
| 1715 } |
| 1716 |
| 1717 // return current position if it is in laid out text |
| 1718 if (layoutObject->isText() && toLayoutText(layoutObject)->firstTextBox()
) { |
| 1719 if (currentNode != startNode) { |
| 1720 // This assertion fires in layout tests in the case-transform.ht
ml test because |
| 1721 // of a mix-up between offsets in the text in the DOM tree with
text in the |
| 1722 // layout tree which can have a different length due to case tra
nsformation. |
| 1723 // Until we resolve that, disable this so we can run the layout
tests! |
| 1724 // ASSERT(currentOffset >= layoutObject->caretMaxOffset()); |
| 1725 return PositionAlgorithm<Strategy>(currentNode, layoutObject->ca
retMaxOffset()); |
| 1726 } |
| 1727 |
| 1728 unsigned textOffset = currentPos.offsetInLeafNode(); |
| 1729 LayoutText* textLayoutObject = toLayoutText(layoutObject); |
| 1730 InlineTextBox* lastTextBox = textLayoutObject->lastTextBox(); |
| 1731 for (InlineTextBox* box = textLayoutObject->firstTextBox(); box; box
= box->nextTextBox()) { |
| 1732 if (textOffset <= box->start() + box->len()) { |
| 1733 if (textOffset > box->start()) |
| 1734 return currentPos.computePosition(); |
| 1735 continue; |
| 1736 } |
| 1737 |
| 1738 if (box == lastTextBox || textOffset != box->start() + box->len(
) + 1) |
| 1739 continue; |
| 1740 |
| 1741 // The text continues on the next line only if the last text box
is not on this line and |
| 1742 // none of the boxes on this line have a larger start offset. |
| 1743 |
| 1744 bool continuesOnNextLine = true; |
| 1745 InlineBox* otherBox = box; |
| 1746 while (continuesOnNextLine) { |
| 1747 otherBox = otherBox->nextLeafChild(); |
| 1748 if (!otherBox) |
| 1749 break; |
| 1750 if (otherBox == lastTextBox || (otherBox->layoutObject() ==
textLayoutObject && toInlineTextBox(otherBox)->start() > textOffset)) |
| 1751 continuesOnNextLine = false; |
| 1752 } |
| 1753 |
| 1754 otherBox = box; |
| 1755 while (continuesOnNextLine) { |
| 1756 otherBox = otherBox->prevLeafChild(); |
| 1757 if (!otherBox) |
| 1758 break; |
| 1759 if (otherBox == lastTextBox || (otherBox->layoutObject() ==
textLayoutObject && toInlineTextBox(otherBox)->start() > textOffset)) |
| 1760 continuesOnNextLine = false; |
| 1761 } |
| 1762 |
| 1763 if (continuesOnNextLine) |
| 1764 return currentPos.computePosition(); |
| 1765 } |
| 1766 } |
| 1767 } |
| 1768 return lastVisible.deprecatedComputePosition(); |
| 1769 } |
| 1770 |
| 1771 Position mostForwardCaretPosition(const Position& position, EditingBoundaryCross
ingRule rule) |
| 1772 { |
| 1773 return mostForwardCaretPosition<EditingStrategy>(position, rule); |
| 1774 } |
| 1775 |
| 1776 PositionInComposedTree mostForwardCaretPosition(const PositionInComposedTree& po
sition, EditingBoundaryCrossingRule rule) |
| 1777 { |
| 1778 return mostForwardCaretPosition<EditingInComposedTreeStrategy>(position, rul
e); |
| 1779 } |
| 1780 |
| 1781 template <typename Strategy> |
| 1782 PositionAlgorithm<Strategy> mostBackwardCaretPosition(const PositionAlgorithm<St
rategy>& position, EditingBoundaryCrossingRule rule) |
| 1783 { |
| 1784 TRACE_EVENT0("blink", "Position::downstream"); |
| 1785 |
| 1786 Node* startNode = position.anchorNode(); |
| 1787 if (!startNode) |
| 1788 return PositionAlgorithm<Strategy>(); |
| 1789 |
| 1790 // iterate forward from there, looking for a qualified position |
| 1791 Node* boundary = enclosingVisualBoundary<Strategy>(startNode); |
| 1792 // FIXME: PositionIterator should respect Before and After positions. |
| 1793 PositionIteratorAlgorithm<Strategy> lastVisible(position.isAfterAnchor() ? P
ositionAlgorithm<Strategy>::editingPositionOf(position.anchorNode(), Strategy::c
aretMaxOffset(*position.anchorNode())) : position); |
| 1794 PositionIteratorAlgorithm<Strategy> currentPos = lastVisible; |
| 1795 bool startEditable = startNode->hasEditableStyle(); |
| 1796 Node* lastNode = startNode; |
| 1797 bool boundaryCrossed = false; |
| 1798 for (; !currentPos.atEnd(); currentPos.increment()) { |
| 1799 Node* currentNode = currentPos.node(); |
| 1800 // Don't check for an editability change if we haven't moved to a differ
ent node, |
| 1801 // to avoid the expense of computing hasEditableStyle(). |
| 1802 if (currentNode != lastNode) { |
| 1803 // Don't change editability. |
| 1804 bool currentEditable = currentNode->hasEditableStyle(); |
| 1805 if (startEditable != currentEditable) { |
| 1806 if (rule == CannotCrossEditingBoundary) |
| 1807 break; |
| 1808 boundaryCrossed = true; |
| 1809 } |
| 1810 |
| 1811 lastNode = currentNode; |
| 1812 } |
| 1813 |
| 1814 // stop before going above the body, up into the head |
| 1815 // return the last visible streamer position |
| 1816 if (isHTMLBodyElement(*currentNode) && currentPos.atEndOfNode()) |
| 1817 break; |
| 1818 |
| 1819 // Do not move to a visually distinct position. |
| 1820 if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode !
= boundary) |
| 1821 return lastVisible.deprecatedComputePosition(); |
| 1822 // Do not move past a visually disinct position. |
| 1823 // Note: The first position after the last in a node whose ends are visu
ally distinct |
| 1824 // positions will be [boundary->parentNode(), originalBlock->nodeIndex()
+ 1]. |
| 1825 if (boundary && Strategy::parent(*boundary) == currentNode) |
| 1826 return lastVisible.deprecatedComputePosition(); |
| 1827 |
| 1828 // skip position in non-laid out or invisible node |
| 1829 LayoutObject* layoutObject = currentNode->layoutObject(); |
| 1830 if (!layoutObject || layoutObject->style()->visibility() != VISIBLE) |
| 1831 continue; |
| 1832 |
| 1833 if (rule == CanCrossEditingBoundary && boundaryCrossed) { |
| 1834 lastVisible = currentPos; |
| 1835 break; |
| 1836 } |
| 1837 |
| 1838 // track last visible streamer position |
| 1839 if (isStreamer<Strategy>(currentPos)) |
| 1840 lastVisible = currentPos; |
| 1841 |
| 1842 // Return position before tables and nodes which have content that can b
e ignored. |
| 1843 if (Strategy::editingIgnoresContent(currentNode) || isRenderedHTMLTableE
lement(currentNode)) { |
| 1844 if (currentPos.offsetInLeafNode() <= layoutObject->caretMinOffset()) |
| 1845 return PositionAlgorithm<Strategy>::editingPositionOf(currentNod
e, layoutObject->caretMinOffset()); |
| 1846 continue; |
| 1847 } |
| 1848 |
| 1849 // return current position if it is in laid out text |
| 1850 if (layoutObject->isText() && toLayoutText(layoutObject)->firstTextBox()
) { |
| 1851 if (currentNode != startNode) { |
| 1852 ASSERT(currentPos.atStartOfNode()); |
| 1853 return PositionAlgorithm<Strategy>(currentNode, layoutObject->ca
retMinOffset()); |
| 1854 } |
| 1855 |
| 1856 unsigned textOffset = currentPos.offsetInLeafNode(); |
| 1857 LayoutText* textLayoutObject = toLayoutText(layoutObject); |
| 1858 InlineTextBox* lastTextBox = textLayoutObject->lastTextBox(); |
| 1859 for (InlineTextBox* box = textLayoutObject->firstTextBox(); box; box
= box->nextTextBox()) { |
| 1860 if (textOffset <= box->end()) { |
| 1861 if (textOffset >= box->start()) |
| 1862 return currentPos.computePosition(); |
| 1863 continue; |
| 1864 } |
| 1865 |
| 1866 if (box == lastTextBox || textOffset != box->start() + box->len(
)) |
| 1867 continue; |
| 1868 |
| 1869 // The text continues on the next line only if the last text box
is not on this line and |
| 1870 // none of the boxes on this line have a larger start offset. |
| 1871 |
| 1872 bool continuesOnNextLine = true; |
| 1873 InlineBox* otherBox = box; |
| 1874 while (continuesOnNextLine) { |
| 1875 otherBox = otherBox->nextLeafChild(); |
| 1876 if (!otherBox) |
| 1877 break; |
| 1878 if (otherBox == lastTextBox || (otherBox->layoutObject() ==
textLayoutObject && toInlineTextBox(otherBox)->start() >= textOffset)) |
| 1879 continuesOnNextLine = false; |
| 1880 } |
| 1881 |
| 1882 otherBox = box; |
| 1883 while (continuesOnNextLine) { |
| 1884 otherBox = otherBox->prevLeafChild(); |
| 1885 if (!otherBox) |
| 1886 break; |
| 1887 if (otherBox == lastTextBox || (otherBox->layoutObject() ==
textLayoutObject && toInlineTextBox(otherBox)->start() >= textOffset)) |
| 1888 continuesOnNextLine = false; |
| 1889 } |
| 1890 |
| 1891 if (continuesOnNextLine) |
| 1892 return currentPos.computePosition(); |
| 1893 } |
| 1894 } |
| 1895 } |
| 1896 |
| 1897 return lastVisible.deprecatedComputePosition(); |
| 1898 } |
| 1899 |
| 1900 Position mostBackwardCaretPosition(const Position& position, EditingBoundaryCros
singRule rule) |
| 1901 { |
| 1902 return mostBackwardCaretPosition<EditingStrategy>(position, rule); |
| 1903 } |
| 1904 |
| 1905 PositionInComposedTree mostBackwardCaretPosition(const PositionInComposedTree& p
osition, EditingBoundaryCrossingRule rule) |
| 1906 { |
| 1907 return mostBackwardCaretPosition<EditingInComposedTreeStrategy>(position, ru
le); |
| 1908 } |
| 1909 |
| 1910 } |
OLD | NEW |