Initial implementation of DOM Level 3 Core methods Node.compareTreePosition() and Node.isSameNode(), Bug 139344, r=jkeiser, sr=jst

This commit is contained in:
caillon%returnzero.com 2002-05-14 01:28:17 +00:00
parent ed1ec0e54d
commit ddd3d5d711
10 changed files with 447 additions and 2 deletions

View File

@ -43,6 +43,7 @@
#include "nsINameSpaceManager.h"
#include "nsDOMError.h"
#include "nsContentUtils.h"
#include "nsUnicharUtils.h"
//----------------------------------------------------------------------
@ -494,6 +495,104 @@ nsDOMAttribute::GetBaseURI(nsAString &aURI)
return rv;
}
NS_IMETHODIMP
nsDOMAttribute::CompareTreePosition(nsIDOMNode* aOther,
PRUint16* aReturn)
{
NS_ENSURE_ARG_POINTER(aOther);
PRUint16 mask = nsIDOMNode::TREE_POSITION_DISCONNECTED;
nsCOMPtr<nsIDOMElement> el;
GetOwnerElement(getter_AddRefs(el));
if (el) {
// Check to see if the other node is also an attribute
PRUint16 nodeType = 0;
aOther->GetNodeType(&nodeType);
if (nodeType == nsIDOMNode::ATTRIBUTE_NODE) {
nsCOMPtr<nsIDOMAttr> otherAttr(do_QueryInterface(aOther));
nsCOMPtr<nsIDOMElement> otherEl;
otherAttr->GetOwnerElement(getter_AddRefs(otherEl));
if (el == otherEl) {
// same parent node, the two attributes have equivalent position
mask |= nsIDOMNode::TREE_POSITION_EQUIVALENT;
PRBool sameNode = PR_FALSE;
IsSameNode(aOther, &sameNode);
if (sameNode) {
mask |= nsIDOMNode::TREE_POSITION_SAME_NODE;
}
}
}
else {
// The other node isn't an attribute.
// Compare position relative to this attribute's owner element.
nsCOMPtr<nsIDOM3Node> parent(do_QueryInterface(el));
PRUint16 parentMask;
parent->CompareTreePosition(aOther, &parentMask);
if (parentMask & nsIDOMNode::TREE_POSITION_SAME_NODE) {
mask |= nsIDOMNode::TREE_POSITION_PRECEDING;
}
else {
mask |= parentMask & (nsIDOMNode::TREE_POSITION_FOLLOWING |
nsIDOMNode::TREE_POSITION_PRECEDING);
}
}
}
*aReturn = mask;
return NS_OK;
}
NS_IMETHODIMP
nsDOMAttribute::IsSameNode(nsIDOMNode* aOther,
PRBool* aReturn)
{
PRBool sameNode = PR_FALSE;
// XXXcaa Comparing pointers on two attributes is not yet reliable.
// When bug 93614 is fixed, this should be changed to simple pointer
// comparisons. But for now, check owner elements and node names.
PRUint16 otherType = 0;
aOther->GetNodeType(&otherType);
if (nsIDOMNode::ATTRIBUTE_NODE == otherType) {
nsCOMPtr<nsIDOMElement> nodeOwner;
GetOwnerElement(getter_AddRefs(nodeOwner));
nsCOMPtr<nsIDOMAttr> other(do_QueryInterface(aOther));
nsCOMPtr<nsIDOMElement> otherOwner;
other->GetOwnerElement(getter_AddRefs(otherOwner));
nsCOMPtr<nsIDOM3Node> owner(do_QueryInterface(nodeOwner));
PRBool sameOwners = PR_FALSE;
owner->IsSameNode(otherOwner, &sameOwners);
// Do these attributes belong to the same element?
if (sameOwners) {
PRBool ci = PR_FALSE;
nsCOMPtr<nsIContent> content(do_QueryInterface(nodeOwner));
// Check to see if we're in HTML.
if (content->IsContentOfType(nsIContent::eHTML)) {
nsCOMPtr<nsINodeInfo> ni;
content->GetNodeInfo(*getter_AddRefs(ni));
if (ni) {
// If there is no namespace, we're in HTML (as opposed to XHTML)
// and we'll need to compare node names case insensitively.
ci = ni->NamespaceEquals(kNameSpaceID_None);
}
}
nsAutoString nodeName;
nsAutoString otherName;
GetNodeName(nodeName);
aOther->GetNodeName(otherName);
// Compare node names
sameNode = ci ? nodeName.Equals(otherName,
nsCaseInsensitiveStringComparator())
: nodeName.Equals(otherName);
}
}
*aReturn = sameNode;
return NS_OK;
}
NS_IMETHODIMP
nsDOMAttribute::LookupNamespacePrefix(const nsAString& aNamespaceURI,
nsAString& aPrefix)

