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 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 RefPtrWillBeRawPtr<Range> textRunRange = nullptr; 80 81 TextIteratorBehaviorFlags behaviorFlags = TextIteratorEmitsObjectReplacementCharacter; 82 if (getRangeFor == ForSelection) 83 behaviorFlags |= TextIteratorEmitsCharactersBetweenAllVisiblePositions; 84 TextIterator it(rangeOfContents(const_cast<ContainerNode*>(&scope)).get(), behaviorFlags); 85 86 // FIXME: the atEnd() check shouldn't be necessary, workaround for <http://bugs.webkit.org/show_bug.cgi?id=6289>. 87 if (!start() && !length() && it.atEnd()) { 88 textRunRange = it.range(); 89 90 resultRange->setStart(textRunRange->startContainer(), 0, ASSERT_NO_EXCEPTION); 91 resultRange->setEnd(textRunRange->startContainer(), 0, ASSERT_NO_EXCEPTION); 92 93 return resultRange.release(); 94 } 95 96 for (; !it.atEnd(); it.advance()) { 97 int len = it.length(); 98 textRunRange = it.range(); 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 is often at the wrong 107 // position for emitted '\n's. 108 if (len == 1 && it.characterAt(0) == '\n') { 109 scope.document().updateLayoutIgnorePendingStylesheets(); 110 it.advance(); 111 if (!it.atEnd()) { 112 RefPtrWillBeRawPtr<Range> range = it.range(); 113 textRunRange->setEnd(range->startContainer(), range->startOffset(), ASSERT_NO_EXCEPTION); 114 } else { 115 Position runStart = textRunRange->startPosition(); 116 Position runEnd = VisiblePosition(runStart).next().deepEquivalent(); 117 if (runEnd.isNotNull()) 118 textRunRange->setEnd(runEnd.containerNode(), runEnd.computeOffsetInContainerNode(), ASSERT_NO_EXCEPTION); 119 } 120 } 121 } 122 123 if (foundStart) { 124 startRangeFound = true; 125 if (textRunRange->startContainer()->isTextNode()) { 126 int offset = start() - docTextPosition; 127 resultRange->setStart(textRunRange->startContainer(), offset + textRunRange->startOffset(), IGNORE_EXCEPTION); 128 } else { 129 if (start() == docTextPosition) 130 resultRange->setStart(textRunRange->startContainer(), textRunRange->startOffset(), IGNORE_EXCEPTION); 131 else 132 resultRange->setStart(textRunRange->endContainer(), textRunRange->endOffset(), IGNORE_EXCEPTION); 133 } 134 } 135 136 if (foundEnd) { 137 if (textRunRange->startContainer()->isTextNode()) { 138 int offset = end() - docTextPosition; 139 resultRange->setEnd(textRunRange->startContainer(), offset + textRunRange->startOffset(), IGNORE_EXCEPTION); 140 } else { 141 if (end() == docTextPosition) 142 resultRange->setEnd(textRunRange->startContainer(), textRunRange->startOffset(), IGNORE_EXCEPTION); 143 else 144 resultRange->setEnd(textRunRange->endContainer(), textRunRange->endOffset(), IGNORE_EXCEPTION); 145 } 146 docTextPosition += len; 147 break; 148 } 149 docTextPosition += len; 150 } 151 152 if (!startRangeFound) 153 return nullptr; 154 155 if (length() && end() > docTextPosition) { // end() is out of bounds 156 resultRange->setEnd(textRunRange->endContainer(), textRunRange->endOffset(), IGNORE_EXCEPTION); 157 } 158 159 return resultRange.release(); 160 } 161 162 PlainTextRange PlainTextRange::create(const Node& scope, const Range& range) 163 { 164 if (!range.startContainer()) 165 return PlainTextRange(); 166 167 // The critical assumption is that this only gets called with ranges that 168 // concentrate on a given area containing the selection root. This is done 169 // because of text fields and textareas. The DOM for those is not 170 // directly in the document DOM, so ensure that the range does not cross a 171 // boundary of one of those. 172 if (range.startContainer() != &scope && !range.startContainer()->isDescendantOf(&scope)) 173 return PlainTextRange(); 174 if (range.endContainer() != scope && !range.endContainer()->isDescendantOf(&scope)) 175 return PlainTextRange(); 176 177 RefPtrWillBeRawPtr<Range> testRange = Range::create(scope.document(), const_cast<Node*>(&scope), 0, range.startContainer(), range.startOffset()); 178 ASSERT(testRange->startContainer() == &scope); 179 size_t start = TextIterator::rangeLength(testRange.get()); 180 181 testRange->setEnd(range.endContainer(), range.endOffset(), IGNORE_EXCEPTION); 182 ASSERT(testRange->startContainer() == &scope); 183 size_t end = TextIterator::rangeLength(testRange.get()); 184 185 return PlainTextRange(start, end); 186 } 187 188 } 189