Bug 735280 - Part 3: Components object specific wrapper. r=bholley
☠☠ backed out by 9c5557c4d1fe ☠ ☠
authorGabor Krizsanits <gkrizsanits@mozilla.com>
Tue, 24 Apr 2012 21:48:02 -0400
changeset 96424 0b170d1f5d105ab22cf2679ff2b199ed82235860
parent 96423 2956b31dcc62a321be432b04e08c9c001dea245a
child 96425 6a8ef70e44b5f1ee58b6cc109e0cb8d60d401c73
push id1116
push userlsblakk@mozilla.com
push dateMon, 16 Jul 2012 19:38:18 +0000
treeherdermozilla-beta@95f959a8b4dc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs735280
milestone15.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 735280 - Part 3: Components object specific wrapper. r=bholley
caps/tests/mochitest/test_bug246699.html
js/src/tests/js1_5/Regress/regress-328897.js
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/tests/unit/test_components.js
js/xpconnect/tests/unit/xpcshell.ini
js/xpconnect/wrappers/AccessCheck.cpp
js/xpconnect/wrappers/AccessCheck.h
js/xpconnect/wrappers/FilteringWrapper.cpp
js/xpconnect/wrappers/WrapperFactory.cpp
js/xpconnect/wrappers/WrapperFactory.h
--- a/caps/tests/mochitest/test_bug246699.html
+++ b/caps/tests/mochitest/test_bug246699.html
@@ -1,34 +1,39 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=246699
 -->
 <head>
   <title>Test for Bug 246699</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=246699">Mozilla Bug 246699</a>
 <p id="display"></p>
 <div id="content" style="display: none">
-<iframe id="load-frame"></iframe>  
+<iframe id="load-frame"></iframe>
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /**
  ** Test for Bug 246699
  ** (should produce stack information for caps errors)
  **/