View File

@ -472,8 +472,9 @@ NS_IMPL_RELEASE_USING_AGGREGATOR(nsXPathDocumentTearoff, mDocument)
// =
// ==================================================================
nsDocument::nsDocument() : mIsGoingAway(PR_FALSE),
mCSSLoader(nsnull), mSubDocuments(nsnull),
nsDocument::nsDocument() : mSubDocuments(nsnull),
mIsGoingAway(PR_FALSE),
mCSSLoader(nsnull),
mXPathDocument(nsnull)
{
NS_INIT_REFCNT();
@ -3336,6 +3337,48 @@ nsDocument::GetBaseURI(nsAString &aURI)
return NS_OK;
}
NS_IMETHODIMP
nsDocument::CompareTreePosition(nsIDOMNode* aOther,
PRUint16* aReturn)
{
NS_ENSURE_ARG_POINTER(aOther);
PRUint16 mask = nsIDOMNode::TREE_POSITION_DISCONNECTED;
PRBool sameNode = PR_FALSE;
IsSameNode(aOther, &sameNode);
if (sameNode) {
mask |= (nsIDOMNode::TREE_POSITION_SAME_NODE |
nsIDOMNode::TREE_POSITION_EQUIVALENT);
}
else {
nsCOMPtr<nsIDOMDocument> otherDoc;
aOther->GetOwnerDocument(getter_AddRefs(otherDoc));
nsCOMPtr<nsIDOMNode> other(do_QueryInterface(otherDoc));
IsSameNode(other, &sameNode);
if (sameNode) {
mask |= (nsIDOMNode::TREE_POSITION_DESCENDANT |
nsIDOMNode::TREE_POSITION_FOLLOWING);
}
}
*aReturn = mask;
return NS_OK;
}
NS_IMETHODIMP
nsDocument::IsSameNode(nsIDOMNode* aOther,
PRBool* aReturn)
{
PRBool sameNode = PR_FALSE;
if (this == aOther) {
sameNode = PR_TRUE;
}
*aReturn = sameNode;
return NS_OK;
}
NS_IMETHODIMP
nsDocument::LookupNamespacePrefix(const nsAString& aNamespaceURI,
nsAString& aPrefix)

View File

@ -125,6 +125,8 @@ public:
// nsIDOM3Node
NS_IMETHOD GetBaseURI(nsAString& aURI)
{ aURI.Truncate(); return NS_OK; }
NS_IMETHOD CompareTreePosition(nsIDOMNode *aOther, PRUint16* aReturn);
NS_IMETHOD IsSameNode(nsIDOMNode *aOther, PRBool* aReturn);
NS_IMETHOD LookupNamespacePrefix(const nsAString& aNamespaceURI,
nsAString& aPrefix) {
aPrefix.Truncate(); return NS_OK;
@ -382,3 +384,62 @@ nsDocumentFragment::CloneNode(PRBool aDeep, nsIDOMNode** aReturn)
return CallQueryInterface(newFragment, aReturn);
}
NS_IMETHODIMP
nsDocumentFragment::CompareTreePosition(nsIDOMNode* aOther,
PRUint16* aReturn)
{
NS_ENSURE_ARG_POINTER(aOther);
PRUint32 mask = nsIDOMNode::TREE_POSITION_DISCONNECTED;
PRBool sameNode = PR_FALSE;
IsSameNode(aOther, &sameNode);
if (sameNode) {
mask |= nsIDOMNode::TREE_POSITION_SAME_NODE;
}
else {
nsCOMPtr<nsIDOMNode> other(aOther);
while (other) {
IsSameNode(other, &sameNode);
if (sameNode) {
mask |= nsIDOMNode::TREE_POSITION_DESCENDANT;
break;
}
nsCOMPtr<nsIDOMNode> tmp(other);
tmp->GetParentNode(getter_AddRefs(other));
if (!other) {
// No parent. Check to see if we're at an attribute node.
PRUint16 nodeType = 0;
tmp->GetNodeType(&nodeType);
if (nodeType == nsIDOMNode::ATTRIBUTE_NODE) {
// If we are, let's get the owner element and continue up the tree
nsCOMPtr<nsIDOMAttr> attr(do_QueryInterface(tmp));
nsCOMPtr<nsIDOMElement> owner;
attr->GetOwnerElement(getter_AddRefs(owner));
other = do_QueryInterface(owner);
continue;
}
break;
}
}
}
*aReturn = mask;
return NS_OK;
}
NS_IMETHODIMP
nsDocumentFragment::IsSameNode(nsIDOMNode* aOther,
PRBool* aReturn)
{
PRBool sameNode = PR_FALSE;
if (this == aOther) {
sameNode = PR_TRUE;
}
*aReturn = sameNode;
return NS_OK;
}

