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
PlainTextRange()38 PlainTextRange::PlainTextRange()
39 : m_start(kNotFound)
40 , m_end(kNotFound)
41 {
42 }
43
PlainTextRange(int location)44 PlainTextRange::PlainTextRange(int location)
45 : m_start(location)
46 , m_end(location)
47 {
48 ASSERT(location >= 0);
49 }
50
PlainTextRange(int start,int end)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
createRange(const ContainerNode & scope) const60 PassRefPtr<Range> PlainTextRange::createRange(const ContainerNode& scope) const
61 {
62 return createRangeFor(scope, ForGeneric);
63 }
64
createRangeForSelection(const ContainerNode & scope) const65 PassRefPtr<Range> PlainTextRange::createRangeForSelection(const ContainerNode& scope) const
66 {
67 return createRangeFor(scope, ForSelection);
68 }
69
createRangeFor(const ContainerNode & scope,GetRangeFor getRangeFor) const70 PassRefPtr<Range> PlainTextRange::createRangeFor(const ContainerNode& scope, GetRangeFor getRangeFor) const
71 {
72 ASSERT(isNotNull());
73
74 RefPtr<Range> resultRange = scope.document().createRange();
75
76 size_t docTextPosition = 0;
77 bool startRangeFound = false;
78
79 RefPtr<Range> textRunRange;
80
81 TextIterator it(rangeOfContents(const_cast<ContainerNode*>(&scope)).get(), getRangeFor == ForSelection ? TextIteratorEmitsCharactersBetweenAllVisiblePositions : TextIteratorDefaultBehavior);
82
83 // FIXME: the atEnd() check shouldn't be necessary, workaround for <http://bugs.webkit.org/show_bug.cgi?id=6289>.
84 if (!start() && !length() && it.atEnd()) {
85 textRunRange = it.range();
86
87 resultRange->setStart(textRunRange->startContainer(), 0, ASSERT_NO_EXCEPTION);
88 resultRange->setEnd(textRunRange->startContainer(), 0, ASSERT_NO_EXCEPTION);
89
90 return resultRange.release();
91 }
92
93 for (; !it.atEnd(); it.advance()) {
94 int len = it.length();
95 textRunRange = it.range();
96
97 bool foundStart = start() >= docTextPosition && start() <= docTextPosition + len;
98 bool foundEnd = end() >= docTextPosition && end() <= docTextPosition + len;
99
100 // Fix textRunRange->endPosition(), but only if foundStart || foundEnd, because it is only
101 // in those cases that textRunRange is used.
102 if (foundEnd) {
103 // FIXME: This is a workaround for the fact that the end of a run is often at the wrong
104 // position for emitted '\n's.
105 if (len == 1 && it.characterAt(0) == '\n') {
106 scope.document().updateLayoutIgnorePendingStylesheets();
107 it.advance();
108 if (!it.atEnd()) {
109 RefPtr<Range> range = it.range();
110 textRunRange->setEnd(range->startContainer(), range->startOffset(), ASSERT_NO_EXCEPTION);
111 } else {
112 Position runStart = textRunRange->startPosition();
113 Position runEnd = VisiblePosition(runStart).next().deepEquivalent();
114 if (runEnd.isNotNull())
115 textRunRange->setEnd(runEnd.containerNode(), runEnd.computeOffsetInContainerNode(), ASSERT_NO_EXCEPTION);
116 }
117 }
118 }
119
120 if (foundStart) {
121 startRangeFound = true;
122 if (textRunRange->startContainer()->isTextNode()) {
123 int offset = start() - docTextPosition;
124 resultRange->setStart(textRunRange->startContainer(), offset + textRunRange->startOffset(), IGNORE_EXCEPTION);
125 } else {
126 if (start() == docTextPosition)
127 resultRange->setStart(textRunRange->startContainer(), textRunRange->startOffset(), IGNORE_EXCEPTION);
128 else
129 resultRange->setStart(textRunRange->endContainer(), textRunRange->endOffset(), IGNORE_EXCEPTION);
130 }
131 }
132
133 if (foundEnd) {
134 if (textRunRange->startContainer()->isTextNode()) {
135 int offset = end() - docTextPosition;
136 resultRange->setEnd(textRunRange->startContainer(), offset + textRunRange->startOffset(), IGNORE_EXCEPTION);
137 } else {
138 if (end() == docTextPosition)
139 resultRange->setEnd(textRunRange->startContainer(), textRunRange->startOffset(), IGNORE_EXCEPTION);
140 else
141 resultRange->setEnd(textRunRange->endContainer(), textRunRange->endOffset(), IGNORE_EXCEPTION);
142 }
143 docTextPosition += len;
144 break;
145 }
146 docTextPosition += len;
147 }
148
149 if (!startRangeFound)
150 return 0;
151
152 if (length() && end() > docTextPosition) { // end() is out of bounds
153 resultRange->setEnd(textRunRange->endContainer(), textRunRange->endOffset(), IGNORE_EXCEPTION);
154 }
155
156 return resultRange.release();
157 }
158
create(const Node & scope,const Range & range)159 PlainTextRange PlainTextRange::create(const Node& scope, const Range& range)
160 {
161 if (!range.startContainer())
162 return PlainTextRange();
163
164 // The critical assumption is that this only gets called with ranges that
165 // concentrate on a given area containing the selection root. This is done
166 // because of text fields and textareas. The DOM for those is not
167 // directly in the document DOM, so ensure that the range does not cross a
168 // boundary of one of those.
169 if (range.startContainer() != &scope && !range.startContainer()->isDescendantOf(&scope))
170 return PlainTextRange();
171 if (range.endContainer() != scope && !range.endContainer()->isDescendantOf(&scope))
172 return PlainTextRange();
173
174 RefPtr<Range> testRange = Range::create(scope.document(), const_cast<Node*>(&scope), 0, range.startContainer(), range.startOffset());
175 ASSERT(testRange->startContainer() == &scope);
176 size_t start = TextIterator::rangeLength(testRange.get());
177
178 testRange->setEnd(range.endContainer(), range.endOffset(), IGNORE_EXCEPTION);
179 ASSERT(testRange->startContainer() == &scope);
180 size_t end = TextIterator::rangeLength(testRange.get());
181
182 return PlainTextRange(start, end);
183 }
184
185 }
186