Bug 1286464 part.0 Add eQueryTextRect tests for line breakers r?smaug draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Sat, 30 Jul 2016 22:00:30 +0900
changeset 394862 b8cb346d31620ba694c520faad5d550a18695ac5
parent 394860 a298bb7559ca85848b65aad803572fce99ba288b
child 394863 db18da2365f89019bdbe597c9148f797e0010762
push id24655
push usermasayuki@d-toybox.com
push dateMon, 01 Aug 2016 08:00:00 +0000
reviewerssmaug
bugs1286464
milestone50.0a1
Bug 1286464 part.0 Add eQueryTextRect tests for line breakers r?smaug MozReview-Commit-ID: 2SxNlyjc4KM
widget/tests/window_composition_text_querycontent.xul
--- a/widget/tests/window_composition_text_querycontent.xul
+++ b/widget/tests/window_composition_text_querycontent.xul
@@ -59,16 +59,30 @@ function is(aLeft, aRight, aMessage)
   window.opener.wrappedJSObject.SimpleTest.is(aLeft, aRight, aMessage);
 }
 
 function isnot(aLeft, aRight, aMessage)
 {
   window.opener.wrappedJSObject.SimpleTest.isnot(aLeft, aRight, aMessage);
 }
 
+function isSimilarTo(aLeft, aRight, aAllowedDifference, aMessage)
+{
+  if (Math.abs(aLeft - aRight) <= aAllowedDifference) {
+    ok(true, aMessage);
+  } else {
+    ok(false, aMessage + ", got=" + aLeft + ", expected=" + (aRight - aAllowedDifference) + "~" + (aRight + aAllowedDifference));
+  }
+}
+
+function isGreaterThan(aLeft, aRight, aMessage)
+{
+  ok(aLeft > aRight, aMessage + ", got=" + aLeft + ", expected minimum value=" + aRight);
+}
+
 function finish()
 {
   window.close();
 }
 
 function onunload()
 {
   window.opener.wrappedJSObject.SimpleTest.finish();
@@ -2228,16 +2242,523 @@ function runCompositionEventTest()
   input.removeEventListener("compositionend",
                             compositionEventHandlerForInput, true);
   input.removeEventListener("compositionupdate",
                             compositionEventHandlerForInput, true);
   input.removeEventListener("input",
                             formEventHandlerForInput, true);
 }
 
