Bug 409380, support 'unconnected' ranges , r+sr+a=sicking

This commit is contained in:
Olli.Pettay@helsinki.fi 2008-02-24 04:46:09 -08:00
Родитель 919d5bd3d3
Коммит 55b7651538
6 изменённых файлов: 424 добавлений и 23 удалений

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

@ -267,11 +267,13 @@ public:
* node/offset pair
* Returns -1 if point1 < point2, 1, if point1 > point2,
* 0 if error or if point1 == point2.
* NOTE! The two nodes MUST be in the same connected subtree!
* if they are not the result is undefined.
* NOTE! If the two nodes aren't in the same connected subtree,
* the result is undefined, but the optional aDisconnected parameter
* is set to PR_TRUE.
*/
static PRInt32 ComparePoints(nsINode* aParent1, PRInt32 aOffset1,
nsINode* aParent2, PRInt32 aOffset2);
nsINode* aParent2, PRInt32 aOffset2,
PRBool* aDisconnected = nsnull);
/**
* Find the first child of aParent with a resolved tag matching

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

@ -1496,7 +1496,8 @@ nsContentUtils::ComparePosition(nsINode* aNode1,
/* static */
PRInt32
nsContentUtils::ComparePoints(nsINode* aParent1, PRInt32 aOffset1,
nsINode* aParent2, PRInt32 aOffset2)
nsINode* aParent2, PRInt32 aOffset2,
PRBool* aDisconnected)
{
if (aParent1 == aParent2) {
return aOffset1 < aOffset2 ? -1 :
@ -1519,8 +1520,12 @@ nsContentUtils::ComparePoints(nsINode* aParent1, PRInt32 aOffset1,
PRUint32 pos1 = parents1.Length() - 1;
PRUint32 pos2 = parents2.Length() - 1;
NS_ASSERTION(parents1.ElementAt(pos1) == parents2.ElementAt(pos2),
NS_ASSERTION(parents1.ElementAt(pos1) == parents2.ElementAt(pos2) ||
aDisconnected,
"disconnected nodes");
if (aDisconnected) {
*aDisconnected = (parents1.ElementAt(pos1) != parents2.ElementAt(pos2));
}
// Find where the parent chains differ
nsINode* parent = parents1.ElementAt(pos1);

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

@ -103,6 +103,7 @@ nsresult
nsRange::CompareNodeToRange(nsIContent* aNode, nsIRange* aRange,
PRBool *outNodeBefore, PRBool *outNodeAfter)
{
NS_ENSURE_STATE(aNode);
// create a pair of dom points that expresses location of node:
// NODE(start), NODE(end)
// Let incoming range be:
@ -142,14 +143,19 @@ nsRange::CompareNodeToRange(nsIContent* aNode, nsIRange* aRange,
PRInt32 rangeEndOffset = range->EndOffset();
// is RANGE(start) <= NODE(start) ?
PRBool disconnected = PR_FALSE;
*outNodeBefore = nsContentUtils::ComparePoints(rangeStartParent,
rangeStartOffset,
parent, nodeStart) > 0;
parent, nodeStart,
&disconnected) > 0;
NS_ENSURE_TRUE(!disconnected, NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
// is RANGE(end) >= NODE(end) ?
*outNodeAfter = nsContentUtils::ComparePoints(rangeEndParent,
rangeEndOffset,
parent, nodeEnd) < 0;
parent, nodeEnd,
&disconnected) < 0;
NS_ENSURE_TRUE(!disconnected, NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
return NS_OK;
}
@ -335,6 +341,18 @@ nsRange::NodeWillBeDestroyed(const nsINode* aNode)
DoSetRange(nsnull, 0, nsnull, 0, nsnull);
}
void
nsRange::ParentChainChanged(nsIContent *aContent)
{
NS_ASSERTION(mRoot == aContent, "Wrong ParentChainChanged notification?");
nsINode* newRoot = mRoot;
nsINode* tmp;
while ((tmp = newRoot->GetNodeParent())) {
newRoot = tmp;
}
DoSetRange(mStartParent, mStartOffset, mEndParent, mEndOffset, newRoot);
}
/********************************************************
* Utilities for comparing points: API from nsIDOMNSRange
********************************************************/
@ -433,7 +451,9 @@ nsRange::DoSetRange(nsINode* aStartN, PRInt32 aStartOffset,
(!aRoot->GetNodeParent() &&
(aRoot->IsNodeOfType(nsINode::eDOCUMENT) ||
aRoot->IsNodeOfType(nsINode::eATTRIBUTE) ||
aRoot->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT))),
aRoot->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT) ||
/*For backward compatibility*/
aRoot->IsNodeOfType(nsINode::eCONTENT))),
"Bad root");
if (mRoot != aRoot) {
@ -592,22 +612,14 @@ nsINode* nsRange::IsValidBoundary(nsINode* aNode)
NS_ASSERTION(!root->IsNodeOfType(nsINode::eDOCUMENT),
"GetCurrentDoc should have returned a doc");
if (root->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT) ||
root->IsNodeOfType(nsINode::eATTRIBUTE)) {
return root;
}
#ifdef DEBUG_smaug
nsCOMPtr<nsIContent> cont = do_QueryInterface(root);
if (cont) {
nsAutoString name;
cont->Tag()->ToString(name);
printf("nsRange::IsValidBoundary: node is not a valid boundary point [%s]\n",
NS_ConvertUTF16toUTF8(name).get());
}
NS_WARN_IF_FALSE(root->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT) ||
root->IsNodeOfType(nsINode::eATTRIBUTE),
"Creating a DOM Range using root which isn't in DOM!");
#endif
return nsnull;
// We allow this because of backward compatibility.
return root;
}
nsresult nsRange::SetStart(nsIDOMNode* aParent, PRInt32 aOffset)
@ -1526,7 +1538,7 @@ nsresult nsRange::CloneContents(nsIDOMDocumentFragment** aReturn)
tmpNode = clone;
res = tmpNode->GetParentNode(getter_AddRefs(clone));
if (NS_FAILED(res)) return res;
if (!node) return NS_ERROR_FAILURE;
if (!clone) return NS_ERROR_FAILURE;
}
commonCloneAncestor = clone;
@ -1576,6 +1588,7 @@ nsresult nsRange::InsertNode(nsIDOMNode* aN)
nsCOMPtr<nsIDOMNode> tSCParentNode;
res = tStartContainer->GetParentNode(getter_AddRefs(tSCParentNode));
if(NS_FAILED(res)) return res;
NS_ENSURE_STATE(tSCParentNode);
PRBool isCollapsed;
res = GetCollapsed(&isCollapsed);

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