+function isError(e)
+{
+  return e.constructor.name === "Error" || e.constructor.name === "TypeError";
+}
+
 function hasStack(e)
 {
-  return e.constructor.name === "Error" && /inciteCaps/.test(e.stack);
+  return isError(e) && /inciteCaps/.test(e.stack);
 }
 
 function inciteCaps(f)
 {
     try {
         f();
         return "operation succeeded";
     } catch (e if hasStack(e)) {
--- a/js/src/tests/js1_5/Regress/regress-328897.js
+++ b/js/src/tests/js1_5/Regress/regress-328897.js
@@ -47,29 +47,30 @@ printBugNumber(BUGNUMBER);
 printStatus (summary);
  
 if (typeof window == 'undefined')
 {
   reportCompare(expect, actual, summary);
 }
 else
 {
-  expect = /(Script error.|Permission denied for <file:\/\/> to get property XPCComponents.classes)/;
+  expect = /(Script error.|Permission denied to access property 'classes')/;
 
   window._onerror = window.onerror;
   window.onerror = (function (msg, page, line) { 
       actual = msg; 
       gDelayTestDriverEnd = false;
       jsTestDriverEnd();
       reportMatch(expect, actual, summary);
     });
 
   gDelayTestDriverEnd = true;
 
-  window.location="javascript:Components.classes";
+  // Trying to set Components.classes will trigger a Permission denied exception
+  window.location="javascript:Components.classes = 42";
   actual = 'No Error';
 }
 
 function onload() 
 {
   if (actual == 'No Error')
   {
     gDelayTestDriverEnd = false;
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -55,16 +55,17 @@
 #include "WrapperFactory.h"
 #include "XrayWrapper.h"
 #include "nsNullPrincipal.h"
 #include "nsJSUtils.h"
 #include "mozJSComponentLoader.h"
 #include "nsContentUtils.h"
 #include "jsgc.h"
 #include "jsfriendapi.h"
+#include "AccessCheck.h"
 #include "mozilla/dom/bindings/Utils.h"
 
 using namespace mozilla;
 using namespace js;
 
 using mozilla::dom::bindings::DestroyProtoOrIfaceCache;
 
 /***************************************************************************/
@@ -4456,22 +4457,23 @@ nsXPCComponents::AttachComponentsObject(
 
     nsCOMPtr<XPCWrappedNative> wrapper;
     xpcObjectHelper helper(cholder);
     XPCWrappedNative::GetNewOrUsed(ccx, helper, aScope, iface, getter_AddRefs(wrapper));
     if (!wrapper)
         return false;
 
     jsid id = ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_COMPONENTS);
-    JSObject* obj;
-
-    return NS_SUCCEEDED(wrapper->GetJSObject(&obj)) &&
-           obj && JS_DefinePropertyById(ccx, aGlobal, id, OBJECT_TO_JSVAL(obj),
-                                        nsnull, nsnull,
-                                        JSPROP_PERMANENT | JSPROP_READONLY);
+    JSObject* obj = wrapper->GetSameCompartmentSecurityWrapper(ccx);
+    if (!wrapper)
+        return false;
+   
+    return JS_DefinePropertyById(ccx, aGlobal, id, OBJECT_TO_JSVAL(obj),
+                                 nsnull, nsnull,
+                                 JSPROP_PERMANENT | JSPROP_READONLY);
 }
 
 /* void lookupMethod (); */
 NS_IMETHODIMP
 nsXPCComponents::LookupMethod(const JS::Value& object,
                               const JS::Value& name,
                               JSContext *cx,
                               JS::Value *retval)
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -1678,16 +1678,18 @@ XPCWrappedNative::ReparentWrapperIfFound
                     if (!innerAC.enter(ccx, aOldScope->GetGlobalJSObject()) ||
                         !wrapper->GetSameCompartmentSecurityWrapper(ccx))
                         return NS_ERROR_FAILURE;
                 }
 
                 JSObject *ww = wrapper->GetWrapper();
                 if (ww) {
                     JSObject *newwrapper;
+                    MOZ_ASSERT(!xpc::WrapperFactory::IsComponentsObject(flat), 
+                               "Components object should never get here");
                     if (xpc::WrapperFactory::IsLocationObject(flat)) {
                         newwrapper = xpc::WrapperFactory::WrapLocationObject(ccx, newobj);
                         if (!newwrapper)
                             return NS_ERROR_FAILURE;
                     } else {
                         NS_ASSERTION(wrapper->NeedsSOW(), "weird wrapper wrapper");
                         newwrapper = xpc::WrapperFactory::WrapSOWObject(ccx, newobj);
                         if (!newwrapper)
@@ -2235,16 +2237,20 @@ XPCWrappedNative::GetSameCompartmentSecu
     if (xpc::WrapperFactory::IsLocationObject(flat)) {
         wrapper = xpc::WrapperFactory::WrapLocationObject(cx, flat);
         if (!wrapper)
             return NULL;
     } else if (NeedsSOW()) {
         wrapper = xpc::WrapperFactory::WrapSOWObject(cx, flat);
         if (!wrapper)
             return NULL;
+    } else if (xpc::WrapperFactory::IsComponentsObject(flat)) {
+        wrapper = xpc::WrapperFactory::WrapComponentsObject(cx, flat);
+        if (!wrapper)
+            return NULL;
     }
 
     // If we made a wrapper, cache it and return it.
     if (wrapper) {
         SetWrapper(wrapper);
         return wrapper;
     }
 
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_components.js
@@ -0,0 +1,43 @@
+function run_test() {
+  var Cu = Components.utils;
+  var sb1 = Cu.Sandbox("http://www.blah.com");
+  var sb2 = Cu.Sandbox("http://www.blah.com");
+  var sb3 = Cu.Sandbox(this);
+  var sb4 = Cu.Sandbox("http://www.other.com");
+  var rv;
+
+  // non-chrome accessing chrome Components
+  sb1.C = Components;
+  rv = Cu.evalInSandbox("C.utils", sb1);
+  do_check_eq(rv, undefined);  
+  rv = Cu.evalInSandbox("C.interfaces", sb1);
+  do_check_neq(rv, undefined);
+
+  // non-chrome accessing own Components
+  rv = Cu.evalInSandbox("Components.utils", sb1);
+  do_check_eq(rv, undefined);
+  rv = Cu.evalInSandbox("Components.interfaces", sb1);
+  do_check_neq(rv, undefined); 
+
+  // non-chrome same origin
+  var C2 = Cu.evalInSandbox("Components", sb2);
+  do_check_neq(rv, C2.utils); 
+  sb1.C2 = C2;
+  rv = Cu.evalInSandbox("C2.utils", sb1);
+  do_check_eq(rv, undefined);
+  rv = Cu.evalInSandbox("C2.interfaces", sb1);
+  do_check_neq(rv, undefined);
+
+  // chrome accessing chrome
+  sb3.C = Components;
+  rv = Cu.evalInSandbox("C.utils", sb3);
+  do_check_eq(rv, Cu);
+
+  // non-chrome cross origin
+  sb4.C2 = C2;
+  rv = Cu.evalInSandbox("C2.interfaces", sb1);
+  do_check_neq(rv, undefined);
+  rv = Cu.evalInSandbox("C2.utils", sb1);
+  do_check_eq(rv, undefined);
+
+}
\ No newline at end of file
--- a/js/xpconnect/tests/unit/xpcshell.ini
+++ b/js/xpconnect/tests/unit/xpcshell.ini
@@ -20,8 +20,9 @@ tail =
 [test_localeCompare.js]
 # Bug 676965: test fails consistently on Android
 fail-if = os == "android"
 [test_recursive_import.js]
 [test_xpcomutils.js]
 [test_unload.js]
 [test_attributes.js]
 [test_params.js]
+[test_components.js]
--- a/js/xpconnect/wrappers/AccessCheck.cpp
+++ b/js/xpconnect/wrappers/AccessCheck.cpp
@@ -602,9 +602,34 @@ ExposedPropertiesOnly::check(JSContext *
         (act != Wrapper::SET && !(access & READ))) {
         return PermitIfUniversalXPConnect(cx, id, act, perm); // Deny
     }
 
     perm = PermitPropertyAccess;
     return true; // Allow
 }
 
+bool
+ComponentsObjectPolicy::check(JSContext *cx, JSObject *wrapper, jsid id, Wrapper::Action act,
+                              Permission &perm) 
+{
+    perm = DenyAccess;
+    JSAutoEnterCompartment ac;
+    if (!ac.enter(cx, wrapper))
+        return false;
+
+    if (JSID_IS_STRING(id) && act == Wrapper::GET) {
+        JSFlatString *flatId = JSID_TO_FLAT_STRING(id);
+        if (JS_FlatStringEqualsAscii(flatId, "isSuccessCode") ||
+            JS_FlatStringEqualsAscii(flatId, "lookupMethod") ||
+            JS_FlatStringEqualsAscii(flatId, "interfaces") ||
+            JS_FlatStringEqualsAscii(flatId, "interfacesByID") ||
+            JS_FlatStringEqualsAscii(flatId, "results"))
+        {
+            perm = PermitPropertyAccess;
+            return true;
+        }
+    }
+
+    return PermitIfUniversalXPConnect(cx, id, act, perm);  // Deny
 }
+
+}
--- a/js/xpconnect/wrappers/AccessCheck.h
+++ b/js/xpconnect/wrappers/AccessCheck.h
@@ -170,9 +170,15 @@ struct LocationPolicy : public Policy {
 
 // This policy only permits access to properties if they appear in the
 // objects exposed properties list.
 struct ExposedPropertiesOnly : public Policy {
     static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act,
                       Permission &perm);
 };
 
+// Components specific policy
+struct ComponentsObjectPolicy : public Policy {
+    static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act,
+                      Permission &perm);
+};
+
 }
