Bug 820170 - Wrapping nodes into documents compartment. r=bholley, r=enn
authorGabor Krizsanits <gkrizsanits@mozilla.com>
Thu, 11 Apr 2013 11:59:42 +0200
changeset 135200 8ce65f4eb1bafe33d2e0359f9409c53d28ddb807
parent 135199 e1336dc1f555274a531476e25c6316f97280e7a3
child 135201 1c28a906a213614c7bae75c7c31f0b9e7bccc0f5
push id3752
push userlsblakk@mozilla.com
push dateMon, 13 May 2013 17:21:10 +0000
treeherdermozilla-aurora@1580544aef0b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley, enn
bugs820170
milestone23.0a1
Bug 820170 - Wrapping nodes into documents compartment. r=bholley, r=enn
content/base/src/nsDocument.cpp
content/xul/document/src/XULDocument.cpp
dom/base/nsDOMClassInfo.cpp
js/xpconnect/tests/unit/test_allowedDomainsXHR.js
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1844,16 +1844,23 @@ nsDocument::Init()
     return NS_ERROR_ALREADY_INITIALIZED;
   }
 
   mIdentifierMap.Init();
   mStyledLinks.Init();
   mRadioGroups.Init();
   mCustomPrototypes.Init();
 
+  // If after creation the owner js global is not set for a document
+  // we use the default compartment for this document, instead of creating
+  // wrapper in some random compartment when the document is exposed to js
+  // via some events.
+  mScopeObject = do_GetWeakReference(xpc::GetNativeForGlobal(xpc::GetJunkScope()));
+  MOZ_ASSERT(mScopeObject);
+
   // Force initialization.
   nsINode::nsSlots* slots = Slots();
 
   // Prepend self as mutation-observer whether we need it or not (some
   // subclasses currently do, other don't). This is because the code in
   // nsNodeUtils always notifies the first observer first, expecting the
   // first observer to be the document.
   NS_ENSURE_TRUE(slots->mMutationObservers.PrependElementUnlessExists(static_cast<nsIMutationObserver*>(this)),
--- a/content/xul/document/src/XULDocument.cpp
+++ b/content/xul/document/src/XULDocument.cpp
@@ -1515,16 +1515,35 @@ XULDocument::GetHeight(int32_t* aHeight)
 int32_t
 XULDocument::GetHeight(ErrorResult& aRv)
 {
     int32_t height;
     aRv = GetHeight(&height);
     return height;
 }
 
+nsIGlobalObject*
+GetScopeObjectOfNode(nsIDOMNode* node)
+{
+    // Window root occasionally keeps alive a node of a document whose
+    // window is already dead. If in this brief period someone calls
+    // GetPopupNode and we return that node, nsNodeSH::PreCreate will throw,
+    // because it will not know which scope this node belongs to. Returning
+    // an orphan node like that to JS would be a bug anyway, so to avoid
+    // this, let's do the same check as nsNodeSH::PreCreate does to
+    // determine the scope and if it fails let's just return null in
+    // XULDocument::GetPopupNode.
+    nsIDocument* doc = nullptr;
+    for (nsCOMPtr<nsINode> inode = do_QueryInterface(node);
+         !doc && inode; inode = inode->GetParent()) {
+        doc = inode->OwnerDoc();
+    }
+    return doc ? doc->GetScopeObject() : nullptr;
+}
+
 //----------------------------------------------------------------------
 //
 // nsIDOMXULDocument interface
 //
 
 NS_IMETHODIMP
 XULDocument::GetPopupNode(nsIDOMNode** aNode)
 {
@@ -1537,18 +1556,20 @@ XULDocument::GetPopupNode(nsIDOMNode** a
 
     if (!node) {
         nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
         if (pm) {
             node = pm->GetLastTriggerPopupNode(this);
         }
     }
 
-    if (node && nsContentUtils::CanCallerAccess(node))
-      node.swap(*aNode);
+    if (node && nsContentUtils::CanCallerAccess(node)
+        && GetScopeObjectOfNode(node)) {
+        node.swap(*aNode);
+    }
 
     return NS_OK;
 }
 
 already_AddRefed<nsINode>
 XULDocument::GetPopupNode()
 {
     nsCOMPtr<nsIDOMNode> node;
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -5716,28 +5716,16 @@ nsNodeSH::PreCreate(nsISupports *nativeO
   // Make sure that we get the owner document of the content node, in case
   // we're in document teardown.  If we are, it's important to *not* use
   // globalObj as the nodes parent since that would give the node the
   // principal of globalObj (i.e. the principal of the document that's being
   // loaded) and not the principal of the document that's being unloaded.
   // See http://bugzilla.mozilla.org/show_bug.cgi?id=227417
   nsIDocument* doc = node->OwnerDoc();
 
-  // If we have a document, make sure one of these is true
-  // (1) it has a script handling object,
-  // (2) has had one, or has been marked to have had one,
-  // (3) we are running a privileged script.
-  // Event handling is possible only if (1). If (2) event handling is prevented.
-  // If document has never had a script handling object,
-  // untrusted scripts (3) shouldn't touch it!
-  bool hasHadScriptHandlingObject = false;
-  NS_ENSURE_STATE(doc->GetScriptHandlingObject(hasHadScriptHandlingObject) ||
-                  hasHadScriptHandlingObject ||
-                  nsContentUtils::IsCallerChrome());
-
   nsINode *native_parent;
 
   bool nodeIsElement = node->IsElement();
   if (nodeIsElement && node->AsElement()->IsXUL()) {
     // For XUL elements, use the parent, if any.
     native_parent = node->GetParent();
 
     if (!native_parent) {
@@ -5776,36 +5764,24 @@ nsNodeSH::PreCreate(nsISupports *nativeO
           if (form) {
             native_parent = form;
           }
         }
       }
     }
   } else {
     // We're called for a document object; set the parent to be the
-    // document's global object, if there is one
-
-    // Get the scope object from the document.
-    nsISupports *scope = doc->GetScopeObject();
-
-    if (scope) {
-        jsval v;
-        nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
-        nsresult rv = WrapNative(cx, globalObj, scope, false, &v,
-                                 getter_AddRefs(holder));
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        holder->GetJSObject(parentObj);
-    }
-    else {
-      // No global object reachable from this document, use the
-      // global object that was passed to this method.
-
-      *parentObj = globalObj;
-    }
+    // document's global object
+
+    // Document should know its global but if the owner window of the
+    // document is already dead at this point, then just throw.
+    nsIGlobalObject* scope = doc->GetScopeObject();
+    NS_ENSURE_TRUE(scope, NS_ERROR_UNEXPECTED);
+
+    *parentObj = scope->GetGlobalJSObject();
 
     // No slim wrappers for a document's scope object.
     return node->ChromeOnlyAccess() ?
       NS_SUCCESS_CHROME_ACCESS_ONLY : NS_OK;
   }
 
   // XXXjst: Maybe we need to find the global to use from the
   // nsIScriptGlobalObject that's reachable from the node we're about
--- a/js/xpconnect/tests/unit/test_allowedDomainsXHR.js
+++ b/js/xpconnect/tests/unit/test_allowedDomainsXHR.js
@@ -27,46 +27,67 @@ function checkResults(xhr)
   do_check_eq(xhr.status, 200);
   do_check_eq(xhr.responseText, httpbody);
 
   var root_node = xhr.responseXML.getElementsByTagName('root').item(0);
   do_check_eq(root_node.firstChild.data, "0123456789");
   return true;
 }
 
+var httpServersClosed = 0;
+function finishIfDone()
+{
+  if (++httpServersClosed == 2)
+    do_test_finished();
+}
+
 function run_test()
 {
+  do_test_pending();
+
   httpserver.registerPathHandler(testpath, serverHandler);
   httpserver.start(4444);
 
   httpserver2.registerPathHandler(negativetestpath, serverHandler);
   httpserver2.start(4445);
 
   // Test sync XHR sending
   cu.evalInSandbox('var createXHR = ' + createXHR.toString(), sb);
   var res = cu.evalInSandbox('var sync = createXHR("4444/simple"); sync.send(null); sync', sb);
   checkResults(res);
 
-  // Test async XHR sending
-  var async = cu.evalInSandbox('var async = createXHR("4444/simple", true); async', sb);
-  async.addEventListener("readystatechange", function(event) {
-    if (checkResults(async))
-      httpserver.stop(do_test_finished);
-  }, false);
-  async.send(null);
-
   // negative test sync XHR sending (to ensure that the xhr do not have chrome caps, see bug 779821)
   try {
     cu.evalInSandbox('var createXHR = ' + createXHR.toString(), sb);
     var res = cu.evalInSandbox('var sync = createXHR("4445/negative"); sync.send(null); sync', sb);
     do_check_false(true, "XHR created from sandbox should not have chrome caps");
   } catch (e) {
     do_check_true(true);
   }
+
+  httpserver2.stop(finishIfDone);
+
+  // Test async XHR sending
+  sb.finish = function(){
+    httpserver.stop(finishIfDone);
+  }
+
+  sb.checkResults = checkResults;
   
-  do_test_pending();
+  sb.do_check_eq = do_check_eq;
+
+  function changeListener(event) {
+    if (checkResults(async))
+      finish();
+  }
+
+  var async = cu.evalInSandbox('var async = createXHR("4444/simple", true);' +
+                               'async.addEventListener("readystatechange", ' +
+                                                       changeListener.toString() + ', false);' +
+                               'async', sb);
+  async.send(null);
 }
 
 function serverHandler(metadata, response)
 {
   response.setHeader("Content-Type", "text/xml", false);
   response.bodyOutputStream.write(httpbody, httpbody.length);
 }