Bug 855971 - Switch HTMLDocument to WebIDL bindings. r=bz.
☠☠ backed out by 2b63238518a3 ☠ ☠
authorPeter Van der Beken <peterv@propagandism.org>
Tue, 11 Dec 2012 21:45:36 -0500
changeset 141806 a2245b038bcc305c8cdb048e8c3199e0ee6d047b
parent 141805 2a999f8ee317323c22e7ebdd81a065f1de508387
child 141807 e6d7033aeb748c3246eb9966f2365f562d01639d
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs855971
milestone23.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 855971 - Switch HTMLDocument to WebIDL bindings. r=bz.
browser/devtools/debugger/test/browser_dbg_propertyview-11.js
browser/devtools/debugger/test/browser_dbg_propertyview-filter-02.js
browser/devtools/webconsole/test/browser_webconsole_bug_659907_console_dir.js
content/base/test/test_bug588990.html
content/html/document/src/ImageDocument.cpp
content/html/document/src/MediaDocument.cpp
content/html/document/src/MediaDocument.h
content/html/document/src/nsHTMLDocument.cpp
content/html/document/src/nsHTMLDocument.h
dom/bindings/Bindings.conf
dom/bindings/DOMJSProxyHandler.cpp
js/xpconnect/src/nsDOMQS.h
js/xpconnect/tests/mochitest/test_bug462428.html
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-11.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-11.js
@@ -59,17 +59,17 @@ function testFrameParameters()
         "Should have the right property name for |buttonAsProto|.");
 
       is(anonymousNodes[2].querySelector(".value").getAttribute("value"), "[object Object]",
         "Should have the right property value for |buttonAsProto|.");
 
       is(globalNodes[3].querySelector(".name").getAttribute("value"), "document",
         "Should have the right property name for |document|.");
 
-      is(globalNodes[3].querySelector(".value").getAttribute("value"), "[object HTMLDocument]",
+      is(globalNodes[3].querySelector(".value").getAttribute("value"), "[object Proxy]",
         "Should have the right property value for |document|.");
 
       let buttonNode = gVars.getItemForNode(anonymousNodes[1]);
       let buttonAsProtoNode = gVars.getItemForNode(anonymousNodes[2]);
       let documentNode = gVars.getItemForNode(globalNodes[3]);
 
       is(buttonNode.expanded, false,
         "The buttonNode should not be expanded at this point.");
@@ -185,28 +185,28 @@ function testFrameParameters()
             "'type' in buttonProtoNode should have the right value.");
           is(buttonProtoNode.get("formMethod").target.querySelector(".name")
              .getAttribute("value"), "formMethod",
             "Should have the right property name for 'formMethod' in buttonProtoNode.");
           is(buttonProtoNode.get("formMethod").target.querySelector(".value")
              .getAttribute("value"), '""',
             "'formMethod' in buttonProtoNode should have the right value.");
 
-          is(documentProtoNode.get("baseURI").target.querySelector(".name")
-             .getAttribute("value"), "baseURI",
-            "Should have the right property name for 'baseURI' in documentProtoNode.");
-          is(documentProtoNode.get("baseURI").target.querySelector(".value")
-             .getAttribute("value"), '"' + TAB_URL + '"',
-            "'baseURI' in documentProtoNode should have the right value.");
-          is(documentProtoNode.get("URL").target.querySelector(".name")
-             .getAttribute("value"), "URL",
-            "Should have the right property name for 'URL' in documentProtoNode.");
-          is(documentProtoNode.get("URL").target.querySelector(".value")
-             .getAttribute("value"), '"' + TAB_URL + '"',
-            "'URL' in documentProtoNode should have the right value.");
+          is(documentProtoNode.get("domain").target.querySelector(".name")
+             .getAttribute("value"), "domain",
+            "Should have the right property name for 'domain' in documentProtoNode.");
+          is(documentProtoNode.get("domain").target.querySelector(".value")
+             .getAttribute("value"), '"example.com"',
+            "'domain' in documentProtoNode should have the right value.");
+          is(documentProtoNode.get("cookie").target.querySelector(".name")
+             .getAttribute("value"), "cookie",
+            "Should have the right property name for 'cookie' in documentProtoNode.");
+          is(documentProtoNode.get("cookie").target.querySelector(".value")
+             .getAttribute("value"), '""',
+            "'cookie' in documentProtoNode should have the right value.");
 
           let buttonAsProtoProtoProtoNode = buttonAsProtoProtoNode.get("__proto__");
 
           is(buttonAsProtoProtoProtoNode.expanded, false,
             "The buttonAsProtoProtoProtoNode should not be expanded at this point.");
 
           // Expand the prototype of the prototype of 'buttonAsProto' tree
           // node. This causes its properties to be retrieved and displayed.
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-filter-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-filter-02.js
@@ -84,52 +84,48 @@ function testVariablesFiltering()
     is(innerScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 1,
       "There should be 1 variable displayed in the inner scope");
     is(mathScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
       "There should be 0 variables displayed in the math scope");
     is(testScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
       "There should be 0 variables displayed in the test scope");
     is(loadScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
       "There should be 0 variables displayed in the load scope");
