Bug 793025: Convert FileReaderSync to WebIDL bindings. r=bz
authorKyle Huey <khuey@kylehuey.com>
Thu, 20 Sep 2012 19:47:47 -0700
changeset 107813 4050caa0b9c5684cbabfb37c55c788da955d071f
parent 107812 eed7b92769c36585304e3c8eb3d80315f66d315d
child 107814 43ab91c9c8e16cf45a1724d2f32f4ba10fca6296
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewersbz
bugs793025
milestone18.0a1
Bug 793025: Convert FileReaderSync to WebIDL bindings. r=bz
dom/bindings/Bindings.conf
dom/bindings/Codegen.py
dom/bindings/test/TestBindingHeader.h
dom/bindings/test/TestCodeGen.webidl
dom/webidl/FileReaderSync.webidl
dom/webidl/WebIDL.mk
dom/workers/DOMBindingBase.cpp
dom/workers/DOMBindingBase.h
dom/workers/DOMBindingInlines.h
dom/workers/FileReaderSync.cpp
dom/workers/FileReaderSync.h
dom/workers/FileReaderSyncPrivate.cpp
dom/workers/FileReaderSyncPrivate.h
dom/workers/Makefile.in
dom/workers/WorkerScope.cpp
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -165,16 +165,22 @@ DOMInterfaces = {
 'FileList': [
 {
     'nativeType': 'nsDOMFileList',
     'headerFile': 'nsDOMFile.h',
     'prefable': True,
     'resultNotAddRefed': [ 'item' ]
 }],
 
+'FileReaderSync': [
+{
+    'workers': True,
+    'headerFile': 'mozilla/dom/workers/bindings/FileReaderSync.h'
+}],
+
 'FormData': [
 {
 },
 {
     'workers': True,
 }],
 
 'HTMLCollection': [
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2844,23 +2844,27 @@ def infallibleForMember(member, type, de
 
     CURRENT ASSUMPTIONS:
         We assume that successCode for wrapping up return values cannot contain
         failure conditions.
     """
     return getWrapTemplateForType(type, descriptorProvider, 'result', None,\
                                   memberIsCreator(member))[1]
 
-def typeNeedsCx(type):
+def typeNeedsCx(type, retVal=False):
     if type is None:
         return False
+    if type.nullable():
+        type = type.inner
     if type.isSequence() or type.isArray():
         type = type.inner
     if type.isUnion():
         return any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes)
+    if retVal and type.isSpiderMonkeyInterface():
+        return True
     return type.isCallback() or type.isAny() or type.isObject()
 
 # Returns a tuple consisting of a CGThing containing the type of the return
 # value, or None if there is no need for a return value, and a boolean signaling
 # whether the return value is passed in an out parameter.
 def getRetvalDeclarationForType(returnType, descriptorProvider,
                                 resultAlreadyAddRefed):
     if returnType is None or returnType.isVoid():
@@ -2944,17 +2948,17 @@ class CGCallGenerator(CGThing):
             args.append(CGGeneric(name))
 
         # Return values that go in outparams go here
         if resultOutParam:
             args.append(CGGeneric("result"))
         if isFallible:
             args.append(CGGeneric("rv"))
 
-        needsCx = (typeNeedsCx(returnType) or
+        needsCx = (typeNeedsCx(returnType, True) or
                    any(typeNeedsCx(a.type) for (a, _) in arguments) or
                    'implicitJSContext' in extendedAttributes)
 
         if not "cx" in argsPre and needsCx:
             args.prepend(CGGeneric("cx"))
 
         # Build up our actual call
         self.cgRoot = CGList([], "\n")
@@ -3611,17 +3615,17 @@ class CGMemberJITInfo(CGThing):
     def __init__(self, descriptor, member):
         self.member = member
         self.descriptor = descriptor
 
     def declare(self):
         return ""
 
     def defineJitInfo(self, infoName, opName, infallible):
-        protoID = "prototypes::id::%s" % self.descriptor.interface.identifier.name
+        protoID = "prototypes::id::%s" % self.descriptor.name
         depth = "PrototypeTraits<%s>::Depth" % protoID
         failstr = "true" if infallible else "false"
         return ("\n"
                 "const JSJitInfo %s = {\n"
                 "  %s,\n"
                 "  %s,\n"
                 "  %s,\n"
                 "  %s,  /* isInfallible. False in setters. */\n"
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -291,16 +291,17 @@ public:
   void PassOptionalNullableSequence(const Optional<Nullable<Sequence<int32_t> > >&);
   void PassOptionalNullableSequenceWithDefaultValue(const Nullable< Sequence<int32_t> >&);
   void PassOptionalObjectSequence(const Optional<Sequence<OwningNonNull<TestInterface> > >&);
 
   void ReceiveStringSequence(nsTArray<nsString>&);
   void PassStringSequence(const Sequence<nsString>&);
 
   void ReceiveAnySequence(JSContext*, nsTArray<JS::Value>&);
+  void ReceiveNullableAnySequence(JSContext*, Nullable<nsTArray<JS::Value> >);
 
   // Typed array types
   void PassArrayBuffer(ArrayBuffer&);
   void PassNullableArrayBuffer(ArrayBuffer*);
   void PassOptionalArrayBuffer(const Optional<ArrayBuffer>&);
   void PassOptionalNullableArrayBuffer(const Optional<ArrayBuffer*>&);
   void PassOptionalNullableArrayBufferWithDefaultValue(ArrayBuffer*);
   void PassArrayBufferView(ArrayBufferView&);
@@ -308,17 +309,17 @@ public:
   void PassInt16Array(Int16Array&);
   void PassInt32Array(Int32Array&);
   void PassUint8Array(Uint8Array&);
   void PassUint16Array(Uint16Array&);
   void PassUint32Array(Uint32Array&);
   void PassUint8ClampedArray(Uint8ClampedArray&);
   void PassFloat32Array(Float32Array&);
   void PassFloat64Array(Float64Array&);
-  JSObject* ReceiveUint8Array();
+  JSObject* ReceiveUint8Array(JSContext*);
 
   // String types
   void PassString(const nsAString&);
   void PassNullableString(const nsAString&);
   void PassOptionalString(const Optional<nsAString>&);
   void PassOptionalStringWithDefaultValue(const nsAString&);
   void PassOptionalNullableString(const Optional<nsAString>&);
   void PassOptionalNullableStringWithDefaultValue(const nsAString&);
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -212,16 +212,17 @@ interface TestInterface {
   void passOptionalNullableSequence(optional sequence<long>? arg);
   void passOptionalNullableSequenceWithDefaultValue(optional sequence<long>? arg = null);
   void passOptionalObjectSequence(optional sequence<TestInterface> arg);
 
   sequence<DOMString> receiveStringSequence();
   void passStringSequence(sequence<DOMString> arg);
 
   sequence<any> receiveAnySequence();
+  sequence<any>? receiveNullableAnySequence();
 
   // Typed array types
   void passArrayBuffer(ArrayBuffer arg);
   void passNullableArrayBuffer(ArrayBuffer? arg);
   void passOptionalArrayBuffer(optional ArrayBuffer arg);
   void passOptionalNullableArrayBuffer(optional ArrayBuffer? arg);
   void passOptionalNullableArrayBufferWithDefaultValue(optional ArrayBuffer? arg= null);
   void passArrayBufferView(ArrayBufferView arg);
new file mode 100644
--- /dev/null
+++ b/dom/webidl/FileReaderSync.webidl
@@ -0,0 +1,28 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://dev.w3.org/2006/webapi/FileAPI/
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+interface Blob;
+
+[Constructor]
+interface FileReaderSync {
+
+  // Synchronously return strings
+
+  [Throws]
+  ArrayBuffer readAsArrayBuffer(Blob blob);
+  [Throws]
+  DOMString readAsBinaryString(Blob blob);
+  [Throws]
+  DOMString readAsText(Blob blob, optional DOMString encoding);
+  [Throws]
+  DOMString readAsDataURL(Blob blob);
+};
--- a/dom/webidl/WebIDL.mk
+++ b/dom/webidl/WebIDL.mk
@@ -15,16 +15,17 @@ webidl_files = \
   ClientRectList.webidl \
   CSSStyleDeclaration.webidl \
   DOMTokenList.webidl \
   DOMSettableTokenList.webidl \
   Function.webidl \
   EventListener.webidl \
   EventTarget.webidl \
   FileList.webidl \
+  FileReaderSync.webidl \
   HTMLCollection.webidl \
   HTMLOptionsCollection.webidl \
   HTMLPropertiesCollection.webidl \
   NodeList.webidl \
   PaintRequestList.webidl \
   Performance.webidl \
   PerformanceNavigation.webidl \
   PerformanceTiming.webidl \
--- a/dom/workers/DOMBindingBase.cpp
+++ b/dom/workers/DOMBindingBase.cpp
@@ -24,16 +24,23 @@ DOMBindingBase::DOMBindingBase(JSContext
 
 DOMBindingBase::~DOMBindingBase()
 {
   if (!mJSContext) {
     AssertIsOnMainThread();
   }
 }
 
+NS_IMPL_ADDREF(DOMBindingBase)
+NS_IMPL_RELEASE(DOMBindingBase)
+NS_INTERFACE_MAP_BEGIN(DOMBindingBase)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+NS_INTERFACE_MAP_END
+
 void
 DOMBindingBase::_trace(JSTracer* aTrc)
 {
   JSObject* obj = GetJSObject();
   if (obj) {
     JS_CALL_OBJECT_TRACER(aTrc, obj, "cached wrapper");
   }
 }
--- a/dom/workers/DOMBindingBase.h
+++ b/dom/workers/DOMBindingBase.h
@@ -22,17 +22,18 @@ BEGIN_WORKERS_NAMESPACE
       aRv = _result; \
       return _retval; \
     } \
   PR_END_MACRO
 
 #define BINDING_ENSURE_SUCCESS(_cond, _result, _retval) \
   BINDING_ENSURE_TRUE(NS_SUCCEEDED(_cond), _result, _retval)
 
-class DOMBindingBase : public nsWrapperCache
+class DOMBindingBase : public nsWrapperCache,
+                       public nsISupports
 {
   JSContext* mJSContext;
   mutable nsCOMPtr<nsIThreadJSContextStack> mContextStack;
 
 protected:
   DOMBindingBase(JSContext* aCx);
   virtual ~DOMBindingBase();
 
@@ -41,17 +42,17 @@ protected:
 
   virtual void
   _finalize(JSFreeOp* aFop);
 
   JSContext*
   GetJSContextFromContextStack() const;
 
 public:
-  NS_INLINE_DECL_REFCOUNTING(DOMBindingBase)
+  NS_DECL_ISUPPORTS
 
   JSContext*
   GetJSContext() const
   {
     return mJSContext ? mJSContext : GetJSContextFromContextStack();
   }
 
 #ifdef DEBUG
--- a/dom/workers/DOMBindingInlines.h
+++ b/dom/workers/DOMBindingInlines.h
@@ -1,21 +1,23 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_workers_dombindinginlines_h__
 #define mozilla_dom_workers_dombindinginlines_h__
 
+#include "mozilla/dom/FileReaderSyncBinding.h"
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 #include "mozilla/dom/XMLHttpRequestUploadBinding.h"
 
 BEGIN_WORKERS_NAMESPACE
 
+class FileReaderSync;
 class XMLHttpRequest;
 class XMLHttpRequestUpload;
 
 namespace {
 
 template <class T>
 struct WrapPrototypeTraits
 { };
@@ -36,16 +38,17 @@ struct WrapPrototypeTraits
     static inline JSObject*                                                    \
     GetProtoObject(JSContext* aCx, JSObject* aGlobal)                          \
     {                                                                          \
       using namespace mozilla::dom;                                            \
       return _class##Binding_workers::GetProtoObject(aCx, aGlobal, aGlobal);   \
     }                                                                          \
   };
 
+SPECIALIZE_PROTO_TRAITS(FileReaderSync)
 SPECIALIZE_PROTO_TRAITS(XMLHttpRequest)
 SPECIALIZE_PROTO_TRAITS(XMLHttpRequestUpload)
 
 #undef SPECIALIZE_PROTO_TRAITS
 
 } // anonymous namespace
 
 template <class T>
--- a/dom/workers/FileReaderSync.cpp
+++ b/dom/workers/FileReaderSync.cpp
@@ -1,336 +1,397 @@
-/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FileReaderSync.h"
 
-#include "nsIDOMFile.h"
+#include "nsCExternalHandlerService.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsDOMClassInfoID.h"
 #include "nsError.h"
-
-#include "jsapi.h"
-#include "jsfriendapi.h"
-#include "nsJSUtils.h"
+#include "nsIDOMFile.h"
+#include "nsCharsetAlias.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 "File.h"
+#include "RuntimeService.h"
+#include "DOMBindingInlines.h"
 
-#include "Exceptions.h"
-#include "File.h"
-#include "FileReaderSyncPrivate.h"
-#include "WorkerInlines.h"
-
-#define FUNCTION_FLAGS \
-  JSPROP_ENUMERATE
+#include "mozilla/Base64.h"
 
 USING_WORKERS_NAMESPACE
+using mozilla::ErrorResult;
+using mozilla::dom::Optional;
 
-using mozilla::dom::workers::exceptions::ThrowDOMExceptionForNSResult;
+NS_IMPL_ADDREF_INHERITED(FileReaderSync, DOMBindingBase)
+NS_IMPL_RELEASE_INHERITED(FileReaderSync, DOMBindingBase)
+NS_INTERFACE_MAP_BEGIN(FileReaderSync)
+  NS_INTERFACE_MAP_ENTRY(nsICharsetDetectionObserver)
+NS_INTERFACE_MAP_END_INHERITING(DOMBindingBase)
+
+FileReaderSync::FileReaderSync(JSContext* aCx)
+  : DOMBindingBase(aCx)
+{
+}
+
+void
+FileReaderSync::_trace(JSTracer* aTrc)
+{
+  DOMBindingBase::_trace(aTrc);
+}
+
+void
+FileReaderSync::_finalize(JSFreeOp* aFop)
+{
+  DOMBindingBase::_finalize(aFop);
+}
 
-namespace {
+// static
+FileReaderSync*
+FileReaderSync::Constructor(JSContext* aCx, JSObject* aGlobal,
+                            ErrorResult& aRv)
+{
+  nsRefPtr<FileReaderSync> frs = new FileReaderSync(aCx);
+
+  if (!Wrap(aCx, aGlobal, frs)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  return frs;
+}
+
+JSObject*
+FileReaderSync::ReadAsArrayBuffer(JSContext* aCx, JSObject* aBlob,
+                                  ErrorResult& aRv)
+{
+  nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
+  if (!blob) {
+    aRv.Throw(NS_ERROR_INVALID_ARG);
+    return nullptr;
+  }
 
-inline bool
-EnsureSucceededOrThrow(JSContext* aCx, nsresult rv)
+  uint64_t blobSize;
+  nsresult rv = blob->GetSize(&blobSize);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return nullptr;
+  }
+
+  JSObject* jsArrayBuffer = JS_NewArrayBuffer(aCx, blobSize);
+  if (!jsArrayBuffer) {
+    // XXXkhuey we need a way to indicate to the bindings that the call failed
+    // but there's already a pending exception that we should not clobber.
+    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    return nullptr;
+  }
+
+  uint32_t bufferLength = JS_GetArrayBufferByteLength(jsArrayBuffer, aCx);
+  uint8_t* arrayBuffer = JS_GetArrayBufferData(jsArrayBuffer, aCx);
+
+  nsCOMPtr<nsIInputStream> stream;
+  rv = blob->GetInternalStream(getter_AddRefs(stream));
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return nullptr;
+  }
+
+  uint32_t numRead;
+  rv = stream->Read((char*)arrayBuffer, bufferLength, &numRead);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return nullptr;
+  }
+  NS_ASSERTION(numRead == bufferLength, "failed to read data");
+
+  return jsArrayBuffer;
+}
+
+void
+FileReaderSync::ReadAsBinaryString(JSObject* aBlob, nsAString& aResult,
+                                   ErrorResult& aRv)
 {
-  if (NS_SUCCEEDED(rv)) {
-    return true;
+  nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
+  if (!blob) {
+    aRv.Throw(NS_ERROR_INVALID_ARG);
+    return;
+  }
+
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = blob->GetInternalStream(getter_AddRefs(stream));
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
   }
 
-  rv = rv == NS_ERROR_FILE_NOT_FOUND ?
-              NS_ERROR_DOM_FILE_NOT_FOUND_ERR :
-              NS_ERROR_DOM_FILE_NOT_READABLE_ERR;
-  ThrowDOMExceptionForNSResult(aCx, rv);
-  return false;
+  uint32_t numRead;
+  do {
+    char readBuf[4096];
+    rv = stream->Read(readBuf, sizeof(readBuf), &numRead);
+    if (NS_FAILED(rv)) {
+      aRv.Throw(rv);
+      return;
+    }
+
+    uint32_t oldLength = aResult.Length();
+    AppendASCIItoUTF16(Substring(readBuf, readBuf + numRead), aResult);
+    if (aResult.Length() - oldLength != numRead) {
+      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+      return;
+    }
+  } while (numRead > 0);
+}
+
+void
+FileReaderSync::ReadAsText(JSObject* aBlob,
+                           const Optional<nsAString>& aEncoding,
+                           nsAString& aResult,
+                           ErrorResult& aRv)
+{
+  nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
+  if (!blob) {
+    aRv.Throw(NS_ERROR_INVALID_ARG);
+    return;
+  }
+
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = blob->GetInternalStream(getter_AddRefs(stream));
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  nsCString charsetGuess;
+  if (!aEncoding.WasPassed() || aEncoding.Value().IsEmpty()) {
+    rv = GuessCharset(stream, charsetGuess);
+    if (NS_FAILED(rv)) {
+      aRv.Throw(rv);
+      return;
+    }
+
+    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(stream);
+    if (!seekable) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+
+    // Seek to 0 because guessing the charset advances the stream.
+    rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+    if (NS_FAILED(rv)) {
+      aRv.Throw(rv);
+      return;
+    }
+  } else {
+    CopyUTF16toUTF8(aEncoding.Value(), charsetGuess);
+  }
+
+  nsCString charset;
+  rv = nsCharsetAlias::GetPreferred(charsetGuess, charset);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  rv = ConvertStream(stream, charset.get(), aResult);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
+  }
 }
 
-inline nsIDOMBlob*
-GetDOMBlobFromJSObject(JSContext* aCx, JSObject* aObj) {
-  // aObj can be null as JS_ConvertArguments("o") successfully converts JS
-  // null to a null pointer to JSObject 
-  if (aObj) {
-    nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aObj);
-    if (blob) {
-      return blob;
+void
+FileReaderSync::ReadAsDataURL(JSObject* aBlob, nsAString& aResult,
+                              ErrorResult& aRv)
+{
+  nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
+  if (!blob) {
+    aRv.Throw(NS_ERROR_INVALID_ARG);
+    return;
+  }
+
+  nsAutoString scratchResult;
+  scratchResult.AssignLiteral("data:");
+
+  nsString contentType;
+  blob->GetType(contentType);
+
+  if (contentType.IsEmpty()) {
+    scratchResult.AppendLiteral("application/octet-stream");
+  } else {
+    scratchResult.Append(contentType);
+  }
+  scratchResult.AppendLiteral(";base64,");
+
+  nsCOMPtr<nsIInputStream> stream;
+  nsresult rv = blob->GetInternalStream(getter_AddRefs(stream));
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  uint64_t size;
+  rv = blob->GetSize(&size);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  nsCOMPtr<nsIInputStream> bufferedStream;
+  rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, size);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  nsAutoString encodedData;
+  rv = Base64EncodeInputStream(bufferedStream, encodedData, size);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  scratchResult.Append(encodedData);
+
+  aResult = scratchResult;
+}
+
+nsresult
+FileReaderSync::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);
+
+  uint32_t numChars;
+  nsString result;
+  while (NS_SUCCEEDED(unicharStream->ReadString(8192, result, &numChars)) &&
+         numChars > 0) {
+    uint32_t oldLength = aResult.Length();
+    aResult.Append(result);
+    if (aResult.Length() - oldLength != result.Length()) {
+      return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
-  JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
-                       aObj ? JS_GetClass(aObj)->name : "Object", "not a Blob.");
-  return NULL;
+  return rv;
 }
 
-class FileReaderSync
+nsresult
+FileReaderSync::GuessCharset(nsIInputStream *aStream, nsACString &aCharset)
 {
-  // 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(JSObject* aObj)
-  {
-    if (aObj) {
-      JSClass* classPtr = JS_GetClass(aObj);
-      if (classPtr == &sClass) {
-        FileReaderSyncPrivate* fileReader =
-          GetJSPrivateSafeish<FileReaderSyncPrivate>(aObj);
-        return fileReader;
-      }
-    }
-    return NULL;
-  }
-
-private:
-  static FileReaderSyncPrivate*
-  GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
-  {
-    FileReaderSyncPrivate* fileReader = GetPrivate(aObj);
-    if (fileReader) {
-      return fileReader;
-    }
-
-    JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL,
-                         JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName,
-                         JS_GetClass(aObj)->name);
-    return NULL;
-  }
-
-  static JSBool
-  Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
-  {
-    JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL);
-    if (!obj) {
-      return false;
-    }
-
-    FileReaderSyncPrivate* fileReader = new FileReaderSyncPrivate();
-    NS_ADDREF(fileReader);
-
-    SetJSPrivateSafeish(obj, fileReader);
+  // 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!");
 
-    JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(obj));
-    return true;
-  }
-
-  static void
-  Finalize(JSFreeOp* aFop, JSObject* aObj)
-  {
-    JS_ASSERT(JS_GetClass(aObj) == &sClass);
-    FileReaderSyncPrivate* fileReader =
-      GetJSPrivateSafeish<FileReaderSyncPrivate>(aObj);
-    NS_IF_RELEASE(fileReader);
-  }
-
-  static JSBool
-  ReadAsArrayBuffer(JSContext* aCx, unsigned aArgc, jsval* aVp)
-  {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
-    if (!obj) {
-      return false;
-    }
-
-    FileReaderSyncPrivate* fileReader =
-      GetInstancePrivate(aCx, obj, "readAsArrayBuffer");
-    if (!fileReader) {
-      return false;
-    }
+    // No universal charset detector, try the default charset detector
+    const nsACString& detectorName = runtime->GetDetectorName();
 
-    JSObject* jsBlob;
-    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o", &jsBlob)) {
-      return false;
-    }
-
-    nsIDOMBlob* blob = GetDOMBlobFromJSObject(aCx, jsBlob);
-    if (!blob) {
-      return false;
-    }
-
-    uint64_t blobSize;
-    nsresult rv = blob->GetSize(&blobSize);
-    if (!EnsureSucceededOrThrow(aCx, rv)) {
-      return false;
+    if (!detectorName.IsEmpty()) {
+      nsAutoCString detectorContractID;
+      detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
+      detectorContractID += detectorName;
+      detector = do_CreateInstance(detectorContractID.get());
     }
-
-    JSObject* jsArrayBuffer = JS_NewArrayBuffer(aCx, blobSize);
-    if (!jsArrayBuffer) {
-      return false;
-    }
-
-    uint32_t bufferLength = JS_GetArrayBufferByteLength(jsArrayBuffer, aCx);
-    uint8_t* arrayBuffer = JS_GetArrayBufferData(jsArrayBuffer, aCx);
-
-    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, unsigned aArgc, jsval* aVp)
-  {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
-    if (!obj) {
-      return false;
-    }
+  nsresult rv;
+  if (detector) {
+    detector->Init(this);
 
-    FileReaderSyncPrivate* fileReader =
-      GetInstancePrivate(aCx, obj, "readAsDataURL");
-    if (!fileReader) {
-      return false;
-    }
+    bool done;
+    uint32_t 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);
 
-    JSObject* jsBlob;
-    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o", &jsBlob)) {
-      return false;
-    }
+    rv = detector->Done();
+    NS_ENSURE_SUCCESS(rv, rv);
+  } else {
+    // no charset detector available, check the BOM
+    unsigned char sniffBuf[4];
+    uint32_t numRead;
+    rv = aStream->Read(reinterpret_cast<char*>(sniffBuf),
+                       sizeof(sniffBuf), &numRead);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    nsIDOMBlob* blob = GetDOMBlobFromJSObject(aCx, jsBlob);
-    if (!blob) {
-      return false;
-    }
-
-    nsString blobText;
-    nsresult rv = fileReader->ReadAsDataURL(blob, blobText);
-    if (!EnsureSucceededOrThrow(aCx, rv)) {
-      return false;
+    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";
     }
-
-    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, unsigned aArgc, jsval* aVp)
-  {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
-    if (!obj) {
-      return false;
-    }
-
-    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;
-    }
+  if (mCharset.IsEmpty()) {
+    RuntimeService* runtime = RuntimeService::GetService();
+    mCharset = runtime->GetSystemCharset();
+  }
 
-    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;
+  if (mCharset.IsEmpty()) {
+    // no sniffed or default charset, try UTF-8
+    mCharset.AssignLiteral("UTF-8");
   }
 
-  static JSBool
-  ReadAsText(JSContext* aCx, unsigned aArgc, jsval* aVp)
-  {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
-    if (!obj) {
-      return false;
-    }
-
-    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;
-    }
+  aCharset = mCharset;
+  mCharset.Truncate();
 
-    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
-};
-
-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);
+  return NS_OK;
 }
 
-} // namespace filereadersync
+NS_IMETHODIMP
+FileReaderSync::Notify(const char* aCharset, nsDetectionConfident aConf)
+{
+  mCharset.Assign(aCharset);
 
-END_WORKERS_NAMESPACE
+  return NS_OK;
+}
--- a/dom/workers/FileReaderSync.h
+++ b/dom/workers/FileReaderSync.h
@@ -1,25 +1,59 @@
-/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_workers_filereadersync_h__
 #define mozilla_dom_workers_filereadersync_h__
 
 #include "Workers.h"
+#include "mozilla/dom/workers/bindings/DOMBindingBase.h"
 
-#include "jspubtd.h"
+#include "nsICharsetDetectionObserver.h"
+#include "nsStringGlue.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/BindingUtils.h"
+
+class nsIInputStream;
+class nsIDOMBlob;
 
 BEGIN_WORKERS_NAMESPACE
 
-namespace filereadersync {
+class FileReaderSync MOZ_FINAL : public DOMBindingBase,
+                                 public nsICharsetDetectionObserver
+{
+  nsCString mCharset;
+  nsresult ConvertStream(nsIInputStream *aStream, const char *aCharset,
+                         nsAString &aResult);
+  nsresult GuessCharset(nsIInputStream *aStream, nsACString &aCharset);
+
+public:
+  virtual void
+  _trace(JSTracer* aTrc) MOZ_OVERRIDE;
+
+  virtual void
+  _finalize(JSFreeOp* aFop) MOZ_OVERRIDE;
 
-bool
-InitClass(JSContext* aCx, JSObject* aGlobal);
+  static FileReaderSync*
+  Constructor(JSContext* aCx, JSObject* aGlobal, ErrorResult& aRv);
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  FileReaderSync(JSContext* aCx);
 
-} // namespace filereadersync
+  JSObject* ReadAsArrayBuffer(JSContext* aCx, JSObject* aBlob,
+                              ErrorResult& aRv);
+  void ReadAsBinaryString(JSObject* aBlob, nsAString& aResult,
+                          ErrorResult& aRv);
+  void ReadAsText(JSObject* aBlob, const Optional<nsAString>& aEncoding,
+                  nsAString& aResult, ErrorResult& aRv);
+  void ReadAsDataURL(JSObject* aBlob, nsAString& aResult, ErrorResult& aRv);
+
+  // From nsICharsetDetectionObserver
+  NS_IMETHOD Notify(const char *aCharset, nsDetectionConfident aConf);
+};
 
 END_WORKERS_NAMESPACE
 
 #endif // mozilla_dom_workers_filereadersync_h__
deleted file mode 100644
--- a/dom/workers/FileReaderSyncPrivate.cpp
+++ /dev/null
@@ -1,281 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "FileReaderSyncPrivate.h"
-
-#include "nsCExternalHandlerService.h"
-#include "nsComponentManagerUtils.h"
-#include "nsCOMPtr.h"
-#include "nsDOMClassInfoID.h"
-#include "nsError.h"
-#include "nsIDOMFile.h"
-#include "nsCharsetAlias.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, uint32_t aLength,
-                                         uint8* aBuffer)
-{
-  nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  uint32_t 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);
-
-  uint32_t numRead;
-  do {
-    char readBuf[4096];
-    rv = stream->Read(readBuf, sizeof(readBuf), &numRead);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    uint32_t 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);
-  }
-
-  nsCString charset;
-  rv = nsCharsetAlias::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);
-
-  uint64_t 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);
-
-  uint32_t numChars;
-  nsString result;
-  while (NS_SUCCEEDED(unicharStream->ReadString(8192, result, &numChars)) &&
-         numChars > 0) {
-    uint32_t 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()) {
-      nsAutoCString detectorContractID;
-      detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
-      detectorContractID += detectorName;
-      detector = do_CreateInstance(detectorContractID.get());
-    }
-  }
-
-  nsresult rv;
-  if (detector) {
-    detector->Init(this);
-
-    bool done;
-    uint32_t 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];
-    uint32_t 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;
-}
deleted file mode 100644
--- a/dom/workers/FileReaderSyncPrivate.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsDOMFileReaderSyncPrivate_h
-#define nsDOMFileReaderSyncPrivate_h
-
-#include "Workers.h"
-
-#include "nsICharsetDetectionObserver.h"
-#include "nsStringGlue.h"
-#include "mozilla/Attributes.h"
-
-class nsIInputStream;
-class nsIDOMBlob;
-
-BEGIN_WORKERS_NAMESPACE
-
-class FileReaderSyncPrivate MOZ_FINAL : 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, uint32_t 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
@@ -19,17 +19,16 @@ CPPSRCS = \
   ChromeWorkerScope.cpp \
   DOMBindingBase.cpp \
   Events.cpp \
   EventListenerManager.cpp \
   EventTarget.cpp \
   Exceptions.cpp \
   File.cpp \
   FileReaderSync.cpp \
-  FileReaderSyncPrivate.cpp \
   ImageData.cpp \
   Location.cpp \
   Navigator.cpp \
   Principal.cpp \
   RuntimeService.cpp \
   ScriptLoader.cpp \
   Worker.cpp \
   WorkerPrivate.cpp \
@@ -47,16 +46,17 @@ EXPORTS_NAMESPACES = \
 # Public stuff.
 EXPORTS_mozilla/dom/workers = Workers.h
 
 # Stuff needed for the bindings, not really public though.
 EXPORTS_mozilla/dom/workers/bindings = \
   DOMBindingBase.h \
   EventListenerManager.h \
   EventTarget.h \
+  FileReaderSync.h \
   WorkerFeature.h \
   XMLHttpRequestEventTarget.h \
   XMLHttpRequestUpload.h \
   XMLHttpRequest.h \
   $(NULL)
 
 LOCAL_INCLUDES = \
   -I$(topsrcdir)/content/base/src \
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -7,16 +7,17 @@
 #include "WorkerScope.h"
 
 #include "jsapi.h"
 #include "jsdbgapi.h"
 #include "mozilla/Util.h"
 #include "mozilla/dom/DOMJSClass.h"
 #include "mozilla/dom/EventTargetBinding.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/FileReaderSyncBinding.h"
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 #include "mozilla/dom/XMLHttpRequestUploadBinding.h"
 #include "mozilla/OSFileConstants.h"
 #include "nsTraceRefcnt.h"
 #include "xpcpublic.h"
 
 #ifdef ANDROID
 #include <android/log.h>
@@ -970,30 +971,30 @@ CreateDedicatedWorkerGlobalScope(JSConte
 
   if (!DefineOSFileConstants(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) ||
       !location::InitClass(aCx, global) ||
       !imagedata::InitClass(aCx, global) ||
       !navigator::InitClass(aCx, global)) {
     return NULL;
   }
 
   // Init other paris-bindings.  Use GetProtoObject so the proto will
   // be correctly cached in the proto cache.  Otherwise we'll end up
   // double-calling CreateInterfaceObjects when we actually create an
   // object which has these protos, which breaks things like
   // instanceof.
-  if (!XMLHttpRequestBinding_workers::GetProtoObject(aCx, global, global) ||
+  if (!FileReaderSyncBinding_workers::GetProtoObject(aCx, global, global) ||
+      !XMLHttpRequestBinding_workers::GetProtoObject(aCx, global, global) ||
       !XMLHttpRequestUploadBinding_workers::GetProtoObject(aCx, global, global)) {
     return NULL;
   }
 
   if (!JS_DefineProfilingFunctions(aCx, global)) {
     return NULL;
   }