dom/workers/FileReaderSync.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "FileReaderSync.h"
michael@0 8
michael@0 9 #include "jsfriendapi.h"
michael@0 10 #include "mozilla/Base64.h"
michael@0 11 #include "mozilla/dom/EncodingUtils.h"
michael@0 12 #include "nsContentUtils.h"
michael@0 13 #include "mozilla/dom/FileReaderSyncBinding.h"
michael@0 14 #include "nsCExternalHandlerService.h"
michael@0 15 #include "nsComponentManagerUtils.h"
michael@0 16 #include "nsCOMPtr.h"
michael@0 17 #include "nsDOMClassInfoID.h"
michael@0 18 #include "nsError.h"
michael@0 19 #include "nsIDOMFile.h"
michael@0 20 #include "nsIConverterInputStream.h"
michael@0 21 #include "nsIInputStream.h"
michael@0 22 #include "nsISeekableStream.h"
michael@0 23 #include "nsISupportsImpl.h"
michael@0 24 #include "nsNetUtil.h"
michael@0 25 #include "nsServiceManagerUtils.h"
michael@0 26
michael@0 27 #include "File.h"
michael@0 28 #include "RuntimeService.h"
michael@0 29
michael@0 30 USING_WORKERS_NAMESPACE
michael@0 31 using namespace mozilla;
michael@0 32 using mozilla::dom::Optional;
michael@0 33 using mozilla::dom::GlobalObject;
michael@0 34
michael@0 35 // static
michael@0 36 already_AddRefed<FileReaderSync>
michael@0 37 FileReaderSync::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
michael@0 38 {
michael@0 39 nsRefPtr<FileReaderSync> frs = new FileReaderSync();
michael@0 40
michael@0 41 return frs.forget();
michael@0 42 }
michael@0 43
michael@0 44 JSObject*
michael@0 45 FileReaderSync::WrapObject(JSContext* aCx)
michael@0 46 {
michael@0 47 return FileReaderSyncBinding_workers::Wrap(aCx, this);
michael@0 48 }
michael@0 49
michael@0 50 void
michael@0 51 FileReaderSync::ReadAsArrayBuffer(JSContext* aCx,
michael@0 52 JS::Handle<JSObject*> aScopeObj,
michael@0 53 JS::Handle<JSObject*> aBlob,
michael@0 54 JS::MutableHandle<JSObject*> aRetval,
michael@0 55 ErrorResult& aRv)
michael@0 56 {
michael@0 57 nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
michael@0 58 if (!blob) {
michael@0 59 aRv.Throw(NS_ERROR_INVALID_ARG);
michael@0 60 return;
michael@0 61 }
michael@0 62
michael@0 63 uint64_t blobSize;
michael@0 64 nsresult rv = blob->GetSize(&blobSize);
michael@0 65 if (NS_FAILED(rv)) {
michael@0 66 aRv.Throw(rv);
michael@0 67 return;
michael@0 68 }
michael@0 69
michael@0 70 JS::Rooted<JSObject*> jsArrayBuffer(aCx, JS_NewArrayBuffer(aCx, blobSize));
michael@0 71 if (!jsArrayBuffer) {
michael@0 72 // XXXkhuey we need a way to indicate to the bindings that the call failed
michael@0 73 // but there's already a pending exception that we should not clobber.
michael@0 74 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
michael@0 75 return;
michael@0 76 }
michael@0 77
michael@0 78 uint32_t bufferLength = JS_GetArrayBufferByteLength(jsArrayBuffer);
michael@0 79 uint8_t* arrayBuffer = JS_GetStableArrayBufferData(aCx, jsArrayBuffer);
michael@0 80 if (!arrayBuffer) {
michael@0 81 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
michael@0 82 return;
michael@0 83 }
michael@0 84
michael@0 85 nsCOMPtr<nsIInputStream> stream;
michael@0 86 rv = blob->GetInternalStream(getter_AddRefs(stream));
michael@0 87 if (NS_FAILED(rv)) {
michael@0 88 aRv.Throw(rv);
michael@0 89 return;
michael@0 90 }
michael@0 91
michael@0 92 uint32_t numRead;
michael@0 93 rv = stream->Read((char*)arrayBuffer, bufferLength, &numRead);
michael@0 94 if (NS_FAILED(rv)) {
michael@0 95 aRv.Throw(rv);
michael@0 96 return;
michael@0 97 }
michael@0 98 NS_ASSERTION(numRead == bufferLength, "failed to read data");
michael@0 99
michael@0 100 aRetval.set(jsArrayBuffer);
michael@0 101 }
michael@0 102
michael@0 103 void
michael@0 104 FileReaderSync::ReadAsBinaryString(JS::Handle<JSObject*> aBlob,
michael@0 105 nsAString& aResult,
michael@0 106 ErrorResult& aRv)
michael@0 107 {
michael@0 108 nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
michael@0 109 if (!blob) {
michael@0 110 aRv.Throw(NS_ERROR_INVALID_ARG);
michael@0 111 return;
michael@0 112 }
michael@0 113
michael@0 114 nsCOMPtr<nsIInputStream> stream;
michael@0 115 nsresult rv = blob->GetInternalStream(getter_AddRefs(stream));
michael@0 116 if (NS_FAILED(rv)) {
michael@0 117 aRv.Throw(rv);
michael@0 118 return;
michael@0 119 }
michael@0 120
michael@0 121 uint32_t numRead;
michael@0 122 do {
michael@0 123 char readBuf[4096];
michael@0 124 rv = stream->Read(readBuf, sizeof(readBuf), &numRead);
michael@0 125 if (NS_FAILED(rv)) {
michael@0 126 aRv.Throw(rv);
michael@0 127 return;
michael@0 128 }
michael@0 129
michael@0 130 uint32_t oldLength = aResult.Length();
michael@0 131 AppendASCIItoUTF16(Substring(readBuf, readBuf + numRead), aResult);
michael@0 132 if (aResult.Length() - oldLength != numRead) {
michael@0 133 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
michael@0 134 return;
michael@0 135 }
michael@0 136 } while (numRead > 0);
michael@0 137 }
michael@0 138
michael@0 139 void
michael@0 140 FileReaderSync::ReadAsText(JS::Handle<JSObject*> aBlob,
michael@0 141 const Optional<nsAString>& aEncoding,
michael@0 142 nsAString& aResult,
michael@0 143 ErrorResult& aRv)
michael@0 144 {
michael@0 145 nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
michael@0 146 if (!blob) {
michael@0 147 aRv.Throw(NS_ERROR_INVALID_ARG);
michael@0 148 return;
michael@0 149 }
michael@0 150
michael@0 151 nsCOMPtr<nsIInputStream> stream;
michael@0 152 nsresult rv = blob->GetInternalStream(getter_AddRefs(stream));
michael@0 153 if (NS_FAILED(rv)) {
michael@0 154 aRv.Throw(rv);
michael@0 155 return;
michael@0 156 }
michael@0 157
michael@0 158 nsAutoCString encoding;
michael@0 159 unsigned char sniffBuf[3] = { 0, 0, 0 };
michael@0 160 uint32_t numRead;
michael@0 161 rv = stream->Read(reinterpret_cast<char*>(sniffBuf),
michael@0 162 sizeof(sniffBuf), &numRead);
michael@0 163 if (NS_FAILED(rv)) {
michael@0 164 aRv.Throw(rv);
michael@0 165 return;
michael@0 166 }
michael@0 167
michael@0 168 // The BOM sniffing is baked into the "decode" part of the Encoding
michael@0 169 // Standard, which the File API references.
michael@0 170 if (!nsContentUtils::CheckForBOM(sniffBuf, numRead, encoding)) {
michael@0 171 // BOM sniffing failed. Try the API argument.
michael@0 172 if (!aEncoding.WasPassed() ||
michael@0 173 !EncodingUtils::FindEncodingForLabel(aEncoding.Value(),
michael@0 174 encoding)) {
michael@0 175 // API argument failed. Try the type property of the blob.
michael@0 176 nsAutoString type16;
michael@0 177 blob->GetType(type16);
michael@0 178 NS_ConvertUTF16toUTF8 type(type16);
michael@0 179 nsAutoCString specifiedCharset;
michael@0 180 bool haveCharset;
michael@0 181 int32_t charsetStart, charsetEnd;
michael@0 182 NS_ExtractCharsetFromContentType(type,
michael@0 183 specifiedCharset,
michael@0 184 &haveCharset,
michael@0 185 &charsetStart,
michael@0 186 &charsetEnd);
michael@0 187 if (!EncodingUtils::FindEncodingForLabel(specifiedCharset, encoding)) {
michael@0 188 // Type property failed. Use UTF-8.
michael@0 189 encoding.AssignLiteral("UTF-8");
michael@0 190 }
michael@0 191 }
michael@0 192 }
michael@0 193
michael@0 194 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(stream);
michael@0 195 if (!seekable) {
michael@0 196 aRv.Throw(NS_ERROR_FAILURE);
michael@0 197 return;
michael@0 198 }
michael@0 199
michael@0 200 // Seek to 0 because to undo the BOM sniffing advance. UTF-8 and UTF-16
michael@0 201 // decoders will swallow the BOM.
michael@0 202 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
michael@0 203 if (NS_FAILED(rv)) {
michael@0 204 aRv.Throw(rv);
michael@0 205 return;
michael@0 206 }
michael@0 207
michael@0 208 rv = ConvertStream(stream, encoding.get(), aResult);
michael@0 209 if (NS_FAILED(rv)) {
michael@0 210 aRv.Throw(rv);
michael@0 211 return;
michael@0 212 }
michael@0 213 }
michael@0 214
michael@0 215 void
michael@0 216 FileReaderSync::ReadAsDataURL(JS::Handle<JSObject*> aBlob, nsAString& aResult,
michael@0 217 ErrorResult& aRv)
michael@0 218 {
michael@0 219 nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
michael@0 220 if (!blob) {
michael@0 221 aRv.Throw(NS_ERROR_INVALID_ARG);
michael@0 222 return;
michael@0 223 }
michael@0 224
michael@0 225 nsAutoString scratchResult;
michael@0 226 scratchResult.AssignLiteral("data:");
michael@0 227
michael@0 228 nsString contentType;
michael@0 229 blob->GetType(contentType);
michael@0 230
michael@0 231 if (contentType.IsEmpty()) {
michael@0 232 scratchResult.AppendLiteral("application/octet-stream");
michael@0 233 } else {
michael@0 234 scratchResult.Append(contentType);
michael@0 235 }
michael@0 236 scratchResult.AppendLiteral(";base64,");
michael@0 237
michael@0 238 nsCOMPtr<nsIInputStream> stream;
michael@0 239 nsresult rv = blob->GetInternalStream(getter_AddRefs(stream));
michael@0 240 if (NS_FAILED(rv)) {
michael@0 241 aRv.Throw(rv);
michael@0 242 return;
michael@0 243 }
michael@0 244
michael@0 245 uint64_t size;
michael@0 246 rv = blob->GetSize(&size);
michael@0 247 if (NS_FAILED(rv)) {
michael@0 248 aRv.Throw(rv);
michael@0 249 return;
michael@0 250 }
michael@0 251
michael@0 252 nsCOMPtr<nsIInputStream> bufferedStream;
michael@0 253 rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, size);
michael@0 254 if (NS_FAILED(rv)) {
michael@0 255 aRv.Throw(rv);
michael@0 256 return;
michael@0 257 }
michael@0 258
michael@0 259 nsAutoString encodedData;
michael@0 260 rv = Base64EncodeInputStream(bufferedStream, encodedData, size);
michael@0 261 if (NS_FAILED(rv)) {
michael@0 262 aRv.Throw(rv);
michael@0 263 return;
michael@0 264 }
michael@0 265
michael@0 266 scratchResult.Append(encodedData);
michael@0 267
michael@0 268 aResult = scratchResult;
michael@0 269 }
michael@0 270
michael@0 271 nsresult
michael@0 272 FileReaderSync::ConvertStream(nsIInputStream *aStream,
michael@0 273 const char *aCharset,
michael@0 274 nsAString &aResult)
michael@0 275 {
michael@0 276 nsCOMPtr<nsIConverterInputStream> converterStream =
michael@0 277 do_CreateInstance("@mozilla.org/intl/converter-input-stream;1");
michael@0 278 NS_ENSURE_TRUE(converterStream, NS_ERROR_FAILURE);
michael@0 279
michael@0 280 nsresult rv = converterStream->Init(aStream, aCharset, 8192,
michael@0 281 nsIConverterInputStream::DEFAULT_REPLACEMENT_CHARACTER);
michael@0 282 NS_ENSURE_SUCCESS(rv, rv);
michael@0 283
michael@0 284 nsCOMPtr<nsIUnicharInputStream> unicharStream =
michael@0 285 do_QueryInterface(converterStream);
michael@0 286 NS_ENSURE_TRUE(unicharStream, NS_ERROR_FAILURE);
michael@0 287
michael@0 288 uint32_t numChars;
michael@0 289 nsString result;
michael@0 290 while (NS_SUCCEEDED(unicharStream->ReadString(8192, result, &numChars)) &&
michael@0 291 numChars > 0) {
michael@0 292 uint32_t oldLength = aResult.Length();
michael@0 293 aResult.Append(result);
michael@0 294 if (aResult.Length() - oldLength != result.Length()) {
michael@0 295 return NS_ERROR_OUT_OF_MEMORY;
michael@0 296 }
michael@0 297 }
michael@0 298
michael@0 299 return rv;
michael@0 300 }
michael@0 301

mercurial