--- a/js/xpconnect/wrappers/FilteringWrapper.cpp
+++ b/js/xpconnect/wrappers/FilteringWrapper.cpp
@@ -146,33 +146,39 @@ FilteringWrapper<Base, Policy>::enter(JS
 #define DXOW   FilteringWrapper<XrayDOM, \
                                 CrossOriginAccessiblePropertiesOnly>
 #define NNXOW FilteringWrapper<CrossCompartmentSecurityWrapper, \
                                CrossOriginAccessiblePropertiesOnly>
 #define LW    FilteringWrapper<XrayWrapper<SameCompartmentSecurityWrapper>, \
                                LocationPolicy>
 #define XLW   FilteringWrapper<XrayWrapper<CrossCompartmentSecurityWrapper>, \
                                LocationPolicy>
-
+#define CW FilteringWrapper<SameCompartmentSecurityWrapper, \
+                            ComponentsObjectPolicy>
+#define XCW FilteringWrapper<CrossCompartmentSecurityWrapper, \
+                            ComponentsObjectPolicy>
 template<> SOW SOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
                               WrapperFactory::SOW_FLAG);
 template<> SCSOW SCSOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
                                   WrapperFactory::SOW_FLAG);
 template<> COW COW::singleton(0);
 template<> XOW XOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
                               WrapperFactory::PARTIALLY_TRANSPARENT);
 template<> PXOW PXOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
                                 WrapperFactory::PARTIALLY_TRANSPARENT);
 template<> DXOW DXOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
                                 WrapperFactory::PARTIALLY_TRANSPARENT);
 template<> NNXOW NNXOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
                                   WrapperFactory::PARTIALLY_TRANSPARENT);
 template<> LW  LW::singleton(WrapperFactory::SHADOWING_FORBIDDEN);
 template<> XLW XLW::singleton(WrapperFactory::SHADOWING_FORBIDDEN);
 
