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, 2013 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/core/v8/ExceptionState.h" 26 #include "core/dom/Document.h" 27 #include "core/dom/ExceptionCode.h" 28 #include "core/dom/MutationObserverInterestGroup.h" 29 #include "core/dom/MutationRecord.h" 30 #include "core/dom/ProcessingInstruction.h" 31 #include "core/dom/Text.h" 32 #include "core/editing/FrameSelection.h" 33 #include "core/events/MutationEvent.h" 34 #include "core/inspector/InspectorInstrumentation.h" 35 #include "wtf/CheckedArithmetic.h" 36 37 namespace blink { 38 39 void CharacterData::atomize() 40 { 41 m_data = AtomicString(m_data); 42 } 43 44 void CharacterData::setData(const String& data) 45 { 46 const String& nonNullData = !data.isNull() ? data : emptyString(); 47 if (m_data == nonNullData) 48 return; 49 50 RefPtrWillBeRawPtr<CharacterData> protect(this); 51 52 unsigned oldLength = length(); 53 54 setDataAndUpdate(nonNullData, 0, oldLength, nonNullData.length()); 55 document().didRemoveText(this, 0, oldLength); 56 } 57 58 String CharacterData::substringData(unsigned offset, unsigned count, ExceptionState& exceptionState) 59 { 60 if (offset > length()) { 61 exceptionState.throwDOMException(IndexSizeError, "The offset " + String::number(offset) + " is greater than the node's length (" + String::number(length()) + ")."); 62 return String(); 63 } 64 65 return m_data.substring(offset, count); 66 } 67 68 void CharacterData::parserAppendData(const String& string) 69 { 70 unsigned oldLength = m_data.length(); 71 m_data = m_data + string; 72 73 ASSERT(!renderer() || isTextNode()); 74 if (isTextNode()) 75 toText(this)->updateTextRenderer(oldLength, 0); 76 77 document().incDOMTreeVersion(); 78 79 if (parentNode()) { 80 ContainerNode::ChildrenChange change = {ContainerNode::TextChanged, previousSibling(), nextSibling(), ContainerNode::ChildrenChangeSourceParser}; 81 parentNode()->childrenChanged(change); 82 } 83 } 84 85 void CharacterData::appendData(const String& data) 86 { 87 String newStr = m_data + data; 88 89 setDataAndUpdate(newStr, m_data.length(), 0, data.length()); 90 91 // FIXME: Should we call textInserted here? 92 } 93 94 void CharacterData::insertData(unsigned offset, const String& data, ExceptionState& exceptionState, RecalcStyleBehavior recalcStyleBehavior) 95 { 96 if (offset > length()) { 97 exceptionState.throwDOMException(IndexSizeError, "The offset " + String::number(offset) + " is greater than the node's length (" + String::number(length()) + ")."); 98 return; 99 } 100 101 String newStr = m_data; 102 newStr.insert(data, offset); 103 104 setDataAndUpdate(newStr, offset, 0, data.length(), recalcStyleBehavior); 105 106 document().didInsertText(this, offset, data.length()); 107 } 108 109 static bool validateOffsetCount(unsigned offset, unsigned count, unsigned length, unsigned& realCount, ExceptionState& exceptionState) 110 { 111 if (offset > length) { 112 exceptionState.throwDOMException(IndexSizeError, "The offset " + String::number(offset) + " is greater than the node's length (" + String::number(length) + ")."); 113 return false; 114 } 115 116 Checked<unsigned, RecordOverflow> offsetCount = offset; 117 offsetCount += count; 118 119 if (offsetCount.hasOverflowed() || offset + count > length) 120 realCount = length - offset; 121 else 122 realCount = count; 123 124 return true; 125 } 126 127 void CharacterData::deleteData(unsigned offset, unsigned count, ExceptionState& exceptionState, RecalcStyleBehavior recalcStyleBehavior) 128 { 129 unsigned realCount = 0; 130 if (!validateOffsetCount(offset, count, length(), realCount, exceptionState)) 131 return; 132 133 String newStr = m_data; 134 newStr.remove(offset, realCount); 135 136 setDataAndUpdate(newStr, offset, realCount, 0, recalcStyleBehavior); 137 138 document().didRemoveText(this, offset, realCount); 139 } 140 141 void CharacterData::replaceData(unsigned offset, unsigned count, const String& data, ExceptionState& exceptionState) 142 { 143 unsigned realCount = 0; 144 if (!validateOffsetCount(offset, count, length(), realCount, exceptionState)) 145 return; 146 147 String newStr = m_data; 148 newStr.remove(offset, realCount); 149 newStr.insert(data, offset); 150 151 setDataAndUpdate(newStr, offset, realCount, data.length()); 152 153 // update the markers for spell checking and grammar checking 154 document().didRemoveText(this, offset, realCount); 155 document().didInsertText(this, offset, data.length()); 156 } 157 158 String CharacterData::nodeValue() const 159 { 160 return m_data; 161 } 162 163 bool CharacterData::containsOnlyWhitespace() const 164 { 165 return m_data.containsOnlyWhitespace(); 166 } 167 168 void CharacterData::setNodeValue(const String& nodeValue) 169 { 170 setData(nodeValue); 171 } 172 173 void CharacterData::setDataAndUpdate(const String& newData, unsigned offsetOfReplacedData, unsigned oldLength, unsigned newLength, RecalcStyleBehavior recalcStyleBehavior) 174 { 175 String oldData = m_data; 176 m_data = newData; 177 178 ASSERT(!renderer() || isTextNode()); 179 if (isTextNode()) 180 toText(this)->updateTextRenderer(offsetOfReplacedData, oldLength, recalcStyleBehavior); 181 182 if (nodeType() == PROCESSING_INSTRUCTION_NODE) 183 toProcessingInstruction(this)->didAttributeChanged(); 184 185 if (document().frame()) 186 document().frame()->selection().didUpdateCharacterData(this, offsetOfReplacedData, oldLength, newLength); 187 188 document().incDOMTreeVersion(); 189 didModifyData(oldData); 190 } 191 192 void CharacterData::didModifyData(const String& oldData) 193 { 194 if (OwnPtrWillBeRawPtr<MutationObserverInterestGroup> mutationRecipients = MutationObserverInterestGroup::createForCharacterDataMutation(*this)) 195 mutationRecipients->enqueueMutationRecord(MutationRecord::createCharacterData(this, oldData)); 196 197 if (parentNode()) { 198 ContainerNode::ChildrenChange change = {ContainerNode::TextChanged, previousSibling(), nextSibling(), ContainerNode::ChildrenChangeSourceAPI}; 199 parentNode()->childrenChanged(change); 200 } 201 202 if (!isInShadowTree()) { 203 if (document().hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER)) 204 dispatchScopedEvent(MutationEvent::create(EventTypeNames::DOMCharacterDataModified, true, nullptr, oldData, m_data)); 205 dispatchSubtreeModifiedEvent(); 206 } 207 InspectorInstrumentation::characterDataModified(this); 208 } 209 210 int CharacterData::maxCharacterOffset() const 211 { 212 return static_cast<int>(length()); 213 } 214 215 bool CharacterData::offsetInCharacters() const 216 { 217 return true; 218 } 219 220 } // namespace blink 221