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 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * the Mozilla Foundation.
20 : * Portions created by the Initial Developer are Copyright (C) 2011
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * William Chen <wchen@mozilla.com> (Original Author)
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "FileReaderSyncPrivate.h"
41 :
42 : #include "nsCExternalHandlerService.h"
43 : #include "nsComponentManagerUtils.h"
44 : #include "nsCOMPtr.h"
45 : #include "nsDOMClassInfoID.h"
46 : #include "nsDOMError.h"
47 : #include "nsIDOMFile.h"
48 : #include "nsCharsetAlias.h"
49 : #include "nsICharsetDetector.h"
50 : #include "nsIConverterInputStream.h"
51 : #include "nsIInputStream.h"
52 : #include "nsIPlatformCharset.h"
53 : #include "nsISeekableStream.h"
54 : #include "nsISupportsImpl.h"
55 : #include "nsISupportsImpl.h"
56 : #include "nsNetUtil.h"
57 : #include "nsServiceManagerUtils.h"
58 : #include "RuntimeService.h"
59 :
60 : #include "mozilla/Base64.h"
61 :
62 : USING_WORKERS_NAMESPACE
63 :
64 0 : NS_IMPL_ISUPPORTS1(FileReaderSyncPrivate, nsICharsetDetectionObserver)
65 :
66 0 : FileReaderSyncPrivate::FileReaderSyncPrivate()
67 : {
68 0 : MOZ_COUNT_CTOR(mozilla::dom::workers::FileReaderSyncPrivate);
69 0 : }
70 :
71 0 : FileReaderSyncPrivate::~FileReaderSyncPrivate()
72 : {
73 0 : MOZ_COUNT_DTOR(mozilla::dom::workers::FileReaderSyncPrivate);
74 0 : }
75 :
76 : nsresult
77 0 : FileReaderSyncPrivate::ReadAsArrayBuffer(nsIDOMBlob* aBlob, PRUint32 aLength,
78 : uint8* aBuffer)
79 : {
80 0 : nsCOMPtr<nsIInputStream> stream;
81 0 : nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream));
82 0 : NS_ENSURE_SUCCESS(rv, rv);
83 :
84 : PRUint32 numRead;
85 0 : rv = stream->Read((char*)aBuffer, aLength, &numRead);
86 0 : NS_ENSURE_SUCCESS(rv, rv);
87 0 : NS_ASSERTION(numRead == aLength, "failed to read data");
88 :
89 0 : return NS_OK;
90 : }
91 :
92 : nsresult
93 0 : FileReaderSyncPrivate::ReadAsBinaryString(nsIDOMBlob* aBlob, nsAString& aResult)
94 : {
95 0 : nsCOMPtr<nsIInputStream> stream;
96 0 : nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream));
97 0 : NS_ENSURE_SUCCESS(rv, rv);
98 :
99 : PRUint32 numRead;
100 0 : do {
101 : char readBuf[4096];
102 0 : rv = stream->Read(readBuf, sizeof(readBuf), &numRead);
103 0 : NS_ENSURE_SUCCESS(rv, rv);
104 :
105 0 : PRUint32 oldLength = aResult.Length();
106 0 : AppendASCIItoUTF16(Substring(readBuf, readBuf + numRead), aResult);
107 0 : if (aResult.Length() - oldLength != numRead) {
108 0 : return NS_ERROR_OUT_OF_MEMORY;
109 : }
110 : } while (numRead > 0);
111 :
112 0 : return NS_OK;
113 : }
114 :
115 : nsresult
116 0 : FileReaderSyncPrivate::ReadAsText(nsIDOMBlob* aBlob,
117 : const nsAString& aEncoding, nsAString& aResult)
118 : {
119 0 : nsCOMPtr<nsIInputStream> stream;
120 0 : nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream));
121 0 : NS_ENSURE_SUCCESS(rv, rv);
122 :
123 0 : nsCString charsetGuess;
124 0 : if (aEncoding.IsEmpty()) {
125 0 : rv = GuessCharset(stream, charsetGuess);
126 0 : NS_ENSURE_SUCCESS(rv, rv);
127 :
128 0 : nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(stream);
129 0 : NS_ENSURE_TRUE(seekable, NS_ERROR_FAILURE);
130 :
131 : // Seek to 0 because guessing the charset advances the stream.
132 0 : rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
133 0 : NS_ENSURE_SUCCESS(rv, rv);
134 : } else {
135 0 : CopyUTF16toUTF8(aEncoding, charsetGuess);
136 : }
137 :
138 0 : nsCString charset;
139 0 : rv = nsCharsetAlias::GetPreferred(charsetGuess, charset);
140 0 : NS_ENSURE_SUCCESS(rv, rv);
141 :
142 0 : return ConvertStream(stream, charset.get(), aResult);
143 : }
144 :
145 : nsresult
146 0 : FileReaderSyncPrivate::ReadAsDataURL(nsIDOMBlob* aBlob, nsAString& aResult)
147 : {
148 0 : nsAutoString scratchResult;
149 0 : scratchResult.AssignLiteral("data:");
150 :
151 0 : nsString contentType;
152 0 : aBlob->GetType(contentType);
153 :
154 0 : if (contentType.IsEmpty()) {
155 0 : scratchResult.AppendLiteral("application/octet-stream");
156 : } else {
157 0 : scratchResult.Append(contentType);
158 : }
159 0 : scratchResult.AppendLiteral(";base64,");
160 :
161 0 : nsCOMPtr<nsIInputStream> stream;
162 0 : nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream));
163 0 : NS_ENSURE_SUCCESS(rv, rv);
164 :
165 : PRUint64 size;
166 0 : rv = aBlob->GetSize(&size);
167 0 : NS_ENSURE_SUCCESS(rv, rv);
168 :
169 0 : nsCOMPtr<nsIInputStream> bufferedStream;
170 0 : rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, size);
171 0 : NS_ENSURE_SUCCESS(rv, rv);
172 :
173 0 : nsAutoString encodedData;
174 0 : rv = Base64EncodeInputStream(bufferedStream, encodedData, size);
175 0 : NS_ENSURE_SUCCESS(rv, rv);
176 :
177 0 : scratchResult.Append(encodedData);
178 :
179 0 : aResult = scratchResult;
180 0 : return NS_OK;
181 : }
182 :
183 : nsresult
184 0 : FileReaderSyncPrivate::ConvertStream(nsIInputStream *aStream,
185 : const char *aCharset,
186 : nsAString &aResult)
187 : {
188 : nsCOMPtr<nsIConverterInputStream> converterStream =
189 0 : do_CreateInstance("@mozilla.org/intl/converter-input-stream;1");
190 0 : NS_ENSURE_TRUE(converterStream, NS_ERROR_FAILURE);
191 :
192 0 : nsresult rv = converterStream->Init(aStream, aCharset, 8192,
193 0 : nsIConverterInputStream::DEFAULT_REPLACEMENT_CHARACTER);
194 0 : NS_ENSURE_SUCCESS(rv, rv);
195 :
196 : nsCOMPtr<nsIUnicharInputStream> unicharStream =
197 0 : do_QueryInterface(converterStream);
198 0 : NS_ENSURE_TRUE(unicharStream, NS_ERROR_FAILURE);
199 :
200 : PRUint32 numChars;
201 0 : nsString result;
202 0 : while (NS_SUCCEEDED(unicharStream->ReadString(8192, result, &numChars)) &&
203 : numChars > 0) {
204 0 : PRUint32 oldLength = aResult.Length();
205 0 : aResult.Append(result);
206 0 : if (aResult.Length() - oldLength != result.Length()) {
207 0 : return NS_ERROR_OUT_OF_MEMORY;
208 : }
209 : }
210 :
211 0 : return rv;
212 : }
213 :
214 : nsresult
215 0 : FileReaderSyncPrivate::GuessCharset(nsIInputStream *aStream,
216 : nsACString &aCharset)
217 : {
218 : // First try the universal charset detector
219 : nsCOMPtr<nsICharsetDetector> detector
220 : = do_CreateInstance(NS_CHARSET_DETECTOR_CONTRACTID_BASE
221 0 : "universal_charset_detector");
222 0 : if (!detector) {
223 0 : RuntimeService* runtime = RuntimeService::GetService();
224 0 : NS_ASSERTION(runtime, "This should never be null!");
225 :
226 : // No universal charset detector, try the default charset detector
227 0 : const nsACString& detectorName = runtime->GetDetectorName();
228 :
229 0 : if (!detectorName.IsEmpty()) {
230 0 : nsCAutoString detectorContractID;
231 0 : detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE);
232 0 : detectorContractID += detectorName;
233 0 : detector = do_CreateInstance(detectorContractID.get());
234 : }
235 : }
236 :
237 : nsresult rv;
238 0 : if (detector) {
239 0 : detector->Init(this);
240 :
241 : bool done;
242 : PRUint32 numRead;
243 0 : do {
244 : char readBuf[4096];
245 0 : rv = aStream->Read(readBuf, sizeof(readBuf), &numRead);
246 0 : NS_ENSURE_SUCCESS(rv, rv);
247 0 : if (numRead <= 0) {
248 0 : break;
249 : }
250 0 : rv = detector->DoIt(readBuf, numRead, &done);
251 0 : NS_ENSURE_SUCCESS(rv, rv);
252 0 : } while (!done);
253 :
254 0 : rv = detector->Done();
255 0 : NS_ENSURE_SUCCESS(rv, rv);
256 : } else {
257 : // no charset detector available, check the BOM
258 : unsigned char sniffBuf[4];
259 : PRUint32 numRead;
260 : rv = aStream->Read(reinterpret_cast<char*>(sniffBuf),
261 0 : sizeof(sniffBuf), &numRead);
262 0 : NS_ENSURE_SUCCESS(rv, rv);
263 :
264 0 : if (numRead >= 4 &&
265 0 : sniffBuf[0] == 0x00 &&
266 0 : sniffBuf[1] == 0x00 &&
267 0 : sniffBuf[2] == 0xfe &&
268 0 : sniffBuf[3] == 0xff) {
269 0 : mCharset = "UTF-32BE";
270 0 : } else if (numRead >= 4 &&
271 0 : sniffBuf[0] == 0xff &&
272 0 : sniffBuf[1] == 0xfe &&
273 0 : sniffBuf[2] == 0x00 &&
274 0 : sniffBuf[3] == 0x00) {
275 0 : mCharset = "UTF-32LE";
276 0 : } else if (numRead >= 2 &&
277 0 : sniffBuf[0] == 0xfe &&
278 0 : sniffBuf[1] == 0xff) {
279 0 : mCharset = "UTF-16BE";
280 0 : } else if (numRead >= 2 &&
281 0 : sniffBuf[0] == 0xff &&
282 0 : sniffBuf[1] == 0xfe) {
283 0 : mCharset = "UTF-16LE";
284 0 : } else if (numRead >= 3 &&
285 0 : sniffBuf[0] == 0xef &&
286 0 : sniffBuf[1] == 0xbb &&
287 0 : sniffBuf[2] == 0xbf) {
288 0 : mCharset = "UTF-8";
289 : }
290 : }
291 :
292 0 : if (mCharset.IsEmpty()) {
293 0 : RuntimeService* runtime = RuntimeService::GetService();
294 0 : mCharset = runtime->GetSystemCharset();
295 : }
296 :
297 0 : if (mCharset.IsEmpty()) {
298 : // no sniffed or default charset, try UTF-8
299 0 : mCharset.AssignLiteral("UTF-8");
300 : }
301 :
302 0 : aCharset = mCharset;
303 0 : mCharset.Truncate();
304 :
305 0 : return NS_OK;
306 : }
307 :
308 : NS_IMETHODIMP
309 0 : FileReaderSyncPrivate::Notify(const char* aCharset, nsDetectionConfident aConf)
310 : {
311 0 : mCharset.Assign(aCharset);
312 :
313 0 : return NS_OK;
314 : }
|