|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 /* |
|
7 * Base class for DOM Core's nsIDOMComment, nsIDOMDocumentType, nsIDOMText, |
|
8 * nsIDOMCDATASection, and nsIDOMProcessingInstruction nodes. |
|
9 */ |
|
10 |
|
11 #include "mozilla/DebugOnly.h" |
|
12 |
|
13 #include "nsGenericDOMDataNode.h" |
|
14 #include "mozilla/AsyncEventDispatcher.h" |
|
15 #include "mozilla/MemoryReporting.h" |
|
16 #include "mozilla/dom/Element.h" |
|
17 #include "mozilla/dom/ShadowRoot.h" |
|
18 #include "nsIDocument.h" |
|
19 #include "nsIDOMDocument.h" |
|
20 #include "nsReadableUtils.h" |
|
21 #include "mozilla/InternalMutationEvent.h" |
|
22 #include "nsIURI.h" |
|
23 #include "nsIDOMEvent.h" |
|
24 #include "nsIDOMText.h" |
|
25 #include "nsCOMPtr.h" |
|
26 #include "nsDOMString.h" |
|
27 #include "nsIDOMUserDataHandler.h" |
|
28 #include "nsChangeHint.h" |
|
29 #include "nsCOMArray.h" |
|
30 #include "nsNodeUtils.h" |
|
31 #include "mozilla/dom/DirectionalityUtils.h" |
|
32 #include "nsBindingManager.h" |
|
33 #include "nsCCUncollectableMarker.h" |
|
34 #include "mozAutoDocUpdate.h" |
|
35 |
|
36 #include "pldhash.h" |
|
37 #include "prprf.h" |
|
38 #include "nsWrapperCacheInlines.h" |
|
39 |
|
40 using namespace mozilla; |
|
41 using namespace mozilla::dom; |
|
42 |
|
43 nsGenericDOMDataNode::nsGenericDOMDataNode(already_AddRefed<nsINodeInfo>& aNodeInfo) |
|
44 : nsIContent(aNodeInfo) |
|
45 { |
|
46 NS_ABORT_IF_FALSE(mNodeInfo->NodeType() == nsIDOMNode::TEXT_NODE || |
|
47 mNodeInfo->NodeType() == nsIDOMNode::CDATA_SECTION_NODE || |
|
48 mNodeInfo->NodeType() == nsIDOMNode::COMMENT_NODE || |
|
49 mNodeInfo->NodeType() == |
|
50 nsIDOMNode::PROCESSING_INSTRUCTION_NODE || |
|
51 mNodeInfo->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE, |
|
52 "Bad NodeType in aNodeInfo"); |
|
53 } |
|
54 |
|
55 nsGenericDOMDataNode::nsGenericDOMDataNode(already_AddRefed<nsINodeInfo>&& aNodeInfo) |
|
56 : nsIContent(aNodeInfo) |
|
57 { |
|
58 NS_ABORT_IF_FALSE(mNodeInfo->NodeType() == nsIDOMNode::TEXT_NODE || |
|
59 mNodeInfo->NodeType() == nsIDOMNode::CDATA_SECTION_NODE || |
|
60 mNodeInfo->NodeType() == nsIDOMNode::COMMENT_NODE || |
|
61 mNodeInfo->NodeType() == |
|
62 nsIDOMNode::PROCESSING_INSTRUCTION_NODE || |
|
63 mNodeInfo->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE, |
|
64 "Bad NodeType in aNodeInfo"); |
|
65 } |
|
66 |
|
67 nsGenericDOMDataNode::~nsGenericDOMDataNode() |
|
68 { |
|
69 NS_PRECONDITION(!IsInDoc(), |
|
70 "Please remove this from the document properly"); |
|
71 if (GetParent()) { |
|
72 NS_RELEASE(mParent); |
|
73 } |
|
74 } |
|
75 |
|
76 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGenericDOMDataNode) |
|
77 |
|
78 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsGenericDOMDataNode) |
|
79 |
|
80 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGenericDOMDataNode) |
|
81 return Element::CanSkip(tmp, aRemovingAllowed); |
|
82 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END |
|
83 |
|
84 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGenericDOMDataNode) |
|
85 return Element::CanSkipInCC(tmp); |
|
86 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END |
|
87 |
|
88 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGenericDOMDataNode) |
|
89 return Element::CanSkipThis(tmp); |
|
90 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END |
|
91 |
|
92 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGenericDOMDataNode) |
|
93 // Always need to traverse script objects, so do that before we check |
|
94 // if we're uncollectable. |
|
95 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS |
|
96 |
|
97 if (!nsINode::Traverse(tmp, cb)) { |
|
98 return NS_SUCCESS_INTERRUPTED_TRAVERSE; |
|
99 } |
|
100 |
|
101 nsDataSlots *slots = tmp->GetExistingDataSlots(); |
|
102 if (slots) { |
|
103 slots->Traverse(cb); |
|
104 } |
|
105 |
|
106 tmp->OwnerDoc()->BindingManager()->Traverse(tmp, cb); |
|
107 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
108 |
|
109 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericDOMDataNode) |
|
110 nsINode::Unlink(tmp); |
|
111 |
|
112 nsDataSlots *slots = tmp->GetExistingDataSlots(); |
|
113 if (slots) { |
|
114 slots->Unlink(); |
|
115 } |
|
116 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
117 |
|
118 NS_INTERFACE_MAP_BEGIN(nsGenericDOMDataNode) |
|
119 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
|
120 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsGenericDOMDataNode) |
|
121 NS_INTERFACE_MAP_ENTRY(nsIContent) |
|
122 NS_INTERFACE_MAP_ENTRY(nsINode) |
|
123 NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget) |
|
124 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget) |
|
125 NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference, |
|
126 new nsNodeSupportsWeakRefTearoff(this)) |
|
127 NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMXPathNSResolver, |
|
128 new nsNode3Tearoff(this)) |
|
129 // DOM bindings depend on the identity pointer being the |
|
130 // same as nsINode (which nsIContent inherits). |
|
131 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent) |
|
132 NS_INTERFACE_MAP_END |
|
133 |
|
134 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGenericDOMDataNode) |
|
135 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsGenericDOMDataNode, |
|
136 nsNodeUtils::LastRelease(this)) |
|
137 |
|
138 |
|
139 void |
|
140 nsGenericDOMDataNode::GetNodeValueInternal(nsAString& aNodeValue) |
|
141 { |
|
142 DebugOnly<nsresult> rv = GetData(aNodeValue); |
|
143 NS_ASSERTION(NS_SUCCEEDED(rv), "GetData() failed!"); |
|
144 } |
|
145 |
|
146 void |
|
147 nsGenericDOMDataNode::SetNodeValueInternal(const nsAString& aNodeValue, |
|
148 ErrorResult& aError) |
|
149 { |
|
150 aError = SetTextInternal(0, mText.GetLength(), aNodeValue.BeginReading(), |
|
151 aNodeValue.Length(), true); |
|
152 } |
|
153 |
|
154 //---------------------------------------------------------------------- |
|
155 |
|
156 // Implementation of nsIDOMCharacterData |
|
157 |
|
158 nsresult |
|
159 nsGenericDOMDataNode::GetData(nsAString& aData) const |
|
160 { |
|
161 if (mText.Is2b()) { |
|
162 aData.Assign(mText.Get2b(), mText.GetLength()); |
|
163 } else { |
|
164 // Must use Substring() since nsDependentCString() requires null |
|
165 // terminated strings. |
|
166 |
|
167 const char *data = mText.Get1b(); |
|
168 |
|
169 if (data) { |
|
170 CopyASCIItoUTF16(Substring(data, data + mText.GetLength()), aData); |
|
171 } else { |
|
172 aData.Truncate(); |
|
173 } |
|
174 } |
|
175 |
|
176 return NS_OK; |
|
177 } |
|
178 |
|
179 nsresult |
|
180 nsGenericDOMDataNode::SetData(const nsAString& aData) |
|
181 { |
|
182 return SetTextInternal(0, mText.GetLength(), aData.BeginReading(), |
|
183 aData.Length(), true); |
|
184 } |
|
185 |
|
186 nsresult |
|
187 nsGenericDOMDataNode::GetLength(uint32_t* aLength) |
|
188 { |
|
189 *aLength = mText.GetLength(); |
|
190 return NS_OK; |
|
191 } |
|
192 |
|
193 nsresult |
|
194 nsGenericDOMDataNode::SubstringData(uint32_t aStart, uint32_t aCount, |
|
195 nsAString& aReturn) |
|
196 { |
|
197 ErrorResult rv; |
|
198 SubstringData(aStart, aCount, aReturn, rv); |
|
199 return rv.ErrorCode(); |
|
200 } |
|
201 |
|
202 void |
|
203 nsGenericDOMDataNode::SubstringData(uint32_t aStart, uint32_t aCount, |
|
204 nsAString& aReturn, ErrorResult& rv) |
|
205 { |
|
206 aReturn.Truncate(); |
|
207 |
|
208 uint32_t textLength = mText.GetLength(); |
|
209 if (aStart > textLength) { |
|
210 rv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); |
|
211 return; |
|
212 } |
|
213 |
|
214 uint32_t amount = aCount; |
|
215 if (amount > textLength - aStart) { |
|
216 amount = textLength - aStart; |
|
217 } |
|
218 |
|
219 if (mText.Is2b()) { |
|
220 aReturn.Assign(mText.Get2b() + aStart, amount); |
|
221 } else { |
|
222 // Must use Substring() since nsDependentCString() requires null |
|
223 // terminated strings. |
|
224 |
|
225 const char *data = mText.Get1b() + aStart; |
|
226 CopyASCIItoUTF16(Substring(data, data + amount), aReturn); |
|
227 } |
|
228 } |
|
229 |
|
230 NS_IMETHODIMP |
|
231 nsGenericDOMDataNode::MozRemove() |
|
232 { |
|
233 Remove(); |
|
234 return NS_OK; |
|
235 } |
|
236 |
|
237 //---------------------------------------------------------------------- |
|
238 |
|
239 nsresult |
|
240 nsGenericDOMDataNode::AppendData(const nsAString& aData) |
|
241 { |
|
242 return SetTextInternal(mText.GetLength(), 0, aData.BeginReading(), |
|
243 aData.Length(), true); |
|
244 } |
|
245 |
|
246 nsresult |
|
247 nsGenericDOMDataNode::InsertData(uint32_t aOffset, |
|
248 const nsAString& aData) |
|
249 { |
|
250 return SetTextInternal(aOffset, 0, aData.BeginReading(), |
|
251 aData.Length(), true); |
|
252 } |
|
253 |
|
254 nsresult |
|
255 nsGenericDOMDataNode::DeleteData(uint32_t aOffset, uint32_t aCount) |
|
256 { |
|
257 return SetTextInternal(aOffset, aCount, nullptr, 0, true); |
|
258 } |
|
259 |
|
260 nsresult |
|
261 nsGenericDOMDataNode::ReplaceData(uint32_t aOffset, uint32_t aCount, |
|
262 const nsAString& aData) |
|
263 { |
|
264 return SetTextInternal(aOffset, aCount, aData.BeginReading(), |
|
265 aData.Length(), true); |
|
266 } |
|
267 |
|
268 nsresult |
|
269 nsGenericDOMDataNode::SetTextInternal(uint32_t aOffset, uint32_t aCount, |
|
270 const char16_t* aBuffer, |
|
271 uint32_t aLength, bool aNotify, |
|
272 CharacterDataChangeInfo::Details* aDetails) |
|
273 { |
|
274 NS_PRECONDITION(aBuffer || !aLength, |
|
275 "Null buffer passed to SetTextInternal!"); |
|
276 |
|
277 // sanitize arguments |
|
278 uint32_t textLength = mText.GetLength(); |
|
279 if (aOffset > textLength) { |
|
280 return NS_ERROR_DOM_INDEX_SIZE_ERR; |
|
281 } |
|
282 |
|
283 if (aCount > textLength - aOffset) { |
|
284 aCount = textLength - aOffset; |
|
285 } |
|
286 |
|
287 uint32_t endOffset = aOffset + aCount; |
|
288 |
|
289 // Make sure the text fragment can hold the new data. |
|
290 if (aLength > aCount && !mText.CanGrowBy(aLength - aCount)) { |
|
291 return NS_ERROR_OUT_OF_MEMORY; |
|
292 } |
|
293 |
|
294 nsIDocument *document = GetCurrentDoc(); |
|
295 mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, aNotify); |
|
296 |
|
297 bool haveMutationListeners = aNotify && |
|
298 nsContentUtils::HasMutationListeners(this, |
|
299 NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED, |
|
300 this); |
|
301 |
|
302 nsCOMPtr<nsIAtom> oldValue; |
|
303 if (haveMutationListeners) { |
|
304 oldValue = GetCurrentValueAtom(); |
|
305 } |
|
306 |
|
307 if (aNotify) { |
|
308 CharacterDataChangeInfo info = { |
|
309 aOffset == textLength, |
|
310 aOffset, |
|
311 endOffset, |
|
312 aLength, |
|
313 aDetails |
|
314 }; |
|
315 nsNodeUtils::CharacterDataWillChange(this, &info); |
|
316 } |
|
317 |
|
318 Directionality oldDir = eDir_NotSet; |
|
319 bool dirAffectsAncestor = (NodeType() == nsIDOMNode::TEXT_NODE && |
|
320 TextNodeWillChangeDirection(this, &oldDir, aOffset)); |
|
321 |
|
322 if (aOffset == 0 && endOffset == textLength) { |
|
323 // Replacing whole text or old text was empty. Don't bother to check for |
|
324 // bidi in this string if the document already has bidi enabled. |
|
325 bool ok = mText.SetTo(aBuffer, aLength, !document || !document->GetBidiEnabled()); |
|
326 NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); |
|
327 } |
|
328 else if (aOffset == textLength) { |
|
329 // Appending to existing |
|
330 bool ok = mText.Append(aBuffer, aLength, !document || !document->GetBidiEnabled()); |
|
331 NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); |
|
332 } |
|
333 else { |
|
334 // Merging old and new |
|
335 |
|
336 // Allocate new buffer |
|
337 int32_t newLength = textLength - aCount + aLength; |
|
338 char16_t* to = new char16_t[newLength]; |
|
339 NS_ENSURE_TRUE(to, NS_ERROR_OUT_OF_MEMORY); |
|
340 |
|
341 // Copy over appropriate data |
|
342 if (aOffset) { |
|
343 mText.CopyTo(to, 0, aOffset); |
|
344 } |
|
345 if (aLength) { |
|
346 memcpy(to + aOffset, aBuffer, aLength * sizeof(char16_t)); |
|
347 } |
|
348 if (endOffset != textLength) { |
|
349 mText.CopyTo(to + aOffset + aLength, endOffset, textLength - endOffset); |
|
350 } |
|
351 |
|
352 bool ok = mText.SetTo(to, newLength, !document || !document->GetBidiEnabled()); |
|
353 |
|
354 delete [] to; |
|
355 |
|
356 NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); |
|
357 } |
|
358 |
|
359 UnsetFlags(NS_CACHED_TEXT_IS_ONLY_WHITESPACE); |
|
360 |
|
361 if (document && mText.IsBidi()) { |
|
362 // If we found bidi characters in mText.SetTo() above, indicate that the |
|
363 // document contains bidi characters. |
|
364 document->SetBidiEnabled(); |
|
365 } |
|
366 |
|
367 if (dirAffectsAncestor) { |
|
368 TextNodeChangedDirection(this, oldDir, aNotify); |
|
369 } |
|
370 |
|
371 // Notify observers |
|
372 if (aNotify) { |
|
373 CharacterDataChangeInfo info = { |
|
374 aOffset == textLength, |
|
375 aOffset, |
|
376 endOffset, |
|
377 aLength, |
|
378 aDetails |
|
379 }; |
|
380 nsNodeUtils::CharacterDataChanged(this, &info); |
|
381 |
|
382 if (haveMutationListeners) { |
|
383 InternalMutationEvent mutation(true, NS_MUTATION_CHARACTERDATAMODIFIED); |
|
384 |
|
385 mutation.mPrevAttrValue = oldValue; |
|
386 if (aLength > 0) { |
|
387 nsAutoString val; |
|
388 mText.AppendTo(val); |
|
389 mutation.mNewAttrValue = do_GetAtom(val); |
|
390 } |
|
391 |
|
392 mozAutoSubtreeModified subtree(OwnerDoc(), this); |
|
393 (new AsyncEventDispatcher(this, mutation))->RunDOMEventWhenSafe(); |
|
394 } |
|
395 } |
|
396 |
|
397 return NS_OK; |
|
398 } |
|
399 |
|
400 //---------------------------------------------------------------------- |
|
401 |
|
402 // Implementation of nsIContent |
|
403 |
|
404 #ifdef DEBUG |
|
405 void |
|
406 nsGenericDOMDataNode::ToCString(nsAString& aBuf, int32_t aOffset, |
|
407 int32_t aLen) const |
|
408 { |
|
409 if (mText.Is2b()) { |
|
410 const char16_t* cp = mText.Get2b() + aOffset; |
|
411 const char16_t* 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 char buf[10]; |
|
423 PR_snprintf(buf, sizeof(buf), "\\u%04x", ch); |
|
424 AppendASCIItoUTF16(buf, aBuf); |
|
425 } else { |
|
426 aBuf.Append(ch); |
|
427 } |
|
428 } |
|
429 } else { |
|
430 unsigned char* cp = (unsigned char*)mText.Get1b() + aOffset; |
|
431 const unsigned char* end = cp + aLen; |
|
432 |
|
433 while (cp < end) { |
|
434 char16_t ch = *cp++; |
|
435 if (ch == '&') { |
|
436 aBuf.AppendLiteral("&"); |
|
437 } else if (ch == '<') { |
|
438 aBuf.AppendLiteral("<"); |
|
439 } else if (ch == '>') { |
|
440 aBuf.AppendLiteral(">"); |
|
441 } else if ((ch < ' ') || (ch >= 127)) { |
|
442 char buf[10]; |
|
443 PR_snprintf(buf, sizeof(buf), "\\u%04x", ch); |
|
444 AppendASCIItoUTF16(buf, aBuf); |
|
445 } else { |
|
446 aBuf.Append(ch); |
|
447 } |
|
448 } |
|
449 } |
|
450 } |
|
451 #endif |
|
452 |
|
453 |
|
454 nsresult |
|
455 nsGenericDOMDataNode::BindToTree(nsIDocument* aDocument, nsIContent* aParent, |
|
456 nsIContent* aBindingParent, |
|
457 bool aCompileEventHandlers) |
|
458 { |
|
459 NS_PRECONDITION(aParent || aDocument, "Must have document if no parent!"); |
|
460 NS_PRECONDITION(NODE_FROM(aParent, aDocument)->OwnerDoc() == OwnerDoc(), |
|
461 "Must have the same owner document"); |
|
462 NS_PRECONDITION(!aParent || aDocument == aParent->GetCurrentDoc(), |
|
463 "aDocument must be current doc of aParent"); |
|
464 NS_PRECONDITION(!GetCurrentDoc() && !IsInDoc(), |
|
465 "Already have a document. Unbind first!"); |
|
466 // Note that as we recurse into the kids, they'll have a non-null parent. So |
|
467 // only assert if our parent is _changing_ while we have a parent. |
|
468 NS_PRECONDITION(!GetParent() || aParent == GetParent(), |
|
469 "Already have a parent. Unbind first!"); |
|
470 NS_PRECONDITION(!GetBindingParent() || |
|
471 aBindingParent == GetBindingParent() || |
|
472 (!aBindingParent && aParent && |
|
473 aParent->GetBindingParent() == GetBindingParent()), |
|
474 "Already have a binding parent. Unbind first!"); |
|
475 NS_PRECONDITION(aBindingParent != this, |
|
476 "Content must not be its own binding parent"); |
|
477 NS_PRECONDITION(!IsRootOfNativeAnonymousSubtree() || |
|
478 aBindingParent == aParent, |
|
479 "Native anonymous content must have its parent as its " |
|
480 "own binding parent"); |
|
481 |
|
482 if (!aBindingParent && aParent) { |
|
483 aBindingParent = aParent->GetBindingParent(); |
|
484 } |
|
485 |
|
486 // First set the binding parent |
|
487 if (aBindingParent) { |
|
488 NS_ASSERTION(IsRootOfNativeAnonymousSubtree() || |
|
489 !HasFlag(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE) || |
|
490 (aParent && aParent->IsInNativeAnonymousSubtree()), |
|
491 "Trying to re-bind content from native anonymous subtree to " |
|
492 "non-native anonymous parent!"); |
|
493 DataSlots()->mBindingParent = aBindingParent; // Weak, so no addref happens. |
|
494 if (aParent->IsInNativeAnonymousSubtree()) { |
|
495 SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE); |
|
496 } |
|
497 if (aParent->HasFlag(NODE_CHROME_ONLY_ACCESS)) { |
|
498 SetFlags(NODE_CHROME_ONLY_ACCESS); |
|
499 } |
|
500 if (aParent->HasFlag(NODE_IS_IN_SHADOW_TREE)) { |
|
501 SetFlags(NODE_IS_IN_SHADOW_TREE); |
|
502 } |
|
503 ShadowRoot* parentContainingShadow = aParent->GetContainingShadow(); |
|
504 if (parentContainingShadow) { |
|
505 DataSlots()->mContainingShadow = parentContainingShadow; |
|
506 } |
|
507 } |
|
508 |
|
509 // Set parent |
|
510 if (aParent) { |
|
511 if (!GetParent()) { |
|
512 NS_ADDREF(aParent); |
|
513 } |
|
514 mParent = aParent; |
|
515 } |
|
516 else { |
|
517 mParent = aDocument; |
|
518 } |
|
519 SetParentIsContent(aParent); |
|
520 |
|
521 // XXXbz sXBL/XBL2 issue! |
|
522 |
|
523 // Set document |
|
524 if (aDocument) { |
|
525 // We no longer need to track the subtree pointer (and in fact we'll assert |
|
526 // if we do this any later). |
|
527 ClearSubtreeRootPointer(); |
|
528 |
|
529 // XXX See the comment in Element::BindToTree |
|
530 SetInDocument(); |
|
531 if (mText.IsBidi()) { |
|
532 aDocument->SetBidiEnabled(); |
|
533 } |
|
534 // Clear the lazy frame construction bits. |
|
535 UnsetFlags(NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES); |
|
536 } else { |
|
537 // If we're not in the doc, update our subtree pointer. |
|
538 SetSubtreeRootPointer(aParent->SubtreeRoot()); |
|
539 } |
|
540 |
|
541 nsNodeUtils::ParentChainChanged(this); |
|
542 |
|
543 UpdateEditableState(false); |
|
544 |
|
545 NS_POSTCONDITION(aDocument == GetCurrentDoc(), "Bound to wrong document"); |
|
546 NS_POSTCONDITION(aParent == GetParent(), "Bound to wrong parent"); |
|
547 NS_POSTCONDITION(aBindingParent == GetBindingParent(), |
|
548 "Bound to wrong binding parent"); |
|
549 |
|
550 return NS_OK; |
|
551 } |
|
552 |
|
553 void |
|
554 nsGenericDOMDataNode::UnbindFromTree(bool aDeep, bool aNullParent) |
|
555 { |
|
556 // Unset frame flags; if we need them again later, they'll get set again. |
|
557 UnsetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE | |
|
558 NS_REFRAME_IF_WHITESPACE | |
|
559 // Also unset the shadow tree flag because it can |
|
560 // no longer be a descendant of a ShadowRoot. |
|
561 NODE_IS_IN_SHADOW_TREE); |
|
562 |
|
563 nsIDocument *document = GetCurrentDoc(); |
|
564 if (document) { |
|
565 // Notify XBL- & nsIAnonymousContentCreator-generated |
|
566 // anonymous content that the document is changing. |
|
567 // This is needed to update the insertion point. |
|
568 document->BindingManager()->RemovedFromDocument(this, document); |
|
569 } |
|
570 |
|
571 if (aNullParent) { |
|
572 if (GetParent()) { |
|
573 NS_RELEASE(mParent); |
|
574 } else { |
|
575 mParent = nullptr; |
|
576 } |
|
577 SetParentIsContent(false); |
|
578 } |
|
579 ClearInDocument(); |
|
580 |
|
581 // Begin keeping track of our subtree root. |
|
582 SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot()); |
|
583 |
|
584 nsDataSlots *slots = GetExistingDataSlots(); |
|
585 if (slots) { |
|
586 slots->mBindingParent = nullptr; |
|
587 slots->mContainingShadow = nullptr; |
|
588 } |
|
589 |
|
590 nsNodeUtils::ParentChainChanged(this); |
|
591 } |
|
592 |
|
593 already_AddRefed<nsINodeList> |
|
594 nsGenericDOMDataNode::GetChildren(uint32_t aFilter) |
|
595 { |
|
596 return nullptr; |
|
597 } |
|
598 |
|
599 nsIAtom * |
|
600 nsGenericDOMDataNode::GetIDAttributeName() const |
|
601 { |
|
602 return nullptr; |
|
603 } |
|
604 |
|
605 nsresult |
|
606 nsGenericDOMDataNode::SetAttr(int32_t aNameSpaceID, nsIAtom* aAttr, |
|
607 nsIAtom* aPrefix, const nsAString& aValue, |
|
608 bool aNotify) |
|
609 { |
|
610 return NS_OK; |
|
611 } |
|
612 |
|
613 nsresult |
|
614 nsGenericDOMDataNode::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttr, |
|
615 bool aNotify) |
|
616 { |
|
617 return NS_OK; |
|
618 } |
|
619 |
|
620 const nsAttrName* |
|
621 nsGenericDOMDataNode::GetAttrNameAt(uint32_t aIndex) const |
|
622 { |
|
623 return nullptr; |
|
624 } |
|
625 |
|
626 uint32_t |
|
627 nsGenericDOMDataNode::GetAttrCount() const |
|
628 { |
|
629 return 0; |
|
630 } |
|
631 |
|
632 uint32_t |
|
633 nsGenericDOMDataNode::GetChildCount() const |
|
634 { |
|
635 return 0; |
|
636 } |
|
637 |
|
638 nsIContent * |
|
639 nsGenericDOMDataNode::GetChildAt(uint32_t aIndex) const |
|
640 { |
|
641 return nullptr; |
|
642 } |
|
643 |
|
644 nsIContent * const * |
|
645 nsGenericDOMDataNode::GetChildArray(uint32_t* aChildCount) const |
|
646 { |
|
647 *aChildCount = 0; |
|
648 return nullptr; |
|
649 } |
|
650 |
|
651 int32_t |
|
652 nsGenericDOMDataNode::IndexOf(const nsINode* aPossibleChild) const |
|
653 { |
|
654 return -1; |
|
655 } |
|
656 |
|
657 nsresult |
|
658 nsGenericDOMDataNode::InsertChildAt(nsIContent* aKid, uint32_t aIndex, |
|
659 bool aNotify) |
|
660 { |
|
661 return NS_OK; |
|
662 } |
|
663 |
|
664 void |
|
665 nsGenericDOMDataNode::RemoveChildAt(uint32_t aIndex, bool aNotify) |
|
666 { |
|
667 } |
|
668 |
|
669 nsIContent * |
|
670 nsGenericDOMDataNode::GetBindingParent() const |
|
671 { |
|
672 nsDataSlots *slots = GetExistingDataSlots(); |
|
673 return slots ? slots->mBindingParent : nullptr; |
|
674 } |
|
675 |
|
676 ShadowRoot * |
|
677 nsGenericDOMDataNode::GetShadowRoot() const |
|
678 { |
|
679 return nullptr; |
|
680 } |
|
681 |
|
682 ShadowRoot * |
|
683 nsGenericDOMDataNode::GetContainingShadow() const |
|
684 { |
|
685 nsDataSlots *slots = GetExistingDataSlots(); |
|
686 return slots ? slots->mContainingShadow : nullptr; |
|
687 } |
|
688 |
|
689 void |
|
690 nsGenericDOMDataNode::SetShadowRoot(ShadowRoot* aShadowRoot) |
|
691 { |
|
692 } |
|
693 |
|
694 nsXBLBinding * |
|
695 nsGenericDOMDataNode::GetXBLBinding() const |
|
696 { |
|
697 return nullptr; |
|
698 } |
|
699 |
|
700 void |
|
701 nsGenericDOMDataNode::SetXBLBinding(nsXBLBinding* aBinding, |
|
702 nsBindingManager* aOldBindingManager) |
|
703 { |
|
704 } |
|
705 |
|
706 nsIContent * |
|
707 nsGenericDOMDataNode::GetXBLInsertionParent() const |
|
708 { |
|
709 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) { |
|
710 nsDataSlots *slots = GetExistingDataSlots(); |
|
711 if (slots) { |
|
712 return slots->mXBLInsertionParent; |
|
713 } |
|
714 } |
|
715 |
|
716 return nullptr; |
|
717 } |
|
718 |
|
719 void |
|
720 nsGenericDOMDataNode::SetXBLInsertionParent(nsIContent* aContent) |
|
721 { |
|
722 nsDataSlots *slots = DataSlots(); |
|
723 if (aContent) { |
|
724 SetFlags(NODE_MAY_BE_IN_BINDING_MNGR); |
|
725 } |
|
726 slots->mXBLInsertionParent = aContent; |
|
727 } |
|
728 |
|
729 CustomElementData * |
|
730 nsGenericDOMDataNode::GetCustomElementData() const |
|
731 { |
|
732 return nullptr; |
|
733 } |
|
734 |
|
735 void |
|
736 nsGenericDOMDataNode::SetCustomElementData(CustomElementData* aData) |
|
737 { |
|
738 } |
|
739 |
|
740 bool |
|
741 nsGenericDOMDataNode::IsNodeOfType(uint32_t aFlags) const |
|
742 { |
|
743 return !(aFlags & ~(eCONTENT | eDATA_NODE)); |
|
744 } |
|
745 |
|
746 void |
|
747 nsGenericDOMDataNode::SaveSubtreeState() |
|
748 { |
|
749 } |
|
750 |
|
751 void |
|
752 nsGenericDOMDataNode::DestroyContent() |
|
753 { |
|
754 // XXX We really should let cycle collection do this, but that currently still |
|
755 // leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684). |
|
756 ReleaseWrapper(this); |
|
757 } |
|
758 |
|
759 #ifdef DEBUG |
|
760 void |
|
761 nsGenericDOMDataNode::List(FILE* out, int32_t aIndent) const |
|
762 { |
|
763 } |
|
764 |
|
765 void |
|
766 nsGenericDOMDataNode::DumpContent(FILE* out, int32_t aIndent, |
|
767 bool aDumpAll) const |
|
768 { |
|
769 } |
|
770 #endif |
|
771 |
|
772 bool |
|
773 nsGenericDOMDataNode::IsLink(nsIURI** aURI) const |
|
774 { |
|
775 *aURI = nullptr; |
|
776 return false; |
|
777 } |
|
778 |
|
779 nsINode::nsSlots* |
|
780 nsGenericDOMDataNode::CreateSlots() |
|
781 { |
|
782 return new nsDataSlots(); |
|
783 } |
|
784 |
|
785 nsGenericDOMDataNode::nsDataSlots::nsDataSlots() |
|
786 : nsINode::nsSlots(), mBindingParent(nullptr) |
|
787 { |
|
788 } |
|
789 |
|
790 void |
|
791 nsGenericDOMDataNode::nsDataSlots::Traverse(nsCycleCollectionTraversalCallback &cb) |
|
792 { |
|
793 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLInsertionParent"); |
|
794 cb.NoteXPCOMChild(mXBLInsertionParent.get()); |
|
795 |
|
796 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mContainingShadow"); |
|
797 cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow)); |
|
798 } |
|
799 |
|
800 void |
|
801 nsGenericDOMDataNode::nsDataSlots::Unlink() |
|
802 { |
|
803 mXBLInsertionParent = nullptr; |
|
804 mContainingShadow = nullptr; |
|
805 } |
|
806 |
|
807 //---------------------------------------------------------------------- |
|
808 |
|
809 // Implementation of the nsIDOMText interface |
|
810 |
|
811 nsresult |
|
812 nsGenericDOMDataNode::SplitData(uint32_t aOffset, nsIContent** aReturn, |
|
813 bool aCloneAfterOriginal) |
|
814 { |
|
815 *aReturn = nullptr; |
|
816 nsresult rv = NS_OK; |
|
817 nsAutoString cutText; |
|
818 uint32_t length = TextLength(); |
|
819 |
|
820 if (aOffset > length) { |
|
821 return NS_ERROR_DOM_INDEX_SIZE_ERR; |
|
822 } |
|
823 |
|
824 uint32_t cutStartOffset = aCloneAfterOriginal ? aOffset : 0; |
|
825 uint32_t cutLength = aCloneAfterOriginal ? length - aOffset : aOffset; |
|
826 rv = SubstringData(cutStartOffset, cutLength, cutText); |
|
827 if (NS_FAILED(rv)) { |
|
828 return rv; |
|
829 } |
|
830 |
|
831 nsIDocument* document = GetCurrentDoc(); |
|
832 mozAutoDocUpdate updateBatch(document, UPDATE_CONTENT_MODEL, true); |
|
833 |
|
834 // Use Clone for creating the new node so that the new node is of same class |
|
835 // as this node! |
|
836 nsCOMPtr<nsIContent> newContent = CloneDataNode(mNodeInfo, false); |
|
837 if (!newContent) { |
|
838 return NS_ERROR_OUT_OF_MEMORY; |
|
839 } |
|
840 newContent->SetText(cutText, true); // XXX should be false? |
|
841 |
|
842 CharacterDataChangeInfo::Details details = { |
|
843 CharacterDataChangeInfo::Details::eSplit, newContent |
|
844 }; |
|
845 rv = SetTextInternal(cutStartOffset, cutLength, nullptr, 0, true, |
|
846 aCloneAfterOriginal ? &details : nullptr); |
|
847 if (NS_FAILED(rv)) { |
|
848 return rv; |
|
849 } |
|
850 |
|
851 nsCOMPtr<nsINode> parent = GetParentNode(); |
|
852 if (parent) { |
|
853 int32_t insertionIndex = parent->IndexOf(this); |
|
854 if (aCloneAfterOriginal) { |
|
855 ++insertionIndex; |
|
856 } |
|
857 parent->InsertChildAt(newContent, insertionIndex, true); |
|
858 } |
|
859 |
|
860 newContent.swap(*aReturn); |
|
861 return rv; |
|
862 } |
|
863 |
|
864 nsresult |
|
865 nsGenericDOMDataNode::SplitText(uint32_t aOffset, nsIDOMText** aReturn) |
|
866 { |
|
867 nsCOMPtr<nsIContent> newChild; |
|
868 nsresult rv = SplitData(aOffset, getter_AddRefs(newChild)); |
|
869 if (NS_SUCCEEDED(rv)) { |
|
870 rv = CallQueryInterface(newChild, aReturn); |
|
871 } |
|
872 return rv; |
|
873 } |
|
874 |
|
875 /* static */ int32_t |
|
876 nsGenericDOMDataNode::FirstLogicallyAdjacentTextNode(nsIContent* aParent, |
|
877 int32_t aIndex) |
|
878 { |
|
879 while (aIndex-- > 0) { |
|
880 nsIContent* sibling = aParent->GetChildAt(aIndex); |
|
881 if (!sibling->IsNodeOfType(nsINode::eTEXT)) |
|
882 return aIndex + 1; |
|
883 } |
|
884 return 0; |
|
885 } |
|
886 |
|
887 /* static */ int32_t |
|
888 nsGenericDOMDataNode::LastLogicallyAdjacentTextNode(nsIContent* aParent, |
|
889 int32_t aIndex, |
|
890 uint32_t aCount) |
|
891 { |
|
892 while (++aIndex < int32_t(aCount)) { |
|
893 nsIContent* sibling = aParent->GetChildAt(aIndex); |
|
894 if (!sibling->IsNodeOfType(nsINode::eTEXT)) |
|
895 return aIndex - 1; |
|
896 } |
|
897 return aCount - 1; |
|
898 } |
|
899 |
|
900 nsresult |
|
901 nsGenericDOMDataNode::GetWholeText(nsAString& aWholeText) |
|
902 { |
|
903 nsIContent* parent = GetParent(); |
|
904 |
|
905 // Handle parent-less nodes |
|
906 if (!parent) |
|
907 return GetData(aWholeText); |
|
908 |
|
909 int32_t index = parent->IndexOf(this); |
|
910 NS_WARN_IF_FALSE(index >= 0, |
|
911 "Trying to use .wholeText with an anonymous" |
|
912 "text node child of a binding parent?"); |
|
913 NS_ENSURE_TRUE(index >= 0, NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
|
914 int32_t first = |
|
915 FirstLogicallyAdjacentTextNode(parent, index); |
|
916 int32_t last = |
|
917 LastLogicallyAdjacentTextNode(parent, index, parent->GetChildCount()); |
|
918 |
|
919 aWholeText.Truncate(); |
|
920 |
|
921 nsCOMPtr<nsIDOMText> node; |
|
922 nsAutoString tmp; |
|
923 do { |
|
924 node = do_QueryInterface(parent->GetChildAt(first)); |
|
925 node->GetData(tmp); |
|
926 aWholeText.Append(tmp); |
|
927 } while (first++ < last); |
|
928 |
|
929 return NS_OK; |
|
930 } |
|
931 |
|
932 //---------------------------------------------------------------------- |
|
933 |
|
934 // Implementation of the nsIContent interface text functions |
|
935 |
|
936 const nsTextFragment * |
|
937 nsGenericDOMDataNode::GetText() |
|
938 { |
|
939 return &mText; |
|
940 } |
|
941 |
|
942 uint32_t |
|
943 nsGenericDOMDataNode::TextLength() const |
|
944 { |
|
945 return mText.GetLength(); |
|
946 } |
|
947 |
|
948 nsresult |
|
949 nsGenericDOMDataNode::SetText(const char16_t* aBuffer, |
|
950 uint32_t aLength, |
|
951 bool aNotify) |
|
952 { |
|
953 return SetTextInternal(0, mText.GetLength(), aBuffer, aLength, aNotify); |
|
954 } |
|
955 |
|
956 nsresult |
|
957 nsGenericDOMDataNode::AppendText(const char16_t* aBuffer, |
|
958 uint32_t aLength, |
|
959 bool aNotify) |
|
960 { |
|
961 return SetTextInternal(mText.GetLength(), 0, aBuffer, aLength, aNotify); |
|
962 } |
|
963 |
|
964 bool |
|
965 nsGenericDOMDataNode::TextIsOnlyWhitespace() |
|
966 { |
|
967 // FIXME: should this method take content language into account? |
|
968 if (mText.Is2b()) { |
|
969 // The fragment contains non-8bit characters and such characters |
|
970 // are never considered whitespace. |
|
971 return false; |
|
972 } |
|
973 |
|
974 if (HasFlag(NS_CACHED_TEXT_IS_ONLY_WHITESPACE)) { |
|
975 return HasFlag(NS_TEXT_IS_ONLY_WHITESPACE); |
|
976 } |
|
977 |
|
978 const char* cp = mText.Get1b(); |
|
979 const char* end = cp + mText.GetLength(); |
|
980 |
|
981 while (cp < end) { |
|
982 char ch = *cp; |
|
983 |
|
984 if (!dom::IsSpaceCharacter(ch)) { |
|
985 UnsetFlags(NS_TEXT_IS_ONLY_WHITESPACE); |
|
986 SetFlags(NS_CACHED_TEXT_IS_ONLY_WHITESPACE); |
|
987 return false; |
|
988 } |
|
989 |
|
990 ++cp; |
|
991 } |
|
992 |
|
993 SetFlags(NS_CACHED_TEXT_IS_ONLY_WHITESPACE | NS_TEXT_IS_ONLY_WHITESPACE); |
|
994 return true; |
|
995 } |
|
996 |
|
997 bool |
|
998 nsGenericDOMDataNode::HasTextForTranslation() |
|
999 { |
|
1000 if (mText.Is2b()) { |
|
1001 // The fragment contains non-8bit characters which means there |
|
1002 // was at least one "interesting" character to trigger non-8bit. |
|
1003 return true; |
|
1004 } |
|
1005 |
|
1006 if (HasFlag(NS_CACHED_TEXT_IS_ONLY_WHITESPACE) && |
|
1007 HasFlag(NS_TEXT_IS_ONLY_WHITESPACE)) { |
|
1008 return false; |
|
1009 } |
|
1010 |
|
1011 const char* cp = mText.Get1b(); |
|
1012 const char* end = cp + mText.GetLength(); |
|
1013 |
|
1014 unsigned char ch; |
|
1015 for (; cp < end; cp++) { |
|
1016 ch = *cp; |
|
1017 |
|
1018 // These are the characters that are letters |
|
1019 // in the first 256 UTF-8 codepoints. |
|
1020 if ((ch >= 'a' && ch <= 'z') || |
|
1021 (ch >= 'A' && ch <= 'Z') || |
|
1022 (ch >= 192 && ch <= 214) || |
|
1023 (ch >= 216 && ch <= 246) || |
|
1024 (ch >= 248)) { |
|
1025 return true; |
|
1026 } |
|
1027 } |
|
1028 |
|
1029 return false; |
|
1030 } |
|
1031 |
|
1032 void |
|
1033 nsGenericDOMDataNode::AppendTextTo(nsAString& aResult) |
|
1034 { |
|
1035 mText.AppendTo(aResult); |
|
1036 } |
|
1037 |
|
1038 bool |
|
1039 nsGenericDOMDataNode::AppendTextTo(nsAString& aResult, const mozilla::fallible_t&) |
|
1040 { |
|
1041 return mText.AppendTo(aResult, mozilla::fallible_t()); |
|
1042 } |
|
1043 |
|
1044 already_AddRefed<nsIAtom> |
|
1045 nsGenericDOMDataNode::GetCurrentValueAtom() |
|
1046 { |
|
1047 nsAutoString val; |
|
1048 GetData(val); |
|
1049 return NS_NewAtom(val); |
|
1050 } |
|
1051 |
|
1052 nsIAtom* |
|
1053 nsGenericDOMDataNode::DoGetID() const |
|
1054 { |
|
1055 return nullptr; |
|
1056 } |
|
1057 |
|
1058 const nsAttrValue* |
|
1059 nsGenericDOMDataNode::DoGetClasses() const |
|
1060 { |
|
1061 NS_NOTREACHED("Shouldn't ever be called"); |
|
1062 return nullptr; |
|
1063 } |
|
1064 |
|
1065 NS_IMETHODIMP |
|
1066 nsGenericDOMDataNode::WalkContentStyleRules(nsRuleWalker* aRuleWalker) |
|
1067 { |
|
1068 return NS_OK; |
|
1069 } |
|
1070 |
|
1071 NS_IMETHODIMP_(bool) |
|
1072 nsGenericDOMDataNode::IsAttributeMapped(const nsIAtom* aAttribute) const |
|
1073 { |
|
1074 return false; |
|
1075 } |
|
1076 |
|
1077 nsChangeHint |
|
1078 nsGenericDOMDataNode::GetAttributeChangeHint(const nsIAtom* aAttribute, |
|
1079 int32_t aModType) const |
|
1080 { |
|
1081 NS_NOTREACHED("Shouldn't be calling this!"); |
|
1082 return nsChangeHint(0); |
|
1083 } |
|
1084 |
|
1085 nsIAtom* |
|
1086 nsGenericDOMDataNode::GetClassAttributeName() const |
|
1087 { |
|
1088 return nullptr; |
|
1089 } |
|
1090 |
|
1091 size_t |
|
1092 nsGenericDOMDataNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
|
1093 { |
|
1094 size_t n = nsIContent::SizeOfExcludingThis(aMallocSizeOf); |
|
1095 n += mText.SizeOfExcludingThis(aMallocSizeOf); |
|
1096 return n; |
|
1097 } |
|
1098 |