Upstream version 9.37.195.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 WebCore {
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     RefPtrWillBeRawPtr<Range> textRunRange = nullptr;
80
81     TextIteratorBehaviorFlags behaviorFlags = TextIteratorEmitsObjectReplacementCharacter;
82     if (getRangeFor == ForSelection)
83         behaviorFlags |= TextIteratorEmitsCharactersBetweenAllVisiblePositions;
84     TextIterator it(rangeOfContents(const_cast<ContainerNode*>(&scope)).get(), behaviorFlags);
85
86     // FIXME: the atEnd() check shouldn't be necessary, workaround for <http://bugs.webkit.org/show_bug.cgi?id=6289>.
87     if (!start() && !length() && it.atEnd()) {
88         textRunRange = it.range();
89
90         resultRange->setStart(textRunRange->startContainer(), 0, ASSERT_NO_EXCEPTION);
91         resultRange->setEnd(textRunRange->startContainer(), 0, ASSERT_NO_EXCEPTION);
92
93         return resultRange.release();
94     }
95
96     for (; !it.atEnd(); it.advance()) {
97         int len = it.length();
98         textRunRange = it.range();
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 is often at the wrong
107             // position for emitted '\n's.
108             if (len == 1 && it.characterAt(0) == '\n') {
109                 scope.document().updateLayoutIgnorePendingStylesheets();
110                 it.advance();
111                 if (!it.atEnd()) {
112                     RefPtrWillBeRawPtr<Range> range = it.range();
113                     textRunRange->setEnd(range->startContainer(), range->startOffset(), ASSERT_NO_EXCEPTION);
114                 } else {
115                     Position runStart = textRunRange->startPosition();
116                     Position runEnd = VisiblePosition(runStart).next().deepEquivalent();
117                     if (runEnd.isNotNull())
118                         textRunRange->setEnd(runEnd.containerNode(), runEnd.computeOffsetInContainerNode(), ASSERT_NO_EXCEPTION);
119                 }
120             }
121         }
122
123         if (foundStart) {
124             startRangeFound = true;
125             if (textRunRange->startContainer()->isTextNode()) {
126                 int offset = start() - docTextPosition;
127                 resultRange->setStart(textRunRange->startContainer(), offset + textRunRange->startOffset(), IGNORE_EXCEPTION);
128             } else {
129                 if (start() == docTextPosition)
130                     resultRange->setStart(textRunRange->startContainer(), textRunRange->startOffset(), IGNORE_EXCEPTION);
131                 else
132                     resultRange->setStart(textRunRange->endContainer(), textRunRange->endOffset(), IGNORE_EXCEPTION);
133             }
134         }
135
136         if (foundEnd) {
137             if (textRunRange->startContainer()->isTextNode()) {
138                 int offset = end() - docTextPosition;
139                 resultRange->setEnd(textRunRange->startContainer(), offset + textRunRange->startOffset(), IGNORE_EXCEPTION);
140             } else {
141                 if (end() == docTextPosition)
142                     resultRange->setEnd(textRunRange->startContainer(), textRunRange->startOffset(), IGNORE_EXCEPTION);
143                 else
144                     resultRange->setEnd(textRunRange->endContainer(), textRunRange->endOffset(), IGNORE_EXCEPTION);
145             }
146             docTextPosition += len;
147             break;
148         }
149         docTextPosition += len;
150     }
151
152     if (!startRangeFound)
153         return nullptr;
154
155     if (length() && end() > docTextPosition) { // end() is out of bounds
156         resultRange->setEnd(textRunRange->endContainer(), textRunRange->endOffset(), IGNORE_EXCEPTION);
157     }
158
159     return resultRange.release();
160 }
161
162 PlainTextRange PlainTextRange::create(const Node& scope, const Range& range)
163 {
164     if (!range.startContainer())
165         return PlainTextRange();
166
167     // The critical assumption is that this only gets called with ranges that
168     // concentrate on a given area containing the selection root. This is done
169     // because of text fields and textareas. The DOM for those is not
170     // directly in the document DOM, so ensure that the range does not cross a
171     // boundary of one of those.
172     if (range.startContainer() != &scope && !range.startContainer()->isDescendantOf(&scope))
173         return PlainTextRange();
174     if (range.endContainer() != scope && !range.endContainer()->isDescendantOf(&scope))
175         return PlainTextRange();
176
177     RefPtrWillBeRawPtr<Range> testRange = Range::create(scope.document(), const_cast<Node*>(&scope), 0, range.startContainer(), range.startOffset());
178     ASSERT(testRange->startContainer() == &scope);
179     size_t start = TextIterator::rangeLength(testRange.get());
180
181     testRange->setEnd(range.endContainer(), range.endOffset(), IGNORE_EXCEPTION);
182     ASSERT(testRange->startContainer() == &scope);
183     size_t end = TextIterator::rangeLength(testRange.get());
184
185     return PlainTextRange(start, end);
186 }
187
188 }