Clean up ContainerNode::childrenChanged
[WebKit-https.git] / Source / WebCore / dom / CharacterData.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@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 "CharacterData.h"
24
25 #include "Document.h"
26 #include "ElementTraversal.h"
27 #include "EventNames.h"
28 #include "ExceptionCode.h"
29 #include "FrameSelection.h"
30 #include "InspectorInstrumentation.h"
31 #include "MutationEvent.h"
32 #include "MutationObserverInterestGroup.h"
33 #include "MutationRecord.h"
34 #include "RenderText.h"
35 #include "StyleInheritedData.h"
36 #include "Text.h"
37 #include "TextBreakIterator.h"
38
39 using namespace std;
40
41 namespace WebCore {
42
43 void CharacterData::setData(const String& data, ExceptionCode&)
44 {
45     const String& nonNullData = !data.isNull() ? data : emptyString();
46     if (m_data == nonNullData)
47         return;
48
49     RefPtr<CharacterData> protect = this;
50
51     unsigned oldLength = length();
52
53     setDataAndUpdate(nonNullData, 0, oldLength, nonNullData.length());
54     document().textRemoved(this, 0, oldLength);
55 }
56
57 String CharacterData::substringData(unsigned offset, unsigned count, ExceptionCode& ec)
58 {
59     checkCharDataOperation(offset, ec);
60     if (ec)
61         return String();
62
63     return m_data.substring(offset, count);
64 }
65
66 unsigned CharacterData::parserAppendData(const String& string, unsigned offset, unsigned lengthLimit)
67 {
68     unsigned oldLength = m_data.length();
69
70     ASSERT(lengthLimit >= oldLength);
71
72     unsigned characterLength = string.length() - offset;
73     unsigned characterLengthLimit = min(characterLength, lengthLimit - oldLength);
74
75     // Check that we are not on an unbreakable boundary.
76     // Some text break iterator implementations work best if the passed buffer is as small as possible,
77     // see <https://bugs.webkit.org/show_bug.cgi?id=29092>.
78     // We need at least two characters look-ahead to account for UTF-16 surrogates.
79     if (characterLengthLimit < characterLength) {
80         NonSharedCharacterBreakIterator it(string.characters() + offset, (characterLengthLimit + 2 > characterLength) ? characterLength : characterLengthLimit + 2);
81         if (!isTextBreak(it, characterLengthLimit))
82             characterLengthLimit = textBreakPreceding(it, characterLengthLimit);
83     }
84
85     if (!characterLengthLimit)
86         return 0;
87
88     if (string.is8Bit())
89         m_data.append(string.characters8() + offset, characterLengthLimit);
90     else
91         m_data.append(string.characters16() + offset, characterLengthLimit);
92
93     ASSERT(!renderer() || isTextNode());
94     if (isTextNode())
95         Style::updateTextRendererAfterContentChange(*toText(this), oldLength, 0);
96
97     document().incDOMTreeVersion();
98     // We don't call dispatchModifiedEvent here because we don't want the
99     // parser to dispatch DOM mutation events.
100     if (parentNode()) {
101         ContainerNode::ChildChange change = {
102             ContainerNode::TextChanged,
103             ElementTraversal::previousSibling(this),
104             ElementTraversal::nextSibling(this),
105             ContainerNode::ChildChangeSourceParser
106         };
107         parentNode()->childrenChanged(change);
108     }
109
110     return characterLengthLimit;
111 }
112
113 void CharacterData::appendData(const String& data, ExceptionCode&)
114 {
115     String newStr = m_data;
116     newStr.append(data);
117
118     setDataAndUpdate(newStr, m_data.length(), 0, data.length());
119
120     // FIXME: Should we call textInserted here?
121 }
122
123 void CharacterData::insertData(unsigned offset, const String& data, ExceptionCode& ec)
124 {
125     checkCharDataOperation(offset, ec);
126     if (ec)
127         return;
128
129     String newStr = m_data;
130     newStr.insert(data, offset);
131
132     setDataAndUpdate(newStr, offset, 0, data.length());
133
134     document().textInserted(this, offset, data.length());
135 }
136
137 void CharacterData::deleteData(unsigned offset, unsigned count, ExceptionCode& ec)
138 {
139     checkCharDataOperation(offset, ec);
140     if (ec)
141         return;
142
143     unsigned realCount;
144     if (offset + count > length())
145         realCount = length() - offset;
146     else
147         realCount = count;
148
149     String newStr = m_data;
150     newStr.remove(offset, realCount);
151
152     setDataAndUpdate(newStr, offset, count, 0);
153
154     document().textRemoved(this, offset, realCount);
155 }
156
157 void CharacterData::replaceData(unsigned offset, unsigned count, const String& data, ExceptionCode& ec)
158 {
159     checkCharDataOperation(offset, ec);
160     if (ec)
161         return;
162
163     unsigned realCount;
164     if (offset + count > length())
165         realCount = length() - offset;
166     else
167         realCount = count;
168
169     String newStr = m_data;
170     newStr.remove(offset, realCount);
171     newStr.insert(data, offset);
172
173     setDataAndUpdate(newStr, offset, count, data.length());
174
175     // update the markers for spell checking and grammar checking
176     document().textRemoved(this, offset, realCount);
177     document().textInserted(this, offset, data.length());
178 }
179
180 String CharacterData::nodeValue() const
181 {
182     return m_data;
183 }
184
185 bool CharacterData::containsOnlyWhitespace() const
186 {
187     return m_data.containsOnlyWhitespace();
188 }
189
190 void CharacterData::setNodeValue(const String& nodeValue, ExceptionCode& ec)
191 {
192     setData(nodeValue, ec);
193 }
194
195 void CharacterData::setDataAndUpdate(const String& newData, unsigned offsetOfReplacedData, unsigned oldLength, unsigned newLength)
196 {
197     String oldData = m_data;
198     m_data = newData;
199
200     ASSERT(!renderer() || isTextNode());
201     if (isTextNode())
202         Style::updateTextRendererAfterContentChange(*toText(this), offsetOfReplacedData, oldLength);
203
204     if (document().frame())
205         document().frame()->selection().textWasReplaced(this, offsetOfReplacedData, oldLength, newLength);
206
207     document().incDOMTreeVersion();
208     dispatchModifiedEvent(oldData);
209 }
210
211 void CharacterData::dispatchModifiedEvent(const String& oldData)
212 {
213     if (OwnPtr<MutationObserverInterestGroup> mutationRecipients = MutationObserverInterestGroup::createForCharacterDataMutation(this))
214         mutationRecipients->enqueueMutationRecord(MutationRecord::createCharacterData(this, oldData));
215     if (!isInShadowTree()) {
216         if (parentNode()) {
217             ContainerNode::ChildChange change = {
218                 ContainerNode::TextChanged,
219                 ElementTraversal::previousSibling(this),
220                 ElementTraversal::nextSibling(this),
221                 ContainerNode::ChildChangeSourceAPI
222             };
223             parentNode()->childrenChanged(change);
224         }
225         if (document().hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER))
226             dispatchScopedEvent(MutationEvent::create(eventNames().DOMCharacterDataModifiedEvent, true, 0, oldData, m_data));
227         dispatchSubtreeModifiedEvent();
228     }
229 #if ENABLE(INSPECTOR)
230     InspectorInstrumentation::characterDataModified(&document(), this);
231 #endif
232 }
233
234 void CharacterData::checkCharDataOperation(unsigned offset, ExceptionCode& ec)
235 {
236     ec = 0;
237
238     // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than the number of 16-bit
239     // units in data.
240     if (offset > length()) {
241         ec = INDEX_SIZE_ERR;
242         return;
243     }
244 }
245
246 int CharacterData::maxCharacterOffset() const
247 {
248     return static_cast<int>(length());
249 }
250
251 bool CharacterData::offsetInCharacters() const
252 {
253     return true;
254 }
255
256 } // namespace WebCore