• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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