-    is(globalScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 2,
-      "There should be 2 variables displayed in the global scope");
+    is(globalScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 1,
+      "There should be 1 variables displayed in the global scope");
 
-    is(innerScope.querySelectorAll(".variables-view-property:not([non-match])").length, 8,
-      "There should be 8 properties displayed in the inner scope");
+    is(innerScope.querySelectorAll(".variables-view-property:not([non-match])").length, 7,
+      "There should be 7 properties displayed in the inner scope");
     is(mathScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
       "There should be 0 properties displayed in the math scope");
     is(testScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
       "There should be 0 properties displayed in the test scope");
     is(loadScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
       "There should be 0 properties displayed in the load scope");
     is(globalScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
       "There should be 0 properties displayed in the global scope");
 
     is(innerScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
       "this", "The only inner variable displayed should be 'this'");
     is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[0].getAttribute("value"),
-      "document", "The first inner property displayed should be 'document'");
+      "window", "The first inner property displayed should be 'window'");
     is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[1].getAttribute("value"),
-      "window", "The second inner property displayed should be 'window'");
+      "document", "The second inner property displayed should be 'document'");
     is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[2].getAttribute("value"),
-      "document", "The third inner property displayed should be 'document'");
+      "location", "The third inner property displayed should be 'location'");
     is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[3].getAttribute("value"),
-      "location", "The fourth inner property displayed should be 'location'");
+      "__proto__", "The fourth inner property displayed should be '__proto__'");
     is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[4].getAttribute("value"),
       "__proto__", "The fifth inner property displayed should be '__proto__'");
     is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[5].getAttribute("value"),
-      "__proto__", "The sixth inner property displayed should be '__proto__'");
+      "HTMLDocument", "The sixth inner property displayed should be 'HTMLDocument'");
     is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[6].getAttribute("value"),
       "HTMLDocument", "The seventh inner property displayed should be 'HTMLDocument'");
-    is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[7].getAttribute("value"),
-      "HTMLDocument", "The eight inner property displayed should be 'HTMLDocument'");
 
     is(globalScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
-      "document", "The first global variable displayed should be 'document'");
-    is(globalScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[1].getAttribute("value"),
       "HTMLDocument", "The first global variable displayed should be 'HTMLDocument'");
   }
 
   function test2()
   {
     innerScopeItem.collapse();
     mathScopeItem.collapse();
     testScopeItem.collapse();
@@ -178,52 +174,48 @@ function testVariablesFiltering()
     is(innerScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 1,
       "There should be 1 variable displayed in the inner scope");
     is(mathScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
       "There should be 0 variables displayed in the math scope");
     is(testScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
       "There should be 0 variables displayed in the test scope");
     is(loadScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 0,
       "There should be 0 variables displayed in the load scope");
-    is(globalScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 2,
-      "There should be 2 variables displayed in the global scope");
+    is(globalScope.querySelectorAll(".variables-view-variable:not([non-match])").length, 1,
+      "There should be 1 variables displayed in the global scope");
 
-    is(innerScope.querySelectorAll(".variables-view-property:not([non-match])").length, 8,
-      "There should be 8 properties displayed in the inner scope");
+    is(innerScope.querySelectorAll(".variables-view-property:not([non-match])").length, 7,
+      "There should be 7 properties displayed in the inner scope");
     is(mathScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
       "There should be 0 properties displayed in the math scope");
     is(testScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
       "There should be 0 properties displayed in the test scope");
     is(loadScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
       "There should be 0 properties displayed in the load scope");
     is(globalScope.querySelectorAll(".variables-view-property:not([non-match])").length, 0,
       "There should be 0 properties displayed in the global scope");
 
     is(innerScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
       "this", "The only inner variable displayed should be 'this'");
     is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[0].getAttribute("value"),
-      "document", "The first inner property displayed should be 'document'");
+      "window", "The first inner property displayed should be 'window'");
     is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[1].getAttribute("value"),
