Bug 659053 Part 2: Implement a faster non-recursive Node.isEqualNode directly on nsINode. r=peterv. Fixes were r=bent

This commit is contained in:
Jonas Sicking
2011-06-14 00:56:48 -07:00
parent 8cfd06e6af
commit 18a93b32e7
8 changed files with 209 additions and 157 deletions

View File

@@ -928,9 +928,6 @@ public:
nsresult LookupNamespaceURI(const nsAString& aNamespacePrefix, nsresult LookupNamespaceURI(const nsAString& aNamespacePrefix,
nsAString& aNamespaceURI) const; nsAString& aNamespaceURI) const;
PRBool IsEqual(nsIContent *aOther);
NS_IMETHOD IsEqualNode(nsIDOMNode* aOther, PRBool* aReturn);
/** /**
* If this content has independent selection, e.g., if this is input field * If this content has independent selection, e.g., if this is input field
* or textarea, this return TRUE. Otherwise, false. * or textarea, this return TRUE. Otherwise, false.

View File

@@ -1062,7 +1062,8 @@ public:
nsresult LookupNamespaceURI(const nsAString& aNamespacePrefix, nsresult LookupNamespaceURI(const nsAString& aNamespacePrefix,
nsAString& aNamespaceURI); nsAString& aNamespaceURI);
NS_IMETHOD IsEqualNode(nsIDOMNode* aOther, PRBool* aReturn) = 0; nsresult IsEqualNode(nsIDOMNode* aOther, PRBool* aReturn);
PRBool IsEqualTo(nsINode* aOther);
nsIContent* GetNextSibling() const { return mNextSibling; } nsIContent* GetNextSibling() const { return mNextSibling; }
nsIContent* GetPreviousSibling() const { return mPreviousSibling; } nsIContent* GetPreviousSibling() const { return mPreviousSibling; }

View File

@@ -514,28 +514,7 @@ nsDOMAttribute::CompareDocumentPosition(nsIDOMNode *other,
NS_IMETHODIMP NS_IMETHODIMP
nsDOMAttribute::IsEqualNode(nsIDOMNode* aOther, PRBool* aResult) nsDOMAttribute::IsEqualNode(nsIDOMNode* aOther, PRBool* aResult)
{ {
*aResult = PR_FALSE; return nsINode::IsEqualNode(aOther, aResult);
nsCOMPtr<nsIAttribute> otherAttr = do_QueryInterface(aOther);
if (!otherAttr)
return NS_OK;
nsDOMAttribute *other = static_cast<nsDOMAttribute*>(otherAttr.get());
// Prefix, namespace URI, local name, node name check.
if (!mNodeInfo->Equals(other->NodeInfo())) {
return NS_OK;
}
// Value check
// Checks not needed: Child nodes, attributes.
nsAutoString ourValue, otherValue;
GetValue(ourValue);
other->GetValue(otherValue);
*aResult = ourValue.Equals(otherValue);
return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP

View File

@@ -5792,30 +5792,7 @@ nsDocument::GetTextContent(nsAString &aTextContent)
NS_IMETHODIMP NS_IMETHODIMP
nsDocument::IsEqualNode(nsIDOMNode* aOther, PRBool* aResult) nsDocument::IsEqualNode(nsIDOMNode* aOther, PRBool* aResult)
{ {
*aResult = PR_FALSE; return nsINode::IsEqualNode(aOther, aResult);
nsCOMPtr<nsIDocument> other = do_QueryInterface(aOther);
if (!other) {
return NS_OK;
}
// Child nodes check.
PRUint32 childCount = GetChildCount();
if (childCount != other->GetChildCount()) {
return NS_OK;
}
for (PRUint32 i = 0; i < childCount; i++) {
if (!GetChildAt(i)->IsEqual(other->GetChildAt(i))) {
return NS_OK;
}
}
// Checks not needed: Prefix, namespace URI, local name, node name,
// node value, attributes.
*aResult = PR_TRUE;
return NS_OK;
} }
NS_IMETHODIMP NS_IMETHODIMP

View File

@@ -801,6 +801,184 @@ nsINode::CompareDocPosition(nsINode* aOtherNode)
nsIDOMNode::DOCUMENT_POSITION_CONTAINED_BY); nsIDOMNode::DOCUMENT_POSITION_CONTAINED_BY);
} }
PRBool
nsINode::IsEqualTo(nsINode* aOther)
{
if (!aOther) {
return PR_FALSE;
}
nsAutoString string1, string2;
nsINode* node1 = this;
nsINode* node2 = aOther;
do {
PRUint16 nodeType = node1->NodeType();
if (nodeType != node2->NodeType()) {
return PR_FALSE;
}
if (!node1->mNodeInfo->Equals(node2->mNodeInfo)) {
return PR_FALSE;
}
switch(nodeType) {
case nsIDOMNode::ELEMENT_NODE:
{
// Both are elements (we checked that their nodeinfos are equal). Do the
// check on attributes.
Element* element1 = node1->AsElement();
Element* element2 = node2->AsElement();
PRUint32 attrCount = element1->GetAttrCount();
if (attrCount != element2->GetAttrCount()) {
return PR_FALSE;
}
// Iterate over attributes.
for (PRUint32 i = 0; i < attrCount; ++i) {
const nsAttrName* attrName = element1->GetAttrNameAt(i);
#ifdef DEBUG
PRBool hasAttr =
#endif
element1->GetAttr(attrName->NamespaceID(), attrName->LocalName(),
string1);
NS_ASSERTION(hasAttr, "Why don't we have an attr?");
if (!element2->AttrValueIs(attrName->NamespaceID(),
attrName->LocalName(),
string1,
eCaseMatters)) {
return PR_FALSE;
}
}
break;
}
case nsIDOMNode::TEXT_NODE:
case nsIDOMNode::COMMENT_NODE:
case nsIDOMNode::CDATA_SECTION_NODE:
case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
{
string1.Truncate();
static_cast<nsIContent*>(node1)->AppendTextTo(string1);
string2.Truncate();
static_cast<nsIContent*>(node2)->AppendTextTo(string2);
if (!string1.Equals(string2)) {
return PR_FALSE;
}
if (nodeType == nsIDOMNode::PROCESSING_INSTRUCTION_NODE) {
nsCOMPtr<nsIDOMNode> domNode1 = do_QueryInterface(node1);
nsCOMPtr<nsIDOMNode> domNode2 = do_QueryInterface(node2);
domNode1->GetNodeName(string1);
domNode2->GetNodeName(string2);
if (!string1.Equals(string2)) {
return PR_FALSE;
}
}
break;
}
case nsIDOMNode::DOCUMENT_NODE:
case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
break;
case nsIDOMNode::ATTRIBUTE_NODE:
{
NS_ASSERTION(node1 == this && node2 == aOther,
"Did we come upon an attribute node while walking a "
"subtree?");
nsCOMPtr<nsIDOMNode> domNode1 = do_QueryInterface(node1);
nsCOMPtr<nsIDOMNode> domNode2 = do_QueryInterface(node2);
domNode1->GetNodeValue(string1);
domNode2->GetNodeValue(string2);
// Returning here as to not bother walking subtree. And there is no
// risk that we're half way through walking some other subtree since
// attribute nodes doesn't appear in subtrees.
return string1.Equals(string2);
}
case nsIDOMNode::DOCUMENT_TYPE_NODE:
{
nsCOMPtr<nsIDOMDocumentType> docType1 = do_QueryInterface(node1);
nsCOMPtr<nsIDOMDocumentType> docType2 = do_QueryInterface(node2);
NS_ASSERTION(docType1 && docType2, "Why don't we have a document type node?");
// Node name
docType1->GetNodeName(string1);
docType2->GetNodeName(string2);
if (!string1.Equals(string2)) {
return PR_FALSE;
}
// Public ID
docType1->GetPublicId(string1);
docType2->GetPublicId(string2);
if (!string1.Equals(string2)) {
return PR_FALSE;
}
// System ID
docType1->GetSystemId(string1);
docType2->GetSystemId(string2);
if (!string1.Equals(string2)) {
return PR_FALSE;
}
// Internal subset
docType1->GetInternalSubset(string1);
docType2->GetInternalSubset(string2);
if (!string1.Equals(string2)) {
return PR_FALSE;
}
break;
}
default:
NS_ABORT_IF_FALSE(PR_FALSE, "Unknown node type");
}
nsINode* nextNode = node1->GetFirstChild();
if (nextNode) {
node1 = nextNode;
node2 = node2->GetFirstChild();
}
else {
if (node2->GetFirstChild()) {
// node2 has a firstChild, but node1 doesn't
return PR_FALSE;
}
// Find next sibling, possibly walking parent chain.
while (1) {
if (node1 == this) {
NS_ASSERTION(node2 == aOther, "Should have reached the start node "
"for both trees at the same time");
return PR_TRUE;
}
nextNode = node1->GetNextSibling();
if (nextNode) {
node1 = nextNode;
node2 = node2->GetNextSibling();
break;
}
if (node2->GetNextSibling()) {
// node2 has a nextSibling, but node1 doesn't
return PR_FALSE;
}
node1 = node1->GetNodeParent();
node2 = node2->GetNodeParent();
NS_ASSERTION(node1 && node2, "no parent while walking subtree");
}
}
} while(node2);
return PR_FALSE;
}
nsresult nsresult
nsINode::LookupNamespaceURI(const nsAString& aNamespacePrefix, nsINode::LookupNamespaceURI(const nsAString& aNamespacePrefix,
nsAString& aNamespaceURI) nsAString& aNamespaceURI)
@@ -1176,112 +1354,6 @@ NS_INTERFACE_MAP_END_AGGREGATED(mNode)
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNode3Tearoff) NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNode3Tearoff)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNode3Tearoff) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNode3Tearoff)
PRBool
nsIContent::IsEqual(nsIContent* aOther)
{
// We use nsIContent instead of nsINode for the attributes of elements.
NS_PRECONDITION(aOther, "Who called IsEqual?");
nsAutoString string1, string2;
// Prefix, namespace URI, local name, node name check.
if (!NodeInfo()->Equals(aOther->NodeInfo())) {
return PR_FALSE;
}
if (Tag() == nsGkAtoms::documentTypeNodeName) {
nsCOMPtr<nsIDOMDocumentType> docType1 = do_QueryInterface(this);
nsCOMPtr<nsIDOMDocumentType> docType2 = do_QueryInterface(aOther);
NS_ASSERTION(docType1 && docType2, "Why don't we have a document type node?");
// Public ID
docType1->GetPublicId(string1);
docType2->GetPublicId(string2);
if (!string1.Equals(string2)) {
return PR_FALSE;
}
// System ID
docType1->GetSystemId(string1);
docType2->GetSystemId(string2);
if (!string1.Equals(string2)) {
return PR_FALSE;
}
// Internal subset
docType1->GetInternalSubset(string1);
docType2->GetInternalSubset(string2);
if (!string1.Equals(string2)) {
return PR_FALSE;
}
}
if (IsElement()) {
// Both are elements (we checked that their nodeinfos are equal). Do the
// check on attributes.
Element* element2 = aOther->AsElement();
PRUint32 attrCount = GetAttrCount();
if (attrCount != element2->GetAttrCount()) {
return PR_FALSE;
}
// Iterate over attributes.
for (PRUint32 i = 0; i < attrCount; ++i) {
const nsAttrName* attrName1 = GetAttrNameAt(i);
#ifdef DEBUG
PRBool hasAttr =
#endif
GetAttr(attrName1->NamespaceID(), attrName1->LocalName(), string1);
NS_ASSERTION(hasAttr, "Why don't we have an attr?");
if (!element2->AttrValueIs(attrName1->NamespaceID(),
attrName1->LocalName(),
string1,
eCaseMatters)) {
return PR_FALSE;
}
}
} else {
// Node value check.
nsCOMPtr<nsIDOMNode> domNode1 = do_QueryInterface(this);
nsCOMPtr<nsIDOMNode> domNode2 = do_QueryInterface(aOther);
NS_ASSERTION(domNode1 && domNode2, "How'd we get nsIContent without nsIDOMNode?");
domNode1->GetNodeValue(string1);
domNode2->GetNodeValue(string2);
if (!string1.Equals(string2)) {
return PR_FALSE;
}
}
// Child nodes count.
PRUint32 childCount = GetChildCount();
if (childCount != aOther->GetChildCount()) {
return PR_FALSE;
}
// Iterate over child nodes.
for (PRUint32 i = 0; i < childCount; ++i) {
if (!GetChildAt(i)->IsEqual(aOther->GetChildAt(i))) {
return PR_FALSE;
}
}
return PR_TRUE;
}
NS_IMETHODIMP
nsIContent::IsEqualNode(nsIDOMNode* aOther, PRBool* aResult)
{
nsCOMPtr<nsIContent> other = do_QueryInterface(aOther);
*aResult = other && IsEqual(other);
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
nsNode3Tearoff::LookupNamespaceURI(const nsAString& aNamespacePrefix, nsNode3Tearoff::LookupNamespaceURI(const nsAString& aNamespacePrefix,
nsAString& aNamespaceURI) nsAString& aNamespaceURI)
@@ -4166,6 +4238,14 @@ nsINode::CompareDocumentPosition(nsIDOMNode* aOther, PRUint16* aReturn)
return NS_OK; return NS_OK;
} }
nsresult
nsINode::IsEqualNode(nsIDOMNode* aOther, PRBool* aReturn)
{
nsCOMPtr<nsINode> other = do_QueryInterface(aOther);
*aReturn = IsEqualTo(other);
return NS_OK;
}
nsresult nsresult
nsINode::IsSameNode(nsIDOMNode* aOther, PRBool* aReturn) nsINode::IsSameNode(nsIDOMNode* aOther, PRBool* aReturn)
{ {

View File

@@ -50,6 +50,7 @@ function run_test()
test_isEqualNode_normalization(); test_isEqualNode_normalization();
test_isEqualNode_whitespace(); test_isEqualNode_whitespace();
test_isEqualNode_namespaces(); test_isEqualNode_namespaces();
test_isEqualNode_wholeDoc();
// XXX should Node.isEqualNode(null) throw or return false? // XXX should Node.isEqualNode(null) throw or return false?
//test_isEqualNode_null(); //test_isEqualNode_null();
@@ -400,6 +401,21 @@ function test_isEqualNode_null()
} }
} }
function test_isEqualNode_wholeDoc()
{
doc = ParseFile("isequalnode_data.xml");
var doc2 = ParseFile("isequalnode_data.xml");
var tw1 =
doc.createTreeWalker(doc, Components.interfaces.nsIDOMNodeFilter.SHOW_ALL,
null, false);
var tw2 =
doc2.createTreeWalker(doc2, Components.interfaces.nsIDOMNodeFilter.SHOW_ALL,
null, false);
do {
check_eq_nodes(tw1.currentNode, tw2.currentNode);
tw1.nextNode();
} while(tw2.nextNode());
}
// UTILITY FUNCTIONS // UTILITY FUNCTIONS

View File

@@ -1731,7 +1731,7 @@ nsFocusManager::Focus(nsPIDOMWindow* aWindow,
mFocusedContent = aContent; mFocusedContent = aContent;
nsIContent* focusedNode = aWindow->GetFocusedNode(); nsIContent* focusedNode = aWindow->GetFocusedNode();
PRBool isRefocus = focusedNode && focusedNode->IsEqual(aContent); PRBool isRefocus = focusedNode && focusedNode->IsEqualTo(aContent);
aWindow->SetFocusedNode(aContent, focusMethod); aWindow->SetFocusedNode(aContent, focusMethod);

View File

@@ -811,6 +811,8 @@ customMethodCalls = {
}, },
'nsIDOMNode_IsEqualNode': { 'nsIDOMNode_IsEqualNode': {
'thisType': 'nsINode', 'thisType': 'nsINode',
'arg0Type': 'nsINode',
'code': ' PRUint16 result = self->IsEqualTo(arg0);',
'canFail': False 'canFail': False
}, },
'nsIDOMNode_GetUserData': { 'nsIDOMNode_GetUserData': {