+function runQueryTextRectInContentEditableTest()
+{
+  contenteditable.focus();
+
+  contenteditable.innerHTML = "<p>abc</p><p>def</p>";
+                      // \n    0  123    4  567
+                      // \r\n  01 234    56 789
+
+  var description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
+
+  // "a"
+  var a = synthesizeQueryTextRect(kLFLen, 1);
+  if (!checkQueryContentResult(a, description + "rect for 'a'")) {
+    return;
+  }
+
+  // "b"
+  var b = synthesizeQueryTextRect(kLFLen + 1, 1);
+  if (!checkQueryContentResult(b, description + "rect for 'b'")) {
+    return;
+  }
+
+  is(b.top, a.top, description + "'a' and 'b' should be at same top");
+  isSimilarTo(b.left, a.left + a.width, 2, description + "left of 'b' should be at similar to right of 'a'");
+  is(b.height, a.height, description + "'a' and 'b' should be same height");
+
+  // "c"
+  var c = synthesizeQueryTextRect(kLFLen + 2, 1);
+  if (!checkQueryContentResult(c, description + "rect for 'c'")) {
+    return;
+  }
+
+  is(c.top, b.top, description + "'b' and 'c' should be at same top");
+  isSimilarTo(c.left, b.left + b.width, 2, description + "left of 'c' should be at similar to right of 'b'");
+  is(c.height, b.height, description + "'b' and 'c' should be same height");
+
+  // 2nd <p> (can be computed with the rect of 'c')
+  var p2 = synthesizeQueryTextRect(kLFLen + 3, 1);
+  if (!checkQueryContentResult(p2, description + "rect for 2nd <p>")) {
+    return;
+  }
+
+  is(p2.top, c.top, description + "'c' and a line breaker caused by 2nd <p> should be at same top");
+  isSimilarTo(p2.left, c.left + c.width, 2, description + "left of a line breaker caused by 2nd <p> should be at similar to right of 'c'");
+  is(p2.height, c.height, description + "'c' and a line breaker caused by 2nd <p> should be same height");
+
+  if (kLFLen > 1) {
+    // \n of \r\n
+    var p2_2 = synthesizeQueryTextRect(kLFLen + 4, 1);
+    if (!checkQueryContentResult(p2_2, description + "rect for \n of \r\n caused by 2nd <p>")) {
+      return;
+    }
+
+    is(p2_2.top, p2.top, description + "'\\r' and '\\n' should be at same top");
+    is(p2_2.left, p2.left, description + "'\\r' and '\\n' should be at same top");
+    is(p2_2.height, p2.height, description + "'\\r' and '\\n' should be same height");
+    is(p2_2.width, p2.width, description + "'\\r' and '\\n' should be same width");
+  }
+
+  // "d"
+  var d = synthesizeQueryTextRect(kLFLen * 2 + 3, 1);
+  if (!checkQueryContentResult(d, description + "rect for 'd'")) {
+    return;
+  }
+
+  isGreaterThan(d.top, a.top + a.height, description + "top of 'd' should be greater than bottom of 'a'");
+  is(d.left, a.left, description + "'a' and 'd' should be same at same left");
+  is(d.height, a.height, description + "'a' and 'd' should be same height");
+
+  // "e"
+  var e = synthesizeQueryTextRect(kLFLen * 2 + 4, 1);
+  if (!checkQueryContentResult(e, description + "rect for 'e'")) {
+    return;
+  }
+
+  is(e.top, d.top, description + "'d' and 'd' should be at same top");
+  isSimilarTo(e.left, d.left + d.width, 2, description + "left of 'e' should be at similar to right of 'd'");
+  is(e.height, d.height, description + "'d' and 'e' should be same height");
+
+  // "f"
+  var f = synthesizeQueryTextRect(kLFLen * 2 + 5, 1);
+  if (!checkQueryContentResult(f, description + "rect for 'f'")) {
+    return;
+  }
+
+  is(f.top, e.top, description + "'e' and 'f' should be at same top");
+  isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
+  is(f.height, e.height, description + "'e' and 'f' should be same height");
+
+  // next of "f" (can be computed with rect of 'f')
+  var next_f = synthesizeQueryTextRect(kLFLen * 2 + 6, 1);
+  if (!checkQueryContentResult(next_f, description + "rect for next of 'f'")) {
+    return;
+  }
+
+  is(next_f.top, d.top, 2, description + "'f' and next of 'f' should be at same top");
+  isSimilarTo(next_f.left, f.left + f.width, 2, description + "left of next of 'f' should be at similar to right of 'f'");
+  is(next_f.height, d.height, description + "'f' and next of 'f' should be same height");
+
+  // too big offset for the editor
+  var tooBigOffset = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
+  if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
+    return;
+  }
+
+  is(tooBigOffset.top, next_f.top, description + "too big offset and next of 'f' should be at same top");
+  is(tooBigOffset.left, next_f.left, description + "too big offset and next of 'f' should be at same left");
+  is(tooBigOffset.height, next_f.height, description + "too big offset and next of 'f' should be same height");
+  is(tooBigOffset.width, next_f.width, description + "too big offset and next of 'f' should be same width");
+
+  contenteditable.innerHTML = "<p>abc</p><p>def</p><p><br></p>";
+                      // \n    0  123    4  567    8  9
+                      // \r\n  01 234    56 789    01 23
+
+  description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
+
+  // "f"
+  f = synthesizeQueryTextRect(kLFLen * 2 + 5, 1);
+  if (!checkQueryContentResult(f, description + "rect for 'f'")) {
+    return;
+  }
+
+  is(f.top, e.top, description + "'e' and 'f' should be at same top");
+  is(f.height, e.height, description + "'e' and 'f' should be same height");
+  isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
+
+  // 3rd <p> (can be computed with rect of 'f')
+  var p3 = synthesizeQueryTextRect(kLFLen * 2 + 6, 1);
+  if (!checkQueryContentResult(p3, description + "rect for 3rd <p>")) {
+    return;
+  }
+
+  is(p3.top, f.top, description + "'f' and a line breaker caused by 3rd <p> should be at same top");
+  is(p3.height, f.height, description + "'f' and a line breaker caused by 3rd <p> should be same height");
+  isSimilarTo(p3.left, f.left + f.width, 2, description + "left of a line breaker caused by 3rd <p> should be similar to right of 'f'");
+
+  if (kLFLen > 1) {
+    // \n of \r\n
+    var p3_2 = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
+    if (!checkQueryContentResult(p3_2, description + "rect for \n of \r\n caused by 3rd <p>")) {
+      return;
+    }
+
+    is(p3_2.top, p3.top, description + "'\\r' and '\\n' should be at same top");
+    is(p3_2.left, p3.left, description + "'\\r' and '\\n' should be at same top");
+    is(p3_2.height, p3.height, description + "'\\r' and '\\n' should be same height");
+    is(p3_2.width, p3.width, description + "'\\r' and '\\n' should be same width");
+  }
+
+  // <br> in 3rd <p>
+  var br = synthesizeQueryTextRect(kLFLen * 3 + 6, 1);
+  if (!checkQueryContentResult(br, description + "rect for <br> in 3rd <p>")) {
+    return;
+  }
+
+  isGreaterThan(br.top, d.top + d.height, description + "a line breaker caused by <br> in 3rd <p> should be greater than bottom of 'd'");
+  isSimilarTo(br.height, d.height, 2, description + "'d' and a line breaker caused by <br> in 3rd <p> should be similar height");
+  is(br.left, d.left, description + "left of a line breaker caused by <br> in 3rd <p> should be same left of 'd'");
+
+  if (kLFLen > 1) {
+    // \n of \r\n
+    var br_2 = synthesizeQueryTextRect(kLFLen * 3 + 7, 1);
+    if (!checkQueryContentResult(br_2, description + "rect for \n of \r\n caused by <br> in 3rd <p>")) {
+      return;
+    }
+
+    is(br_2.top, br.top, description + "'\\r' and '\\n' should be at same top");
+    is(br_2.left, br.left, description + "'\\r' and '\\n' should be at same top");
+    is(br_2.height, br.height, description + "'\\r' and '\\n' should be same height");
+    is(br_2.width, br.width, description + "'\\r' and '\\n' should be same width");
+  }
+
+  // next of <br> in 3rd <p>
+  var next_br = synthesizeQueryTextRect(kLFLen * 4 + 6, 1);
+  if (!checkQueryContentResult(next_br, description + "rect for next of <br> in 3rd <p>")) {
+    return;
+  }
+
+  is(next_br.top, br.top, description + "next of <br> and <br> should be at same top");
+  is(next_br.left, br.left, description + "next of <br> and <br> should be at same left");
+  is(next_br.height, br.height, description + "next of <br> and <br> should be same height");
+  is(next_br.width, br.width, description + "next of <br> and <br> should be same width");
+
+  // too big offset for the editor
+  tooBigOffset = synthesizeQueryTextRect(kLFLen * 4 + 7, 1);
+  if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
+    return;
+  }
+
+  is(tooBigOffset.top, next_br.top, description + "too big offset and next of 3rd <p> should be at same top");
+  is(tooBigOffset.left, next_br.left, description + "too big offset and next of 3rd <p> should be at same left");
+  is(tooBigOffset.height, next_br.height, description + "too big offset and next of 3rd <p> should be same height");
+  is(tooBigOffset.width, next_br.width, description + "too big offset and next of 3rd <p> should be same width");
+
+  contenteditable.innerHTML = "<p>abc</p><p>def</p><p></p>";
+                      // \n    0  123    4  567    8
+                      // \r\n  01 234    56 789    0
+
+  description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
+
+  // "f"
+  f = synthesizeQueryTextRect(kLFLen * 2 + 5, 1);
+  if (!checkQueryContentResult(f, description + "rect for 'f'")) {
+    return;
+  }
+
+  is(f.top, e.top, description + "'e' and 'f' should be at same top");
+  isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
+  is(f.height, e.height, description + "'e' and 'f' should be same height");
+
+  // 3rd <p> (can be computed with rect of 'f')
+  var p3 = synthesizeQueryTextRect(kLFLen * 2 + 6, 1);
+  if (!checkQueryContentResult(p3, description + "rect for 3rd <p>")) {
+    return;
+  }
+
+  is(p3.top, f.top, description + "'f' and a line breaker caused by 3rd <p> should be at same top");
+  is(p3.height, f.height, description + "'f' and a line breaker caused by 3rd <p> should be same height");
+  isSimilarTo(p3.left, f.left + f.width, 2, description + "left of a line breaker caused by 3rd <p> should be similar to right of 'f'");
+
+  if (kLFLen > 1) {
+    // \n of \r\n
+    p3_2 = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
+    if (!checkQueryContentResult(p3_2, description + "rect for \n of \r\n caused by 3rd <p>")) {
+      return;
+    }
+
+    is(p3_2.top, p3.top, description + "'\\r' and '\\n' should be at same top");
+    is(p3_2.left, p3.left, description + "'\\r' and '\\n' should be at same top");
+    is(p3_2.height, p3.height, description + "'\\r' and '\\n' should be same height");
+    is(p3_2.width, p3.width, description + "'\\r' and '\\n' should be same width");
+  }
+
+  // next of 3rd <p>
+  var next_p3 = synthesizeQueryTextRect(kLFLen * 3 + 6, 1);
+  if (!checkQueryContentResult(next_p3, description + "rect for next of 3rd <p>")) {
+    return;
+  }
+
+  isGreaterThan(next_p3.top, d.top + d.height, description + "top of next of 3rd <p> should equal to or be bigger than bottom of 'd'");
+  isSimilarTo(next_p3.left, d.left, 2, description + "left of next of 3rd <p> should be at similar to left of 'd'");
+  isSimilarTo(next_p3.height, d.height, 2, description + "next of 3rd <p> and 'd' should be similar height");
+
+  // too big offset for the editor
+  tooBigOffset = synthesizeQueryTextRect(kLFLen * 3 + 7, 1);
+  if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
+    return;
+  }
+
+  is(tooBigOffset.top, next_p3.top, description + "too big offset and next of 3rd <p> should be at same top");
+  is(tooBigOffset.left, next_p3.left, description + "too big offset and next of 3rd <p> should be at same left");
+  is(tooBigOffset.height, next_p3.height, description + "too big offset and next of 3rd <p> should be same height");
+  is(tooBigOffset.width, next_p3.width, description + "too big offset and next of 3rd <p> should be same width");
+
+  contenteditable.innerHTML = "abc<br>def";
+                      // \n    0123   456
+                      // \r\n  01234  567
+
+  description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
+
+  // "a"
+  a = synthesizeQueryTextRect(0, 1);
+  if (!checkQueryContentResult(a, description + "rect for 'a'")) {
+    return;
+  }
+
+  // "b"
+  b = synthesizeQueryTextRect(1, 1);
+  if (!checkQueryContentResult(b, description + "rect for 'b'")) {
+    return;
+  }
+
+  is(b.top, a.top, description + "'a' and 'b' should be at same top");
+  isSimilarTo(b.left, a.left + a.width, 2, description + "left of 'b' should be at similar to right of 'a'");
+  is(b.height, a.height, description + "'a' and 'b' should be same height");
+
+  // "c"
+  c = synthesizeQueryTextRect(2, 1);
+  if (!checkQueryContentResult(c, description + "rect for 'c'")) {
+    return;
+  }
+
+  is(c.top, b.top, description + "'b' and 'c' should be at same top");
+  isSimilarTo(c.left, b.left + b.width, 2, description + "left of 'c' should be at similar to right of 'b'");
+  is(c.height, b.height, description + "'b' and 'c' should be same height");
+
+  // <br> (can be computed with the rect of 'c')
+  br = synthesizeQueryTextRect(3, 1);
+  if (!checkQueryContentResult(br, description + "rect for <br>")) {
+    return;
+  }
+
+  is(br.top, c.top, description + "'c' and a line breaker caused by <br> should be at same top");
+  isSimilarTo(br.left, c.left + c.width, 2, description + "left of a line breaker caused by <br> should be at similar to right of 'c'");
+  is(br.height, c.height, description + "'c' and a line breaker caused by <br> should be same height");
+
+  if (kLFLen > 1) {
+    // \n of \r\n
+    var br_2 = synthesizeQueryTextRect(4, 1);
+    if (!checkQueryContentResult(br_2, description + "rect for \n of \r\n caused by <br>")) {
+      return;
+    }
+
+    is(br_2.top, br.top, description + "'\\r' and '\\n' should be at same top");
+    is(br_2.left, br.left, description + "'\\r' and '\\n' should be at same top");
+    is(br_2.height, br.height, description + "'\\r' and '\\n' should be same height");
+    is(br_2.width, br.width, description + "'\\r' and '\\n' should be same width");
+  }
+
+  // "d"
+  d = synthesizeQueryTextRect(kLFLen + 3, 1);
+  if (!checkQueryContentResult(d, description + "rect for 'd'")) {
+    return;
+  }
+
+  isSimilarTo(d.top, a.top + a.height, 2, description + "top of 'd' should be at similar to bottom of 'a'");
+  is(d.left, a.left, description + "'a' and 'd' should be same at same left");
+  is(d.height, a.height, description + "'a' and 'd' should be same height");
+
+  // "e"
+  e = synthesizeQueryTextRect(kLFLen + 4, 1);
+  if (!checkQueryContentResult(e, description + "rect for 'e'")) {
+    return;
+  }
+
+  is(e.top, d.top, description + "'d' and 'd' should be at same top");
+  isSimilarTo(e.left, d.left + d.width, 2, description + "left of 'e' should be at similar to right of 'd'");
+  is(e.height, d.height, description + "'d' and 'e' should be same height");
+
+  // "f"
+  f = synthesizeQueryTextRect(kLFLen + 5, 1);
+  if (!checkQueryContentResult(f, description + "rect for 'f'")) {
+    return;
+  }
+
+  is(f.top, e.top, description + "'e' and 'f' should be at same top");
+  isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
+  is(f.height, e.height, description + "'e' and 'f' should be same height");
+
+  // next of "f" (can be computed with rect of 'f')
+  next_f = synthesizeQueryTextRect(kLFLen + 6, 1);
+  if (!checkQueryContentResult(next_f, description + "rect for next of 'f'")) {
+    return;
+  }
+
+  is(next_f.top, d.top, 2, description + "'f' and next of 'f' should be at same top");
+  isSimilarTo(next_f.left, f.left + f.width, 2, description + "left of next of 'f' should be at similar to right of 'f'");
+  is(next_f.height, d.height, description + "'f' and next of 'f' should be same height");
+
+  // too big offset for the editor
+  tooBigOffset = synthesizeQueryTextRect(kLFLen + 7, 1);
+  if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
+    return;
+  }
+
+  is(tooBigOffset.top, next_f.top, description + "too big offset and next of 'f' should be at same top");
+  is(tooBigOffset.left, next_f.left, description + "too big offset and next of 'f' should be at same left");
+  is(tooBigOffset.height, next_f.height, description + "too big offset and next of 'f' should be same height");
+  is(tooBigOffset.width, next_f.width, description + "too big offset and next of 'f' should be same width");
+
+  // Note that this case does not have an empty line at the end.
+  contenteditable.innerHTML = "abc<br>def<br>";
+                      // \n    0123   4567
+                      // \r\n  01234  56789
+
+  description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
+
+  // "f"
+  f = synthesizeQueryTextRect(kLFLen + 5, 1);
+  if (!checkQueryContentResult(f, description + "rect for 'f'")) {
+    return;
+  }
+
+  is(f.top, e.top, description + "'e' and 'f' should be at same top");
+  is(f.height, e.height, description + "'e' and 'f' should be same height");
+  isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
+
+  // 2nd <br> (can be computed with rect of 'f')
+  var br2 = synthesizeQueryTextRect(kLFLen + 6, 1);
+  if (!checkQueryContentResult(br2, description + "rect for 2nd <br>")) {
+    return;
+  }
+
+  is(br2.top, f.top, description + "'f' and a line breaker caused by 2nd <br> should be at same top");
+  is(br2.height, f.height, description + "'f' and a line breaker caused by 2nd <br> should be same height");
+  isSimilarTo(br2.left, f.left + f.width, 2, description + "left of a line breaker caused by 2nd <br> should be similar to right of 'f'");
+
+  if (kLFLen > 1) {
+    // \n of \r\n
+    var br2_2 = synthesizeQueryTextRect(kLFLen + 7, 1);
+    if (!checkQueryContentResult(br2_2, description + "rect for \n of \r\n caused by 2nd <br>")) {
+      return;
+    }
+
+    is(br2_2.top, br2.top, description + "'\\r' and '\\n' should be at same top");
+    is(br2_2.left, br2.left, description + "'\\r' and '\\n' should be at same top");
+    is(br2_2.height, br2.height, description + "'\\r' and '\\n' should be same height");
+    is(br2_2.width, br2.width, description + "'\\r' and '\\n' should be same width");
+  }
+
+  // next of 2nd <br>
+  var next_br2 = synthesizeQueryTextRect(kLFLen * 2 + 6, 1);
+  if (!checkQueryContentResult(next_br2, description + "rect for next of 2nd <br>")) {
+    return;
+  }
+
+  is(next_br2.top, br2.top, description + "2nd <br> and next of 2nd <br> should be at same top");
+  is(next_br2.left, br2.left, description + "2nd <br> and next of 2nd <br> should be at same top");
+  is(next_br2.height, br2.height, description + "2nd <br> and next of 2nd <br> should be same height");
+  is(next_br2.width, br2.width, description + "2nd <br> and next of 2nd <br> should be same width");
+
+  // too big offset for the editor
+  tooBigOffset = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
+  if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
+    return;
+  }
+
+  is(tooBigOffset.top, next_br2.top, description + "too big offset and next of 2nd <br> should be at same top");
+  is(tooBigOffset.left, next_br2.left, description + "too big offset and next of 2nd <br> should be at same left");
+  is(tooBigOffset.height, next_br2.height, description + "too big offset and next of 2nd <br> should be same height");
+  is(tooBigOffset.width, next_br2.width, description + "too big offset and next of 2nd <br> should be same width");
+
+  contenteditable.innerHTML = "abc<br>def<br><br>";
+                      // \n    0123   4567   8
+                      // \r\n  01234  56789  01
+
+  description = "runTextRectInContentEditableTest: \"" + contenteditable.innerHTML + "\", ";
+
+  // "f"
+  f = synthesizeQueryTextRect(kLFLen + 5, 1);
+  if (!checkQueryContentResult(f, description + "rect for 'f'")) {
+    return;
+  }
+
+  is(f.top, e.top, description + "'e' and 'f' should be at same top");
+  isSimilarTo(f.left, e.left + e.width, 2, description + "left of 'f' should be at similar to right of 'e'");
+  is(f.height, e.height, description + "'e' and 'f' should be same height");
+
+  // 2nd <br>
+  br2 = synthesizeQueryTextRect(kLFLen + 6, 1);
+  if (!checkQueryContentResult(br2, description + "rect for 2nd <br>")) {
+    return;
+  }
+
+  is(br2.top, f.top, description + "'f' and a line breaker caused by 2nd <br> should be at same top");
+  is(br2.height, f.height, description + "'f' and a line breaker caused by 2nd <br> should be same height");
+  ok(f.left < br2.left, description + "left of a line breaker caused by 2nd <br> should be bigger than left of 'f', f.left=" + f.left + ", br2.left=" + br2.left);
+
+  if (kLFLen > 1) {
+    // \n of \r\n
+    br2_2 = synthesizeQueryTextRect(kLFLen + 7, 1);
+    if (!checkQueryContentResult(br2_2, description + "rect for \n of \r\n caused by 2nd <br>")) {
+      return;
+    }
+
+    is(br2_2.top, br2.top, description + "'\\r' and '\\n' should be at same top");
+    is(br2_2.left, br2.left, description + "'\\r' and '\\n' should be at same top");
+    is(br2_2.height, br2.height, description + "'\\r' and '\\n' should be same height");
+    is(br2_2.width, br2.width, description + "'\\r' and '\\n' should be same width");
+  }
+
+  // 3rd <br>
+  var br3 = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
+  if (!checkQueryContentResult(br3, description + "rect for next of 3rd <br>")) {
+    return;
+  }
+
+  isSimilarTo(br3.top, d.top + d.height, 3, description + "top of next of 3rd <br> should at similar to bottom of 'd'");
+  isSimilarTo(br3.left, d.left, 2, description + "left of next of 3rd <br> should be at similar to left of 'd'");
+  isSimilarTo(br3.height, d.height, 2, description + "next of 3rd <br> and 'd' should be similar height");
+
+  if (kLFLen > 1) {
+    // \n of \r\n
+    var br3_2 = synthesizeQueryTextRect(kLFLen * 2 + 7, 1);
+    if (!checkQueryContentResult(br3_2, description + "rect for \n of \r\n caused by 3rd <br>")) {
+      return;
+    }
+
+    is(br3_2.top, br3.top, description + "'\\r' and '\\n' should be at same top");
+    is(br3_2.left, br3.left, description + "'\\r' and '\\n' should be at same left");
+    is(br3_2.height, br3.height, description + "'\\r' and '\\n' should be same height");
+    is(br3_2.width, br3.width, description + "'\\r' and '\\n' should be same width");
+  }
+
+  // next of 3rd <br>
+  var next_br3 = synthesizeQueryTextRect(kLFLen * 3 + 6, 1);
+  if (!checkQueryContentResult(next_br3, description + "rect for next of 3rd <br>")) {
+    return;
+  }
+
+  is(next_br3.top, br3.top, description + "3rd <br> and next of 3rd <br> should be at same top");
+  is(next_br3.left, br3.left, description + "3rd <br> and next of 3rd <br> should be at same left");
+  is(next_br3.height, br3.height, description + "3rd <br> and next of 3rd <br> should be same height");
+  is(next_br3.width, br3.width, description + "3rd <br> and next of 3rd <br> should be same width");
+
+  // too big offset for the editor
+  tooBigOffset = synthesizeQueryTextRect(kLFLen * 3 + 7, 1);
+  if (!checkQueryContentResult(tooBigOffset, description + "rect for too big offset")) {
+    return;
+  }
+
+  is(tooBigOffset.top, next_br3.top, description + "too big offset and next of 3rd <br> should be at same top");
+  is(tooBigOffset.left, next_br3.left, description + "too big offset and next of 3rd <br> should be at same left");
+  is(tooBigOffset.height, next_br3.height, description + "too big offset and next of 3rd <br> should be same height");
+  is(tooBigOffset.width, next_br3.width, description + "too big offset and next of 3rd <br> should be same width");
+}
+
 function runCharAtPointTest(aFocusedEditor, aTargetName)
 {
   aFocusedEditor.value = "This is a test of the\nContent Events";
                        // 012345678901234567890  12345678901234
                        // 0         1         2           3    
 
   aFocusedEditor.focus();
 
@@ -3779,43 +4300,83 @@ function runBug722639Test()
   textarea.value += textarea.value;
   textarea.value += textarea.value; // 80 characters
 
   var firstLine = synthesizeQueryTextRect(0, 1);
   if (!checkQueryContentResult(firstLine,
         "runBug722639Test: firstLine")) {
     return;
   }
+  ok(true, "runBug722639Test: 1st line, top=" + firstLine.top + ", left=" + firstLine.left);
+  if (kLFLen > 1) {
+    var firstLineLF = synthesizeQueryTextRect(1, 1);
+    if (!checkQueryContentResult(firstLineLF,
+          "runBug722639Test: firstLineLF")) {
+      return;
+    }
+    is(firstLineLF.top, firstLine.top, "runBug722639Test: 1st line's \\n rect should be same as 1st line's \\r rect");
+    is(firstLineLF.left, firstLine.left, "runBug722639Test: 1st line's \\n rect should be same as 1st line's \\r rect");
+    is(firstLineLF.height, firstLine.height, "runBug722639Test: 1st line's \\n rect should be same as 1st line's \\r rect");
+    is(firstLineLF.width, firstLine.width, "runBug722639Test: 1st line's \\n rect should be same as 1st line's \\r rect");
+  }
   var secondLine = synthesizeQueryTextRect(kLFLen, 1);
   if (!checkQueryContentResult(secondLine,
         "runBug722639Test: secondLine")) {
     return;
   }
+  ok(true, "runBug722639Test: 2nd line, top=" + secondLine.top + ", left=" + secondLine.left);
+  if (kLFLen > 1) {
+    var secondLineLF = synthesizeQueryTextRect(kLFLen + 1, 1);
+    if (!checkQueryContentResult(secondLineLF,
+          "runBug722639Test: secondLineLF")) {
+      return;
+    }
+    is(secondLineLF.top, secondLine.top, "runBug722639Test: 2nd line's \\n rect should be same as 2nd line's \\r rect");
+    is(secondLineLF.left, secondLine.left, "runBug722639Test: 2nd line's \\n rect should be same as 2nd line's \\r rect");
+    is(secondLineLF.height, secondLine.height, "runBug722639Test: 2nd line's \\n rect should be same as 2nd line's \\r rect");
+    is(secondLineLF.width, secondLine.width, "runBug722639Test: 2nd line's \\n rect should be same as 2nd line's \\r rect");
+  }
   var lineHeight = secondLine.top -  firstLine.top;
   ok(lineHeight > 0,
      "runBug722639Test: lineHeight must be positive");
   is(secondLine.left, firstLine.left,
      "runBug722639Test: the left value must be always same value");
+  is(secondLine.height, firstLine.height,
+     "runBug722639Test: the height must be always same value");
   var previousTop = secondLine.top;
-  for (var i = 2; i < textarea.value.length; i++) {
-    var currentLine = synthesizeQueryTextRect(kLFLen * i, 1);
+  for (var i = 3; i <= textarea.value.length + 1; i++) {
+    var currentLine = synthesizeQueryTextRect(kLFLen * (i - 1), 1);
     if (!checkQueryContentResult(currentLine,
            "runBug722639Test: " + i + "th currentLine")) {
       return;
     }
+    ok(true, "runBug722639Test: " + i + "th line, top=" + currentLine.top + ", left=" + currentLine.left);
     // NOTE: the top position may be 1px larger or smaller than other lines
     //       due to sub pixel positioning.
     if (Math.abs(currentLine.top - (previousTop + lineHeight)) <= 1) {
       ok(true, "runBug722639Test: " + i + "th line's top is expected");
     } else {
       is(currentLine.top, previousTop + lineHeight,
          "runBug722639Test: " + i + "th line's top is unexpected");
     }
     is(currentLine.left, firstLine.left,
        "runBug722639Test: " + i + "th line's left is unexpected");
+    is(currentLine.height, firstLine.height,
+       "runBug722639Test: " + i + "th line's height is unexpected");
+    if (kLFLen > 1) {
+      var currentLineLF = synthesizeQueryTextRect(kLFLen * (i - 1) + 1, 1);
+      if (!checkQueryContentResult(currentLineLF,
+            "runBug722639Test: " + i + "th currentLineLF")) {
+        return;
+      }
+      is(currentLineLF.top, currentLine.top, "runBug722639Test: " + i + "th line's \\n rect should be same as same line's \\r rect");
+      is(currentLineLF.left, currentLine.left, "runBug722639Test: " + i + "th line's \\n rect should be same as same line's \\r rect");
+      is(currentLineLF.height, currentLine.height, "runBug722639Test: " + i + "th line's \\n rect should be same as same line's \\r rect");
+      is(currentLineLF.width, currentLine.width, "runBug722639Test: " + i + "th line's \\n rect should be same as same line's \\r rect");
+    }
     previousTop = currentLine.top;
   }
 }
 
 function runForceCommitTest()
 {
   var events;
   function eventHandler(aEvent)
@@ -6026,16 +6587,17 @@ function runTest()
   windowOfContenteditable = document.getElementById("iframe4").contentWindow;
   textareaInFrame = iframe.contentDocument.getElementById("textarea");
 
   runUndoRedoTest();
   runCompositionCommitAsIsTest();
   runCompositionCommitTest();
   runCompositionTest();
   runCompositionEventTest();
+  runQueryTextRectInContentEditableTest();
   runCharAtPointTest(textarea, "textarea in the document");
   runCharAtPointAtOutsideTest();
   runSetSelectionEventTest();
   runQueryTextContentEventTest();
   runQueryIMESelectionTest();
   runQueryContentEventRelativeToInsertionPoint();
   runCSSTransformTest();
   runBug722639Test();