Bug 793025: Convert FileReaderSync to WebIDL bindings. r=bz
authorKyle Huey <khuey@kylehuey.com>
Thu, 20 Sep 2012 19:47:47 -0700
changeset 107678 4050caa0b9c5684cbabfb37c55c788da955d071f
parent 107677 eed7b92769c36585304e3c8eb3d80315f66d315d
child 107679 43ab91c9c8e16cf45a1724d2f32f4ba10fca6296
push id15165
push userkhuey@mozilla.com
push dateFri, 21 Sep 2012 02:48:00 +0000
treeherdermozilla-inbound@4050caa0b9c5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs793025
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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;
   }