Bug 664783 - Implement FileReaderSync for Workers. r=bent+sicking.
authorWilliam Chen <wchen@mozilla.com>
Mon, 15 Aug 2011 20:40:38 -0700
changeset 75356 1ad8506a40d295eea16c52a0ec5ba65c3ba8bd28
parent 75355 e5c1fbc8df7ed2ebea6a8763b47f73eb7d776112
child 75357 4e895b191eb29410bb522a90f9496b6c33f2c6ec
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewersbent
bugs664783
milestone8.0a1
Bug 664783 - Implement FileReaderSync for Workers. r=bent+sicking.
content/base/src/nsDOMFile.cpp
dom/workers/Events.cpp
dom/workers/Events.h
dom/workers/Exceptions.cpp
dom/workers/Exceptions.h
dom/workers/File.cpp
dom/workers/File.h
dom/workers/FileReaderSync.cpp
dom/workers/FileReaderSync.h
dom/workers/FileReaderSyncPrivate.cpp
dom/workers/FileReaderSyncPrivate.h
dom/workers/Makefile.in
dom/workers/RuntimeService.cpp
dom/workers/RuntimeService.h
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
dom/workers/WorkerScope.cpp
dom/workers/test/Makefile.in
dom/workers/test/fileBlobSubWorker_worker.js
dom/workers/test/fileBlob_worker.js
dom/workers/test/fileMozSlice_worker.js
dom/workers/test/filePosting_worker.js
dom/workers/test/fileReadMozSlice_worker.js
dom/workers/test/fileReaderSyncErrors_worker.js
dom/workers/test/fileReaderSync_worker.js
dom/workers/test/fileSubWorker_worker.js
dom/workers/test/file_worker.js
dom/workers/test/test_file.xul
dom/workers/test/test_fileBlobPosting.xul
dom/workers/test/test_fileBlobSubWorker.xul
dom/workers/test/test_fileMozSlice.xul
dom/workers/test/test_filePosting.xul
dom/workers/test/test_fileReadMozSlice.xul
dom/workers/test/test_fileReaderSync.xul
dom/workers/test/test_fileReaderSyncErrors.xul
dom/workers/test/test_fileSubWorker.xul
--- a/content/base/src/nsDOMFile.cpp
+++ b/content/base/src/nsDOMFile.cpp
@@ -143,31 +143,38 @@ NS_INTERFACE_MAP_BEGIN(nsDOMFileBase)
   NS_INTERFACE_MAP_ENTRY(nsIDOMBlob)
   NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile)
   NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
   NS_INTERFACE_MAP_ENTRY(nsIMutable)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, mIsFile)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !mIsFile)
 NS_INTERFACE_MAP_END
 
