1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozila.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Mozilla Foundation
19 : * Portions created by the Initial Developer are Copyright (C) 2007
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsDOMFileReader.h"
39 :
40 : #include "nsContentCID.h"
41 : #include "nsContentUtils.h"
42 : #include "nsDOMClassInfoID.h"
43 : #include "nsDOMFile.h"
44 : #include "nsDOMError.h"
45 : #include "nsCharsetAlias.h"
46 : #include "nsICharsetDetector.h"
47 : #include "nsICharsetConverterManager.h"
48 : #include "nsIConverterInputStream.h"
49 : #include "nsIFile.h"
50 : #include "nsIFileStreams.h"
51 : #include "nsIInputStream.h"
52 : #include "nsIMIMEService.h"
53 : #include "nsIPlatformCharset.h"
54 : #include "nsIUnicodeDecoder.h"
55 : #include "nsNetCID.h"
56 : #include "nsNetUtil.h"
57 :
58 : #include "plbase64.h"
59 : #include "prmem.h"
60 :
61 : #include "nsLayoutCID.h"
62 : #include "nsXPIDLString.h"
63 : #include "nsReadableUtils.h"
64 : #include "nsIURI.h"
65 : #include "nsStreamUtils.h"
66 : #include "nsXPCOM.h"
67 : #include "nsIPrivateDOMEvent.h"
68 : #include "nsIDOMEventListener.h"
69 : #include "nsIJSContextStack.h"
70 : #include "nsJSEnvironment.h"
71 : #include "nsIScriptGlobalObject.h"
72 : #include "nsCExternalHandlerService.h"
73 : #include "nsIStreamConverterService.h"
74 : #include "nsCycleCollectionParticipant.h"
75 : #include "nsLayoutStatics.h"
76 : #include "nsIScriptObjectPrincipal.h"
77 : #include "nsBlobProtocolHandler.h"
78 : #include "mozilla/Preferences.h"
79 : #include "xpcpublic.h"
80 : #include "nsIScriptSecurityManager.h"
81 : #include "nsDOMJSUtils.h"
82 : #include "nsDOMEventTargetHelper.h"
83 :
84 : #include "jstypedarray.h"
85 :
86 : using namespace mozilla;
87 :
88 : #define LOAD_STR "load"
89 : #define LOADSTART_STR "loadstart"
90 : #define LOADEND_STR "loadend"
91 :
92 : using mozilla::dom::FileIOObject;
93 :
94 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMFileReader)
95 :
96 4 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMFileReader,
97 : FileIOObject)
98 4 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFile)
99 4 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPrincipal)
100 4 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannel)
101 4 : NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(load)
102 4 : NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(loadstart)
103 4 : NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(loadend)
104 4 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
105 :
106 2 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMFileReader,
107 : FileIOObject)
108 2 : tmp->mResultArrayBuffer = nsnull;
109 2 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFile)
110 2 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPrincipal)
111 2 : NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(load)
112 2 : NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(loadstart)
113 2 : NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(loadend)
114 2 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
115 :
116 :
117 16 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsDOMFileReader,
118 : nsDOMEventTargetHelper)
119 16 : if(tmp->mResultArrayBuffer) {
120 12 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(tmp->mResultArrayBuffer,
121 : "mResultArrayBuffer")
122 : }
123 16 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
124 :
125 : DOMCI_DATA(FileReader, nsDOMFileReader)
126 :
127 222 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMFileReader)
128 140 : NS_INTERFACE_MAP_ENTRY(nsIDOMFileReader)
129 130 : NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
130 130 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
131 130 : NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
132 130 : NS_INTERFACE_MAP_ENTRY(nsICharsetDetectionObserver)
133 130 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(FileReader)
134 128 : NS_INTERFACE_MAP_END_INHERITING(FileIOObject)
135 :
136 142 : NS_IMPL_ADDREF_INHERITED(nsDOMFileReader, FileIOObject)
137 142 : NS_IMPL_RELEASE_INHERITED(nsDOMFileReader, FileIOObject)
138 :
139 : void
140 2 : nsDOMFileReader::RootResultArrayBuffer()
141 : {
142 : nsContentUtils::PreserveWrapper(
143 : static_cast<nsIDOMEventTarget*>(
144 2 : static_cast<nsDOMEventTargetHelper*>(this)), this);
145 2 : }
146 :
147 : //nsICharsetDetectionObserver
148 :
149 : NS_IMETHODIMP
150 0 : nsDOMFileReader::Notify(const char *aCharset, nsDetectionConfident aConf)
151 : {
152 0 : mCharset = aCharset;
153 0 : return NS_OK;
154 : }
155 :
156 : //nsDOMFileReader constructors/initializers
157 :
158 2 : nsDOMFileReader::nsDOMFileReader()
159 : : mFileData(nsnull),
160 : mDataLen(0), mDataFormat(FILE_AS_BINARY),
161 2 : mResultArrayBuffer(nsnull)
162 : {
163 2 : nsLayoutStatics::AddRef();
164 2 : SetDOMStringToNull(mResult);
165 2 : }
166 :
167 6 : nsDOMFileReader::~nsDOMFileReader()
168 : {
169 2 : FreeFileData();
170 :
171 2 : nsLayoutStatics::Release();
172 8 : }
173 :
174 : nsresult
175 2 : nsDOMFileReader::Init()
176 : {
177 2 : nsDOMEventTargetHelper::Init();
178 :
179 2 : nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
180 4 : nsCOMPtr<nsIPrincipal> subjectPrincipal;
181 2 : if (secMan) {
182 2 : nsresult rv = secMan->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
183 2 : NS_ENSURE_SUCCESS(rv, rv);
184 : }
185 2 : NS_ENSURE_STATE(subjectPrincipal);
186 2 : mPrincipal.swap(subjectPrincipal);
187 :
188 2 : return NS_OK;
189 : }
190 :
191 0 : NS_IMPL_EVENT_HANDLER(nsDOMFileReader, load)
192 0 : NS_IMPL_EVENT_HANDLER(nsDOMFileReader, loadstart)
193 0 : NS_IMPL_EVENT_HANDLER(nsDOMFileReader, loadend)
194 0 : NS_IMPL_FORWARD_EVENT_HANDLER(nsDOMFileReader, abort, FileIOObject)
195 0 : NS_IMPL_FORWARD_EVENT_HANDLER(nsDOMFileReader, progress, FileIOObject)
196 0 : NS_IMPL_FORWARD_EVENT_HANDLER(nsDOMFileReader, error, FileIOObject)
197 :
198 : NS_IMETHODIMP
199 0 : nsDOMFileReader::Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
200 : PRUint32 argc, jsval *argv)
201 : {
202 0 : nsCOMPtr<nsPIDOMWindow> owner = do_QueryInterface(aOwner);
203 0 : if (!owner) {
204 0 : NS_WARNING("Unexpected nsIJSNativeInitializer owner");
205 0 : return NS_OK;
206 : }
207 :
208 0 : BindToOwner(owner);
209 :
210 : // This object is bound to a |window|,
211 : // so reset the principal.
212 0 : nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal = do_QueryInterface(aOwner);
213 0 : NS_ENSURE_STATE(scriptPrincipal);
214 0 : mPrincipal = scriptPrincipal->GetPrincipal();
215 :
216 0 : return NS_OK;
217 : }
218 :
219 : // nsIInterfaceRequestor
220 :
221 : NS_IMETHODIMP
222 0 : nsDOMFileReader::GetInterface(const nsIID & aIID, void **aResult)
223 : {
224 0 : return QueryInterface(aIID, aResult);
225 : }
226 :
227 : // nsIDOMFileReader
228 :
229 : NS_IMETHODIMP
230 2 : nsDOMFileReader::GetReadyState(PRUint16 *aReadyState)
231 : {
232 2 : return FileIOObject::GetReadyState(aReadyState);
233 : }
234 :
235 : NS_IMETHODIMP
236 2 : nsDOMFileReader::GetResult(JSContext* aCx, jsval* aResult)
237 : {
238 2 : if (mDataFormat == FILE_AS_ARRAYBUFFER) {
239 2 : if (mReadyState == nsIDOMFileReader::DONE && mResultArrayBuffer) {
240 2 : JSObject* tmp = mResultArrayBuffer;
241 2 : *aResult = OBJECT_TO_JSVAL(tmp);
242 : } else {
243 0 : *aResult = JSVAL_NULL;
244 : }
245 2 : return NS_OK;
246 : }
247 :
248 0 : nsString tmpResult = mResult;
249 0 : if (!xpc::StringToJsval(aCx, tmpResult, aResult)) {
250 0 : return NS_ERROR_FAILURE;
251 : }
252 0 : return NS_OK;
253 : }
254 :
255 : NS_IMETHODIMP
256 0 : nsDOMFileReader::GetError(nsIDOMDOMError** aError)
257 : {
258 0 : return FileIOObject::GetError(aError);
259 : }
260 :
261 : NS_IMETHODIMP
262 2 : nsDOMFileReader::ReadAsArrayBuffer(nsIDOMBlob* aFile, JSContext* aCx)
263 : {
264 2 : return ReadFileContent(aCx, aFile, EmptyString(), FILE_AS_ARRAYBUFFER);
265 : }
266 :
267 : NS_IMETHODIMP
268 0 : nsDOMFileReader::ReadAsBinaryString(nsIDOMBlob* aFile)
269 : {
270 0 : return ReadFileContent(nsnull, aFile, EmptyString(), FILE_AS_BINARY);
271 : }
272 :
273 : NS_IMETHODIMP
274 0 : nsDOMFileReader::ReadAsText(nsIDOMBlob* aFile,
275 : const nsAString &aCharset)
276 : {
277 0 : return ReadFileContent(nsnull, aFile, aCharset, FILE_AS_TEXT);
278 : }
279 :
280 : NS_IMETHODIMP
281 0 : nsDOMFileReader::ReadAsDataURL(nsIDOMBlob* aFile)
282 : {
283 0 : return ReadFileContent(nsnull, aFile, EmptyString(), FILE_AS_DATAURL);
284 : }
285 :
286 : NS_IMETHODIMP
287 2 : nsDOMFileReader::Abort()
288 : {
289 2 : return FileIOObject::Abort();
290 : }
291 :
292 : nsresult
293 0 : nsDOMFileReader::DoAbort(nsAString& aEvent)
294 : {
295 : // Revert status and result attributes
296 0 : SetDOMStringToNull(mResult);
297 0 : mResultArrayBuffer = nsnull;
298 :
299 : // Non-null channel indicates a read is currently active
300 0 : if (mChannel) {
301 : // Cancel request requires an error status
302 0 : mChannel->Cancel(NS_ERROR_FAILURE);
303 0 : mChannel = nsnull;
304 : }
305 0 : mFile = nsnull;
306 :
307 : //Clean up memory buffer
308 0 : FreeFileData();
309 :
310 : // Tell the base class which event to dispatch
311 0 : aEvent = NS_LITERAL_STRING(LOADEND_STR);
312 0 : return NS_OK;
313 : }
314 :
315 : static
316 : NS_METHOD
317 0 : ReadFuncBinaryString(nsIInputStream* in,
318 : void* closure,
319 : const char* fromRawSegment,
320 : PRUint32 toOffset,
321 : PRUint32 count,
322 : PRUint32 *writeCount)
323 : {
324 0 : PRUnichar* dest = static_cast<PRUnichar*>(closure) + toOffset;
325 0 : PRUnichar* end = dest + count;
326 0 : const unsigned char* source = (const unsigned char*)fromRawSegment;
327 0 : while (dest != end) {
328 0 : *dest = *source;
329 0 : ++dest;
330 0 : ++source;
331 : }
332 0 : *writeCount = count;
333 :
334 0 : return NS_OK;
335 : }
336 :
337 : nsresult
338 2 : nsDOMFileReader::DoOnDataAvailable(nsIRequest *aRequest,
339 : nsISupports *aContext,
340 : nsIInputStream *aInputStream,
341 : PRUint32 aOffset,
342 : PRUint32 aCount)
343 : {
344 2 : if (mDataFormat == FILE_AS_BINARY) {
345 : //Continuously update our binary string as data comes in
346 0 : NS_ASSERTION(mResult.Length() == aOffset,
347 : "unexpected mResult length");
348 0 : PRUint32 oldLen = mResult.Length();
349 0 : PRUnichar *buf = nsnull;
350 0 : mResult.GetMutableData(&buf, oldLen + aCount);
351 0 : NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
352 :
353 0 : PRUint32 bytesRead = 0;
354 0 : aInputStream->ReadSegments(ReadFuncBinaryString, buf + oldLen, aCount,
355 0 : &bytesRead);
356 0 : NS_ASSERTION(bytesRead == aCount, "failed to read data");
357 : }
358 2 : else if (mDataFormat == FILE_AS_ARRAYBUFFER) {
359 2 : JSObject* abuf = js::ArrayBuffer::getArrayBuffer(mResultArrayBuffer);
360 2 : NS_ASSERTION(abuf, "What happened?");
361 :
362 2 : PRUint32 bytesRead = 0;
363 2 : aInputStream->Read((char*)JS_GetArrayBufferData(abuf) + aOffset, aCount, &bytesRead);
364 2 : NS_ASSERTION(bytesRead == aCount, "failed to read data");
365 : }
366 : else {
367 : //Update memory buffer to reflect the contents of the file
368 0 : mFileData = (char *)PR_Realloc(mFileData, aOffset + aCount);
369 0 : NS_ENSURE_TRUE(mFileData, NS_ERROR_OUT_OF_MEMORY);
370 :
371 0 : PRUint32 bytesRead = 0;
372 0 : aInputStream->Read(mFileData + aOffset, aCount, &bytesRead);
373 0 : NS_ASSERTION(bytesRead == aCount, "failed to read data");
374 :
375 0 : mDataLen += aCount;
376 : }
377 :
378 2 : return NS_OK;
379 : }
380 :
381 : nsresult
382 2 : nsDOMFileReader::DoOnStopRequest(nsIRequest *aRequest,
383 : nsISupports *aContext,
384 : nsresult aStatus,
385 : nsAString& aSuccessEvent,
386 : nsAString& aTerminationEvent)
387 : {
388 2 : aSuccessEvent = NS_LITERAL_STRING(LOAD_STR);
389 2 : aTerminationEvent = NS_LITERAL_STRING(LOADEND_STR);
390 :
391 : // Clear out the data if necessary
392 2 : if (NS_FAILED(aStatus)) {
393 0 : FreeFileData();
394 0 : return NS_OK;
395 : }
396 :
397 2 : nsresult rv = NS_OK;
398 2 : switch (mDataFormat) {
399 : case FILE_AS_ARRAYBUFFER:
400 2 : break; //Already accumulated mResultArrayBuffer
401 : case FILE_AS_BINARY:
402 0 : break; //Already accumulated mResult
403 : case FILE_AS_TEXT:
404 0 : rv = GetAsText(mCharset, mFileData, mDataLen, mResult);
405 0 : break;
406 : case FILE_AS_DATAURL:
407 0 : rv = GetAsDataURL(mFile, mFileData, mDataLen, mResult);
408 0 : break;
409 : }
410 :
411 2 : mResult.SetIsVoid(false);
412 :
413 2 : FreeFileData();
414 :
415 2 : return rv;
416 : }
417 :
418 : // Helper methods
419 :
420 : nsresult
421 2 : nsDOMFileReader::ReadFileContent(JSContext* aCx,
422 : nsIDOMBlob* aFile,
423 : const nsAString &aCharset,
424 : eDataFormat aDataFormat)
425 : {
426 : nsresult rv;
427 2 : NS_ENSURE_TRUE(aFile, NS_ERROR_NULL_POINTER);
428 :
429 : //Implicit abort to clear any other activity going on
430 2 : Abort();
431 2 : mError = nsnull;
432 2 : SetDOMStringToNull(mResult);
433 2 : mTransferred = 0;
434 2 : mTotal = 0;
435 2 : mReadyState = nsIDOMFileReader::EMPTY;
436 2 : FreeFileData();
437 :
438 2 : mFile = aFile;
439 2 : mDataFormat = aDataFormat;
440 2 : CopyUTF16toUTF8(aCharset, mCharset);
441 :
442 : //Establish a channel with our file
443 : {
444 : // Hold the internal URL alive only as long as necessary
445 : // After the channel is created it will own whatever is backing
446 : // the DOMFile.
447 4 : nsDOMFileInternalUrlHolder urlHolder(mFile, mPrincipal);
448 :
449 4 : nsCOMPtr<nsIURI> uri;
450 2 : rv = NS_NewURI(getter_AddRefs(uri), urlHolder.mUrl);
451 2 : NS_ENSURE_SUCCESS(rv, rv);
452 :
453 2 : rv = NS_NewChannel(getter_AddRefs(mChannel), uri);
454 2 : NS_ENSURE_SUCCESS(rv, rv);
455 : }
456 :
457 : //Obtain the total size of the file before reading
458 2 : mTotal = mozilla::dom::kUnknownSize;
459 2 : mFile->GetSize(&mTotal);
460 :
461 2 : rv = mChannel->AsyncOpen(this, nsnull);
462 2 : NS_ENSURE_SUCCESS(rv, rv);
463 :
464 : //FileReader should be in loading state here
465 2 : mReadyState = nsIDOMFileReader::LOADING;
466 2 : DispatchProgressEvent(NS_LITERAL_STRING(LOADSTART_STR));
467 :
468 2 : if (mDataFormat == FILE_AS_ARRAYBUFFER) {
469 2 : RootResultArrayBuffer();
470 2 : mResultArrayBuffer = js_CreateArrayBuffer(aCx, mTotal);
471 2 : if (!mResultArrayBuffer) {
472 0 : NS_WARNING("Failed to create JS array buffer");
473 0 : return NS_ERROR_FAILURE;
474 : }
475 : }
476 :
477 2 : return NS_OK;
478 : }
479 :
480 : nsresult
481 0 : nsDOMFileReader::GetAsText(const nsACString &aCharset,
482 : const char *aFileData,
483 : PRUint32 aDataLen,
484 : nsAString& aResult)
485 : {
486 : nsresult rv;
487 0 : nsCAutoString charsetGuess;
488 0 : if (!aCharset.IsEmpty()) {
489 0 : charsetGuess = aCharset;
490 : } else {
491 0 : rv = GuessCharset(aFileData, aDataLen, charsetGuess);
492 0 : NS_ENSURE_SUCCESS(rv, rv);
493 : }
494 :
495 0 : nsCAutoString charset;
496 0 : rv = nsCharsetAlias::GetPreferred(charsetGuess, charset);
497 0 : NS_ENSURE_SUCCESS(rv, rv);
498 :
499 0 : rv = ConvertStream(aFileData, aDataLen, charset.get(), aResult);
500 :
501 0 : return NS_OK;
502 : }
503 :
504 : nsresult
505 0 : nsDOMFileReader::GetAsDataURL(nsIDOMBlob *aFile,
506 : const char *aFileData,
507 : PRUint32 aDataLen,
508 : nsAString& aResult)
509 : {
510 0 : aResult.AssignLiteral("data:");
511 :
512 : nsresult rv;
513 0 : nsString contentType;
514 0 : rv = aFile->GetType(contentType);
515 0 : if (NS_SUCCEEDED(rv) && !contentType.IsEmpty()) {
516 0 : aResult.Append(contentType);
517 : } else {
518 0 : aResult.AppendLiteral("application/octet-stream");
519 : }
520 0 : aResult.AppendLiteral(";base64,");
521 :
522 0 : PRUint32 totalRead = 0;
523 0 : while (aDataLen > totalRead) {
524 0 : PRUint32 numEncode = 4096;
525 0 : PRUint32 amtRemaining = aDataLen - totalRead;
526 0 : if (numEncode > amtRemaining)
527 0 : numEncode = amtRemaining;
528 :
529 : //Unless this is the end of the file, encode in multiples of 3
530 0 : if (numEncode > 3) {
531 0 : PRUint32 leftOver = numEncode % 3;
532 0 : numEncode -= leftOver;
533 : }
534 :
535 : //Out buffer should be at least 4/3rds the read buf, plus a terminator
536 0 : char *base64 = PL_Base64Encode(aFileData + totalRead, numEncode, nsnull);
537 0 : AppendASCIItoUTF16(nsDependentCString(base64), aResult);
538 0 : PR_Free(base64);
539 :
540 0 : totalRead += numEncode;
541 : }
542 :
543 0 : return NS_OK;
544 : }
545 :
546 : nsresult
547 0 : nsDOMFileReader::ConvertStream(const char *aFileData,
548 : PRUint32 aDataLen,
549 : const char *aCharset,
550 : nsAString &aResult)
551 : {
552 : nsresult rv;
553 : nsCOMPtr<nsICharsetConverterManager> charsetConverter =
554 0 : do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
555 0 : NS_ENSURE_SUCCESS(rv, rv);
556 :
557 0 : nsCOMPtr<nsIUnicodeDecoder> unicodeDecoder;
558 0 : rv = charsetConverter->GetUnicodeDecoder(aCharset, getter_AddRefs(unicodeDecoder));
559 0 : NS_ENSURE_SUCCESS(rv, rv);
560 :
561 : PRInt32 destLength;
562 0 : rv = unicodeDecoder->GetMaxLength(aFileData, aDataLen, &destLength);
563 0 : NS_ENSURE_SUCCESS(rv, rv);
564 :
565 0 : aResult.SetLength(destLength); //Make sure we have enough space for the conversion
566 0 : destLength = aResult.Length();
567 :
568 0 : PRInt32 srcLength = aDataLen;
569 0 : rv = unicodeDecoder->Convert(aFileData, &srcLength, aResult.BeginWriting(), &destLength);
570 0 : aResult.SetLength(destLength); //Trim down to the correct size
571 :
572 0 : return rv;
573 : }
574 :
575 : nsresult
576 0 : nsDOMFileReader::GuessCharset(const char *aFileData,
577 : PRUint32 aDataLen,
578 : nsACString &aCharset)
579 : {
580 : // First try the universal charset detector
581 : nsCOMPtr<nsICharsetDetector> detector
582 : = do_CreateInstance(NS_CHARSET_DETECTOR_CONTRACTID_BASE
583 0 : "universal_charset_detector");
584 0 : if (!detector) {
585 : // No universal charset detector, try the default charset detector
586 : const nsAdoptingCString& detectorName =
587 0 : Preferences::GetLocalizedCString("intl.charset.detector");
588 0 : if (!detectorName.IsEmpty()) {
589 0 : nsCAutoString detectorContractID;
590 0 : detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
591 0 : detectorContractID += detectorName;
592 0 : detector = do_CreateInstance(detectorContractID.get());
593 : }
594 : }
595 :
596 : nsresult rv;
597 : // The charset detector doesn't work for empty (null) aFileData. Testing
598 : // aDataLen instead of aFileData so that we catch potential errors.
599 0 : if (detector && aDataLen != 0) {
600 0 : mCharset.Truncate();
601 0 : detector->Init(this);
602 :
603 : bool done;
604 :
605 0 : rv = detector->DoIt(aFileData, aDataLen, &done);
606 0 : NS_ENSURE_SUCCESS(rv, rv);
607 :
608 0 : rv = detector->Done();
609 0 : NS_ENSURE_SUCCESS(rv, rv);
610 :
611 0 : aCharset = mCharset;
612 : } else {
613 : // no charset detector available, check the BOM
614 : unsigned char sniffBuf[3];
615 0 : PRUint32 numRead = (aDataLen >= sizeof(sniffBuf) ? sizeof(sniffBuf) : aDataLen);
616 0 : memcpy(sniffBuf, aFileData, numRead);
617 :
618 0 : if (numRead >= 2 &&
619 0 : sniffBuf[0] == 0xfe &&
620 0 : sniffBuf[1] == 0xff) {
621 0 : aCharset = "UTF-16BE";
622 0 : } else if (numRead >= 2 &&
623 0 : sniffBuf[0] == 0xff &&
624 0 : sniffBuf[1] == 0xfe) {
625 0 : aCharset = "UTF-16LE";
626 0 : } else if (numRead >= 3 &&
627 0 : sniffBuf[0] == 0xef &&
628 0 : sniffBuf[1] == 0xbb &&
629 0 : sniffBuf[2] == 0xbf) {
630 0 : aCharset = "UTF-8";
631 : }
632 : }
633 :
634 0 : if (aCharset.IsEmpty()) {
635 : // no charset detected, default to the system charset
636 : nsCOMPtr<nsIPlatformCharset> platformCharset =
637 0 : do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
638 0 : if (NS_SUCCEEDED(rv)) {
639 0 : rv = platformCharset->GetCharset(kPlatformCharsetSel_PlainTextInFile,
640 0 : aCharset);
641 : }
642 : }
643 :
644 0 : if (aCharset.IsEmpty()) {
645 : // no sniffed or default charset, try UTF-8
646 0 : aCharset.AssignLiteral("UTF-8");
647 : }
648 :
649 0 : return NS_OK;
650 4392 : }
|