Coverage Report

Created: 2018-09-25 14:53

/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("&amp;");
399
      } else if (ch == '<') {
400
        aBuf.AppendLiteral("&lt;");
401
      } else if (ch == '>') {
402
        aBuf.AppendLiteral("&gt;");
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("&amp;");
417
      } else if (ch == '<') {
418
        aBuf.AppendLiteral("&lt;");
419
      } else if (ch == '>') {
420
        aBuf.AppendLiteral("&gt;");
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