-      "window", "The second inner property displayed should be 'window'");
+      "document", "The second inner property displayed should be 'document'");
     is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[2].getAttribute("value"),
-      "document", "The third inner property displayed should be 'document'");
+      "location", "The third inner property displayed should be 'location'");
     is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[3].getAttribute("value"),
-      "location", "The fourth inner property displayed should be 'location'");
+      "__proto__", "The fourth inner property displayed should be '__proto__'");
     is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[4].getAttribute("value"),
       "__proto__", "The fifth inner property displayed should be '__proto__'");
     is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[5].getAttribute("value"),
-      "__proto__", "The sixth inner property displayed should be '__proto__'");
+      "HTMLDocument", "The sixth inner property displayed should be 'HTMLDocument'");
     is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[6].getAttribute("value"),
       "HTMLDocument", "The seventh inner property displayed should be 'HTMLDocument'");
-    is(innerScope.querySelectorAll(".variables-view-property:not([non-match]) > .title > .name")[7].getAttribute("value"),
-      "HTMLDocument", "The eight inner property displayed should be 'HTMLDocument'");
 
     is(globalScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[0].getAttribute("value"),
-      "document", "The first global variable displayed should be 'document'");
-    is(globalScope.querySelectorAll(".variables-view-variable:not([non-match]) > .title > .name")[1].getAttribute("value"),
       "HTMLDocument", "The first global variable displayed should be 'HTMLDocument'");
   }
 
   var scopes = gDebugger.DebuggerView.Variables._list,
       innerScope = scopes.querySelectorAll(".variables-view-scope")[0],
       mathScope = scopes.querySelectorAll(".variables-view-scope")[1],
       testScope = scopes.querySelectorAll(".variables-view-scope")[2],
       loadScope = scopes.querySelectorAll(".variables-view-scope")[3],
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_659907_console_dir.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_659907_console_dir.js
@@ -17,13 +17,13 @@ function test() {
 
 function consoleOpened(hud) {
   hud.jsterm.execute("console.dir(document)");
   hud.jsterm.once("variablesview-fetched", testConsoleDir.bind(null, hud));
 }
 
 function testConsoleDir(hud, ev, view) {
   findVariableViewProperties(view, [
-    { name: "__proto__.querySelectorAll", value: "[object Function]" },
+    { name: "__proto__.__proto__.querySelectorAll", value: "[object Function]" },
     { name: "location", value: "[object Location]" },
     { name: "__proto__.write", value: "[object Function]" },
   ], { webconsole: hud }).then(finishTest);
 }
--- a/content/base/test/test_bug588990.html
+++ b/content/base/test/test_bug588990.html
@@ -64,19 +64,19 @@ function checkHasNoName(removed, test) {
   var attrValue = removed ? null : "";
   is(i1_1.getAttribute("name"), attrValue, "i1_1 getAttribute " + test);
   is(i2_1.getAttribute("name"), attrValue, "i2_1 getAttribute " + test);
   is(i2_2.getAttribute("name"), attrValue, "i2_2 getAttribute " + test);
   is(i3_1.getAttribute("name"), attrValue, "i3_1 getAttribute " + test);
   is(i3_2.getAttribute("name"), attrValue, "i3_2 getAttribute " + test);
   is(i3_3.getAttribute("name"), attrValue, "i3_3 getAttribute " + test);
   
-  todo_is(document.n1,  undefined, "doc.n1 " + test);
-  todo_is(document.n2,  undefined, "doc.n2 " + test);
-  todo_is(document.n3,  undefined, "doc.n3 " + test);
+  is(document.n1,  undefined, "doc.n1 " + test);
+  is(document.n2,  undefined, "doc.n2 " + test);
+  is(document.n3,  undefined, "doc.n3 " + test);
 }
 
 // Check that dynamic modifications of attribute work
 
 checkHasName("in markup");
 
 i1_1.name = "";
 i2_1.name = "";
@@ -321,16 +321,16 @@ i3_1.addEventListener("DOMAttrModified",
   is(e.target, i3_1, "target is i3_1");
   is(i3_1.name, "n3", "i3_1 no longer has name");
   is(i3_1.getAttribute("name"), "n3", "i3_1 has empty name attr");
   i3_1.removeAttribute("name");
   mutateFired = true;
 }, false);
 i3_1.name = "n3";
 ok(mutateFired, "mutation event fired");
