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/TextIterator.h"
34 #include "core/editing/VisiblePosition.h"
38 PlainTextRange::PlainTextRange()
44 PlainTextRange::PlainTextRange(int location)
48 ASSERT(location >= 0);
51 PlainTextRange::PlainTextRange(int start, int end)
60 PassRefPtrWillBeRawPtr<Range> PlainTextRange::createRange(const ContainerNode& scope) const
62 return createRangeFor(scope, ForGeneric);
65 PassRefPtrWillBeRawPtr<Range> PlainTextRange::createRangeForSelection(const ContainerNode& scope) const
67 return createRangeFor(scope, ForSelection);
70 PassRefPtrWillBeRawPtr<Range> PlainTextRange::createRangeFor(const ContainerNode& scope, GetRangeFor getRangeFor) const
74 RefPtrWillBeRawPtr<Range> resultRange = scope.document().createRange();
76 size_t docTextPosition = 0;
77 bool startRangeFound = false;
79 Position textRunStartPosition;
80 Position textRunEndPosition;
82 TextIteratorBehaviorFlags behaviorFlags = TextIteratorEmitsObjectReplacementCharacter;
83 if (getRangeFor == ForSelection)
84 behaviorFlags |= TextIteratorEmitsCharactersBetweenAllVisiblePositions;
85 TextIterator it(rangeOfContents(const_cast<ContainerNode*>(&scope)).get(), behaviorFlags);
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();
94 for (; !it.atEnd(); it.advance()) {
95 int len = it.length();
97 textRunStartPosition = it.startPosition();
98 textRunEndPosition = it.endPosition();
100 bool foundStart = start() >= docTextPosition && start() <= docTextPosition + len;
101 bool foundEnd = end() >= docTextPosition && end() <= docTextPosition + len;
103 // Fix textRunRange->endPosition(), but only if foundStart || foundEnd, because it is only
104 // in those cases that textRunRange is used.
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())) {
112 textRunEndPosition = it.startPosition();
114 Position runEnd = VisiblePosition(textRunStartPosition).next().deepEquivalent();
115 if (runEnd.isNotNull())
116 textRunEndPosition = createLegacyEditingPosition(runEnd.containerNode(), runEnd.computeOffsetInContainerNode());
122 startRangeFound = true;
123 if (textRunStartPosition.containerNode()->isTextNode()) {
124 int offset = start() - docTextPosition;
125 resultRange->setStart(textRunStartPosition.containerNode(), offset + textRunStartPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
127 if (start() == docTextPosition)
128 resultRange->setStart(textRunStartPosition.containerNode(), textRunStartPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
130 resultRange->setStart(textRunEndPosition.containerNode(), textRunEndPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
135 if (textRunStartPosition.containerNode()->isTextNode()) {
136 int offset = end() - docTextPosition;
137 resultRange->setEnd(textRunStartPosition.containerNode(), offset + textRunStartPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
139 if (end() == docTextPosition)
140 resultRange->setEnd(textRunStartPosition.containerNode(), textRunStartPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
142 resultRange->setEnd(textRunEndPosition.containerNode(), textRunEndPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
144 docTextPosition += len;
147 docTextPosition += len;
150 if (!startRangeFound)
153 if (length() && end() > docTextPosition) { // end() is out of bounds
154 resultRange->setEnd(textRunEndPosition.containerNode(), textRunEndPosition.offsetInContainerNode(), IGNORE_EXCEPTION);
157 return resultRange.release();
160 PlainTextRange PlainTextRange::create(const ContainerNode& scope, const Range& range)
162 if (!range.startContainer())
163 return PlainTextRange();
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();
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());
179 testRange->setEnd(range.endContainer(), range.endOffset(), IGNORE_EXCEPTION);
180 ASSERT(testRange->startContainer() == &scope);
181 size_t end = TextIterator::rangeLength(testRange.get());
183 return PlainTextRange(start, end);