Bug 683852 - Implement Node.contains(node), r=bz
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Fri, 02 Sep 2011 23:15:53 +0300
changeset 76453 3dc687a271e777b8cc1eae8152b98b9228383906
parent 76452 e1d9d6120f84423acdf19cdc87d9f6c9cf204c85
child 76460 9ca928d8095c07ac3ba0a66baeb9799dfd15215c
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
reviewersbz
bugs683852
milestone9.0a1
Bug 683852 - Implement Node.contains(node), r=bz
content/base/public/nsINode.h
content/base/src/nsDOMAttribute.cpp
content/base/src/nsDocument.cpp
content/base/src/nsGenericElement.cpp
content/base/test/chrome/Makefile.in
content/base/test/chrome/test_bug683852.xul
dom/interfaces/core/nsIDOMNode.idl
js/src/xpconnect/src/dom_quickstubs.qsconf
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -277,18 +277,18 @@ private:
 #define DOM_USER_DATA         1
 #define DOM_USER_DATA_HANDLER 2
 #ifdef MOZ_SMIL
 #define SMIL_MAPPED_ATTR_ANIMVAL 3
 #endif // MOZ_SMIL
 
 // IID for the nsINode interface
 #define NS_INODE_IID \
-{ 0x5572c8a9, 0xbda9, 0x4b78, \
-  { 0xb4, 0x1a, 0xdb, 0x1a, 0x83, 0xef, 0x53, 0x7e } }
+{ 0xb59269fe, 0x7f60, 0x4672, \
+  { 0x8e, 0x56, 0x01, 0x84, 0xb2, 0x58, 0x14, 0xb0 } }
 
 /**
  * An internal interface that abstracts some DOMNode-related parts that both
  * nsIContent and nsIDocument share.  An instance of this interface has a list
  * of nsIContent children and provides access to them.
  */
 class nsINode : public nsIDOMEventTarget,
                 public nsWrapperCache
@@ -1080,16 +1080,24 @@ public:
    * descendants of aRoot, not including aRoot itself, will be returned.
    * Returns null if there are no more nodes to traverse.
    */
   nsIContent* GetNextNonChildNode(const nsINode* aRoot = nsnull) const
   {
     return GetNextNodeImpl(aRoot, PR_TRUE);
   }
 
