Bug 683852 - Implement Node.contains(node), r=bz
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Fri, 02 Sep 2011 23:15:53 +0300
changeset 77493 3dc687a271e777b8cc1eae8152b98b9228383906
parent 77492 e1d9d6120f84423acdf19cdc87d9f6c9cf204c85
child 77500 9ca928d8095c07ac3ba0a66baeb9799dfd15215c
push id340
push userclegnitto@mozilla.com
push dateTue, 08 Nov 2011 22:56:33 +0000
treeherdermozilla-beta@f745dc151615 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs683852
milestone9.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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',