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