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"
16 #include "nsDOMClassInfoID.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"
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
)
45 already_AddRefed
<FileReaderSync
>
46 FileReaderSync::Constructor(const GlobalObject
& aGlobal
, ErrorResult
& aRv
)
48 nsRefPtr
<FileReaderSync
> frs
= new FileReaderSync();
54 FileReaderSync::WrapObject(JSContext
* aCx
, JS::Handle
<JSObject
*> aScope
)
56 return FileReaderSyncBinding_workers::Wrap(aCx
, aScope
, this);
60 FileReaderSync::ReadAsArrayBuffer(JSContext
* aCx
,
61 JS::Handle
<JSObject
*> aScopeObj
,
62 JS::Handle
<JSObject
*> aBlob
,
65 nsIDOMBlob
* blob
= file::GetDOMBlobFromJSObject(aBlob
);
67 aRv
.Throw(NS_ERROR_INVALID_ARG
);
72 nsresult rv
= blob
->GetSize(&blobSize
);
78 JS::Rooted
<JSObject
*> jsArrayBuffer(aCx
, JS_NewArrayBuffer(aCx
, blobSize
));
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
);
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
));
97 rv
= stream
->Read((char*)arrayBuffer
, bufferLength
, &numRead
);
102 NS_ASSERTION(numRead
== bufferLength
, "failed to read data");
104 return jsArrayBuffer
;
108 FileReaderSync::ReadAsBinaryString(JS::Handle
<JSObject
*> aBlob
,
112 nsIDOMBlob
* blob
= file::GetDOMBlobFromJSObject(aBlob
);
114 aRv
.Throw(NS_ERROR_INVALID_ARG
);
118 nsCOMPtr
<nsIInputStream
> stream
;
119 nsresult rv
= blob
->GetInternalStream(getter_AddRefs(stream
));
128 rv
= stream
->Read(readBuf
, sizeof(readBuf
), &numRead
);
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
);
140 } while (numRead
> 0);
144 FileReaderSync::ReadAsText(JS::Handle
<JSObject
*> aBlob
,
145 const Optional
<nsAString
>& aEncoding
,
149 nsIDOMBlob
* blob
= file::GetDOMBlobFromJSObject(aBlob
);
151 aRv
.Throw(NS_ERROR_INVALID_ARG
);
155 nsCOMPtr
<nsIInputStream
> stream
;
156 nsresult rv
= blob
->GetInternalStream(getter_AddRefs(stream
));
162 nsCString charsetGuess
;
163 if (!aEncoding
.WasPassed() || aEncoding
.Value().IsEmpty()) {
164 rv
= GuessCharset(stream
, charsetGuess
);
170 nsCOMPtr
<nsISeekableStream
> seekable
= do_QueryInterface(stream
);
172 aRv
.Throw(NS_ERROR_FAILURE
);
176 // Seek to 0 because guessing the charset advances the stream.
177 rv
= seekable
->Seek(nsISeekableStream::NS_SEEK_SET
, 0);
183 CopyUTF16toUTF8(aEncoding
.Value(), charsetGuess
);
187 if (!EncodingUtils::FindEncodingForLabel(charsetGuess
, charset
)) {
188 aRv
.Throw(NS_ERROR_DOM_ENCODING_NOT_SUPPORTED_ERR
);
192 rv
= ConvertStream(stream
, charset
.get(), aResult
);
200 FileReaderSync::ReadAsDataURL(JS::Handle
<JSObject
*> aBlob
, nsAString
& aResult
,
203 nsIDOMBlob
* blob
= file::GetDOMBlobFromJSObject(aBlob
);
205 aRv
.Throw(NS_ERROR_INVALID_ARG
);
209 nsAutoString scratchResult
;
210 scratchResult
.AssignLiteral("data:");
212 nsString contentType
;
213 blob
->GetType(contentType
);
215 if (contentType
.IsEmpty()) {
216 scratchResult
.AppendLiteral("application/octet-stream");
218 scratchResult
.Append(contentType
);
220 scratchResult
.AppendLiteral(";base64,");
222 nsCOMPtr
<nsIInputStream
> stream
;
223 nsresult rv
= blob
->GetInternalStream(getter_AddRefs(stream
));
230 rv
= blob
->GetSize(&size
);
236 nsCOMPtr
<nsIInputStream
> bufferedStream
;
237 rv
= NS_NewBufferedInputStream(getter_AddRefs(bufferedStream
), stream
, size
);
243 nsAutoString encodedData
;
244 rv
= Base64EncodeInputStream(bufferedStream
, encodedData
, size
);
250 scratchResult
.Append(encodedData
);
252 aResult
= scratchResult
;
256 FileReaderSync::ConvertStream(nsIInputStream
*aStream
,
257 const char *aCharset
,
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
);
274 while (NS_SUCCEEDED(unicharStream
->ReadString(8192, result
, &numChars
)) &&
276 uint32_t oldLength
= aResult
.Length();
277 aResult
.Append(result
);
278 if (aResult
.Length() - oldLength
!= result
.Length()) {
279 return NS_ERROR_OUT_OF_MEMORY
;
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");
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());
310 detector
->Init(this);
316 rv
= aStream
->Read(readBuf
, sizeof(readBuf
), &numRead
);
317 NS_ENSURE_SUCCESS(rv
, rv
);
321 rv
= detector
->DoIt(readBuf
, numRead
, &done
);
322 NS_ENSURE_SUCCESS(rv
, rv
);
325 rv
= detector
->Done();
326 NS_ENSURE_SUCCESS(rv
, rv
);
328 // no charset detector available, check the BOM
329 unsigned char sniffBuf
[4];
331 rv
= aStream
->Read(reinterpret_cast<char*>(sniffBuf
),
332 sizeof(sniffBuf
), &numRead
);
333 NS_ENSURE_SUCCESS(rv
, rv
);
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) {
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");
368 FileReaderSync::Notify(const char* aCharset
, nsDetectionConfident aConf
)
370 mCharset
.Assign(aCharset
);