Backed out changeset 3fe07c50c854 (bug 946316) for bustage. a=backout
[gecko.git] / dom / workers / FileReaderSync.cpp
blob208c86a072c41ba696a78dcb23012fa91fdeab64
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "FileReaderSync.h"
9 #include "jsfriendapi.h"
10 #include "mozilla/Base64.h"
11 #include "mozilla/dom/EncodingUtils.h"
12 #include "mozilla/dom/FileReaderSyncBinding.h"
13 #include "nsCExternalHandlerService.h"
14 #include "nsComponentManagerUtils.h"
15 #include "nsCOMPtr.h"
16 #include "nsDOMClassInfoID.h"
17 #include "nsError.h"
18 #include "nsIDOMFile.h"
19 #include "nsICharsetDetector.h"
20 #include "nsIConverterInputStream.h"
21 #include "nsIInputStream.h"
22 #include "nsIPlatformCharset.h"
23 #include "nsISeekableStream.h"
24 #include "nsISupportsImpl.h"
25 #include "nsISupportsImpl.h"
26 #include "nsNetUtil.h"
27 #include "nsServiceManagerUtils.h"
29 #include "File.h"
30 #include "RuntimeService.h"
32 USING_WORKERS_NAMESPACE
33 using namespace mozilla;
34 using mozilla::dom::Optional;
35 using mozilla::dom::GlobalObject;
37 NS_IMPL_ADDREF(FileReaderSync)
38 NS_IMPL_RELEASE(FileReaderSync)
40 NS_INTERFACE_MAP_BEGIN(FileReaderSync)
41 NS_INTERFACE_MAP_ENTRY(nsICharsetDetectionObserver)
42 NS_INTERFACE_MAP_END
44 // static
45 already_AddRefed<FileReaderSync>
46 FileReaderSync::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
48 nsRefPtr<FileReaderSync> frs = new FileReaderSync();
50 return frs.forget();
53 JSObject*
54 FileReaderSync::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
56 return FileReaderSyncBinding_workers::Wrap(aCx, aScope, this);
59 JSObject*
60 FileReaderSync::ReadAsArrayBuffer(JSContext* aCx,
61 JS::Handle<JSObject*> aScopeObj,
62 JS::Handle<JSObject*> aBlob,
63 ErrorResult& aRv)
65 nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
66 if (!blob) {
67 aRv.Throw(NS_ERROR_INVALID_ARG);
68 return nullptr;
71 uint64_t blobSize;
72 nsresult rv = blob->GetSize(&blobSize);
73 if (NS_FAILED(rv)) {
74 aRv.Throw(rv);
75 return nullptr;
78 JS::Rooted<JSObject*> jsArrayBuffer(aCx, JS_NewArrayBuffer(aCx, blobSize));
79 if (!jsArrayBuffer) {
80 // XXXkhuey we need a way to indicate to the bindings that the call failed
81 // but there's already a pending exception that we should not clobber.
82 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
83 return nullptr;
86 uint32_t bufferLength = JS_GetArrayBufferByteLength(jsArrayBuffer);
87 uint8_t* arrayBuffer = JS_GetArrayBufferData(jsArrayBuffer);
89 nsCOMPtr<nsIInputStream> stream;
90 rv = blob->GetInternalStream(getter_AddRefs(stream));
91 if (NS_FAILED(rv)) {
92 aRv.Throw(rv);
93 return nullptr;
96 uint32_t numRead;
97 rv = stream->Read((char*)arrayBuffer, bufferLength, &numRead);
98 if (NS_FAILED(rv)) {
99 aRv.Throw(rv);
100 return nullptr;
102 NS_ASSERTION(numRead == bufferLength, "failed to read data");
104 return jsArrayBuffer;
107 void
108 FileReaderSync::ReadAsBinaryString(JS::Handle<JSObject*> aBlob,
109 nsAString& aResult,
110 ErrorResult& aRv)
112 nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
113 if (!blob) {
114 aRv.Throw(NS_ERROR_INVALID_ARG);
115 return;
118 nsCOMPtr<nsIInputStream> stream;
119 nsresult rv = blob->GetInternalStream(getter_AddRefs(stream));
120 if (NS_FAILED(rv)) {
121 aRv.Throw(rv);
122 return;
125 uint32_t numRead;
126 do {
127 char readBuf[4096];
128 rv = stream->Read(readBuf, sizeof(readBuf), &numRead);
129 if (NS_FAILED(rv)) {
130 aRv.Throw(rv);
131 return;
134 uint32_t oldLength = aResult.Length();
135 AppendASCIItoUTF16(Substring(readBuf, readBuf + numRead), aResult);
136 if (aResult.Length() - oldLength != numRead) {
137 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
138 return;
140 } while (numRead > 0);
143 void
144 FileReaderSync::ReadAsText(JS::Handle<JSObject*> aBlob,
145 const Optional<nsAString>& aEncoding,
146 nsAString& aResult,
147 ErrorResult& aRv)
149 nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
150 if (!blob) {
151 aRv.Throw(NS_ERROR_INVALID_ARG);
152 return;
155 nsCOMPtr<nsIInputStream> stream;
156 nsresult rv = blob->GetInternalStream(getter_AddRefs(stream));
157 if (NS_FAILED(rv)) {
158 aRv.Throw(rv);
159 return;
162 nsCString charsetGuess;
163 if (!aEncoding.WasPassed() || aEncoding.Value().IsEmpty()) {
164 rv = GuessCharset(stream, charsetGuess);
165 if (NS_FAILED(rv)) {
166 aRv.Throw(rv);
167 return;
170 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(stream);
171 if (!seekable) {
172 aRv.Throw(NS_ERROR_FAILURE);
173 return;
176 // Seek to 0 because guessing the charset advances the stream.
177 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
178 if (NS_FAILED(rv)) {
179 aRv.Throw(rv);
180 return;
182 } else {
183 CopyUTF16toUTF8(aEncoding.Value(), charsetGuess);
186 nsCString charset;
187 if (!EncodingUtils::FindEncodingForLabel(charsetGuess, charset)) {
188 aRv.Throw(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR);
189 return;
192 rv = ConvertStream(stream, charset.get(), aResult);
193 if (NS_FAILED(rv)) {
194 aRv.Throw(rv);
195 return;
199 void
200 FileReaderSync::ReadAsDataURL(JS::Handle<JSObject*> aBlob, nsAString& aResult,
201 ErrorResult& aRv)
203 nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
204 if (!blob) {
205 aRv.Throw(NS_ERROR_INVALID_ARG);
206 return;
209 nsAutoString scratchResult;
210 scratchResult.AssignLiteral("data:");
212 nsString contentType;
213 blob->GetType(contentType);
215 if (contentType.IsEmpty()) {
216 scratchResult.AppendLiteral("application/octet-stream");
217 } else {
218 scratchResult.Append(contentType);
220 scratchResult.AppendLiteral(";base64,");
222 nsCOMPtr<nsIInputStream> stream;
223 nsresult rv = blob->GetInternalStream(getter_AddRefs(stream));
224 if (NS_FAILED(rv)) {
225 aRv.Throw(rv);
226 return;
229 uint64_t size;
230 rv = blob->GetSize(&size);
231 if (NS_FAILED(rv)) {
232 aRv.Throw(rv);
233 return;
236 nsCOMPtr<nsIInputStream> bufferedStream;
237 rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, size);
238 if (NS_FAILED(rv)) {
239 aRv.Throw(rv);
240 return;
243 nsAutoString encodedData;
244 rv = Base64EncodeInputStream(bufferedStream, encodedData, size);
245 if (NS_FAILED(rv)) {
246 aRv.Throw(rv);
247 return;
250 scratchResult.Append(encodedData);
252 aResult = scratchResult;
255 nsresult
256 FileReaderSync::ConvertStream(nsIInputStream *aStream,
257 const char *aCharset,
258 nsAString &aResult)
260 nsCOMPtr<nsIConverterInputStream> converterStream =
261 do_CreateInstance("@mozilla.org/intl/converter-input-stream;1");
262 NS_ENSURE_TRUE(converterStream, NS_ERROR_FAILURE);
264 nsresult rv = converterStream->Init(aStream, aCharset, 8192,
265 nsIConverterInputStream::DEFAULT_REPLACEMENT_CHARACTER);
266 NS_ENSURE_SUCCESS(rv, rv);
268 nsCOMPtr<nsIUnicharInputStream> unicharStream =
269 do_QueryInterface(converterStream);
270 NS_ENSURE_TRUE(unicharStream, NS_ERROR_FAILURE);
272 uint32_t numChars;
273 nsString result;
274 while (NS_SUCCEEDED(unicharStream->ReadString(8192, result, &numChars)) &&
275 numChars > 0) {
276 uint32_t oldLength = aResult.Length();
277 aResult.Append(result);
278 if (aResult.Length() - oldLength != result.Length()) {
279 return NS_ERROR_OUT_OF_MEMORY;
283 return rv;
286 nsresult
287 FileReaderSync::GuessCharset(nsIInputStream *aStream, nsACString &aCharset)
289 // First try the universal charset detector
290 nsCOMPtr<nsICharsetDetector> detector
291 = do_CreateInstance(NS_CHARSET_DETECTOR_CONTRACTID_BASE
292 "universal_charset_detector");
293 if (!detector) {
294 RuntimeService* runtime = RuntimeService::GetService();
295 NS_ASSERTION(runtime, "This should never be null!");
297 // No universal charset detector, try the default charset detector
298 const nsACString& detectorName = runtime->GetDetectorName();
300 if (!detectorName.IsEmpty()) {
301 nsAutoCString detectorContractID;
302 detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
303 detectorContractID += detectorName;
304 detector = do_CreateInstance(detectorContractID.get());
308 nsresult rv;
309 if (detector) {
310 detector->Init(this);
312 bool done;
313 uint32_t numRead;
314 do {
315 char readBuf[4096];
316 rv = aStream->Read(readBuf, sizeof(readBuf), &numRead);
317 NS_ENSURE_SUCCESS(rv, rv);
318 if (numRead <= 0) {
319 break;
321 rv = detector->DoIt(readBuf, numRead, &done);
322 NS_ENSURE_SUCCESS(rv, rv);
323 } while (!done);
325 rv = detector->Done();
326 NS_ENSURE_SUCCESS(rv, rv);
327 } else {
328 // no charset detector available, check the BOM
329 unsigned char sniffBuf[4];
330 uint32_t numRead;
331 rv = aStream->Read(reinterpret_cast<char*>(sniffBuf),
332 sizeof(sniffBuf), &numRead);
333 NS_ENSURE_SUCCESS(rv, rv);
335 if (numRead >= 2 &&
336 sniffBuf[0] == 0xfe &&
337 sniffBuf[1] == 0xff) {
338 mCharset = "UTF-16BE";
339 } else if (numRead >= 2 &&
340 sniffBuf[0] == 0xff &&
341 sniffBuf[1] == 0xfe) {
342 mCharset = "UTF-16LE";
343 } else if (numRead >= 3 &&
344 sniffBuf[0] == 0xef &&
345 sniffBuf[1] == 0xbb &&
346 sniffBuf[2] == 0xbf) {
347 mCharset = "UTF-8";
351 if (mCharset.IsEmpty()) {
352 RuntimeService* runtime = RuntimeService::GetService();
353 mCharset = runtime->GetSystemCharset();
356 if (mCharset.IsEmpty()) {
357 // no sniffed or default charset, try UTF-8
358 mCharset.AssignLiteral("UTF-8");
361 aCharset = mCharset;
362 mCharset.Truncate();
364 return NS_OK;
367 NS_IMETHODIMP
368 FileReaderSync::Notify(const char* aCharset, nsDetectionConfident aConf)
370 mCharset.Assign(aCharset);
372 return NS_OK;