-NS_IMPL_ADDREF(nsDOMFileBase)
-NS_IMPL_RELEASE(nsDOMFileBase)
+// Threadsafe when GetMutable() == PR_FALSE
+NS_IMPL_THREADSAFE_ADDREF(nsDOMFileBase)
+NS_IMPL_THREADSAFE_RELEASE(nsDOMFileBase)
 
 NS_IMETHODIMP
 nsDOMFileBase::GetName(nsAString &aFileName)
 {
   NS_ASSERTION(mIsFile, "Should only be called on files");
   aFileName = mName;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMFileBase::GetMozFullPath(nsAString &aFileName)
 {
   NS_ASSERTION(mIsFile, "Should only be called on files");
+
+  // It is unsafe to call IsCallerTrustedForCapability on a non-main thread. If
+  // you hit the following assertion you need to figure out some other way to
+  // determine privileges and call GetMozFullPathInternal.
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
   if (nsContentUtils::IsCallerTrustedForCapability("UniversalFileRead")) {
     return GetMozFullPathInternal(aFileName);
   }
   aFileName.Truncate();
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/dom/workers/Events.cpp
+++ b/dom/workers/Events.cpp
@@ -1,9 +1,10 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
 /* ***** 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/
  *
@@ -40,16 +41,17 @@
 
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsobj.h"
 
 #include "nsTraceRefcnt.h"
 
 #include "WorkerInlines.h"
+#include "WorkerPrivate.h"
 
 #define PROPERTY_FLAGS \
   JSPROP_ENUMERATE | JSPROP_SHARED
 
 #define FUNCTION_FLAGS \
   JSPROP_ENUMERATE
 
 #define CONSTANT_FLAGS \
@@ -410,16 +412,18 @@ class MessageEvent : public Event
   static JSClass sMainRuntimeClass;
 
   static JSPropertySpec sProperties[];
   static JSFunctionSpec sFunctions[];
 
 protected:
   uint64* mData;
   size_t mDataByteCount;
+  nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
+  bool mMainRuntime;
 
 public:
   static bool
   IsThisClass(JSClass* aClass)
   {
     return aClass == &sClass || aClass == &sMainRuntimeClass;
   }
 
@@ -430,46 +434,48 @@ public:
     JSClass* clasp = aMainRuntime ? &sMainRuntimeClass : &sClass;
 
     return JS_InitClass(aCx, aObj, aParentProto, clasp, Construct, 0,
                         sProperties, sFunctions, NULL, NULL);
   }
 
   static JSObject*
   Create(JSContext* aCx, JSObject* aParent, JSAutoStructuredCloneBuffer& aData,
-         bool aMainRuntime)
+         nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects, bool aMainRuntime)
   {
     JSString* type = JS_InternString(aCx, "message");
     if (!type) {
       return NULL;
     }
 
     JSClass* clasp = aMainRuntime ? &sMainRuntimeClass : &sClass;
 
     JSObject* obj = JS_NewObject(aCx, clasp, NULL, aParent);
     if (!obj) {
       return NULL;
     }
 
-    MessageEvent* priv = new MessageEvent();
+    MessageEvent* priv = new MessageEvent(aMainRuntime);
     if (!SetJSPrivateSafeish(aCx, obj, priv) ||
         !InitMessageEventCommon(aCx, obj, priv, type, false, false, NULL, NULL,
                                 NULL, true)) {
       SetJSPrivateSafeish(aCx, obj, NULL);
       delete priv;
       return NULL;
     }
 
     aData.steal(&priv->mData, &priv->mDataByteCount);
+    priv->mClonedObjects.SwapElements(aClonedObjects);
+
     return obj;
   }
 
 protected:
-  MessageEvent()
-  : mData(NULL), mDataByteCount(0)
+  MessageEvent(bool aMainRuntime)
+  : mData(NULL), mDataByteCount(0), mMainRuntime(aMainRuntime)
   {
     MOZ_COUNT_CTOR(mozilla::dom::workers::MessageEvent);
   }
 
   virtual ~MessageEvent()
   {
     MOZ_COUNT_DTOR(mozilla::dom::workers::MessageEvent);
     JS_ASSERT(!mData);
@@ -565,18 +571,24 @@ private:
     // Deserialize and save the data value if we can.
     if (slot == SLOT_data && event->mData) {
       JSAutoStructuredCloneBuffer buffer;
       buffer.adopt(event->mData, event->mDataByteCount);
 
       event->mData = NULL;
       event->mDataByteCount = 0;
 
+      // Release reference to objects that were AddRef'd for
+      // cloning into worker when array goes out of scope.
+      nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
+      clonedObjects.SwapElements(event->mClonedObjects);
+
       jsval data;
-      if (!buffer.read(aCx, &data) ||
+      if (!buffer.read(aCx, &data,
+                       WorkerStructuredCloneCallbacks(event->mMainRuntime)) ||
           !JS_SetReservedSlot(aCx, aObj, slot, data)) {
         return false;
       }
 
       *aVp = data;
       return true;
     }
 
@@ -1052,20 +1064,21 @@ CreateGenericEvent(JSContext* aCx, JSStr
                    bool aCancelable, bool aMainRuntime)
 {
   JSObject* global = JS_GetGlobalForScopeChain(aCx);
   return Event::Create(aCx, global, aType, aBubbles, aCancelable, aMainRuntime);
 }
 
 JSObject*
 CreateMessageEvent(JSContext* aCx, JSAutoStructuredCloneBuffer& aData,
+                   nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
                    bool aMainRuntime)
 {
   JSObject* global = JS_GetGlobalForScopeChain(aCx);
-  return MessageEvent::Create(aCx, global, aData, aMainRuntime);
+  return MessageEvent::Create(aCx, global, aData, aClonedObjects, aMainRuntime);
 }
 
 JSObject*
 CreateErrorEvent(JSContext* aCx, JSString* aMessage, JSString* aFilename,
                  uint32 aLineNumber, bool aMainRuntime)
 {
   JSObject* global = JS_GetGlobalForScopeChain(aCx);
   return ErrorEvent::Create(aCx, global, aMessage, aFilename, aLineNumber,
--- a/dom/workers/Events.h
+++ b/dom/workers/Events.h
@@ -37,32 +37,35 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef mozilla_dom_workers_events_h__
 #define mozilla_dom_workers_events_h__
 
 #include "Workers.h"
 
 #include "jspubtd.h"
+#include "nsTArray.h"
+#include "nsCOMPtr.h"
 
 class JSAutoStructuredCloneBuffer;
 
 BEGIN_WORKERS_NAMESPACE
 
 namespace events {
 
 bool
 InitClasses(JSContext* aCx, JSObject* aGlobal, bool aMainRuntime);
 
 JSObject*
 CreateGenericEvent(JSContext* aCx, JSString* aType, bool aBubbles,
                    bool aCancelable, bool aMainRuntime);
 
 JSObject*
 CreateMessageEvent(JSContext* aCx, JSAutoStructuredCloneBuffer& aData,
+                   nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
                    bool aMainRuntime);
 
 JSObject*
 CreateErrorEvent(JSContext* aCx, JSString* aMessage, JSString* aFilename,
                  uint32 aLineNumber, bool aMainRuntime);
 
 JSObject*
 CreateProgressEvent(JSContext* aCx, JSString* aType, bool aLengthComputable,
--- a/dom/workers/Exceptions.cpp
+++ b/dom/workers/Exceptions.cpp
@@ -1,9 +1,10 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
 /* ***** 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/
  *
@@ -184,17 +185,16 @@ private:
 JSClass DOMException::sClass = {
   "DOMException",
   JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT),
   JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
   JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
-
 JSPropertySpec DOMException::sProperties[] = {
   { "code", SLOT_code, PROPERTY_FLAGS, GetProperty, js_GetterOnlyPropertyStub },
   { "name", SLOT_name, PROPERTY_FLAGS, GetProperty, js_GetterOnlyPropertyStub },
   { 0, 0, 0, NULL, NULL }
 };
 
 JSFunctionSpec DOMException::sFunctions[] = {
   JS_FN("toString", ToString, 0, 0),
@@ -272,32 +272,192 @@ DOMException::Create(JSContext* aCx, int
   if (!SetJSPrivateSafeish(aCx, obj, priv)) {
     delete priv;
     return NULL;
   }
 
   return obj;
 }
 
+class FileException : public PrivatizableBase
+{
+  static JSClass sClass;
+  static JSPropertySpec sProperties[];
+  static JSPropertySpec sStaticProperties[];
+
+  enum SLOT {
+    SLOT_code = 0,
+    SLOT_name,
+
+    SLOT_COUNT
+  };
+
+public:
+  static JSObject*
+  InitClass(JSContext* aCx, JSObject* aObj)
+  {
+    return JS_InitClass(aCx, aObj, NULL, &sClass, Construct, 0, sProperties,
+                        NULL, sStaticProperties, NULL);
+  }
+
+  static JSObject*
+  Create(JSContext* aCx, intN aCode);
+
+private:
+  FileException()
+  {
+    MOZ_COUNT_CTOR(mozilla::dom::workers::exceptions::FileException);
+  }
+
+  ~FileException()
+  {
+    MOZ_COUNT_DTOR(mozilla::dom::workers::exceptions::FileException);
+  }
+
+  static JSBool
+  Construct(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
+                         sClass.name);
+    return false;
+  }
+
+  static void
+  Finalize(JSContext* aCx, JSObject* aObj)
+  {
+    JS_ASSERT(JS_GET_CLASS(aCx, aObj) == &sClass);
+    delete GetJSPrivateSafeish<FileException>(aCx, aObj);
+  }
+
+  static JSBool
+  GetProperty(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
+  {
+    JS_ASSERT(JSID_IS_INT(aIdval));
+
+    int32 slot = JSID_TO_INT(aIdval);
+
+    JSClass* classPtr = JS_GET_CLASS(aCx, aObj);
+
+    if (classPtr != &sClass ||
+        !GetJSPrivateSafeish<FileException>(aCx, aObj)) {
+      JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL,
+                           JSMSG_INCOMPATIBLE_PROTO, sClass.name,
+                           sProperties[slot].name,
+                           classPtr ? classPtr->name : "object");
+      return false;
+    }
+
+    return JS_GetReservedSlot(aCx, aObj, slot, aVp);
+  }
+
+  static JSBool
+  GetConstant(JSContext* aCx, JSObject* aObj, jsid idval, jsval* aVp)
+  {
+    JS_ASSERT(JSID_IS_INT(idval));
+    *aVp = INT_TO_JSVAL(JSID_TO_INT(idval));
+    return true;
+  }
+};
+
+JSClass FileException::sClass = {
+  "FileException",
+  JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT),
+  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
+  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
+  JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+JSPropertySpec FileException::sProperties[] = {
+  { "code", SLOT_code, PROPERTY_FLAGS, GetProperty, js_GetterOnlyPropertyStub },
+  { "name", SLOT_name, PROPERTY_FLAGS, GetProperty, js_GetterOnlyPropertyStub },
+  { 0, 0, 0, NULL, NULL }
+};
+
+JSPropertySpec FileException::sStaticProperties[] = {
+
+#define EXCEPTION_ENTRY(_name) \
+  { #_name, FILE_##_name, CONSTANT_FLAGS, GetConstant, NULL },
+
+  EXCEPTION_ENTRY(NOT_FOUND_ERR)
+  EXCEPTION_ENTRY(SECURITY_ERR)
+  EXCEPTION_ENTRY(ABORT_ERR)
+  EXCEPTION_ENTRY(NOT_READABLE_ERR)
+  EXCEPTION_ENTRY(ENCODING_ERR)
+
+#undef EXCEPTION_ENTRY
+
+  { 0, 0, 0, NULL, NULL }
+};
+
+// static
+JSObject*
+FileException::Create(JSContext* aCx, intN aCode)
+{
+  JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL);
+  if (!obj) {
+    return NULL;
+  }
+
+  size_t foundIndex = size_t(-1);
+  for (size_t index = 0;
+       index < JS_ARRAY_LENGTH(sStaticProperties) - 1;
+       index++) {
+    if (sStaticProperties[index].tinyid == aCode) {
+      foundIndex = index;
+      break;
+    }
+  }
+
+  JS_ASSERT(foundIndex != size_t(-1));
+
+  JSString* name = JS_NewStringCopyZ(aCx, sStaticProperties[foundIndex].name);
+  if (!name) {
+    return NULL;
+  }
+
+  if (!JS_SetReservedSlot(aCx, obj, SLOT_code, INT_TO_JSVAL(aCode)) ||
+      !JS_SetReservedSlot(aCx, obj, SLOT_name, STRING_TO_JSVAL(name))) {
+    return NULL;
+  }
+
+  FileException* priv = new FileException();
+  if (!SetJSPrivateSafeish(aCx, obj, priv)) {
+    delete priv;
+    return NULL;
+  }
+
+  return obj;
+}
+
 } // anonymous namespace
 
 BEGIN_WORKERS_NAMESPACE
 
 namespace exceptions {
 
 bool
 InitClasses(JSContext* aCx, JSObject* aGlobal)
 {
-  return !!DOMException::InitClass(aCx, aGlobal);
+  return DOMException::InitClass(aCx, aGlobal) &&
+         FileException::InitClass(aCx, aGlobal);
 }
 
 void
 ThrowDOMExceptionForCode(JSContext* aCx, intN aCode)
 {
   JSObject* exception = DOMException::Create(aCx, aCode);
   JS_ASSERT(exception);
 
   JS_SetPendingException(aCx, OBJECT_TO_JSVAL(exception));
 }
 
+void
+ThrowFileExceptionForCode(JSContext* aCx, intN aCode)
+{
+  JSObject* exception = FileException::Create(aCx, aCode);
+  JS_ASSERT(exception);
+
+  JS_SetPendingException(aCx, OBJECT_TO_JSVAL(exception));
+}
+
 } // namespace exceptions
 
 END_WORKERS_NAMESPACE
--- a/dom/workers/Exceptions.h
+++ b/dom/workers/Exceptions.h
@@ -1,9 +1,10 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
 /* ***** 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/
  *
@@ -38,16 +39,17 @@
 
 #ifndef mozilla_dom_workers_exceptions_h__
 #define mozilla_dom_workers_exceptions_h__
 
 #include "Workers.h"
 
 #include "jspubtd.h"
 
+// DOMException Codes.
 #define INDEX_SIZE_ERR 1
 #define DOMSTRING_SIZE_ERR 2
 #define HIERARCHY_REQUEST_ERR 3
 #define WRONG_DOCUMENT_ERR 4
 #define INVALID_CHARACTER_ERR 5
 #define NO_DATA_ALLOWED_ERR 6
 #define NO_MODIFICATION_ALLOWED_ERR 7
 #define NOT_FOUND_ERR 8
@@ -64,23 +66,33 @@
 #define NETWORK_ERR 19
 #define ABORT_ERR 20
 #define URL_MISMATCH_ERR 21
 #define QUOTA_EXCEEDED_ERR 22
 #define TIMEOUT_ERR 23
 #define INVALID_NODE_TYPE_ERR 24
 #define DATA_CLONE_ERR 25
 
+// FileException Codes
+#define FILE_NOT_FOUND_ERR 1
+#define FILE_SECURITY_ERR 2
+#define FILE_ABORT_ERR 3
+#define FILE_NOT_READABLE_ERR 4
+#define FILE_ENCODING_ERR 5
+
 BEGIN_WORKERS_NAMESPACE
 
 namespace exceptions {
 
 bool
 InitClasses(JSContext* aCx, JSObject* aGlobal);
 
 void
 ThrowDOMExceptionForCode(JSContext* aCx, intN aCode);
 
+void
+ThrowFileExceptionForCode(JSContext* aCx, intN aCode);
+
 } // namespace exceptions
 
 END_WORKERS_NAMESPACE
 
 #endif // mozilla_dom_workers_exceptions_h__
new file mode 100644
--- /dev/null
+++ b/dom/workers/File.cpp
@@ -0,0 +1,452 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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 Web Workers.
+ *
+ * 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):
+ *   William Chen <wchen@mozilla.com> (Original Author)
+ *
+ * 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 ***** */
+
+#include "File.h"
+
+#include "nsIDOMFile.h"
+
+#include "jsapi.h"
+#include "jsatom.h"
+#include "jscntxt.h"
+#include "nsCOMPtr.h"
+#include "nsJSUtils.h"
+#include "nsStringGlue.h"
+#include "xpcprivate.h"
+#include "xpcquickstubs.h"
+
+#include "Exceptions.h"
+#include "WorkerInlines.h"
+#include "WorkerPrivate.h"
+
+#define PROPERTY_FLAGS \
+  JSPROP_ENUMERATE | JSPROP_SHARED
+
+USING_WORKERS_NAMESPACE
+
+using mozilla::dom::workers::exceptions::ThrowFileExceptionForCode;
+
+namespace {
+
+class Blob
+{
+  // Blob should never be instantiated.
+  Blob();
+  ~Blob();
+
+  static JSClass sClass;
+  static JSPropertySpec sProperties[];
+  static JSFunctionSpec sFunctions[];
+
+public:
+  static JSObject*
+  InitClass(JSContext* aCx, JSObject* aObj)
+  {
+    return JS_InitClass(aCx, aObj, NULL, &sClass, Construct, 0, sProperties,
+                        sFunctions, NULL, NULL);
+  }
+
+  static JSObject*
+  Create(JSContext* aCx, nsIDOMBlob* aBlob)
+  {
+    JS_ASSERT(SameCOMIdentity(static_cast<nsISupports*>(aBlob), aBlob));
+
+    JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL);
+    if (obj) {
+      if (!JS_SetPrivate(aCx, obj, aBlob)) {
+        return NULL;
+      }
+      NS_ADDREF(aBlob);
+    }
+    return obj;
+  }
+
+  static nsIDOMBlob*
+  GetPrivate(JSContext* aCx, JSObject* aObj);
+
+private:
+  static nsIDOMBlob*
+  GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
+  {
+    JSClass* classPtr = NULL;
+
+    if (aObj) {
+      nsIDOMBlob* blob = GetPrivate(aCx, aObj);
+      if (blob) {
+        return blob;
+      }
+
+      classPtr = JS_GET_CLASS(aCx, aObj);
+    }
+
+    JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL,
+                         JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName,
+                         classPtr ? classPtr->name : "Object");
+    return NULL;
+  }
+
+  static JSBool
+  Construct(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
+                         sClass.name);
+    return false;
+  }
+
+  static void
+  Finalize(JSContext* aCx, JSObject* aObj)
+  {
+    JS_ASSERT(JS_GET_CLASS(aCx, aObj) == &sClass);
+
+    nsIDOMBlob* blob = GetPrivate(aCx, aObj);
+    NS_IF_RELEASE(blob);
+  }
+
+  static JSBool
+  GetSize(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
+  {
+    nsIDOMBlob* blob = GetInstancePrivate(aCx, aObj, "size");
+    if (!blob) {
+      return false;
+    }
+
+    PRUint64 size;
+    if (NS_FAILED(blob->GetSize(&size))) {
+      ThrowFileExceptionForCode(aCx, FILE_NOT_READABLE_ERR);
+    }
+
+    if (!JS_NewNumberValue(aCx, jsdouble(size), aVp)) {
+      return false;
+    }
+
+    return true;
+  }
+
+  static JSBool
+  GetType(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
+  {
+    nsIDOMBlob* blob = GetInstancePrivate(aCx, aObj, "type");
+    if (!blob) {
+      return false;
+    }
+
+    nsString type;
+    if (NS_FAILED(blob->GetType(type))) {
+      ThrowFileExceptionForCode(aCx, FILE_NOT_READABLE_ERR);
+    }
+
+    JSString* jsType = JS_NewUCStringCopyN(aCx, type.get(), type.Length());
+    if (!jsType) {
+      return false;
+    }
+
+    *aVp = STRING_TO_JSVAL(jsType);
+
+    return true;
+  }
+
+  static JSBool
+  MozSlice(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+
+    nsIDOMBlob* blob = GetInstancePrivate(aCx, obj, "mozSlice");
+    if (!blob) {
+      return false;
+    }
+
+    jsdouble start = 0, end = 0;
+    JSString* jsContentType = JS_GetEmptyString(JS_GetRuntime(aCx));
+    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "/IIS", &start,
+                             &end, &jsContentType)) {
+      return false;
+    }
+
+    nsDependentJSString contentType;
+    if (!contentType.init(aCx, jsContentType)) {
+      return false;
+    }
+
+    PRUint8 optionalArgc = aArgc;
+    nsCOMPtr<nsIDOMBlob> rtnBlob;
+    if (NS_FAILED(blob->MozSlice(xpc_qsDoubleToUint64(start),
+                                 xpc_qsDoubleToUint64(end),
+                                 contentType, optionalArgc,
+                                 getter_AddRefs(rtnBlob)))) {
+      ThrowFileExceptionForCode(aCx, FILE_NOT_READABLE_ERR);
+      return false;
+    }
+
+    JSObject* rtnObj = file::CreateBlob(aCx, rtnBlob);
+    if (!rtnObj) {
+      return false;
+    }
+
+    JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(rtnObj));
+    return true;
+  }
+};
+
+JSClass Blob::sClass = {
+  "Blob",
+  JSCLASS_HAS_PRIVATE,
+  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
+  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
+  JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+JSPropertySpec Blob::sProperties[] = {
+  { "size", 0, PROPERTY_FLAGS, GetSize, js_GetterOnlyPropertyStub },
+  { "type", 0, PROPERTY_FLAGS, GetType, js_GetterOnlyPropertyStub },
+  { 0, 0, 0, NULL, NULL }
+};
+
+JSFunctionSpec Blob::sFunctions[] = {
+  JS_FN("mozSlice", MozSlice, 1, JSPROP_ENUMERATE),
+  JS_FS_END
+};
+
+class File : public Blob
+{
+  // File should never be instantiated.
+  File();
+  ~File();
+
+  static JSClass sClass;
+  static JSPropertySpec sProperties[];
+
+public:
+  static JSObject*
+  InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto)
+  {
+    return JS_InitClass(aCx, aObj, aParentProto, &sClass, Construct, 0,
+                        sProperties, NULL, NULL, NULL);
+  }
+
+  static JSObject*
+  Create(JSContext* aCx, nsIDOMFile* aFile)
+  {
+    JS_ASSERT(SameCOMIdentity(static_cast<nsISupports*>(aFile), aFile));
+
+    JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL);
+    if (obj) {
+      if (!JS_SetPrivate(aCx, obj, aFile)) {
+        return NULL;
+      }
+      NS_ADDREF(aFile);
+    }
+    return obj;
+  }
+
+  static nsIDOMFile*
+  GetPrivate(JSContext* aCx, JSObject* aObj)
+  {
+    if (aObj) {
+      JSClass* classPtr = JS_GET_CLASS(aCx, aObj);
+      if (classPtr == &sClass) {
+        nsISupports* priv = static_cast<nsISupports*>(JS_GetPrivate(aCx, aObj));
+        nsCOMPtr<nsIDOMFile> file = do_QueryInterface(priv);
+        JS_ASSERT_IF(priv, file);
+        return file;
+      }
+    }
+    return NULL;
+  }
+
+  static JSClass*
+  Class()
+  {
+    return &sClass;
+  }
+
+private:
+  static nsIDOMFile*
+  GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
+  {
+    JSClass* classPtr = NULL;
+
+    if (aObj) {
+      nsIDOMFile* file = GetPrivate(aCx, aObj);
+      if (file) {
+        return file;
+      }
+      classPtr = JS_GET_CLASS(aCx, aObj);
+    }
+
+    JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL,
+                         JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName,
+                         classPtr ? classPtr->name : "Object");
+    return NULL;
+  }
+
+  static JSBool
+  Construct(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
+                         sClass.name);
+    return false;
+  }
+
+  static void
+  Finalize(JSContext* aCx, JSObject* aObj)
+  {
+    JS_ASSERT(JS_GET_CLASS(aCx, aObj) == &sClass);
+
+    nsIDOMFile* file = GetPrivate(aCx, aObj);
+    NS_IF_RELEASE(file);
+  }
+
+  static JSBool
+  GetMozFullPath(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
+  {
+    nsIDOMFile* file = GetInstancePrivate(aCx, aObj, "mozFullPath");
+    if (!file) {
+      return false;
+    }
+
+    nsString fullPath;
+
+    if (GetWorkerPrivateFromContext(aCx)->UsesSystemPrincipal() &&
+        NS_FAILED(file->GetMozFullPathInternal(fullPath))) {
+      ThrowFileExceptionForCode(aCx, FILE_NOT_READABLE_ERR);
+      return false;
+    }
+
+    JSString* jsFullPath = JS_NewUCStringCopyN(aCx, fullPath.get(),
+                                               fullPath.Length());
+    if (!jsFullPath) {
+      return false;
+    }
+
+    *aVp = STRING_TO_JSVAL(jsFullPath);
+    return true;
+  }
+
+  static JSBool
+  GetName(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
+  {
+    nsIDOMFile* file = GetInstancePrivate(aCx, aObj, "name");
+    if (!file) {
+      return false;
+    }
+
+    nsString name;
+    if (NS_FAILED(file->GetName(name))) {
+      name.Truncate();
+    }
+
+    JSString* jsName = JS_NewUCStringCopyN(aCx, name.get(), name.Length());
+    if (!jsName) {
+      return false;
+    }
+
+    *aVp = STRING_TO_JSVAL(jsName);
+    return true;
+  }
+};
+
+JSClass File::sClass = {
+  "File",
+  JSCLASS_HAS_PRIVATE,
+  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
+  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
+  JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+JSPropertySpec File::sProperties[] = {
+  { "name", 0, PROPERTY_FLAGS, GetName, js_GetterOnlyPropertyStub },
+  { "mozFullPath", 0, PROPERTY_FLAGS, GetMozFullPath,
+    js_GetterOnlyPropertyStub },
+  { 0, 0, 0, NULL, NULL }
+};
+
+nsIDOMBlob*
+Blob::GetPrivate(JSContext* aCx, JSObject* aObj)
+{
+  if (aObj) {
+    JSClass* classPtr = JS_GET_CLASS(aCx, aObj);
+    if (classPtr == &sClass || classPtr == File::Class()) {
+      nsISupports* priv = static_cast<nsISupports*>(JS_GetPrivate(aCx, aObj));
+      nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(priv);
+      JS_ASSERT_IF(priv, blob);
+      return blob;
+    }
+  }
+  return NULL;
+}
+
+} // anonymous namespace
+
+BEGIN_WORKERS_NAMESPACE
+
+namespace file {
+
+JSObject*
+CreateBlob(JSContext* aCx, nsIDOMBlob* aBlob)
+{
+  return Blob::Create(aCx, aBlob);
+}
+
+bool
+InitClasses(JSContext* aCx, JSObject* aGlobal)
+{
+  JSObject* blobProto = Blob::InitClass(aCx, aGlobal);
+  return blobProto && File::InitClass(aCx, aGlobal, blobProto);
+}
+
+nsIDOMBlob*
+GetDOMBlobFromJSObject(JSContext* aCx, JSObject* aObj)
+{
+  return Blob::GetPrivate(aCx, aObj);
+}
+
+JSObject*
+CreateFile(JSContext* aCx, nsIDOMFile* aFile)
+{
+  return File::Create(aCx, aFile);
+}
+
+nsIDOMFile*
+GetDOMFileFromJSObject(JSContext* aCx, JSObject* aObj)
+{
+  return File::GetPrivate(aCx, aObj);
+}
+
+} // namespace file
+
+END_WORKERS_NAMESPACE
new file mode 100644
--- /dev/null
+++ b/dom/workers/File.h
@@ -0,0 +1,73 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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 Web Workers.
+ *
+ * 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):
+ *   William Chen <wchen@mozilla.com> (Original Author)
+ *
+ * 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 mozilla_dom_workers_file_h__
+#define mozilla_dom_workers_file_h__
+
+#include "Workers.h"
+
+#include "jspubtd.h"
+
+class nsIDOMFile;
+class nsIDOMBlob;
+
+BEGIN_WORKERS_NAMESPACE
+
+namespace file {
+
+bool
+InitClasses(JSContext* aCx, JSObject* aGlobal);
+
+JSObject*
+CreateBlob(JSContext* aCx, nsIDOMBlob* aBlob);
+
+nsIDOMBlob*
+GetDOMBlobFromJSObject(JSContext* aCx, JSObject* aObj);
+
+JSObject*
+CreateFile(JSContext* aCx, nsIDOMFile* aFile);
+
+nsIDOMFile*
+GetDOMFileFromJSObject(JSContext* aCx, JSObject* aObj);
+
+} // namespace file
+
+END_WORKERS_NAMESPACE
+
+#endif /* mozilla_dom_workers_file_h__ */
new file mode 100644
--- /dev/null
+++ b/dom/workers/FileReaderSync.cpp
@@ -0,0 +1,372 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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 Web Workers.
+ *
+ * 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):
+ *   William Chen <wchen@mozilla.com> (Original Author)
+ *
+ * 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 ***** */
+
+#include "FileReaderSync.h"
+
+#include "nsIDOMFile.h"
+
+#include "jsapi.h"
+#include "jsatom.h"
+#include "jscntxt.h"
+#include "jstypedarray.h"
+#include "nsJSUtils.h"
+#include "xpcprivate.h"
+
+#include "Exceptions.h"
+#include "File.h"
+#include "FileReaderSyncPrivate.h"
+#include "WorkerInlines.h"
+
+#define FUNCTION_FLAGS \
+  JSPROP_ENUMERATE
+
+USING_WORKERS_NAMESPACE
+
+using mozilla::dom::workers::exceptions::ThrowFileExceptionForCode;
+using js::ArrayBuffer;
+
+namespace {
+
+inline bool
+EnsureSucceededOrThrow(JSContext* aCx, nsresult rv)
+{
+  if (NS_SUCCEEDED(rv)) {
+    return true;
+  }
+
+  intN code = rv == NS_ERROR_FILE_NOT_FOUND ?
+              FILE_NOT_FOUND_ERR :
+              FILE_NOT_READABLE_ERR;
+  ThrowFileExceptionForCode(aCx, code);
+  return false;
+}
+
+inline nsIDOMBlob*
+GetDOMBlobFromJSObject(JSContext* aCx, JSObject* aObj) {
+  JSClass* classPtr = NULL;
+
+  if (aObj) {
+    nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aCx, aObj);
+    if (blob) {
+      return blob;
+    }
+
+    classPtr = JS_GET_CLASS(aCx, aObj);
+  }
+
+  JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
+                       classPtr ? classPtr->name : "Object", "not a Blob.");
+  return NULL;
+}
+
+class FileReaderSync
+{
+  // FileReaderSync should not be instantiated.
+  FileReaderSync();
+  ~FileReaderSync();
+
+  static JSClass sClass;
+  static JSFunctionSpec sFunctions[];
+
+public:
+  static JSObject*
+  InitClass(JSContext* aCx, JSObject* aObj)
+  {
+    return JS_InitClass(aCx, aObj, NULL, &sClass, Construct, 0,
+                        NULL, sFunctions, NULL, NULL);
+  }
+
+  static FileReaderSyncPrivate*
+  GetPrivate(JSContext* aCx, JSObject* aObj)
+  {
+    if (aObj) {
+      JSClass* classPtr = JS_GET_CLASS(aCx, aObj);
+      if (classPtr == &sClass) {
+        FileReaderSyncPrivate* fileReader =
+          GetJSPrivateSafeish<FileReaderSyncPrivate>(aCx, aObj);
+        return fileReader;
+      }
+    }
+    return NULL;
+  }
+
+private:
+  static FileReaderSyncPrivate*
+  GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
+  {
+    JSClass* classPtr = NULL;
+
+    if (aObj) {
+      FileReaderSyncPrivate* fileReader = GetPrivate(aCx, aObj);
+      if (fileReader) {
+        return fileReader;
+      }
+
+      classPtr = JS_GET_CLASS(aCx, aObj);
+    }
+
+    JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL,
+                         JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName,
+                         classPtr ? classPtr->name : "Object");
+    return NULL;
+  }
+
+  static JSBool
+  Construct(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL);
+    if (!obj) {
+      return false;
+    }
+
+    FileReaderSyncPrivate* fileReader = new FileReaderSyncPrivate();
+    NS_ADDREF(fileReader);
+
+    if (!SetJSPrivateSafeish(aCx, obj, fileReader)) {
+      NS_RELEASE(fileReader);
+      return false;
+    }
+
+    JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(obj));
+    return true;
+  }
+
+  static void
+  Finalize(JSContext* aCx, JSObject* aObj)
+  {
+    JS_ASSERT(JS_GET_CLASS(aCx, aObj) == &sClass);
+    FileReaderSyncPrivate* fileReader =
+      GetJSPrivateSafeish<FileReaderSyncPrivate>(aCx, aObj);
+    NS_IF_RELEASE(fileReader);
+  }
+
+  static JSBool
+  ReadAsArrayBuffer(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+
+    FileReaderSyncPrivate* fileReader =
+      GetInstancePrivate(aCx, obj, "readAsArrayBuffer");
+    if (!fileReader) {
+      return false;
+    }
+
+    JSObject* jsBlob;
+    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o", &jsBlob)) {
+      return false;
+    }
+
+    nsIDOMBlob* blob = GetDOMBlobFromJSObject(aCx, jsBlob);
+    if (!blob) {
+      return false;
+    }
+
+    PRUint64 blobSize;
+    nsresult rv = blob->GetSize(&blobSize);
+    if (!EnsureSucceededOrThrow(aCx, rv)) {
+      return false;
+    }
+
+    JSObject* jsArrayBuffer = js_CreateArrayBuffer(aCx, blobSize);
+    if (!jsArrayBuffer) {
+      return false;
+    }
+
+    JSUint32 bufferLength = JS_GetArrayBufferByteLength(jsArrayBuffer);
+    uint8* arrayBuffer = JS_GetArrayBufferData(jsArrayBuffer);
+
+    rv = fileReader->ReadAsArrayBuffer(blob, bufferLength, arrayBuffer);
+    if (!EnsureSucceededOrThrow(aCx, rv)) {
+      return false;
+    }
+
+    JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(jsArrayBuffer));
+    return true;
+  }
+
+  static JSBool
+  ReadAsDataURL(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+
+    FileReaderSyncPrivate* fileReader =
+      GetInstancePrivate(aCx, obj, "readAsDataURL");
+    if (!fileReader) {
+      return false;
+    }
+
+    JSObject* jsBlob;
+    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o", &jsBlob)) {
+      return false;
+    }
+
+    nsIDOMBlob* blob = GetDOMBlobFromJSObject(aCx, jsBlob);
+    if (!blob) {
+      return false;
+    }
+
+    nsString blobText;
+    nsresult rv = fileReader->ReadAsDataURL(blob, blobText);
+    if (!EnsureSucceededOrThrow(aCx, rv)) {
+      return false;
+    }
+
+    JSString* jsBlobText = JS_NewUCStringCopyN(aCx, blobText.get(),
+                                               blobText.Length());
+    if (!jsBlobText) {
+      return false;
+    }
+
+    JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(jsBlobText));
+    return true;
+  }
+
+  static JSBool
+  ReadAsBinaryString(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+
+    FileReaderSyncPrivate* fileReader =
+      GetInstancePrivate(aCx, obj, "readAsBinaryString");
+    if (!fileReader) {
+      return false;
+    }
+
+    JSObject* jsBlob;
+    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o", &jsBlob)) {
+      return false;
+    }
+
+    nsIDOMBlob* blob = GetDOMBlobFromJSObject(aCx, jsBlob);
+    if (!blob) {
+      return false;
+    }
+
+    nsString blobText;
+    nsresult rv = fileReader->ReadAsBinaryString(blob, blobText);
+    if (!EnsureSucceededOrThrow(aCx, rv)) {
+      return false;
+    }
+
+    JSString* jsBlobText = JS_NewUCStringCopyN(aCx, blobText.get(),
+                                               blobText.Length());
+    if (!jsBlobText) {
+      return false;
+    }
+
+    JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(jsBlobText));
+    return true;
+  }
+
+  static JSBool
+  ReadAsText(JSContext* aCx, uintN aArgc, jsval* aVp)
+  {
+    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+
+    FileReaderSyncPrivate* fileReader =
+      GetInstancePrivate(aCx, obj, "readAsText");
+    if (!fileReader) {
+      return false;
+    }
+
+    JSObject* jsBlob;
+    JSString* jsEncoding = JS_GetEmptyString(JS_GetRuntime(aCx));
+    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o/S", &jsBlob,
+                             &jsEncoding)) {
+      return false;
+    }
+
+    nsDependentJSString encoding;
+    if (!encoding.init(aCx, jsEncoding)) {
+      return false;
+    }
+
+    nsIDOMBlob* blob = GetDOMBlobFromJSObject(aCx, jsBlob);
+    if (!blob) {
+      return false;
+    }
+
+    nsString blobText;
+    nsresult rv = fileReader->ReadAsText(blob, encoding, blobText);
+    if (!EnsureSucceededOrThrow(aCx, rv)) {
+      return false;
+    }
+
+    JSString* jsBlobText = JS_NewUCStringCopyN(aCx, blobText.get(),
+                                               blobText.Length());
+    if (!jsBlobText) {
+      return false;
+    }
+
+    JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(jsBlobText));
+    return true;
+  }
+};
+
+JSClass FileReaderSync::sClass = {
+  "FileReaderSync",
+  JSCLASS_HAS_PRIVATE,
+  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
+  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
+  JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+JSFunctionSpec FileReaderSync::sFunctions[] = {
+  JS_FN("readAsArrayBuffer", ReadAsArrayBuffer, 1, FUNCTION_FLAGS),
+  JS_FN("readAsBinaryString", ReadAsBinaryString, 1, FUNCTION_FLAGS),
+  JS_FN("readAsText", ReadAsText, 1, FUNCTION_FLAGS),
+  JS_FN("readAsDataURL", ReadAsDataURL, 1, FUNCTION_FLAGS),
+  JS_FS_END
+};
+
+} // anonymous namespace
+
+BEGIN_WORKERS_NAMESPACE
+
+namespace filereadersync {
+
+bool
+InitClass(JSContext* aCx, JSObject* aGlobal)
+{
+  return !!FileReaderSync::InitClass(aCx, aGlobal);
+}
+
+} // namespace filereadersync
+
+END_WORKERS_NAMESPACE
new file mode 100644
--- /dev/null
+++ b/dom/workers/FileReaderSync.h
@@ -0,0 +1,58 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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 Web Workers.
+ *
+ * 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):
+ *   William Chen <wchen@mozilla.com> (Original Author)
+ *
+ * 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 mozilla_dom_workers_filereadersync_h__
+#define mozilla_dom_workers_filereadersync_h__
+
+#include "Workers.h"
+
+#include "jspubtd.h"
+
+BEGIN_WORKERS_NAMESPACE
+
+namespace filereadersync {
+
+bool
+InitClass(JSContext* aCx, JSObject* aGlobal);
+
+} // namespace filereadersync
+
+END_WORKERS_NAMESPACE
+
+#endif // mozilla_dom_workers_filereadersync_h__
new file mode 100644
--- /dev/null
+++ b/dom/workers/FileReaderSyncPrivate.cpp
@@ -0,0 +1,318 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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.org 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):
+ *   William Chen <wchen@mozilla.com> (Original Author)
+ *
+ * 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 ***** */
+
+#include "FileReaderSyncPrivate.h"
+
+#include "nsCExternalHandlerService.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsDOMClassInfo.h"
+#include "nsDOMError.h"
+#include "nsIDOMFile.h"
+#include "nsICharsetAlias.h"
+#include "nsICharsetDetector.h"
+#include "nsIConverterInputStream.h"
+#include "nsIInputStream.h"
+#include "nsIPlatformCharset.h"
+#include "nsISeekableStream.h"
+#include "nsISupportsImpl.h"
+#include "nsISupportsImpl.h"
+#include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+#include "RuntimeService.h"
+
+#include "mozilla/Base64.h"
+
+USING_WORKERS_NAMESPACE
+
+NS_IMPL_ISUPPORTS1(FileReaderSyncPrivate, nsICharsetDetectionObserver)
+
+FileReaderSyncPrivate::FileReaderSyncPrivate()
+{
+  MOZ_COUNT_CTOR(mozilla::dom::workers::FileReaderSyncPrivate);
+}
+
+FileReaderSyncPrivate::~FileReaderSyncPrivate()
+{
+  MOZ_COUNT_DTOR(mozilla::dom::workers::FileReaderSyncPrivate);
+}
+
+nsresult
+FileReaderSyncPrivate::ReadAsArrayBuffer(nsIDOMBlob* aBlob, PRUint32 aLength,
+                                         uint8* aBuffer)
+{
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint32 numRead;
+  rv = stream->Read((char*)aBuffer, aLength, &numRead);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ASSERTION(numRead == aLength, "failed to read data");
+
+  return NS_OK;
+}
+
+nsresult
+FileReaderSyncPrivate::ReadAsBinaryString(nsIDOMBlob* aBlob, nsAString& aResult)
+{
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint32 numRead;
+  do {
+    char readBuf[4096];
+    rv = stream->Read(readBuf, sizeof(readBuf), &numRead);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRUint32 oldLength = aResult.Length();
+    AppendASCIItoUTF16(Substring(readBuf, readBuf + numRead), aResult);
+    if (aResult.Length() - oldLength != numRead) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+  } while (numRead > 0);
+
+  return NS_OK;
+}
+
+nsresult
+FileReaderSyncPrivate::ReadAsText(nsIDOMBlob* aBlob,
+                                  const nsAString& aEncoding, nsAString& aResult)
+{
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCString charsetGuess;
+  if (aEncoding.IsEmpty()) {
+    rv = GuessCharset(stream, charsetGuess);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(stream);
+    NS_ENSURE_TRUE(seekable, NS_ERROR_FAILURE);
+
+    // Seek to 0 because guessing the charset advances the stream.
+    rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else {
+    CopyUTF16toUTF8(aEncoding, charsetGuess);
+  }
+
+  nsCOMPtr<nsICharsetAlias> alias =
+    do_GetService(NS_CHARSETALIAS_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCString charset;
+  rv = alias->GetPreferred(charsetGuess, charset);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return ConvertStream(stream, charset.get(), aResult);
+}
+
+nsresult
+FileReaderSyncPrivate::ReadAsDataURL(nsIDOMBlob* aBlob, nsAString& aResult)
+{
+  nsAutoString scratchResult;
+  scratchResult.AssignLiteral("data:");
+
+  nsString contentType;
+  aBlob->GetType(contentType);
+
+  if (contentType.IsEmpty()) {
+    scratchResult.AppendLiteral("application/octet-stream");
+  } else {
+    scratchResult.Append(contentType);
+  }
+  scratchResult.AppendLiteral(";base64,");
+
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint64 size;
+  rv = aBlob->GetSize(&size);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIInputStream> bufferedStream;
+  rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, size);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoString encodedData;
+  rv = Base64EncodeInputStream(bufferedStream, encodedData, size);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  scratchResult.Append(encodedData);
+
+  aResult = scratchResult;
+  return NS_OK;
+}
+
+nsresult
+FileReaderSyncPrivate::ConvertStream(nsIInputStream *aStream,
+                                     const char *aCharset,
+                                     nsAString &aResult)
+{
+  nsCOMPtr<nsIConverterInputStream> converterStream =
+    do_CreateInstance("@mozilla.org/intl/converter-input-stream;1");
+  NS_ENSURE_TRUE(converterStream, NS_ERROR_FAILURE);
+
+  nsresult rv = converterStream->Init(aStream, aCharset, 8192,
+                  nsIConverterInputStream::DEFAULT_REPLACEMENT_CHARACTER);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIUnicharInputStream> unicharStream =
+    do_QueryInterface(converterStream);
+  NS_ENSURE_TRUE(unicharStream, NS_ERROR_FAILURE);
+
+  PRUint32 numChars;
+  nsString result;
+  while (NS_SUCCEEDED(unicharStream->ReadString(8192, result, &numChars)) &&
+         numChars > 0) {
+    PRUint32 oldLength = aResult.Length();
+    aResult.Append(result);
+    if (aResult.Length() - oldLength != result.Length()) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+  }
+
+  return rv;
+}
+
+nsresult
+FileReaderSyncPrivate::GuessCharset(nsIInputStream *aStream,
+                                    nsACString &aCharset)
+{
+  // First try the universal charset detector
+  nsCOMPtr<nsICharsetDetector> detector
+    = do_CreateInstance(NS_CHARSET_DETECTOR_CONTRACTID_BASE
+                        "universal_charset_detector");
+  if (!detector) {
+    RuntimeService* runtime = RuntimeService::GetService();
+    NS_ASSERTION(runtime, "This should never be null!");
+
+    // No universal charset detector, try the default charset detector
+    const nsACString& detectorName = runtime->GetDetectorName();
+
+    if (!detectorName.IsEmpty()) {
+      nsCAutoString detectorContractID;
+      detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
+      detectorContractID += detectorName;
+      detector = do_CreateInstance(detectorContractID.get());
+    }
+  }
+
+  nsresult rv;
+  if (detector) {
+    detector->Init(this);
+
+    PRBool done;
+    PRUint32 numRead;
+    do {
+      char readBuf[4096];
+      rv = aStream->Read(readBuf, sizeof(readBuf), &numRead);
+      NS_ENSURE_SUCCESS(rv, rv);
+      if (numRead <= 0) {
+        break;
+      }
+      rv = detector->DoIt(readBuf, numRead, &done);
+      NS_ENSURE_SUCCESS(rv, rv);
+    } while (!done);
+
+    rv = detector->Done();
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else {
+    // no charset detector available, check the BOM
+    unsigned char sniffBuf[4];
+    PRUint32 numRead;
+    rv = aStream->Read(reinterpret_cast<char*>(sniffBuf),
+                       sizeof(sniffBuf), &numRead);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (numRead >= 4 &&
+        sniffBuf[0] == 0x00 &&
+        sniffBuf[1] == 0x00 &&
+        sniffBuf[2] == 0xfe &&
+        sniffBuf[3] == 0xff) {
+      mCharset = "UTF-32BE";
+    } else if (numRead >= 4 &&
+               sniffBuf[0] == 0xff &&
+               sniffBuf[1] == 0xfe &&
+               sniffBuf[2] == 0x00 &&
+               sniffBuf[3] == 0x00) {
+      mCharset = "UTF-32LE";
+    } else if (numRead >= 2 &&
+               sniffBuf[0] == 0xfe &&
+               sniffBuf[1] == 0xff) {
+      mCharset = "UTF-16BE";
+    } else if (numRead >= 2 &&
+               sniffBuf[0] == 0xff &&
+               sniffBuf[1] == 0xfe) {
+      mCharset = "UTF-16LE";
+    } else if (numRead >= 3 &&
+               sniffBuf[0] == 0xef &&
+               sniffBuf[1] == 0xbb &&
+               sniffBuf[2] == 0xbf) {
+      mCharset = "UTF-8";
+    }
+  }
+
+  if (mCharset.IsEmpty()) {
+    RuntimeService* runtime = RuntimeService::GetService();
+    mCharset = runtime->GetSystemCharset();
+  }
+
+  if (mCharset.IsEmpty()) {
+    // no sniffed or default charset, try UTF-8
+    mCharset.AssignLiteral("UTF-8");
+  }
+
+  aCharset = mCharset;
+  mCharset.Truncate();
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileReaderSyncPrivate::Notify(const char* aCharset, nsDetectionConfident aConf)
+{
+  mCharset.Assign(aCharset);
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/FileReaderSyncPrivate.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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.org 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):
+ *   William Chen <wchen@mozilla.com> (Original Author)
+ *
+ * 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 nsDOMFileReaderSyncPrivate_h
+#define nsDOMFileReaderSyncPrivate_h
+
+#include "Workers.h"
+
+#include "nsICharsetDetectionObserver.h"
+#include "nsStringGlue.h"
+
+class nsIInputStream;
+class nsIDOMBlob;
+
+BEGIN_WORKERS_NAMESPACE
+
+class FileReaderSyncPrivate : public PrivatizableBase,
+                              public nsICharsetDetectionObserver
+{
+  nsCString mCharset;
+  nsresult ConvertStream(nsIInputStream *aStream, const char *aCharset,
+                         nsAString &aResult);
+  nsresult GuessCharset(nsIInputStream *aStream, nsACString &aCharset);
+
+public:
+  NS_DECL_ISUPPORTS
+
+  FileReaderSyncPrivate();
+  ~FileReaderSyncPrivate();
+
+  nsresult ReadAsArrayBuffer(nsIDOMBlob* aBlob, PRUint32 aLength,
+                             uint8* aBuffer);
+  nsresult ReadAsBinaryString(nsIDOMBlob* aBlob, nsAString& aResult);
+  nsresult ReadAsText(nsIDOMBlob* aBlob, const nsAString& aEncoding,
+                      nsAString& aResult);
+  nsresult ReadAsDataURL(nsIDOMBlob* aBlob, nsAString& aResult);
+
+  // From nsICharsetDetectionObserver
+  NS_IMETHOD Notify(const char *aCharset, nsDetectionConfident aConf);
+};
+
+END_WORKERS_NAMESPACE
+
+#endif
--- a/dom/workers/Makefile.in
+++ b/dom/workers/Makefile.in
@@ -52,16 +52,19 @@ EXPORTS_NAMESPACES = mozilla/dom/workers
 
 EXPORTS_mozilla/dom/workers = Workers.h
 
 CPPSRCS = \
   ChromeWorkerScope.cpp \
   Events.cpp \
   EventTarget.cpp \
   Exceptions.cpp \
+  File.cpp \
+  FileReaderSync.cpp \
+  FileReaderSyncPrivate.cpp \
   ListenerManager.cpp \
   Location.cpp \
   Navigator.cpp \
   Principal.cpp \
   RuntimeService.cpp \
   ScriptLoader.cpp \
   Worker.cpp \
   WorkerPrivate.cpp \
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -1,9 +1,10 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
 /* ***** 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/
  *
@@ -37,16 +38,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "RuntimeService.h"
 
 #include "nsIDOMChromeWindow.h"
 #include "nsIDocument.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIObserverService.h"
+#include "nsIPlatformCharset.h"
 #include "nsIPrincipal.h"
 #include "nsIJSContextStack.h"
 #include "nsIMemoryReporter.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsISupportsPriority.h"
 #include "nsITimer.h"
 #include "nsPIDOMWindow.h"
 
@@ -238,25 +240,16 @@ CreateJSContextForWorker(WorkerPrivate* 
   NS_ASSERTION(!aWorkerPrivate->GetJSContext(), "Already has a context!");
 
   JSRuntime* runtime = JS_NewRuntime(WORKER_RUNTIME_HEAPSIZE);
   if (!runtime) {
     NS_WARNING("Could not create new runtime!");
     return nsnull;
   }
 
-  // ChromeWorker has extra clone callbacks for passing threadsafe XPCOM
-  // components.
-  JSStructuredCloneCallbacks* callbacks =
-    aWorkerPrivate->IsChromeWorker() ?
-    ChromeWorkerStructuredCloneCallbacks() :
-    WorkerStructuredCloneCallbacks();
-
-  JS_SetStructuredCloneCallbacks(runtime, callbacks);
-
   JSContext* workerCx = JS_NewContext(runtime, 0);
   if (!workerCx) {
     JS_DestroyRuntime(runtime);
     NS_WARNING("Could not create new context!");
     return nsnull;
   }
 
   JS_SetContextPrivate(workerCx, aWorkerPrivate);
@@ -925,16 +918,25 @@ RuntimeService::Init()
                                             MAX_SCRIPT_RUN_TIME_SEC))) {
       NS_WARNING("Failed to register timeout cache?!");
   }
 
   PRInt32 maxPerDomain = Preferences::GetInt(PREF_WORKERS_MAX_PER_DOMAIN,
                                              MAX_WORKERS_PER_DOMAIN);
   gMaxWorkersPerDomain = NS_MAX(0, maxPerDomain);
 
+  mDetectorName = Preferences::GetLocalizedCString("intl.charset.detector");
+
+  nsCOMPtr<nsIPlatformCharset> platformCharset =
+    do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
+  if (NS_SUCCEEDED(rv)) {
+    rv = platformCharset->GetCharset(kPlatformCharsetSel_PlainTextInFile,
+                                     mSystemCharset);
+  }
+
   return NS_OK;
 }
 
 // This spins the event loop until all workers are finished and their threads
 // have been joined.
 void
 RuntimeService::Cleanup()
 {
--- a/dom/workers/RuntimeService.h
+++ b/dom/workers/RuntimeService.h
@@ -1,9 +1,10 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
 /* ***** 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/
  *
@@ -94,16 +95,19 @@ class RuntimeService : public nsIObserve
   nsTArray<IdleThreadInfo> mIdleThreadArray;
 
   // *Not* protected by mMutex.
   nsClassHashtable<nsVoidPtrHashKey, nsTArray<WorkerPrivate*> > mWindowMap;
 
   // Only used on the main thread.
   nsCOMPtr<nsITimer> mIdleThreadTimer;
 
+  nsCString mDetectorName;
+  nsCString mSystemCharset;
+
   static PRUint32 sDefaultJSContextOptions;
   static PRInt32 sCloseHandlerTimeoutSeconds;
 
 #ifdef JS_GC_ZEAL
   static PRUint8 sDefaultGCZeal;
 #endif
 
 public:
@@ -143,16 +147,28 @@ public:
   CancelWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow);
 
   void
   SuspendWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow);
 
   void
   ResumeWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow);
 
+  const nsACString&
+  GetDetectorName() const
+  {
+    return mDetectorName;
+  }
+
+  const nsACString&
+  GetSystemCharset() const
+  {
+    return mSystemCharset;
+  }
+
   const NavigatorStrings&
   GetNavigatorStrings() const
   {
     return mNavigatorStrings;
   }
 
   void
   NoteIdleThread(nsIThread* aThread);
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1,9 +1,10 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
 /* ***** 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/
  *
@@ -35,16 +36,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "WorkerPrivate.h"
 
 #include "nsIClassInfo.h"
 #include "nsIConsoleService.h"
+#include "nsIDOMFile.h"
 #include "nsIDocument.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIJSContextStack.h"
 #include "nsIScriptError.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsPIDOMWindow.h"
 #include "nsITextToSubURI.h"
@@ -63,16 +65,17 @@
 #include "nsJSEnvironment.h"
 #include "nsJSUtils.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
 #include "xpcpublic.h"
 
 #include "Events.h"
 #include "Exceptions.h"
+#include "File.h"
 #include "Principal.h"
 #include "RuntimeService.h"
 #include "ScriptLoader.h"
 #include "WorkerFeature.h"
 #include "WorkerScope.h"
 
 #include "WorkerInlines.h"
 
@@ -138,24 +141,107 @@ SwapToISupportsArray(SmartPtr<T>& aSrc,
 }
 
 struct WorkerStructuredCloneCallbacks
 {
   static JSObject*
   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32 aTag,
        uint32 aData, void* aClosure)
   {
+    // See if object is a nsIDOMFile pointer.
+    if (aTag == DOMWORKER_SCTAG_FILE) {
+      JS_ASSERT(!aData);
+
+      nsIDOMFile* file;
+      if (JS_ReadBytes(aReader, &file, sizeof(file))) {
+        JS_ASSERT(file);
+
+#ifdef DEBUG
+        {
+          // File should not be mutable.
+          nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
+          PRBool isMutable;
+          NS_ASSERTION(NS_SUCCEEDED(mutableFile->GetMutable(&isMutable)) &&
+                       !isMutable,
+                       "Only immutable file should be passed to worker");
+        }
+#endif
+
+        // nsIDOMFiles should be threadsafe, thus we will use the same instance
+        // in the worker.
+        JSObject* jsFile = file::CreateFile(aCx, file);
+        return jsFile;
+      }
+    }
+    // See if object is a nsIDOMBlob pointer.
+    else if (aTag == DOMWORKER_SCTAG_BLOB) {
+      JS_ASSERT(!aData);
+
+      nsIDOMBlob* blob;
+      if (JS_ReadBytes(aReader, &blob, sizeof(blob))) {
+        JS_ASSERT(blob);
+
+#ifdef DEBUG
+        {
+          // Blob should not be mutable.
+          nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
+          PRBool isMutable;
+          NS_ASSERTION(NS_SUCCEEDED(mutableBlob->GetMutable(&isMutable)) &&
+                       !isMutable,
+                       "Only immutable blob should be passed to worker");
+        }
+#endif
+
+        // nsIDOMBlob should be threadsafe, thus we will use the same instance
+        // in the worker.
+        JSObject* jsBlob = file::CreateBlob(aCx, blob);
+        return jsBlob;
+      }
+    }
+
     Error(aCx, 0);
     return nsnull;
   }
 
   static JSBool
   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
         void* aClosure)
   {
+    NS_ASSERTION(aClosure, "Null pointer!");
+
+    // We'll stash any nsISupports pointers that need to be AddRef'd here.
+    nsTArray<nsCOMPtr<nsISupports> >* clonedObjects =
+      static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure);
+
+    // See if this is a File object.
+    {
+      nsIDOMFile* file = file::GetDOMFileFromJSObject(aCx, aObj);
+      if (file) {
+        if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FILE, 0) &&
+            JS_WriteBytes(aWriter, &file, sizeof(file))) {
+          clonedObjects->AppendElement(file);
+          return true;
+        }
+      }
+    }
+
+    // See if this is a Blob object.
+    {
+      nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aCx, aObj);
+      if (blob) {
+        nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
+        if (mutableBlob && NS_SUCCEEDED(mutableBlob->SetMutable(PR_FALSE)) &&
+            JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) &&
+            JS_WriteBytes(aWriter, &blob, sizeof(blob))) {
+          clonedObjects->AppendElement(blob);
+          return true;
+        }
+      }
+    }
+
     Error(aCx, 0);
     return false;
   }
 
   static void
   Error(JSContext* aCx, uint32 /* aErrorId */)
   {
     ThrowDOMExceptionForCode(aCx, DATA_CLONE_ERR);
@@ -171,32 +257,144 @@ JSStructuredCloneCallbacks gWorkerStruct
 struct MainThreadWorkerStructuredCloneCallbacks
 {
   static JSObject*
   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32 aTag,
        uint32 aData, void* aClosure)
   {
     AssertIsOnMainThread();
 
+    // See if object is a nsIDOMFile pointer.
+    if (aTag == DOMWORKER_SCTAG_FILE) {
+      JS_ASSERT(!aData);
+
+      nsIDOMFile* file;
+      if (JS_ReadBytes(aReader, &file, sizeof(file))) {
+        JS_ASSERT(file);
+
+#ifdef DEBUG
+        {
+          // File should not be mutable.
+          nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
+          PRBool isMutable;
+          NS_ASSERTION(NS_SUCCEEDED(mutableFile->GetMutable(&isMutable)) &&
+                       !isMutable,
+                       "Only immutable file should be passed to worker");
+        }
+#endif
+
+        // nsIDOMFiles should be threadsafe, thus we will use the same instance
+        // on the main thread.
+        jsval wrappedFile;
+        nsresult rv =
+          nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), file,
+                                     &NS_GET_IID(nsIDOMFile), &wrappedFile);
+        if (NS_FAILED(rv)) {
+          Error(aCx, DATA_CLONE_ERR);
+          return nsnull;
+        }
+
+        return JSVAL_TO_OBJECT(wrappedFile);
+      }
+    }
+    // See if object is a nsIDOMBlob pointer.
+    else if (aTag == DOMWORKER_SCTAG_BLOB) {
+      JS_ASSERT(!aData);
+
+      nsIDOMBlob* blob;
+      if (JS_ReadBytes(aReader, &blob, sizeof(blob))) {
+        JS_ASSERT(blob);
+
+#ifdef DEBUG
+        {
+          // Blob should not be mutable.
+          nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
+          PRBool isMutable;
+          NS_ASSERTION(NS_SUCCEEDED(mutableBlob->GetMutable(&isMutable)) &&
+                       !isMutable,
+                       "Only immutable blob should be passed to worker");
+        }
+#endif
+
+        // nsIDOMBlobs should be threadsafe, thus we will use the same instance
+        // on the main thread.
+        jsval wrappedBlob;
+        nsresult rv =
+          nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), blob,
+                                     &NS_GET_IID(nsIDOMBlob), &wrappedBlob);
+        if (NS_FAILED(rv)) {
+          Error(aCx, DATA_CLONE_ERR);
+          return nsnull;
+        }
+
+        return JSVAL_TO_OBJECT(wrappedBlob);
+      }
+    }
+
     JSObject* clone =
       WorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData, aClosure);
     if (clone) {
       return clone;
     }
 
     JS_ClearPendingException(aCx);
     return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nsnull);
   }
 
   static JSBool
   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
         void* aClosure)
   {
     AssertIsOnMainThread();
 
+    NS_ASSERTION(aClosure, "Null pointer!");
+
+    // We'll stash any nsISupports pointers that need to be AddRef'd here.
+    nsTArray<nsCOMPtr<nsISupports> >* clonedObjects =
+      static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure);
+
+    // See if this is a wrapped native.
+    nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
+    nsContentUtils::XPConnect()->
+      GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative));
+
+    if (wrappedNative) {
+      // Get the raw nsISupports out of it.
+      nsISupports* wrappedObject = wrappedNative->Native();
+      NS_ASSERTION(wrappedObject, "Null pointer?!");
+
+      // See if the wrapped native is a nsIDOMFile.
+      nsCOMPtr<nsIDOMFile> file = do_QueryInterface(wrappedObject);
+      if (file) {
+        nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
+        if (mutableFile && NS_SUCCEEDED(mutableFile->SetMutable(PR_FALSE))) {
+          nsIDOMFile* filePtr = file;
+          if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FILE, 0) &&
+              JS_WriteBytes(aWriter, &filePtr, sizeof(filePtr))) {
+            clonedObjects->AppendElement(file);
+            return true;
+          }
+        }
+      }
+
+      // See if the wrapped native is a nsIDOMBlob.
+      nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(wrappedObject);
+      if (blob) {
+        nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
+        if (mutableBlob && NS_SUCCEEDED(mutableBlob->SetMutable(PR_FALSE))) {
+          nsIDOMBlob* blobPtr = blob;
+          if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) &&
+              JS_WriteBytes(aWriter, &blobPtr, sizeof(blobPtr))) {
+            clonedObjects->AppendElement(blob);
+            return true;
+          }
+        }
+      }
+    }
+
     JSBool ok =
       WorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, aClosure);
     if (ok) {
       return ok;
     }
 
     JS_ClearPendingException(aCx);
     return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nsnull);
