Bug 925293 - Refactoring SandboxOptions parsing. r=bholley
authorGabor Krizsanits <gkrizsanits@mozilla.com>
Wed, 16 Oct 2013 15:18:43 +0200
changeset 164759 e2c492172295ca050b275c2370a97f13b6320337
parent 164758 9027ca2649fcf15315d8cdec0694bbe3976ad39c
child 164760 89600e65912368d6bf02974029b7bec611e8ee3b
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs925293
milestone27.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 925293 - Refactoring SandboxOptions parsing. r=bholley
js/xpconnect/src/Sandbox.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/XPCWrappedNativeScope.cpp
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -1115,283 +1115,272 @@ nsXPCComponents_utils_Sandbox::Construct
     RootedObject obj(cx, objArg);
     return CallOrConstruct(wrapper, cx, obj, args, _retval);
 }
 
 /*
  * For sandbox constructor the first argument can be a URI string in which case
  * we use the related Codebase Principal for the sandbox.
  */
-static nsresult
-GetPrincipalFromString(JSContext *cx, HandleString codebase, nsIPrincipal **principal)
+bool
+ParsePrincipal(JSContext *cx, HandleString codebase, nsIPrincipal **principal)
 {
     MOZ_ASSERT(principal);
     MOZ_ASSERT(codebase);
     nsCOMPtr<nsIURI> uri;
     nsDependentJSString codebaseStr;
-    NS_ENSURE_TRUE(codebaseStr.init(cx, codebase), NS_ERROR_FAILURE);
+    NS_ENSURE_TRUE(codebaseStr.init(cx, codebase), false);
     nsresult rv = NS_NewURI(getter_AddRefs(uri), codebaseStr);
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_FAILED(rv)) {
+        JS_ReportError(cx, "Creating URI from string failed");
+        return false;
+    }
 
     nsCOMPtr<nsIScriptSecurityManager> secman =
         do_GetService(kScriptSecurityManagerContractID);
-    NS_ENSURE_TRUE(secman, NS_ERROR_FAILURE);
+    NS_ENSURE_TRUE(secman, false);
 
     // We could allow passing in the app-id and browser-element info to the
     // sandbox constructor. But creating a sandbox based on a string is a
     // deprecated API so no need to add features to it.
     rv = secman->GetNoAppCodebasePrincipal(uri, principal);
-    NS_ENSURE_SUCCESS(rv, rv);
-    NS_ENSURE_TRUE(*principal, NS_ERROR_FAILURE);
-
-    return NS_OK;
+    if (NS_FAILED(rv) || !*principal) {
+        JS_ReportError(cx, "Creating Principal from URI failed");
+        return false;
+    }
+    return true;
 }
 
 /*
  * For sandbox constructor the first argument can be a principal object or
  * a script object principal (Document, Window).
  */
-static nsresult
+static bool
 GetPrincipalOrSOP(JSContext *cx, HandleObject from, nsISupports **out)
 {
     MOZ_ASSERT(out);
     *out = nullptr;
 
     nsXPConnect* xpc = nsXPConnect::XPConnect();
     nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
     xpc->GetWrappedNativeOfJSObject(cx, from,
                                     getter_AddRefs(wrapper));
 
-    NS_ENSURE_TRUE(wrapper, NS_ERROR_INVALID_ARG);
+    NS_ENSURE_TRUE(wrapper, false);
 
     if (nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryWrappedNative(wrapper)) {
         sop.forget(out);
-        return NS_OK;
+        return true;
     }
 
     nsCOMPtr<nsIPrincipal> principal = do_QueryWrappedNative(wrapper);
     principal.forget(out);
-    NS_ENSURE_TRUE(*out, NS_ERROR_INVALID_ARG);
+    NS_ENSURE_TRUE(*out, false);
 
-    return NS_OK;
+    return true;
 }
 
 /*
  * The first parameter of the sandbox constructor might be an array of principals, either in string
  * format or actual objects (see GetPrincipalOrSOP)
  */
