Bug 803924 - Crash with range, splitText. r=smaug

This commit is contained in:
Mats Palmgren 2012-11-20 21:14:15 +01:00
Родитель 43366dc10f
Коммит e446981985
6 изменённых файлов: 310 добавлений и 2 удалений

Просмотреть файл

@ -375,6 +375,8 @@ nsRange::CharacterDataChanged(nsIDocument* aDocument,
nsIContent* aContent,
CharacterDataChangeInfo* aInfo)
{
MOZ_ASSERT(mAssertNextInsertOrAppendIndex == -1,
"splitText failed to notify insert/append?");
NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned");
nsINode* newRoot = nullptr;
@ -383,6 +385,35 @@ nsRange::CharacterDataChanged(nsIDocument* aDocument,
uint32_t newStartOffset = 0;
uint32_t newEndOffset = 0;
if (aInfo->mDetails &&
aInfo->mDetails->mType == CharacterDataChangeInfo::Details::eSplit) {
// If the splitted text node is immediately before a range boundary point
// that refers to a child index (i.e. its parent is the boundary container)
// then we need to increment the corresponding offset to account for the new
// text node that will be inserted. If so, we need to prevent the next
// ContentInserted or ContentAppended for this range from incrementing it
// again (when the new text node is notified).
nsINode* parentNode = aContent->GetParentNode();
int32_t index = -1;
if (parentNode == mEndParent && mEndOffset > 0 &&
(index = parentNode->IndexOf(aContent)) + 1 == mEndOffset) {
++mEndOffset;
mEndOffsetWasIncremented = true;
}
if (parentNode == mStartParent && mStartOffset > 0 &&
(index != -1 ? index : parentNode->IndexOf(aContent)) + 1 == mStartOffset) {
++mStartOffset;
mStartOffsetWasIncremented = true;
}
#ifdef DEBUG
if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) {
mAssertNextInsertOrAppendIndex =
(mStartOffsetWasIncremented ? mStartOffset : mEndOffset) - 1;
mAssertNextInsertOrAppendNode = aInfo->mDetails->mNextSibling;
}
#endif
}
// If the changed node contains our start boundary and the change starts
// before the boundary we'll need to adjust the offset.
if (aContent == mStartParent &&
@ -504,6 +535,17 @@ nsRange::ContentAppended(nsIDocument* aDocument,
child = child->GetNextSibling();
}
}
if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) {
MOZ_ASSERT(mAssertNextInsertOrAppendIndex == aNewIndexInContainer);
MOZ_ASSERT(mAssertNextInsertOrAppendNode == aFirstNewContent);
MOZ_ASSERT(aFirstNewContent->IsNodeOfType(nsINode::eTEXT));
mStartOffsetWasIncremented = mEndOffsetWasIncremented = false;
#ifdef DEBUG
mAssertNextInsertOrAppendIndex = -1;
mAssertNextInsertOrAppendNode = nullptr;
#endif
}
}
void
@ -517,10 +559,12 @@ nsRange::ContentInserted(nsIDocument* aDocument,
nsINode* container = NODE_FROM(aContainer, aDocument);
// Adjust position if a sibling was inserted.
if (container == mStartParent && aIndexInContainer < mStartOffset) {
if (container == mStartParent && aIndexInContainer < mStartOffset &&
!mStartOffsetWasIncremented) {
++mStartOffset;
}
if (container == mEndParent && aIndexInContainer < mEndOffset) {
if (container == mEndParent && aIndexInContainer < mEndOffset &&
!mEndOffsetWasIncremented) {
++mEndOffset;
}
if (container->IsSelectionDescendant() &&
@ -528,6 +572,17 @@ nsRange::ContentInserted(nsIDocument* aDocument,
MarkDescendants(aChild);
aChild->SetDescendantOfCommonAncestorForRangeInSelection();
}
if (mStartOffsetWasIncremented || mEndOffsetWasIncremented) {
MOZ_ASSERT(mAssertNextInsertOrAppendIndex == aIndexInContainer);
MOZ_ASSERT(mAssertNextInsertOrAppendNode == aChild);
MOZ_ASSERT(aChild->IsNodeOfType(nsINode::eTEXT));
mStartOffsetWasIncremented = mEndOffsetWasIncremented = false;
#ifdef DEBUG
mAssertNextInsertOrAppendIndex = -1;
mAssertNextInsertOrAppendNode = nullptr;
#endif
}
}
void
@ -538,6 +593,9 @@ nsRange::ContentRemoved(nsIDocument* aDocument,
nsIContent* aPreviousSibling)
{
NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned");
MOZ_ASSERT(!mStartOffsetWasIncremented && !mEndOffsetWasIncremented &&
mAssertNextInsertOrAppendIndex == -1,
"splitText failed to notify insert/append?");
nsINode* container = NODE_FROM(aContainer, aDocument);
bool gravitateStart = false;
@ -581,6 +639,9 @@ nsRange::ContentRemoved(nsIDocument* aDocument,
void
nsRange::ParentChainChanged(nsIContent *aContent)
{
MOZ_ASSERT(!mStartOffsetWasIncremented && !mEndOffsetWasIncremented &&
mAssertNextInsertOrAppendIndex == -1,
"splitText failed to notify insert/append?");
NS_ASSERTION(mRoot == aContent, "Wrong ParentChainChanged notification?");
nsINode* newRoot = IsValidBoundary(mStartParent);
NS_ASSERTION(newRoot, "No valid boundary or root found!");

Просмотреть файл

@ -30,6 +30,12 @@ public:
, mIsDetached(false)
, mMaySpanAnonymousSubtrees(false)
, mInSelection(false)
, mStartOffsetWasIncremented(false)
, mEndOffsetWasIncremented(false)
#ifdef DEBUG
, mAssertNextInsertOrAppendIndex(-1)
, mAssertNextInsertOrAppendNode(nullptr)
#endif
{}
virtual ~nsRange();
@ -229,6 +235,12 @@ protected:
bool mIsDetached;
bool mMaySpanAnonymousSubtrees;
bool mInSelection;
bool mStartOffsetWasIncremented;
bool mEndOffsetWasIncremented;
#ifdef DEBUG
int32_t mAssertNextInsertOrAppendIndex;
nsINode* mAssertNextInsertOrAppendNode;
#endif
};
#endif /* nsRange_h___ */

Просмотреть файл

@ -593,6 +593,7 @@ MOCHITEST_FILES_B = \
file_bug804395.jar \
test_bug804395.html \
test_bug809003.html \
test_textnode_split_in_selection.html \
$(NULL)
# OOP tests don't work on Windows (bug 763081) or native-fennec

Просмотреть файл

@ -0,0 +1,221 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=803924
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 803924</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=803924">Mozilla Bug 803924</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 803924 **/
var sel = document.getSelection();
var flush = true;
var dry = true;
var run = "";
var empty_range;
var empty_first_text_range;
var empty_last_text_range;
var full_range;
function check(range, expected, test)
{
is(""+range, expected, test);
is(""+empty_range, "", "empty range test after: "+test);
is(""+empty_first_text_range, "", "empty first text range test after: "+test);
if (empty_last_text_range) is(""+empty_last_text_range, "", "empty last text range test after: "+test);
is(""+full_range, full_range.startContainer.textContent, "full range test after: "+test);
}
function newDiv()
{
var div = document.createElement('div');
for (var i = 0; i < arguments.length; ++i) {
div.appendChild(document.createTextNode(arguments[i]));
}
document.body.appendChild(div)
empty_range = document.createRange();
empty_range.setStart(div,0);
empty_range.setEnd(div,0);
var firstTextNode = div.childNodes[0];
var lastTextNode = div.childNodes[div.childNodes.length - 1];
empty_first_text_range = document.createRange();
empty_first_text_range.setStart(firstTextNode,0);
empty_first_text_range.setEnd(firstTextNode,0);
empty_last_text_range = null;
if (firstTextNode != lastTextNode) {
empty_last_text_range = document.createRange();
empty_last_text_range.setStart(lastTextNode,0);
empty_last_text_range.setEnd(lastTextNode,0);
}
full_range = document.createRange();
full_range.setStart(div,0);
full_range.setEnd(div,div.childNodes.length);
return div;
}
function selEnd(div,child,index,split,s)
{
var start = div.childNodes[child];
var r = document.createRange();
sel.addRange(r);
r.setStart(start, index);
r.setEnd(div, div.childNodes.length);
if (!dry) start.splitText(split);
check(r,s,run+" selEnd "+child+","+index+","+split);
}
function selStart(div,child,index,split,s)
{
if (flush) document.body.getClientRects();
var start = div.childNodes[child];
var r = document.createRange();
sel.addRange(r);
r.setStart(div, 0);
r.setEnd(start, index);
if (!dry) start.splitText(split);
check(r,s,run+" selStart "+child+","+index+","+split);
}
function selMiddleStart(div,child,index,split,s)
{
if (flush) document.body.getClientRects();
var start = div.childNodes[child];
var r = document.createRange();
sel.addRange(r);
r.setStart(div, 1);
r.setEnd(start, index);
if (!dry) start.splitText(split);
check(r,s,run+" selMiddleStart "+child+","+index+","+split);
}
function selMiddleEnd(div,child,index,split,s)
{
if (flush) document.body.getClientRects();
var start = div.childNodes[child];
var r = document.createRange();
sel.addRange(r);
r.setStart(start, index);
r.setEnd(div, 2);
if (!dry) start.splitText(split);
check(r,s,run+" selMiddleEnd "+child+","+index+","+split);
}
function splitBefore(div,child,index,split,s)
{
if (flush) document.body.getClientRects();
var start = div.childNodes[child];
var r = document.createRange();
sel.addRange(r);
r.setStart(div, 1);
r.setEnd(start, index);
if (!dry) div.childNodes[0].splitText(split);
check(r,s,run+" splitBefore "+child+","+index+","+split);
}
function runTests(s)
{
run = s+":";
selEnd(newDiv('111'), 0,0,0,'111');
selEnd(newDiv('111'), 0,0,1,'111');
selEnd(newDiv('111'), 0,0,3,'111');
selEnd(newDiv(''), 0,0,0,'');
selEnd(newDiv('111'), 0,1,0,'11');
selEnd(newDiv('111'), 0,2,1,'1');
selEnd(newDiv('111'), 0,1,3,'11');
selEnd(newDiv('111','222'), 0,1,0,'11222');
selEnd(newDiv('111','222'), 0,2,1,'1222');
selEnd(newDiv('111','222'), 0,1,3,'11222');
selEnd(newDiv('111','222'), 1,1,0,'22');
selEnd(newDiv('111','222'), 1,2,1,'2');
selEnd(newDiv('','222'), 1,1,1,'22');
selEnd(newDiv('','222'), 0,0,0,'222');
selEnd(newDiv('111',''), 0,1,0,'11');
selEnd(newDiv('111','222'), 1,1,3,'22');
selEnd(newDiv('111','222','333'), 1,1,0,'22333');
selEnd(newDiv('111','222','333'), 1,2,1,'2333');
selEnd(newDiv('111','222','333'), 1,1,3,'22333');
selEnd(newDiv('111','222',''), 1,1,3,'22');
selEnd(newDiv('111','','333'), 0,1,3,'11333');
selStart(newDiv('111'), 0,0,0,'');
selStart(newDiv('111'), 0,0,1,'');
selStart(newDiv('111'), 0,0,3,'');
selStart(newDiv('111'), 0,1,0,'1');
selStart(newDiv('111'), 0,2,1,'11');
selStart(newDiv('111'), 0,1,3,'1');
selStart(newDiv(''), 0,0,0,'');
selStart(newDiv('111','222'), 0,1,0,'1');
selStart(newDiv('111','222'), 0,2,1,'11');
selStart(newDiv('111','222'), 0,1,3,'1');
selStart(newDiv('111','222'), 1,1,0,'1112');
selStart(newDiv('111','222'), 1,2,1,'11122');
selStart(newDiv('111','222'), 1,1,3,'1112');
selStart(newDiv('','222'), 1,1,2,'2');
selStart(newDiv('','222'), 0,0,0,'');
selStart(newDiv('111',''), 1,0,0,'111');
selStart(newDiv('111','222','333'), 1,1,0,'1112');
selStart(newDiv('111','222','333'), 1,2,1,'11122');
selStart(newDiv('111','222','333'), 1,1,3,'1112');
selStart(newDiv('111','','333'), 1,0,0,'111');
selStart(newDiv('111','222',''), 1,1,3,'1112');
selMiddleStart(newDiv('111','222','333'), 1,1,0,'2');
selMiddleStart(newDiv('111','222','333'), 1,2,1,'22');
selMiddleStart(newDiv('111','222','333'), 1,1,3,'2');
selMiddleStart(newDiv('111','222','333'), 2,1,0,'2223');
selMiddleStart(newDiv('111','222','333'), 2,2,1,'22233');
selMiddleStart(newDiv('111','222','333'), 2,1,3,'2223');
selMiddleStart(newDiv('111','','333'), 2,1,2,'3');
selMiddleStart(newDiv('111','','333'), 1,0,0,'');
selMiddleEnd(newDiv('111','222','333'), 0,1,0,'11222');
selMiddleEnd(newDiv('111','222','333'), 0,2,1,'1222');
selMiddleEnd(newDiv('111','222','333'), 0,1,3,'11222');
selMiddleEnd(newDiv('111','222','333'), 1,1,0,'22');
selMiddleEnd(newDiv('111','222','333'), 1,2,1,'2');
selMiddleEnd(newDiv('111','222','333'), 1,1,3,'22');
selMiddleEnd(newDiv('111','','333'), 0,1,2,'11');
selMiddleEnd(newDiv('111','','333'), 0,1,3,'11');
selMiddleEnd(newDiv('111','','333'), 1,0,0,'');
splitBefore(newDiv('111','222','333'), 1,1,0,'2');
splitBefore(newDiv('111','222','333'), 1,2,1,'22');
splitBefore(newDiv('111','222','333'), 1,1,3,'2');
splitBefore(newDiv('111','222','333'), 2,1,0,'2223');
splitBefore(newDiv('111','222','333'), 2,2,1,'22233');
splitBefore(newDiv('111','222','333'), 2,1,3,'2223');
splitBefore(newDiv('','222','333'), 1,1,0,'2');
splitBefore(newDiv('','','333'), 1,0,0,'');
splitBefore(newDiv('','222',''), 2,0,0,'222');
splitBefore(newDiv('111','','333'), 2,1,2,'3');
}
function boom()
{
runTests("dry run"); // this is to verify the result strings without splitText()
dry = false;
flush = false;
runTests("no flush");
flush = true;
runTests("flush");
}
boom();
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -23,6 +23,7 @@ MOCHITEST_FILES := \
test_Range-extractContents.html.json \
test_Range-intersectsNode.html.json \
test_Range-isPointInRange.html.json \
test_Range-mutations.html.json \
test_Range-set.html.json \
test_interfaces.html.json \
$(NULL)

Просмотреть файл

@ -0,0 +1,12 @@
{
"paras[0].firstChild.splitText(1), with unselected range on paras[0] from 0 to 1": true,
"paras[0].firstChild.splitText(1), with selected range on paras[0] from 0 to 1": true,
"paras[0].firstChild.splitText(1), with unselected range collapsed at (paras[0], 1)": true,
"paras[0].firstChild.splitText(1), with selected range collapsed at (paras[0], 1)": true,
"paras[0].firstChild.splitText(1), with unselected range from (paras[0].firstChild, 1) to (paras[0], 1)": true,
"paras[0].firstChild.splitText(1), with selected range from (paras[0].firstChild, 1) to (paras[0], 1)": true,
"paras[0].firstChild.splitText(2), with unselected range from (paras[0].firstChild, 1) to (paras[0], 1)": true,
"paras[0].firstChild.splitText(2), with selected range from (paras[0].firstChild, 1) to (paras[0], 1)": true,
"paras[0].firstChild.splitText(3), with unselected range from (paras[0].firstChild, 1) to (paras[0], 1)": true,
"paras[0].firstChild.splitText(3), with selected range from (paras[0].firstChild, 1) to (paras[0], 1)": true
}