@@ -514,25 +712,31 @@ public:
     aWorkerPrivate->CloseHandlerFinished();
   }
 };
 
 class MessageEventRunnable : public WorkerRunnable
 {
   uint64* mData;
   size_t mDataByteCount;
+  nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
 
 public:
   MessageEventRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget,
-                       JSAutoStructuredCloneBuffer& aData)
+                       JSAutoStructuredCloneBuffer& aData,
+                       nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects)
   : WorkerRunnable(aWorkerPrivate, aTarget, aTarget == WorkerThread ?
                                                        ModifyBusyCount :
                                                        UnchangedBusyCount)
   {
     aData.steal(&mData, &mDataByteCount);
+
+    if (!mClonedObjects.SwapElements(aClonedObjects)) {
+      NS_ERROR("This should never fail!");
+    }
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
     JSAutoStructuredCloneBuffer buffer;
     buffer.adopt(mData, mDataByteCount);
 
@@ -564,17 +768,18 @@ public:
       NS_ASSERTION(aWorkerPrivate == GetWorkerPrivateFromContext(aCx),
                    "Badness!");
       mainRuntime = false;
       target = JS_GetGlobalObject(aCx);
     }
 
     NS_ASSERTION(target, "This should never be null!");
 
-    JSObject* event = events::CreateMessageEvent(aCx, buffer, mainRuntime);
+    JSObject* event = events::CreateMessageEvent(aCx, buffer, mClonedObjects,
+                                                 mainRuntime);
     if (!event) {
       return false;
     }
 
     bool dummy;
     return events::DispatchEventToTarget(aCx, target, event, &dummy);
   }
 };
