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 +