CharacterData.cpp (19892B)
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 "mozAutoDocUpdate.h" 15 #include "mozilla/AsyncEventDispatcher.h" 16 #include "mozilla/Sprintf.h" 17 #include "mozilla/dom/BindContext.h" 18 #include "mozilla/dom/DirectionalityUtils.h" 19 #include "mozilla/dom/Document.h" 20 #include "mozilla/dom/Element.h" 21 #include "mozilla/dom/MutationObservers.h" 22 #include "mozilla/dom/ShadowRoot.h" 23 #include "mozilla/dom/UnbindContext.h" 24 #include "nsBidiUtils.h" 25 #include "nsIContentInlines.h" 26 #include "nsReadableUtils.h" 27 #include "nsTextNode.h" 28 #include "nsWindowSizes.h" 29 30 #if defined(ACCESSIBILITY) && defined(DEBUG) 31 # include "nsAccessibilityService.h" 32 #endif 33 34 namespace mozilla::dom { 35 36 CharacterData::CharacterData(already_AddRefed<dom::NodeInfo>&& aNodeInfo) 37 : nsIContent(std::move(aNodeInfo)) { 38 MOZ_ASSERT(mNodeInfo->NodeType() == TEXT_NODE || 39 mNodeInfo->NodeType() == CDATA_SECTION_NODE || 40 mNodeInfo->NodeType() == COMMENT_NODE || 41 mNodeInfo->NodeType() == PROCESSING_INSTRUCTION_NODE || 42 mNodeInfo->NodeType() == DOCUMENT_TYPE_NODE, 43 "Bad NodeType in aNodeInfo"); 44 } 45 46 CharacterData::~CharacterData() { 47 MOZ_ASSERT(!IsInUncomposedDoc(), 48 "Please remove this from the document properly"); 49 if (GetParent()) { 50 NS_RELEASE(mParent); 51 } 52 } 53 54 Element* CharacterData::GetNameSpaceElement() { 55 return Element::FromNodeOrNull(GetParentNode()); 56 } 57 58 // Note, _INHERITED macro isn't used here since nsINode implementations are 59 // rather special. 60 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(CharacterData) 61 62 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(CharacterData) 63 return Element::CanSkip(tmp, aRemovingAllowed); 64 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END 65 66 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(CharacterData) 67 return Element::CanSkipInCC(tmp); 68 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END 69 70 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(CharacterData) 71 return Element::CanSkipThis(tmp); 72 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END 73 74 // We purposefully don't TRAVERSE_BEGIN_INHERITED here. All the bits 75 // we should traverse should be added here or in nsINode::Traverse. 76 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(CharacterData) 77 if (MOZ_UNLIKELY(cb.WantDebugInfo())) { 78 char name[40]; 79 SprintfLiteral(name, "CharacterData (len=%d)", tmp->mBuffer.GetLength()); 80 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name); 81 } else { 82 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(CharacterData, tmp->mRefCnt.get()) 83 } 84 85 if (!nsIContent::Traverse(tmp, cb)) { 86 return NS_SUCCESS_INTERRUPTED_TRAVERSE; 87 } 88 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 89 90 // We purposefully don't UNLINK_BEGIN_INHERITED here. 91 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CharacterData) 92 nsIContent::Unlink(tmp); 93 94 if (nsContentSlots* slots = tmp->GetExistingContentSlots()) { 95 slots->Unlink(*tmp); 96 } 97 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 98 99 NS_INTERFACE_MAP_BEGIN(CharacterData) 100 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(CharacterData) 101 NS_INTERFACE_MAP_END_INHERITING(nsIContent) 102 103 void CharacterData::GetNodeValueInternal(nsAString& aNodeValue) { 104 GetData(aNodeValue); 105 } 106 107 void CharacterData::SetNodeValueInternal( 108 const nsAString& aNodeValue, ErrorResult& aError, 109 MutationEffectOnScript aMutationEffectOnScript) { 110 aError = SetTextInternal(0, mBuffer.GetLength(), aNodeValue.BeginReading(), 111 aNodeValue.Length(), true, aMutationEffectOnScript); 112 } 113 114 //---------------------------------------------------------------------- 115 116 // Implementation of CharacterData 117 118 void CharacterData::SetTextContentInternal( 119 const nsAString& aTextContent, nsIPrincipal* aSubjectPrincipal, 120 ErrorResult& aError, MutationEffectOnScript aMutationEffectOnScript) { 121 return SetNodeValueInternal(aTextContent, aError, aMutationEffectOnScript); 122 } 123 124 void CharacterData::GetData(nsAString& aData) const { 125 if (mBuffer.Is2b()) { 126 aData.Truncate(); 127 mBuffer.AppendTo(aData); 128 } else { 129 // Must use Substring() since nsDependentCString() requires null 130 // terminated strings. 131 132 const char* data = mBuffer.Get1b(); 133 134 if (data) { 135 CopyASCIItoUTF16(Substring(data, data + mBuffer.GetLength()), aData); 136 } else { 137 aData.Truncate(); 138 } 139 } 140 } 141 142 void CharacterData::SetDataInternal( 143 const nsAString& aData, MutationEffectOnScript aMutationEffectOnScript, 144 ErrorResult& aRv) { 145 nsresult rv = SetTextInternal(0, mBuffer.GetLength(), aData.BeginReading(), 146 aData.Length(), true, aMutationEffectOnScript); 147 if (NS_FAILED(rv)) { 148 aRv.Throw(rv); 149 } 150 } 151 152 void CharacterData::SubstringData(uint32_t aStart, uint32_t aCount, 153 nsAString& aReturn, ErrorResult& rv) { 154 aReturn.Truncate(); 155 156 uint32_t textLength = mBuffer.GetLength(); 157 if (aStart > textLength) { 158 rv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); 159 return; 160 } 161 162 uint32_t amount = aCount; 163 if (amount > textLength - aStart) { 164 amount = textLength - aStart; 165 } 166 167 if (mBuffer.Is2b()) { 168 aReturn.Assign(mBuffer.Get2b() + aStart, amount); 169 } else { 170 // Must use Substring() since nsDependentCString() requires null 171 // terminated strings. 172 173 const char* data = mBuffer.Get1b() + aStart; 174 CopyASCIItoUTF16(Substring(data, data + amount), aReturn); 175 } 176 } 177 178 //---------------------------------------------------------------------- 179 180 void CharacterData::AppendDataInternal( 181 const nsAString& aData, MutationEffectOnScript aMutationEffectOnScript, 182 ErrorResult& aRv) { 183 InsertDataInternal(mBuffer.GetLength(), aData, aMutationEffectOnScript, aRv); 184 } 185 186 void CharacterData::InsertDataInternal( 187 uint32_t aOffset, const nsAString& aData, 188 MutationEffectOnScript aMutationEffectOnScript, ErrorResult& aRv) { 189 nsresult rv = SetTextInternal(aOffset, 0, aData.BeginReading(), 190 aData.Length(), true, aMutationEffectOnScript); 191 if (NS_FAILED(rv)) { 192 aRv.Throw(rv); 193 } 194 } 195 196 void CharacterData::DeleteDataInternal( 197 uint32_t aOffset, uint32_t aCount, 198 MutationEffectOnScript aMutationEffectOnScript, ErrorResult& aRv) { 199 nsresult rv = SetTextInternal(aOffset, aCount, nullptr, 0, true, 200 aMutationEffectOnScript); 201 if (NS_FAILED(rv)) { 202 aRv.Throw(rv); 203 } 204 } 205 206 void CharacterData::ReplaceDataInternal( 207 uint32_t aOffset, uint32_t aCount, const nsAString& aData, 208 MutationEffectOnScript aMutationEffectOnScript, ErrorResult& aRv) { 209 nsresult rv = SetTextInternal(aOffset, aCount, aData.BeginReading(), 210 aData.Length(), true, aMutationEffectOnScript); 211 if (NS_FAILED(rv)) { 212 aRv.Throw(rv); 213 } 214 } 215 216 nsresult CharacterData::SetTextInternal( 217 uint32_t aOffset, uint32_t aCount, const char16_t* aBuffer, 218 uint32_t aLength, bool aNotify, 219 MutationEffectOnScript aMutationEffectOnScript, 220 CharacterDataChangeInfo::Details* aDetails) { 221 MOZ_ASSERT(aBuffer || !aLength, "Null buffer passed to SetTextInternal!"); 222 223 // sanitize arguments 224 uint32_t textLength = mBuffer.GetLength(); 225 if (aOffset > textLength) { 226 return NS_ERROR_DOM_INDEX_SIZE_ERR; 227 } 228 229 if (aCount > textLength - aOffset) { 230 aCount = textLength - aOffset; 231 } 232 233 uint32_t endOffset = aOffset + aCount; 234 235 // Make sure the text fragment can hold the new data. 236 if (aLength > aCount && !mBuffer.CanGrowBy(aLength - aCount)) { 237 return NS_ERROR_OUT_OF_MEMORY; 238 } 239 240 Document* document = GetComposedDoc(); 241 mozAutoDocUpdate updateBatch(document, aNotify); 242 243 if (aNotify) { 244 CharacterDataChangeInfo info = { 245 aOffset == textLength, aOffset, endOffset, aLength, 246 aMutationEffectOnScript, aDetails}; 247 MutationObservers::NotifyCharacterDataWillChange(this, info); 248 } 249 250 auto oldDir = Directionality::Unset; 251 const bool dirAffectsAncestor = 252 IsText() && TextNodeWillChangeDirection(AsText(), &oldDir, aOffset); 253 254 if (aOffset == 0 && endOffset == textLength) { 255 // Replacing whole text or old text was empty. 256 // If this is marked as "maybe modified frequently", the text should be 257 // stored as char16_t since converting char* to char16_t* is expensive. 258 bool ok = mBuffer.SetTo(aBuffer, aLength, true, 259 HasFlag(NS_MAYBE_MODIFIED_FREQUENTLY)); 260 NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); 261 } else if (aOffset == textLength) { 262 // Appending to existing. 263 bool ok = mBuffer.Append(aBuffer, aLength, !mBuffer.IsBidi(), 264 HasFlag(NS_MAYBE_MODIFIED_FREQUENTLY)); 265 NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); 266 } else { 267 // Merging old and new 268 269 bool bidi = mBuffer.IsBidi(); 270 271 // Allocate new buffer 272 const uint32_t newLength = textLength - aCount + aLength; 273 // Use nsString and not nsAutoString so that we get a nsStringBuffer which 274 // can be just AddRefed in CharacterDataBuffer. 275 nsString to; 276 to.SetCapacity(newLength); 277 278 // Copy over appropriate data 279 if (aOffset) { 280 mBuffer.AppendTo(to, 0, aOffset); 281 } 282 if (aLength) { 283 to.Append(aBuffer, aLength); 284 if (!bidi) { 285 bidi = HasRTLChars(Span(aBuffer, aLength)); 286 } 287 } 288 if (endOffset != textLength) { 289 mBuffer.AppendTo(to, endOffset, textLength - endOffset); 290 } 291 292 // If this is marked as "maybe modified frequently", the text should be 293 // stored as char16_t since converting char* to char16_t* is expensive. 294 // Use char16_t also when we have bidi characters. 295 bool use2b = HasFlag(NS_MAYBE_MODIFIED_FREQUENTLY) || bidi; 296 bool ok = mBuffer.SetTo(to, false, use2b); 297 mBuffer.SetBidi(bidi); 298 299 NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY); 300 } 301 302 UnsetFlags(NS_CACHED_TEXT_IS_ONLY_WHITESPACE); 303 304 if (document && mBuffer.IsBidi()) { 305 // If we found bidi characters in mBuffer.SetTo() above, indicate that the 306 // document contains bidi characters. 307 document->SetBidiEnabled(); 308 } 309 310 if (dirAffectsAncestor) { 311 // dirAffectsAncestor being true implies that we have a text node, see 312 // above. 313 MOZ_ASSERT(IsText()); 314 TextNodeChangedDirection(AsText(), oldDir, aNotify); 315 } 316 317 // Notify observers 318 if (aNotify) { 319 CharacterDataChangeInfo info = { 320 aOffset == textLength, aOffset, endOffset, aLength, 321 aMutationEffectOnScript, aDetails}; 322 MutationObservers::NotifyCharacterDataChanged(this, info); 323 } 324 325 return NS_OK; 326 } 327 328 //---------------------------------------------------------------------- 329 330 // Implementation of nsIContent 331 332 #ifdef MOZ_DOM_LIST 333 void CharacterData::ToCString(nsAString& aBuf, int32_t aOffset, 334 int32_t aLen) const { 335 if (mBuffer.Is2b()) { 336 const char16_t* cp = mBuffer.Get2b() + aOffset; 337 const char16_t* end = cp + aLen; 338 339 while (cp < end) { 340 char16_t ch = *cp++; 341 if (ch == '&') { 342 aBuf.AppendLiteral("&"); 343 } else if (ch == '<') { 344 aBuf.AppendLiteral("<"); 345 } else if (ch == '>') { 346 aBuf.AppendLiteral(">"); 347 } else if ((ch < ' ') || (ch >= 127)) { 348 aBuf.AppendPrintf("\\u%04x", ch); 349 } else { 350 aBuf.Append(ch); 351 } 352 } 353 } else { 354 unsigned char* cp = (unsigned char*)mBuffer.Get1b() + aOffset; 355 const unsigned char* end = cp + aLen; 356 357 while (cp < end) { 358 char16_t ch = *cp++; 359 if (ch == '&') { 360 aBuf.AppendLiteral("&"); 361 } else if (ch == '<') { 362 aBuf.AppendLiteral("<"); 363 } else if (ch == '>') { 364 aBuf.AppendLiteral(">"); 365 } else if ((ch < ' ') || (ch >= 127)) { 366 aBuf.AppendPrintf("\\u%04x", ch); 367 } else { 368 aBuf.Append(ch); 369 } 370 } 371 } 372 } 373 #endif 374 375 nsresult CharacterData::BindToTree(BindContext& aContext, nsINode& aParent) { 376 MOZ_ASSERT(aParent.IsContent() || aParent.IsDocument(), 377 "Must have content or document parent!"); 378 MOZ_ASSERT(aParent.OwnerDoc() == OwnerDoc(), 379 "Must have the same owner document"); 380 MOZ_ASSERT(OwnerDoc() == &aContext.OwnerDoc(), "These should match too"); 381 MOZ_ASSERT(!IsInUncomposedDoc(), "Already have a document. Unbind first!"); 382 MOZ_ASSERT(!IsInComposedDoc(), "Already have a document. Unbind first!"); 383 // Note that as we recurse into the kids, they'll have a non-null parent. So 384 // only assert if our parent is _changing_ while we have a parent. 385 MOZ_ASSERT(!GetParentNode() || &aParent == GetParentNode(), 386 "Already have a parent. Unbind first!"); 387 388 const bool hadParent = !!GetParentNode(); 389 390 if (aParent.IsInNativeAnonymousSubtree()) { 391 SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE); 392 } 393 if (IsRootOfNativeAnonymousSubtree()) { 394 aParent.SetMayHaveAnonymousChildren(); 395 } else if (aParent.HasFlag(NODE_HAS_BEEN_IN_UA_WIDGET)) { 396 SetFlags(NODE_HAS_BEEN_IN_UA_WIDGET); 397 } 398 399 // Set parent 400 mParent = &aParent; 401 if (!hadParent && aParent.IsContent()) { 402 SetParentIsContent(true); 403 NS_ADDREF(mParent); 404 } 405 MOZ_ASSERT(!!GetParent() == aParent.IsContent()); 406 407 if (aParent.IsInUncomposedDoc() || aParent.IsInShadowTree()) { 408 // We no longer need to track the subtree pointer (and in fact we'll assert 409 // if we do this any later). 410 ClearSubtreeRootPointer(); 411 SetIsConnected(aParent.IsInComposedDoc()); 412 413 if (aParent.IsInUncomposedDoc()) { 414 SetIsInDocument(); 415 } else { 416 SetFlags(NODE_IS_IN_SHADOW_TREE); 417 MOZ_ASSERT(aParent.IsContent() && 418 aParent.AsContent()->GetContainingShadow()); 419 ExtendedContentSlots()->mContainingShadow = 420 aParent.AsContent()->GetContainingShadow(); 421 } 422 423 if (IsInComposedDoc() && mBuffer.IsBidi()) { 424 aContext.OwnerDoc().SetBidiEnabled(); 425 } 426 427 // Clear the lazy frame construction bits. 428 UnsetFlags(NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES); 429 } else { 430 // If we're not in the doc and not in a shadow tree, 431 // update our subtree pointer. 432 SetSubtreeRootPointer(aParent.SubtreeRoot()); 433 } 434 435 MutationObservers::NotifyParentChainChanged(this); 436 437 UpdateEditableState(false); 438 439 // Ensure we only do these once, in the case we move the shadow host around. 440 if (aContext.SubtreeRootChanges()) { 441 HandleShadowDOMRelatedInsertionSteps(hadParent); 442 } 443 444 MOZ_ASSERT(OwnerDoc() == aParent.OwnerDoc(), "Bound to wrong document"); 445 MOZ_ASSERT(IsInComposedDoc() == aContext.InComposedDoc()); 446 MOZ_ASSERT(IsInUncomposedDoc() == aContext.InUncomposedDoc()); 447 MOZ_ASSERT(&aParent == GetParentNode(), "Bound to wrong parent node"); 448 MOZ_ASSERT(aParent.IsInUncomposedDoc() == IsInUncomposedDoc()); 449 MOZ_ASSERT(aParent.IsInComposedDoc() == IsInComposedDoc()); 450 MOZ_ASSERT(aParent.IsInShadowTree() == IsInShadowTree()); 451 MOZ_ASSERT(aParent.SubtreeRoot() == SubtreeRoot()); 452 return NS_OK; 453 } 454 455 void CharacterData::UnbindFromTree(UnbindContext& aContext) { 456 // Unset frame flags; if we need them again later, they'll get set again. 457 UnsetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE | NS_REFRAME_IF_WHITESPACE); 458 459 const bool nullParent = aContext.IsUnbindRoot(this); 460 HandleShadowDOMRelatedRemovalSteps(nullParent); 461 462 if (nullParent) { 463 if (GetParent()) { 464 NS_RELEASE(mParent); 465 } else { 466 mParent = nullptr; 467 } 468 SetParentIsContent(false); 469 } 470 ClearInDocument(); 471 SetIsConnected(false); 472 473 if (nullParent || !mParent->IsInShadowTree()) { 474 UnsetFlags(NODE_IS_IN_SHADOW_TREE); 475 476 // Begin keeping track of our subtree root. 477 SetSubtreeRootPointer(nullParent ? this : mParent->SubtreeRoot()); 478 479 if (nsExtendedContentSlots* slots = GetExistingExtendedContentSlots()) { 480 slots->mContainingShadow = nullptr; 481 } 482 } 483 484 MutationObservers::NotifyParentChainChanged(this); 485 486 #if defined(ACCESSIBILITY) && defined(DEBUG) 487 MOZ_ASSERT(!GetAccService() || !GetAccService()->HasAccessible(this), 488 "An accessible for this element still exists!"); 489 #endif 490 } 491 492 //---------------------------------------------------------------------- 493 494 // Implementation of the nsIContent interface text functions 495 496 nsresult CharacterData::SetText(const char16_t* aBuffer, uint32_t aLength, 497 bool aNotify) { 498 return SetTextInternal(0, mBuffer.GetLength(), aBuffer, aLength, aNotify, 499 MutationEffectOnScript::KeepTrustWorthiness); 500 } 501 502 nsresult CharacterData::AppendText(const char16_t* aBuffer, uint32_t aLength, 503 bool aNotify) { 504 return SetTextInternal(mBuffer.GetLength(), 0, aBuffer, aLength, aNotify, 505 MutationEffectOnScript::KeepTrustWorthiness); 506 } 507 508 bool CharacterData::TextIsOnlyWhitespace() { 509 MOZ_ASSERT(NS_IsMainThread()); 510 if (!ThreadSafeTextIsOnlyWhitespace()) { 511 UnsetFlags(NS_TEXT_IS_ONLY_WHITESPACE); 512 SetFlags(NS_CACHED_TEXT_IS_ONLY_WHITESPACE); 513 return false; 514 } 515 516 SetFlags(NS_CACHED_TEXT_IS_ONLY_WHITESPACE | NS_TEXT_IS_ONLY_WHITESPACE); 517 return true; 518 } 519 520 bool CharacterData::ThreadSafeTextIsOnlyWhitespace() const { 521 // FIXME: should this method take content language into account? 522 if (mBuffer.Is2b()) { 523 // The fragment contains non-8bit characters and such characters 524 // are never considered whitespace. 525 // 526 // FIXME(emilio): This is not quite true in presence of the 527 // NS_MAYBE_MODIFIED_FREQUENTLY flag... But looks like we only set that on 528 // anonymous nodes, so should be fine... 529 return false; 530 } 531 532 if (HasFlag(NS_CACHED_TEXT_IS_ONLY_WHITESPACE)) { 533 return HasFlag(NS_TEXT_IS_ONLY_WHITESPACE); 534 } 535 536 return CheckTextIsOnlyWhitespace(0, mBuffer.GetLength()); 537 } 538 539 bool CharacterData::TextStartsWithOnlyWhitespace(uint32_t aOffset) const { 540 MOZ_ASSERT(aOffset <= mBuffer.GetLength()); 541 542 if (HasFlag(NS_CACHED_TEXT_IS_ONLY_WHITESPACE) && 543 HasFlag(NS_TEXT_IS_ONLY_WHITESPACE)) { 544 return true; 545 } 546 547 return CheckTextIsOnlyWhitespace(0, aOffset); 548 } 549 550 bool CharacterData::TextEndsWithOnlyWhitespace(uint32_t aOffset) const { 551 MOZ_ASSERT(aOffset <= mBuffer.GetLength()); 552 553 if (HasFlag(NS_CACHED_TEXT_IS_ONLY_WHITESPACE) && 554 HasFlag(NS_TEXT_IS_ONLY_WHITESPACE)) { 555 return true; 556 } 557 558 return CheckTextIsOnlyWhitespace(aOffset, mBuffer.GetLength()); 559 } 560 561 bool CharacterData::CheckTextIsOnlyWhitespace(uint32_t aStartOffset, 562 uint32_t aEndOffset) const { 563 if (mBuffer.Is2b()) { 564 const char16_t* cp = mBuffer.Get2b() + aStartOffset; 565 const char16_t* end = mBuffer.Get2b() + aEndOffset; 566 567 while (cp < end) { 568 char16_t ch = *cp; 569 570 // NOTE(emilio): If you ever change the definition of "whitespace" here, 571 // you need to change it too in RestyleManager::CharacterDataChanged. 572 if (!dom::IsSpaceCharacter(ch)) { 573 return false; 574 } 575 576 ++cp; 577 } 578 } else { 579 const char* cp = mBuffer.Get1b() + aStartOffset; 580 const char* end = mBuffer.Get1b() + aEndOffset; 581 582 while (cp < end) { 583 char ch = *cp; 584 585 // NOTE(emilio): If you ever change the definition of "whitespace" here, 586 // you need to change it too in RestyleManager::CharacterDataChanged. 587 if (!dom::IsSpaceCharacter(ch)) { 588 return false; 589 } 590 591 ++cp; 592 } 593 } 594 return true; 595 } 596 597 already_AddRefed<nsAtom> CharacterData::GetCurrentValueAtom() { 598 nsAutoString val; 599 GetData(val); 600 return NS_Atomize(val); 601 } 602 603 void CharacterData::AddSizeOfExcludingThis(nsWindowSizes& aSizes, 604 size_t* aNodeSize) const { 605 nsIContent::AddSizeOfExcludingThis(aSizes, aNodeSize); 606 *aNodeSize += mBuffer.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf); 607 } 608 609 } // namespace mozilla::dom