@@ -1385,17 +1590,17 @@ WorkerPrivateParent<Derived>::WorkerPriv
                                      nsCOMPtr<nsIURI>& aBaseURI,
                                      nsCOMPtr<nsIPrincipal>& aPrincipal,
                                      nsCOMPtr<nsIDocument>& aDocument)
 : mMutex("WorkerPrivateParent Mutex"),
   mCondVar(mMutex, "WorkerPrivateParent CondVar"),
   mJSObject(aObject), mParent(aParent), mParentJSContext(aParentJSContext),
   mScriptURL(aScriptURL), mDomain(aDomain), mBusyCount(0),
   mParentStatus(Pending), mJSObjectRooted(false), mParentSuspended(false),
-  mIsChromeWorker(aIsChromeWorker)
+  mIsChromeWorker(aIsChromeWorker), mPrincipalIsSystem(false)
 {
   MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivateParent);
 
   if (aWindow) {
     NS_ASSERTION(aWindow->IsInnerWindow(), "Should have inner window here!");
   }
 
   mWindow.swap(aWindow);
@@ -1688,37 +1893,45 @@ WorkerPrivateParent<Derived>::ForgetMain
 template <class Derived>
 bool
 WorkerPrivateParent<Derived>::PostMessage(JSContext* aCx, jsval aMessage)
 {
   AssertIsOnParentThread();
 
   JSStructuredCloneCallbacks* callbacks;
   if (GetParent()) {
-    callbacks = nsnull;
+    if (IsChromeWorker()) {
+      callbacks = &gChromeWorkerStructuredCloneCallbacks;
+    }
+    else {
+      callbacks = &gWorkerStructuredCloneCallbacks;
+    }
   }
   else {
     AssertIsOnMainThread();
 
     if (IsChromeWorker()) {
       callbacks = &gMainThreadChromeWorkerStructuredCloneCallbacks;
     }
     else {
       callbacks = &gMainThreadWorkerStructuredCloneCallbacks;
     }
   }
 
+  nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
+
   JSAutoStructuredCloneBuffer buffer;
-  if (!buffer.write(aCx, aMessage, callbacks, nsnull)) {
+  if (!buffer.write(aCx, aMessage, callbacks, &clonedObjects)) {
     return false;
   }
 
   nsRefPtr<MessageEventRunnable> runnable =
     new MessageEventRunnable(ParentAsWorkerPrivate(),
-                             WorkerRunnable::WorkerThread, buffer);
+                             WorkerRunnable::WorkerThread, buffer,
+                             clonedObjects);
   return runnable->Dispatch(aCx);
 }
 
 template <class Derived>
 PRUint64
 WorkerPrivateParent<Derived>::GetOuterWindowId()
 {
   AssertIsOnMainThread();
@@ -1835,16 +2048,26 @@ WorkerPrivateParent<Derived>::SetBaseURI
   else {
     mLocationInfo.mHost.Assign(mLocationInfo.mHostname);
   }
 
   return NS_OK;
 }
 
 template <class Derived>