-todo_is(document.n3, undefined, "named was readded during mutation");
+is(document.n3, undefined, "named was readded during mutation");
 removeNode(i3_1);
 SpecialPowers.gc();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/html/document/src/ImageDocument.cpp
+++ b/content/html/document/src/ImageDocument.cpp
@@ -212,17 +212,18 @@ ImageListener::OnStartRequest(nsIRequest
   imageLoader->AddObserver(imgDoc);
   imgDoc->mObservingImageLoader = true;
   imageLoader->LoadImageWithChannel(channel, getter_AddRefs(mNextStream));
 
   return MediaDocumentStreamListener::OnStartRequest(request, ctxt);
 }
 
 ImageDocument::ImageDocument()
-  : mOriginalZoomLevel(1.0)
+  : MediaDocument(true),
+    mOriginalZoomLevel(1.0)
 {
   // NOTE! nsDocument::operator new() zeroes out all members, so don't
   // bother initializing members to 0.
 }
 
 ImageDocument::~ImageDocument()
 {
 }
--- a/content/html/document/src/MediaDocument.cpp
+++ b/content/html/document/src/MediaDocument.cpp
@@ -92,18 +92,19 @@ MediaDocumentStreamListener::OnDataAvail
 const char* const MediaDocument::sFormatNames[4] = 
 {
   "MediaTitleWithNoInfo",    // eWithNoInfo
   "MediaTitleWithFile",      // eWithFile
   "",                        // eWithDim
   ""                         // eWithDimAndFile
 };
 
-MediaDocument::MediaDocument()
-    : mDocumentElementInserted(false)
+MediaDocument::MediaDocument(bool aUseXPConnectToWrap)
+    : nsHTMLDocument(aUseXPConnectToWrap),
+      mDocumentElementInserted(false)
 {
 }
 MediaDocument::~MediaDocument()
 {
 }
 
 nsresult
 MediaDocument::Init()
--- a/content/html/document/src/MediaDocument.h
+++ b/content/html/document/src/MediaDocument.h
@@ -14,17 +14,17 @@
 #define NSMEDIADOCUMENT_PROPERTIES_URI "chrome://global/locale/layout/MediaDocument.properties"
 
 namespace mozilla {
 namespace dom {
 
 class MediaDocument : public nsHTMLDocument
 {
 public:
-  MediaDocument();
+  MediaDocument(bool aUseXPConnectToWrap = false);
   virtual ~MediaDocument();
 
   virtual nsresult Init();
 
   virtual nsresult StartDocumentLoad(const char*         aCommand,
                                      nsIChannel*         aChannel,
                                      nsILoadGroup*       aLoadGroup,
                                      nsISupports*        aContainer,
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -97,17 +97,19 @@
 #include "mozilla/dom/Element.h"
 #include "mozilla/Preferences.h"
 #include "nsMimeTypes.h"
 #include "nsIRequest.h"
 #include "nsHtml5TreeOpExecutor.h"
 #include "nsHtml5Parser.h"
 #include "nsIDOMJSWindow.h"
 #include "nsSandboxFlags.h"
+#include "nsIImageDocument.h"
 #include "mozilla/dom/HTMLBodyElement.h"
+#include "mozilla/dom/HTMLDocumentBinding.h"
 #include "nsCharsetSource.h"
 #include "nsIStringBundle.h"
 #include "nsDOMClassInfo.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #define NS_MAX_DOCUMENT_WRITE_DEPTH 20
@@ -190,25 +192,29 @@ NS_NewHTMLDocument(nsIDocument** aInstan
   doc->SetLoadedAsData(aLoadedAsData);
 
   return rv;
 }
 
   // NOTE! nsDocument::operator new() zeroes out all members, so don't
   // bother initializing members to 0.
 
-nsHTMLDocument::nsHTMLDocument()
+nsHTMLDocument::nsHTMLDocument(bool aUseXPConnectToWrap)
   : nsDocument("text/html")
 {
   // NOTE! nsDocument::operator new() zeroes out all members, so don't
   // bother initializing members to 0.
 
   mIsRegularHTML = true;
   mDefaultElementType = kNameSpaceID_XHTML;
   mCompatMode = eCompatibility_NavQuirks;
+
+  if (!aUseXPConnectToWrap) {
+    SetIsDOMBinding();
+  }
 }
 
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLDocument, nsDocument)
   NS_ASSERTION(!nsCCUncollectableMarker::InGeneration(cb, tmp->GetMarkedCCGeneration()),
                "Shouldn't traverse nsHTMLDocument!");
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImages)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplets)
@@ -246,16 +252,31 @@ NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION
   NS_DOCUMENT_INTERFACE_TABLE_BEGIN(nsHTMLDocument)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLDocument, nsIHTMLDocument)
     NS_INTERFACE_TABLE_ENTRY(nsHTMLDocument, nsIDOMHTMLDocument)
   NS_OFFSET_AND_INTERFACE_TABLE_END
   NS_OFFSET_AND_INTERFACE_TABLE_TO_MAP_SEGUE
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(HTMLDocument)
 NS_INTERFACE_MAP_END_INHERITING(nsDocument)
 
