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