nsDeflateConverter.cpp (5843B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 */ 5 6 #include "StreamFunctions.h" 7 #include "MainThreadUtils.h" 8 #include "nsDeflateConverter.h" 9 #include "nsIThreadRetargetableStreamListener.h" 10 #include "nsStringStream.h" 11 #include "nsComponentManagerUtils.h" 12 #include "nsCRT.h" 13 #include "plstr.h" 14 #include "mozilla/UniquePtr.h" 15 16 #define ZLIB_TYPE "deflate" 17 #define GZIP_TYPE "gzip" 18 #define X_GZIP_TYPE "x-gzip" 19 20 using namespace mozilla; 21 22 /** 23 * nsDeflateConverter is a stream converter applies the deflate compression 24 * method to the data. 25 */ 26 NS_IMPL_ISUPPORTS(nsDeflateConverter, nsIStreamConverter, nsIStreamListener, 27 nsIThreadRetargetableStreamListener, nsIRequestObserver) 28 29 nsresult nsDeflateConverter::Init() { 30 int zerr; 31 32 mOffset = 0; 33 34 mZstream.zalloc = Z_NULL; 35 mZstream.zfree = Z_NULL; 36 mZstream.opaque = Z_NULL; 37 38 int32_t window = MAX_WBITS; 39 switch (mWrapMode) { 40 case WRAP_NONE: 41 window = -window; 42 break; 43 case WRAP_GZIP: 44 window += 16; 45 break; 46 default: 47 break; 48 } 49 50 zerr = deflateInit2(&mZstream, mLevel, Z_DEFLATED, window, 8, 51 Z_DEFAULT_STRATEGY); 52 if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY; 53 54 mZstream.next_out = mWriteBuffer; 55 mZstream.avail_out = sizeof(mWriteBuffer); 56 57 // mark the input buffer as empty. 58 mZstream.avail_in = 0; 59 mZstream.next_in = Z_NULL; 60 61 return NS_OK; 62 } 63 64 NS_IMETHODIMP nsDeflateConverter::Convert(nsIInputStream* aFromStream, 65 const char* aFromType, 66 const char* aToType, 67 nsISupports* aCtxt, 68 nsIInputStream** _retval) { 69 return NS_ERROR_NOT_IMPLEMENTED; 70 } 71 72 NS_IMETHODIMP nsDeflateConverter::AsyncConvertData(const char* aFromType, 73 const char* aToType, 74 nsIStreamListener* aListener, 75 nsISupports* aCtxt) { 76 if (mListener) return NS_ERROR_ALREADY_INITIALIZED; 77 78 NS_ENSURE_ARG_POINTER(aListener); 79 80 if (!PL_strncasecmp(aToType, ZLIB_TYPE, sizeof(ZLIB_TYPE) - 1)) { 81 mWrapMode = WRAP_ZLIB; 82 } else if (!nsCRT::strcasecmp(aToType, GZIP_TYPE) || 83 !nsCRT::strcasecmp(aToType, X_GZIP_TYPE)) { 84 mWrapMode = WRAP_GZIP; 85 } else { 86 mWrapMode = WRAP_NONE; 87 } 88 89 nsresult rv = Init(); 90 NS_ENSURE_SUCCESS(rv, rv); 91 92 mListener = aListener; 93 mContext = aCtxt; 94 return rv; 95 } 96 97 NS_IMETHODIMP 98 nsDeflateConverter::GetConvertedType(const nsACString& aFromType, 99 nsIChannel* aChannel, 100 nsACString& aToType) { 101 return NS_ERROR_NOT_IMPLEMENTED; 102 } 103 104 NS_IMETHODIMP nsDeflateConverter::OnDataAvailable(nsIRequest* aRequest, 105 nsIInputStream* aInputStream, 106 uint64_t aOffset, 107 uint32_t aCount) { 108 if (!mListener) return NS_ERROR_NOT_INITIALIZED; 109 110 auto buffer = MakeUnique<char[]>(aCount); 111 NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY); 112 113 nsresult rv = ZW_ReadData(aInputStream, buffer.get(), aCount); 114 NS_ENSURE_SUCCESS(rv, rv); 115 116 // make sure we aren't reading too much 117 mZstream.avail_in = aCount; 118 mZstream.next_in = (unsigned char*)buffer.get(); 119 120 int zerr = Z_OK; 121 // deflate loop 122 while (mZstream.avail_in > 0 && zerr == Z_OK) { 123 zerr = deflate(&mZstream, Z_NO_FLUSH); 124 125 while (mZstream.avail_out == 0) { 126 // buffer is full, push the data out to the listener 127 rv = PushAvailableData(aRequest); 128 NS_ENSURE_SUCCESS(rv, rv); 129 zerr = deflate(&mZstream, Z_NO_FLUSH); 130 } 131 } 132 133 return NS_OK; 134 } 135 136 NS_IMETHODIMP 137 nsDeflateConverter::MaybeRetarget(nsIRequest* request) { 138 return NS_ERROR_NOT_IMPLEMENTED; 139 } 140 141 NS_IMETHODIMP nsDeflateConverter::OnStartRequest(nsIRequest* aRequest) { 142 if (!mListener) return NS_ERROR_NOT_INITIALIZED; 143 144 return mListener->OnStartRequest(aRequest); 145 } 146 147 NS_IMETHODIMP 148 nsDeflateConverter::OnDataFinished(nsresult aStatus) { 149 nsCOMPtr<nsIThreadRetargetableStreamListener> retargetable = 150 do_QueryInterface(mListener); 151 152 if (retargetable) { 153 return retargetable->OnDataFinished(aStatus); 154 } 155 156 return NS_OK; 157 } 158 159 NS_IMETHODIMP 160 nsDeflateConverter::CheckListenerChain() { return NS_ERROR_NO_INTERFACE; } 161 162 NS_IMETHODIMP nsDeflateConverter::OnStopRequest(nsIRequest* aRequest, 163 nsresult aStatusCode) { 164 if (!mListener) return NS_ERROR_NOT_INITIALIZED; 165 166 nsresult rv; 167 168 int zerr; 169 do { 170 zerr = deflate(&mZstream, Z_FINISH); 171 rv = PushAvailableData(aRequest); 172 NS_ENSURE_SUCCESS(rv, rv); 173 } while (zerr == Z_OK); 174 175 deflateEnd(&mZstream); 176 177 return mListener->OnStopRequest(aRequest, aStatusCode); 178 } 179 180 nsresult nsDeflateConverter::PushAvailableData(nsIRequest* aRequest) { 181 uint32_t bytesToWrite = sizeof(mWriteBuffer) - mZstream.avail_out; 182 // We don't need to do anything if there isn't any data 183 if (bytesToWrite == 0) return NS_OK; 184 185 MOZ_ASSERT(bytesToWrite <= INT32_MAX); 186 nsCOMPtr<nsIInputStream> stream; 187 nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), 188 Span((char*)mWriteBuffer, bytesToWrite), 189 NS_ASSIGNMENT_DEPEND); 190 NS_ENSURE_SUCCESS(rv, rv); 191 192 rv = mListener->OnDataAvailable(aRequest, stream, mOffset, bytesToWrite); 193 194 // now set the state for 'deflate' 195 mZstream.next_out = mWriteBuffer; 196 mZstream.avail_out = sizeof(mWriteBuffer); 197 198 mOffset += bytesToWrite; 199 return rv; 200 }