Bug 673742: Allow postMessage()ing File and FileLists between same-origin windows. r=sicking
authorKyle Huey <khuey@kylehuey.com>
Fri, 12 Aug 2011 12:58:43 -0400
changeset 74334 31eddfef6549ba56a9f75248f50d671f934773e4
parent 74333 1152582647874054db5be23554d9986860bf0ac9
child 74335 6de555980733e2838d081c6532e33b83925c25c2
child 75292 2fed775753c7c731c1f0852358d4ac35cebba563
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewerssicking
bugs673742
milestone8.0a1
Bug 673742: Allow postMessage()ing File and FileLists between same-origin windows. r=sicking
dom/base/Makefile.in
dom/base/StructuredCloneTags.h
dom/base/nsGlobalWindow.cpp
--- a/dom/base/Makefile.in
+++ b/dom/base/Makefile.in
@@ -83,16 +83,21 @@ EXPORTS = \
   nsPIWindowRoot.h \
   nsFocusManager.h \
   nsWrapperCache.h \
   nsContentPermissionHelper.h \
   nsStructuredCloneContainer.h \
   nsDOMMemoryReporter.h \
   $(NULL)
 
+EXPORTS_NAMESPACES = mozilla/dom
+EXPORTS_mozilla/dom = \
+  StructuredCloneTags.h \
+  $(NULL)
+
 CPPSRCS =			\
 	nsBarProps.cpp          \
 	nsDOMException.cpp 	\
 	nsDOMWindowUtils.cpp 	\
 	nsJSEnvironment.cpp	\
 	nsJSTimeoutHandler.cpp	\
 	nsFocusManager.cpp \
 	nsGlobalWindow.cpp      \
