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 blink { 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 Position textRunStartPosition; 80 Position textRunEndPosition; 81 82 TextIteratorBehaviorFlags behaviorFlags = TextIteratorEmitsObjectReplacementCharacter; 83 if (getRangeFor == ForSelection) 84 behaviorFlags |= TextIteratorEmitsCharactersBetweenAllVisiblePositions; 85 TextIterator it(rangeOfContents(const_cast<ContainerNode*>(&scope)).get(), behaviorFlags); 86 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(); 92 } 93 94 for (; !it.atEnd(); it.advance()) { 95 int len = it.length(); 96 97 textRunStartPosition = it.startPosition(); 98 textRunEndPosition = it.endPosition(); 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 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())) { 110 it.advance(); 111 if (!it.atEnd()) { 112 textRunEndPosition = it.startPosition(); 113 } else { 114 Position runEnd = VisiblePosition(textRunStartPosition).next().deepEquivalent(); 115 if (runEnd.isNotNull()) 116 textRunEndPosition = createLegacyEditingPosition(runEnd.containerNode(), runEnd.computeOffsetInContainerNode()); 117 } 118 } 119 } 120 121 if (foundStart) { 122 startRangeFound = true; 123 if (textRunStartPosition.containerNode()->isTextNode()) { 124 int offset = start() - docTextPosition; 125 resultRange->setStart(textRunStartPosition.containerNode(), offset + textRunStartPosition.offsetInContainerNode(), IGNORE_EXCEPTION); 126 } else { 127 if (start() == docTextPosition) 128 resultRange->setStart(textRunStartPosition.containerNode(), textRunStartPosition.offsetInContainerNode(), IGNORE_EXCEPTION); 129 else 130 resultRange->setStart(textRunEndPosition.containerNode(), textRunEndPosition.offsetInContainerNode(), IGNORE_EXCEPTION); 131 } 132 } 133 134 if (foundEnd) { 135 if (textRunStartPosition.containerNode()->isTextNode()) { 136 int offset = end() - docTextPosition; 137 resultRange->setEnd(textRunStartPosition.containerNode(), offset + textRunStartPosition.offsetInContainerNode(), IGNORE_EXCEPTION); 138 } else { 139 if (end() == docTextPosition) 140 resultRange->setEnd(textRunStartPosition.containerNode(), textRunStartPosition.offsetInContainerNode(), IGNORE_EXCEPTION); 141 else 142 resultRange->setEnd(textRunEndPosition.containerNode(), textRunEndPosition.offsetInContainerNode(), IGNORE_EXCEPTION); 143 } 144 docTextPosition += len; 145 break; 146 } 147 docTextPosition += len; 148 } 149 150 if (!startRangeFound) 151 return nullptr; 152 153 if (length() && end() > docTextPosition) { // end() is out of bounds 154 resultRange->setEnd(textRunEndPosition.containerNode(), textRunEndPosition.offsetInContainerNode(), IGNORE_EXCEPTION); 155 } 156 157 return resultRange.release(); 158 } 159 160 PlainTextRange PlainTextRange::create(const ContainerNode& scope, const Range& range) 161 { 162 if (!range.startContainer()) 163 return PlainTextRange(); 164 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(); 174 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()); 178 179 testRange->setEnd(range.endContainer(), range.endOffset(), IGNORE_EXCEPTION); 180 ASSERT(testRange->startContainer() == &scope); 181 size_t end = TextIterator::rangeLength(testRange.get()); 182 183 return PlainTextRange(start, end); 184 } 185 186 } 187