1 : /* -*- Mode: C++; tab-width: 4; 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 mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2002
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Boris Zbarsky <bzbarsky@mit.edu> (original author)
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsUnicharStreamLoader.h"
40 : #include "nsIInputStream.h"
41 : #include "nsICharsetConverterManager.h"
42 : #include "nsIServiceManager.h"
43 :
44 : #define SNIFFING_BUFFER_SIZE 512 // specified in draft-abarth-mime-sniff-06
45 :
46 : NS_IMETHODIMP
47 2 : nsUnicharStreamLoader::Init(nsIUnicharStreamLoaderObserver *aObserver)
48 : {
49 2 : NS_ENSURE_ARG_POINTER(aObserver);
50 :
51 2 : mObserver = aObserver;
52 :
53 2 : if (!mRawData.SetCapacity(SNIFFING_BUFFER_SIZE))
54 0 : return NS_ERROR_OUT_OF_MEMORY;
55 :
56 2 : return NS_OK;
57 : }
58 :
59 : nsresult
60 2 : nsUnicharStreamLoader::Create(nsISupports *aOuter,
61 : REFNSIID aIID,
62 : void **aResult)
63 : {
64 2 : if (aOuter) return NS_ERROR_NO_AGGREGATION;
65 :
66 2 : nsUnicharStreamLoader* it = new nsUnicharStreamLoader();
67 2 : NS_ADDREF(it);
68 2 : nsresult rv = it->QueryInterface(aIID, aResult);
69 2 : NS_RELEASE(it);
70 2 : return rv;
71 : }
72 :
73 46 : NS_IMPL_ISUPPORTS3(nsUnicharStreamLoader, nsIUnicharStreamLoader,
74 : nsIRequestObserver, nsIStreamListener)
75 :
76 : /* readonly attribute nsIChannel channel; */
77 : NS_IMETHODIMP
78 1 : nsUnicharStreamLoader::GetChannel(nsIChannel **aChannel)
79 : {
80 1 : NS_IF_ADDREF(*aChannel = mChannel);
81 1 : return NS_OK;
82 : }
83 :
84 : /* readonly attribute nsACString charset */
85 : NS_IMETHODIMP
86 0 : nsUnicharStreamLoader::GetCharset(nsACString& aCharset)
87 : {
88 0 : aCharset = mCharset;
89 0 : return NS_OK;
90 : }
91 :
92 : /* nsIRequestObserver implementation */
93 : NS_IMETHODIMP
94 1 : nsUnicharStreamLoader::OnStartRequest(nsIRequest*, nsISupports*)
95 : {
96 1 : return NS_OK;
97 : }
98 :
99 : NS_IMETHODIMP
100 1 : nsUnicharStreamLoader::OnStopRequest(nsIRequest *aRequest,
101 : nsISupports *aContext,
102 : nsresult aStatus)
103 : {
104 1 : if (!mObserver) {
105 0 : NS_ERROR("nsUnicharStreamLoader::OnStopRequest called before ::Init");
106 0 : return NS_ERROR_UNEXPECTED;
107 : }
108 :
109 1 : mContext = aContext;
110 1 : mChannel = do_QueryInterface(aRequest);
111 :
112 1 : nsresult rv = NS_OK;
113 1 : if (mRawData.Length() > 0 && NS_SUCCEEDED(aStatus)) {
114 0 : NS_ABORT_IF_FALSE(mBuffer.Length() == 0,
115 : "should not have both decoded and raw data");
116 0 : rv = DetermineCharset();
117 : }
118 :
119 1 : if (NS_FAILED(rv)) {
120 : // Call the observer but pass it no data.
121 0 : mObserver->OnStreamComplete(this, mContext, rv, EmptyString());
122 : } else {
123 1 : mObserver->OnStreamComplete(this, mContext, aStatus, mBuffer);
124 : }
125 :
126 1 : mObserver = nsnull;
127 1 : mDecoder = nsnull;
128 1 : mContext = nsnull;
129 1 : mChannel = nsnull;
130 1 : mCharset.Truncate();
131 1 : mBuffer.Truncate();
132 1 : return rv;
133 : }
134 :
135 : /* nsIStreamListener implementation */
136 : NS_IMETHODIMP
137 0 : nsUnicharStreamLoader::OnDataAvailable(nsIRequest *aRequest,
138 : nsISupports *aContext,
139 : nsIInputStream *aInputStream,
140 : PRUint32 aSourceOffset,
141 : PRUint32 aCount)
142 : {
143 0 : if (!mObserver) {
144 0 : NS_ERROR("nsUnicharStreamLoader::OnDataAvailable called before ::Init");
145 0 : return NS_ERROR_UNEXPECTED;
146 : }
147 :
148 0 : mContext = aContext;
149 0 : mChannel = do_QueryInterface(aRequest);
150 :
151 0 : nsresult rv = NS_OK;
152 0 : if (mDecoder) {
153 : // process everything we've got
154 : PRUint32 dummy;
155 0 : aInputStream->ReadSegments(WriteSegmentFun, this, aCount, &dummy);
156 : } else {
157 : // no decoder yet. Read up to SNIFFING_BUFFER_SIZE octets into
158 : // mRawData (this is the cutoff specified in
159 : // draft-abarth-mime-sniff-06). If we can get that much, then go
160 : // ahead and fire charset detection and read the rest. Otherwise
161 : // wait for more data.
162 :
163 0 : PRUint32 haveRead = mRawData.Length();
164 0 : PRUint32 toRead = NS_MIN(SNIFFING_BUFFER_SIZE - haveRead, aCount);
165 : PRUint32 n;
166 0 : char *here = mRawData.BeginWriting() + haveRead;
167 :
168 0 : rv = aInputStream->Read(here, toRead, &n);
169 0 : if (NS_SUCCEEDED(rv)) {
170 0 : mRawData.SetLength(haveRead + n);
171 0 : if (mRawData.Length() == SNIFFING_BUFFER_SIZE) {
172 0 : rv = DetermineCharset();
173 0 : if (NS_SUCCEEDED(rv)) {
174 : // process what's left
175 : PRUint32 dummy;
176 0 : aInputStream->ReadSegments(WriteSegmentFun, this, aCount - n, &dummy);
177 : }
178 : } else {
179 0 : NS_ABORT_IF_FALSE(n == aCount, "didn't read as much as was available");
180 : }
181 : }
182 : }
183 :
184 0 : mContext = nsnull;
185 0 : mChannel = nsnull;
186 0 : return rv;
187 : }
188 :
189 : /* internal */
190 : static NS_DEFINE_CID(kCharsetConverterManagerCID,
191 : NS_ICHARSETCONVERTERMANAGER_CID);
192 :
193 : nsresult
194 0 : nsUnicharStreamLoader::DetermineCharset()
195 : {
196 0 : nsresult rv = mObserver->OnDetermineCharset(this, mContext,
197 0 : mRawData, mCharset);
198 0 : if (NS_FAILED(rv) || mCharset.IsEmpty()) {
199 : // The observer told us nothing useful
200 0 : mCharset.AssignLiteral("UTF-8");
201 : }
202 :
203 : // Create the decoder for this character set
204 : nsCOMPtr<nsICharsetConverterManager> ccm =
205 0 : do_GetService(kCharsetConverterManagerCID, &rv);
206 0 : if (NS_FAILED(rv)) return rv;
207 :
208 0 : rv = ccm->GetUnicodeDecoder(mCharset.get(), getter_AddRefs(mDecoder));
209 0 : if (NS_FAILED(rv)) return rv;
210 :
211 : // Process the data into mBuffer
212 : PRUint32 dummy;
213 : rv = WriteSegmentFun(nsnull, this,
214 : mRawData.BeginReading(),
215 : 0, mRawData.Length(),
216 0 : &dummy);
217 0 : mRawData.Truncate();
218 0 : return rv;
219 : }
220 :
221 : NS_METHOD
222 0 : nsUnicharStreamLoader::WriteSegmentFun(nsIInputStream *,
223 : void *aClosure,
224 : const char *aSegment,
225 : PRUint32,
226 : PRUint32 aCount,
227 : PRUint32 *aWriteCount)
228 : {
229 0 : nsUnicharStreamLoader* self = static_cast<nsUnicharStreamLoader*>(aClosure);
230 :
231 0 : PRUint32 haveRead = self->mBuffer.Length();
232 0 : PRUint32 consumed = 0;
233 : nsresult rv;
234 0 : do {
235 0 : PRInt32 srcLen = aCount - consumed;
236 : PRInt32 dstLen;
237 0 : self->mDecoder->GetMaxLength(aSegment + consumed, srcLen, &dstLen);
238 :
239 0 : PRUint32 capacity = haveRead + dstLen;
240 0 : if (!self->mBuffer.SetCapacity(capacity)) {
241 0 : return NS_ERROR_OUT_OF_MEMORY;
242 : }
243 :
244 0 : rv = self->mDecoder->Convert(aSegment + consumed,
245 : &srcLen,
246 0 : self->mBuffer.BeginWriting() + haveRead,
247 0 : &dstLen);
248 0 : haveRead += dstLen;
249 : // XXX if srcLen is negative, we want to drop the _first_ byte in
250 : // the erroneous byte sequence and try again. This is not quite
251 : // possible right now -- see bug 160784
252 0 : consumed += srcLen;
253 0 : if (NS_FAILED(rv)) {
254 0 : NS_ASSERTION(0 < capacity - haveRead,
255 : "Decoder returned an error but filled the output buffer! "
256 : "Should not happen.");
257 0 : self->mBuffer.BeginWriting()[haveRead++] = 0xFFFD;
258 0 : ++consumed;
259 : // XXX this is needed to make sure we don't underrun our buffer;
260 : // bug 160784 again
261 0 : consumed = NS_MAX<PRUint32>(consumed, 0);
262 0 : self->mDecoder->Reset();
263 : }
264 : } while (consumed < aCount);
265 :
266 0 : self->mBuffer.SetLength(haveRead);
267 0 : *aWriteCount = aCount;
268 0 : return NS_OK;
269 : }
|