View File

@ -246,6 +246,177 @@ nsNode3Tearoff::GetBaseURI(nsAString& aURI)
return NS_OK;
}
NS_IMETHODIMP
nsNode3Tearoff::CompareTreePosition(nsIDOMNode* aOther,
PRUint16* aReturn)
{
NS_ENSURE_ARG_POINTER(aOther);
PRUint16 mask = nsIDOMNode::TREE_POSITION_DISCONNECTED;
PRBool sameNode = PR_FALSE;
IsSameNode(aOther, &sameNode);
if (sameNode) {
mask |= nsIDOMNode::TREE_POSITION_SAME_NODE;
nsCOMPtr<nsIDocument> doc;
mContent->GetDocument(*getter_AddRefs(doc));
// Loose nodes without an owner document are not equivalent
// in tree position since they are not in any tree.
// Add the 'equivalent' flag only if we have an owner document.
if (doc) {
mask |= nsIDOMNode::TREE_POSITION_EQUIVALENT;
}
*aReturn = mask;
return NS_OK;
}
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
// If the other node is an attribute, document, or document fragment,
// we can find the position easier by comparing this node relative to
// the other node, and then reversing positions.
PRUint16 otherType = 0;
aOther->GetNodeType(&otherType);
if (otherType == nsIDOMNode::ATTRIBUTE_NODE ||
otherType == nsIDOMNode::DOCUMENT_NODE ||
otherType == nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
PRUint16 otherMask = nsIDOMNode::TREE_POSITION_DISCONNECTED;
nsCOMPtr<nsIDOM3Node> other(do_QueryInterface(aOther));
other->CompareTreePosition(node, &otherMask);
if (otherMask & nsIDOMNode::TREE_POSITION_FOLLOWING) {
mask |= nsIDOMNode::TREE_POSITION_PRECEDING;
} else if (otherMask & nsIDOMNode::TREE_POSITION_PRECEDING) {
mask |= nsIDOMNode::TREE_POSITION_FOLLOWING;
}
if (otherMask & nsIDOMNode::TREE_POSITION_ANCESTOR) {
mask |= nsIDOMNode::TREE_POSITION_DESCENDANT;
} else if (otherMask & nsIDOMNode::TREE_POSITION_DESCENDANT) {
mask |= nsIDOMNode::TREE_POSITION_ANCESTOR;
}
*aReturn = mask;
return NS_OK;
}
#ifdef DEBUG
{
PRUint16 nodeType = 0;
node->GetNodeType(&nodeType);
NS_ASSERTION((nodeType == nsIDOMNode::ELEMENT_NODE ||
nodeType == nsIDOMNode::TEXT_NODE ||
nodeType == nsIDOMNode::CDATA_SECTION_NODE ||
nodeType == nsIDOMNode::ENTITY_REFERENCE_NODE ||
nodeType == nsIDOMNode::PROCESSING_INSTRUCTION_NODE ||
nodeType == nsIDOMNode::COMMENT_NODE),
"Invalid node type!");
}
#endif
nsAutoVoidArray nodeAncestorList;
nsAutoVoidArray otherAncestorList;
PRInt32 nodeAncestorIdx = 0;
PRInt32 otherAncestorIdx = 0;
nodeAncestorIdx = nsRange::FillArrayWithAncestors(&nodeAncestorList, node);
otherAncestorIdx = nsRange::FillArrayWithAncestors(&otherAncestorList, aOther);
NS_ASSERTION(nodeAncestorIdx >= 0 && otherAncestorIdx >= 0,
"nsRange::FillArrayWithAncestors() failed!");
nsIDOMNode* nodeRoot =
NS_STATIC_CAST(nsIDOMNode*, nodeAncestorList.ElementAt(nodeAncestorIdx));
nsIDOMNode* otherRoot =
NS_STATIC_CAST(nsIDOMNode*, otherAncestorList.ElementAt(otherAncestorIdx));
if (nodeRoot == otherRoot) {
// There is at least one common ancestor (the root node)
//
// First, let's find the common ancestor closest to both nodes
// Go back through the array starting with the root until the
// first different ancestor is found. When the loop finishes
// iterating, the indices will both be 1 below the common ancestor.
while (nodeAncestorIdx >= 0 && otherAncestorIdx >= 0 &&
nodeAncestorList.ElementAt(nodeAncestorIdx) ==
otherAncestorList.ElementAt(otherAncestorIdx)) {
--nodeAncestorIdx;
--otherAncestorIdx;
}
// Check that the root element is a document node. If it is, we can have
// preceding and following flags.
PRUint16 rootType = 0;
nodeRoot->GetNodeType(&rootType);
if (nodeAncestorIdx < 0) {
// If we went below this node to find the common ancestor before
// exiting, then this node must be the common ancestor.
mask |= nsIDOMNode::TREE_POSITION_DESCENDANT;
if (rootType == nsIDOMNode::DOCUMENT_NODE) {
mask |= nsIDOMNode::TREE_POSITION_FOLLOWING;
}
}
else if (otherAncestorIdx < 0) {
// If we went below the other node to find the common ancestor before
// exiting, then the other node must be the common ancestor.
mask |= nsIDOMNode::TREE_POSITION_ANCESTOR;
if (rootType == nsIDOMNode::DOCUMENT_NODE) {
mask |= nsIDOMNode::TREE_POSITION_PRECEDING;
}
}
else if (rootType == nsIDOMNode::DOCUMENT_NODE) {
// These were the first different ancestors,
// so grab these two, and then the parent for the common ancestor.
nsIDOMNode* nodeAncestor =
NS_STATIC_CAST(nsIDOMNode*, nodeAncestorList.ElementAt(nodeAncestorIdx));
nsIDOMNode* otherAncestor =
NS_STATIC_CAST(nsIDOMNode*, otherAncestorList.ElementAt(otherAncestorIdx));
nsIDOMNode* commonAncestor =
NS_STATIC_CAST(nsIDOMNode*, nodeAncestorList.ElementAt(nodeAncestorIdx + 1));
// Find out which of the two nodes comes first in the document order.
// First get the children of the common ancestor.
nsCOMPtr<nsIDOMNodeList> children;
commonAncestor->GetChildNodes(getter_AddRefs(children));
PRUint32 numKids;
children->GetLength(&numKids);
for (PRUint32 i = 0; i < numKids; ++i) {
// Then go through the children one at a time to see which we hit first.
nsCOMPtr<nsIDOMNode> childNode;
children->Item(i, getter_AddRefs(childNode));
if (childNode == nodeAncestor) {
mask |= nsIDOMNode::TREE_POSITION_FOLLOWING;
break;
}
if (childNode == otherAncestor) {
mask |= nsIDOMNode::TREE_POSITION_PRECEDING;
break;
}
}
}
}
*aReturn = mask;
return NS_OK;
}
NS_IMETHODIMP
nsNode3Tearoff::IsSameNode(nsIDOMNode* aOther,
PRBool* aReturn)
{
PRBool sameNode = PR_FALSE;
nsCOMPtr<nsIContent> other(do_QueryInterface(aOther));
if (mContent == other) {
sameNode = PR_TRUE;
}
*aReturn = sameNode;
return NS_OK;
}
NS_IMETHODIMP
nsNode3Tearoff::LookupNamespacePrefix(const nsAString& aNamespaceURI,
nsAString& aPrefix)

