Bug 1048695 part 2. Make interface members not be exposed based on their nonExposedGlobals. r=peterv
authorBoris Zbarsky <bzbarsky@mit.edu>
Tue, 01 Dec 2015 12:02:36 -0500
changeset 309211 cfd1e40e0248e8618b3013583e6aa6d75ca38151
parent 309210 60d26cef51f1ef88bc271af38ce5e559b5bfab41
child 309212 0d88461fff1b4ca69147817284b021aac512ab33
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv
bugs1048695
milestone45.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 1048695 part 2. Make interface members not be exposed based on their nonExposedGlobals. r=peterv
dom/bindings/BindingUtils.cpp
dom/bindings/BindingUtils.h
dom/bindings/DOMJSClass.h
dom/webidl/EventTarget.webidl
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -2529,16 +2529,65 @@ CheckAllPermissions(JSContext* aCx, JSOb
     permMgr->TestPermissionFromWindow(window, *aPermissions, &permission);
     if (permission != nsIPermissionManager::ALLOW_ACTION) {
       return false;
     }
   } while (*(++aPermissions));
   return true;
 }
 
+bool
+IsNonExposedGlobal(JSContext* aCx, JSObject* aGlobal,
+                   uint32_t aNonExposedGlobals)
+{
+  MOZ_ASSERT(aNonExposedGlobals, "Why did we get called?");
+  MOZ_ASSERT((aNonExposedGlobals &
+              ~(GlobalNames::Window |
+                GlobalNames::BackstagePass |
+                GlobalNames::DedicatedWorkerGlobalScope |
+                GlobalNames::SharedWorkerGlobalScope |
+                GlobalNames::ServiceWorkerGlobalScope |
+                GlobalNames::WorkerDebuggerGlobalScope)) == 0,
+             "Unknown non-exposed global type");
+
+  const char* name = js::GetObjectClass(aGlobal)->name;
+
+  if ((aNonExposedGlobals & GlobalNames::Window) &&
+      !strcmp(name, "Window")) {
+    return true;
+  }
+
+  if ((aNonExposedGlobals & GlobalNames::BackstagePass) &&
+      !strcmp(name, "BackstagePass")) {
+    return true;
+  }
+
+  if ((aNonExposedGlobals & GlobalNames::DedicatedWorkerGlobalScope) &&
+      !strcmp(name, "DedicatedWorkerGlobalScope")) {
+    return true;
+  }
+
+  if ((aNonExposedGlobals & GlobalNames::SharedWorkerGlobalScope) &&
+      !strcmp(name, "SharedWorkerGlobalScope")) {
+    return true;
+  }
+
+  if ((aNonExposedGlobals & GlobalNames::ServiceWorkerGlobalScope) &&
+      !strcmp(name, "ServiceWorkerGlobalScope")) {
+    return true;
+  }
+
+  if ((aNonExposedGlobals & GlobalNames::WorkerDebuggerGlobalScope) &&
+      !strcmp(name, "WorkerDebuggerGlobalScopex")) {
+    return true;
+  }
+
+  return false;
+}
+
 void
 HandlePrerenderingViolation(nsPIDOMWindow* aWindow)
 {
   // Suspend the window and its workers, and its children too.
   aWindow->SuspendTimeouts();
 
   // Suspend event handling on the document
   nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -3155,26 +3155,16 @@ ConvertExceptionToPromise(JSContext* cx,
                           JS::MutableHandle<JS::Value> rval);
 
 #ifdef DEBUG
 void
 AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitinfo,
                                JS::Handle<JS::Value> aValue);
 #endif
 
-// Returns true if aObj's global has any of the permissions named in aPermissions
-// set to nsIPermissionManager::ALLOW_ACTION. aPermissions must be null-terminated.
-bool
-CheckAnyPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]);
-
-// Returns true if aObj's global has all of the permissions named in aPermissions
-// set to nsIPermissionManager::ALLOW_ACTION. aPermissions must be null-terminated.
-bool
-CheckAllPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]);
-
 // This function is called by the bindings layer for methods/getters/setters
 // that are not safe to be called in prerendering mode.  It checks to make sure
 // that the |this| object is not running in a global that is in prerendering
 // mode.  Otherwise, it aborts execution of timers and event handlers, and
 // returns false which gets converted to an uncatchable exception by the
 // bindings layer.
 bool
 EnforceNotInPrerendering(JSContext* aCx, JSObject* aObj);