-static nsresult
+static bool
 GetExpandedPrincipal(JSContext *cx, HandleObject arrayObj, nsIExpandedPrincipal **out)
 {
     MOZ_ASSERT(out);
     uint32_t length;
 
     if (!JS_IsArrayObject(cx, arrayObj) ||
         !JS_GetArrayLength(cx, arrayObj, &length) ||
         !length)
     {
         // We need a whitelist of principals or uri strings to create an
         // expanded principal, if we got an empty array or something else
         // report error.
-        return NS_ERROR_INVALID_ARG;
+        JS_ReportError(cx, "Expected an array of URI strings");
+        return false;
     }
 
     nsTArray< nsCOMPtr<nsIPrincipal> > allowedDomains(length);
     allowedDomains.SetLength(length);
     nsIScriptSecurityManager *ssm = XPCWrapper::GetSecurityManager();
-    NS_ENSURE_TRUE(ssm, NS_ERROR_XPC_UNEXPECTED);
+    NS_ENSURE_TRUE(ssm, false);
 
     for (uint32_t i = 0; i < length; ++i) {
         RootedValue allowed(cx);
         if (!JS_GetElement(cx, arrayObj, i, &allowed))
-            return NS_ERROR_INVALID_ARG;
+            return false;
 
         nsresult rv;
         nsCOMPtr<nsIPrincipal> principal;
         if (allowed.isString()) {
             // In case of string let's try to fetch a codebase principal from it.
             RootedString str(cx, allowed.toString());
-            rv = GetPrincipalFromString(cx, str, getter_AddRefs(principal));
-            NS_ENSURE_SUCCESS(rv, rv);
+            if (!ParsePrincipal(cx, str, getter_AddRefs(principal)))
+                return false;
+
         } else if (allowed.isObject()) {
             // In case of object let's see if it's a Principal or a ScriptObjectPrincipal.
             nsCOMPtr<nsISupports> prinOrSop;
             RootedObject obj(cx, &allowed.toObject());
-            rv = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop));
-            NS_ENSURE_SUCCESS(rv, rv);
+            if (!GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)))
+                return false;
 
             nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(prinOrSop));
             principal = do_QueryInterface(prinOrSop);
-            if (sop) {
+            if (sop)
                 principal = sop->GetPrincipal();
-            }
         }
-        NS_ENSURE_TRUE(principal, NS_ERROR_INVALID_ARG);
+        NS_ENSURE_TRUE(principal, false);
 
         // We do not allow ExpandedPrincipals to contain any system principals.
         bool isSystem;
         rv = ssm->IsSystemPrincipal(principal, &isSystem);
-        NS_ENSURE_SUCCESS(rv, rv);
-        NS_ENSURE_FALSE(isSystem, NS_ERROR_INVALID_ARG);
+        NS_ENSURE_SUCCESS(rv, false);
+        if (isSystem) {
+            JS_ReportError(cx, "System principal is not allowed in an expanded principal");
+            return false;
+        }
         allowedDomains[i] = principal;
   }
 
   nsCOMPtr<nsIExpandedPrincipal> result = new nsExpandedPrincipal(allowedDomains);
   result.forget(out);
-  return NS_OK;
+  return true;
 }
 
 /*
  * Helper that tries to get a property form the options object.
  */
-static nsresult
-GetPropFromOptions(JSContext *cx, HandleObject from, const char *name, MutableHandleValue prop,
-                   bool *found)
+bool
+OptionsBase::ParseValue(const char *name, MutableHandleValue prop, bool *found)
 {
     MOZ_ASSERT(found);
-    bool ok = JS_HasProperty(cx, from, name, found);
-    NS_ENSURE_TRUE(ok, NS_ERROR_INVALID_ARG);
+    bool ok = JS_HasProperty(mCx, mObject, name, found);
+    NS_ENSURE_TRUE(ok, false);
 
     if (!*found)
-        return NS_OK;
+        return true;
 
-    ok = JS_GetProperty(cx, from, name, prop);
-    NS_ENSURE_TRUE(ok, NS_ERROR_INVALID_ARG);
-    return NS_OK;
+    return JS_GetProperty(mCx, mObject, name, prop);
 }
 
 /*
  * Helper that tries to get a boolean property form the options object.
  */
-static nsresult
-GetBoolPropFromOptions(JSContext *cx, HandleObject from, const char *name, bool *prop)
+bool
+OptionsBase::ParseBoolean(const char *name, bool *prop)
 {
     MOZ_ASSERT(prop);
-
-    RootedValue value(cx);
+    RootedValue value(mCx);
     bool found;
-    nsresult rv = GetPropFromOptions(cx, from, name, &value, &found);
-    NS_ENSURE_SUCCESS(rv, rv);
+    bool ok = ParseValue(name, &value, &found);
+    NS_ENSURE_TRUE(ok, false);
 
     if (!found)
-        return NS_OK;
+        return true;
 
-    NS_ENSURE_TRUE(value.isBoolean(), NS_ERROR_INVALID_ARG);
+    if (!value.isBoolean()) {
+        JS_ReportError(mCx, "Expected a boolean value for property %s", name);
+        return false;
+    }
 
     *prop = value.toBoolean();
-    return NS_OK;
+    return true;
 }
 
 /*
  * Helper that tries to get an object property form the options object.
  */
