Bug 1363200 - JSAPI for realms: Move mIsContentXBLScope to the CompartmentPrivate. r=mrbkap
authorJason Orendorff <jorendorff@mozilla.com>
Wed, 12 Jul 2017 15:00:47 -0500
changeset 380064 0cc8ef854b497929161250f9aa067228abe85e63
parent 380063 8d394e8d01d4df4c9a5b5a6db1d63864930cd409
child 380065 0495d0052c24fcc9fb7f3d6d96b06314e8fd3234
push id94819
push userjorendorff@mozilla.com
push dateMon, 11 Sep 2017 17:51:21 +0000
treeherdermozilla-inbound@0495d0052c24 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs1363200
milestone57.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 1363200 - JSAPI for realms: Move mIsContentXBLScope to the CompartmentPrivate. r=mrbkap In the new order, it will be a compartment-level bit rather than a realm-level bit, so it does not belong on the Scope.
caps/nsScriptSecurityManager.cpp
dom/base/nsContentUtils.cpp
dom/bindings/BindingUtils.cpp
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
js/xpconnect/src/xpcpublic.h
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -1296,17 +1296,17 @@ nsScriptSecurityManager::CanCreateWrappe
 
     if (nsContentUtils::IsCallerChrome())
     {
         return NS_OK;
     }
 
     // We want to expose nsIDOMXULCommandDispatcher and nsITreeSelection implementations
     // in XBL scopes.