new file mode 100644
--- /dev/null
+++ b/dom/base/StructuredCloneTags.h
@@ -0,0 +1,55 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Structured Clone Code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Kyle Huey <me@kylehuey.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef StructuredCloneTags_h__
+#define StructuredCloneTags_h__
+
+#include "jsapi.h"
+
+namespace mozilla {
+namespace dom {
+
+enum StructuredCloneTags {
+  SCTAG_BASE = JS_SCTAG_USER_MIN,
+  SCTAG_DOM_BLOB,
+  SCTAG_DOM_FILELIST,
+  SCTAG_DOM_MAX
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // StructuredCloneTags_h__
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -174,16 +174,17 @@
 #include "nsIControllers.h"
 #include "nsIControllerContext.h"
 #include "nsGlobalWindowCommands.h"
 #include "nsAutoPtr.h"
 #include "nsContentUtils.h"
 #include "nsCSSProps.h"
 #include "nsFileDataProtocolHandler.h"
 #include "nsIDOMFile.h"
+#include "nsIDOMFileList.h"
 #include "nsIURIFixup.h"
 #include "mozilla/FunctionTimer.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsEventDispatcher.h"
 #include "nsIObserverService.h"
 #include "nsIXULAppInfo.h"
 #include "nsNetUtil.h"
 #include "nsFocusManager.h"
@@ -235,16 +236,18 @@
 #define FORCE_PR_LOG 1
 #endif
 #include "prlog.h"
 #include "prenv.h"
 
 #include "mozilla/dom/indexedDB/IDBFactory.h"
 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
 
+#include "mozilla/dom/StructuredCloneTags.h"
+
 #include "nsRefreshDriver.h"
 #include "mozAutoDocUpdate.h"
 
 #include "mozilla/Telemetry.h"
 #include "nsLocation.h"
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDOMLeakPRLog;
@@ -5906,17 +5909,16 @@ nsGlobalWindow::CallerInnerWindow()
   // raw pointer here and let the QI's addref be balanced by the nsCOMPtr
   // destructor's release.
   nsCOMPtr<nsPIDOMWindow> win = do_QueryWrappedNative(wrapper);
   if (!win)
     return GetCurrentInnerWindowInternal();
   return static_cast<nsGlobalWindow*>(win.get());
 }
 
-
 /**
  * Class used to represent events generated by calls to Window.postMessage,
  * which asynchronously creates and dispatches events.
  */
 class PostMessageEvent : public nsRunnable
 {
   public:
     NS_DECL_NSIRUNNABLE
@@ -5944,26 +5946,126 @@ class PostMessageEvent : public nsRunnab
     }
 
     void SetJSData(JSAutoStructuredCloneBuffer& aBuffer)
     {
       NS_ASSERTION(!mMessage && mMessageLen == 0, "Don't call twice!");
       aBuffer.steal(&mMessage, &mMessageLen);
     }
 
+    bool StoreISupports(nsISupports* aSupports)
+    {
+      mSupportsArray.AppendElement(aSupports);
+      return true;
+    }
+
   private:
     nsRefPtr<nsGlobalWindow> mSource;
     nsString mCallerOrigin;
     JSUint64* mMessage;
     size_t mMessageLen;
     nsRefPtr<nsGlobalWindow> mTargetWindow;
     nsCOMPtr<nsIURI> mProvidedOrigin;
     PRBool mTrustedCaller;
+    nsTArray<nsCOMPtr<nsISupports> > mSupportsArray;
 };
 
+namespace {
+
+struct StructuredCloneInfo {
+  PostMessageEvent* event;
+  PRBool subsumes;
+};
+
+static JSObject*
+PostMessageReadStructuredClone(JSContext* cx,
+                               JSStructuredCloneReader* reader,
+                               uint32 tag,
+                               uint32 data,
+                               void* closure)
+{
+  StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
+  NS_ASSERTION(scInfo, "Must have scInfo!");
+
+  if (tag == SCTAG_DOM_BLOB || tag == SCTAG_DOM_FILELIST) {
+    NS_ASSERTION(!data, "Data should be empty");
+
+    nsISupports* supports;
+    if (JS_ReadBytes(reader, &supports, sizeof(supports))) {
+      JSObject* global = JS_GetGlobalForObject(cx, JS_GetScopeChain(cx));
+      if (global) {
+        jsval val;
+        nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
+        if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, global, supports,
+                                                    &val,
+                                                    getter_AddRefs(wrapper)))) {
+          return JSVAL_TO_OBJECT(val);
+        }
+      }
+    }
+  }
+
+  const JSStructuredCloneCallbacks* runtimeCallbacks =
+    cx->runtime->structuredCloneCallbacks;
+
+  if (runtimeCallbacks) {
+    return runtimeCallbacks->read(cx, reader, tag, data, nsnull);
+  }
+
+  return JS_FALSE;
+}
+
+static JSBool
+PostMessageWriteStructuredClone(JSContext* cx,
+                                JSStructuredCloneWriter* writer,
+                                JSObject* obj,
+                                void *closure)
+{
+  StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
+  NS_ASSERTION(scInfo, "Must have scInfo!");
+
+  nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
+  nsContentUtils::XPConnect()->
+    GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
+  if (wrappedNative) {
+    PRUint32 scTag = 0;
+    nsISupports* supports = wrappedNative->Native();
+
+    nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(supports);
+    if (blob && scInfo->subsumes)
+      scTag = SCTAG_DOM_BLOB;
+
+    nsCOMPtr<nsIDOMFileList> list = do_QueryInterface(supports);
+    if (list && scInfo->subsumes)
+      scTag = SCTAG_DOM_FILELIST;
+
+    if (scTag)
+      return JS_WriteUint32Pair(writer, scTag, 0) &&
+             JS_WriteBytes(writer, &supports, sizeof(supports)) &&
+             scInfo->event->StoreISupports(supports);
+  }
+
+  const JSStructuredCloneCallbacks* runtimeCallbacks =
+    cx->runtime->structuredCloneCallbacks;
+
+  if (runtimeCallbacks) {
+    return runtimeCallbacks->write(cx, writer, obj, nsnull);
+  }
+
+  return JS_FALSE;
+}
+
+JSStructuredCloneCallbacks kPostMessageCallbacks = {
+  PostMessageReadStructuredClone,
+  PostMessageWriteStructuredClone,
+  nsnull
+};
+
+} // anonymous namespace
+
 NS_IMETHODIMP
 PostMessageEvent::Run()
 {
   NS_ABORT_IF_FALSE(mTargetWindow->IsOuterWindow(),
                     "should have been passed an outer window!");
   NS_ABORT_IF_FALSE(!mSource || mSource->IsOuterWindow(),
                     "should have been passed an outer window!");
 
@@ -6041,18 +6143,20 @@ PostMessageEvent::Run()
     if (NS_FAILED(rv))
       return NS_OK;
   }
 
   // Deserialize the structured clone data
   jsval messageData;
   {
     JSAutoRequest ar(cx);
-
-    if (!buffer.read(cx, &messageData, nsnull))
+    StructuredCloneInfo scInfo;
+    scInfo.event = this;
+
+    if (!buffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo))
       return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
 
   // Create the event
   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(targetWindow->mDocument);
   if (!domDoc)
     return NS_OK;
   nsCOMPtr<nsIDOMEvent> event;
@@ -6168,18 +6272,24 @@ nsGlobalWindow::PostMessageMoz(const jsv
                          origin,
                          this,
                          providedOrigin,
                          nsContentUtils::IsCallerTrustedForWrite());
 
   // We *must* clone the data here, or the jsval could be modified
   // by script
   JSAutoStructuredCloneBuffer buffer;
-
-  if (!buffer.write(aCx, aMessage, nsnull, nsnull))
+  StructuredCloneInfo scInfo;
+  scInfo.event = event;
+
+  nsIPrincipal* principal = GetPrincipal();
+  if (NS_FAILED(callerPrin->Subsumes(principal, &scInfo.subsumes)))
+    return NS_ERROR_DOM_DATA_CLONE_ERR;
+
+  if (!buffer.write(aCx, aMessage, &kPostMessageCallbacks, &scInfo))
     return NS_ERROR_DOM_DATA_CLONE_ERR;
 
   event->SetJSData(buffer);
 
   return NS_DispatchToCurrentThread(event);
 }
 
 class nsCloseEvent : public nsRunnable {