зеркало из https://github.com/mozilla/gecko-dev.git
Bug 803924 - Crash with range, splitText. r=smaug
This commit is contained in:
Родитель
43366dc10f
Коммит
e446981985
|
@ -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
|
||||
}
|
Загрузка…
Ссылка в новой задаче