/src/mozilla-central/dom/base/CharacterData.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | /* |
8 | | * Base class for DOM Core's Comment, DocumentType, Text, |
9 | | * CDATASection and ProcessingInstruction nodes. |
10 | | */ |
11 | | |
12 | | #include "mozilla/dom/CharacterData.h" |
13 | | |
14 | | #include "mozilla/DebugOnly.h" |
15 | | |
16 | | #include "mozilla/AsyncEventDispatcher.h" |
17 | | #include "mozilla/MemoryReporting.h" |
18 | | #include "mozilla/dom/Element.h" |
19 | | #include "mozilla/dom/HTMLSlotElement.h" |
20 | | #include "mozilla/dom/ShadowRoot.h" |
21 | | #include "nsIDocument.h" |
22 | | #include "nsReadableUtils.h" |
23 | | #include "mozilla/InternalMutationEvent.h" |
24 | | #include "nsIURI.h" |
25 | | #include "nsCOMPtr.h" |
26 | | #include "nsDOMString.h" |
27 | | #include "nsChangeHint.h" |
28 | | #include "nsCOMArray.h" |
29 | | #include "nsNodeUtils.h" |
30 | | #include "mozilla/dom/DirectionalityUtils.h" |
31 | | #include "nsBindingManager.h" |
32 | | #include "nsCCUncollectableMarker.h" |
33 | | #include "mozAutoDocUpdate.h" |
34 | | #include "nsTextNode.h" |
35 | | #include "nsBidiUtils.h" |
36 | | #include "PLDHashTable.h" |
37 | | #include "mozilla/Sprintf.h" |
38 | | #include "nsWindowSizes.h" |
39 | | #include "nsWrapperCacheInlines.h" |
40 | | |
41 | | namespace mozilla { |
42 | | namespace dom { |
43 | | |
44 | | CharacterData::CharacterData(already_AddRefed<dom::NodeInfo>&& aNodeInfo) |
45 | | : nsIContent(std::move(aNodeInfo)) |
46 | 0 | { |
47 | 0 | MOZ_ASSERT(mNodeInfo->NodeType() == TEXT_NODE || |
48 | 0 | mNodeInfo->NodeType() == CDATA_SECTION_NODE || |
49 | 0 | mNodeInfo->NodeType() == COMMENT_NODE || |
50 | 0 | mNodeInfo->NodeType() == PROCESSING_INSTRUCTION_NODE || |
51 | 0 | mNodeInfo->NodeType() == DOCUMENT_TYPE_NODE, |
52 | 0 | "Bad NodeType in aNodeInfo"); |
53 | 0 | } |
54 | | |
55 | | CharacterData::~CharacterData() |
56 | 0 | { |
57 | 0 | MOZ_ASSERT(!IsInUncomposedDoc(), |
58 | 0 | "Please remove this from the document properly"); |
59 | 0 | if (GetParent()) { |
60 | 0 | NS_RELEASE(mParent); |
61 | 0 | } |
62 | 0 | } |
63 | | |
64 | | NS_IMPL_CYCLE_COLLECTION_CLASS(CharacterData) |
65 | | |
66 | | NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(CharacterData) |
67 | | |
68 | 0 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(CharacterData) |
69 | 0 | return Element::CanSkip(tmp, aRemovingAllowed); |
70 | 0 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END |
71 | | |
72 | 0 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(CharacterData) |
73 | 0 | return Element::CanSkipInCC(tmp); |
74 | 0 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END |
75 | | |
76 | 0 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(CharacterData) |
77 | 0 | return Element::CanSkipThis(tmp); |
78 | 0 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END |
79 | | |
80 | | // We purposefully don't TRAVERSE_BEGIN_INHERITED here. All the bits |
81 | | // we should traverse should be added here or in nsINode::Traverse. |
82 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(CharacterData) |
83 | 0 | if (MOZ_UNLIKELY(cb.WantDebugInfo())) { |
84 | 0 | char name[40]; |
85 | 0 | SprintfLiteral(name, "CharacterData (len=%d)", |
86 | 0 | tmp->mText.GetLength()); |
87 | 0 | cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name); |
88 | 0 | } else { |
89 | 0 | NS_IMPL_CYCLE_COLLECTION_DESCRIBE(CharacterData, tmp->mRefCnt.get()) |
90 | 0 | } |
91 | 0 |
|
92 | 0 | if (!nsIContent::Traverse(tmp, cb)) { |
93 | 0 | return NS_SUCCESS_INTERRUPTED_TRAVERSE; |
94 | 0 | } |
95 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
96 | 0 |
|
97 | 0 | // We purposefully don't UNLINK_BEGIN_INHERITED here. |
98 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CharacterData) |
99 | 0 | nsIContent::Unlink(tmp); |
100 | 0 |
|
101 | 0 | // Clear flag here because unlinking slots will clear the |
102 | 0 | // containing shadow root pointer. |
103 | 0 | tmp->UnsetFlags(NODE_IS_IN_SHADOW_TREE); |
104 | 0 |
|
105 | 0 | nsContentSlots* slots = tmp->GetExistingContentSlots(); |
106 | 0 | if (slots) { |
107 | 0 | slots->Unlink(); |
108 | 0 | } |
109 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
110 | | |
111 | 0 | NS_INTERFACE_MAP_BEGIN(CharacterData) |
112 | 0 | NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(CharacterData) |
113 | 0 | NS_INTERFACE_MAP_END_INHERITING(nsIContent) |
114 | | |
115 | | void |
116 | | CharacterData::GetNodeValueInternal(nsAString& aNodeValue) |
117 | 0 | { |
118 | 0 | GetData(aNodeValue); |
119 | 0 | } |
120 | | |
121 | | void |
122 | | CharacterData::SetNodeValueInternal(const nsAString& aNodeValue, |
123 | | ErrorResult& aError) |
124 | 0 | { |
125 | 0 | aError = SetTextInternal(0, mText.GetLength(), aNodeValue.BeginReading(), |
126 | 0 | aNodeValue.Length(), true); |
127 | 0 | } |
128 | | |
129 | | //---------------------------------------------------------------------- |
130 | | |
131 | | // Implementation of CharacterData |
132 | | |
133 | | void |
134 | | CharacterData::GetData(nsAString& aData) const |
135 | 0 | { |
136 | 0 | if (mText.Is2b()) { |
137 | 0 | aData.Truncate(); |
138 | 0 | mText.AppendTo(aData); |
139 | 0 | } else { |
140 | 0 | // Must use Substring() since nsDependentCString() requires null |
141 | 0 | // terminated strings. |
142 | 0 |
|
143 | 0 | const char *data = mText.Get1b(); |
144 | 0 |
|
145 | 0 | if (data) { |
146 | 0 | CopyASCIItoUTF16(Substring(data, data + mText.GetLength()), aData); |
147 | 0 | } else { |
148 | 0 | aData.Truncate(); |
149 | 0 | } |
150 | 0 | } |
151 | 0 | } |
152 | | |
153 | | void |
154 | | CharacterData::SetData(const nsAString& aData, ErrorResult& aRv) |
155 | 0 | { |
156 | 0 | nsresult rv = SetTextInternal(0, mText.GetLength(), aData.BeginReading(), |
157 | 0 | aData.Length(), true); |
158 | 0 | if (NS_FAILED(rv)) { |
159 | 0 | aRv.Throw(rv); |
160 | 0 | } |
161 | 0 | } |
162 | | |
163 | | void |
164 | | CharacterData::SubstringData(uint32_t aStart, uint32_t aCount, |
165 | | nsAString& aReturn, ErrorResult& rv) |
166 | 0 | { |
167 | 0 | aReturn.Truncate(); |
168 | 0 |
|
169 | 0 | uint32_t textLength = mText.GetLength(); |
170 | 0 | if (aStart > textLength) { |
171 | 0 | rv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); |
172 | 0 | return; |
173 | 0 | } |
174 | 0 | |
175 | 0 | uint32_t amount = aCount; |
176 | 0 | if (amount > textLength - aStart) { |
177 | 0 | amount = textLength - aStart; |
178 | 0 | } |
179 | 0 |
|
180 | 0 | if (mText.Is2b()) { |
181 | 0 | aReturn.Assign(mText.Get2b() + aStart, amount); |
182 | 0 | } else { |
183 | 0 | // Must use Substring() since nsDependentCString() requires null |
184 | 0 | // terminated strings. |
185 | 0 |
|
186 | 0 | const char *data = mText.Get1b() + aStart; |
187 | 0 | CopyASCIItoUTF16(Substring(data, data + amount), aReturn); |
188 | 0 | } |
189 | 0 | } |
190 | | |
191 | | //---------------------------------------------------------------------- |
192 | | |
193 | | void |
194 | | CharacterData::AppendData(const nsAString& aData, ErrorResult& aRv) |
195 | 0 | { |
196 | 0 | InsertData(mText.GetLength(), aData, aRv); |
197 | 0 | } |
198 | | |
199 | | void |
200 | | CharacterData::InsertData(uint32_t aOffset, |
201 | | const nsAString& aData, |
202 | | ErrorResult& aRv) |
203 | 0 | { |
204 | 0 | nsresult rv = SetTextInternal(aOffset, 0, aData.BeginReading(), |
205 | 0 | aData.Length(), true); |
206 | 0 | if (NS_FAILED(rv)) { |
207 | 0 | aRv.Throw(rv); |
208 | 0 | } |
209 | 0 | } |
210 | | |
211 | | void |
212 | | CharacterData::DeleteData(uint32_t aOffset, uint32_t aCount, ErrorResult& aRv) |
213 | 0 | { |
214 | 0 | nsresult rv = SetTextInternal(aOffset, aCount, nullptr, 0, true); |
215 | 0 | if (NS_FAILED(rv)) { |
216 | 0 | aRv.Throw(rv); |
217 | 0 | } |
218 | 0 | } |
219 | | |
220 | | void |
221 | | CharacterData::ReplaceData(uint32_t aOffset, uint32_t aCount, |
222 | | const nsAString& aData, ErrorResult& aRv) |
223 | 0 | { |
224 | 0 | nsresult rv = SetTextInternal(aOffset, aCount, aData.BeginReading(), |
225 | 0 | aData.Length(), true); |
226 | 0 | if (NS_FAILED(rv)) { |
227 | 0 | aRv.Throw(rv); |
228 | 0 | } |
229 | 0 | } |
230 | | |
231 | | nsresult |
232 | | CharacterData::SetTextInternal(uint32_t aOffset, uint32_t aCount, |
233 | | const char16_t* aBuffer, |
234 | | uint32_t aLength, bool aNotify, |
235 | | CharacterDataChangeInfo::Details* aDetails) |
236 | 0 | { |
237 | 0 | MOZ_ASSERT(aBuffer || !aLength, |
238 | 0 | "Null buffer passed to SetTextInternal!"); |
239 | 0 |
|
240 | 0 | // sanitize arguments |
241 | 0 | uint32_t textLength = mText.GetLength(); |
242 | 0 | if (aOffset > textLength) { |
243 | 0 | return NS_ERROR_DOM_INDEX_SIZE_ERR; |
244 | 0 | } |
245 | 0 | |
246 | 0 | if (aCount > textLength - aOffset) { |
247 | 0 | aCount = textLength - aOffset; |
248 | 0 | } |
249 | 0 |
|
250 | 0 | uint32_t endOffset = aOffset + aCount; |
251 | 0 |
|
252 | 0 | // Make sure the text fragment can hold the new data. |
253 | 0 | if (aLength > aCount && !mText.CanGrowBy(aLength - aCount)) { |
254 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
255 | 0 | } |
256 | 0 | |
257 | 0 | nsIDocument *document = GetComposedDoc(); |
258 | 0 | mozAutoDocUpdate updateBatch(document, aNotify); |
259 | 0 |
|
260 | 0 | bool haveMutationListeners = aNotify && |
261 | 0 | nsContentUtils::HasMutationListeners(this, |
262 | 0 | NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED, |
263 | 0 | this); |
264 | 0 |
|
265 | 0 | RefPtr<nsAtom> oldValue; |
266 | 0 | if (haveMutationListeners) { |
267 | 0 | oldValue = GetCurrentValueAtom(); |
268 | 0 | } |
269 | 0 |
|
270 | 0 | if (aNotify) { |
271 | 0 | CharacterDataChangeInfo info = { |
272 | 0 | aOffset == textLength, |
273 | 0 | aOffset, |
274 | 0 | endOffset, |
275 | 0 | aLength, |
276 | 0 | aDetails |
277 | 0 | }; |
278 | 0 | nsNodeUtils::CharacterDataWillChange(this, info); |
279 | 0 | } |
280 | 0 |
|
281 | 0 | Directionality oldDir = eDir_NotSet; |
282 | 0 | bool dirAffectsAncestor = (NodeType() == TEXT_NODE && |
283 | 0 | TextNodeWillChangeDirection(this, &oldDir, aOffset)); |
284 | 0 |
|
285 | 0 | if (aOffset == 0 && endOffset == textLength) { |
286 | 0 | // Replacing whole text or old text was empty. Don't bother to check for |
287 | 0 | // bidi in this string if the document already has bidi enabled. |
288 | 0 | // If this is marked as "maybe modified frequently", the text should be |
289 | 0 | // stored as char16_t since converting char* to char16_t* is expensive. |
290 | 0 | bool ok = |
291 | 0 | mText.SetTo(aBuffer, aLength, !document || !document->GetBidiEnabled(), |
292 | 0 | HasFlag(NS_MAYBE_MODIFIED_FREQUENTLY)); |
293 | 0 | NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); |
294 | 0 | } |
295 | 0 | else if (aOffset == textLength) { |
296 | 0 | // Appending to existing |
297 | 0 | bool ok = |
298 | 0 | mText.Append(aBuffer, aLength, !document || !document->GetBidiEnabled(), |
299 | 0 | HasFlag(NS_MAYBE_MODIFIED_FREQUENTLY)); |
300 | 0 | NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); |
301 | 0 | } |
302 | 0 | else { |
303 | 0 | // Merging old and new |
304 | 0 |
|
305 | 0 | bool bidi = mText.IsBidi(); |
306 | 0 |
|
307 | 0 | // Allocate new buffer |
308 | 0 | int32_t newLength = textLength - aCount + aLength; |
309 | 0 | // Use nsString and not nsAutoString so that we get a nsStringBuffer which |
310 | 0 | // can be just AddRefed in nsTextFragment. |
311 | 0 | nsString to; |
312 | 0 | to.SetCapacity(newLength); |
313 | 0 |
|
314 | 0 | // Copy over appropriate data |
315 | 0 | if (aOffset) { |
316 | 0 | mText.AppendTo(to, 0, aOffset); |
317 | 0 | } |
318 | 0 | if (aLength) { |
319 | 0 | to.Append(aBuffer, aLength); |
320 | 0 | if (!bidi && (!document || !document->GetBidiEnabled())) { |
321 | 0 | bidi = HasRTLChars(MakeSpan(aBuffer, aLength)); |
322 | 0 | } |
323 | 0 | } |
324 | 0 | if (endOffset != textLength) { |
325 | 0 | mText.AppendTo(to, endOffset, textLength - endOffset); |
326 | 0 | } |
327 | 0 |
|
328 | 0 | // If this is marked as "maybe modified frequently", the text should be |
329 | 0 | // stored as char16_t since converting char* to char16_t* is expensive. |
330 | 0 | // Use char16_t also when we have bidi characters. |
331 | 0 | bool use2b = HasFlag(NS_MAYBE_MODIFIED_FREQUENTLY) || bidi; |
332 | 0 | bool ok = mText.SetTo(to, false, use2b); |
333 | 0 | mText.SetBidi(bidi); |
334 | 0 |
|
335 | 0 | NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); |
336 | 0 | } |
337 | 0 |
|
338 | 0 | UnsetFlags(NS_CACHED_TEXT_IS_ONLY_WHITESPACE); |
339 | 0 |
|
340 | 0 | if (document && mText.IsBidi()) { |
341 | 0 | // If we found bidi characters in mText.SetTo() above, indicate that the |
342 | 0 | // document contains bidi characters. |
343 | 0 | document->SetBidiEnabled(); |
344 | 0 | } |
345 | 0 |
|
346 | 0 | if (dirAffectsAncestor) { |
347 | 0 | // dirAffectsAncestor being true implies that we have a text node, see |
348 | 0 | // above. |
349 | 0 | MOZ_ASSERT(NodeType() == TEXT_NODE); |
350 | 0 | TextNodeChangedDirection(static_cast<nsTextNode*>(this), oldDir, aNotify); |
351 | 0 | } |
352 | 0 |
|
353 | 0 | // Notify observers |
354 | 0 | if (aNotify) { |
355 | 0 | CharacterDataChangeInfo info = { |
356 | 0 | aOffset == textLength, |
357 | 0 | aOffset, |
358 | 0 | endOffset, |
359 | 0 | aLength, |
360 | 0 | aDetails |
361 | 0 | }; |
362 | 0 | nsNodeUtils::CharacterDataChanged(this, info); |
363 | 0 |
|
364 | 0 | if (haveMutationListeners) { |
365 | 0 | InternalMutationEvent mutation(true, eLegacyCharacterDataModified); |
366 | 0 |
|
367 | 0 | mutation.mPrevAttrValue = oldValue; |
368 | 0 | if (aLength > 0) { |
369 | 0 | nsAutoString val; |
370 | 0 | mText.AppendTo(val); |
371 | 0 | mutation.mNewAttrValue = NS_Atomize(val); |
372 | 0 | } |
373 | 0 |
|
374 | 0 | mozAutoSubtreeModified subtree(OwnerDoc(), this); |
375 | 0 | (new AsyncEventDispatcher(this, mutation))->RunDOMEventWhenSafe(); |
376 | 0 | } |
377 | 0 | } |
378 | 0 |
|
379 | 0 | return NS_OK; |
380 | 0 | } |
381 | | |
382 | | //---------------------------------------------------------------------- |
383 | | |
384 | | // Implementation of nsIContent |
385 | | |
386 | | #ifdef DEBUG |
387 | | void |
388 | | CharacterData::ToCString(nsAString& aBuf, int32_t aOffset, |
389 | | int32_t aLen) const |
390 | | { |
391 | | if (mText.Is2b()) { |
392 | | const char16_t* cp = mText.Get2b() + aOffset; |
393 | | const char16_t* end = cp + aLen; |
394 | | |
395 | | while (cp < end) { |
396 | | char16_t ch = *cp++; |
397 | | if (ch == '&') { |
398 | | aBuf.AppendLiteral("&"); |
399 | | } else if (ch == '<') { |
400 | | aBuf.AppendLiteral("<"); |
401 | | } else if (ch == '>') { |
402 | | aBuf.AppendLiteral(">"); |
403 | | } else if ((ch < ' ') || (ch >= 127)) { |
404 | | aBuf.AppendPrintf("\\u%04x", ch); |
405 | | } else { |
406 | | aBuf.Append(ch); |
407 | | } |
408 | | } |
409 | | } else { |
410 | | unsigned char* cp = (unsigned char*)mText.Get1b() + aOffset; |
411 | | const unsigned char* end = cp + aLen; |
412 | | |
413 | | while (cp < end) { |
414 | | char16_t ch = *cp++; |
415 | | if (ch == '&') { |
416 | | aBuf.AppendLiteral("&"); |
417 | | } else if (ch == '<') { |
418 | | aBuf.AppendLiteral("<"); |
419 | | } else if (ch == '>') { |
420 | | aBuf.AppendLiteral(">"); |
421 | | } else if ((ch < ' ') || (ch >= 127)) { |
422 | | aBuf.AppendPrintf("\\u%04x", ch); |
423 | | } else { |
424 | | aBuf.Append(ch); |
425 | | } |
426 | | } |
427 | | } |
428 | | } |
429 | | #endif |
430 | | |
431 | | |
432 | | nsresult |
433 | | CharacterData::BindToTree(nsIDocument* aDocument, |
434 | | nsIContent* aParent, |
435 | | nsIContent* aBindingParent) |
436 | 0 | { |
437 | 0 | MOZ_ASSERT(aParent || aDocument, "Must have document if no parent!"); |
438 | 0 | MOZ_ASSERT(NODE_FROM(aParent, aDocument)->OwnerDoc() == OwnerDoc(), |
439 | 0 | "Must have the same owner document"); |
440 | 0 | MOZ_ASSERT(!aParent || aDocument == aParent->GetUncomposedDoc(), |
441 | 0 | "aDocument must be current doc of aParent"); |
442 | 0 | MOZ_ASSERT(!GetUncomposedDoc() && !IsInUncomposedDoc(), |
443 | 0 | "Already have a document. Unbind first!"); |
444 | 0 | MOZ_ASSERT(!IsInComposedDoc(), |
445 | 0 | "Already have a document. Unbind first!"); |
446 | 0 | // Note that as we recurse into the kids, they'll have a non-null parent. So |
447 | 0 | // only assert if our parent is _changing_ while we have a parent. |
448 | 0 | MOZ_ASSERT(!GetParent() || aParent == GetParent(), |
449 | 0 | "Already have a parent. Unbind first!"); |
450 | 0 | MOZ_ASSERT(!GetBindingParent() || |
451 | 0 | aBindingParent == GetBindingParent() || |
452 | 0 | (!aBindingParent && aParent && |
453 | 0 | aParent->GetBindingParent() == GetBindingParent()), |
454 | 0 | "Already have a binding parent. Unbind first!"); |
455 | 0 | MOZ_ASSERT(aBindingParent != this, |
456 | 0 | "Content must not be its own binding parent"); |
457 | 0 | MOZ_ASSERT(!IsRootOfNativeAnonymousSubtree() || |
458 | 0 | aBindingParent == aParent, |
459 | 0 | "Native anonymous content must have its parent as its " |
460 | 0 | "own binding parent"); |
461 | 0 |
|
462 | 0 | if (!aBindingParent && aParent) { |
463 | 0 | aBindingParent = aParent->GetBindingParent(); |
464 | 0 | } |
465 | 0 |
|
466 | 0 | // First set the binding parent |
467 | 0 | if (aBindingParent) { |
468 | 0 | NS_ASSERTION(IsRootOfNativeAnonymousSubtree() || |
469 | 0 | !HasFlag(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE) || |
470 | 0 | (aParent && aParent->IsInNativeAnonymousSubtree()), |
471 | 0 | "Trying to re-bind content from native anonymous subtree to " |
472 | 0 | "non-native anonymous parent!"); |
473 | 0 | ExtendedContentSlots()->mBindingParent = aBindingParent; // Weak, so no addref happens. |
474 | 0 | if (aParent->IsInNativeAnonymousSubtree()) { |
475 | 0 | SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE); |
476 | 0 | } |
477 | 0 | if (aParent->HasFlag(NODE_CHROME_ONLY_ACCESS)) { |
478 | 0 | SetFlags(NODE_CHROME_ONLY_ACCESS); |
479 | 0 | } |
480 | 0 | if (HasFlag(NODE_IS_ANONYMOUS_ROOT)) { |
481 | 0 | aParent->SetMayHaveAnonymousChildren(); |
482 | 0 | } |
483 | 0 | } |
484 | 0 |
|
485 | 0 | if (aParent && aParent->IsInShadowTree()) { |
486 | 0 | ClearSubtreeRootPointer(); |
487 | 0 | SetFlags(NODE_IS_IN_SHADOW_TREE); |
488 | 0 | SetIsConnected(aParent->IsInComposedDoc()); |
489 | 0 | MOZ_ASSERT(aParent->GetContainingShadow()); |
490 | 0 | ExtendedContentSlots()->mContainingShadow = aParent->GetContainingShadow(); |
491 | 0 | } |
492 | 0 |
|
493 | 0 | bool hadParent = !!GetParentNode(); |
494 | 0 |
|
495 | 0 | // Set parent |
496 | 0 | if (aParent) { |
497 | 0 | if (!GetParent()) { |
498 | 0 | NS_ADDREF(aParent); |
499 | 0 | } |
500 | 0 | mParent = aParent; |
501 | 0 | } else { |
502 | 0 | mParent = aDocument; |
503 | 0 | } |
504 | 0 | SetParentIsContent(aParent); |
505 | 0 |
|
506 | 0 | // XXXbz sXBL/XBL2 issue! |
507 | 0 |
|
508 | 0 | // Set document |
509 | 0 | if (aDocument) { |
510 | 0 | // We no longer need to track the subtree pointer (and in fact we'll assert |
511 | 0 | // if we do this any later). |
512 | 0 | ClearSubtreeRootPointer(); |
513 | 0 |
|
514 | 0 | // XXX See the comment in Element::BindToTree |
515 | 0 | SetIsInDocument(); |
516 | 0 | SetIsConnected(true); |
517 | 0 | if (mText.IsBidi()) { |
518 | 0 | aDocument->SetBidiEnabled(); |
519 | 0 | } |
520 | 0 | // Clear the lazy frame construction bits. |
521 | 0 | UnsetFlags(NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES); |
522 | 0 | } else if (!IsInShadowTree()) { |
523 | 0 | // If we're not in the doc and not in a shadow tree, |
524 | 0 | // update our subtree pointer. |
525 | 0 | SetSubtreeRootPointer(aParent->SubtreeRoot()); |
526 | 0 | } |
527 | 0 |
|
528 | 0 | nsNodeUtils::ParentChainChanged(this); |
529 | 0 | if (!hadParent && IsRootOfNativeAnonymousSubtree()) { |
530 | 0 | nsNodeUtils::NativeAnonymousChildListChange(this, false); |
531 | 0 | } |
532 | 0 |
|
533 | 0 | UpdateEditableState(false); |
534 | 0 |
|
535 | 0 | MOZ_ASSERT(aDocument == GetUncomposedDoc(), "Bound to wrong document"); |
536 | 0 | MOZ_ASSERT(aParent == GetParent(), "Bound to wrong parent"); |
537 | 0 | MOZ_ASSERT(aBindingParent == GetBindingParent(), |
538 | 0 | "Bound to wrong binding parent"); |
539 | 0 |
|
540 | 0 | return NS_OK; |
541 | 0 | } |
542 | | |
543 | | void |
544 | | CharacterData::UnbindFromTree(bool aDeep, bool aNullParent) |
545 | 0 | { |
546 | 0 | // Unset frame flags; if we need them again later, they'll get set again. |
547 | 0 | UnsetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE | NS_REFRAME_IF_WHITESPACE); |
548 | 0 |
|
549 | 0 | nsIDocument* document = GetComposedDoc(); |
550 | 0 |
|
551 | 0 | if (aNullParent) { |
552 | 0 | if (this->IsRootOfNativeAnonymousSubtree()) { |
553 | 0 | nsNodeUtils::NativeAnonymousChildListChange(this, true); |
554 | 0 | } |
555 | 0 | if (GetParent()) { |
556 | 0 | NS_RELEASE(mParent); |
557 | 0 | } else { |
558 | 0 | mParent = nullptr; |
559 | 0 | } |
560 | 0 | SetParentIsContent(false); |
561 | 0 | } |
562 | 0 | ClearInDocument(); |
563 | 0 | SetIsConnected(false); |
564 | 0 |
|
565 | 0 | if (aNullParent || !mParent->IsInShadowTree()) { |
566 | 0 | UnsetFlags(NODE_IS_IN_SHADOW_TREE); |
567 | 0 |
|
568 | 0 | // Begin keeping track of our subtree root. |
569 | 0 | SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot()); |
570 | 0 | } |
571 | 0 |
|
572 | 0 | if (document && !GetContainingShadow()) { |
573 | 0 | // Notify XBL- & nsIAnonymousContentCreator-generated |
574 | 0 | // anonymous content that the document is changing. |
575 | 0 | // Unlike XBL, bindings for web components shadow DOM |
576 | 0 | // do not get uninstalled. |
577 | 0 | if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) { |
578 | 0 | nsContentUtils::AddScriptRunner( |
579 | 0 | new RemoveFromBindingManagerRunnable(document->BindingManager(), this, |
580 | 0 | document)); |
581 | 0 | } |
582 | 0 | } |
583 | 0 |
|
584 | 0 | nsExtendedContentSlots* slots = GetExistingExtendedContentSlots(); |
585 | 0 | if (slots) { |
586 | 0 | slots->mBindingParent = nullptr; |
587 | 0 | if (aNullParent || !mParent->IsInShadowTree()) { |
588 | 0 | slots->mContainingShadow = nullptr; |
589 | 0 | } |
590 | 0 | } |
591 | 0 |
|
592 | 0 | nsNodeUtils::ParentChainChanged(this); |
593 | 0 | } |
594 | | |
595 | | //---------------------------------------------------------------------- |
596 | | |
597 | | // Implementation of the nsIContent interface text functions |
598 | | |
599 | | nsresult |
600 | | CharacterData::SetText(const char16_t* aBuffer, |
601 | | uint32_t aLength, |
602 | | bool aNotify) |
603 | 0 | { |
604 | 0 | return SetTextInternal(0, mText.GetLength(), aBuffer, aLength, aNotify); |
605 | 0 | } |
606 | | |
607 | | nsresult |
608 | | CharacterData::AppendText(const char16_t* aBuffer, |
609 | | uint32_t aLength, |
610 | | bool aNotify) |
611 | 0 | { |
612 | 0 | return SetTextInternal(mText.GetLength(), 0, aBuffer, aLength, aNotify); |
613 | 0 | } |
614 | | |
615 | | bool |
616 | | CharacterData::TextIsOnlyWhitespace() |
617 | 0 | { |
618 | 0 |
|
619 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
620 | 0 | if (!ThreadSafeTextIsOnlyWhitespace()) { |
621 | 0 | UnsetFlags(NS_TEXT_IS_ONLY_WHITESPACE); |
622 | 0 | SetFlags(NS_CACHED_TEXT_IS_ONLY_WHITESPACE); |
623 | 0 | return false; |
624 | 0 | } |
625 | 0 | |
626 | 0 | SetFlags(NS_CACHED_TEXT_IS_ONLY_WHITESPACE | NS_TEXT_IS_ONLY_WHITESPACE); |
627 | 0 | return true; |
628 | 0 | } |
629 | | |
630 | | bool |
631 | | CharacterData::ThreadSafeTextIsOnlyWhitespace() const |
632 | 0 | { |
633 | 0 | // FIXME: should this method take content language into account? |
634 | 0 | if (mText.Is2b()) { |
635 | 0 | // The fragment contains non-8bit characters and such characters |
636 | 0 | // are never considered whitespace. |
637 | 0 | // |
638 | 0 | // FIXME(emilio): This is not quite true in presence of the |
639 | 0 | // NS_MAYBE_MODIFIED_FREQUENTLY flag... But looks like we only set that on |
640 | 0 | // anonymous nodes, so should be fine... |
641 | 0 | return false; |
642 | 0 | } |
643 | 0 | |
644 | 0 | if (HasFlag(NS_CACHED_TEXT_IS_ONLY_WHITESPACE)) { |
645 | 0 | return HasFlag(NS_TEXT_IS_ONLY_WHITESPACE); |
646 | 0 | } |
647 | 0 | |
648 | 0 | const char* cp = mText.Get1b(); |
649 | 0 | const char* end = cp + mText.GetLength(); |
650 | 0 |
|
651 | 0 | while (cp < end) { |
652 | 0 | char ch = *cp; |
653 | 0 |
|
654 | 0 | // NOTE(emilio): If you ever change the definition of "whitespace" here, you |
655 | 0 | // need to change it too in RestyleManager::CharacterDataChanged. |
656 | 0 | if (!dom::IsSpaceCharacter(ch)) { |
657 | 0 | return false; |
658 | 0 | } |
659 | 0 | |
660 | 0 | ++cp; |
661 | 0 | } |
662 | 0 |
|
663 | 0 | return true; |
664 | 0 | } |
665 | | |
666 | | already_AddRefed<nsAtom> |
667 | | CharacterData::GetCurrentValueAtom() |
668 | 0 | { |
669 | 0 | nsAutoString val; |
670 | 0 | GetData(val); |
671 | 0 | return NS_Atomize(val); |
672 | 0 | } |
673 | | |
674 | | void |
675 | | CharacterData::AddSizeOfExcludingThis(nsWindowSizes& aSizes, |
676 | | size_t* aNodeSize) const |
677 | 0 | { |
678 | 0 | nsIContent::AddSizeOfExcludingThis(aSizes, aNodeSize); |
679 | 0 | *aNodeSize += mText.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf); |
680 | 0 | } |
681 | | |
682 | | } // namespace dom |
683 | | } // namespace mozilla |