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 "EventNames.h"
27#include "ExceptionCode.h"
28#include "FrameSelection.h"
29#include "InspectorInstrumentation.h"
30#include "MutationEvent.h"
31#include "MutationObserverInterestGroup.h"
32#include "MutationRecord.h"
33#include "NodeRenderingContext.h"
34#include "RenderText.h"
35#include "StyleInheritedData.h"
36#include "Text.h"
37#include "TextBreakIterator.h"
38
39using namespace std;
40
41namespace WebCore {
42
43void 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
57String 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
66unsigned 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        toText(this)->updateTextRenderer(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        parentNode()->childrenChanged();
102
103    return characterLengthLimit;
104}
105
106void CharacterData::appendData(const String& data, ExceptionCode&)
107{
108    String newStr = m_data;
109    newStr.append(data);
110
111    setDataAndUpdate(newStr, m_data.length(), 0, data.length());
112
113    // FIXME: Should we call textInserted here?
114}
115
116void CharacterData::insertData(unsigned offset, const String& data, ExceptionCode& ec)
117{
118    checkCharDataOperation(offset, ec);
119    if (ec)
120        return;
121
122    String newStr = m_data;
123    newStr.insert(data, offset);
124
125    setDataAndUpdate(newStr, offset, 0, data.length());
126
127    document()->textInserted(this, offset, data.length());
128}
129
130void CharacterData::deleteData(unsigned offset, unsigned count, ExceptionCode& ec)
131{
132    checkCharDataOperation(offset, ec);
133    if (ec)
134        return;
135
136    unsigned realCount;
137    if (offset + count > length())
138        realCount = length() - offset;
139    else
140        realCount = count;
141
142    String newStr = m_data;
143    newStr.remove(offset, realCount);
144
145    setDataAndUpdate(newStr, offset, count, 0);
146
147    document()->textRemoved(this, offset, realCount);
148}
149
150void CharacterData::replaceData(unsigned offset, unsigned count, const String& data, ExceptionCode& ec)
151{
152    checkCharDataOperation(offset, ec);
153    if (ec)
154        return;
155
156    unsigned realCount;
157    if (offset + count > length())
158        realCount = length() - offset;
159    else
160        realCount = count;
161
162    String newStr = m_data;
163    newStr.remove(offset, realCount);
164    newStr.insert(data, offset);
165
166    setDataAndUpdate(newStr, offset, count, data.length());
167
168    // update the markers for spell checking and grammar checking
169    document()->textRemoved(this, offset, realCount);
170    document()->textInserted(this, offset, data.length());
171}
172
173String CharacterData::nodeValue() const
174{
175    return m_data;
176}
177
178bool CharacterData::containsOnlyWhitespace() const
179{
180    return m_data.containsOnlyWhitespace();
181}
182
183void CharacterData::setNodeValue(const String& nodeValue, ExceptionCode& ec)
184{
185    setData(nodeValue, ec);
186}
187
188void CharacterData::setDataAndUpdate(const String& newData, unsigned offsetOfReplacedData, unsigned oldLength, unsigned newLength)
189{
190    String oldData = m_data;
191    m_data = newData;
192
193    ASSERT(!renderer() || isTextNode());
194    if (isTextNode())
195        toText(this)->updateTextRenderer(offsetOfReplacedData, oldLength);
196
197    if (document()->frame())
198        document()->frame()->selection()->textWasReplaced(this, offsetOfReplacedData, oldLength, newLength);
199
200    document()->incDOMTreeVersion();
201    dispatchModifiedEvent(oldData);
202}
203
204void CharacterData::dispatchModifiedEvent(const String& oldData)
205{
206    if (OwnPtr<MutationObserverInterestGroup> mutationRecipients = MutationObserverInterestGroup::createForCharacterDataMutation(this))
207        mutationRecipients->enqueueMutationRecord(MutationRecord::createCharacterData(this, oldData));
208    if (!isInShadowTree()) {
209        if (parentNode())
210            parentNode()->childrenChanged();
211        if (document()->hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER))
212            dispatchScopedEvent(MutationEvent::create(eventNames().DOMCharacterDataModifiedEvent, true, 0, oldData, m_data));
213        dispatchSubtreeModifiedEvent();
214    }
215#if ENABLE(INSPECTOR)
216    InspectorInstrumentation::characterDataModified(document(), this);
217#endif
218}
219
220void CharacterData::checkCharDataOperation(unsigned offset, ExceptionCode& ec)
221{
222    ec = 0;
223
224    // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than the number of 16-bit
225    // units in data.
226    if (offset > length()) {
227        ec = INDEX_SIZE_ERR;
228        return;
229    }
230}
231
232int CharacterData::maxCharacterOffset() const
233{
234    return static_cast<int>(length());
235}
236
237bool CharacterData::offsetInCharacters() const
238{
239    return true;
240}
241
242} // namespace WebCore
243