Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / editing / PlainTextRange.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All rights reserved.
3  * Copyright (C) 2005 Alexey Proskuryakov.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "core/editing/PlainTextRange.h"
29
30 #include "core/dom/ContainerNode.h"
31 #include "core/dom/Document.h"
32 #include "core/dom/Range.h"
33 #include "core/editing/TextIterator.h"
34 #include "core/editing/VisiblePosition.h"
35
36 namespace blink {
37
38 PlainTextRange::PlainTextRange()
39     : m_start(kNotFound)
40     , m_end(kNotFound)
41 {
42 }
43
44 PlainTextRange::PlainTextRange(int location)
45     : m_start(location)
46     , m_end(location)
47 {
48     ASSERT(location >= 0);
49 }
50
51 PlainTextRange::PlainTextRange(int start, int end)
52     : m_start(start)
53     , m_end(end)
54 {
55     ASSERT(start >= 0);
56     ASSERT(end >= 0);
57     ASSERT(start <= end);
58 }
59
60 PassRefPtrWillBeRawPtr<Range> PlainTextRange::createRange(const ContainerNode& scope) const
61 {
62     return createRangeFor(scope, ForGeneric);
63 }
64
65 PassRefPtrWillBeRawPtr<Range> PlainTextRange::createRangeForSelection(const ContainerNode& scope) const
66 {
67     return createRangeFor(scope, ForSelection);
68 }
69
70 PassRefPtrWillBeRawPtr<Range> PlainTextRange::createRangeFor(const ContainerNode& scope, GetRangeFor getRangeFor) const
71 {
72     ASSERT(isNotNull());
73
74     RefPtrWillBeRawPtr<Range> resultRange = scope.document().createRange();
75
76     size_t docTextPosition = 0;
77     bool startRangeFound = false;
78
79     Position textRunStartPosition;
80     Position textRunEndPosition;
81
82     TextIteratorBehaviorFlags behaviorFlags = TextIteratorEmitsObjectReplacementCharacter;
83     if (getRangeFor == ForSelection)
84         behaviorFlags |= TextIteratorEmitsCharactersBetweenAllVisiblePositions;
85     TextIterator it(rangeOfContents(const_cast<ContainerNode*>(&scope)).get(), behaviorFlags);
86
87     // FIXME: the atEnd() check shouldn't be necessary, workaround for <http://bugs.webkit.org/show_bug.cgi?id=6289>.
88     if (!start() && !length() && it.atEnd()) {
89         resultRange->setStart(it.startContainer(), 0, ASSERT_NO_EXCEPTION);
90         resultRange->setEnd(it.startContainer(), 0, ASSERT_NO_EXCEPTION);
91         return resultRange.release();
92     }
93
94     for (; !it.atEnd(); it.advance()) {
95         int len = it.length();
96
97         textRunStartPosition = it.startPosition();
98         textRunEndPosition = it.endPosition();
99
100         bool foundStart = start() >= docTextPosition && start() <= docTextPosition + len;
101         bool foundEnd = end() >= docTextPosition && end() <= docTextPosition + len;
102
103         // Fix textRunRange->endPosition(), but only if foundStart || foundEnd, because it is only
104         // in those cases that textRunRange is used.
105         if (foundEnd) {
106             // FIXME: This is a workaround for the fact that the end of a run
107             // is often at the wrong position for emitted '\n's or if the
108             // renderer of the current node is a replaced element.
109             if (len == 1 && (it.characterAt(0) == '\n' || it.isInsideReplacedElement())) {
110                 it.advance();
111                 if (!it.atEnd()) {
112                     textRunEndPosition = it.startPosition();
113                 } else {
114                     Position runEnd = VisiblePosition(textRunStartPosition).next().deepEquivalent();
115                     if (runEnd.isNotNull())
116                         textRunEndPosition = createLegacyEditingPosition(runEnd.containerNode(), runEnd.computeOffsetInContainerNode());
117                 }
118             }
119         }
120
121         if (foundStart) {
122             startRangeFound = true;
123             if (textRunStartPosition.containerNode()->isTextNode()) {
124                 int offset = start() - docTextPosition;
125                 resultRange->setStart(textRunStartPosition.containerNode(), offset + textRunStartPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
126             } else {
127                 if (start() == docTextPosition)
128                     resultRange->setStart(textRunStartPosition.containerNode(), textRunStartPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
129                 else
130                     resultRange->setStart(textRunEndPosition.containerNode(), textRunEndPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
131             }
132         }
133
134         if (foundEnd) {
135             if (textRunStartPosition.containerNode()->isTextNode()) {
136                 int offset = end() - docTextPosition;
137                 resultRange->setEnd(textRunStartPosition.containerNode(), offset + textRunStartPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
138             } else {
139                 if (end() == docTextPosition)
140                     resultRange->setEnd(textRunStartPosition.containerNode(), textRunStartPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
141                 else
142                     resultRange->setEnd(textRunEndPosition.containerNode(), textRunEndPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
143             }
144             docTextPosition += len;
145             break;
146         }
147         docTextPosition += len;
148     }
149
150     if (!startRangeFound)
151         return nullptr;
152
153     if (length() && end() > docTextPosition) { // end() is out of bounds
154         resultRange->setEnd(textRunEndPosition.containerNode(), textRunEndPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
155     }
156
157     return resultRange.release();
158 }
159
160 PlainTextRange PlainTextRange::create(const ContainerNode& scope, const Range& range)
161 {
162     if (!range.startContainer())
163         return PlainTextRange();
164
165     // The critical assumption is that this only gets called with ranges that
166     // concentrate on a given area containing the selection root. This is done
167     // because of text fields and textareas. The DOM for those is not
168     // directly in the document DOM, so ensure that the range does not cross a
169     // boundary of one of those.
170     if (range.startContainer() != &scope && !range.startContainer()->isDescendantOf(&scope))
171         return PlainTextRange();
172     if (range.endContainer() != scope && !range.endContainer()->isDescendantOf(&scope))
173         return PlainTextRange();
174
175     RefPtrWillBeRawPtr<Range> testRange = Range::create(scope.document(), const_cast<ContainerNode*>(&scope), 0, range.startContainer(), range.startOffset());
176     ASSERT(testRange->startContainer() == &scope);
177     size_t start = TextIterator::rangeLength(testRange.get());
178
179     testRange->setEnd(range.endContainer(), range.endOffset(), IGNORE_EXCEPTION);
180     ASSERT(testRange->startContainer() == &scope);
181     size_t end = TextIterator::rangeLength(testRange.get());
182
183     return PlainTextRange(start, end);
184 }
185
186 }