Bug 804784 - Crash with range, normalize. r=smaug
authorMats Palmgren <matspal@gmail.com>
Tue, 20 Nov 2012 21:14:15 +0100
changeset 113812 6ee5de769c4ac0e067acc84456f945690d092fa0
parent 113811 88bad356f5a4d3dfc4888305e3670ec80e576a4c
child 113813 bc10a4e38e7ab8d124916e496b6d3cb3b941e3e9
push id23890
push userryanvm@gmail.com
push dateWed, 21 Nov 2012 02:43:32 +0000
treeherdermozilla-central@4f19e7fd8bea [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs804784
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 804784 - Crash with range, normalize. r=smaug
content/base/src/nsRange.cpp
content/base/test/Makefile.in
content/base/test/test_textnode_normalize_in_selection.html
--- a/content/base/src/nsRange.cpp
+++ b/content/base/src/nsRange.cpp
@@ -495,17 +495,37 @@ nsRange::CharacterDataChanged(nsIDocumen
     }
     if (removed == mEndParent) {
       newEndOffset = static_cast<uint32_t>(mEndOffset) + aInfo->mChangeStart;
       newEndNode = aContent;
       if (MOZ_UNLIKELY(removed == mRoot)) {
         newRoot = IsValidBoundary(newEndNode);
       }
     }
+    // When the removed text node's parent is one of our boundary nodes we may
+    // need to adjust the offset to account for the removed node. However,
+    // there will also be a ContentRemoved notification later so the only cases
+    // we need to handle here is when the removed node is the text node after
+    // the boundary.  (The m*Offset > 0 check is an optimization - a boundary
+    // point before the first child is never affected by normalize().)
+    nsINode* parentNode = aContent->GetParentNode();
+    if (parentNode == mStartParent && mStartOffset > 0 &&
+        mStartOffset < parentNode->GetChildCount() &&
+        removed == parentNode->GetChildAt(mStartOffset)) {
+      newStartNode = aContent;
+      newStartOffset = aInfo->mChangeStart;
+    }
+    if (parentNode == mEndParent && mEndOffset > 0 &&
+        mEndOffset < parentNode->GetChildCount() &&
+        removed == parentNode->GetChildAt(mEndOffset)) {
+      newEndNode = aContent;
+      newEndOffset = aInfo->mChangeEnd;
+    }
   }
