Bug 892203 - DOMConstructors for SandboxOptions. r=bholley
authorGabor Krizsanits <gkrizsanits@mozilla.com>
Wed, 04 Sep 2013 12:16:04 +0200
changeset 158344 365f2aca45cf51fc85184c09ed844744ee68ab46
parent 158343 f5743e7da43667013c15cb3796cffebd5c723fe0
child 158345 f186c97c90117a68f1dc91793e5a9e6e8f07b1a4
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs892203
milestone26.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 892203 - DOMConstructors for SandboxOptions. r=bholley
dom/tests/mochitest/chrome/test_sandbox_eventhandler.xul
js/xpconnect/src/Sandbox.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/tests/unit/test_allowedDomainsXHR.js
js/xpconnect/tests/unit/test_bug868675.js
js/xpconnect/tests/unit/test_bug872772.js
js/xpconnect/tests/unit/test_exportFunction.js
--- a/dom/tests/mochitest/chrome/test_sandbox_eventhandler.xul
+++ b/dom/tests/mochitest/chrome/test_sandbox_eventhandler.xul
@@ -15,17 +15,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript">
   <![CDATA[
 
   /** Test for Bug 817284 **/
   var cu = Components.utils;
-  var sb = cu.Sandbox("http://example.com", {wantXHRConstructor: true});
+  var sb = cu.Sandbox("http://example.com", { wantDOMConstructors: ["XMLHttpRequest"] });
 
   // Test event handler calls
   var xhr = cu.evalInSandbox(
       'var xhr = new XMLHttpRequest();\
        var called = false;\
        xhr.onload = function() { called = true; };\
        xhr', sb);
 
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -865,16 +865,51 @@ xpc::SandboxProxyHandler::keys(JSContext
 
 bool
 xpc::SandboxProxyHandler::iterate(JSContext *cx, JS::Handle<JSObject*> proxy,
                                   unsigned flags, JS::MutableHandle<Value> vp)
 {
     return BaseProxyHandler::iterate(cx, proxy, flags, vp);
 }
 
+bool
+xpc::SandboxOptions::DOMConstructors::Parse(JSContext* cx, JS::HandleObject obj)
+{
+    NS_ENSURE_TRUE(JS_IsArrayObject(cx, obj), false);
+
+    uint32_t length;
+    bool ok = JS_GetArrayLength(cx, obj, &length);
+    NS_ENSURE_TRUE(ok, false);
+    for (uint32_t i = 0; i < length; i++) {
+        RootedValue nameValue(cx);
+        ok = JS_GetElement(cx, obj, i, &nameValue);
+        NS_ENSURE_TRUE(ok, false);
+        NS_ENSURE_TRUE(nameValue.isString(), false);
+        char *name = JS_EncodeString(cx, nameValue.toString());
+        NS_ENSURE_TRUE(name, false);
+        if (!strcmp(name, "XMLHttpRequest")) {
+            XMLHttpRequest = true;
+        } else {
+            // Reporting error, if one of the DOM constructor names is unknown.
+            return false;
+        }
+    }
+    return true;
+}
+
+bool
+xpc::SandboxOptions::DOMConstructors::Define(JSContext* cx, JS::HandleObject obj)
+{
+    if (XMLHttpRequest &&
+        !JS_DefineFunction(cx, obj, "XMLHttpRequest", CreateXMLHttpRequest, 0, JSFUN_CONSTRUCTOR))
+        return false;
+
+    return true;
+}
+
 nsresult
 xpc::CreateSandboxObject(JSContext *cx, jsval *vp, nsISupports *prinOrSop, SandboxOptions& options)
 {
     // Create the sandbox global object
     nsresult rv;
     nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
     if (NS_FAILED(rv))
         return NS_ERROR_XPC_UNEXPECTED;
@@ -967,25 +1002,23 @@ xpc::CreateSandboxObject(JSContext *cx, 
             return NS_ERROR_XPC_UNEXPECTED;
 
         if (!XPCNativeWrapper::AttachNewConstructorObject(cx, sandbox))
             return NS_ERROR_XPC_UNEXPECTED;
 
         if (!JS_DefineFunctions(cx, sandbox, SandboxFunctions))
             return NS_ERROR_XPC_UNEXPECTED;
 
-        if (options.wantXHRConstructor &&
-            !JS_DefineFunction(cx, sandbox, "XMLHttpRequest", CreateXMLHttpRequest, 0, JSFUN_CONSTRUCTOR))
-            return NS_ERROR_XPC_UNEXPECTED;
-
         if (options.wantExportHelpers &&
             (!JS_DefineFunction(cx, sandbox, "exportFunction", ExportFunction, 3, 0) ||
              !JS_DefineFunction(cx, sandbox, "evalInWindow", EvalInWindow, 2, 0)))
             return NS_ERROR_XPC_UNEXPECTED;
 
+        if (!options.DOMConstructors.Define(cx, sandbox))
+            return NS_ERROR_XPC_UNEXPECTED;
     }
 
     if (vp) {
         // We have this crazy behavior where wantXrays=false also implies that the
         // returned sandbox is implicitly waived. We've stopped advertising it, but
         // keep supporting it for now.
         *vp = OBJECT_TO_JSVAL(sandbox);
         if (options.wantXrays && !JS_WrapValue(cx, vp))
@@ -1238,16 +1271,36 @@ GetStringPropFromOptions(JSContext *cx, 
 
     char *tmp = JS_EncodeString(cx, value.toString());
     NS_ENSURE_TRUE(tmp, NS_ERROR_INVALID_ARG);
     prop.Adopt(tmp, strlen(tmp));
     return NS_OK;
 }
 
 /*
+ * Helper that tries to get a list of DOM constructors from the options object.
+ */
+static nsresult
+GetDOMConstructorsFromOptions(JSContext *cx, HandleObject from, SandboxOptions& options)
+{
+    RootedValue value(cx);
+    bool found;
+    nsresult rv = GetPropFromOptions(cx, from, "wantDOMConstructors", &value, &found);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (!found)
+        return NS_OK;
+
+    NS_ENSURE_TRUE(value.isObject(), NS_ERROR_INVALID_ARG);
+    RootedObject ctors(cx, &value.toObject());
+    bool ok = options.DOMConstructors.Parse(cx, ctors);
+    NS_ENSURE_TRUE(ok, NS_ERROR_INVALID_ARG);
+    return NS_OK;
+}
+
+/*
  * Helper that parsing the sandbox options object (from) and sets the fields of the incoming options struct (options).
  */
 static nsresult
 ParseOptionsObject(JSContext *cx, jsval from, SandboxOptions &options)
 {
     NS_ENSURE_TRUE(from.isObject(), NS_ERROR_INVALID_ARG);
     RootedObject optionsObject(cx, &from.toObject());
     nsresult rv = GetObjPropFromOptions(cx, optionsObject,
@@ -1258,31 +1311,30 @@ ParseOptionsObject(JSContext *cx, jsval 
                                 "wantXrays", &options.wantXrays);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = GetBoolPropFromOptions(cx, optionsObject,
                                 "wantComponents", &options.wantComponents);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = GetBoolPropFromOptions(cx, optionsObject,
-                                "wantXHRConstructor", &options.wantXHRConstructor);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = GetBoolPropFromOptions(cx, optionsObject,
                                 "wantExportHelpers", &options.wantExportHelpers);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = GetStringPropFromOptions(cx, optionsObject,
                                   "sandboxName", options.sandboxName);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = GetObjPropFromOptions(cx, optionsObject,
                                "sameZoneAs", options.sameZoneAs.address());
     NS_ENSURE_SUCCESS(rv, rv);
 
+    rv = GetDOMConstructorsFromOptions(cx, optionsObject, options);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     return NS_OK;
 }
 
 static nsresult
 AssembleSandboxMemoryReporterName(JSContext *cx, nsCString &sandboxName)
 {
     // Use a default name when the caller did not provide a sandboxName.
     if (sandboxName.IsEmpty())
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -3694,32 +3694,38 @@ ThrowAndFail(nsresult errNum, JSContext 
 already_AddRefed<nsIXPCComponents_utils_Sandbox>
 NewSandboxConstructor();
 
 // Returns true if class of 'obj' is SandboxClass.
 bool
 IsSandbox(JSObject *obj);
 
 struct SandboxOptions {
+    struct DOMConstructors {
+        DOMConstructors() { mozilla::PodZero(this); }
+        bool Parse(JSContext* cx, JS::HandleObject obj);
+        bool Define(JSContext* cx, JS::HandleObject obj);
+        bool XMLHttpRequest;
+    };
+
     SandboxOptions(JSContext *cx)
         : wantXrays(true)
         , wantComponents(true)
-        , wantXHRConstructor(false)
         , wantExportHelpers(false)
         , proto(xpc_GetSafeJSContext())
         , sameZoneAs(xpc_GetSafeJSContext())
     { }
 
     bool wantXrays;
     bool wantComponents;
-    bool wantXHRConstructor;
     bool wantExportHelpers;
     JS::RootedObject proto;
     nsCString sandboxName;
     JS::RootedObject sameZoneAs;
+    DOMConstructors DOMConstructors;
 };
 
 JSObject *
 CreateGlobalObject(JSContext *cx, JSClass *clasp, nsIPrincipal *principal,
                    JS::CompartmentOptions& aOptions);
 
 // Helper for creating a sandbox object to use for evaluating
 // untrusted code completely separated from all other code in the
--- a/js/xpconnect/tests/unit/test_allowedDomainsXHR.js
+++ b/js/xpconnect/tests/unit/test_allowedDomainsXHR.js
@@ -5,17 +5,17 @@ cu.import("resource://testing-common/htt
 var httpserver = new HttpServer();
 var httpserver2 = new HttpServer();
 var testpath = "/simple";
 var negativetestpath = "/negative";
 var httpbody = "<?xml version='1.0' ?><root>0123456789</root>";
 
 var sb = cu.Sandbox(["http://www.example.com",
                      "http://localhost:4444/simple"],
-                     {wantXHRConstructor: true});
+                     { wantDOMConstructors: ["XMLHttpRequest"] });
 
 function createXHR(loc, async)
 {
   var xhr = new XMLHttpRequest();
   xhr.open("GET", "http://localhost:" + loc, async);
   return xhr;
 }
 
--- a/js/xpconnect/tests/unit/test_bug868675.js
+++ b/js/xpconnect/tests/unit/test_bug868675.js
@@ -6,17 +6,17 @@ function run_test() {
   try { result = XPCNativeWrapper.unwrap(2); } catch (e) {}
   do_check_eq(result, 2);
   result = "threw";
   try { result = XPCNativeWrapper(2); } catch (e) {}
   do_check_eq(result, 2);
 
   // Make sure that we can waive on a non-Xrayable object, and that we preserve
   // transitive waiving behavior.
-  var sb = new Cu.Sandbox('http://www.example.com', {wantXHRConstructor: true});
+  var sb = new Cu.Sandbox('http://www.example.com', { wantDOMConstructors: ["XMLHttpRequest"] });
   Cu.evalInSandbox('this.xhr = new XMLHttpRequest();', sb);
   Cu.evalInSandbox('this.jsobj = {mynative: xhr};', sb);
   do_check_true(!Cu.isXrayWrapper(XPCNativeWrapper.unwrap(sb.xhr)));
   do_check_true(Cu.isXrayWrapper(sb.jsobj.mynative));
   do_check_true(!Cu.isXrayWrapper(XPCNativeWrapper.unwrap(sb.jsobj).mynative));
 
   // Test the new Cu API.
   var waived = Cu.waiveXrays(sb.xhr);
--- a/js/xpconnect/tests/unit/test_bug872772.js
+++ b/js/xpconnect/tests/unit/test_bug872772.js
@@ -1,16 +1,16 @@
 const Cu = Components.utils;
 function run_test() {
 
   // Make a content sandbox with an Xrayable object.
   // NB: We use an nsEP here so that we can have access to Components, but still
   // have Xray behavior from this scope.
   var contentSB = new Cu.Sandbox(['http://www.google.com'],
-                                 {wantXHRConstructor: true, wantComponents: true});
+                                 { wantDOMConstructors: ["XMLHttpRequest"], wantComponents: true });
 
   // Make an XHR in the content sandbox.
   Cu.evalInSandbox('xhr = new XMLHttpRequest();', contentSB);
 
   // Make sure that waivers can be set as Xray expandos.
   var xhr = contentSB.xhr;
   do_check_true(Cu.isXrayWrapper(xhr));
   xhr.unwaivedExpando = xhr;
--- a/js/xpconnect/tests/unit/test_exportFunction.js
+++ b/js/xpconnect/tests/unit/test_exportFunction.js
@@ -1,13 +1,13 @@
 function run_test() {
   var Cu = Components.utils;
   var epsb = new Cu.Sandbox(["http://example.com", "http://example.org"], { wantExportHelpers: true });
-  subsb = new Cu.Sandbox("http://example.com", { wantXHRConstructor: true });
-  subsb2 = new Cu.Sandbox("http://example.com", { wantXHRConstructor: true });
+  subsb = new Cu.Sandbox("http://example.com", { wantDOMConstructors: ["XMLHttpRequest"] });
+  subsb2 = new Cu.Sandbox("http://example.com", { wantDOMConstructors: ["XMLHttpRequest"] });
   xorigsb = new Cu.Sandbox("http://test.com");
 
   epsb.subsb = subsb;
   epsb.xorigsb = xorigsb;
   epsb.do_check_true = do_check_true;
   epsb.do_check_eq = do_check_eq;
   epsb.do_check_neq = do_check_neq;