FileReaderSync.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "FileReaderSync.h"
  6. #include "jsfriendapi.h"
  7. #include "mozilla/Unused.h"
  8. #include "mozilla/Base64.h"
  9. #include "mozilla/dom/EncodingUtils.h"
  10. #include "mozilla/dom/File.h"
  11. #include "nsContentUtils.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 "nsIConverterInputStream.h"
  19. #include "nsIInputStream.h"
  20. #include "nsISeekableStream.h"
  21. #include "nsISupportsImpl.h"
  22. #include "nsNetUtil.h"
  23. #include "nsServiceManagerUtils.h"
  24. #include "RuntimeService.h"
  25. using namespace mozilla;
  26. using namespace mozilla::dom;
  27. using mozilla::dom::Optional;
  28. using mozilla::dom::GlobalObject;
  29. // static
  30. already_AddRefed<FileReaderSync>
  31. FileReaderSync::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
  32. {
  33. RefPtr<FileReaderSync> frs = new FileReaderSync();
  34. return frs.forget();
  35. }
  36. bool
  37. FileReaderSync::WrapObject(JSContext* aCx,
  38. JS::Handle<JSObject*> aGivenProto,
  39. JS::MutableHandle<JSObject*> aReflector)
  40. {
  41. return FileReaderSyncBinding::Wrap(aCx, this, aGivenProto, aReflector);
  42. }
  43. void
  44. FileReaderSync::ReadAsArrayBuffer(JSContext* aCx,
  45. JS::Handle<JSObject*> aScopeObj,
  46. Blob& aBlob,
  47. JS::MutableHandle<JSObject*> aRetval,
  48. ErrorResult& aRv)
  49. {
  50. uint64_t blobSize = aBlob.GetSize(aRv);
  51. if (NS_WARN_IF(aRv.Failed())) {
  52. return;
  53. }
  54. UniquePtr<char[], JS::FreePolicy> bufferData(js_pod_malloc<char>(blobSize));
  55. if (!bufferData) {
  56. aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
  57. return;
  58. }
  59. nsCOMPtr<nsIInputStream> stream;
  60. aBlob.GetInternalStream(getter_AddRefs(stream), aRv);
  61. if (NS_WARN_IF(aRv.Failed())) {
  62. return;
  63. }
  64. uint32_t numRead;
  65. aRv = stream->Read(bufferData.get(), blobSize, &numRead);
  66. if (NS_WARN_IF(aRv.Failed())) {
  67. return;
  68. }
  69. NS_ASSERTION(numRead == blobSize, "failed to read data");
  70. JSObject* arrayBuffer = JS_NewArrayBufferWithContents(aCx, blobSize, bufferData.get());
  71. if (!arrayBuffer) {
  72. aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
  73. return;
  74. }
  75. // arrayBuffer takes the ownership when it is not null. Otherwise we
  76. // need to release it explicitly.
  77. mozilla::Unused << bufferData.release();
  78. aRetval.set(arrayBuffer);
  79. }
  80. void
  81. FileReaderSync::ReadAsBinaryString(Blob& aBlob,
  82. nsAString& aResult,
  83. ErrorResult& aRv)
  84. {
  85. nsCOMPtr<nsIInputStream> stream;
  86. aBlob.GetInternalStream(getter_AddRefs(stream), aRv);
  87. if (NS_WARN_IF(aRv.Failed())) {
  88. return;
  89. }
  90. uint32_t numRead;
  91. do {
  92. char readBuf[4096];
  93. aRv = stream->Read(readBuf, sizeof(readBuf), &numRead);
  94. if (NS_WARN_IF(aRv.Failed())) {
  95. return;
  96. }
  97. uint32_t oldLength = aResult.Length();
  98. AppendASCIItoUTF16(Substring(readBuf, readBuf + numRead), aResult);
  99. if (aResult.Length() - oldLength != numRead) {
  100. aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
  101. return;
  102. }
  103. } while (numRead > 0);
  104. }
  105. void
  106. FileReaderSync::ReadAsText(Blob& aBlob,
  107. const Optional<nsAString>& aEncoding,
  108. nsAString& aResult,
  109. ErrorResult& aRv)
  110. {
  111. nsCOMPtr<nsIInputStream> stream;
  112. aBlob.GetInternalStream(getter_AddRefs(stream), aRv);
  113. if (NS_WARN_IF(aRv.Failed())) {
  114. return;
  115. }
  116. nsAutoCString encoding;
  117. unsigned char sniffBuf[3] = { 0, 0, 0 };
  118. uint32_t numRead;
  119. aRv = stream->Read(reinterpret_cast<char*>(sniffBuf),
  120. sizeof(sniffBuf), &numRead);
  121. if (NS_WARN_IF(aRv.Failed())) {
  122. return;
  123. }
  124. // The BOM sniffing is baked into the "decode" part of the Encoding
  125. // Standard, which the File API references.
  126. if (!nsContentUtils::CheckForBOM(sniffBuf, numRead, encoding)) {
  127. // BOM sniffing failed. Try the API argument.
  128. if (!aEncoding.WasPassed() ||
  129. !EncodingUtils::FindEncodingForLabel(aEncoding.Value(),
  130. encoding)) {
  131. // API argument failed. Try the type property of the blob.
  132. nsAutoString type16;
  133. aBlob.GetType(type16);
  134. NS_ConvertUTF16toUTF8 type(type16);
  135. nsAutoCString specifiedCharset;
  136. bool haveCharset;
  137. int32_t charsetStart, charsetEnd;
  138. NS_ExtractCharsetFromContentType(type,
  139. specifiedCharset,
  140. &haveCharset,
  141. &charsetStart,
  142. &charsetEnd);
  143. if (!EncodingUtils::FindEncodingForLabel(specifiedCharset, encoding)) {
  144. // Type property failed. Use UTF-8.
  145. encoding.AssignLiteral("UTF-8");
  146. }
  147. }
  148. }
  149. nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(stream);
  150. if (!seekable) {
  151. aRv.Throw(NS_ERROR_FAILURE);
  152. return;
  153. }
  154. // Seek to 0 because to undo the BOM sniffing advance. UTF-8 and UTF-16
  155. // decoders will swallow the BOM.
  156. aRv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
  157. if (NS_WARN_IF(aRv.Failed())) {
  158. return;
  159. }
  160. aRv = ConvertStream(stream, encoding.get(), aResult);
  161. if (NS_WARN_IF(aRv.Failed())) {
  162. return;
  163. }
  164. }
  165. void
  166. FileReaderSync::ReadAsDataURL(Blob& aBlob, nsAString& aResult,
  167. ErrorResult& aRv)
  168. {
  169. nsAutoString scratchResult;
  170. scratchResult.AssignLiteral("data:");
  171. nsString contentType;
  172. aBlob.GetType(contentType);
  173. if (contentType.IsEmpty()) {
  174. scratchResult.AppendLiteral("application/octet-stream");
  175. } else {
  176. scratchResult.Append(contentType);
  177. }
  178. scratchResult.AppendLiteral(";base64,");
  179. nsCOMPtr<nsIInputStream> stream;
  180. aBlob.GetInternalStream(getter_AddRefs(stream), aRv);
  181. if (NS_WARN_IF(aRv.Failed())){
  182. return;
  183. }
  184. uint64_t size = aBlob.GetSize(aRv);
  185. if (NS_WARN_IF(aRv.Failed())){
  186. return;
  187. }
  188. nsCOMPtr<nsIInputStream> bufferedStream;
  189. aRv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, size);
  190. if (NS_WARN_IF(aRv.Failed())){
  191. return;
  192. }
  193. nsAutoString encodedData;
  194. aRv = Base64EncodeInputStream(bufferedStream, encodedData, size);
  195. if (NS_WARN_IF(aRv.Failed())){
  196. return;
  197. }
  198. scratchResult.Append(encodedData);
  199. aResult = scratchResult;
  200. }
  201. nsresult
  202. FileReaderSync::ConvertStream(nsIInputStream *aStream,
  203. const char *aCharset,
  204. nsAString &aResult)
  205. {
  206. nsCOMPtr<nsIConverterInputStream> converterStream =
  207. do_CreateInstance("@mozilla.org/intl/converter-input-stream;1");
  208. NS_ENSURE_TRUE(converterStream, NS_ERROR_FAILURE);
  209. nsresult rv = converterStream->Init(aStream, aCharset, 8192,
  210. nsIConverterInputStream::DEFAULT_REPLACEMENT_CHARACTER);
  211. NS_ENSURE_SUCCESS(rv, rv);
  212. nsCOMPtr<nsIUnicharInputStream> unicharStream =
  213. do_QueryInterface(converterStream);
  214. NS_ENSURE_TRUE(unicharStream, NS_ERROR_FAILURE);
  215. uint32_t numChars;
  216. nsString result;
  217. while (NS_SUCCEEDED(unicharStream->ReadString(8192, result, &numChars)) &&
  218. numChars > 0) {
  219. uint32_t oldLength = aResult.Length();
  220. aResult.Append(result);
  221. if (aResult.Length() - oldLength != result.Length()) {
  222. return NS_ERROR_OUT_OF_MEMORY;
  223. }
  224. }
  225. return rv;
  226. }