-    if (xpc::IsContentXBLScope(contextCompartment)) {
+    if (xpc::IsContentXBLCompartment(contextCompartment)) {
       nsCOMPtr<nsIDOMXULCommandDispatcher> dispatcher = do_QueryInterface(aObj);
       if (dispatcher) {
         return NS_OK;
       }
 
       nsCOMPtr<nsITreeSelection> treeSelection = do_QueryInterface(aObj);
       if (treeSelection) {
         return NS_OK;
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -2518,17 +2518,17 @@ nsContentUtils::IsCallerContentXBL()
 
     // For remote XUL, we run XBL in the XUL scope. Given that we care about
     // compat and not security for remote XUL, just always claim to be XBL.
     if (!xpc::AllowContentXBLScope(c)) {
       MOZ_ASSERT(nsContentUtils::AllowXULXBLForPrincipal(xpc::GetCompartmentPrincipal(c)));
       return true;
     }
 
-    return xpc::IsContentXBLScope(c);
+    return xpc::IsContentXBLCompartment(c);
 }
 
 bool
 nsContentUtils::IsSystemCaller(JSContext* aCx)
 {
   // Note that SubjectPrincipal() assumes we are in a compartment here.
   return SubjectPrincipal(aCx) == sSystemPrincipal;
 }
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1611,17 +1611,17 @@ static void
 DEBUG_CheckXBLCallable(JSContext *cx, JSObject *obj)
 {
     // In general, we shouldn't have cross-compartment wrappers here, because
     // we should be running in an XBL scope, and the content prototype should
     // contain wrappers to functions defined in the XBL scope. But if the node
     // has been adopted into another compartment, those prototypes will now point
     // to a different XBL scope (which is ok).
     MOZ_ASSERT_IF(js::IsCrossCompartmentWrapper(obj),
-                  xpc::IsContentXBLScope(js::GetObjectCompartment(js::UncheckedUnwrap(obj))));
+                  xpc::IsInContentXBLScope(js::UncheckedUnwrap(obj)));
     MOZ_ASSERT(JS::IsCallable(obj));
 }
 
 static void
 DEBUG_CheckXBLLookup(JSContext *cx, JS::PropertyDescriptor *desc)
 {
     if (!desc->obj)
         return;
@@ -1710,17 +1710,17 @@ XrayResolveOwnProperty(JSContext* cx, JS
     // specialized XBL machinery.
     //
     // While we have to do some sketchy walking through content land, we should
     // be protected by read-only/non-configurable properties, and any functions
     // we end up with should _always_ be living in our own scope (the XBL scope).
     // Make sure to assert that.
     JS::Rooted<JSObject*> maybeElement(cx, obj);
     Element* element;
-    if (xpc::ObjectScope(wrapper)->IsContentXBLScope() &&
+    if (xpc::IsInContentXBLScope(wrapper) &&
         NS_SUCCEEDED(UNWRAP_OBJECT(Element, &maybeElement, element))) {
       if (!nsContentUtils::LookupBindingMember(cx, element, id, desc)) {
         return false;
       }
 
       DEBUG_CheckXBLLookup(cx, desc.address());
 
       if (desc.object()) {
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -1114,16 +1114,17 @@ xpc::CreateSandboxObject(JSContext* cx, 
     if (!sandbox)
         return NS_ERROR_FAILURE;
 
     CompartmentPrivate* priv = CompartmentPrivate::Get(sandbox);
     priv->allowWaivers = options.allowWaivers;
     priv->writeToGlobalPrototype = options.writeToGlobalPrototype;
     priv->isWebExtensionContentScript = options.isWebExtensionContentScript;
     priv->waiveInterposition = options.waiveInterposition;
+    priv->isContentXBLCompartment = options.isContentXBLScope;
 
     // Set up the wantXrays flag, which indicates whether xrays are desired even
     // for same-origin access.
     //
     // This flag has historically been ignored for chrome sandboxes due to
     // quirks in the wrapping implementation that have now been removed. Indeed,
     // same-origin Xrays for chrome->chrome access seems a bit superfluous.
     // Arguably we should just flip the default for chrome and still honor the
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -172,16 +172,17 @@ CompartmentPrivate::CompartmentPrivate(J
     , allowWaivers(true)
     , writeToGlobalPrototype(false)
     , skipWriteToGlobalPrototype(false)
     , isWebExtensionContentScript(false)
     , hasInterposition(false)
     , waiveInterposition(false)
     , addonCallInterposition(false)
     , allowCPOWs(false)
+    , isContentXBLCompartment(false)
     , universalXPConnectEnabled(false)
     , forcePermissiveCOWs(false)
     , wasNuked(false)
     , scriptability(c)
     , scope(nullptr)
     , mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_LENGTH))
 {
     MOZ_COUNT_CTOR(xpc::CompartmentPrivate);
@@ -420,29 +421,33 @@ Scriptability::SetDocShellAllowsScript(b
 /* static */
 Scriptability&
 Scriptability::Get(JSObject* aScope)
 {
     return CompartmentPrivate::Get(aScope)->scriptability;
 }
 
 bool
-IsContentXBLScope(JSCompartment* compartment)
+IsContentXBLCompartment(JSCompartment* compartment)
 {
-    // We always eagerly create compartment privates for XBL scopes.
+    // We always eagerly create compartment privates for content XBL compartments.
     CompartmentPrivate* priv = CompartmentPrivate::Get(compartment);
-    if (!priv || !priv->scope)
-        return false;
-    return priv->scope->IsContentXBLScope();
+    return priv && priv->isContentXBLCompartment;
+}
+
+bool
+IsContentXBLScope(JS::Realm* realm)
+{
+    return IsContentXBLCompartment(JS::GetCompartmentForRealm(realm));
 }
 
 bool
 IsInContentXBLScope(JSObject* obj)
 {
-    return IsContentXBLScope(js::GetObjectCompartment(obj));
+    return IsContentXBLCompartment(js::GetObjectCompartment(obj));
 }
 
 bool
 IsInAddonScope(JSObject* obj)
 {
     return ObjectScope(obj)->IsAddonScope();
 }
 
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -92,17 +92,16 @@ RemoteXULForbidsXBLScope(nsIPrincipal* a
 
 XPCWrappedNativeScope::XPCWrappedNativeScope(JSContext* cx,
                                              JS::HandleObject aGlobal)
       : mWrappedNativeMap(Native2WrappedNativeMap::newMap(XPC_NATIVE_MAP_LENGTH)),
         mWrappedNativeProtoMap(ClassInfo2WrappedNativeProtoMap::newMap(XPC_NATIVE_PROTO_MAP_LENGTH)),
         mComponents(nullptr),
         mNext(nullptr),
         mGlobalJSObject(aGlobal),
-        mIsContentXBLScope(false),
         mIsAddonScope(false)
 {
     // add ourselves to the scopes list
     {
         MOZ_ASSERT(aGlobal);
         DebugOnly<const js::Class*> clasp = js::GetObjectClass(aGlobal);
         MOZ_ASSERT(clasp->flags & (JSCLASS_PRIVATE_IS_NSISUPPORTS |
                                    JSCLASS_HAS_PRIVATE) ||
@@ -261,17 +260,17 @@ CompartmentPerAddon()
     return pref;
 }
 
 JSObject*
 XPCWrappedNativeScope::EnsureContentXBLScope(JSContext* cx)
 {
     JS::RootedObject global(cx, GetGlobalJSObject());
     MOZ_ASSERT(js::IsObjectInContextCompartment(global, cx));
-    MOZ_ASSERT(!mIsContentXBLScope);
+    MOZ_ASSERT(!IsContentXBLScope());
     MOZ_ASSERT(strcmp(js::GetObjectClass(global)->name,
                       "nsXBLPrototypeScript compilation scope"));
 
     // If we already have a special XBL scope object, we know what to use.
     if (mContentXBLScope)
         return mContentXBLScope;
 
     // If this scope doesn't need an XBL scope, just return the global.
@@ -289,16 +288,17 @@ XPCWrappedNativeScope::EnsureContentXBLS
     // anonymous element that lives in a content XBL scope, which isn't a tested
     // or audited codepath. So let's avoid hitting that case by opting out of
     // same-origin Xrays.
     SandboxOptions options;
     options.wantXrays = false;
     options.wantComponents = true;
     options.proto = global;
     options.sameZoneAs = global;
+    options.isContentXBLScope = true;
 
     // Use an ExpandedPrincipal to create asymmetric security.
     nsIPrincipal* principal = GetPrincipal();
     MOZ_ASSERT(!nsContentUtils::IsExpandedPrincipal(principal));
     nsTArray<nsCOMPtr<nsIPrincipal>> principalAsArray(1);
     principalAsArray.AppendElement(principal);
     RefPtr<ExpandedPrincipal> ep =
         ExpandedPrincipal::Create(principalAsArray,
@@ -307,18 +307,17 @@ XPCWrappedNativeScope::EnsureContentXBLS
     // Create the sandbox.
     RootedValue v(cx);
     nsresult rv = CreateSandboxObject(cx, &v,
                                       static_cast<nsIExpandedPrincipal*>(ep),
                                       options);
     NS_ENSURE_SUCCESS(rv, nullptr);
     mContentXBLScope = &v.toObject();
 
-    // Tag it.
-    CompartmentPrivate::Get(js::UncheckedUnwrap(mContentXBLScope))->scope->mIsContentXBLScope = true;
+    MOZ_ASSERT(xpc::IsInContentXBLScope(js::UncheckedUnwrap(mContentXBLScope)));
 
     // Good to go!
     return mContentXBLScope;
 }
 
 bool
 XPCWrappedNativeScope::AllowContentXBLScope()
 {
@@ -392,17 +391,17 @@ ClearContentXBLScope(JSObject* global)
 
 } /* namespace xpc */
 
 JSObject*
 XPCWrappedNativeScope::EnsureAddonScope(JSContext* cx, JSAddonId* addonId)
 {
     JS::RootedObject global(cx, GetGlobalJSObject());
     MOZ_ASSERT(js::IsObjectInContextCompartment(global, cx));
-    MOZ_ASSERT(!mIsContentXBLScope);
+    MOZ_ASSERT(!IsContentXBLScope());
     MOZ_ASSERT(!mIsAddonScope);
     MOZ_ASSERT(addonId);
     MOZ_ASSERT(nsContentUtils::IsSystemPrincipal(GetPrincipal()));
 
     // In bug 1092156, we found that add-on scopes don't work correctly when the
     // window navigates. The add-on global's prototype is an outer window, so,
     // after the navigation, looking up window properties in the add-on scope
     // will fail. However, in most cases where the window can be navigated, the
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -1372,17 +1372,17 @@ IsChromeOrXBL(JSContext* cx, JSObject* /
     JSCompartment* c = js::GetContextCompartment(cx);
 
     // For remote XUL, we run XBL in the XUL scope. Given that we care about
     // compat and not security for remote XUL, we just always claim to be XBL.
     //
     // Note that, for performance, we don't check AllowXULXBLForPrincipal here,
     // and instead rely on the fact that AllowContentXBLScope() only returns false in
     // remote XUL situations.
-    return AccessCheck::isChrome(c) || IsContentXBLScope(c) || !AllowContentXBLScope(c);
+    return AccessCheck::isChrome(c) || IsContentXBLCompartment(c) || !AllowContentXBLScope(c);
 }
 
 namespace workers {
 extern bool IsCurrentThreadRunningChromeWorker();
 } // namespace workers
 
 bool
 ThreadSafeIsChromeOrXBL(JSContext* cx, JSObject* obj)
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -1006,17 +1006,19 @@ public:
     JSObject* EnsureContentXBLScope(JSContext* cx);
 
     JSObject* EnsureAddonScope(JSContext* cx, JSAddonId* addonId);
 
     XPCWrappedNativeScope(JSContext* cx, JS::HandleObject aGlobal);
 
     nsAutoPtr<JSObject2JSObjectMap> mWaiverWrapperMap;
 
-    bool IsContentXBLScope() { return mIsContentXBLScope; }
+    JSCompartment* Compartment() const { return js::GetObjectCompartment(mGlobalJSObject); }
+
+    bool IsContentXBLScope() { return xpc::IsContentXBLCompartment(Compartment()); }
     bool AllowContentXBLScope();
     bool UseContentXBLScope() { return mUseContentXBLScope; }
     void ClearContentXBLScope() { mContentXBLScope = nullptr; }
 
     bool IsAddonScope() { return mIsAddonScope; }
 
     inline bool HasInterposition() { return mInterposition; }
     nsCOMPtr<nsIAddonInterposition> GetInterposition();
@@ -1073,17 +1075,16 @@ private:
     nsTArray<JS::ObjectPtr>          mAddonScopes;
 
     // This is a service that will be use to interpose on some property accesses on
     // objects from other scope, for add-on compatibility reasons.
     nsCOMPtr<nsIAddonInterposition>  mInterposition;
 
     JS::WeakMapPtr<JSObject*, JSObject*> mXrayExpandos;
 
-    bool mIsContentXBLScope;
     bool mIsAddonScope;
 
     // For remote XUL domains, we run all XBL in the content scope for compat
     // reasons (though we sometimes pref this off for automation). We separately
     // track the result of this decision (mAllowContentXBLScope), from the decision
     // of whether to actually _use_ an XBL scope (mUseContentXBLScope), which depends
     // on the type of global and whether the compartment is system principal
     // or not.
@@ -2790,16 +2791,17 @@ public:
         , wantExportHelpers(false)
         , isWebExtensionContentScript(false)
         , waiveInterposition(false)
         , proto(cx)
         , addonId(cx)
         , writeToGlobalPrototype(false)
         , sameZoneAs(cx)
         , freshZone(false)
+        , isContentXBLScope(false)
         , invisibleToDebugger(false)
         , discardSource(false)
         , metadata(cx)
         , userContextId(0)
         , originAttributes(cx)
     { }
 
     virtual bool Parse();
@@ -2811,16 +2813,17 @@ public:
     bool isWebExtensionContentScript;
     bool waiveInterposition;
     JS::RootedObject proto;
     nsCString sandboxName;
     JS::RootedString addonId;
     bool writeToGlobalPrototype;
     JS::RootedObject sameZoneAs;
     bool freshZone;
+    bool isContentXBLScope;
     bool invisibleToDebugger;
     bool discardSource;
     GlobalProperties globalProperties;
     JS::RootedValue metadata;
     uint32_t userContextId;
     JS::RootedObject originAttributes;
 
 protected:
@@ -3087,16 +3090,20 @@ public:
 
     // If CPOWs are disabled for browser code via the
     // dom.ipc.cpows.forbid-unsafe-from-browser preferences, then only
     // add-ons can use CPOWs. This flag allows a non-addon scope
     // to opt into CPOWs. It's necessary for the implementation of
     // RemoteAddonsParent.jsm.
     bool allowCPOWs;
 
+    // True if this compartment is a content XBL compartment. Every global in
+    // such a compartment is a content XBL scope.
+    bool isContentXBLCompartment;
+
     // This is only ever set during mochitest runs when enablePrivilege is called.
     // It's intended as a temporary stopgap measure until we can finish ripping out
     // enablePrivilege. Once set, this value is never unset (i.e., it doesn't follow
     // the old scoping rules of enablePrivilege).
     //
     // Using it in production is inherently unsafe.
     bool universalXPConnectEnabled;
 
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -72,17 +72,18 @@ private:
     // Whether the new-style domain policy when this compartment was created
     // forbids script execution.
     bool mScriptBlockedByPolicy;
 };
 
 JSObject*
 TransplantObject(JSContext* cx, JS::HandleObject origobj, JS::HandleObject target);
 
-bool IsContentXBLScope(JSCompartment* compartment);
+bool IsContentXBLCompartment(JSCompartment* compartment);
+bool IsContentXBLScope(JS::Realm* realm);
 bool IsInContentXBLScope(JSObject* obj);
 
 // Return a raw XBL scope object corresponding to contentScope, which must
 // be an object whose global is a DOM window.
 //
 // The return value is not wrapped into cx->compartment, so be sure to enter
 // its compartment before doing anything meaningful.
 //