+
   if (newStartNode || newEndNode) {
     if (!newStartNode) {
       newStartNode = mStartParent;
       newStartOffset = mStartOffset;
     }
     if (!newEndNode) {
       newEndNode = mEndParent;
       newEndOffset = mEndOffset;
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -589,16 +589,17 @@ MOCHITEST_FILES_B = \
     file_mixed_content_main_bug803225.html \
     file_mixed_content_main_bug803225_websocket_wsh.py \
     bug803225_test_mailto.html \
 		test_bug789856.html \
 		file_bug804395.jar \
 		test_bug804395.html \
 		test_bug809003.html \
 		test_textnode_split_in_selection.html \
+		test_textnode_normalize_in_selection.html \
 		$(NULL)
 
 # OOP tests don't work on Windows (bug 763081) or native-fennec
 # (see Bug 774939)
 ifneq ($(OS_ARCH),WINNT)
 ifndef MOZ_ANDROID_OMTC
 MOCHITEST_FILES_B += \
 		test_messagemanager_assertpermission.html \
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_textnode_normalize_in_selection.html
@@ -0,0 +1,201 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=804784
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 804784</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=804784">Mozilla Bug 804784</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 804784 **/
+
+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,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) div.normalize();
+  check(r,s,run+" selEnd "+child+","+index);
+}
+
+function selStart(div,child,index,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) div.normalize();
+  check(r,s,run+" selStart "+child+","+index);
+}
+
+function selMiddleStart(div,child,index,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);
+  div.normalize();
+  check(r,s,run+" selMiddleStart "+child+","+index);
+}
+
+function selMiddleEnd(div,child,index,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) div.normalize();
+  check(r,s,run+" selMiddleEnd "+child+","+index);
+}
+
+function mergeBefore(div,child,index,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.normalize();
+  check(r,s,run+" mergeBefore "+child+","+index);
+}
+
+function runTests(s)
+{
+  run = s+":";
+  selEnd(newDiv('111'), 0,0,'111');
+  selEnd(newDiv('111'), 0,1,'11');
+  selEnd(newDiv('111'), 0,2,'1');
+  selEnd(newDiv(''), 0,0,'');
+  selEnd(newDiv('',''), 1,0,'');
+  selEnd(newDiv('','',''), 1,0,'');
+  selEnd(newDiv('111','222'), 0,1,'11222');
+  selEnd(newDiv('111','222'), 0,2,'1222');
+  selEnd(newDiv('111','222'), 1,1,'22');
+  selEnd(newDiv('','222'), 1,2,'2');
+  selEnd(newDiv('111',''), 0,1,'11');
+  selEnd(newDiv('111','222'), 1,2,'2');
+  selEnd(newDiv('111','222','333'), 1,1,'22333');
+  selEnd(newDiv('111','222','333'), 1,2,'2333');
+  selEnd(newDiv('111','','333'), 0,2,'1333');
+  selEnd(newDiv('111','','333'), 1,0,'333');
+  selEnd(newDiv('111','','333'), 2,0,'333');
+
+  selStart(newDiv('111'), 0,0,'');
+  selStart(newDiv('111'), 0,1,'1');
+  selStart(newDiv('111'), 0,2,'11');
+  selStart(newDiv(''), 0,0,'');
+  selStart(newDiv('111','222'), 0,1,'1');
+  selStart(newDiv('111','222'), 0,2,'11');
+  selStart(newDiv('111','222'), 1,1,'1112');
+  selStart(newDiv('111','222'), 1,2,'11122');
+  selStart(newDiv('111',''), 1,0,'111');
+  selStart(newDiv('111',''), 0,2,'11');
+  selStart(newDiv('111','222','333'), 1,1,'1112');
+  selStart(newDiv('111','222','333'), 1,2,'11122');
+  selStart(newDiv('111','222','333'), 1,2,'11122');
+  selStart(newDiv('111','','333'), 1,0,'111');
+
+  selMiddleStart(newDiv('111','222','333'), 1,1,'2');
+  selMiddleStart(newDiv('111','222','333'), 1,2,'22');
+  selMiddleStart(newDiv('111','222','333'), 2,1,'2223');
+  selMiddleStart(newDiv('111','222','333'), 2,2,'22233');
+  selMiddleStart(newDiv('111','','333'), 2,2,'33');
+  selMiddleStart(newDiv('111','222',''), 2,0,'222');
+
+  selMiddleEnd(newDiv('111','222','333'), 0,1,'11222');
+  selMiddleEnd(newDiv('111','222','333'), 0,2,'1222');
+  selMiddleEnd(newDiv('111','222','333'), 1,1,'22');
+  selMiddleEnd(newDiv('111','222','333'), 1,2,'2');
+  selMiddleEnd(newDiv('111','','333'), 1,0,'');
+  selMiddleEnd(newDiv('','222','333'), 0,0,'222');
+
+  mergeBefore(newDiv('111','222'), 1,1,'2');
+  mergeBefore(newDiv('111','222','333'), 1,2,'22');
+  mergeBefore(newDiv('111','222','333'), 2,1,'2223');
+  mergeBefore(newDiv('111','222','333'), 2,2,'22233');
+  mergeBefore(newDiv('111','','333'), 2,0,'');
+  mergeBefore(newDiv('111','','333'), 2,2,'33');
+}
+
+function boom()
+{
+  runTests("dry run");  // this is to verify the result strings without normalize()
+  dry = false;
+  flush = false;
+  runTests("no flush");
+  flush = true;
+  runTests("flush");
+}
+
+boom();
+
+
+</script>
+</pre>
+</body>
+</html>