| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) | 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) |
| 4 * (C) 2007 David Smith (catfish.man@gmail.com) | 4 * (C) 2007 David Smith (catfish.man@gmail.com) |
| 5 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc.
All rights reserved. | 5 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc.
All rights reserved. |
| 6 * Copyright (C) Research In Motion Limited 2010. All rights reserved. | 6 * Copyright (C) Research In Motion Limited 2010. All rights reserved. |
| 7 * | 7 * |
| 8 * This library is free software; you can redistribute it and/or | 8 * This library is free software; you can redistribute it and/or |
| 9 * modify it under the terms of the GNU Library General Public | 9 * modify it under the terms of the GNU Library General Public |
| 10 * License as published by the Free Software Foundation; either | 10 * License as published by the Free Software Foundation; either |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 #include "core/layout/LayoutBlock.h" | 24 #include "core/layout/LayoutBlock.h" |
| 25 | 25 |
| 26 #include "core/HTMLNames.h" | 26 #include "core/HTMLNames.h" |
| 27 #include "core/dom/AXObjectCache.h" | 27 #include "core/dom/AXObjectCache.h" |
| 28 #include "core/dom/Document.h" | 28 #include "core/dom/Document.h" |
| 29 #include "core/dom/Element.h" | 29 #include "core/dom/Element.h" |
| 30 #include "core/dom/StyleEngine.h" | 30 #include "core/dom/StyleEngine.h" |
| 31 #include "core/dom/shadow/ShadowRoot.h" | 31 #include "core/dom/shadow/ShadowRoot.h" |
| 32 #include "core/editing/DragCaretController.h" | 32 #include "core/editing/DragCaretController.h" |
| 33 #include "core/editing/EditingUtilities.h" | 33 #include "core/editing/EditingUtilities.h" |
| 34 #include "core/editing/Editor.h" | |
| 35 #include "core/editing/FrameSelection.h" | 34 #include "core/editing/FrameSelection.h" |
| 36 #include "core/frame/FrameView.h" | 35 #include "core/frame/FrameView.h" |
| 37 #include "core/frame/LocalFrame.h" | 36 #include "core/frame/LocalFrame.h" |
| 38 #include "core/frame/Settings.h" | 37 #include "core/frame/Settings.h" |
| 39 #include "core/html/HTMLMarqueeElement.h" | 38 #include "core/html/HTMLMarqueeElement.h" |
| 40 #include "core/layout/HitTestLocation.h" | 39 #include "core/layout/HitTestLocation.h" |
| 41 #include "core/layout/HitTestResult.h" | 40 #include "core/layout/HitTestResult.h" |
| 42 #include "core/layout/LayoutAnalyzer.h" | 41 #include "core/layout/LayoutAnalyzer.h" |
| 43 #include "core/layout/LayoutFlexibleBox.h" | 42 #include "core/layout/LayoutFlexibleBox.h" |
| 44 #include "core/layout/LayoutFlowThread.h" | 43 #include "core/layout/LayoutFlowThread.h" |
| (...skipping 1565 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1610 } | 1609 } |
| 1611 | 1610 |
| 1612 static inline bool isEditingBoundary(LayoutObject* ancestor, LineLayoutBox child
) | 1611 static inline bool isEditingBoundary(LayoutObject* ancestor, LineLayoutBox child
) |
| 1613 { | 1612 { |
| 1614 ASSERT(!ancestor || ancestor->nonPseudoNode()); | 1613 ASSERT(!ancestor || ancestor->nonPseudoNode()); |
| 1615 ASSERT(child && child.nonPseudoNode()); | 1614 ASSERT(child && child.nonPseudoNode()); |
| 1616 return !ancestor || !ancestor->parent() || (ancestor->hasLayer() && ancestor
->parent()->isLayoutView()) | 1615 return !ancestor || !ancestor->parent() || (ancestor->hasLayer() && ancestor
->parent()->isLayoutView()) |
| 1617 || ancestor->nonPseudoNode()->hasEditableStyle() == child.nonPseudoNode(
)->hasEditableStyle(); | 1616 || ancestor->nonPseudoNode()->hasEditableStyle() == child.nonPseudoNode(
)->hasEditableStyle(); |
| 1618 } | 1617 } |
| 1619 | 1618 |
| 1620 // FIXME: This function should go on LayoutObject as an instance method. Then | 1619 // FIXME: This function should go on LayoutObject. |
| 1621 // all cases in which positionForPoint recurs could call this instead to | 1620 // Then all cases in which positionForPoint recurs could call this instead to |
| 1622 // prevent crossing editable boundaries. This would require many tests. | 1621 // prevent crossing editable boundaries. This would require many tests. |
| 1623 static PositionWithAffinity positionForPointRespectingEditingBoundaries(LayoutBl
ock* parent, LineLayoutBox child, const LayoutPoint& pointInParentCoordinates) | 1622 PositionWithAffinity LayoutBlock::positionForPointRespectingEditingBoundaries(Li
neLayoutBox child, const LayoutPoint& pointInParentCoordinates) |
| 1624 { | 1623 { |
| 1625 LayoutPoint childLocation = child.location(); | 1624 LayoutPoint childLocation = child.location(); |
| 1626 if (child.isInFlowPositioned()) | 1625 if (child.isInFlowPositioned()) |
| 1627 childLocation += child.offsetForInFlowPosition(); | 1626 childLocation += child.offsetForInFlowPosition(); |
| 1628 | 1627 |
| 1629 // FIXME: This is wrong if the child's writing-mode is different from the pa
rent's. | 1628 // FIXME: This is wrong if the child's writing-mode is different from the pa
rent's. |
| 1630 LayoutPoint pointInChildCoordinates(toLayoutPoint(pointInParentCoordinates -
childLocation)); | 1629 LayoutPoint pointInChildCoordinates(toLayoutPoint(pointInParentCoordinates -
childLocation)); |
| 1631 | 1630 |
| 1632 // If this is an anonymous layoutObject, we just recur normally | 1631 // If this is an anonymous layoutObject, we just recur normally |
| 1633 Node* childNode = child.nonPseudoNode(); | 1632 Node* childNode = child.nonPseudoNode(); |
| 1634 if (!childNode) | 1633 if (!childNode) |
| 1635 return child.positionForPoint(pointInChildCoordinates); | 1634 return child.positionForPoint(pointInChildCoordinates); |
| 1636 | 1635 |
| 1637 // Otherwise, first make sure that the editability of the parent and child a
gree. | 1636 // Otherwise, first make sure that the editability of the parent and child a
gree. |
| 1638 // If they don't agree, then we return a visible position just before or aft
er the child | 1637 // If they don't agree, then we return a visible position just before or aft
er the child |
| 1639 LayoutObject* ancestor = parent; | 1638 LayoutObject* ancestor = this; |
| 1640 while (ancestor && !ancestor->nonPseudoNode()) | 1639 while (ancestor && !ancestor->nonPseudoNode()) |
| 1641 ancestor = ancestor->parent(); | 1640 ancestor = ancestor->parent(); |
| 1642 | 1641 |
| 1643 // If we can't find an ancestor to check editability on, or editability is u
nchanged, we recur like normal | 1642 // If we can't find an ancestor to check editability on, or editability is u
nchanged, we recur like normal |
| 1644 if (isEditingBoundary(ancestor, child)) | 1643 if (isEditingBoundary(ancestor, child)) |
| 1645 return child.positionForPoint(pointInChildCoordinates); | 1644 return child.positionForPoint(pointInChildCoordinates); |
| 1646 | 1645 |
| 1647 // Otherwise return before or after the child, depending on if the click was
to the logical left or logical right of the child | 1646 // Otherwise return before or after the child, depending on if the click was
to the logical left or logical right of the child |
| 1648 LayoutUnit childMiddle = parent->logicalWidthForChildSize(child.size()) / 2; | 1647 LayoutUnit childMiddle = logicalWidthForChildSize(child.size()) / 2; |
| 1649 LayoutUnit logicalLeft = parent->isHorizontalWritingMode() ? pointInChildCoo
rdinates.x() : pointInChildCoordinates.y(); | 1648 LayoutUnit logicalLeft = isHorizontalWritingMode() ? pointInChildCoordinates
.x() : pointInChildCoordinates.y(); |
| 1650 if (logicalLeft < childMiddle) | 1649 if (logicalLeft < childMiddle) |
| 1651 return ancestor->createPositionWithAffinity(childNode->nodeIndex()); | 1650 return ancestor->createPositionWithAffinity(childNode->nodeIndex()); |
| 1652 return ancestor->createPositionWithAffinity(childNode->nodeIndex() + 1, Text
Affinity::Upstream); | 1651 return ancestor->createPositionWithAffinity(childNode->nodeIndex() + 1, Text
Affinity::Upstream); |
| 1653 } | 1652 } |
| 1654 | 1653 |
| 1655 PositionWithAffinity LayoutBlock::positionForPointWithInlineChildren(const Layou
tPoint& pointInLogicalContents) | 1654 PositionWithAffinity LayoutBlock::positionForPointIfOutsideAtomicInlineLevel(con
st LayoutPoint& point) |
| 1656 { | 1655 { |
| 1657 ASSERT(childrenInline()); | 1656 ASSERT(isAtomicInlineLevel()); |
| 1657 // FIXME: This seems wrong when the object's writing-mode doesn't match the
line's writing-mode. |
| 1658 LayoutUnit pointLogicalLeft = isHorizontalWritingMode() ? point.x() : point.
y(); |
| 1659 LayoutUnit pointLogicalTop = isHorizontalWritingMode() ? point.y() : point.x
(); |
| 1658 | 1660 |
| 1659 if (!firstRootBox()) | 1661 if (pointLogicalLeft < 0) |
| 1660 return createPositionWithAffinity(0); | 1662 return createPositionWithAffinity(caretMinOffset()); |
| 1661 | 1663 if (pointLogicalLeft >= logicalWidth()) |
| 1662 bool linesAreFlipped = style()->isFlippedLinesWritingMode(); | 1664 return createPositionWithAffinity(caretMaxOffset()); |
| 1663 bool blocksAreFlipped = style()->isFlippedBlocksWritingMode(); | 1665 if (pointLogicalTop < 0) |
| 1664 | 1666 return createPositionWithAffinity(caretMinOffset()); |
| 1665 // look for the closest line box in the root box which is at the passed-in y
coordinate | 1667 if (pointLogicalTop >= logicalHeight()) |
| 1666 InlineBox* closestBox = nullptr; | 1668 return createPositionWithAffinity(caretMaxOffset()); |
| 1667 RootInlineBox* firstRootBoxWithChildren = nullptr; | 1669 return PositionWithAffinity(); |
| 1668 RootInlineBox* lastRootBoxWithChildren = nullptr; | |
| 1669 for (RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox())
{ | |
| 1670 if (!root->firstLeafChild()) | |
| 1671 continue; | |
| 1672 if (!firstRootBoxWithChildren) | |
| 1673 firstRootBoxWithChildren = root; | |
| 1674 | |
| 1675 if (!linesAreFlipped && root->isFirstAfterPageBreak() && (pointInLogical
Contents.y() < root->lineTopWithLeading() | |
| 1676 || (blocksAreFlipped && pointInLogicalContents.y() == root->lineTopW
ithLeading()))) | |
| 1677 break; | |
| 1678 | |
| 1679 lastRootBoxWithChildren = root; | |
| 1680 | |
| 1681 // check if this root line box is located at this y coordinate | |
| 1682 if (pointInLogicalContents.y() < root->selectionBottom() || (blocksAreFl
ipped && pointInLogicalContents.y() == root->selectionBottom())) { | |
| 1683 if (linesAreFlipped) { | |
| 1684 RootInlineBox* nextRootBoxWithChildren = root->nextRootBox(); | |
| 1685 while (nextRootBoxWithChildren && !nextRootBoxWithChildren->firs
tLeafChild()) | |
| 1686 nextRootBoxWithChildren = nextRootBoxWithChildren->nextRootB
ox(); | |
| 1687 | |
| 1688 if (nextRootBoxWithChildren && nextRootBoxWithChildren->isFirstA
fterPageBreak() && (pointInLogicalContents.y() > nextRootBoxWithChildren->lineTo
pWithLeading() | |
| 1689 || (!blocksAreFlipped && pointInLogicalContents.y() == nextR
ootBoxWithChildren->lineTopWithLeading()))) | |
| 1690 continue; | |
| 1691 } | |
| 1692 closestBox = root->closestLeafChildForLogicalLeftPosition(pointInLog
icalContents.x()); | |
| 1693 if (closestBox) | |
| 1694 break; | |
| 1695 } | |
| 1696 } | |
| 1697 | |
| 1698 bool moveCaretToBoundary = document().frame()->editor().behavior().shouldMov
eCaretToHorizontalBoundaryWhenPastTopOrBottom(); | |
| 1699 | |
| 1700 if (!moveCaretToBoundary && !closestBox && lastRootBoxWithChildren) { | |
| 1701 // y coordinate is below last root line box, pretend we hit it | |
| 1702 closestBox = lastRootBoxWithChildren->closestLeafChildForLogicalLeftPosi
tion(pointInLogicalContents.x()); | |
| 1703 } | |
| 1704 | |
| 1705 if (closestBox) { | |
| 1706 if (moveCaretToBoundary) { | |
| 1707 LayoutUnit firstRootBoxWithChildrenTop = std::min<LayoutUnit>(firstR
ootBoxWithChildren->selectionTop(), firstRootBoxWithChildren->logicalTop()); | |
| 1708 if (pointInLogicalContents.y() < firstRootBoxWithChildrenTop | |
| 1709 || (blocksAreFlipped && pointInLogicalContents.y() == firstRootB
oxWithChildrenTop)) { | |
| 1710 InlineBox* box = firstRootBoxWithChildren->firstLeafChild(); | |
| 1711 if (box->isLineBreak()) { | |
| 1712 if (InlineBox* newBox = box->nextLeafChildIgnoringLineBreak(
)) | |
| 1713 box = newBox; | |
| 1714 } | |
| 1715 // y coordinate is above first root line box, so return the star
t of the first | |
| 1716 return PositionWithAffinity(positionForBox(box, true)); | |
| 1717 } | |
| 1718 } | |
| 1719 | |
| 1720 // pass the box a top position that is inside it | |
| 1721 LayoutPoint point(pointInLogicalContents.x(), closestBox->root().blockDi
rectionPointInLine()); | |
| 1722 if (!isHorizontalWritingMode()) | |
| 1723 point = point.transposedPoint(); | |
| 1724 if (closestBox->getLineLayoutItem().isAtomicInlineLevel()) | |
| 1725 return positionForPointRespectingEditingBoundaries(this, LineLayoutB
ox(closestBox->getLineLayoutItem()), point); | |
| 1726 return closestBox->getLineLayoutItem().positionForPoint(point); | |
| 1727 } | |
| 1728 | |
| 1729 if (lastRootBoxWithChildren) { | |
| 1730 // We hit this case for Mac behavior when the Y coordinate is below the
last box. | |
| 1731 ASSERT(moveCaretToBoundary); | |
| 1732 InlineBox* logicallyLastBox; | |
| 1733 if (lastRootBoxWithChildren->getLogicalEndBoxWithNode(logicallyLastBox)) | |
| 1734 return PositionWithAffinity(positionForBox(logicallyLastBox, false))
; | |
| 1735 } | |
| 1736 | |
| 1737 // Can't reach this. We have a root line box, but it has no kids. | |
| 1738 // FIXME: This should ASSERT_NOT_REACHED(), but clicking on placeholder text | |
| 1739 // seems to hit this code path. | |
| 1740 return createPositionWithAffinity(0); | |
| 1741 } | 1670 } |
| 1742 | 1671 |
| 1743 static inline bool isChildHitTestCandidate(LayoutBox* box) | 1672 static inline bool isChildHitTestCandidate(LayoutBox* box) |
| 1744 { | 1673 { |
| 1745 return box->size().height() && box->style()->visibility() == VISIBLE && !box
->isFloatingOrOutOfFlowPositioned() && !box->isLayoutFlowThread(); | 1674 return box->size().height() && box->style()->visibility() == VISIBLE && !box
->isFloatingOrOutOfFlowPositioned() && !box->isLayoutFlowThread(); |
| 1746 } | 1675 } |
| 1747 | 1676 |
| 1748 PositionWithAffinity LayoutBlock::positionForPoint(const LayoutPoint& point) | 1677 PositionWithAffinity LayoutBlock::positionForPoint(const LayoutPoint& point) |
| 1749 { | 1678 { |
| 1750 if (isTable()) | 1679 if (isTable()) |
| 1751 return LayoutBox::positionForPoint(point); | 1680 return LayoutBox::positionForPoint(point); |
| 1752 | 1681 |
| 1753 if (isAtomicInlineLevel()) { | 1682 if (isAtomicInlineLevel()) { |
| 1754 // FIXME: This seems wrong when the object's writing-mode doesn't match
the line's writing-mode. | 1683 PositionWithAffinity position = positionForPointIfOutsideAtomicInlineLev
el(point); |
| 1755 LayoutUnit pointLogicalLeft = isHorizontalWritingMode() ? point.x() : po
int.y(); | 1684 if (!position.isNull()) |
| 1756 LayoutUnit pointLogicalTop = isHorizontalWritingMode() ? point.y() : poi
nt.x(); | 1685 return position; |
| 1757 | |
| 1758 if (pointLogicalLeft < 0) | |
| 1759 return createPositionWithAffinity(caretMinOffset()); | |
| 1760 if (pointLogicalLeft >= logicalWidth()) | |
| 1761 return createPositionWithAffinity(caretMaxOffset()); | |
| 1762 if (pointLogicalTop < 0) | |
| 1763 return createPositionWithAffinity(caretMinOffset()); | |
| 1764 if (pointLogicalTop >= logicalHeight()) | |
| 1765 return createPositionWithAffinity(caretMaxOffset()); | |
| 1766 } | 1686 } |
| 1767 | 1687 |
| 1768 LayoutPoint pointInContents = point; | 1688 LayoutPoint pointInContents = point; |
| 1769 offsetForContents(pointInContents); | 1689 offsetForContents(pointInContents); |
| 1770 LayoutPoint pointInLogicalContents(pointInContents); | 1690 LayoutPoint pointInLogicalContents(pointInContents); |
| 1771 if (!isHorizontalWritingMode()) | 1691 if (!isHorizontalWritingMode()) |
| 1772 pointInLogicalContents = pointInLogicalContents.transposedPoint(); | 1692 pointInLogicalContents = pointInLogicalContents.transposedPoint(); |
| 1773 | 1693 |
| 1774 if (childrenInline()) | 1694 ASSERT(!childrenInline()); |
| 1775 return positionForPointWithInlineChildren(pointInLogicalContents); | |
| 1776 | 1695 |
| 1777 LayoutBox* lastCandidateBox = lastChildBox(); | 1696 LayoutBox* lastCandidateBox = lastChildBox(); |
| 1778 while (lastCandidateBox && !isChildHitTestCandidate(lastCandidateBox)) | 1697 while (lastCandidateBox && !isChildHitTestCandidate(lastCandidateBox)) |
| 1779 lastCandidateBox = lastCandidateBox->previousSiblingBox(); | 1698 lastCandidateBox = lastCandidateBox->previousSiblingBox(); |
| 1780 | 1699 |
| 1781 bool blocksAreFlipped = style()->isFlippedBlocksWritingMode(); | 1700 bool blocksAreFlipped = style()->isFlippedBlocksWritingMode(); |
| 1782 if (lastCandidateBox) { | 1701 if (lastCandidateBox) { |
| 1783 if (pointInLogicalContents.y() > logicalTopForChild(*lastCandidateBox) | 1702 if (pointInLogicalContents.y() > logicalTopForChild(*lastCandidateBox) |
| 1784 || (!blocksAreFlipped && pointInLogicalContents.y() == logicalTopFor
Child(*lastCandidateBox))) | 1703 || (!blocksAreFlipped && pointInLogicalContents.y() == logicalTopFor
Child(*lastCandidateBox))) |
| 1785 return positionForPointRespectingEditingBoundaries(this, LineLayoutB
ox(lastCandidateBox), pointInContents); | 1704 return positionForPointRespectingEditingBoundaries(LineLayoutBox(las
tCandidateBox), pointInContents); |
| 1786 | 1705 |
| 1787 for (LayoutBox* childBox = firstChildBox(); childBox; childBox = childBo
x->nextSiblingBox()) { | 1706 for (LayoutBox* childBox = firstChildBox(); childBox; childBox = childBo
x->nextSiblingBox()) { |
| 1788 if (!isChildHitTestCandidate(childBox)) | 1707 if (!isChildHitTestCandidate(childBox)) |
| 1789 continue; | 1708 continue; |
| 1790 LayoutUnit childLogicalBottom = logicalTopForChild(*childBox) + logi
calHeightForChild(*childBox); | 1709 LayoutUnit childLogicalBottom = logicalTopForChild(*childBox) + logi
calHeightForChild(*childBox); |
| 1791 // We hit child if our click is above the bottom of its padding box
(like IE6/7 and FF3). | 1710 // We hit child if our click is above the bottom of its padding box
(like IE6/7 and FF3). |
| 1792 if (isChildHitTestCandidate(childBox) && (pointInLogicalContents.y()
< childLogicalBottom | 1711 if (isChildHitTestCandidate(childBox) && (pointInLogicalContents.y()
< childLogicalBottom |
| 1793 || (blocksAreFlipped && pointInLogicalContents.y() == childLogic
alBottom))) | 1712 || (blocksAreFlipped && pointInLogicalContents.y() == childLogic
alBottom))) |
| 1794 return positionForPointRespectingEditingBoundaries(this, LineLay
outBox(childBox), pointInContents); | 1713 return positionForPointRespectingEditingBoundaries(LineLayoutBox
(childBox), pointInContents); |
| 1795 } | 1714 } |
| 1796 } | 1715 } |
| 1797 | 1716 |
| 1798 // We only get here if there are no hit test candidate children below the cl
ick. | 1717 // We only get here if there are no hit test candidate children below the cl
ick. |
| 1799 return LayoutBox::positionForPoint(point); | 1718 return LayoutBox::positionForPoint(point); |
| 1800 } | 1719 } |
| 1801 | 1720 |
| 1802 void LayoutBlock::offsetForContents(LayoutPoint& offset) const | 1721 void LayoutBlock::offsetForContents(LayoutPoint& offset) const |
| 1803 { | 1722 { |
| 1804 offset = flipForWritingMode(offset); | 1723 offset = flipForWritingMode(offset); |
| (...skipping 794 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2599 for (TrackedLayoutBoxListHashSet::const_iterator it = positionedDescenda
ntSet->begin(); it != end; ++it) { | 2518 for (TrackedLayoutBoxListHashSet::const_iterator it = positionedDescenda
ntSet->begin(); it != end; ++it) { |
| 2600 LayoutBox* currBox = *it; | 2519 LayoutBox* currBox = *it; |
| 2601 ASSERT(!currBox->needsLayout()); | 2520 ASSERT(!currBox->needsLayout()); |
| 2602 } | 2521 } |
| 2603 } | 2522 } |
| 2604 } | 2523 } |
| 2605 | 2524 |
| 2606 #endif | 2525 #endif |
| 2607 | 2526 |
| 2608 } // namespace blink | 2527 } // namespace blink |
| OLD | NEW |