+void
+WorkerPrivateParent<Derived>::SetPrincipal(nsIPrincipal* aPrincipal)
+{
+  AssertIsOnMainThread();
+
+  mPrincipal = aPrincipal;
+  mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
+}
+
+template <class Derived>
 JSContext*
 WorkerPrivateParent<Derived>::ParentJSContext() const
 {
   AssertIsOnParentThread();
 
   if (!mParent) {
     AssertIsOnMainThread();
 
@@ -2624,23 +2847,31 @@ WorkerPrivate::StopSyncLoop(PRUint32 aSy
   syncQueue->mComplete = true;
 }
 
 bool
 WorkerPrivate::PostMessageToParent(JSContext* aCx, jsval aMessage)
 {
   AssertIsOnWorkerThread();
 
+  JSStructuredCloneCallbacks* callbacks =
+    IsChromeWorker() ?
+    &gChromeWorkerStructuredCloneCallbacks :
+    &gWorkerStructuredCloneCallbacks;
+
+  nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
+
   JSAutoStructuredCloneBuffer buffer;
-  if (!buffer.write(aCx, aMessage, nsnull, nsnull)) {
+  if (!buffer.write(aCx, aMessage, callbacks, &clonedObjects)) {
     return false;
   }
 
   nsRefPtr<MessageEventRunnable> runnable =
-    new MessageEventRunnable(this, WorkerRunnable::ParentThread, buffer);
+    new MessageEventRunnable(this, WorkerRunnable::ParentThread, buffer,
+                             clonedObjects);
   return runnable->Dispatch(aCx);
 }
 
 bool
 WorkerPrivate::NotifyInternal(JSContext* aCx, Status aStatus)
 {
   AssertIsOnWorkerThread();
 
@@ -3205,20 +3436,24 @@ template class WorkerPrivateParent<Worke
 WorkerPrivate*
 GetWorkerPrivateFromContext(JSContext* aCx)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
   return static_cast<WorkerPrivate*>(JS_GetContextPrivate(aCx));
 }
 
 JSStructuredCloneCallbacks*
-WorkerStructuredCloneCallbacks()
+WorkerStructuredCloneCallbacks(bool aMainRuntime)
 {
-  return &gWorkerStructuredCloneCallbacks;
+  return aMainRuntime ?
+         &gMainThreadWorkerStructuredCloneCallbacks :
+         &gWorkerStructuredCloneCallbacks;
 }
 
 JSStructuredCloneCallbacks*
-ChromeWorkerStructuredCloneCallbacks()
+ChromeWorkerStructuredCloneCallbacks(bool aMainRuntime)
 {
-  return &gChromeWorkerStructuredCloneCallbacks;
+  return aMainRuntime ?
+         &gMainThreadChromeWorkerStructuredCloneCallbacks :
+         &gChromeWorkerStructuredCloneCallbacks;
 }
 
 END_WORKERS_NAMESPACE
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -214,16 +214,17 @@ private:
 
   PRUint64 mBusyCount;
   Status mParentStatus;
   PRUint32 mJSContextOptions;
   PRUint8 mGCZeal;
   bool mJSObjectRooted;
   bool mParentSuspended;
   bool mIsChromeWorker;
+  bool mPrincipalIsSystem;
 
 protected:
   WorkerPrivateParent(JSContext* aCx, JSObject* aObject, WorkerPrivate* aParent,
                       JSContext* aParentJSContext, const nsAString& aScriptURL,
                       bool aIsChromeWorker, const nsACString& aDomain,
                       nsCOMPtr<nsPIDOMWindow>& aWindow,
                       nsCOMPtr<nsIScriptContext>& aScriptContext,
                       nsCOMPtr<nsIURI>& aBaseURI,
@@ -393,20 +394,22 @@ public:
   nsIPrincipal*
   GetPrincipal() const
   {
     AssertIsOnMainThread();
     return mPrincipal;
   }
 
   void
-  SetPrincipal(nsIPrincipal* aPrincipal)
+  SetPrincipal(nsIPrincipal* aPrincipal);
+
+  bool
+  UsesSystemPrincipal() const
   {
-    AssertIsOnMainThread();
-    mPrincipal = aPrincipal;
+    return mPrincipalIsSystem;
   }
 
   nsIDocument*
   GetDocument() const
   {
     AssertIsOnMainThread();
     return mDocument;
   }
@@ -740,17 +743,25 @@ private:
     ClearQueue(&mQueue);
     ClearQueue(&mControlQueue);
   }
 };
 
 WorkerPrivate*
 GetWorkerPrivateFromContext(JSContext* aCx);
 
-JSStructuredCloneCallbacks*
-WorkerStructuredCloneCallbacks();
+enum WorkerStructuredDataType
+{
+  DOMWORKER_SCTAG_FILE = JS_SCTAG_USER_MIN + 0x1000,
+  DOMWORKER_SCTAG_BLOB,
+
+  DOMWORKER_SCTAG_END
+};
 
 JSStructuredCloneCallbacks*
-ChromeWorkerStructuredCloneCallbacks();
+WorkerStructuredCloneCallbacks(bool aMainRuntime);
+
+JSStructuredCloneCallbacks*
+ChromeWorkerStructuredCloneCallbacks(bool aMainRuntime);
 
 END_WORKERS_NAMESPACE
 
 #endif /* mozilla_dom_workers_workerprivate_h__ */
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -1,9 +1,10 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
 /* ***** 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/
  *
@@ -44,16 +45,18 @@
 
 #include "nsTraceRefcnt.h"
 #include "xpcprivate.h"
 
 #include "ChromeWorkerScope.h"
 #include "Events.h"
 #include "EventTarget.h"
 #include "Exceptions.h"
+#include "File.h"
+#include "FileReaderSync.h"
 #include "ListenerManager.h"
 #include "Location.h"
 #include "Navigator.h"
 #include "Principal.h"
 #include "ScriptLoader.h"
 #include "Worker.h"
 #include "WorkerPrivate.h"
 #include "XMLHttpRequest.h"
@@ -870,16 +873,18 @@ CreateDedicatedWorkerGlobalScope(JSConte
     if (!chromeworker::InitClass(aCx, global, workerProto, false) ||
         !chromeworker::DefineChromeWorkerFunctions(aCx, global)) {
       return NULL;
     }
   }
 
   // Init other classes we care about.
   if (!events::InitClasses(aCx, global, false) ||
+      !file::InitClasses(aCx, global) ||
+      !filereadersync::InitClass(aCx, global) ||
       !exceptions::InitClasses(aCx, global) ||
       !xhr::InitClasses(aCx, global, eventTargetProto) ||
       !location::InitClass(aCx, global) ||
       !navigator::InitClass(aCx, global)) {
     return NULL;
   }
 
   if (!JS_DefineProfilingFunctions(aCx, global)) {
--- a/dom/workers/test/Makefile.in
+++ b/dom/workers/test/Makefile.in
@@ -115,16 +115,34 @@ include $(topsrcdir)/config/rules.mk
   relativeLoad_sub_worker.js \
   relativeLoad_sub_worker2.js \
   relativeLoad_sub_import.js \
   $(NULL)
 
 _CHROME_TEST_FILES = \
   test_chromeWorker.xul \
   test_chromeWorkerJSM.xul \
+  test_file.xul \
+  test_fileMozSlice.xul \
+  test_fileBlobPosting.xul \
+  test_filePosting.xul \
+  test_fileReaderSync.xul \
+  test_fileReaderSyncErrors.xul \
+  test_fileReadMozSlice.xul \
+  test_fileSubWorker.xul \
+  test_fileBlobSubWorker.xul \
+  file_worker.js \
+  fileBlob_worker.js \
+  fileMozSlice_worker.js \
+  filePosting_worker.js \
+  fileReaderSync_worker.js \
+  fileReaderSyncErrors_worker.js \
+  fileReadMozSlice_worker.js \
+  fileSubWorker_worker.js \
+  fileBlobSubWorker_worker.js \
   WorkerTest.jsm \
   WorkerTest_worker.js \
   WorkerTest_subworker.js \
   chromeWorker_worker.js \
   chromeWorker_subworker.js \
   test_workersDisabled.xul \
   workersDisabled_worker.js \
   $(NULL)
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/fileBlobSubWorker_worker.js
@@ -0,0 +1,17 @@
+/**
+ * Expects a blob. Returns an object containing the size, type.
+ *  Used to test posting of blob from worker to worker.
+ */
+onmessage = function(event) {
+  var worker = new Worker("fileBlob_worker.js");
+
+  worker.postMessage(event.data);
+
+  worker.onmessage = function(event) {
+    postMessage(event.data);
+  }
+
+  worker.onerror = function(event) {
+    postMessage(undefined);
+  }
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/fileBlob_worker.js
@@ -0,0 +1,13 @@
+/**
+ * Expects a blob. Returns an object containing the size, type.
+ */
+onmessage = function(event) {
+  var file = event.data;
+
+  var rtnObj = new Object();
+
+  rtnObj.size = file.size;
+  rtnObj.type = file.type;
+
+  postMessage(rtnObj);
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/fileMozSlice_worker.js
@@ -0,0 +1,27 @@
+/**
+ * Expects an object containing a blob, a start offset, an end offset
+ * and an optional content type to slice the blob. Returns an object
+ * containing the size and type of the sliced blob.
+ */
+onmessage = function(event) {
+  var blob = event.data.blob;
+  var start = event.data.start;
+  var end = event.data.end;
+  var contentType = event.data.contentType;
+
+  var slicedBlob;
+  if (contentType == undefined && end == undefined) {
+    slicedBlob = blob.mozSlice(start);
+  } else if (contentType == undefined) {
+    slicedBlob = blob.mozSlice(start, end);
+  } else {
+    slicedBlob = blob.mozSlice(start, end, contentType);
+  }
+
+  var rtnObj = new Object();
+
+  rtnObj.size = slicedBlob.size;
+  rtnObj.type = slicedBlob.type;
+
+  postMessage(rtnObj);
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/filePosting_worker.js
@@ -0,0 +1,3 @@
+onmessage = function(event) {
+  postMessage(event.data);
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/fileReadMozSlice_worker.js
@@ -0,0 +1,16 @@
+/**
+ * Expects an object containing a blob, a start index and an end index
+ * for slicing. Returns the contents of the blob read as text.
+ */
+onmessage = function(event) {
+  var blob = event.data.blob;
+  var start = event.data.start;
+  var end = event.data.end;
+
+  var slicedBlob = blob.mozSlice(start, end);
+
+  var fileReader = new FileReaderSync();
+  var text = fileReader.readAsText(slicedBlob);
+
+  postMessage(text);
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/fileReaderSyncErrors_worker.js
@@ -0,0 +1,79 @@
+/**
+ * Delegates "is" evaluation back to main thread.
+ */
+function is(actual, expected, message) {
+  var rtnObj = new Object();
+  rtnObj.actual = actual;
+  rtnObj.expected = expected;
+  rtnObj.message = message;
+  postMessage(rtnObj);
+}
+
+/**
+ * Tries to write to property.
+ */
+function writeProperty(file, property) {
+  try {
+    var oldValue = file[property];
+    file[property] = -1;
+    is(false, true, "Should have thrown an exception setting a read only property.");
+  } catch (ex) {
+    is(true, true, "Should have thrown an exception setting a read only property.");
+  }
+}
+
+/**
+ * Passes junk arguments to FileReaderSync methods and expects an exception to
+ * be thrown.
+ */
+function fileReaderJunkArgument(blob) {
+  var fileReader = new FileReaderSync();
+
+  try {
+    fileReader.readAsBinaryString(blob);
+    is(false, true, "Should have thrown an exception calling readAsBinaryString.");
+  } catch(ex) {
+    is(true, true, "Should have thrown an exception.");
+  }
+
+  try {
+    fileReader.readAsDataURL(blob);
+    is(false, true, "Should have thrown an exception calling readAsDataURL.");
+  } catch(ex) {
+    is(true, true, "Should have thrown an exception.");
+  }
+
+  try {
+    fileReader.readAsArrayBuffer(blob);
+    is(false, true, "Should have thrown an exception calling readAsArrayBuffer.");
+  } catch(ex) {
+    is(true, true, "Should have thrown an exception.");
+  }
+
+  try {
+    fileReader.readAsText(blob);
+    is(false, true, "Should have thrown an exception calling readAsText.");
+  } catch(ex) {
+    is(true, true, "Should have thrown an exception.");
+  }
+}
+
+onmessage = function(event) {
+  var file = event.data;
+
+  // Test read only properties.
+  writeProperty(file, "size");
+  writeProperty(file, "type");
+  writeProperty(file, "name");
+  writeProperty(file, "mozFullPath");
+
+  // Bad types.
+  fileReaderJunkArgument(undefined);
+  fileReaderJunkArgument(-1);
+  fileReaderJunkArgument(1);
+  fileReaderJunkArgument(new Object());
+  fileReaderJunkArgument("hello");
+
+    // Post undefined to indicate that testing has finished.
+  postMessage(undefined);
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/fileReaderSync_worker.js
@@ -0,0 +1,25 @@
+var reader = new FileReaderSync();
+
+/**
+ * Expects an object containing a file and an encoding then uses a
+ * FileReaderSync to read the file. Returns an object containing the
+ * file read a binary string, text, url and ArrayBuffer.
+ */
+onmessage = function(event) {
+  var file = event.data.file;
+  var encoding = event.data.encoding;
+
+  var rtnObj = new Object();
+
+  if (encoding != undefined) {
+    rtnObj.text = reader.readAsText(file, encoding);
+  } else {
+    rtnObj.text = reader.readAsText(file);
+  }
+
+  rtnObj.bin = reader.readAsBinaryString(file);
+  rtnObj.url = reader.readAsDataURL(file);
+  rtnObj.arrayBuffer = reader.readAsArrayBuffer(file);
+
+  postMessage(rtnObj);
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/fileSubWorker_worker.js
@@ -0,0 +1,17 @@
+/**
+ * Expects a file. Returns an object containing the size, type, name and path
+ * using another worker. Used to test posting of file from worker to worker.
+ */
+onmessage = function(event) {
+  var worker = new Worker("file_worker.js");
+
+  worker.postMessage(event.data);
+
+  worker.onmessage = function(event) {
+    postMessage(event.data);
+  }
+
+  worker.onerror = function(event) {
+    postMessage(undefined);
+  }
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/file_worker.js
@@ -0,0 +1,15 @@
+/**
+ * Expects a file. Returns an object containing the size, type, name and path.
+ */
+onmessage = function(event) {
+  var file = event.data;
+
+  var rtnObj = new Object();
+
+  rtnObj.size = file.size;
+  rtnObj.type = file.type;
+  rtnObj.name = file.name;
+  rtnObj.mozFullPath = file.mozFullPath;
+
+  postMessage(rtnObj);
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_file.xul
@@ -0,0 +1,97 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=123456
+-->
+<window title="Mozilla Bug 123456"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=123456"
+     target="_blank">Mozilla Bug 123456</a>
+
+  <div id="content" style="display: none">
+    <input id="fileList" type="file"></input>
+  </div>
+
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 123456 **/
+
+  var fileNum = 0;
+
+  /**
+   * Create a file which contains the given data and optionally adds the specified file extension.
+   */
+  function createFileWithData(fileData, /** optional */ extension) {
+    var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+    var fileExtension = (extension == undefined) ? "" : "." + extension;
+    testFile.append("workerFile" + fileNum++ + fileExtension);
+
+    var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                        .createInstance(Components.interfaces.nsIFileOutputStream);
+    outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                   0666, 0);
+    outStream.write(fileData, fileData.length);
+    outStream.close();
+
+    var fileList = document.getElementById('fileList');
+    fileList.value = testFile.path;
+
+    return fileList.files[0];
+  }
+
+  /**
+   * Create a worker to access file properties.
+   */
+  function accessFileProperties(file, expectedSize, expectedType) {
+    var worker = new Worker("file_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    worker.onmessage = function(event) {
+      is(event.data.size, expectedSize, "size proproperty accessed from worker is not the same as on main thread.");
+      is(event.data.type, expectedType, "type proproperty accessed from worker is incorrect.");
+      is(event.data.name, file.name, "name proproperty accessed from worker is incorrect.");
+      is(event.data.mozFullPath, file.mozFullPath, "mozFullPath proproperty accessed from worker is not the same as on main thread.");
+      SimpleTest.finish();
+    };
+
+    worker.postMessage(file);
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  // Empty file.
+  accessFileProperties(createFileWithData(""), 0, "");
+
+  // Typical use case.
+  accessFileProperties(createFileWithData("Hello"), 5, "");
+
+  // Longish file.
+  var text = "";
+  for (var i = 0; i < 10000; ++i) {
+    text += "long";
+  }
+  accessFileProperties(createFileWithData(text), 40000, "");
+
+  // Type detection based on extension.
+  accessFileProperties(createFileWithData("text", "txt"), 4, "text/plain");
+
+  ]]>
+  </script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_fileBlobPosting.xul
@@ -0,0 +1,87 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+     target="_blank">Mozilla Bug 664783</a>
+
+  <div id="content" style="display: none">
+    <input id="fileList" type="file"></input>
+  </div>
+
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 664783 **/
+
+  var fileNum = 0;
+
+  /**
+   * Create a file which contains the given data.
+   */
+  function createFileWithData(fileData) {
+    var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+    testFile.append("workerBlobPosting" + fileNum++);
+
+    var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                        .createInstance(Components.interfaces.nsIFileOutputStream);
+    outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                   0666, 0);
+    outStream.write(fileData, fileData.length);
+    outStream.close();
+
+    var fileList = document.getElementById('fileList');
+    fileList.value = testFile.path;
+
+    return fileList.files[0];
+  }
+
+  /**
+   * Create a worker which posts the same blob given. Used to test cloning of blobs.
+   * Checks the size, type, name and path of the file posted from the worker to ensure it
+   * is the same as the original.
+   */
+  function postBlob(file) {
+    var worker = new Worker("filePosting_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    worker.onmessage = function(event) {
+      console.log(event.data);
+      is(event.data.size, file.size, "size of file posted from worker does not match file posted to worker.");
+      SimpleTest.finish();
+    };
+
+    var blob = file.mozSlice();
+    worker.postMessage(blob);
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  // Empty file.
+  postBlob(createFileWithData(""));
+
+  // Typical use case.
+  postBlob(createFileWithData("Hello"));
+
+  ]]>
+  </script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_fileBlobSubWorker.xul
@@ -0,0 +1,99 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+     target="_blank">Mozilla Bug 664783</a>
+
+  <div id="content" style="display: none">
+    <input id="fileList" type="file"></input>
+  </div>
+
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 664783 **/
+
+  var fileNum = 0;
+
+  /**
+   * Create a file which contains the given data and optionally adds the specified file extension.
+   */
+  function createFileWithData(fileData, /** optional */ extension) {
+    var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+    var fileExtension = (extension == undefined) ? "" : "." + extension;
+    testFile.append("workerBlobSubWorker" + fileNum++ + fileExtension);
+
+    var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                        .createInstance(Components.interfaces.nsIFileOutputStream);
+    outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                   0666, 0);
+    outStream.write(fileData, fileData.length);
+    outStream.close();
+
+    var fileList = document.getElementById('fileList');
+    fileList.value = testFile.path;
+
+    return fileList.files[0];
+  }
+
+  /**
+   * Create a worker to access blob properties.
+   */
+  function accessFileProperties(file, expectedSize) {
+    var worker = new Worker("fileBlobSubWorker_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    worker.onmessage = function(event) {
+      if (event.data == undefined) {
+        ok(false, "Worker had an error.");
+      } else {
+        is(event.data.size, expectedSize, "size proproperty accessed from worker is not the same as on main thread.");
+      }
+      SimpleTest.finish();
+    };
+
+    var blob = file.mozSlice();
+    worker.postMessage(blob);
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  // Empty file.
+  accessFileProperties(createFileWithData(""), 0);
+
+  // Typical use case.
+  accessFileProperties(createFileWithData("Hello"), 5);
+
+  // Longish file.
+  var text = "";
+  for (var i = 0; i < 10000; ++i) {
+    text += "long";
+  }
+  accessFileProperties(createFileWithData(text), 40000);
+
+  // Type detection based on extension.
+  accessFileProperties(createFileWithData("text", "txt"), 4);
+
+  ]]>
+  </script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_fileMozSlice.xul
@@ -0,0 +1,107 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+     target="_blank">Mozilla Bug 664783</a>
+
+  <div id="content" style="display: none">
+    <input id="fileList" type="file"></input>
+  </div>
+
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 664783 **/
+
+  var fileNum = 0;
+
+  /**
+   * Create a file which contains the given data and optionally adds the specified file extension.
+   */
+  function createFileWithData(fileData, /** optional */ extension) {
+    var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+    var fileExtension = (extension == undefined) ? "" : "." + extension;
+    testFile.append("workerMozSlice" + fileNum++ + fileExtension);
+
+    var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                        .createInstance(Components.interfaces.nsIFileOutputStream);
+    outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                   0666, 0);
+    outStream.write(fileData, fileData.length);
+    outStream.close();
+
+    var fileList = document.getElementById('fileList');
+    fileList.value = testFile.path;
+
+    return fileList.files[0];
+  }
+
+  /**
+   * Starts a worker which slices the blob to the given start offset and optional end offset and
+   * content type. It then verifies that the size and type of the sliced blob is correct.
+   */
+  function createMozSlice(blob, start, expectedLength, /** optional */ end, /** optional */ contentType) {
+    var worker = new Worker("fileMozSlice_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    worker.onmessage = function(event) {
+      is(event.data.size, expectedLength, "size property of slice is incorrect.");
+      is(event.data.type, contentType ? contentType : blob.type, "type property of slice is incorrect.");
+      SimpleTest.finish();
+    };
+
+    var params = {blob: blob, start: start, end: end, contentType: contentType};
+    worker.postMessage(params);
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  // Empty file.
+  createMozSlice(createFileWithData(""), 0, 0, 0);
+
+  // Typical use case.
+  createMozSlice(createFileWithData("Hello"), 1, 1, 2);
+
+  // Longish file.
+  var text = "";
+  for (var i = 0; i < 10000; ++i) {
+    text += "long";
+  }
+  createMozSlice(createFileWithData(text), 2000, 2000, 4000);
+
+  // Slice to different type.
+  createMozSlice(createFileWithData("text", "txt"), 0, 2, 2, "image/png");
+
+  // Length longer than blob.
+  createMozSlice(createFileWithData("text"), 0, 4, 20);
+
+  // Start longer than blob.
+  createMozSlice(createFileWithData("text"), 20, 0, 4);
+
+  // No optional arguments
+  createMozSlice(createFileWithData("text"), 0, 4);
+  createMozSlice(createFileWithData("text"), 2, 2);
+
+  ]]>
+  </script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_filePosting.xul
@@ -0,0 +1,88 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+     target="_blank">Mozilla Bug 664783</a>
+
+  <div id="content" style="display: none">
+    <input id="fileList" type="file"></input>
+  </div>
+
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 664783 **/
+
+  var fileNum = 0;
+
+  /**
+   * Create a file which contains the given data.
+   */
+  function createFileWithData(fileData) {
+    var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+    testFile.append("workerFilePosting" + fileNum++);
+
+    var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                        .createInstance(Components.interfaces.nsIFileOutputStream);
+    outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                   0666, 0);
+    outStream.write(fileData, fileData.length);
+    outStream.close();
+
+    var fileList = document.getElementById('fileList');
+    fileList.value = testFile.path;
+
+    return fileList.files[0];
+  }
+
+  /**
+   * Create a worker which posts the same file given. Used to test cloning of files.
+   * Checks the size, type, name and path of the file posted from the worker to ensure it
+   * is the same as the original.
+   */
+  function postFile(file) {
+    var worker = new Worker("file_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    worker.onmessage = function(event) {
+      is(event.data.size, file.size, "size of file posted from worker does not match file posted to worker.");
+      is(event.data.type, file.type, "type of file posted from worker does not match file posted to worker.");
+      is(event.data.name, file.name, "name of file posted from worker does not match file posted to worker.");
+      is(event.data.mozFullPath, file.mozFullPath, "mozFullPath of file posted from worker does not match file posted to worker.");
+      SimpleTest.finish();
+    };
+
+    worker.postMessage(file);
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  // Empty file.
+  postFile(createFileWithData(""));
+
+  // Typical use case.
+  postFile(createFileWithData("Hello"));
+
+  ]]>
+  </script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_fileReadMozSlice.xul
@@ -0,0 +1,91 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+     target="_blank">Mozilla Bug 664783</a>
+
+  <div id="content" style="display: none">
+    <input id="fileList" type="file"></input>
+  </div>
+
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 664783 **/
+
+  var fileNum = 0;
+
+  /**
+   * Create a file which contains the given data.
+   */
+  function createFileWithData(fileData) {
+    var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+    testFile.append("workerReadMozSlice" + fileNum++);
+
+    var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                        .createInstance(Components.interfaces.nsIFileOutputStream);
+    outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                   0666, 0);
+    outStream.write(fileData, fileData.length);
+    outStream.close();
+
+    var fileList = document.getElementById('fileList');
+    fileList.value = testFile.path;
+
+    return fileList.files[0];
+  }
+
+  /**
+   * Creates a worker which slices a blob to the given start and end offset and
+   * reads the content as text.
+   */
+  function readMozSlice(blob, start, end, expectedText) {
+    var worker = new Worker("fileReadMozSlice_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    worker.onmessage = function(event) {
+      is(event.data, expectedText, "Text from sliced blob in worker is incorrect.");
+      SimpleTest.finish();
+    };
+
+    var params = {blob: blob, start: start, end: end};
+    worker.postMessage(params);
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  // Empty file.
+  readMozSlice(createFileWithData(""), 0, 0, "");
+
+  // Typical use case.
+  readMozSlice(createFileWithData("HelloBye"), 5, 8, "Bye");
+
+  // End offset too large.
+  readMozSlice(createFileWithData("HelloBye"), 5, 9, "Bye");
+
+  // Start of file.
+  readMozSlice(createFileWithData("HelloBye"), 0, 5, "Hello");
+
+  ]]>
+  </script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_fileReaderSync.xul
@@ -0,0 +1,200 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+     target="_blank">Mozilla Bug 664783</a>
+
+  <div id="content" style="display: none">
+    <input id="fileList" type="file"></input>
+  </div>
+
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 664783 **/
+
+  var fileNum = 0;
+
+  /**
+   * Create a file which contains the given data and optionally adds the specified file extension.
+   */
+  function createFileWithData(fileData, /** optional */ extension) {
+    var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+    var fileExtension = (extension == undefined) ? "" : "." + extension;
+    testFile.append("workerFileReaderSync" + fileNum++ + fileExtension);
+
+    var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                        .createInstance(Components.interfaces.nsIFileOutputStream);
+    outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                   0666, 0);
+    outStream.write(fileData, fileData.length);
+    outStream.close();
+
+    var fileList = document.getElementById('fileList');
+    fileList.value = testFile.path;
+
+    return fileList.files[0];
+  }
+
+  function convertToUTF16(s) {
+    res = "";
+    for (var i = 0; i < s.length; ++i) {
+      c = s.charCodeAt(i);
+      res += String.fromCharCode(c >>> 8, c & 255);
+    }
+    return res;
+  }
+
+  /**
+   * Converts the given string to a data URL of the specified mime type.
+   */
+  function convertToDataURL(mime, s) {
+    return "data:" + mime + ";base64," + btoa(s);
+  }
+
+  /**
+   * Create a worker to read a file containing fileData using FileReaderSync and
+   * checks the return type against the expected type. Optionally set an encoding
+   * for reading the file as text.
+   */
+  function readFileData(fileData, expectedText, /** optional */ encoding) {
+    var worker = new Worker("fileReaderSync_worker.js");
+
+    worker.onmessage = function(event) {
+      is(event.data.text, expectedText, "readAsText in worker returned incorrect result.");
+      is(event.data.bin, fileData, "readAsBinaryString in worker returned incorrect result.");
+      is(event.data.url, convertToDataURL("application/octet-stream", fileData), "readAsDataURL in worker returned incorrect result.");
+      is(event.data.arrayBuffer.byteLength, fileData.length, "readAsArrayBuffer returned buffer of incorrect length.");
+      SimpleTest.finish();
+    };
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    var params = {file: createFileWithData(fileData), encoding: encoding};
+
+    worker.postMessage(params);
+
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  /**
+   * Create a worker which reuses a FileReaderSync to read multiple files as DataURLs.
+   */
+  function reuseReaderForURL(files, expected) {
+    var worker = new Worker("fileReaderSync_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    var k = 0;
+    worker.onmessage = function(event) {
+      is(event.data.url, expected[k], "readAsDataURL in worker returned incorrect result when reusing FileReaderSync.");
+      k++;
+      SimpleTest.finish();
+    };
+
+    for (var i = 0; i < files.length; ++i) {
+      var params = {file: files[i], encoding: undefined};
+      worker.postMessage(params);
+      SimpleTest.waitForExplicitFinish();
+    }
+  }
+
+  /**
+   * Create a worker which reuses a FileReaderSync to read multiple files as text.
+   */
+  function reuseReaderForText(fileData, expected) {
+    var worker = new Worker("fileReaderSync_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    var k = 0;
+    worker.onmessage = function(event) {
+      is(event.data.text, expected[k++], "readAsText in worker returned incorrect result when reusing FileReaderSync.");
+      SimpleTest.finish();
+    };
+
+    for (var i = 0; i < fileData.length; ++i) {
+      var params = {file: createFileWithData(fileData[i]), encoding: undefined};
+      worker.postMessage(params);
+      SimpleTest.waitForExplicitFinish();
+    }
+  }
+
+
+  /**
+   * Creates a a worker which reads a file containing fileData as an ArrayBuffer.
+   * Verifies that the ArrayBuffer when interpreted as a string matches the original data.
+   */
+  function readArrayBuffer(fileData) {
+    var worker = new Worker("fileReaderSync_worker.js");
+
+    worker.onmessage = function(event) {
+      var view = new Uint8Array(event.data.arrayBuffer);
+      is(event.data.arrayBuffer.byteLength, fileData.length, "readAsArrayBuffer returned buffer of incorrect length.");
+      is(String.fromCharCode.apply(String, view), fileData, "readAsArrayBuffer returned buffer containing incorrect data.");
+      SimpleTest.finish();
+    };
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    var params = {file: createFileWithData(fileData), encoding: undefined};
+
+    worker.postMessage(params);
+
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  // Empty file.
+  readFileData("", "");
+
+  // Typical use case.
+  readFileData("text", "text");
+
+  // Test reading UTF-16 characters.
+  readFileData(convertToUTF16("text"), "text", "UTF-16");
+
+  // First read a file of type "text/plain", then read a file of type "application/octet-stream".
+  reuseReaderForURL([createFileWithData("text", "txt"), createFileWithData("text")],
+                    [convertToDataURL("text/plain", "text"),
+                     convertToDataURL("application/octet-stream", "text")]);
+
+  // First read UTF-16 characters marked using BOM, then read UTF-8 characters.
+  reuseReaderForText([convertToUTF16("\ufefftext"), "text"],
+                     ["text", "text"]);
+
+  // Reading data as ArrayBuffer.
+  readArrayBuffer("");
+  readArrayBuffer("text");
+
+  ]]>
+  </script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_fileReaderSyncErrors.xul
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+     target="_blank">Mozilla Bug 664783</a>
+
+  <div id="content" style="display: none">
+    <input id="fileList" type="file"></input>
+  </div>
+
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 664783 **/
+
+  var fileNum = 0;
+
+  /**
+   * Create a file which contains the given data.
+   */
+  function createFileWithData(fileData) {
+    var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+    testFile.append("workerFileReaderSyncErrors" + fileNum++);
+
+    var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                        .createInstance(Components.interfaces.nsIFileOutputStream);
+    outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                   0666, 0);
+    outStream.write(fileData, fileData.length);
+    outStream.close();
+
+    var fileList = document.getElementById('fileList');
+    fileList.value = testFile.path;
+
+    return fileList.files[0];
+  }
+
+  /**
+   * Creates a worker which runs errors cases.
+   */
+  function runWorkerErrors(file) {
+    var worker = new Worker("fileReaderSyncErrors_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    worker.onmessage = function(event) {
+      if(event.data == undefined) {
+        // Worker returns undefined when tests have finished running.
+        SimpleTest.finish();
+      } else {
+        // Otherwise worker will return results of tests to be evaluated.
+        is(event.data.actual, event.data.expected, event.data.message);
+      }
+    };
+
+    worker.postMessage(file);
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  // Run worker which creates exceptions.
+  runWorkerErrors(createFileWithData("text"));
+
+  ]]>
+  </script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_fileSubWorker.xul
@@ -0,0 +1,101 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+     target="_blank">Mozilla Bug 664783</a>
+
+  <div id="content" style="display: none">
+    <input id="fileList" type="file"></input>
+  </div>
+
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 664783 **/
+
+  var fileNum = 0;
+
+  /**
+   * Create a file which contains the given data and optionally adds the specified file extension.
+   */
+  function createFileWithData(fileData, /** optional */ extension) {
+    var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+                       .getService(Components.interfaces.nsIProperties)
+                       .get("ProfD", Components.interfaces.nsIFile);
+    var fileExtension = (extension == undefined) ? "" : "." + extension;
+    testFile.append("workerSubWorker" + fileNum++ + fileExtension);
+
+    var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+                        .createInstance(Components.interfaces.nsIFileOutputStream);
+    outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+                   0666, 0);
+    outStream.write(fileData, fileData.length);
+    outStream.close();
+
+    var fileList = document.getElementById('fileList');
+    fileList.value = testFile.path;
+
+    return fileList.files[0];
+  }
+
+  /**
+   * Create a worker to access file properties.
+   */
+  function accessFileProperties(file, expectedSize, expectedType) {
+    var worker = new Worker("fileSubWorker_worker.js");
+
+    worker.onerror = function(event) {
+      ok(false, "Worker had an error: " + event.data);
+      SimpleTest.finish();
+    };
+
+    worker.onmessage = function(event) {
+      if (event.data == undefined) {
+        ok(false, "Worker had an error.");
+      } else {
+        is(event.data.size, expectedSize, "size proproperty accessed from worker is not the same as on main thread.");
+        is(event.data.type, expectedType, "type proproperty accessed from worker is incorrect.");
+        is(event.data.name, file.name, "name proproperty accessed from worker is incorrect.");
+        is(event.data.mozFullPath, file.mozFullPath, "mozFullPath proproperty accessed from worker is not the same as on main thread.");
+      }
+      SimpleTest.finish();
+    };
+
+    worker.postMessage(file);
+    SimpleTest.waitForExplicitFinish();
+  }
+
+  // Empty file.
+  accessFileProperties(createFileWithData(""), 0, "");
+
+  // Typical use case.
+  accessFileProperties(createFileWithData("Hello"), 5, "");
+
+  // Longish file.
+  var text = "";
+  for (var i = 0; i < 10000; ++i) {
+    text += "long";
+  }
+  accessFileProperties(createFileWithData(text), 40000, "");
+
+  // Type detection based on extension.
+  accessFileProperties(createFileWithData("text", "txt"), 4, "text/plain");
+
+  ]]>
+  </script>
+</window>