+  /**
+   * Returns true if 'this' is either document or element or
+   * document fragment and aOther is a descendant in the same
+   * anonymous tree.
+   */
+  PRBool Contains(const nsINode* aOther) const;
+  nsresult Contains(nsIDOMNode* aOther, PRBool* aReturn);
+
 private:
 
   nsIContent* GetNextNodeImpl(const nsINode* aRoot,
                               const PRBool aSkipChildren) const
   {
     // Can't use nsContentUtils::ContentIsDescendantOf here, since we
     // can't include it here.
 #ifdef DEBUG
--- a/content/base/src/nsDOMAttribute.cpp
+++ b/content/base/src/nsDOMAttribute.cpp
@@ -653,16 +653,22 @@ nsDOMAttribute::SetTextContent(const nsA
 NS_IMETHODIMP
 nsDOMAttribute::IsSameNode(nsIDOMNode *other, PRBool *aResult)
 {
   *aResult = other == this;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDOMAttribute::Contains(nsIDOMNode* aOther, PRBool* aReturn)
+{
+  return nsINode::Contains(aOther, aReturn);
+}
+
+NS_IMETHODIMP
 nsDOMAttribute::LookupPrefix(const nsAString & namespaceURI,
                              nsAString & aResult)
 {
   SetDOMStringToNull(aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -5870,16 +5870,22 @@ nsDocument::SetUserData(const nsAString 
 NS_IMETHODIMP
 nsDocument::GetUserData(const nsAString & key,
                         nsIVariant **aResult)
 {
   return nsINode::GetUserData(key, aResult);
 }
 
 NS_IMETHODIMP
+nsDocument::Contains(nsIDOMNode* aOther, PRBool* aReturn)
+{
+  return nsINode::Contains(aOther, aReturn);
+}
+
+NS_IMETHODIMP
 nsDocument::GetInputEncoding(nsAString& aInputEncoding)
 {
   if (mHaveInputEncoding) {
     return GetCharacterSet(aInputEncoding);
   }
 
   SetDOMStringToNull(aInputEncoding);
   return NS_OK;
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -5525,8 +5525,49 @@ nsGenericElement::SizeOf() const
       return NS_OK;                                                          \
     }                                                                        \
     return elm->SetJSEventListenerToJsval(nsGkAtoms::on##name_, cx, obj, v); \
 }
 #define TOUCH_EVENT EVENT
 #include "nsEventNameList.h"
 #undef TOUCH_EVENT
 #undef EVENT
+
+PRBool
+nsINode::Contains(const nsINode* aOther) const
+{
+  if (!aOther ||
+      aOther == this ||
+      GetOwnerDoc() != aOther->GetOwnerDoc() ||
+      IsInDoc() != aOther->IsInDoc() ||
+      !(aOther->IsElement() ||
+        aOther->IsNodeOfType(nsINode::eCONTENT)) ||
+      !GetFirstChild()) {
+    return PR_FALSE;
+  }
+
+  const nsIContent* other = static_cast<const nsIContent*>(aOther);
+  if (this == GetOwnerDoc()) {
+    // document.contains(aOther) returns true if aOther is in the document,
+    // but is not in any anonymous subtree.
+    // IsInDoc() check is done already before this.
+    return !other->IsInAnonymousSubtree();
+  }
+
+  if (!IsElement() && !IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT)) {
+    return PR_FALSE;
+  }
+
+  const nsIContent* thisContent = static_cast<const nsIContent*>(this);
+  if (thisContent->GetBindingParent() != other->GetBindingParent()) {
+    return PR_FALSE;
+  }
+
+  return nsContentUtils::ContentIsDescendantOf(other, this);
+}
+
+nsresult
+nsINode::Contains(nsIDOMNode* aOther, PRBool* aReturn)
+{
+  nsCOMPtr<nsINode> node = do_QueryInterface(aOther);
+  *aReturn = Contains(node);
+  return NS_OK;
+}
--- a/content/base/test/chrome/Makefile.in
+++ b/content/base/test/chrome/Makefile.in
@@ -64,15 +64,16 @@ include $(topsrcdir)/config/rules.mk
     file_bug616841.xul \
     test_bug635835.xul \
     test_fileconstructor.xul \
     fileconstructor_file.png \
     test_bug339494.xul \
     test_bug357450.xul \
     test_bug571390.xul \
     test_bug574596.html \
+    test_bug683852.xul \
     $(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
 
 libs:: $(_CHROME_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/base/test/chrome/test_bug683852.xul
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=683852
+-->
+<window title="Mozilla Bug 683852"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+  <button value="testbutton" id="testbutton"/>
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=683852"
+     target="_blank" id="link">Mozilla Bug 683852</a>
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+  /** Test for Bug 683852 **/
+  SimpleTest.waitForExplicitFinish();
+
+  function startTest() {
+    is(document.contains(document), false, "Document should not contain itself!");
+
+    var tb = document.getElementById("testbutton");
+    is(document.contains(tb), true, "Document should contain element in it!");
+    var anon = document.getAnonymousElementByAttribute(tb, "anonid", "button-box");
+    is(document.contains(anon), false, "Document should not contain anonymous element in it!");
+    is(tb.contains(anon), false, "Element should not contain anonymous element in it!");
+    is(document.documentElement.contains(tb), true, "Element should contain element in it!");
+    is(document.contains(document.createElement("foo")), false, "Document shouldn't contain element which is't in the document");
+    is(document.contains(document.createTextNode("foo")), false, "Document shouldn't contain text node which is't in the document");
+
+    var link = document.getElementById("link");
+    is(document.contains(link.firstChild), true,
+       "Document should contain a text node in it.");
+    is(link.contains(link.firstChild), true,
+       "Element should contain a text node in it.");
+    is(link.firstChild.contains(link), false, "text node shouldn't contain its parent.");
+
+    is(document.contains(null), false, "Document shouldn't contain null.");
+
+    var pi = document.createProcessingInstruction("adf", "asd");
+    is(pi.contains(document), false, "Processing instruction shouldn't contain document");
+    document.documentElement.appendChild(pi);
+    document.contains(pi, true, "Document should contain processing instruction");
+
+    var df = document.createRange().createContextualFragment("<div>foo</div>");
+    is(df.contains(df.firstChild), true, "Document fragment should contain its child");
+    is(df.contains(df.firstChild.firstChild), true,
+       "Document fragment should contain its descendant");
+    is(df.contains(df), false, "Document fragment shouldn't contain itself.");
+
+    var d = document.implementation.createHTMLDocument("");
+    is(document.contains(d), false,
+       "Document shouldn't contain another document.");
+    is(document.contains(d.createElement("div")), false,
+       "Document shouldn't contain an element from another document.");
+
+    SimpleTest.finish();
+  }
+
+  addLoadEvent(startTest);
+  ]]>
+  </script>
+</window>
--- a/dom/interfaces/core/nsIDOMNode.idl
+++ b/dom/interfaces/core/nsIDOMNode.idl
@@ -47,17 +47,17 @@ interface nsIDOMUserDataHandler;
  * The nsIDOMNode interface is the primary datatype for the entire 
  * Document Object Model.
  * It represents a single node in the document tree.
  *
  * For more information on this interface please see 
  * http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html
  */
 
-[scriptable, uuid(29a95243-c73e-454c-a996-272f6727b03c)]
+[scriptable, uuid(af9b19f7-7c88-4d16-9a3a-97390f824c58)]
 interface nsIDOMNode : nsISupports
 {
   const unsigned short      ELEMENT_NODE       = 1;
   const unsigned short      ATTRIBUTE_NODE     = 2;
   const unsigned short      TEXT_NODE          = 3;
   const unsigned short      CDATA_SECTION_NODE = 4;
   const unsigned short      ENTITY_REFERENCE_NODE = 5;
   const unsigned short      ENTITY_NODE        = 6;
@@ -141,9 +141,11 @@ interface nsIDOMNode : nsISupports
   // Introduced in DOM Level 3:
   boolean            isEqualNode(in nsIDOMNode arg);
   // Introduced in DOM Level 3:
   nsIVariant         setUserData(in DOMString key, 
                                  in nsIVariant data, 
                                  in nsIDOMUserDataHandler handler);
   // Introduced in DOM Level 3:
   nsIVariant         getUserData(in DOMString key);
+
+  boolean            contains(in nsIDOMNode aOther);
 };
--- a/js/src/xpconnect/src/dom_quickstubs.qsconf
+++ b/js/src/xpconnect/src/dom_quickstubs.qsconf
@@ -834,16 +834,22 @@ customMethodCalls = {
         'arg0Type': 'nsINode',
         'code': '    PRBool result = self->IsEqualTo(arg0);',
         'canFail': False
         },
     'nsIDOMNode_GetUserData': {
         'thisType': 'nsINode',
         'canFail': False
         },
+    'nsIDOMNode_Contains': {
+        'thisType': 'nsINode',
+        'arg0Type': 'nsINode',
+        'code': '    PRBool result = self->Contains(arg0);',
+        'canFail': False
+        },
     'nsIDOMNSHTMLElement_': {
         'thisType': 'nsGenericHTMLElement'
         },
     'nsIDOMHTMLElement_': {
         'thisType': 'nsGenericHTMLElement'
         },
     'nsIDOMElementCSSInlineStyle_GetStyle': {
         'thisType': 'nsStyledElement',