-static nsresult
-GetObjPropFromOptions(JSContext *cx, HandleObject from, const char *name, JSObject **prop)
+bool
+OptionsBase::ParseObject(const char *name, MutableHandleObject prop)
 {
-    MOZ_ASSERT(prop);
-
-    RootedValue value(cx);
+    RootedValue value(mCx);
     bool found;
-    nsresult rv = GetPropFromOptions(cx, from, name, &value, &found);
-    NS_ENSURE_SUCCESS(rv, rv);
+    bool ok = ParseValue(name, &value, &found);
+    NS_ENSURE_TRUE(ok, false);
+
+    if (!found)
+        return true;
 
-    if (!found) {
-        *prop = nullptr;
-        return NS_OK;
+    if (!value.isObject()) {
+        JS_ReportError(mCx, "Expected an object value for property %s", name);
+        return false;
     }
-
-    NS_ENSURE_TRUE(value.isObject(), NS_ERROR_INVALID_ARG);
-    *prop = &value.toObject();
-    return NS_OK;
+    prop.set(&value.toObject());
+    return true;
 }
 
 /*
  * Helper that tries to get a string property form the options object.
  */
-static nsresult
-GetStringPropFromOptions(JSContext *cx, HandleObject from, const char *name, nsCString &prop)
+bool
+OptionsBase::ParseString(const char *name, nsCString &prop)
 {
-    RootedValue value(cx);
+    RootedValue value(mCx);
     bool found;
-    nsresult rv = GetPropFromOptions(cx, from, name, &value, &found);
-    NS_ENSURE_SUCCESS(rv, rv);
+    bool ok = ParseValue(name, &value, &found);
+    NS_ENSURE_TRUE(ok, false);
 
     if (!found)
-        return NS_OK;
-
-    NS_ENSURE_TRUE(value.isString(), NS_ERROR_INVALID_ARG);
+        return true;
 
-    char *tmp = JS_EncodeString(cx, value.toString());
-    NS_ENSURE_TRUE(tmp, NS_ERROR_INVALID_ARG);
+    if (!value.isString()) {
+        JS_ReportError(mCx, "Expected a string value for property %s", name);
+        return false;
+    }
+
+    char *tmp = JS_EncodeString(mCx, value.toString());
+    NS_ENSURE_TRUE(tmp, false);
     prop.Adopt(tmp, strlen(tmp));
-    return NS_OK;
+    return true;
 }
 
 /*
  * Helper that tries to get a list of DOM constructors and other helpers from the options object.
  */
-static nsresult
-GetGlobalPropertiesFromOptions(JSContext *cx, HandleObject from, SandboxOptions& options)
+bool
+SandboxOptions::ParseGlobalProperties()
 {
-    RootedValue value(cx);
+    RootedValue value(mCx);
     bool found;
-    nsresult rv = GetPropFromOptions(cx, from, "wantGlobalProperties", &value, &found);
-    NS_ENSURE_SUCCESS(rv, rv);
+    bool ok = ParseValue("wantGlobalProperties", &value, &found);
+    NS_ENSURE_TRUE(ok, false);
     if (!found)
-        return NS_OK;
+        return true;
+
+    if (!value.isObject()) {
+        JS_ReportError(mCx, "Expected an array value for wantGlobalProperties");
+        return false;
+    }
 
-    NS_ENSURE_TRUE(value.isObject(), NS_ERROR_INVALID_ARG);
-    RootedObject ctors(cx, &value.toObject());
-    NS_ENSURE_TRUE(JS_IsArrayObject(cx, ctors), NS_ERROR_INVALID_ARG);
-    bool ok = options.globalProperties.Parse(cx, ctors);
-    NS_ENSURE_TRUE(ok, NS_ERROR_INVALID_ARG);
-    return NS_OK;
+    RootedObject ctors(mCx, &value.toObject());
+    if (!JS_IsArrayObject(mCx, ctors)) {
+        JS_ReportError(mCx, "Expected an array value for wantGlobalProperties");
+        return false;
+    }
+
+    return globalProperties.Parse(mCx, ctors);
 }
 
 /*
  * 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)
+bool
+SandboxOptions::Parse()
 {
-    NS_ENSURE_TRUE(from.isObject(), NS_ERROR_INVALID_ARG);
-    RootedObject optionsObject(cx, &from.toObject());
-    nsresult rv = GetObjPropFromOptions(cx, optionsObject,
-                                        "sandboxPrototype", options.proto.address());
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = GetBoolPropFromOptions(cx, optionsObject,
-                                "wantXrays", &options.wantXrays);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = GetBoolPropFromOptions(cx, optionsObject,
-                                "wantComponents", &options.wantComponents);
-    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 = GetGlobalPropertiesFromOptions(cx, optionsObject, options);
-    NS_ENSURE_SUCCESS(rv, rv);
-
     bool found;
-    rv = GetPropFromOptions(cx, optionsObject,
-                            "metadata", &options.metadata, &found);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    return NS_OK;
+    return ParseObject("sandboxPrototype", &proto) &&
+           ParseBoolean("wantXrays", &wantXrays) &&
+           ParseBoolean("wantComponents", &wantComponents) &&
+           ParseBoolean("wantExportHelpers", &wantExportHelpers) &&
+           ParseString("sandboxName", sandboxName) &&
+           ParseObject("sameZoneAs", &sameZoneAs) &&
+           ParseGlobalProperties() &&
+           ParseValue("metadata", &metadata, &found);
 }
 
 static nsresult
 AssembleSandboxMemoryReporterName(JSContext *cx, nsCString &sandboxName)
 {
     // Use a default name when the caller did not provide a sandboxName.
     if (sandboxName.IsEmpty())
         sandboxName = NS_LITERAL_CSTRING("[anonymous sandbox]");
@@ -1428,59 +1417,61 @@ nsresult
 nsXPCComponents_utils_Sandbox::CallOrConstruct(nsIXPConnectWrappedNative *wrapper,
                                                JSContext *cx, HandleObject obj,
                                                const CallArgs &args, bool *_retval)
 {
     if (args.length() < 1)
         return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval);
 
     nsresult rv;
+    bool ok = false;
 
     // Make sure to set up principals on the sandbox before initing classes.
     nsCOMPtr<nsIPrincipal> principal;
     nsCOMPtr<nsIExpandedPrincipal> expanded;
     nsCOMPtr<nsISupports> prinOrSop;
 
     if (args[0].isString()) {
         RootedString str(cx, args[0].toString());
-        rv = GetPrincipalFromString(cx, str, getter_AddRefs(principal));
+        ok = ParsePrincipal(cx, str, getter_AddRefs(principal));
         prinOrSop = principal;
     } else if (args[0].isObject()) {
         RootedObject obj(cx, &args[0].toObject());
         if (JS_IsArrayObject(cx, obj)) {
-            rv = GetExpandedPrincipal(cx, obj, getter_AddRefs(expanded));
+            ok = GetExpandedPrincipal(cx, obj, getter_AddRefs(expanded));
             prinOrSop = expanded;
         } else {
-            rv = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop));
+            ok = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop));
         }
-    } else {
-        return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
     }
 
-    if (NS_FAILED(rv))
-        return ThrowAndFail(rv, cx, _retval);
+    if (!ok)
+        return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
 
-    SandboxOptions options(cx);
+    bool calledWithOptions = args.length() > 1;
+    if (calledWithOptions && !args[1].isObject())
+        return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
 
-    if (args.length() > 1 && args[1].isObject()) {
-        if (NS_FAILED(ParseOptionsObject(cx, args[1], options)))
-            return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
-    }
+    RootedObject optionsObject(cx, calledWithOptions ? &args[1].toObject()
+                                                     : nullptr);
+
+    SandboxOptions options(cx, optionsObject);
+    if (calledWithOptions && !options.Parse())
+        return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
 
     if (NS_FAILED(AssembleSandboxMemoryReporterName(cx, options.sandboxName)))
         return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
 
     rv = CreateSandboxObject(cx, args.rval().address(), prinOrSop, options);
 
     if (NS_FAILED(rv))
         return ThrowAndFail(rv, cx, _retval);
 
     *_retval = true;
-
-    return rv;
+    return NS_OK;
 }
 
 class ContextHolder : public nsIScriptObjectPrincipal
 {
 public:
     ContextHolder(JSContext *aOuterCx, HandleObject aSandbox, nsIPrincipal *aPrincipal);
     virtual ~ContextHolder();
 
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -3320,17 +3320,17 @@ XPCJSRuntime::RemoveContextCallback(xpcC
     }
 }
 
 JSObject *
 XPCJSRuntime::GetJunkScope()
 {
     if (!mJunkScope) {
         AutoSafeJSContext cx;
-        SandboxOptions options(cx);
+        SandboxOptions options;
         options.sandboxName.AssignASCII("XPConnect Junk Compartment");
         RootedValue v(cx);
         nsresult rv = CreateSandboxObject(cx, v.address(),
                                           nsContentUtils::GetSystemPrincipal(),
                                           options);
 
         NS_ENSURE_SUCCESS(rv, nullptr);
 
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -240,17 +240,17 @@ XPCWrappedNativeScope::EnsureXBLScope(JS
     // Set up the sandbox options. Note that we use the DOM global as the
     // sandboxPrototype so that the XBL scope can access all the DOM objects
     // it's accustomed to accessing.
     //
     // NB: One would think that wantXrays wouldn't make a difference here.
     // However, wantXrays lives a secret double life, and one of its other
     // hobbies is to waive Xray on the returned sandbox when set to false.
     // So make sure to keep this set to true, here.
-    SandboxOptions options(cx);
+    SandboxOptions options;
     options.wantXrays = true;
     options.wantComponents = true;
     options.proto = global;
     options.sameZoneAs = global;
 
     // Use an nsExpandedPrincipal to create asymmetric security.
     nsIPrincipal *principal = GetPrincipal();
     nsCOMPtr<nsIExpandedPrincipal> ep;
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -908,17 +908,17 @@ nsXPConnect::ClearAllWrappedNativeSecuri
 NS_IMETHODIMP
 nsXPConnect::CreateSandbox(JSContext *cx, nsIPrincipal *principal,
                            nsIXPConnectJSObjectHolder **_retval)
 {
     *_retval = nullptr;
 
     RootedValue rval(cx, JSVAL_VOID);
 
-    SandboxOptions options(cx);
+    SandboxOptions options;
     nsresult rv = CreateSandboxObject(cx, rval.address(), principal, options);
     MOZ_ASSERT(NS_FAILED(rv) || !JSVAL_IS_PRIMITIVE(rval),
                "Bad return value from xpc_CreateSandboxObject()!");
 
     if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(rval)) {
         *_retval = XPCJSObjectHolder::newHolder(JSVAL_TO_OBJECT(rval));
         NS_ENSURE_TRUE(*_retval, NS_ERROR_OUT_OF_MEMORY);
 
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -3602,34 +3602,62 @@ struct GlobalProperties {
 // Infallible.
 already_AddRefed<nsIXPCComponents_utils_Sandbox>
 NewSandboxConstructor();
 
 // Returns true if class of 'obj' is SandboxClass.
 bool
 IsSandbox(JSObject *obj);
 
-struct SandboxOptions {
-    SandboxOptions(JSContext *cx)
-        : wantXrays(true)
+class MOZ_STACK_CLASS OptionsBase {
+public:
+    OptionsBase(JSContext *cx = xpc_GetSafeJSContext(),
+                JS::HandleObject options = JS::NullPtr())
+        : mCx(cx)
+        , mObject(cx, options)
+    { }
+
+    virtual bool Parse() = 0;
+
+protected:
+    bool ParseValue(const char *name, JS::MutableHandleValue prop, bool *found);
+    bool ParseBoolean(const char *name, bool *prop);
+    bool ParseObject(const char *name, JS::MutableHandleObject prop);
+    bool ParseString(const char *name, nsCString &prop);
+
+    JSContext *mCx;
+    JS::RootedObject mObject;
+};
+
+class MOZ_STACK_CLASS SandboxOptions : public OptionsBase {
+public:
+    SandboxOptions(JSContext *cx = xpc_GetSafeJSContext(),
+                   JS::HandleObject options = JS::NullPtr())
+        : OptionsBase(cx, options)
+        , wantXrays(true)
         , wantComponents(true)
         , wantExportHelpers(false)
-        , proto(xpc_GetSafeJSContext())
-        , sameZoneAs(xpc_GetSafeJSContext())
-        , metadata(xpc_GetSafeJSContext())
+        , proto(cx)
+        , sameZoneAs(cx)
+        , metadata(cx)
     { }
 
+    virtual bool Parse();
+
     bool wantXrays;
     bool wantComponents;
     bool wantExportHelpers;
     JS::RootedObject proto;
     nsCString sandboxName;
     JS::RootedObject sameZoneAs;
     GlobalProperties globalProperties;
     JS::RootedValue metadata;
+
+protected:
+    bool ParseGlobalProperties();
 };
 
 JSObject *
 CreateGlobalObject(JSContext *cx, const 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