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 |