Bug 1216150 - Split xpc::InitGlobalObject into an options-setting component and a global-object-modifying component, with the options-setting component being called before global object creation in all callers. r=bz
authorJeff Walden <jwalden@mit.edu>
Mon, 01 Feb 2016 12:48:03 -0800
changeset 282612 1c95b0d32d5a800b599c1da747d82d7fa71dad22
parent 282611 bf8737df4163fd0a59cd972cab20022a11f91301
child 282613 70f112b2cd34b6b67b95337abfd8582be30caeb4
push id17362
push usercbook@mozilla.com
push dateTue, 02 Feb 2016 10:54:53 +0000
treeherderfx-team@e5f1b4782e38 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs1216150
milestone47.0a1
Bug 1216150 - Split xpc::InitGlobalObject into an options-setting component and a global-object-modifying component, with the options-setting component being called before global object creation in all callers. r=bz
dom/base/nsGlobalWindow.cpp
dom/bindings/BindingUtils.h
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2335,29 +2335,32 @@ CreateNativeGlobalForInner(JSContext* aC
   // no one creates one accidentally.
   nsCOMPtr<nsIExpandedPrincipal> nsEP = do_QueryInterface(aPrincipal);
   MOZ_RELEASE_ASSERT(!nsEP, "DOMWindow with nsEP is not supported");
 
   nsGlobalWindow *top = nullptr;
   if (aNewInner->GetOuterWindow()) {
     top = aNewInner->GetTopInternal();
   }
+
   JS::CompartmentOptions options;
 
   // Sometimes add-ons load their own XUL windows, either as separate top-level
   // windows or inside a browser element. In such cases we want to tag the
   // window's compartment with the add-on ID. See bug 1092156.
   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
     options.creationOptions().setAddonId(MapURIToAddonID(aURI));
   }
 
   if (top && top->GetGlobalJSObject()) {
     options.creationOptions().setSameZoneAs(top->GetGlobalJSObject());
   }
 
+  xpc::InitGlobalObjectOptions(options, aPrincipal);
+
   // Determine if we need the Components object.
   bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) ||
                         TreatAsRemoteXUL(aPrincipal);
   uint32_t flags = needComponents ? 0 : nsIXPConnect::OMIT_COMPONENTS_OBJECT;
   flags |= nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK;
 
   if (!WindowBinding::Wrap(aCx, aNewInner, aNewInner, options,
                            nsJSPrincipals::get(aPrincipal), false, aGlobal) ||
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -3018,16 +3018,21 @@ struct CreateGlobalOptions<nsGlobalWindo
   static bool PostCreateGlobal(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
 };
 
 nsresult
 RegisterDOMNames();
 
 // The return value is whatever the ProtoHandleGetter we used
 // returned.  This should be the DOM prototype for the global.
+//
+// Typically this method's caller will want to ensure that
+// xpc::InitGlobalObjectOptions is called before, and xpc::InitGlobalObject is
+// called after, this method, to ensure that this global object and its
+// compartment are consistent with other global objects.
 template <class T, ProtoHandleGetter GetProto>
 JS::Handle<JSObject*>
 CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
              const JSClass* aClass, JS::CompartmentOptions& aOptions,
              JSPrincipals* aPrincipal, bool aInitStandardClasses,
              JS::MutableHandle<JSObject*> aGlobal)
 {
   aOptions.creationOptions().setTrace(CreateGlobalOptions<T>::TraceGlobal);
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -384,51 +384,62 @@ CreateGlobalObject(JSContext* cx, const 
                                     strcmp(className, "ChromeWindow") == 0)
                                    ? ProtoAndIfaceCache::WindowLike
                                    : ProtoAndIfaceCache::NonWindowLike);
     }
 
     return global;
 }
 
+void
+InitGlobalObjectOptions(JS::CompartmentOptions& aOptions,
+                        nsIPrincipal* aPrincipal)
+{
+    bool shouldDiscardSystemSource = ShouldDiscardSystemSource();
+    bool extraWarningsForSystemJS = ExtraWarningsForSystemJS();
+
+    bool isSystem = false;
+    if (shouldDiscardSystemSource || extraWarningsForSystemJS)
+        isSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
+
+    if (shouldDiscardSystemSource) {
+        bool discardSource = isSystem;
+        if (!discardSource) {
+            short status = aPrincipal->GetAppStatus();
+            discardSource = status == nsIPrincipal::APP_STATUS_PRIVILEGED ||
+                            status == nsIPrincipal::APP_STATUS_CERTIFIED;
+        }
+
+        aOptions.behaviors().setDiscardSource(discardSource);
+    }
+
+    if (extraWarningsForSystemJS) {
+        if (isSystem)
+            aOptions.behaviors().extraWarningsOverride().set(true);
+    }
+}
+
 bool
 InitGlobalObject(JSContext* aJSContext, JS::Handle<JSObject*> aGlobal, uint32_t aFlags)
 {
-    // Immediately enter the global's compartment, so that everything else we
-    // create ends up there.
+    // Immediately enter the global's compartment so that everything we create
+    // ends up there.
     JSAutoCompartment ac(aJSContext, aGlobal);
+
+    // Stuff coming through this path always ends up as a DOM global.
+    MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL);
+
     if (!(aFlags & nsIXPConnect::OMIT_COMPONENTS_OBJECT)) {
         // XPCCallContext gives us an active request needed to save/restore.
         if (!CompartmentPrivate::Get(aGlobal)->scope->AttachComponentsObject(aJSContext) ||
             !XPCNativeWrapper::AttachNewConstructorObject(aJSContext, aGlobal)) {
             return UnexpectedFailure(false);
         }
     }
 
