dom/workers/FileReaderSync.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/workers/FileReaderSync.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,301 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "FileReaderSync.h"
    1.11 +
    1.12 +#include "jsfriendapi.h"
    1.13 +#include "mozilla/Base64.h"
    1.14 +#include "mozilla/dom/EncodingUtils.h"
    1.15 +#include "nsContentUtils.h"
    1.16 +#include "mozilla/dom/FileReaderSyncBinding.h"
    1.17 +#include "nsCExternalHandlerService.h"
    1.18 +#include "nsComponentManagerUtils.h"
    1.19 +#include "nsCOMPtr.h"
    1.20 +#include "nsDOMClassInfoID.h"
    1.21 +#include "nsError.h"
    1.22 +#include "nsIDOMFile.h"
    1.23 +#include "nsIConverterInputStream.h"
    1.24 +#include "nsIInputStream.h"
    1.25 +#include "nsISeekableStream.h"
    1.26 +#include "nsISupportsImpl.h"
    1.27 +#include "nsNetUtil.h"
    1.28 +#include "nsServiceManagerUtils.h"
    1.29 +
    1.30 +#include "File.h"
    1.31 +#include "RuntimeService.h"
    1.32 +
    1.33 +USING_WORKERS_NAMESPACE
    1.34 +using namespace mozilla;
    1.35 +using mozilla::dom::Optional;
    1.36 +using mozilla::dom::GlobalObject;
    1.37 +
    1.38 +// static
    1.39 +already_AddRefed<FileReaderSync>
    1.40 +FileReaderSync::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
    1.41 +{
    1.42 +  nsRefPtr<FileReaderSync> frs = new FileReaderSync();
    1.43 +
    1.44 +  return frs.forget();
    1.45 +}
    1.46 +
    1.47 +JSObject*
    1.48 +FileReaderSync::WrapObject(JSContext* aCx)
    1.49 +{
    1.50 +  return FileReaderSyncBinding_workers::Wrap(aCx, this);
    1.51 +}
    1.52 +
    1.53 +void
    1.54 +FileReaderSync::ReadAsArrayBuffer(JSContext* aCx,
    1.55 +                                  JS::Handle<JSObject*> aScopeObj,
    1.56 +                                  JS::Handle<JSObject*> aBlob,
    1.57 +                                  JS::MutableHandle<JSObject*> aRetval,
    1.58 +                                  ErrorResult& aRv)
    1.59 +{
    1.60 +  nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
    1.61 +  if (!blob) {
    1.62 +    aRv.Throw(NS_ERROR_INVALID_ARG);
    1.63 +    return;
    1.64 +  }
    1.65 +
    1.66 +  uint64_t blobSize;
    1.67 +  nsresult rv = blob->GetSize(&blobSize);
    1.68 +  if (NS_FAILED(rv)) {
    1.69 +    aRv.Throw(rv);
    1.70 +    return;
    1.71 +  }
    1.72 +
    1.73 +  JS::Rooted<JSObject*> jsArrayBuffer(aCx, JS_NewArrayBuffer(aCx, blobSize));
    1.74 +  if (!jsArrayBuffer) {
    1.75 +    // XXXkhuey we need a way to indicate to the bindings that the call failed
    1.76 +    // but there's already a pending exception that we should not clobber.
    1.77 +    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    1.78 +    return;
    1.79 +  }
    1.80 +
    1.81 +  uint32_t bufferLength = JS_GetArrayBufferByteLength(jsArrayBuffer);
    1.82 +  uint8_t* arrayBuffer = JS_GetStableArrayBufferData(aCx, jsArrayBuffer);
    1.83 +  if (!arrayBuffer) {
    1.84 +    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    1.85 +    return;
    1.86 +  }
    1.87 +
    1.88 +  nsCOMPtr<nsIInputStream> stream;
    1.89 +  rv = blob->GetInternalStream(getter_AddRefs(stream));
    1.90 +  if (NS_FAILED(rv)) {
    1.91 +    aRv.Throw(rv);
    1.92 +    return;
    1.93 +  }
    1.94 +
    1.95 +  uint32_t numRead;
    1.96 +  rv = stream->Read((char*)arrayBuffer, bufferLength, &numRead);
    1.97 +  if (NS_FAILED(rv)) {
    1.98 +    aRv.Throw(rv);
    1.99 +    return;
   1.100 +  }
   1.101 +  NS_ASSERTION(numRead == bufferLength, "failed to read data");
   1.102 +
   1.103 +  aRetval.set(jsArrayBuffer);
   1.104 +}
   1.105 +
   1.106 +void
   1.107 +FileReaderSync::ReadAsBinaryString(JS::Handle<JSObject*> aBlob,
   1.108 +                                   nsAString& aResult,
   1.109 +                                   ErrorResult& aRv)
   1.110 +{
   1.111 +  nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
   1.112 +  if (!blob) {
   1.113 +    aRv.Throw(NS_ERROR_INVALID_ARG);
   1.114 +    return;
   1.115 +  }
   1.116 +
   1.117 +  nsCOMPtr<nsIInputStream> stream;
   1.118 +  nsresult rv = blob->GetInternalStream(getter_AddRefs(stream));
   1.119 +  if (NS_FAILED(rv)) {
   1.120 +    aRv.Throw(rv);
   1.121 +    return;
   1.122 +  }
   1.123 +
   1.124 +  uint32_t numRead;
   1.125 +  do {
   1.126 +    char readBuf[4096];
   1.127 +    rv = stream->Read(readBuf, sizeof(readBuf), &numRead);
   1.128 +    if (NS_FAILED(rv)) {
   1.129 +      aRv.Throw(rv);
   1.130 +      return;
   1.131 +    }
   1.132 +
   1.133 +    uint32_t oldLength = aResult.Length();
   1.134 +    AppendASCIItoUTF16(Substring(readBuf, readBuf + numRead), aResult);
   1.135 +    if (aResult.Length() - oldLength != numRead) {
   1.136 +      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   1.137 +      return;
   1.138 +    }
   1.139 +  } while (numRead > 0);
   1.140 +}
   1.141 +
   1.142 +void
   1.143 +FileReaderSync::ReadAsText(JS::Handle<JSObject*> aBlob,
   1.144 +                           const Optional<nsAString>& aEncoding,
   1.145 +                           nsAString& aResult,
   1.146 +                           ErrorResult& aRv)
   1.147 +{
   1.148 +  nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
   1.149 +  if (!blob) {
   1.150 +    aRv.Throw(NS_ERROR_INVALID_ARG);
   1.151 +    return;
   1.152 +  }
   1.153 +
   1.154 +  nsCOMPtr<nsIInputStream> stream;
   1.155 +  nsresult rv = blob->GetInternalStream(getter_AddRefs(stream));
   1.156 +  if (NS_FAILED(rv)) {
   1.157 +    aRv.Throw(rv);
   1.158 +    return;
   1.159 +  }
   1.160 +
   1.161 +  nsAutoCString encoding;
   1.162 +  unsigned char sniffBuf[3] = { 0, 0, 0 };
   1.163 +  uint32_t numRead;
   1.164 +  rv = stream->Read(reinterpret_cast<char*>(sniffBuf),
   1.165 +                    sizeof(sniffBuf), &numRead);
   1.166 +  if (NS_FAILED(rv)) {
   1.167 +    aRv.Throw(rv);
   1.168 +    return;
   1.169 +  }
   1.170 +
   1.171 +  // The BOM sniffing is baked into the "decode" part of the Encoding
   1.172 +  // Standard, which the File API references.
   1.173 +  if (!nsContentUtils::CheckForBOM(sniffBuf, numRead, encoding)) {
   1.174 +    // BOM sniffing failed. Try the API argument.
   1.175 +    if (!aEncoding.WasPassed() ||
   1.176 +        !EncodingUtils::FindEncodingForLabel(aEncoding.Value(),
   1.177 +                                             encoding)) {
   1.178 +      // API argument failed. Try the type property of the blob.
   1.179 +      nsAutoString type16;
   1.180 +      blob->GetType(type16);
   1.181 +      NS_ConvertUTF16toUTF8 type(type16);
   1.182 +      nsAutoCString specifiedCharset;
   1.183 +      bool haveCharset;
   1.184 +      int32_t charsetStart, charsetEnd;
   1.185 +      NS_ExtractCharsetFromContentType(type,
   1.186 +                                       specifiedCharset,
   1.187 +                                       &haveCharset,
   1.188 +                                       &charsetStart,
   1.189 +                                       &charsetEnd);
   1.190 +      if (!EncodingUtils::FindEncodingForLabel(specifiedCharset, encoding)) {
   1.191 +        // Type property failed. Use UTF-8.
   1.192 +        encoding.AssignLiteral("UTF-8");
   1.193 +      }
   1.194 +    }
   1.195 +  }
   1.196 +
   1.197 +  nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(stream);
   1.198 +  if (!seekable) {
   1.199 +    aRv.Throw(NS_ERROR_FAILURE);
   1.200 +    return;
   1.201 +  }
   1.202 +
   1.203 +  // Seek to 0 because to undo the BOM sniffing advance. UTF-8 and UTF-16
   1.204 +  // decoders will swallow the BOM.
   1.205 +  rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
   1.206 +  if (NS_FAILED(rv)) {
   1.207 +    aRv.Throw(rv);
   1.208 +    return;
   1.209 +  }
   1.210 +
   1.211 +  rv = ConvertStream(stream, encoding.get(), aResult);
   1.212 +  if (NS_FAILED(rv)) {
   1.213 +    aRv.Throw(rv);
   1.214 +    return;
   1.215 +  }
   1.216 +}
   1.217 +
   1.218 +void
   1.219 +FileReaderSync::ReadAsDataURL(JS::Handle<JSObject*> aBlob, nsAString& aResult,
   1.220 +                              ErrorResult& aRv)
   1.221 +{
   1.222 +  nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
   1.223 +  if (!blob) {
   1.224 +    aRv.Throw(NS_ERROR_INVALID_ARG);
   1.225 +    return;
   1.226 +  }
   1.227 +
   1.228 +  nsAutoString scratchResult;
   1.229 +  scratchResult.AssignLiteral("data:");
   1.230 +
   1.231 +  nsString contentType;
   1.232 +  blob->GetType(contentType);
   1.233 +
   1.234 +  if (contentType.IsEmpty()) {
   1.235 +    scratchResult.AppendLiteral("application/octet-stream");
   1.236 +  } else {
   1.237 +    scratchResult.Append(contentType);
   1.238 +  }
   1.239 +  scratchResult.AppendLiteral(";base64,");
   1.240 +
   1.241 +  nsCOMPtr<nsIInputStream> stream;
   1.242 +  nsresult rv = blob->GetInternalStream(getter_AddRefs(stream));
   1.243 +  if (NS_FAILED(rv)) {
   1.244 +    aRv.Throw(rv);
   1.245 +    return;
   1.246 +  }
   1.247 +
   1.248 +  uint64_t size;
   1.249 +  rv = blob->GetSize(&size);
   1.250 +  if (NS_FAILED(rv)) {
   1.251 +    aRv.Throw(rv);
   1.252 +    return;
   1.253 +  }
   1.254 +
   1.255 +  nsCOMPtr<nsIInputStream> bufferedStream;
   1.256 +  rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, size);
   1.257 +  if (NS_FAILED(rv)) {
   1.258 +    aRv.Throw(rv);
   1.259 +    return;
   1.260 +  }
   1.261 +
   1.262 +  nsAutoString encodedData;
   1.263 +  rv = Base64EncodeInputStream(bufferedStream, encodedData, size);
   1.264 +  if (NS_FAILED(rv)) {
   1.265 +    aRv.Throw(rv);
   1.266 +    return;
   1.267 +  }
   1.268 +
   1.269 +  scratchResult.Append(encodedData);
   1.270 +
   1.271 +  aResult = scratchResult;
   1.272 +}
   1.273 +
   1.274 +nsresult
   1.275 +FileReaderSync::ConvertStream(nsIInputStream *aStream,
   1.276 +                              const char *aCharset,
   1.277 +                              nsAString &aResult)
   1.278 +{
   1.279 +  nsCOMPtr<nsIConverterInputStream> converterStream =
   1.280 +    do_CreateInstance("@mozilla.org/intl/converter-input-stream;1");
   1.281 +  NS_ENSURE_TRUE(converterStream, NS_ERROR_FAILURE);
   1.282 +
   1.283 +  nsresult rv = converterStream->Init(aStream, aCharset, 8192,
   1.284 +                  nsIConverterInputStream::DEFAULT_REPLACEMENT_CHARACTER);
   1.285 +  NS_ENSURE_SUCCESS(rv, rv);
   1.286 +
   1.287 +  nsCOMPtr<nsIUnicharInputStream> unicharStream =
   1.288 +    do_QueryInterface(converterStream);
   1.289 +  NS_ENSURE_TRUE(unicharStream, NS_ERROR_FAILURE);
   1.290 +
   1.291 +  uint32_t numChars;
   1.292 +  nsString result;
   1.293 +  while (NS_SUCCEEDED(unicharStream->ReadString(8192, result, &numChars)) &&
   1.294 +         numChars > 0) {
   1.295 +    uint32_t oldLength = aResult.Length();
   1.296 +    aResult.Append(result);
   1.297 +    if (aResult.Length() - oldLength != result.Length()) {
   1.298 +      return NS_ERROR_OUT_OF_MEMORY;
   1.299 +    }
   1.300 +  }
   1.301 +
   1.302 +  return rv;
   1.303 +}
   1.304 +

mercurial