Bug 733035 - postMessage support for sandboxes. r=khuey
authorGabor Krizsanits <gkrizsanits@mozilla.com>
Thu, 05 Apr 2012 18:33:20 -0400
changeset 94434 8f03b8e2cc5a14c98a9bdf316516a8fa9790b464
parent 94433 c6fe3d26bece1b1d56bee1b5d2934496cabb647a
child 94435 e7ebb831c3298a530dc754b1b1fc1df7a375922c
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs733035
milestone14.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 733035 - postMessage support for sandboxes. r=khuey
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/tests/mochitest/chrome/Makefile.in
dom/tests/mochitest/chrome/test_sandbox_postMessage.html
js/xpconnect/src/xpcpublic.h
js/xpconnect/wrappers/AccessCheck.h
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -5957,18 +5957,17 @@ nsGlobalWindow::GetFrames(nsIDOMWindow**
   *aFrames = this;
   NS_ADDREF(*aFrames);
 
   FlushPendingNotifications(Flush_ContentAndNotify);
 
   return NS_OK;
 }
 
-nsGlobalWindow*
-nsGlobalWindow::CallerInnerWindow()
+JSObject* nsGlobalWindow::CallerGlobal()
 {
   JSContext *cx = nsContentUtils::GetCurrentJSContext();
   if (!cx) {
     NS_ERROR("Please don't call this method from C++!");
 
     return nsnull;
   }
 
@@ -5983,16 +5982,31 @@ nsGlobalWindow::CallerInnerWindow()
 
     if (fp)
       scope = JS_GetGlobalForFrame(fp);
   }
 
   if (!scope)
     scope = JS_GetGlobalForScopeChain(cx);
 