View File

@ -1859,6 +1859,20 @@ nsHTMLDocument::GetBaseURI(nsAString &aURI)
return NS_OK;
}
NS_IMETHODIMP
nsHTMLDocument::CompareTreePosition(nsIDOMNode* aOther,
PRUint16* aReturn)
{
return nsDocument::CompareTreePosition(aOther, aReturn);
}
NS_IMETHODIMP
nsHTMLDocument::IsSameNode(nsIDOMNode* aOther,
PRBool* aReturn)
{
return nsDocument::IsSameNode(aOther, aReturn);
}
NS_IMETHODIMP
nsHTMLDocument::LookupNamespacePrefix(const nsAString& aNamespaceURI,

View File

@ -389,6 +389,8 @@ nsXULAttribute::IsSupported(const nsAString& aFeature,
return NS_ERROR_NOT_IMPLEMENTED;
}
// nsIDOM3Node interface
NS_IMETHODIMP
nsXULAttribute::GetBaseURI(nsAString &aURI)
{
@ -396,6 +398,23 @@ nsXULAttribute::GetBaseURI(nsAString &aURI)
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsXULAttribute::CompareTreePosition(nsIDOMNode* aOther,
PRUint16* aReturn)
{
NS_NOTYETIMPLEMENTED("write me");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsXULAttribute::IsSameNode(nsIDOMNode* aOther,
PRBool* aReturn)
{
NS_NOTYETIMPLEMENTED("write me");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsXULAttribute::LookupNamespacePrefix(const nsAString& aNamespaceURI,
nsAString& aPrefix)

View File

@ -4288,6 +4288,11 @@ nsXULDocument::IsSupported(const nsAString& aFeature,
return NS_ERROR_NOT_IMPLEMENTED;
}
//----------------------------------------------------------------------
//
// nsIDOM3Node interface
//
NS_IMETHODIMP
nsXULDocument::GetBaseURI(nsAString &aURI)
{
@ -4300,6 +4305,21 @@ nsXULDocument::GetBaseURI(nsAString &aURI)
return NS_OK;
}
NS_IMETHODIMP
nsXULDocument::CompareTreePosition(nsIDOMNode* aOther,
PRUint16* aReturn)
{
NS_NOTYETIMPLEMENTED("write me");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsXULDocument::IsSameNode(nsIDOMNode* aOther,
PRBool* aReturn)
{
NS_NOTYETIMPLEMENTED("write me");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsXULDocument::LookupNamespacePrefix(const nsAString& aNamespaceURI,

View File

@ -21,6 +21,7 @@
*
* Contributor(s):
* Johnny Stenback <jst@netscape.com> (original author)
* Christopher A. Aillon <christopher@aillon.com>
*
*
* Alternatively, the contents of this file may be used under the terms of
@ -45,6 +46,10 @@ interface nsIDOM3Node : nsISupports
// Introduced in DOM Level 3:
readonly attribute DOMString baseURI;
// Introduced in DOM Level 3:
unsigned short compareTreePosition(in nsIDOMNode other);
// Introduced in DOM Level 3:
boolean isSameNode(in nsIDOMNode other);
// Introduced in DOM Level 3:
DOMString lookupNamespacePrefix(in DOMString namespaceURI);
// Introduced in DOM Level 3:
DOMString lookupNamespaceURI(in DOMString prefix);

View File

@ -109,4 +109,13 @@ interface nsIDOMNode : nsISupports
readonly attribute DOMString localName;
// Introduced in DOM Level 2:
boolean hasAttributes();
// Introduced in DOM Level 3:
const unsigned short TREE_POSITION_PRECEDING = 0x01;
const unsigned short TREE_POSITION_FOLLOWING = 0x02;
const unsigned short TREE_POSITION_ANCESTOR = 0x04;
const unsigned short TREE_POSITION_DESCENDANT = 0x08;
const unsigned short TREE_POSITION_EQUIVALENT = 0x10;
const unsigned short TREE_POSITION_SAME_NODE = 0x20;
const unsigned short TREE_POSITION_DISCONNECTED = 0x00;
};

View File

@ -1259,6 +1259,7 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDocumentTraversal)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDocumentXBL)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
DOM_CLASSINFO_MAP_ENTRY(nsIDOM3Node)
DOM_CLASSINFO_MAP_END_WITH_XPATH
DOM_CLASSINFO_MAP_BEGIN(DocumentType, nsIDOMDocumentType)
@ -1361,6 +1362,7 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDocumentTraversal)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDocumentXBL)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
DOM_CLASSINFO_MAP_ENTRY(nsIDOM3Node)
DOM_CLASSINFO_MAP_END_WITH_XPATH
DOM_CLASSINFO_MAP_BEGIN(HTMLCollection, nsIDOMHTMLCollection)
@ -1790,6 +1792,7 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDocumentRange)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDocumentTraversal)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
DOM_CLASSINFO_MAP_ENTRY(nsIDOM3Node)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(XULElement, nsIDOMXULElement)
@ -1862,6 +1865,7 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDocument)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSDocument)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMNode)
DOM_CLASSINFO_MAP_ENTRY(nsIDOM3Node)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDocumentEvent)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDocumentView)