Firefox · DOM
CVE-2026-109754
Logic Error in DOM
Overview
Moderate
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifdom/base/CharacterDataBuffer.cpp |
modified | |
ifeditor/libeditor/CompositionTransaction.cpp |
modified | |
MOZ_STACK_CLASSeditor/libeditor/EditorBase.cpp |
modified | |
ifeditor/libeditor/EditorBase.cpp |
modified |
Files Changed
dom/base/CharacterDataBuffer.cppdom/base/CharacterDataBuffer.hdom/base/nsINode.hdom/base/test/gtest/TestCharacterDataBuffer.cppdom/script/ScriptElement.cppeditor/libeditor/CompositionTransaction.cppeditor/libeditor/DeleteTextTransaction.cppeditor/libeditor/EditorBase.cppeditor/libeditor/EditorBase.heditor/libeditor/HTMLEditor.cppeditor/libeditor/InsertTextTransaction.cppeditor/libeditor/ReplaceTextTransaction.cpp
Patch
diff --git a/dom/base/CharacterDataBuffer.cpp b/dom/base/CharacterDataBuffer.cpp
index fa1764d6c1a..bad1b5f1fd1 100644
--- a/dom/base/CharacterDataBuffer.cpp
+++ b/dom/base/CharacterDataBuffer.cpp
@@ -542,3 +542,33 @@ bool CharacterDataBuffer::BufferEquals(
return true;
}
+
+bool CharacterDataBuffer::Equals(const nsAString& aString) const {
+ const uint32_t length = GetLength();
+ if (!length) {
+ return aString.IsEmpty();
+ }
+ if (length != aString.Length()) {
+ return false;
+ }
+ if (Is2b()) {
+ return aString.Equals(nsDependentSubstring(Get2b(), length));
+ }
+ return aString.Equals(
+ NS_ConvertASCIItoUTF16(nsDependentCSubstring(Get1b(), length)));
+}
+
+bool CharacterDataBuffer::Equals(const nsACString& aString) const {
+ const uint32_t length = GetLength();
+ if (!length) {
+ return aString.IsEmpty();
+ }
+ if (length != aString.Length()) {
+ return false;
+ }
+ if (!Is2b()) {
+ return aString.Equals(nsDependentCSubstring(Get1b(), length));
+ }
+ NS_ConvertASCIItoUTF16 string(aString);
+ return string.Equals(nsDependentSubstring(Get2b(), length));
+}
diff --git a/dom/base/CharacterDataBuffer.h b/dom/base/CharacterDataBuffer.h
index b05bda5799c..86cde4110a5 100644
--- a/dom/base/CharacterDataBuffer.h
+++ b/dom/base/CharacterDataBuffer.h
@@ -334,6 +334,16 @@ class CharacterDataBuffer final {
*/
[[nodiscard]] bool BufferEquals(const CharacterDataBuffer& aOther) const;
+ /**
+ * Return true if aString matches with the data.
+ */
+ [[nodiscard]] bool Equals(const nsAString& aString) const;
+
+ /**
+ * Return true if aString matches with the data.
+ */
+ [[nodiscard]] bool Equals(const nsACString& aString) const;
+
// FYI: FragmentBits::mLength is only 29 bits. Therefore, UINT32_MAX won't
// be valid offset in the data.
constexpr static uint32_t kNotFound = UINT32_MAX;
diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h
index 271c98870e2..c0ecbaadff4 100644
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -667,6 +667,13 @@ class nsINode : public mozilla::dom::EventTarget {
return false;
}
+ /**
+ * Return true if the concrete class inherits ScriptElement.
+ */
+ [[nodiscard]] bool IsScriptElement() const {
+ return IsHTMLElement(nsGkAtoms::script) || IsSVGElement(nsGkAtoms::script);
+ }
+
// Returns non-null if this element subclasses `LinkStyle`.
virtual const mozilla::dom::LinkStyle* AsLinkStyle() const { return nullptr; }
mozilla::dom::LinkStyle* AsLinkStyle() {
diff --git a/dom/script/ScriptElement.cpp b/dom/script/ScriptElement.cpp
index 5598ccc23d8..febd3836528 100644
--- a/dom/script/ScriptElement.cpp
+++ b/dom/script/ScriptElement.cpp
@@ -164,6 +164,9 @@ void ScriptElement::ContentWillBeRemoved(nsIContent* aChild,
bool ScriptElement::MaybeProcessScript(nsCOMPtr<nsIParser> aParser) {
nsIContent* cont = GetAsContent();
+ MOZ_ASSERT(cont->IsScriptElement(),
+ "nsINode::IsScriptElement should return true if the node inherits "
+ "ScriptElement");
NS_ASSERTION(cont->DebugGetSlots()->mMutationObservers.contains(this),
"You forgot to add self as observer");
diff --git a/editor/libeditor/CompositionTransaction.cpp b/editor/libeditor/CompositionTransaction.cpp
index a5952101802..8fe06766590 100644
--- a/editor/libeditor/CompositionTransaction.cpp
+++ b/editor/libeditor/CompositionTransaction.cpp
@@ -123,11 +123,10 @@ NS_IMETHODIMP CompositionTransaction::DoTransaction() {
// Advance caret: This requires the presentation shell to get the selection.
if (mReplaceLength == 0) {
- IgnoredErrorResult error;
- editorBase->DoInsertText(*textNode, mOffset, mStringToInsert, error);
- if (error.Failed()) {
+ nsresult rv = editorBase->DoInsertText(*textNode, mOffset, mStringToInsert);
+ if (NS_FAILED(rv)) [[unlikely]] {
NS_WARNING("EditorBase::DoInsertText() failed");
- return error.StealNSResult();
+ return rv;
}
editorBase->RangeUpdaterRef().SelAdjInsertText(*textNode, mOffset,
mStringToInsert.Length());
@@ -182,15 +181,14 @@ NS_IMETHODIMP CompositionTransaction::DoTransaction() {
return {offsetInTextNode,
replaceEndOffsetInCompositionStartTextNode - offsetInTextNode};
}();
- IgnoredErrorResult error;
// FIXME: If mStringToInsert is empty string and mOffset is 0 in HTMLEditor,
// we should delete the `Text` instead. See bug 2019186.
- editorBase->DoReplaceText(*textNode, replaceStartInFirstText,
- replaceableLengthInFirstText, mStringToInsert,
- error);
- if (error.Failed()) [[unlikely]] {
+ nsresult rv = editorBase->DoReplaceText(*textNode, replaceStartInFirstText,
+ replaceableLengthInFirstText,
+ mStringToInsert);
+ if (NS_FAILED(rv)) [[unlikely]] {
NS_WARNING("EditorBase::DoReplaceText() failed");
- return error.StealNSResult();
+ return rv;
}
// Don't use RangeUpdaterRef().SelAdjReplaceText() here because undoing
@@ -210,7 +208,6 @@ NS_IMETHODIMP CompositionTransaction::DoTransaction() {
// non-empty text nodes which are inserted by JS. Instead, we
// should remove all text in the ranges of IME selections.
uint32_t remainingLength = mReplaceLength - replaceableLengthInFirstText;
- IgnoredErrorResult ignoredError;
for (RefPtr<Text> text = Text::FromNodeOrNull(textNode->GetNextSibling());
text && remainingLength;
text = Text::FromNodeOrNull(text->GetNextSibling())) {
@@ -218,10 +215,11 @@ NS_IMETHODIMP CompositionTransaction::DoTransaction() {
std::min(text->TextDataLength(), remainingLength);
// FIXME: We should delete the Text when all of its data is deleted
// now and we're working for HTMLEditor, see bug 2019186.
- editorBase->DoDeleteText(*text, 0, deletableLengthInText, ignoredError);
- NS_WARNING_ASSERTION(!ignoredError.Failed(),
- "EditorBase::DoDeleteText() failed, but ignored");
- ignoredError.SuppressException();
+ nsresult rv = editorBase->DoDeleteText(*text, 0, deletableLengthInText);
+ if (NS_FAILED(rv)) [[unlikely]] {
+ NS_WARNING("EditorBase::DoDeleteText() failed, but ignored");
+ return rv;
+ }
editorBase->RangeUpdaterRef().SelAdjDeleteText(*text, 0,
deletableLengthInText);
remainingLength -= deletableLengthInText;
@@ -261,17 +259,17 @@ NS_IMETHODIMP CompositionTransaction::UndoTransaction() {
}
const OwningNonNull<EditorBase> editorBase = *mEditorBase;
- IgnoredErrorResult error;
- editorBase->DoDeleteText(*textNode, mOffset, mStringToInsert.Length(), error);
- if (MOZ_UNLIKELY(error.Failed())) {
+ nsresult rv =
+ editorBase->DoDeleteText(*textNode, mOffset, mStringToInsert.Length());
+ if (NS_FAILED(rv)) [[unlikely]] {
NS_WARNING("EditorBase::DoDeleteText() failed");
- return error.StealNSResult();
+ return rv;
}
// set the selection to the insertion point where the string was removed
- editorBase->CollapseSelectionTo(EditorRawDOMPoint(textNode, mOffset), error);
- NS_ASSERTION(!error.Failed(), "EditorBase::CollapseSelectionTo() failed");
- return error.StealNSResult();
+ rv = editorBase->CollapseSelectionTo(EditorRawDOMPoint(textNode, mOffset));
+ NS_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::CollapseSelectionTo() failed");
+ return rv;
}
NS_IMETHODIMP CompositionTransaction::RedoTransaction() {
diff --git a/editor/libeditor/DeleteTextTransaction.cpp b/editor/libeditor/DeleteTextTransaction.cpp
index 6bfc0d6ca6b..4ecc428d5d3 100644
--- a/editor/libeditor/DeleteTextTransaction.cpp
+++ b/editor/libeditor/DeleteTextTransaction.cpp
@@ -146,10 +146,10 @@ NS_IMETHODIMP DeleteTextTransaction::DoTransaction() {
}
const OwningNonNull<EditorBase> editorBase = *mEditorBase;
- editorBase->DoDeleteText(*textNode, mOffset, mLengthToDelete, error);
- if (MOZ_UNLIKELY(error.Failed())) {
+ nsresult rv = editorBase->DoDeleteText(*textNode, mOffset, mLengthToDelete);
+ if (NS_FAILED(rv)) [[unlikely]] {
NS_WARNING("EditorBase::DoDeleteText() failed");
- return error.StealNSResult();
+ return rv;
}
editorBase->RangeUpdaterRef().SelAdjDeleteText(*textNode, mOffset,
@@ -195,10 +195,12 @@ NS_IMETHODIMP DeleteTextTransaction::UndoTransaction() {
return NS_ERROR_NOT_AVAILABLE;
}
const OwningNonNull<EditorBase> editorBase = *mEditorBase;
- IgnoredErrorResult error;
- editorBase->DoInsertText(*textNode, mOffset, mDeletedText, error);
- NS_WARNING_ASSERTION(!error.Failed(), "EditorBase::DoInsertText() failed");
- return error.StealNSResult();
+ nsresult rv = editorBase->DoInsertText(*textNode, mOffset, mDeletedText);
+ if (NS_FAILED(rv)) [[unlikely]] {
+ return rv;
+ }
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::DoInsertText() failed");
+ return rv;
}
NS_IMETHODIMP DeleteTextTransaction::RedoTransaction() {
diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp
index 07a22bd5d06..cc66a578948 100644
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -116,6 +116,7 @@
#include "nsIInlineSpellChecker.h" // for nsIInlineSpellChecker, etc.
#include "nsNameSpaceManager.h" // for kNameSpaceID_None, etc.
#include "nsINode.h" // for nsINode, etc.
+#include "nsIScriptElement.h" // for nsIScriptElement
#include "nsISelectionController.h" // for nsISelectionController, etc.
#include "nsISelectionDisplay.h" // for nsISelectionDisplay, etc.
#include "nsISupports.h" // for nsISupports
@@ -152,6 +153,95 @@ static LazyLogModule gHTMLEditorEditActionStartLog("HTMLEditorEditActionStart");
LazyLogModule gTextInputLog("EditorTextInput");
+/******************************************************************************
+ * mozilla::EditorBase::AutoTextEditVerifier
+ *****************************************************************************/
+class MOZ_STACK_CLASS EditorBase::AutoTextEditVerifier {
+ public:
+ MOZ_CAN_RUN_SCRIPT explicit AutoTextEditVerifier(EditorBase& aEditorBase,
+ const Text& aText)
+ : mEditorBase(aEditorBase), mText(aText), mDoCheck(false) {
+ if (mEditorBase.IsHTMLEditor() && mEditorBase.GetEditActionEditContext()) {
+ return;
+ }
+ // The most expensive cost of this check must be that we need to duplicate
+ // the data as a string. Fortunately, known unsafe situation is only when
+ // the `Text` is a descendant of a <script>. For avoiding the bad affect to
+ // usual web apps, we should check whether we need to check it strictly only
+ // when the known unsafe case.
+ for (Element* const element : mText.AncestorsOfType<Element>()) {
+ if (element->IsScriptElement()) {
+ mDoCheck = true;
+ break;
+ }
+ }
+ if (!mDoCheck) [[likely]] {
+ return;
+ }
+ mText.AppendTextTo(mExpectedData);
+ mParentNode = mText.GetParentNode();
+ }
+
+ [[nodiscard]] bool IsExpectedData() const {
+ if (!mDoCheck) [[likely]] {
+ return true;
+ }
+ if (mText.TextDataLength() != mExpectedData.Length() ||
+ mParentNode != mText.GetParentNode()) [[unlikely]] {
+ return false;
+ }
+ return mText.DataBuffer().Equals(mExpectedData);
+ }
+
+ void WillInsertText(uint32_t aOffset, const nsAString& aStringToInsert) {
+ if (!mDoCheck) {
+ return;
+ }
+ // FYI: CharacterData::InsertData() returns error if aOffset is greater than
+ // its data length, but in that case, IsExpectedData() won't be used. For
+ // unexpected behavior change of CharacterData, we should make mExpectedData
+ // even in the case.
+ mExpectedData.Insert(aStringToInsert,
+ std::min<uint32_t>(aOffset, mExpectedData.Length()));
+ }
+
+ void WillDeleteText(uint32_t aOffset, uint32_t aCount) {
+ if (!mDoCheck) {
+ return;
+ }
+ // FYI: CharacterData::DeleteData() returns error if aOffset is greater than
+ // its data length, but in that case, IsExpectedData() won't be used. For
+ // unexpected behavior change of CharacterData, we should make mExpectedData
+ // even in the case.
+ const uint32_t offset = std::min<uint32_t>(aOffset, mExpectedData.Length());
+ const uint32_t count = aCount > mExpectedData.Length() - offset
+ ? mExpectedData.Length() - offset
+ : aCount;
+ if (!count) {
+ return;
+ }
+ mExpectedData.Cut(offset, count);
+ }
+
+ void WillReplaceText(uint32_t aOffset, uint32_t aCount,
+ const nsAString& aStringToInsert) {
+ WillDeleteText(aOffset, aCount);
+ WillInsertText(aOffset, aStringToInsert);
+ }
+
+ void WillSetText(const nsAString& aStringToSet) {
+ WillDeleteText(0, mText.TextDataLength());
+ WillInsertText(0, aStringToSet);
+ }
+
+ private:
+ MOZ_KNOWN_LIVE EditorBase& mEditorBase;
+ MOZ_KNOWN_LIVE const Text& mText;
+ nsCOMPtr<nsINode> mParentNode;
+ nsAutoString mExpectedData;
+ bool mDoCheck;
+};
+
/*****************************************************************************
* mozilla::EditorBase
*****************************************************************************/
@@ -3205,68 +3295,94 @@ nsresult EditorBase::OnEndHandlingTopLevelEditSubAction() {
return NS_OK;
}
-void EditorBase::DoInsertText(Text& aText, uint32_t aOffset,
- const nsAString& aStringToInsert,
- ErrorResult& aRv) {
+nsresult EditorBase::DoInsertText(Text& aText, uint32_t aOffset,
+ const nsAString& aStringToInsert) {
+ AutoTextEditVerifier verify(*this, aText);
+ verify.WillInsertText(aOffset, aStringToInsert);
{
AutoCharacterDataAPIWrapper charDataWrapper(*this, aText);
- aRv = charDataWrapper.InsertData(aOffset, aStringToInsert);
- if (MOZ_UNLIKELY(aRv.Failed())) {
+ nsresult rv = charDataWrapper.InsertData(aOffset, aStringToInsert);
+ if (NS_WARN_IF(Destroyed())) [[unlikely]] {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
+ if (NS_FAILED(rv)) [[unlikely]] {
NS_WARNING("AutoCharacterDataAPIWrapper::InsertData() failed");
- return;
+ return rv;
}
- NS_WARNING_ASSERTION(charDataWrapper.IsExpectedResult(aStringToInsert),
+ NS_WARNING_ASSERTION(!verify.IsExpectedData() ||
+ charDataWrapper.IsExpectedResult(aStringToInsert),
"Inserting data caused other mutations, but ignored");
}
if (IsTextEditor() && !aStringToInsert.IsEmpty()) {
- aRv = MOZ_KnownLive(AsTextEditor())
- ->DidInsertText(aText.TextLength(), aOffset,
- aStringToInsert.Length());
- NS_WARNING_ASSERTION(!aRv.Failed(), "TextEditor::DidInsertText() failed");
+ nsresult rv = MOZ_KnownLive(AsTextEditor())
+ ->DidInsertText(aText.TextLength(), aOffset,
+ aStringToInsert.Length());
+ if (NS_FAILED(rv)) [[unlikely]] {
+ NS_WARNING("TextEditor::DidInsertText() failed");
+ return rv;
+ }
}
+ return verify.IsExpectedData() ? NS_OK : NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE;
}
-void EditorBase::DoDeleteText(Text& aText, uint32_t aOffset, uint32_t aCount,
- ErrorResult& aRv) {
+nsresult EditorBase::DoDeleteText(Text& aText, uint32_t aOffset,
+ uint32_t aCount) {
+ AutoTextEditVerifier verify(*this, aText);
+ verify.WillDeleteText(aOffset, aCount);
if (IsTextEditor() && aCount > 0) {
AsTextEditor()->WillDeleteText(aText.TextLength(), aOffset, aCount);
}
- AutoCharacterDataAPIWrapper charDataWrapper(*this, aText);
- aRv = charDataWrapper.DeleteData(aOffset, aCount);
- if (MOZ_UNLIKELY(aRv.Failed())) {
- NS_WARNING("AutoCharacterDataAPIWrapper::DeleteData() failed");
- return;
+ {
+ AutoCharacterDataAPIWrapper charDataWrapper(*this, aText);
+ nsresult rv = charDataWrapper.DeleteData(aOffset, aCount);
+ if (NS_WARN_IF(Destroyed())) {
+ return NS_ERROR_EDITOR_DESTROYED;
+ }
+ if (NS_FAILED(rv)) [[unlikely]] {
+ NS_WARNING("AutoCharacterDataAPIWrapper::DeleteData() failed");
+ return rv;
+ }
+ NS_WARNING_ASSERTION(!verify.IsExpectedData() ||
+ charDataWrapper.IsExpectedResult(EmptyString()),
+ "Deleting data caused other mutations, but ignored");
}
- NS_WARNING_ASSERTION(charDataWrapper.IsExpectedResult(EmptyString()),
- "Deleting data caused other mutations, but ignored");
+ return verify.IsExpectedData() ? NS_OK : NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE;
}
... (file diff truncated)
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/dom/base/test/gtest/TestCharacterDataBuffer.cpp b/dom/base/test/gtest/TestCharacterDataBuffer.cpp
index c41467dce09..6f4a9d7dd31 100644
--- a/dom/base/test/gtest/TestCharacterDataBuffer.cpp
+++ b/dom/base/test/gtest/TestCharacterDataBuffer.cpp
@@ -536,4 +536,108 @@ TEST(CharacterDataBufferTest, SafeAPIsChar1b)
EXPECT_EQ(characterDataBuffer.SafeCharAt(100), u'\0');
}
+TEST(CharacterDataBufferTest, EqualsString1b)
+{
+ const RefPtr<Document> doc = CreateHTMLDoc();
+ const RefPtr<nsTextNode> textNode = doc->CreateTextNode(EmptyString());
+ MOZ_RELEASE_ASSERT(textNode);
+ CharacterDataBuffer& characterDataBuffer =
+ const_cast<CharacterDataBuffer&>(textNode->DataBuffer());
+
+ MOZ_RELEASE_ASSERT(!characterDataBuffer.Is2b());
+ EXPECT_EQ(characterDataBuffer.Equals(EmptyString()), true);
+ EXPECT_EQ(characterDataBuffer.Equals(u"a"_ns), false);
+
+ characterDataBuffer.SetTo(u"abc"_ns, /* aUpdateBidi = */ false,
+ /* aForce2b = */ false);
+ MOZ_RELEASE_ASSERT(!characterDataBuffer.Is2b());
+ EXPECT_EQ(characterDataBuffer.Equals(u"abc"_ns), true);
+ EXPECT_EQ(characterDataBuffer.Equals(u"abcd"_ns), false);
+ EXPECT_EQ(characterDataBuffer.Equals(u"ab"_ns), false);
+ EXPECT_EQ(characterDataBuffer.Equals(EmptyString()), false);
+
+ characterDataBuffer.SetTo(u"\xC0"_ns + u"bc"_ns, /* aUpdateBidi = */ false,
+ /* aForce2b = */ false);
+ MOZ_RELEASE_ASSERT(!characterDataBuffer.Is2b());
+ EXPECT_EQ(characterDataBuffer.Equals(u"\xC0"_ns + u"bc"_ns), true);
+ EXPECT_EQ(characterDataBuffer.Equals(u"abc"_ns), false);
+ EXPECT_EQ(characterDataBuffer.Equals(EmptyString()), false);
+}
+
+TEST(CharacterDataBufferTest, EqualsString2b)
+{
+ const RefPtr<Document> doc = CreateHTMLDoc();
+ const RefPtr<nsTextNode> textNode = doc->CreateTextNode(EmptyString());
+ MOZ_RELEASE_ASSERT(textNode);
+ CharacterDataBuffer& characterDataBuffer =
+ const_cast<CharacterDataBuffer&>(textNode->DataBuffer());
+
+ characterDataBuffer.SetTo(u"abc"_ns, /* aUpdateBidi = */ false,
+ /* aForce2b = */ true);
+ MOZ_RELEASE_ASSERT(characterDataBuffer.Is2b());
+ EXPECT_EQ(characterDataBuffer.Equals(u"abc"_ns), true);
+ EXPECT_EQ(characterDataBuffer.Equals(u"abcd"_ns), false);
+ EXPECT_EQ(characterDataBuffer.Equals(u"ab"_ns), false);
+ EXPECT_EQ(characterDataBuffer.Equals(EmptyString()), false);
+
+ characterDataBuffer.SetTo(u"\xC0"_ns + u"bc"_ns, /* aUpdateBidi = */ false,
+ /* aForce2b = */ true);
+ MOZ_RELEASE_ASSERT(characterDataBuffer.Is2b());
+ EXPECT_EQ(characterDataBuffer.Equals(u"\xC0"_ns + u"bc"_ns), true);
+ EXPECT_EQ(characterDataBuffer.Equals(u"abc"_ns), false);
+ EXPECT_EQ(characterDataBuffer.Equals(EmptyString()), false);
+}
+
+TEST(CharacterDataBufferTest, EqualsCString1b)
+{
+ const RefPtr<Document> doc = CreateHTMLDoc();
+ const RefPtr<nsTextNode> textNode = doc->CreateTextNode(EmptyString());
+ MOZ_RELEASE_ASSERT(textNode);
+ CharacterDataBuffer& characterDataBuffer =
+ const_cast<CharacterDataBuffer&>(textNode->DataBuffer());
+
+ MOZ_RELEASE_ASSERT(!characterDataBuffer.Is2b());
+ EXPECT_EQ(characterDataBuffer.Equals(EmptyCString()), true);
+ EXPECT_EQ(characterDataBuffer.Equals("a"_ns), false);
+
+ characterDataBuffer.SetTo(u"abc"_ns, /* aUpdateBidi = */ false,
+ /* aForce2b = */ false);
+ MOZ_RELEASE_ASSERT(!characterDataBuffer.Is2b());
+ EXPECT_EQ(characterDataBuffer.Equals("abc"_ns), true);
+ EXPECT_EQ(characterDataBuffer.Equals("abcd"_ns), false);
+ EXPECT_EQ(characterDataBuffer.Equals("ab"_ns), false);
+ EXPECT_EQ(characterDataBuffer.Equals(EmptyCString()), false);
+
+ characterDataBuffer.SetTo(u"\xC0"_ns + u"bc"_ns, /* aUpdateBidi = */ false,
+ /* aForce2b = */ false);
+ MOZ_RELEASE_ASSERT(!characterDataBuffer.Is2b());
+ EXPECT_EQ(characterDataBuffer.Equals("\xC0"_ns + "bc"_ns), true);
+ EXPECT_EQ(characterDataBuffer.Equals("abc"_ns), false);
+ EXPECT_EQ(characterDataBuffer.Equals(EmptyCString()), false);
+}
+
+TEST(CharacterDataBufferTest, EqualsCString2b)
+{
+ const RefPtr<Document> doc = CreateHTMLDoc();
+ const RefPtr<nsTextNode> textNode = doc->CreateTextNode(EmptyString());
+ MOZ_RELEASE_ASSERT(textNode);
+ CharacterDataBuffer& characterDataBuffer =
+ const_cast<CharacterDataBuffer&>(textNode->DataBuffer());
+
+ characterDataBuffer.SetTo(u"abc"_ns, /* aUpdateBidi = */ false,
+ /* aForce2b = */ true);
+ MOZ_RELEASE_ASSERT(characterDataBuffer.Is2b());
+ EXPECT_EQ(characterDataBuffer.Equals("abc"_ns), true);
+ EXPECT_EQ(characterDataBuffer.Equals("abcd"_ns), false);
+ EXPECT_EQ(characterDataBuffer.Equals("ab"_ns), false);
+ EXPECT_EQ(characterDataBuffer.Equals(EmptyCString()), false);
+
+ characterDataBuffer.SetTo(u"\xC0"_ns + u"bc"_ns, /* aUpdateBidi = */ false,
+ /* aForce2b = */ true);
+ MOZ_RELEASE_ASSERT(characterDataBuffer.Is2b());
+ EXPECT_EQ(characterDataBuffer.Equals("\xC0"_ns + "bc"_ns), true);
+ EXPECT_EQ(characterDataBuffer.Equals("abc"_ns), false);
+ EXPECT_EQ(characterDataBuffer.Equals(EmptyCString()), false);
+}
+
}; // namespace mozilla::dom
Loading diff…
References
On This Page