1/*
2 * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
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.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE 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 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.
24 */
25
26#include "config.h"
27#include "core/dom/PositionIterator.h"
28
29#include "core/editing/htmlediting.h"
30#include "core/html/HTMLHtmlElement.h"
31#include "core/rendering/RenderBlock.h"
32
33namespace blink {
34
35using namespace HTMLNames;
36
37PositionIterator::operator Position() const
38{
39    if (m_nodeAfterPositionInAnchor) {
40        ASSERT(m_nodeAfterPositionInAnchor->parentNode() == m_anchorNode);
41        // FIXME: This check is inadaquete because any ancestor could be ignored by editing
42        if (editingIgnoresContent(m_nodeAfterPositionInAnchor->parentNode()))
43            return positionBeforeNode(m_anchorNode);
44        return positionInParentBeforeNode(*m_nodeAfterPositionInAnchor);
45    }
46    if (m_anchorNode->hasChildren())
47        return lastPositionInOrAfterNode(m_anchorNode);
48    return createLegacyEditingPosition(m_anchorNode, m_offsetInAnchor);
49}
50
51void PositionIterator::increment()
52{
53    if (!m_anchorNode)
54        return;
55
56    if (m_nodeAfterPositionInAnchor) {
57        m_anchorNode = m_nodeAfterPositionInAnchor;
58        m_nodeAfterPositionInAnchor = m_anchorNode->firstChild();
59        m_offsetInAnchor = 0;
60        return;
61    }
62
63    if (!m_anchorNode->hasChildren() && m_offsetInAnchor < lastOffsetForEditing(m_anchorNode))
64        m_offsetInAnchor = Position::uncheckedNextOffset(m_anchorNode, m_offsetInAnchor);
65    else {
66        m_nodeAfterPositionInAnchor = m_anchorNode;
67        m_anchorNode = m_nodeAfterPositionInAnchor->parentNode();
68        m_nodeAfterPositionInAnchor = m_nodeAfterPositionInAnchor->nextSibling();
69        m_offsetInAnchor = 0;
70    }
71}
72
73void PositionIterator::decrement()
74{
75    if (!m_anchorNode)
76        return;
77
78    if (m_nodeAfterPositionInAnchor) {
79        m_anchorNode = m_nodeAfterPositionInAnchor->previousSibling();
80        if (m_anchorNode) {
81            m_nodeAfterPositionInAnchor = nullptr;
82            m_offsetInAnchor = m_anchorNode->hasChildren() ? 0 : lastOffsetForEditing(m_anchorNode);
83        } else {
84            m_nodeAfterPositionInAnchor = m_nodeAfterPositionInAnchor->parentNode();
85            m_anchorNode = m_nodeAfterPositionInAnchor->parentNode();
86            m_offsetInAnchor = 0;
87        }
88        return;
89    }
90
91    if (m_anchorNode->hasChildren()) {
92        m_anchorNode = m_anchorNode->lastChild();
93        m_offsetInAnchor = m_anchorNode->hasChildren()? 0: lastOffsetForEditing(m_anchorNode);
94    } else {
95        if (m_offsetInAnchor)
96            m_offsetInAnchor = Position::uncheckedPreviousOffset(m_anchorNode, m_offsetInAnchor);
97        else {
98            m_nodeAfterPositionInAnchor = m_anchorNode;
99            m_anchorNode = m_anchorNode->parentNode();
100        }
101    }
102}
103
104bool PositionIterator::atStart() const
105{
106    if (!m_anchorNode)
107        return true;
108    if (m_anchorNode->parentNode())
109        return false;
110    return (!m_anchorNode->hasChildren() && !m_offsetInAnchor) || (m_nodeAfterPositionInAnchor && !m_nodeAfterPositionInAnchor->previousSibling());
111}
112
113bool PositionIterator::atEnd() const
114{
115    if (!m_anchorNode)
116        return true;
117    if (m_nodeAfterPositionInAnchor)
118        return false;
119    return !m_anchorNode->parentNode() && (m_anchorNode->hasChildren() || m_offsetInAnchor >= lastOffsetForEditing(m_anchorNode));
120}
121
122bool PositionIterator::atStartOfNode() const
123{
124    if (!m_anchorNode)
125        return true;
126    if (!m_nodeAfterPositionInAnchor)
127        return !m_anchorNode->hasChildren() && !m_offsetInAnchor;
128    return !m_nodeAfterPositionInAnchor->previousSibling();
129}
130
131bool PositionIterator::atEndOfNode() const
132{
133    if (!m_anchorNode)
134        return true;
135    if (m_nodeAfterPositionInAnchor)
136        return false;
137    return m_anchorNode->hasChildren() || m_offsetInAnchor >= lastOffsetForEditing(m_anchorNode);
138}
139
140bool PositionIterator::isCandidate() const
141{
142    if (!m_anchorNode)
143        return false;
144
145    RenderObject* renderer = m_anchorNode->renderer();
146    if (!renderer)
147        return false;
148
149    if (renderer->style()->visibility() != VISIBLE)
150        return false;
151
152    if (renderer->isBR()) {
153        // For br element, the condition
154        // |(!m_anchorNode->hasChildren() || m_nodeAfterPositionInAnchor)|
155        // corresponds to the condition
156        // |m_anchorType != PositionIsAfterAnchor| in Position.isCandaite.
157        // Both conditions say this position is not in tail of the element.
158        // If conditions lose consitency, VisiblePosition::canonicalPosition
159        // will fail on |canonicalizeCandidate(previousCandidate(position))|,
160        // because previousCandidate returns a Position converted from
161        // a "Candidate" PositionIterator and cannonicalizeCandidate(Position)
162        // assumes the Position is "Candidate".
163        return !m_offsetInAnchor && (!m_anchorNode->hasChildren() || m_nodeAfterPositionInAnchor) && !Position::nodeIsUserSelectNone(m_anchorNode->parentNode());
164    }
165    if (renderer->isText())
166        return !Position::nodeIsUserSelectNone(m_anchorNode) && Position(*this).inRenderedText();
167
168    if (renderer->isSVG()) {
169        // We don't consider SVG elements are contenteditable except for
170        // associated renderer returns isText() true, e.g. RenderSVGInlineText.
171        return false;
172    }
173
174    if (isRenderedHTMLTableElement(m_anchorNode) || editingIgnoresContent(m_anchorNode))
175        return (atStartOfNode() || atEndOfNode()) && !Position::nodeIsUserSelectNone(m_anchorNode->parentNode());
176
177    if (!isHTMLHtmlElement(*m_anchorNode) && renderer->isRenderBlockFlow()) {
178        if (toRenderBlock(renderer)->logicalHeight() || isHTMLBodyElement(*m_anchorNode)) {
179            if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(renderer))
180                return atStartOfNode() && !Position::nodeIsUserSelectNone(m_anchorNode);
181            return m_anchorNode->hasEditableStyle() && !Position::nodeIsUserSelectNone(m_anchorNode) && Position(*this).atEditingBoundary();
182        }
183    }
184
185    return false;
186}
187
188} // namespace blink
189