2 * Copyright (C) 2004 Apple Computer, 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.
27 #include "VisiblePosition.h"
31 #include "HTMLNames.h"
32 #include "InlineTextBox.h"
36 #include "htmlediting.h"
37 #include "visible_units.h"
41 using namespace HTMLNames;
43 VisiblePosition::VisiblePosition(const Position &pos, EAffinity affinity)
48 VisiblePosition::VisiblePosition(Node *node, int offset, EAffinity affinity)
51 init(Position(node, offset), affinity);
54 void VisiblePosition::init(const Position& position, EAffinity affinity)
56 m_affinity = affinity;
58 m_deepPosition = canonicalPosition(position);
60 // When not at a line wrap, make sure to end up with DOWNSTREAM affinity.
61 if (m_affinity == UPSTREAM && (isNull() || inSameLine(VisiblePosition(position, DOWNSTREAM), *this)))
62 m_affinity = DOWNSTREAM;
65 VisiblePosition VisiblePosition::next(bool dontChangeEditability) const
67 VisiblePosition next(nextVisuallyDistinctCandidate(m_deepPosition), m_affinity);
69 if (!dontChangeEditability)
72 return firstPositionWithSameEditabilityAtOrAfter(next);
75 VisiblePosition VisiblePosition::previous(bool dontChangeEditability) const
77 // find first previous DOM position that is visible
78 Position pos = previousVisuallyDistinctCandidate(m_deepPosition);
80 // return null visible position if there is no previous visible position
82 return VisiblePosition();
84 VisiblePosition prev = VisiblePosition(pos, DOWNSTREAM);
85 ASSERT(prev != *this);
88 // we should always be able to make the affinity DOWNSTREAM, because going previous from an
89 // UPSTREAM position can never yield another UPSTREAM position (unless line wrap length is 0!).
90 if (prev.isNotNull() && m_affinity == UPSTREAM) {
91 VisiblePosition temp = prev;
92 temp.setAffinity(UPSTREAM);
93 ASSERT(inSameLine(temp, prev));
97 if (!dontChangeEditability)
100 return lastPositionWithSameEditabilityAtOrBefore(prev);
103 VisiblePosition VisiblePosition::lastPositionWithSameEditabilityAtOrBefore(const VisiblePosition &pos) const
108 Node* highestRoot = highestEditableRoot(deepEquivalent());
110 if (highestRoot && !pos.deepEquivalent().node()->isDescendantOf(highestRoot))
111 return VisiblePosition();
113 // FIXME: In the non-editable case, just because the new position is non-editable doesn't mean movement
114 // to it is allowed. Selection::adjustForEditableContent has this problem too.
115 if (highestEditableRoot(pos.deepEquivalent()) == highestRoot)
118 // FIXME: Move to the previous non-editable region.
120 return VisiblePosition();
122 return lastEditablePositionBeforePositionInRoot(pos.deepEquivalent(), highestRoot);
125 VisiblePosition VisiblePosition::firstPositionWithSameEditabilityAtOrAfter(const VisiblePosition &pos) const
130 Node* highestRoot = highestEditableRoot(deepEquivalent());
132 if (highestRoot && !pos.deepEquivalent().node()->isDescendantOf(highestRoot))
133 return VisiblePosition();
135 // FIXME: In the non-editable case, just because the new position is non-editable doesn't mean movement
136 // to it is allowed. Selection::adjustForEditableContent has this problem too.
137 if (highestEditableRoot(pos.deepEquivalent()) == highestRoot)
140 // FIXME: Move to the previous non-editable region.
142 return VisiblePosition();
144 return firstEditablePositionAfterPositionInRoot(pos.deepEquivalent(), highestRoot);
147 Position canonicalizeCandidate(const Position& candidate)
149 if (candidate.isNull())
151 ASSERT(candidate.isCandidate());
152 Position upstream = candidate.upstream();
153 if (upstream.isCandidate())
158 Position VisiblePosition::canonicalPosition(const Position& position)
160 // FIXME (9535): Canonicalizing to the leftmost candidate means that if we're at a line wrap, we will
161 // ask renderers to paint downstream carets for other renderers.
162 // To fix this, we need to either a) add code to all paintCarets to pass the responsibility off to
163 // the appropriate renderer for VisiblePosition's like these, or b) canonicalize to the rightmost candidate
164 // unless the affinity is upstream.
165 Node* node = position.node();
169 node->document()->updateLayoutIgnorePendingStylesheets();
171 Position candidate = position.upstream();
172 if (candidate.isCandidate())
174 candidate = position.downstream();
175 if (candidate.isCandidate())
178 // When neither upstream or downstream gets us to a candidate (upstream/downstream won't leave
179 // blocks or enter new ones), we search forward and backward until we find one.
180 Position next = canonicalizeCandidate(nextCandidate(position));
181 Position prev = canonicalizeCandidate(previousCandidate(position));
182 Node* nextNode = next.node();
183 Node* prevNode = prev.node();
185 // The new position must be in the same editable element. Enforce that first.
186 // Unless the descent is from a non-editable html element to an editable body.
187 if (node->hasTagName(htmlTag) && !node->isContentEditable())
188 return next.isNotNull() ? next : prev;
190 Node* editingRoot = editableRootForPosition(position);
192 // If the html element is editable, descending into its body will look like a descent
193 // from non-editable to editable content since rootEditableElement() always stops at the body.
194 if (editingRoot && editingRoot->hasTagName(htmlTag) || position.node()->isDocumentNode())
195 return next.isNotNull() ? next : prev;
197 bool prevIsInSameEditableElement = prevNode && editableRootForPosition(prev) == editingRoot;
198 bool nextIsInSameEditableElement = nextNode && editableRootForPosition(next) == editingRoot;
199 if (prevIsInSameEditableElement && !nextIsInSameEditableElement)
202 if (nextIsInSameEditableElement && !prevIsInSameEditableElement)
205 if (!nextIsInSameEditableElement && !prevIsInSameEditableElement)
208 // The new position should be in the same block flow element. Favor that.
209 Node *originalBlock = node->enclosingBlockFlowElement();
210 bool nextIsOutsideOriginalBlock = !nextNode->isDescendantOf(originalBlock) && nextNode != originalBlock;
211 bool prevIsOutsideOriginalBlock = !prevNode->isDescendantOf(originalBlock) && prevNode != originalBlock;
212 if (nextIsOutsideOriginalBlock && !prevIsOutsideOriginalBlock)
218 UChar VisiblePosition::characterAfter() const
220 // We canonicalize to the first of two equivalent candidates, but the second of the two candidates
221 // is the one that will be inside the text node containing the character after this visible position.
222 Position pos = m_deepPosition.downstream();
223 Node* node = pos.node();
224 if (!node || !node->isTextNode())
226 Text* textNode = static_cast<Text*>(pos.node());
227 int offset = pos.offset();
228 if ((unsigned)offset >= textNode->length())
230 return textNode->data()[offset];
233 IntRect VisiblePosition::caretRect() const
235 if (!m_deepPosition.node() || !m_deepPosition.node()->renderer())
238 return m_deepPosition.node()->renderer()->caretRect(m_deepPosition.offset(), m_affinity);
241 void VisiblePosition::debugPosition(const char *msg) const
244 fprintf(stderr, "Position [%s]: null\n", msg);
246 fprintf(stderr, "Position [%s]: %s [%p] at %d\n", msg, m_deepPosition.node()->nodeName().deprecatedString().latin1(), m_deepPosition.node(), m_deepPosition.offset());
251 void VisiblePosition::formatForDebugger(char* buffer, unsigned length) const
253 m_deepPosition.formatForDebugger(buffer, length);
256 void VisiblePosition::showTreeForThis() const
258 m_deepPosition.showTreeForThis();
263 PassRefPtr<Range> makeRange(const VisiblePosition &start, const VisiblePosition &end)
265 Position s = rangeCompliantEquivalent(start);
266 Position e = rangeCompliantEquivalent(end);
267 return new Range(s.node()->document(), s.node(), s.offset(), e.node(), e.offset());
270 VisiblePosition startVisiblePosition(const Range *r, EAffinity affinity)
273 return VisiblePosition(r->startContainer(exception), r->startOffset(exception), affinity);
276 VisiblePosition endVisiblePosition(const Range *r, EAffinity affinity)
279 return VisiblePosition(r->endContainer(exception), r->endOffset(exception), affinity);
282 bool setStart(Range *r, const VisiblePosition &visiblePosition)
286 Position p = rangeCompliantEquivalent(visiblePosition);
288 r->setStart(p.node(), p.offset(), code);
292 bool setEnd(Range *r, const VisiblePosition &visiblePosition)
296 Position p = rangeCompliantEquivalent(visiblePosition);
298 r->setEnd(p.node(), p.offset(), code);
302 Node *enclosingBlockFlowElement(const VisiblePosition &visiblePosition)
304 if (visiblePosition.isNull())
307 return visiblePosition.deepEquivalent().node()->enclosingBlockFlowElement();
310 bool isFirstVisiblePositionInNode(const VisiblePosition &visiblePosition, const Node *node)
312 if (visiblePosition.isNull())
315 if (!visiblePosition.deepEquivalent().node()->isDescendantOf(node))
318 VisiblePosition previous = visiblePosition.previous();
319 return previous.isNull() || !previous.deepEquivalent().node()->isDescendantOf(node);
322 bool isLastVisiblePositionInNode(const VisiblePosition &visiblePosition, const Node *node)
324 if (visiblePosition.isNull())
327 if (!visiblePosition.deepEquivalent().node()->isDescendantOf(node))
330 VisiblePosition next = visiblePosition.next();
331 return next.isNull() || !next.deepEquivalent().node()->isDescendantOf(node);
334 } // namespace WebCore
338 void showTree(const WebCore::VisiblePosition* vpos)
341 vpos->showTreeForThis();
344 void showTree(const WebCore::VisiblePosition& vpos)
346 vpos.showTreeForThis();