2 * Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "TextAffinity.h"
30 #include "TextDirection.h"
31 #include "Node.h" // for position creation functions
32 #include <wtf/Assertions.h>
33 #include <wtf/PassRefPtr.h>
34 #include <wtf/RefPtr.h>
38 class CSSComputedStyleDeclaration
;
45 enum PositionMoveType
{
46 CodePoint
, // Move by a single code point.
47 Character
, // Move to the next Unicode character break.
48 BackwardDeletion
// Subject to platform conventions.
54 PositionIsOffsetInAnchor
,
55 PositionIsAfterAnchor
,
56 PositionIsBeforeAnchor
59 enum EditingBoundaryCrossingRule
{
60 CanCrossEditingBoundary
,
61 CannotCrossEditingBoundary
66 , m_anchorType(PositionIsOffsetInAnchor
)
67 , m_isLegacyEditingPosition(false)
71 // For creating legacy editing positions: (Anchor type will be determined from editingIgnoresContent(node))
72 Position(PassRefPtr
<Node
> anchorNode
, int offset
);
74 // For creating before/after positions:
75 Position(PassRefPtr
<Node
> anchorNode
, AnchorType
);
76 // For creating offset positions:
77 Position(PassRefPtr
<Node
> anchorNode
, int offset
, AnchorType
);
79 AnchorType
anchorType() const { return static_cast<AnchorType
>(m_anchorType
); }
81 void clear() { m_anchorNode
.clear(); m_offset
= 0; m_anchorType
= PositionIsOffsetInAnchor
; m_isLegacyEditingPosition
= false; }
83 // These are always DOM compliant values. Editing positions like [img, 0] (aka [img, before])
84 // will return img->parentNode() and img->nodeIndex() from these functions.
85 Node
* containerNode() const; // NULL for a before/after position anchored to a node with no parent
86 int computeOffsetInContainerNode() const; // O(n) for before/after-anchored positions, O(1) for parent-anchored positions
88 // Inline O(1) access for Positions which callers know to be parent-anchored
89 int offsetInContainerNode() const
91 ASSERT(anchorType() == PositionIsOffsetInAnchor
);
95 // New code should not use this function.
96 int deprecatedEditingOffset() const
98 // This should probably ASSERT(m_isLegacyEditingPosition);
102 // These are convenience methods which are smart about whether the position is neighbor anchored or parent anchored
103 Node
* computeNodeBeforePosition() const;
104 Node
* computeNodeAfterPosition() const;
106 Node
* anchorNode() const { return m_anchorNode
.get(); }
108 // FIXME: Callers should be moved off of node(), node() is not always the container for this position.
109 // For nodes which editingIgnoresContent(node()) returns true, positions like [ignoredNode, 0]
110 // will be treated as before ignoredNode (thus node() is really after the position, not containing it).
111 Node
* node() const { return m_anchorNode
.get(); }
113 // These should only be used for PositionIsOffsetInAnchor positions, unless
114 // the position is a legacy editing position.
115 void moveToPosition(PassRefPtr
<Node
> anchorNode
, int offset
);
116 void moveToOffset(int offset
);
118 bool isNull() const { return !m_anchorNode
; }
119 bool isNotNull() const { return m_anchorNode
; }
121 Element
* element() const;
122 PassRefPtr
<CSSComputedStyleDeclaration
> computedStyle() const;
124 // Move up or down the DOM by one position.
125 // Offsets are computed using render text for nodes that have renderers - but note that even when
126 // using composed characters, the result may be inside a single user-visible character if a ligature is formed.
127 Position
previous(PositionMoveType
= CodePoint
) const;
128 Position
next(PositionMoveType
= CodePoint
) const;
129 static int uncheckedPreviousOffset(const Node
*, int current
);
130 static int uncheckedPreviousOffsetForBackwardDeletion(const Node
*, int current
);
131 static int uncheckedNextOffset(const Node
*, int current
);
133 // These can be either inside or just before/after the node, depending on
134 // if the node is ignored by editing or not.
135 bool atFirstEditingPositionForNode() const;
136 bool atLastEditingPositionForNode() const;
138 // Retuns true if the visually equivalent positions around have different editability
139 bool atEditingBoundary() const;
141 bool atStartOfTree() const;
142 bool atEndOfTree() const;
144 // FIXME: Make these non-member functions and put them somewhere in the editing directory.
145 // These aren't really basic "position" operations. More high level editing helper functions.
146 Position
leadingWhitespacePosition(EAffinity
, bool considerNonCollapsibleWhitespace
= false) const;
147 Position
trailingWhitespacePosition(EAffinity
, bool considerNonCollapsibleWhitespace
= false) const;
149 // These return useful visually equivalent positions.
150 Position
upstream(EditingBoundaryCrossingRule
= CannotCrossEditingBoundary
) const;
151 Position
downstream(EditingBoundaryCrossingRule
= CannotCrossEditingBoundary
) const;
153 bool isCandidate() const;
154 bool inRenderedText() const;
155 bool isRenderedCharacter() const;
156 bool rendersInDifferentPosition(const Position
&) const;
158 void getInlineBoxAndOffset(EAffinity
, InlineBox
*&, int& caretOffset
) const;
159 void getInlineBoxAndOffset(EAffinity
, TextDirection primaryDirection
, InlineBox
*&, int& caretOffset
) const;
161 static bool hasRenderedNonAnonymousDescendantsWithHeight(RenderObject
*);
162 static bool nodeIsUserSelectNone(Node
*);
164 void debugPosition(const char* msg
= "") const;
167 void formatForDebugger(char* buffer
, unsigned length
) const;
168 void showTreeForThis() const;
172 int renderedOffset() const;
174 Position
previousCharacterPosition(EAffinity
) const;
175 Position
nextCharacterPosition(EAffinity
) const;
177 static AnchorType
anchorTypeForLegacyEditingPosition(Node
* anchorNode
, int offset
);
179 RefPtr
<Node
> m_anchorNode
;
180 // m_offset can be the offset inside m_anchorNode, or if editingIgnoresContent(m_anchorNode)
181 // returns true, then other places in editing will treat m_offset == 0 as "before the anchor"
182 // and m_offset > 0 as "after the anchor node". See rangeCompliantEquivalent for more info.
184 unsigned m_anchorType
: 2;
185 bool m_isLegacyEditingPosition
: 1;
188 inline bool operator==(const Position
& a
, const Position
& b
)
190 // FIXME: In <div><img></div> [div, 0] != [img, 0] even though most of the
191 // editing code will treat them as identical.
192 return a
.anchorNode() == b
.anchorNode() && a
.deprecatedEditingOffset() == b
.deprecatedEditingOffset();
195 inline bool operator!=(const Position
& a
, const Position
& b
)
200 // We define position creation functions to make callsites more readable.
201 // These are inline to prevent ref-churn when returning a Position object.
202 // If we ever add a PassPosition we can make these non-inline.
204 inline Position
positionInParentBeforeNode(const Node
* node
)
206 // FIXME: This should ASSERT(node->parentNode())
207 // At least one caller currently hits this ASSERT though, which indicates
208 // that the caller is trying to make a position relative to a disconnected node (which is likely an error)
209 // Specifically, editing/deleting/delete-ligature-001.html crashes with ASSERT(node->parentNode())
210 return Position(node
->parentNode(), node
->nodeIndex(), Position::PositionIsOffsetInAnchor
);
213 inline Position
positionInParentAfterNode(const Node
* node
)
215 ASSERT(node
->parentNode());
216 return Position(node
->parentNode(), node
->nodeIndex() + 1, Position::PositionIsOffsetInAnchor
);
219 // positionBeforeNode and positionAfterNode return neighbor-anchored positions, construction is O(1)
220 inline Position
positionBeforeNode(Node
* anchorNode
)
223 return Position(anchorNode
, Position::PositionIsBeforeAnchor
);
226 inline Position
positionAfterNode(Node
* anchorNode
)
229 return Position(anchorNode
, Position::PositionIsAfterAnchor
);
232 inline int lastOffsetInNode(Node
* node
)
234 return node
->offsetInCharacters() ? node
->maxCharacterOffset() : node
->childNodeCount();
237 // firstPositionInNode and lastPositionInNode return parent-anchored positions, lastPositionInNode construction is O(n) due to childNodeCount()
238 inline Position
firstPositionInNode(Node
* anchorNode
)
240 return Position(anchorNode
, 0, Position::PositionIsOffsetInAnchor
);
243 inline Position
lastPositionInNode(Node
* anchorNode
)
245 return Position(anchorNode
, lastOffsetInNode(anchorNode
), Position::PositionIsOffsetInAnchor
);
248 } // namespace WebCore
251 // Outside the WebCore namespace for ease of invocation from gdb.
252 void showTree(const WebCore::Position
&);
253 void showTree(const WebCore::Position
*);