nsUnicharStreamLoader.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. /* -*- Mode: C++; tab-width: 2; 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 "mozilla/DebugOnly.h"
  6. #include "nsUnicharStreamLoader.h"
  7. #include "nsIInputStream.h"
  8. #include <algorithm>
  9. #include "mozilla/dom/EncodingUtils.h"
  10. // 1024 bytes is specified in
  11. // http://www.whatwg.org/specs/web-apps/current-work/#charset for HTML; for
  12. // other resource types (e.g. CSS) typically fewer bytes are fine too, since
  13. // they only look at things right at the beginning of the data.
  14. #define SNIFFING_BUFFER_SIZE 1024
  15. using namespace mozilla;
  16. using mozilla::dom::EncodingUtils;
  17. NS_IMETHODIMP
  18. nsUnicharStreamLoader::Init(nsIUnicharStreamLoaderObserver *aObserver)
  19. {
  20. NS_ENSURE_ARG_POINTER(aObserver);
  21. mObserver = aObserver;
  22. if (!mRawData.SetCapacity(SNIFFING_BUFFER_SIZE, fallible))
  23. return NS_ERROR_OUT_OF_MEMORY;
  24. return NS_OK;
  25. }
  26. nsresult
  27. nsUnicharStreamLoader::Create(nsISupports *aOuter,
  28. REFNSIID aIID,
  29. void **aResult)
  30. {
  31. if (aOuter) return NS_ERROR_NO_AGGREGATION;
  32. nsUnicharStreamLoader* it = new nsUnicharStreamLoader();
  33. NS_ADDREF(it);
  34. nsresult rv = it->QueryInterface(aIID, aResult);
  35. NS_RELEASE(it);
  36. return rv;
  37. }
  38. NS_IMPL_ISUPPORTS(nsUnicharStreamLoader, nsIUnicharStreamLoader,
  39. nsIRequestObserver, nsIStreamListener)
  40. NS_IMETHODIMP
  41. nsUnicharStreamLoader::GetChannel(nsIChannel **aChannel)
  42. {
  43. NS_IF_ADDREF(*aChannel = mChannel);
  44. return NS_OK;
  45. }
  46. NS_IMETHODIMP
  47. nsUnicharStreamLoader::GetCharset(nsACString& aCharset)
  48. {
  49. aCharset = mCharset;
  50. return NS_OK;
  51. }
  52. /* nsIRequestObserver implementation */
  53. NS_IMETHODIMP
  54. nsUnicharStreamLoader::OnStartRequest(nsIRequest*, nsISupports*)
  55. {
  56. return NS_OK;
  57. }
  58. NS_IMETHODIMP
  59. nsUnicharStreamLoader::OnStopRequest(nsIRequest *aRequest,
  60. nsISupports *aContext,
  61. nsresult aStatus)
  62. {
  63. if (!mObserver) {
  64. NS_ERROR("nsUnicharStreamLoader::OnStopRequest called before ::Init");
  65. return NS_ERROR_UNEXPECTED;
  66. }
  67. mContext = aContext;
  68. mChannel = do_QueryInterface(aRequest);
  69. nsresult rv = NS_OK;
  70. if (mRawData.Length() > 0 && NS_SUCCEEDED(aStatus)) {
  71. MOZ_ASSERT(mBuffer.Length() == 0,
  72. "should not have both decoded and raw data");
  73. rv = DetermineCharset();
  74. }
  75. if (NS_FAILED(rv)) {
  76. // Call the observer but pass it no data.
  77. mObserver->OnStreamComplete(this, mContext, rv, EmptyString());
  78. } else {
  79. mObserver->OnStreamComplete(this, mContext, aStatus, mBuffer);
  80. }
  81. mObserver = nullptr;
  82. mDecoder = nullptr;
  83. mContext = nullptr;
  84. mChannel = nullptr;
  85. mCharset.Truncate();
  86. mRawData.Truncate();
  87. mRawBuffer.Truncate();
  88. mBuffer.Truncate();
  89. return rv;
  90. }
  91. NS_IMETHODIMP
  92. nsUnicharStreamLoader::GetRawBuffer(nsACString& aRawBuffer)
  93. {
  94. aRawBuffer = mRawBuffer;
  95. return NS_OK;
  96. }
  97. /* nsIStreamListener implementation */
  98. NS_IMETHODIMP
  99. nsUnicharStreamLoader::OnDataAvailable(nsIRequest *aRequest,
  100. nsISupports *aContext,
  101. nsIInputStream *aInputStream,
  102. uint64_t aSourceOffset,
  103. uint32_t aCount)
  104. {
  105. if (!mObserver) {
  106. NS_ERROR("nsUnicharStreamLoader::OnDataAvailable called before ::Init");
  107. return NS_ERROR_UNEXPECTED;
  108. }
  109. mContext = aContext;
  110. mChannel = do_QueryInterface(aRequest);
  111. nsresult rv = NS_OK;
  112. if (mDecoder) {
  113. // process everything we've got
  114. uint32_t dummy;
  115. aInputStream->ReadSegments(WriteSegmentFun, this, aCount, &dummy);
  116. } else {
  117. // no decoder yet. Read up to SNIFFING_BUFFER_SIZE octets into
  118. // mRawData (this is the cutoff specified in
  119. // draft-abarth-mime-sniff-06). If we can get that much, then go
  120. // ahead and fire charset detection and read the rest. Otherwise
  121. // wait for more data.
  122. uint32_t haveRead = mRawData.Length();
  123. uint32_t toRead = std::min(SNIFFING_BUFFER_SIZE - haveRead, aCount);
  124. uint32_t n;
  125. char *here = mRawData.BeginWriting() + haveRead;
  126. rv = aInputStream->Read(here, toRead, &n);
  127. if (NS_SUCCEEDED(rv)) {
  128. mRawData.SetLength(haveRead + n);
  129. if (mRawData.Length() == SNIFFING_BUFFER_SIZE) {
  130. rv = DetermineCharset();
  131. if (NS_SUCCEEDED(rv)) {
  132. // process what's left
  133. uint32_t dummy;
  134. aInputStream->ReadSegments(WriteSegmentFun, this, aCount - n, &dummy);
  135. }
  136. } else {
  137. MOZ_ASSERT(n == aCount, "didn't read as much as was available");
  138. }
  139. }
  140. }
  141. mContext = nullptr;
  142. mChannel = nullptr;
  143. return rv;
  144. }
  145. nsresult
  146. nsUnicharStreamLoader::DetermineCharset()
  147. {
  148. nsresult rv = mObserver->OnDetermineCharset(this, mContext,
  149. mRawData, mCharset);
  150. if (NS_FAILED(rv) || mCharset.IsEmpty()) {
  151. // The observer told us nothing useful
  152. mCharset.AssignLiteral("UTF-8");
  153. }
  154. // Sadly, nsIUnicharStreamLoader is exposed to extensions, so we can't
  155. // assume mozilla::css::Loader to be the only caller. Special-casing
  156. // replacement, since it's not invariant under a second label resolution
  157. // operation.
  158. if (mCharset.EqualsLiteral("replacement")) {
  159. mDecoder = EncodingUtils::DecoderForEncoding(mCharset);
  160. } else {
  161. nsAutoCString charset;
  162. if (!EncodingUtils::FindEncodingForLabelNoReplacement(mCharset, charset)) {
  163. // If we got replacement here, the caller was not mozilla::css::Loader
  164. // but an extension.
  165. return NS_ERROR_UCONV_NOCONV;
  166. }
  167. mDecoder = EncodingUtils::DecoderForEncoding(charset);
  168. }
  169. // Process the data into mBuffer
  170. uint32_t dummy;
  171. rv = WriteSegmentFun(nullptr, this,
  172. mRawData.BeginReading(),
  173. 0, mRawData.Length(),
  174. &dummy);
  175. mRawData.Truncate();
  176. return rv;
  177. }
  178. nsresult
  179. nsUnicharStreamLoader::WriteSegmentFun(nsIInputStream *,
  180. void *aClosure,
  181. const char *aSegment,
  182. uint32_t,
  183. uint32_t aCount,
  184. uint32_t *aWriteCount)
  185. {
  186. nsUnicharStreamLoader* self = static_cast<nsUnicharStreamLoader*>(aClosure);
  187. uint32_t haveRead = self->mBuffer.Length();
  188. int32_t srcLen = aCount;
  189. int32_t dstLen;
  190. nsresult rv = self->mDecoder->GetMaxLength(aSegment, srcLen, &dstLen);
  191. if (NS_WARN_IF(NS_FAILED(rv))) {
  192. return rv;
  193. }
  194. uint32_t capacity = haveRead + dstLen;
  195. if (!self->mBuffer.SetCapacity(capacity, fallible)) {
  196. return NS_ERROR_OUT_OF_MEMORY;
  197. }
  198. if (!self->mRawBuffer.Append(aSegment, aCount, fallible)) {
  199. return NS_ERROR_OUT_OF_MEMORY;
  200. }
  201. rv = self->mDecoder->Convert(aSegment,
  202. &srcLen,
  203. self->mBuffer.BeginWriting() + haveRead,
  204. &dstLen);
  205. if (NS_WARN_IF(NS_FAILED(rv))) {
  206. return rv;
  207. }
  208. MOZ_ASSERT(srcLen == static_cast<int32_t>(aCount));
  209. haveRead += dstLen;
  210. self->mBuffer.SetLength(haveRead);
  211. *aWriteCount = aCount;
  212. return NS_OK;
  213. }