+  return scope;
+}
+
+nsGlobalWindow*
+nsGlobalWindow::CallerInnerWindow()
+{
+  JSContext *cx = nsContentUtils::GetCurrentJSContext();
+  if (!cx) {
+    NS_ERROR("Please don't call this method from C++!");
+
+    return nsnull;
+  }
+
+  JSObject *scope = CallerGlobal();
+
   JSAutoEnterCompartment ac;
   if (!ac.enter(cx, scope))
     return nsnull;
 
   nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
   nsContentUtils::XPConnect()->
     GetWrappedNativeOfJSObject(cx, scope, getter_AddRefs(wrapper));
   if (!wrapper)
@@ -6305,65 +6319,79 @@ nsGlobalWindow::PostMessageMoz(const jsv
   // As such, this code must be particularly careful in the information it
   // exposes to calling code.
   //
   // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html
   //
 
   // First, get the caller's window
   nsRefPtr<nsGlobalWindow> callerInnerWin = CallerInnerWindow();
-  if (!callerInnerWin)
-    return NS_OK;
-  NS_ABORT_IF_FALSE(callerInnerWin->IsInnerWindow(),
-                    "should have gotten an inner window here");
-
-  // Compute the caller's origin either from its principal or, in the case the
-  // principal doesn't carry a URI (e.g. the system principal), the caller's
-  // document.  We must get this now instead of when the event is created and
-  // dispatched, because ultimately it is the identity of the calling window
-  // *now* that determines who sent the message (and not an identity which might
-  // have changed due to intervening navigations).
-  nsIPrincipal* callerPrin = callerInnerWin->GetPrincipal();
+  nsIPrincipal* callerPrin;
+  if (callerInnerWin) {
+    NS_ABORT_IF_FALSE(callerInnerWin->IsInnerWindow(),
+                      "should have gotten an inner window here");
+
+    // Compute the caller's origin either from its principal or, in the case the
+    // principal doesn't carry a URI (e.g. the system principal), the caller's
+    // document.  We must get this now instead of when the event is created and
+    // dispatched, because ultimately it is the identity of the calling window
+    // *now* that determines who sent the message (and not an identity which might
+    // have changed due to intervening navigations).
+    callerPrin = callerInnerWin->GetPrincipal();
+  }
+  else {
+    // In case the global is not a window, it can be a sandbox, and the sandbox's
+    // principal can be used for the security check.
+    JSObject *global = CallerGlobal();
+    NS_ASSERTION(global, "Why is there no global object?");
+    JSCompartment *compartment = js::GetObjectCompartment(global);
+    callerPrin = xpc::GetCompartmentPrincipal(compartment);
+  }
   if (!callerPrin)
     return NS_OK;
-  
+
   nsCOMPtr<nsIURI> callerOuterURI;
   if (NS_FAILED(callerPrin->GetURI(getter_AddRefs(callerOuterURI))))
     return NS_OK;
 
   nsAutoString origin;
   if (callerOuterURI) {
     // if the principal has a URI, use that to generate the origin
     nsContentUtils::GetUTFOrigin(callerPrin, origin);
   }
-  else {
+  else if (callerInnerWin) {
     // otherwise use the URI of the document to generate origin
-    nsCOMPtr<nsIDocument> doc = do_QueryInterface(callerInnerWin->mDocument);
+    nsCOMPtr<nsIDocument> doc = do_QueryInterface(callerInnerWin->GetExtantDocument());
     if (!doc)
       return NS_OK;
     callerOuterURI = doc->GetDocumentURI();
     // if the principal has a URI, use that to generate the origin
     nsContentUtils::GetUTFOrigin(callerOuterURI, origin);
   }
+  else {
+    // in case of a sandbox with a system principal origin can be empty
+    if (!nsContentUtils::IsSystemPrincipal(callerPrin))
+      return NS_OK;
+  }
 
   // Convert the provided origin string into a URI for comparison purposes.
   // "*" indicates no specific origin is required.
   nsCOMPtr<nsIURI> providedOrigin;
   if (!aOrigin.EqualsASCII("*")) {
     if (NS_FAILED(NS_NewURI(getter_AddRefs(providedOrigin), aOrigin)))
       return NS_ERROR_DOM_SYNTAX_ERR;
     if (NS_FAILED(providedOrigin->SetUserPass(EmptyCString())) ||
         NS_FAILED(providedOrigin->SetPath(EmptyCString())))
       return NS_OK;
   }
 
   // Create and asynchronously dispatch a runnable which will handle actual DOM
   // event creation and dispatch.
   nsRefPtr<PostMessageEvent> event =
-    new PostMessageEvent(nsContentUtils::IsCallerChrome()
+    new PostMessageEvent(nsContentUtils::IsCallerChrome() || !callerInnerWin
                          ? nsnull
                          : callerInnerWin->GetOuterWindowInternal(),
                          origin,
                          this,
                          providedOrigin,
                          nsContentUtils::IsCallerTrustedForWrite());
 
   // We *must* clone the data here, or the jsval could be modified
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -570,16 +570,17 @@ protected:
 
   // Object Management
   virtual ~nsGlobalWindow();
   void CleanUp(bool aIgnoreModalDialog);
   void ClearControllers();
   nsresult FinalClose();
 
   void FreeInnerObjects();
+  JSObject *CallerGlobal();
   nsGlobalWindow *CallerInnerWindow();
 
   nsresult InnerSetNewDocument(nsIDocument* aDocument);
 
   nsresult DefineArgumentsProperty(nsIArray *aArguments);
 
   // Get the parent, returns null if this is a toplevel window
   nsIDOMWindow* GetParentInternal();
--- a/dom/tests/mochitest/chrome/Makefile.in
+++ b/dom/tests/mochitest/chrome/Makefile.in
@@ -72,16 +72,17 @@ include $(topsrcdir)/config/rules.mk
 		test_nodesFromRect.html \
 		489127.html \
 		test_focus_docnav.xul \
 		window_focus_docnav.xul \
 		test_clonewrapper.xul \
 		test_moving_nodeList.xul \
 		test_callback_wrapping.xul \
 		window_callback_wrapping.xul \
+		test_sandbox_postMessage.html \
 		$(NULL)
 
 ifeq (WINNT,$(OS_ARCH))
 _TEST_FILES += \
 		test_sizemode_attribute.xul \
 		sizemode_attribute.xul \
 		$(NULL)
 endif
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/chrome/test_sandbox_postMessage.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Testing postMessage from sandbox</title>
+  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+<script type="application/javascript">
+  SimpleTest.waitForExplicitFinish();
+  function doTest() {
+    var sandbox = Components.utils.Sandbox("http://mochi.test:8888/", { wantXrays: true });
+    var win = window.frames["sameDomain"];
+    sandbox.win = win;
+    sandbox.is = is;
+    sandbox.done = SimpleTest.finish;
+
+    result = Components.utils.evalInSandbox('var data = {some:"data"};'
+    +'win.addEventListener("message", receiveMessage, false);'
+    +'function receiveMessage(event)'
+    +'{'
+    +'  is(JSON.stringify(event.data), JSON.stringify(data), "Received the expected message data");'
+    +'  done();'
+    +'}'
+    +'win.postMessage(data, "*")'
+    , sandbox);
+  }
+
+  addLoadEvent(doTest);
+</script>
+</head>
+<body>
+<iframe src="http://mochi.test:8888/"
+  id="sameDomain" name="sameDomain">
+</iframe>
+</body>
+</html>
+
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -218,16 +218,18 @@ bool Base64Decode(JSContext *cx, JS::Val
 /**
  * Convert an nsString to jsval, returning true on success.
  * Note, the ownership of the string buffer may be moved from str to rval.
  * If that happens, str will point to an empty string after this call.
  */
 bool StringToJsval(JSContext *cx, nsAString &str, JS::Value *rval);
 bool NonVoidStringToJsval(JSContext *cx, nsAString &str, JS::Value *rval);
 
+nsIPrincipal *GetCompartmentPrincipal(JSCompartment *compartment);
+
 #ifdef DEBUG
 void DumpJSHeap(FILE* file);
 #endif
 } // namespace xpc
 
 class nsIMemoryMultiReporterCallback;
 
 namespace mozilla {
--- a/js/xpconnect/wrappers/AccessCheck.h
+++ b/js/xpconnect/wrappers/AccessCheck.h
@@ -40,19 +40,16 @@
 #include "jsapi.h"
 #include "jswrapper.h"
 #include "WrapperFactory.h"
 
 class nsIPrincipal;
 
 namespace xpc {
 
-nsIPrincipal *
-GetCompartmentPrincipal(JSCompartment *compartment);
-
 class AccessCheck {
   public:
     static bool isSameOrigin(JSCompartment *a, JSCompartment *b);
     static bool isChrome(JSCompartment *compartment);
     static nsIPrincipal *getPrincipal(JSCompartment *compartment);
     static bool isCrossOriginAccessPermitted(JSContext *cx, JSObject *obj, jsid id,
                                              js::Wrapper::Action act);
     static bool isSystemOnlyAccessPermitted(JSContext *cx);