1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 */ 21 22 #include "config.h" 23 #include "core/dom/CharacterData.h" 24 25 #include "bindings/v8/ExceptionState.h" 26 #include "core/dom/Document.h" 27 #include "core/dom/EventNames.h" 28 #include "core/dom/ExceptionCode.h" 29 #include "core/dom/MutationEvent.h" 30 #include "core/dom/MutationObserverInterestGroup.h" 31 #include "core/dom/MutationRecord.h" 32 #include "core/dom/Text.h" 33 #include "core/editing/FrameSelection.h" 34 #include "core/inspector/InspectorInstrumentation.h" 35 #include "core/platform/text/TextBreakIterator.h" 36 37 using namespace std; 38 39 namespace WebCore { 40 41 void CharacterData::atomize() 42 { 43 m_data = AtomicString(m_data); 44 } 45 46 void CharacterData::setData(const String& data) 47 { 48 const String& nonNullData = !data.isNull() ? data : emptyString(); 49 if (m_data == nonNullData) 50 return; 51 52 RefPtr<CharacterData> protect = this; 53 54 unsigned oldLength = length(); 55 56 setDataAndUpdate(nonNullData, 0, oldLength, nonNullData.length()); 57 document()->textRemoved(this, 0, oldLength); 58 } 59 60 String CharacterData::substringData(unsigned offset, unsigned count, ExceptionState& es) 61 { 62 if (offset > length()) { 63 es.throwDOMException(IndexSizeError); 64 return String(); 65 } 66 67 return m_data.substring(offset, count); 68 } 69 70 unsigned CharacterData::parserAppendData(const String& string, unsigned offset, unsigned lengthLimit) 71 { 72 unsigned oldLength = m_data.length(); 73 74 ASSERT(lengthLimit >= oldLength); 75 76 unsigned characterLength = string.length() - offset; 77 unsigned characterLengthLimit = min(characterLength, lengthLimit - oldLength); 78 79 // Check that we are not on an unbreakable boundary. 80 // Some text break iterator implementations work best if the passed buffer is as small as possible, 81 // see <https://bugs.webkit.org/show_bug.cgi?id=29092>. 82 // We need at least two characters look-ahead to account for UTF-16 surrogates. 83 ASSERT(!string.is8Bit() || string.containsOnlyLatin1()); // Latin-1 doesn't have unbreakable boundaries. 84 if (characterLengthLimit < characterLength && !string.is8Bit()) { 85 NonSharedCharacterBreakIterator it(string.characters16() + offset, (characterLengthLimit + 2 > characterLength) ? characterLength : characterLengthLimit + 2); 86 if (!it.isBreak(characterLengthLimit)) 87 characterLengthLimit = it.preceding(characterLengthLimit); 88 } 89 90 if (!characterLengthLimit) 91 return 0; 92 93 if (string.is8Bit()) 94 m_data.append(string.characters8() + offset, characterLengthLimit); 95 else 96 m_data.append(string.characters16() + offset, characterLengthLimit); 97 98 ASSERT(!renderer() || isTextNode()); 99 if (isTextNode()) 100 toText(this)->updateTextRenderer(oldLength, 0); 101 102 document()->incDOMTreeVersion(); 103 104 if (parentNode()) 105 parentNode()->childrenChanged(); 106 107 return characterLengthLimit; 108 } 109 110 void CharacterData::appendData(const String& data) 111 { 112 String newStr = m_data + data; 113 114 setDataAndUpdate(newStr, m_data.length(), 0, data.length()); 115 116 // FIXME: Should we call textInserted here? 117 } 118 119 void CharacterData::insertData(unsigned offset, const String& data, ExceptionState& es) 120 { 121 if (offset > length()) { 122 es.throwDOMException(IndexSizeError); 123 return; 124 } 125 126 String newStr = m_data; 127 newStr.insert(data, offset); 128 129 setDataAndUpdate(newStr, offset, 0, data.length()); 130 131 document()->textInserted(this, offset, data.length()); 132 } 133 134 void CharacterData::deleteData(unsigned offset, unsigned count, ExceptionState& es) 135 { 136 if (offset > length()) { 137 es.throwDOMException(IndexSizeError); 138 return; 139 } 140 141 unsigned realCount; 142 if (offset + count > length()) 143 realCount = length() - offset; 144 else 145 realCount = count; 146 147 String newStr = m_data; 148 newStr.remove(offset, realCount); 149 150 setDataAndUpdate(newStr, offset, count, 0); 151 152 document()->textRemoved(this, offset, realCount); 153 } 154 155 void CharacterData::replaceData(unsigned offset, unsigned count, const String& data, ExceptionState& es) 156 { 157 if (offset > length()) { 158 es.throwDOMException(IndexSizeError); 159 return; 160 } 161 162 unsigned realCount; 163 if (offset + count > length()) 164 realCount = length() - offset; 165 else 166 realCount = count; 167 168 String newStr = m_data; 169 newStr.remove(offset, realCount); 170 newStr.insert(data, offset); 171 172 setDataAndUpdate(newStr, offset, count, data.length()); 173 174 // update the markers for spell checking and grammar checking 175 document()->textRemoved(this, offset, realCount); 176 document()->textInserted(this, offset, data.length()); 177 } 178 179 String CharacterData::nodeValue() const 180 { 181 return m_data; 182 } 183 184 bool CharacterData::containsOnlyWhitespace() const 185 { 186 return m_data.containsOnlyWhitespace(); 187 } 188 189 void CharacterData::setNodeValue(const String& nodeValue) 190 { 191 setData(nodeValue); 192 } 193 194 void CharacterData::setDataAndUpdate(const String& newData, unsigned offsetOfReplacedData, unsigned oldLength, unsigned newLength) 195 { 196 String oldData = m_data; 197 m_data = newData; 198 199 ASSERT(!renderer() || isTextNode()); 200 if (isTextNode()) 201 toText(this)->updateTextRenderer(offsetOfReplacedData, oldLength); 202 203 if (document()->frame()) 204 document()->frame()->selection()->textWasReplaced(this, offsetOfReplacedData, oldLength, newLength); 205 206 document()->incDOMTreeVersion(); 207 didModifyData(oldData); 208 } 209 210 void CharacterData::didModifyData(const String& oldData) 211 { 212 if (OwnPtr<MutationObserverInterestGroup> mutationRecipients = MutationObserverInterestGroup::createForCharacterDataMutation(this)) 213 mutationRecipients->enqueueMutationRecord(MutationRecord::createCharacterData(this, oldData)); 214 215 if (parentNode()) 216 parentNode()->childrenChanged(); 217 218 if (!isInShadowTree()) { 219 if (document()->hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER)) 220 dispatchScopedEvent(MutationEvent::create(eventNames().DOMCharacterDataModifiedEvent, true, 0, oldData, m_data)); 221 dispatchSubtreeModifiedEvent(); 222 } 223 InspectorInstrumentation::characterDataModified(document(), this); 224 } 225 226 int CharacterData::maxCharacterOffset() const 227 { 228 return static_cast<int>(length()); 229 } 230 231 bool CharacterData::offsetInCharacters() const 232 { 233 return true; 234 } 235 236 } // namespace WebCore 237