--- a/dom/bindings/DOMJSClass.h
+++ b/dom/bindings/DOMJSClass.h
@@ -34,22 +34,34 @@ typedef bool
                        JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                        JS::MutableHandle<JSPropertyDescriptor> desc);
 
 typedef bool
 (* EnumerateOwnProperties)(JSContext* cx, JS::Handle<JSObject*> wrapper,
                            JS::Handle<JSObject*> obj,
                            JS::AutoIdVector& props);
 
+// Returns true if aObj's global has any of the permissions named in
+// aPermissions set to nsIPermissionManager::ALLOW_ACTION. aPermissions must be
+// null-terminated.
 bool
 CheckAnyPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]);
 
+// Returns true if aObj's global has all of the permissions named in
+// aPermissions set to nsIPermissionManager::ALLOW_ACTION. aPermissions must be
+// null-terminated.
 bool
 CheckAllPermissions(JSContext* aCx, JSObject* aObj, const char* const aPermissions[]);
 
+// Returns true if the given global is of a type whose bit is set in
+// aNonExposedGlobals.
+bool
+IsNonExposedGlobal(JSContext* aCx, JSObject* aGlobal,
+                   uint32_t aNonExposedGlobals);
+
 struct ConstantSpec
 {
   const char* name;
   JS::Value value;
 };
 
 typedef bool (*PropertyEnabled)(JSContext* cx, JSObject* global);
 
@@ -63,16 +75,30 @@ static const uint32_t DedicatedWorkerGlo
 static const uint32_t SharedWorkerGlobalScope = 1u << 3;
 static const uint32_t ServiceWorkerGlobalScope = 1u << 4;
 static const uint32_t WorkerDebuggerGlobalScope = 1u << 5;
 } // namespace GlobalNames
 
 template<typename T>
 struct Prefable {
   inline bool isEnabled(JSContext* cx, JS::Handle<JSObject*> obj) const {
+    // Reading "enabled" on a worker thread is technically undefined behavior,
+    // because it's written only on main threads, with no barriers of any sort.
+    // So we want to avoid doing that.  But we don't particularly want to make
+    // expensive NS_IsMainThread calls here.
+    //
+    // The good news is that "enabled" is only written for things that have a
+    // Pref annotation, and such things can never be exposed on non-Window
+    // globals; our IDL parser enforces that.  So as long as we check our
+    // exposure set before checking "enabled" we will be ok.
+    if (nonExposedGlobals &&
+        IsNonExposedGlobal(cx, js::GetGlobalForObjectCrossCompartment(obj),
+                           nonExposedGlobals)) {
+      return false;
+    }
     if (!enabled) {
       return false;
     }
     if (!enabledFunc && !availableFunc && !checkAnyPermissions && !checkAllPermissions) {
       return true;
     }
     if (enabledFunc &&
         !enabledFunc(cx, js::GetGlobalForObjectCrossCompartment(obj))) {
--- a/dom/webidl/EventTarget.webidl
+++ b/dom/webidl/EventTarget.webidl
@@ -44,11 +44,11 @@ partial interface EventTarget {
   [ChromeOnly]
   EventHandler getEventHandler(DOMString type);
 };
 
 // Mozilla extension to make firing events on event targets from
 // chrome easier.  This returns the window which can be used to create
 // events to fire at this EventTarget, or null if there isn't one.
 partial interface EventTarget {
-  [ChromeOnly, Exposed=Window, BinaryName="ownerGlobalForBindings"]
+  [ChromeOnly, Exposed=(Window,System), BinaryName="ownerGlobalForBindings"]
   readonly attribute WindowProxy? ownerGlobal;
 };