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 PassRefPtr<Range> PlainTextRange::createRange(const ContainerNode& scope) const 61 { 62 return createRangeFor(scope, ForGeneric); 63 } 64 65 PassRefPtr<Range> PlainTextRange::createRangeForSelection(const ContainerNode& scope) const 66 { 67 return createRangeFor(scope, ForSelection); 68 } 69 70 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 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