2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All rights reserved.
3 * Copyright (C) 2005 Alexey Proskuryakov.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
28 #include "core/editing/PlainTextRange.h"
30 #include "core/dom/ContainerNode.h"
31 #include "core/dom/Document.h"
32 #include "core/dom/Range.h"
33 #include "core/editing/EphemeralRange.h"
34 #include "core/editing/VisiblePosition.h"
35 #include "core/editing/iterators/TextIterator.h"
39 PlainTextRange::PlainTextRange()
45 PlainTextRange::PlainTextRange(int location
)
49 ASSERT(location
>= 0);
52 PlainTextRange::PlainTextRange(int start
, int end
)
61 EphemeralRange
PlainTextRange::createRange(const ContainerNode
& scope
) const
63 return createRangeFor(scope
, ForGeneric
);
66 EphemeralRange
PlainTextRange::createRangeForSelection(const ContainerNode
& scope
) const
68 return createRangeFor(scope
, ForSelection
);
71 EphemeralRange
PlainTextRange::createRangeFor(const ContainerNode
& scope
, GetRangeFor getRangeFor
) const
75 size_t docTextPosition
= 0;
76 bool startRangeFound
= false;
78 Position textRunStartPosition
;
79 Position textRunEndPosition
;
81 TextIteratorBehaviorFlags behaviorFlags
= TextIteratorEmitsObjectReplacementCharacter
;
82 if (getRangeFor
== ForSelection
)
83 behaviorFlags
|= TextIteratorEmitsCharactersBetweenAllVisiblePositions
;
84 auto range
= EphemeralRange::rangeOfContents(scope
);
85 TextIterator
it(range
.startPosition(), range
.endPosition(), behaviorFlags
);
87 // FIXME: the atEnd() check shouldn't be necessary, workaround for
88 // <http://bugs.webkit.org/show_bug.cgi?id=6289>.
89 if (!start() && !length() && it
.atEnd())
90 return EphemeralRange(Position(it
.currentContainer(), 0));
92 Position resultStart
= Position(&scope
.document(), 0);
93 Position resultEnd
= resultStart
;
95 for (; !it
.atEnd(); it
.advance()) {
96 int len
= it
.length();
98 textRunStartPosition
= it
.startPositionInCurrentContainer();
99 textRunEndPosition
= it
.endPositionInCurrentContainer();
101 bool foundStart
= start() >= docTextPosition
&& start() <= docTextPosition
+ len
;
102 bool foundEnd
= end() >= docTextPosition
&& end() <= docTextPosition
+ len
;
104 // Fix textRunRange->endPosition(), but only if foundStart || foundEnd, because it is only
105 // in those cases that textRunRange is used.
107 // FIXME: This is a workaround for the fact that the end of a run
108 // is often at the wrong position for emitted '\n's or if the
109 // layoutObject of the current node is a replaced element.
110 if (len
== 1 && (it
.text().characterAt(0) == '\n' || it
.isInsideReplacedElement())) {
113 textRunEndPosition
= it
.startPositionInCurrentContainer();
115 Position runEnd
= VisiblePosition(textRunStartPosition
).next().deepEquivalent();
116 if (runEnd
.isNotNull())
117 textRunEndPosition
= runEnd
;
123 startRangeFound
= true;
124 if (textRunStartPosition
.containerNode()->isTextNode()) {
125 int offset
= start() - docTextPosition
;
126 resultStart
= Position(textRunStartPosition
.containerNode(), offset
+ textRunStartPosition
.offsetInContainerNode());
128 if (start() == docTextPosition
)
129 resultStart
= textRunStartPosition
;
131 resultStart
= textRunEndPosition
;
136 if (textRunStartPosition
.containerNode()->isTextNode()) {
137 int offset
= end() - docTextPosition
;
138 resultEnd
= Position(textRunStartPosition
.containerNode(), offset
+ textRunStartPosition
.offsetInContainerNode());
140 if (end() == docTextPosition
)
141 resultEnd
= textRunStartPosition
;
143 resultEnd
= textRunEndPosition
;
145 docTextPosition
+= len
;
148 docTextPosition
+= len
;
151 if (!startRangeFound
)
152 return EphemeralRange();
154 if (length() && end() > docTextPosition
) { // end() is out of bounds
155 resultEnd
= textRunEndPosition
;
158 return EphemeralRange(resultStart
.toOffsetInAnchor(), resultEnd
.toOffsetInAnchor());
161 PlainTextRange
PlainTextRange::create(const ContainerNode
& scope
, const EphemeralRange
& range
)
164 return PlainTextRange();
166 // The critical assumption is that this only gets called with ranges that
167 // concentrate on a given area containing the selection root. This is done
168 // because of text fields and textareas. The DOM for those is not
169 // directly in the document DOM, so ensure that the range does not cross a
170 // boundary of one of those.
171 Node
* startContainer
= range
.startPosition().containerNode();
172 if (startContainer
!= &scope
&& !startContainer
->isDescendantOf(&scope
))
173 return PlainTextRange();
174 Node
* endContainer
= range
.endPosition().containerNode();
175 if (endContainer
!= scope
&& !endContainer
->isDescendantOf(&scope
))
176 return PlainTextRange();
178 size_t start
= TextIterator::rangeLength(Position(&const_cast<ContainerNode
&>(scope
), 0), range
.startPosition());
179 size_t end
= TextIterator::rangeLength(Position(&const_cast<ContainerNode
&>(scope
), 0), range
.endPosition());
181 return PlainTextRange(start
, end
);
184 PlainTextRange
PlainTextRange::create(const ContainerNode
& scope
, const Range
& range
)
186 return create(scope
, EphemeralRange(&range
));