+JSObject*
+nsHTMLDocument::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+#ifdef DEBUG
+  // Don't do it yet for image documents
+  nsCOMPtr<nsIImageDocument> imgDoc = do_QueryObject(this);
+  MOZ_ASSERT(!imgDoc, "Who called SetIsDOMBinding()?");
+#endif
+
+  JS::Rooted<JSObject*> obj(aCx, HTMLDocumentBinding::Wrap(aCx, aScope, this));
+  if (obj && !PostCreateWrapper(aCx, obj)) {
+    return nullptr;
+  }
+  return obj;
+}
 
 nsresult
 nsHTMLDocument::Init()
 {
   nsresult rv = nsDocument::Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Now reset the compatibility mode of the CSSLoader
@@ -1655,24 +1676,23 @@ nsHTMLDocument::Open(JSContext* cx,
 #endif
 
     // Now make sure we're not flagged as the initial document anymore, now
     // that we've had stuff done to us.  From now on, if anyone tries to
     // document.open() us, they get a new inner window.
     SetIsInitialDocument(false);
 
     nsCOMPtr<nsIScriptGlobalObject> newScope(do_QueryReferent(mScopeObject));
-    if (oldScope && newScope != oldScope) {
-      nsIXPConnect *xpc = nsContentUtils::XPConnect();
-      rv = xpc->ReparentWrappedNativeIfFound(cx, oldScope->GetGlobalJSObject(),
-                                             newScope->GetGlobalJSObject(),
-                                             static_cast<nsINode*>(this));
+    JS::RootedObject wrapper(cx, GetWrapper());
+    if (oldScope && newScope != oldScope && wrapper) {
+      rv = mozilla::dom::ReparentWrapper(cx, wrapper);
       if (rv.Failed()) {
         return nullptr;
       }
+      nsIXPConnect *xpc = nsContentUtils::XPConnect();
       rv = xpc->RescueOrphansInScope(cx, oldScope->GetGlobalJSObject());
       if (rv.Failed()) {
         return nullptr;
       }
     }
   }
 
   // Call Reset(), this will now do the full reset
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -37,17 +37,17 @@ class nsICachingChannel;
 class nsHTMLDocument : public nsDocument,
                        public nsIHTMLDocument,
                        public nsIDOMHTMLDocument
 {
 public:
   using nsDocument::SetDocumentURI;
   using nsDocument::GetPlugins;
 
-  nsHTMLDocument();
+  nsHTMLDocument(bool aUseXPConnectToWrap = false);
   virtual nsresult Init();
 
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
 
   NS_IMETHOD_(nsrefcnt) AddRef(void);
   NS_IMETHOD_(nsrefcnt) Release(void);
 
   virtual void Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup);
@@ -175,16 +175,18 @@ public:
   virtual nsXPCClassInfo* GetClassInfo();
 
   virtual void DocSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const;
   // DocSizeOfIncludingThis is inherited from nsIDocument.
 
   virtual bool WillIgnoreCharsetOverride();
 
   // WebIDL API
+  virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aScope)
+    MOZ_OVERRIDE;
   void GetDomain(nsAString& aDomain, mozilla::ErrorResult& rv);
   void SetDomain(const nsAString& aDomain, mozilla::ErrorResult& rv);
   void GetCookie(nsAString& aCookie, mozilla::ErrorResult& rv);
   void SetCookie(const nsAString& aCookie, mozilla::ErrorResult& rv);
   JSObject* NamedGetter(JSContext* cx, const nsAString& aName, bool& aFound,
                         mozilla::ErrorResult& rv);
   void GetSupportedNames(nsTArray<nsString>& aNames);
   nsGenericHTMLElement *GetBody();
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -410,17 +410,16 @@ DOMInterfaces = {
 },
 
 'HTMLDListElement': {
     'nativeType' : 'mozilla::dom::HTMLSharedListElement'
 },
 
 'HTMLDocument': {
     'nativeType': 'nsHTMLDocument',
-    'register': False,
     'hasXPConnectImpls': True,
     'resultNotAddRefed': [ 'body', 'head', 'images', 'embeds', 'plugins',
                            'links', 'forms', 'scripts', 'anchors', 'applets' ],
     'implicitJSContext': [ 'open', 'write', 'writeln' ]
 },
 
 'HTMLElement': {
     'nativeType': 'nsGenericHTMLElement',
--- a/dom/bindings/DOMJSProxyHandler.cpp
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -78,26 +78,28 @@ DOMProxyHandler::GetAndClearExpandoObjec
   MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
   JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
   if (v.isUndefined()) {
     return nullptr;
   }
 
   if (v.isObject()) {
     js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, UndefinedValue());
-    return &v.toObject();
+  } else {
+    js::ExpandoAndGeneration* expandoAndGeneration =
+      static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
+    v = expandoAndGeneration->expando;
+    if (v.isUndefined()) {
+      return nullptr;
+    }
+    expandoAndGeneration->expando = UndefinedValue();
   }
 
-  js::ExpandoAndGeneration* expandoAndGeneration =
-    static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
-  v = expandoAndGeneration->expando;
-  if (v.isUndefined()) {
-    return nullptr;
-  }
-  expandoAndGeneration->expando = UndefinedValue();
+  xpc::GetObjectScope(obj)->RemoveDOMExpandoObject(obj);
+
   return &v.toObject();
 }
 
 // static
 JSObject*
 DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JS::Handle<JSObject*> obj)
 {
   NS_ASSERTION(IsDOMProxy(obj), "expected a DOM proxy object");
--- a/js/xpconnect/src/nsDOMQS.h
+++ b/js/xpconnect/src/nsDOMQS.h
@@ -20,16 +20,17 @@
 #include "nsDOMMouseEvent.h"
 #include "nsDOMUIEvent.h"
 #include "mozilla/dom/EventTargetBinding.h"
 #include "mozilla/dom/NodeBinding.h"
 #include "mozilla/dom/ElementBinding.h"
 #include "mozilla/dom/HTMLElementBinding.h"
 #include "mozilla/dom/DocumentBinding.h"
 #include "mozilla/dom/SVGElementBinding.h"
+#include "mozilla/dom/HTMLDocumentBinding.h"
 
 template<class T>
 struct ProtoIDAndDepth
 {
     enum {
         PrototypeID = mozilla::dom::prototypes::id::_ID_Count,
         Depth = -1
     };
@@ -47,16 +48,17 @@ struct ProtoIDAndDepth<_native>         
 }
 
 NEW_BINDING(mozilla::dom::EventTarget, EventTarget);
 NEW_BINDING(nsINode, Node);
 NEW_BINDING(mozilla::dom::Element, Element);
 NEW_BINDING(nsGenericHTMLElement, HTMLElement);
 NEW_BINDING(nsIDocument, Document);
 NEW_BINDING(nsDocument, Document);
+NEW_BINDING(nsHTMLDocument, HTMLDocument);
 NEW_BINDING(nsSVGElement, SVGElement);
 NEW_BINDING(nsDOMEvent, Event);
 NEW_BINDING(nsDOMMouseEvent, MouseEvent);
 NEW_BINDING(nsDOMUIEvent, UIEvent);
 
 #define DEFINE_UNWRAP_CAST(_interface, _base, _bit)                           \
 template <>                                                                   \
 MOZ_ALWAYS_INLINE JSBool                                                      \
--- a/js/xpconnect/tests/mochitest/test_bug462428.html
+++ b/js/xpconnect/tests/mochitest/test_bug462428.html
@@ -28,17 +28,17 @@ for (var i in document) {
         sawProp = true;
     }
 }
 
 ok(sawProp, "property should be enumerable");
 
 is(getter.call(document), document.documentElement, "the getter actually works");
 
-Object.getPrototypeOf(document).__defineSetter__('documentElement', function() {});
+Document.prototype.__defineSetter__('documentElement', function() {});
 is(getter.call(document), document.documentElement, "the getter works after defineSetter");
 
 var oldTitle = document.title;
 try {
     var setter = document.__lookupSetter__('title');
     setter.call(document, "title 1");
     is(document.title, "title 1", "the setter is bound correctly");
 } finally {