-    if (ShouldDiscardSystemSource()) {
-        nsIPrincipal* prin = GetObjectPrincipal(aGlobal);
-        bool isSystem = nsContentUtils::IsSystemPrincipal(prin);
-        if (!isSystem) {
-            short status = prin->GetAppStatus();
-            isSystem = status == nsIPrincipal::APP_STATUS_PRIVILEGED ||
-                       status == nsIPrincipal::APP_STATUS_CERTIFIED;
-        }
-        JS::CompartmentBehaviorsRef(aGlobal).setDiscardSource(isSystem);
-    }
-
-    if (ExtraWarningsForSystemJS()) {
-        nsIPrincipal* prin = GetObjectPrincipal(aGlobal);
-        bool isSystem = nsContentUtils::IsSystemPrincipal(prin);
-        if (isSystem)
-            JS::CompartmentBehaviorsRef(aGlobal).extraWarningsOverride().set(true);
-    }
-
-    // Stuff coming through this path always ends up as a DOM global.
-    MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL);
-
     if (!(aFlags & nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK))
         JS_FireOnNewGlobalObject(aJSContext, aGlobal);
 
     return true;
 }
 
 } // namespace xpc
 
@@ -443,16 +454,18 @@ nsXPConnect::InitClassesWithNewWrappedGl
     MOZ_ASSERT(aJSContext, "bad param");
     MOZ_ASSERT(aCOMObj, "bad param");
     MOZ_ASSERT(_retval, "bad param");
 
     // We pass null for the 'extra' pointer during global object creation, so
     // we need to have a principal.
     MOZ_ASSERT(aPrincipal);
 
+    InitGlobalObjectOptions(aOptions, aPrincipal);
+
     // Call into XPCWrappedNative to make a new global object, scope, and global
     // prototype.
     xpcObjectHelper helper(aCOMObj);
     MOZ_ASSERT(helper.GetScriptableFlags() & nsIXPCScriptable::IS_GLOBAL_OBJECT);
     RefPtr<XPCWrappedNative> wrappedGlobal;
     nsresult rv =
         XPCWrappedNative::WrapNewGlobal(helper, aPrincipal,
                                         aFlags & nsIXPConnect::INIT_JS_STANDARD_CLASSES,
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -3586,18 +3586,33 @@ public:
     // If true, the resulting object is deep-frozen after being cloned.
     bool deepFreeze;
 };
 
 JSObject*
 CreateGlobalObject(JSContext* cx, const JSClass* clasp, nsIPrincipal* principal,
                    JS::CompartmentOptions& aOptions);
 
-// InitGlobalObject enters the compartment of aGlobal, so it doesn't matter what
-// compartment aJSContext is in.
+// Modify the provided compartment options, consistent with |aPrincipal| and
+// with globally-cached values of various preferences.
+//
+// Call this function *before* |aOptions| is used to create the corresponding
+// global object, as not all of the options it sets can be modified on an
+// existing global object.  (The type system should make this obvious, because
+// you can't get a *mutable* JS::CompartmentOptions& from an existing global
+// object.)
+void
+InitGlobalObjectOptions(JS::CompartmentOptions& aOptions,
+                        nsIPrincipal* aPrincipal);
+
+// Finish initializing an already-created, not-yet-exposed-to-script global
+// object.  This will attach a Components object (if necessary) and call
+// |JS_FireOnNewGlobalObject| (if necessary).
+//
+// If you must modify compartment options, see InitGlobalObjectOptions above.
 bool
 InitGlobalObject(JSContext* aJSContext, JS::Handle<JSObject*> aGlobal,
                  uint32_t aFlags);
 
 // Helper for creating a sandbox object to use for evaluating
 // untrusted code completely separated from all other code in the
 // system using EvalInSandbox(). Takes the JSContext on which to
 // do setup etc on, puts the sandbox object in *vp (which must be