Bug 1381710 - Selection.addRange will be failure with dynamic table insertion. r?tnikkel
When range is selected table element, Selection.addRange uses nsFrameSelection. If frame isn't constructed yet, addRange throws NS_ERROR_FAILURE even if table element isn't editable element.
When getting nsITableCellLayout, we should flush frame to construct cell frame.
MozReview-Commit-ID: 9qWwW46RYNL
--- a/dom/base/Selection.cpp
+++ b/dom/base/Selection.cpp
@@ -600,24 +600,34 @@ Selection::GetTableCellLocationFromRange
// Get the child content (the cell) pointed to by starting node of range
// We do minimal checking since GetTableSelectionType assures
// us that this really is a table cell
nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartContainer());
if (!content)
return NS_ERROR_FAILURE;
- nsIContent *child = content->GetChildAt(aRange->StartOffset());
+ nsCOMPtr<nsIContent> child = content->GetChildAt(aRange->StartOffset());
if (!child)
return NS_ERROR_FAILURE;
+ // GetCellLayout depends on current frame, we need flush frame to get
+ // nsITableCellLayout
+ nsCOMPtr<nsIPresShell> presShell = mFrameSelection->GetShell();
+ if (presShell) {
+ presShell->FlushPendingNotifications(FlushType::Frames);
+
+ // Since calling FlushPendingNotifications, so check whether disconnected.
+ if (!mFrameSelection || !mFrameSelection->GetShell()) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
//Note: This is a non-ref-counted pointer to the frame
nsITableCellLayout *cellLayout = mFrameSelection->GetCellLayout(child);
- if (NS_FAILED(result))
- return result;
if (!cellLayout)
return NS_ERROR_FAILURE;
return cellLayout->GetCellIndexes(*aRow, *aCol);
}
nsresult
Selection::AddTableCellRange(nsRange* aRange, bool* aDidAddRange,
@@ -2271,16 +2281,19 @@ Selection::AddRangeInternal(nsRange& aRa
if (aDocument != rangeRoot && (!rangeRoot ||
aDocument != rangeRoot->GetComposedDoc())) {
// http://w3c.github.io/selection-api/#dom-selection-addrange
// "... if the root of the range's boundary points are the document
// associated with context object. Otherwise, this method must do nothing."
return;
}
+ // AddTableCellRange might flush frame.
+ RefPtr<Selection> kungFuDeathGrip(this);
+
// This inserts a table cell range in proper document order
// and returns NS_OK if range doesn't contain just one table cell
bool didAddRange;
int32_t rangeIndex;
nsresult result = AddTableCellRange(&aRange, &didAddRange, &rangeIndex);
if (NS_FAILED(result)) {
aRv.Throw(result);
return;
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -616,16 +616,17 @@ skip-if = toolkit == 'android'
[test_bug1274806.html]
[test_bug1281963.html]
[test_bug1295852.html]
[test_bug1307730.html]
[test_bug1308069.html]
[test_bug1314032.html]
[test_bug1318303.html]
[test_bug1375050.html]
+[test_bug1381710.html]
[test_bug1384658.html]
skip-if = toolkit == 'android'
[test_caretPositionFromPoint.html]
[test_change_policy.html]
[test_clearTimeoutIntervalNoArg.html]
[test_constructor-assignment.html]
[test_constructor.html]
[test_copyimage.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_bug1381710.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1381710
+-->
+<head>
+ <title>Test for Mozilla Bug 1381710</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=1381710">Mozilla Bug 1381710</a>
+<div id="content">
+ <div id="nonedit"></div>
+ <div id="edit" contenteditable=true></div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+function test(element)
+{
+ let selection = window.getSelection();
+ selection.removeAllRanges();
+ let range = document.createRange();
+
+ element.innerHTML = "<table><tr id=tr><td id=td>A</td><td>B</td><tr></table>";
+ let td = document.getElementById("td");
+ range.selectNode(td);
+ // Don't throw exception
+ selection.addRange(range);
+ is(selection.anchorNode, document.getElementById("tr"),
+ "anchor node should be <TR> element");
+ element.innerHTML = "";
+}
+
+test(document.getElementById("nonedit"));
+test(document.getElementById("edit"));
+</script>
+</pre>
+</body>
+</html>