@ -110,6 +110,7 @@ public:
nsIContent* aChild,
PRInt32 aIndexInContainer);
virtual void NodeWillBeDestroyed(const nsINode* aNode);
virtual void ParentChainChanged(nsIContent *aContent);
private:
// no copy's or assigns

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

@ -164,6 +164,7 @@ _TEST_FILES = test_bug5141.html \
test_bug403868.xml \
test_bug405182.html \
test_bug403841.html \
test_bug409380.html \
test_bug410229.html \
test_bug415860.html \
test_bug414190.html \

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

@ -0,0 +1,379 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=409380
-->
<head>
<title>Test for Bug 409380</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/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=409380">Mozilla Bug 409380</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
/** Test for Bug 409380 **/
function runRangeTest()
{
// Bug 336381
// This is a case which can't be supported (at least not at the moment)
// because DOM Range requires that when the start boundary point is text node,
// it must be splitted. But in this case boundary point doesn't have parent,
// so splitting doesn't work.
var zz = document.getElementById("connectedDiv").firstChild;
zz.parentNode.removeChild(zz);
var range = document.createRange();
var hadException = false;
try {
range.setStart(zz, 0);
range.setEnd(zz, 0);
} catch (ex) {
hadException = true;
}
ok(!hadException ,
"It should be possible to select text node even if the node is not in DOM.");
hadException = false;
try {
r.insertNode(document.createTextNode('5'));
} catch (ex) {
hadException = true;
}
ok(hadException,
"It shouldn't be possible to insert text node to a detached range.");
// Bug 409380
var element = document.createElement('div');
var elementContent = "This is the element content";
element.innerHTML = elementContent;
range = element.ownerDocument.createRange();
hadException = false;
try {
range.selectNodeContents(element);
} catch (ex) {
hadException = true;
}
ok(!hadException,
"It should be possible to select node contents of a detached element.");
ok(range.toString() == elementContent, "Wrong range selection");
// range.selectNode can't succeed because selectNode sets boundary points
// to be parentNode, which in this testcase is null.
element = document.createElement('div');
range = element.ownerDocument.createRange();
hadException = false;
try {
range.selectNode(element);
} catch (ex) {
hadException = true;
}
ok(hadException, "It shouldn't be possible to select a detached element.");
// Testing contextual fragment.
range = element.ownerDocument.createRange();
var cf = null;
var testContent = "<span>foo</span><span>bar</span>";
try {
range.selectNodeContents(element);
cf = range.createContextualFragment(testContent);
element.appendChild(cf);
} catch (ex) {
}
ok(cf, "Creating contextual fragment didn't succeed!");
ok(element.innerHTML == testContent, "Wrong innerHTML!");
element = document.createElement('div');
element.textContent = "foobar";
range = element.ownerDocument.createRange();
try {
range.selectNodeContents(element);
element.firstChild.insertData(3, " ");
} catch (ex) {
}
ok(range.toString() == "foo bar");
// Testing contextual fragment, but inserting element to document
// after creating range.
element = document.createElement('div');
range = element.ownerDocument.createRange();
document.body.appendChild(element);
cf = null;
testContent = "<span>foo</span><span>bar</span>";
try {
range.selectNodeContents(element);
cf = range.createContextualFragment(testContent);
element.appendChild(cf);
} catch (ex) {
}
ok(cf, "Creating contextual fragment didn't succeed!");
ok(element.innerHTML == testContent, "Wrong innerHTML!");
// Testing contextual fragment, but inserting element to document
// before creating range.
element = document.createElement('div');
document.body.appendChild(element);
range = element.ownerDocument.createRange();
cf = null;
testContent = "<span>foo</span><span>bar</span>";
try {
range.selectNodeContents(element);
cf = range.createContextualFragment(testContent);
element.appendChild(cf);
} catch (ex) {
}
ok(cf, "Creating contextual fragment didn't succeed!");
ok(element.innerHTML == testContent, "Wrong innerHTML!");
element = document.createElement('div');
var range2 = element.ownerDocument.createRange();
hadException = false;
try {
range2.selectNodeContents(element);
} catch (ex) {
hadException = true;
}
ok(!hadException,
"It should be possible to select node contents of a detached element.");
// Now the boundary points of range are in DOM, but boundary points of
// range2 aren't.
hadException = false;
try {
range.compareBoundaryPoints(range.START_TO_START, range2);
} catch (ex) {
hadException = true;
}
ok(hadException, "Should have got an exception!");
hadException = false;
try {
range.compareBoundaryPoints(range.START_TO_END, range2);
} catch (ex) {
hadException = true;
}
ok(hadException, "Should have got an exception!");
hadException = false;
try {
range.compareBoundaryPoints(range.END_TO_START, range2);
} catch (ex) {
hadException = true;
}
ok(hadException, "Should have got an exception!");
hadException = false;
try {
range.compareBoundaryPoints(range.END_TO_END, range2);
} catch (ex) {
hadException = true;
}
ok(hadException, "Should have got an exception!");
hadException = false;
try {
range2.compareBoundaryPoints(range.START_TO_START, range);
} catch (ex) {
hadException = true;
}
ok(hadException, "Should have got an exception!");
hadException = false;
try {
range2.compareBoundaryPoints(range.START_TO_END, range);
} catch (ex) {
hadException = true;
}
ok(hadException, "Should have got an exception!");
hadException = false;
try {
range2.compareBoundaryPoints(range.END_TO_START, range);
} catch (ex) {
hadException = true;
}
ok(hadException, "Should have got an exception!");
hadException = false;
try {
range2.compareBoundaryPoints(range.END_TO_END, range);
} catch (ex) {
hadException = true;
}
ok(hadException, "Should have got an exception!");
// range3 will be in document
element = document.createElement('div');
document.body.appendChild(element);
range3 = element.ownerDocument.createRange();
hadException = false;
try {
range3.selectNodeContents(element);
} catch (ex) {
hadException = true;
}
ok(!hadException,
"It should be possible to select node contents of a detached element.");
hadException = false;
try {
range3.compareBoundaryPoints(range.START_TO_START, range);
} catch (ex) {
hadException = true;
}
ok(!hadException, "Shouldn't have got an exception!");
hadException = false;
try {
range3.compareBoundaryPoints(range.START_TO_END, range);
} catch (ex) {
hadException = true;
}
ok(!hadException, "Shouldn't have got an exception!");
hadException = false;
try {
range3.compareBoundaryPoints(range.END_TO_START, range);
} catch (ex) {
hadException = true;
}
ok(!hadException, "Shouldn't have got an exception!");
hadException = false;
try {
range3.compareBoundaryPoints(range.END_TO_END, range);
} catch (ex) {
hadException = true;
}
ok(!hadException, "Shouldn't have got an exception!");
// range4 won't be in document
element = document.createElement('div');
var range4 = element.ownerDocument.createRange();
hadException = false;
try {
range4.selectNodeContents(element);
} catch (ex) {
hadException = true;
}
ok(!hadException,
"It should be possible to select node contents of a detached element.");
hadException = false;
try {
range4.compareBoundaryPoints(range.START_TO_START, range);
} catch (ex) {
hadException = true;
}
ok(hadException, "Should have got an exception!");
hadException = false;
try {
range2.compareBoundaryPoints(range.START_TO_END, range);
} catch (ex) {
hadException = true;
}
ok(hadException, "Should have got an exception!");
hadException = false;
try {
range4.compareBoundaryPoints(range.END_TO_START, range);
} catch (ex) {
hadException = true;
}
ok(hadException, "Should have got an exception!");
hadException = false;
try {
range4.compareBoundaryPoints(range.END_TO_END, range);
} catch (ex) {
hadException = true;
}
ok(hadException, "Should have got an exception!");
// Compare range to itself.
hadException = false;
try {
range.compareBoundaryPoints(range.START_TO_START, range);
} catch (ex) {
hadException = true;
}
ok(!hadException, "Shouldn't have got an exception!");
hadException = false;
try {
range.compareBoundaryPoints(range.START_TO_END, range);
} catch (ex) {
hadException = true;
}
ok(!hadException, "Shouldn't have got an exception!");
hadException = false;
try {
range.compareBoundaryPoints(range.END_TO_START, range);
} catch (ex) {
hadException = true;
}
ok(!hadException, "Shouldn't have got an exception!");
hadException = false;
try {
range.compareBoundaryPoints(range.END_TO_END, range);
} catch (ex) {
hadException = true;
}
ok(!hadException, "Shouldn't have got an exception!");
// Attach startContainer of range2 to document.
ok(range2.startContainer == range2.endContainer, "Wrong container?");
document.body.appendChild(range2.startContainer);
hadException = false;
try {
range2.compareBoundaryPoints(range.START_TO_START, range);
} catch (ex) {
hadException = true;
}
ok(!hadException, "Shouldn't have got an exception!");
hadException = false;
try {
range2.compareBoundaryPoints(range.START_TO_END, range);
} catch (ex) {
hadException = true;
}
ok(!hadException, "Shouldn't have got an exception!");
hadException = false;
try {
range2.compareBoundaryPoints(range.END_TO_START, range);
} catch (ex) {
hadException = true;
}
ok(!hadException, "Shouldn't have got an exception!");
hadException = false;
try {
range2.compareBoundaryPoints(range.END_TO_END, range);
} catch (ex) {
hadException = true;
}
ok(!hadException, "Shouldn't have got an exception!");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(runRangeTest);
</script>
</pre>
<div id="connectedDiv">zz</div>
</body>
</html>