+template<> CW CW::singleton(0);
+template<> XCW XCW::singleton(0);
+
 template class SOW;
 template class COW;
 template class XOW;
 template class PXOW;
 template class DXOW;
 template class NNXOW;
 template class LW;
 template class XLW;
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -361,16 +361,19 @@ WrapperFactory::Rewrap(JSContext *cx, JS
             if (IsLocationObject(obj))
                 wrapper = &FilteringWrapper<Xray, LocationPolicy>::singleton;
             else
                 wrapper = &FilteringWrapper<Xray, CrossOriginAccessiblePropertiesOnly>::singleton;
         } else if (mozilla::dom::binding::instanceIsProxy(obj)) {
             wrapper = &FilteringWrapper<XrayProxy, CrossOriginAccessiblePropertiesOnly>::singleton;
         } else if (mozilla::dom::bindings::IsDOMClass(JS_GetClass(obj))) {
             wrapper = &FilteringWrapper<XrayDOM, CrossOriginAccessiblePropertiesOnly>::singleton;
+        } else if (IsComponentsObject(obj)) {
+            wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,
+                                        ComponentsObjectPolicy>::singleton;
         } else {
             wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,
                                         ExposedPropertiesOnly>::singleton;
         }
     } else if (AccessCheck::isSameOrigin(origin, target)) {
         // For the same-origin case we use a transparent wrapper, unless one
         // of the following is true:
         // * The object is flagged as needing a SOW.
@@ -395,16 +398,19 @@ WrapperFactory::Rewrap(JSContext *cx, JS
         // to the LW in the host compartment, rather than an XLW directly to the
         // Location object. This still doesn't share expandos in the
         // document.domain case, but that's probably fine. Double-wrapping sucks,
         // but it's kind of unavoidable here.
         XrayType type;
         if (AccessCheck::needsSystemOnlyWrapper(obj)) {
             wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,
                                         OnlyIfSubjectIsSystem>::singleton;
+        } else if (IsComponentsObject(obj)) {
+            wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,
+                                        ComponentsObjectPolicy>::singleton;
         } else if (!targetdata || !targetdata->wantXrays ||
                    (type = GetXrayType(obj)) == NotXray) {
             // Do the double-wrapping if need be.
             if (IsLocationObject(obj)) {
                 JSAutoEnterCompartment ac;
                 if (!ac.enter(cx, obj))
                     return nsnull;
                 XPCWrappedNative *wn = GetWrappedNative(cx, obj);
@@ -519,9 +525,26 @@ WrapperFactory::WrapSOWObject(JSContext 
 {
     JSObject *wrapperObj =
         Wrapper::New(cx, obj, JS_GetPrototype(obj), JS_GetGlobalForObject(cx, obj),
                      &FilteringWrapper<SameCompartmentSecurityWrapper,
                      OnlyIfSubjectIsSystem>::singleton);
     return wrapperObj;
 }
 
+bool
+WrapperFactory::IsComponentsObject(JSObject *obj)
+{
+    const char *name = js::GetObjectClass(obj)->name;
+    return name[0] == 'n' && !strcmp(name, "nsXPCComponents");
 }
+
+JSObject *
+WrapperFactory::WrapComponentsObject(JSContext *cx, JSObject *obj)
+{
+    JSObject *wrapperObj =
+        Wrapper::New(cx, obj, JS_GetPrototype(obj), JS_GetGlobalForObject(cx, obj),
+                     &FilteringWrapper<SameCompartmentSecurityWrapper, ComponentsObjectPolicy>::singleton);
+
+    return wrapperObj;
+}
+
+}
--- a/js/xpconnect/wrappers/WrapperFactory.h
+++ b/js/xpconnect/wrappers/WrapperFactory.h
@@ -105,15 +105,21 @@ class WrapperFactory {
     // Wrap a location object.
     static JSObject *WrapLocationObject(JSContext *cx, JSObject *obj);
 
     // Wrap wrapped object into a waiver wrapper and then re-wrap it.
     static bool WaiveXrayAndWrap(JSContext *cx, jsval *vp);
 
     // Wrap a (same compartment) object in a SOW.
     static JSObject *WrapSOWObject(JSContext *cx, JSObject *obj);
+
+    // Return true if this is a Components object.
+    static bool IsComponentsObject(JSObject *obj);
+
+    // Wrap a (same compartment) Components object.
+    static JSObject *WrapComponentsObject(JSContext *cx, JSObject *obj);
 };
 
 extern js::Wrapper WaiveXrayWrapperWrapper;
 
 }
 
 #endif /* _xpc_WRAPPERFACTORY_H */