1 : /* -*- Mode: C++; tab-width: 2; 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) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsNPAPIPluginStreamListener.h"
39 : #include "plstr.h"
40 : #include "prmem.h"
41 : #include "nsDirectoryServiceDefs.h"
42 : #include "nsDirectoryServiceUtils.h"
43 : #include "nsILocalFile.h"
44 : #include "nsNetUtil.h"
45 : #include "nsPluginHost.h"
46 : #include "nsNPAPIPlugin.h"
47 : #include "nsPluginSafety.h"
48 : #include "nsPluginLogging.h"
49 : #include "nsPluginStreamListenerPeer.h"
50 :
51 0 : NS_IMPL_ISUPPORTS1(nsPluginStreamToFile, nsIOutputStream)
52 :
53 0 : nsPluginStreamToFile::nsPluginStreamToFile(const char* target,
54 : nsIPluginInstanceOwner* owner)
55 0 : : mTarget(PL_strdup(target)),
56 0 : mOwner(owner)
57 : {
58 : nsresult rv;
59 0 : nsCOMPtr<nsIFile> pluginTmp;
60 0 : rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(pluginTmp));
61 0 : if (NS_FAILED(rv)) return;
62 :
63 0 : mTempFile = do_QueryInterface(pluginTmp, &rv);
64 0 : if (NS_FAILED(rv)) return;
65 :
66 : // need to create a file with a unique name - use target as the basis
67 0 : rv = mTempFile->AppendNative(nsDependentCString(target));
68 0 : if (NS_FAILED(rv)) return;
69 :
70 : // Yes, make it unique.
71 0 : rv = mTempFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0700);
72 0 : if (NS_FAILED(rv)) return;
73 :
74 : // create the file
75 0 : rv = NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), mTempFile, -1, 00600);
76 0 : if (NS_FAILED(rv))
77 : return;
78 :
79 : // construct the URL we'll use later in calls to GetURL()
80 0 : NS_GetURLSpecFromFile(mTempFile, mFileURL);
81 :
82 : #ifdef NS_DEBUG
83 0 : printf("File URL = %s\n", mFileURL.get());
84 : #endif
85 : }
86 :
87 0 : nsPluginStreamToFile::~nsPluginStreamToFile()
88 : {
89 : // should we be deleting mTempFile here?
90 0 : if (nsnull != mTarget)
91 0 : PL_strfree(mTarget);
92 0 : }
93 :
94 : NS_IMETHODIMP
95 0 : nsPluginStreamToFile::Flush()
96 : {
97 0 : return NS_OK;
98 : }
99 :
100 : NS_IMETHODIMP
101 0 : nsPluginStreamToFile::Write(const char* aBuf, PRUint32 aCount,
102 : PRUint32 *aWriteCount)
103 : {
104 0 : mOutputStream->Write(aBuf, aCount, aWriteCount);
105 0 : mOutputStream->Flush();
106 0 : mOwner->GetURL(mFileURL.get(), mTarget, nsnull, nsnull, 0);
107 :
108 0 : return NS_OK;
109 : }
110 :
111 : NS_IMETHODIMP
112 0 : nsPluginStreamToFile::WriteFrom(nsIInputStream *inStr, PRUint32 count,
113 : PRUint32 *_retval)
114 : {
115 0 : NS_NOTREACHED("WriteFrom");
116 0 : return NS_ERROR_NOT_IMPLEMENTED;
117 : }
118 :
119 : NS_IMETHODIMP
120 0 : nsPluginStreamToFile::WriteSegments(nsReadSegmentFun reader, void * closure,
121 : PRUint32 count, PRUint32 *_retval)
122 : {
123 0 : NS_NOTREACHED("WriteSegments");
124 0 : return NS_ERROR_NOT_IMPLEMENTED;
125 : }
126 :
127 : NS_IMETHODIMP
128 0 : nsPluginStreamToFile::IsNonBlocking(bool *aNonBlocking)
129 : {
130 0 : *aNonBlocking = false;
131 0 : return NS_OK;
132 : }
133 :
134 : NS_IMETHODIMP
135 0 : nsPluginStreamToFile::Close(void)
136 : {
137 0 : mOutputStream->Close();
138 0 : mOwner->GetURL(mFileURL.get(), mTarget, nsnull, nsnull, 0);
139 0 : return NS_OK;
140 : }
141 :
142 : // nsNPAPIPluginStreamListener Methods
143 :
144 0 : NS_IMPL_ISUPPORTS3(nsNPAPIPluginStreamListener, nsIPluginStreamListener,
145 : nsITimerCallback, nsIHTTPHeaderListener)
146 :
147 0 : nsNPAPIPluginStreamListener::nsNPAPIPluginStreamListener(nsNPAPIPluginInstance* inst,
148 : void* notifyData,
149 : const char* aURL)
150 : : mStreamBuffer(nsnull),
151 : mNotifyURL(aURL ? PL_strdup(aURL) : nsnull),
152 : mInst(inst),
153 : mStreamListenerPeer(nsnull),
154 : mStreamBufferSize(0),
155 : mStreamBufferByteCount(0),
156 : mStreamType(NP_NORMAL),
157 : mStreamStarted(false),
158 : mStreamCleanedUp(false),
159 : mCallNotify(notifyData ? true : false),
160 : mIsSuspended(false),
161 0 : mIsPluginInitJSStream(mInst->mInPluginInitCall &&
162 : aURL && strncmp(aURL, "javascript:",
163 0 : sizeof("javascript:") - 1) == 0),
164 : mRedirectDenied(false),
165 0 : mResponseHeaderBuf(nsnull)
166 : {
167 0 : memset(&mNPStream, 0, sizeof(mNPStream));
168 0 : mNPStream.notifyData = notifyData;
169 0 : }
170 :
171 0 : nsNPAPIPluginStreamListener::~nsNPAPIPluginStreamListener()
172 : {
173 : // remove this from the plugin instance's stream list
174 0 : nsTArray<nsNPAPIPluginStreamListener*> *streamListeners = mInst->StreamListeners();
175 0 : streamListeners->RemoveElement(this);
176 :
177 : // For those cases when NewStream is never called, we still may need
178 : // to fire a notification callback. Return network error as fallback
179 : // reason because for other cases, notify should have already been
180 : // called for other reasons elsewhere.
181 0 : CallURLNotify(NPRES_NETWORK_ERR);
182 :
183 : // lets get rid of the buffer
184 0 : if (mStreamBuffer) {
185 0 : PR_Free(mStreamBuffer);
186 0 : mStreamBuffer=nsnull;
187 : }
188 :
189 0 : if (mNotifyURL)
190 0 : PL_strfree(mNotifyURL);
191 :
192 0 : if (mResponseHeaderBuf)
193 0 : PL_strfree(mResponseHeaderBuf);
194 0 : }
195 :
196 : nsresult
197 0 : nsNPAPIPluginStreamListener::CleanUpStream(NPReason reason)
198 : {
199 0 : nsresult rv = NS_ERROR_FAILURE;
200 :
201 0 : if (mStreamCleanedUp)
202 0 : return NS_OK;
203 :
204 0 : mStreamCleanedUp = true;
205 :
206 0 : StopDataPump();
207 :
208 : // Release any outstanding redirect callback.
209 0 : if (mHTTPRedirectCallback) {
210 0 : mHTTPRedirectCallback->OnRedirectVerifyCallback(NS_ERROR_FAILURE);
211 0 : mHTTPRedirectCallback = nsnull;
212 : }
213 :
214 : // Seekable streams have an extra addref when they are created which must
215 : // be matched here.
216 0 : if (NP_SEEK == mStreamType)
217 0 : NS_RELEASE_THIS();
218 :
219 0 : if (!mInst || !mInst->CanFireNotifications())
220 0 : return rv;
221 :
222 0 : mStreamInfo = NULL;
223 :
224 0 : PluginDestructionGuard guard(mInst);
225 :
226 0 : nsNPAPIPlugin* plugin = mInst->GetPlugin();
227 0 : if (!plugin || !plugin->GetLibrary())
228 0 : return rv;
229 :
230 0 : NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
231 :
232 : NPP npp;
233 0 : mInst->GetNPP(&npp);
234 :
235 0 : if (mStreamStarted && pluginFunctions->destroystream) {
236 0 : NPPAutoPusher nppPusher(npp);
237 :
238 : NPError error;
239 0 : NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->destroystream)(npp, &mNPStream, reason), mInst);
240 :
241 0 : NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
242 : ("NPP DestroyStream called: this=%p, npp=%p, reason=%d, return=%d, url=%s\n",
243 : this, npp, reason, error, mNPStream.url));
244 :
245 0 : if (error == NPERR_NO_ERROR)
246 0 : rv = NS_OK;
247 : }
248 :
249 0 : mStreamStarted = false;
250 :
251 : // fire notification back to plugin, just like before
252 0 : CallURLNotify(reason);
253 :
254 0 : return rv;
255 : }
256 :
257 : void
258 0 : nsNPAPIPluginStreamListener::CallURLNotify(NPReason reason)
259 : {
260 0 : if (!mCallNotify || !mInst || !mInst->CanFireNotifications())
261 0 : return;
262 :
263 0 : PluginDestructionGuard guard(mInst);
264 :
265 0 : mCallNotify = false; // only do this ONCE and prevent recursion
266 :
267 0 : nsNPAPIPlugin* plugin = mInst->GetPlugin();
268 0 : if (!plugin || !plugin->GetLibrary())
269 : return;
270 :
271 0 : NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
272 :
273 0 : if (pluginFunctions->urlnotify) {
274 : NPP npp;
275 0 : mInst->GetNPP(&npp);
276 :
277 0 : NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlnotify)(npp, mNotifyURL, reason, mNPStream.notifyData), mInst);
278 :
279 0 : NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
280 : ("NPP URLNotify called: this=%p, npp=%p, notify=%p, reason=%d, url=%s\n",
281 : this, npp, mNPStream.notifyData, reason, mNotifyURL));
282 : }
283 : }
284 :
285 : NS_IMETHODIMP
286 0 : nsNPAPIPluginStreamListener::OnStartBinding(nsIPluginStreamInfo* pluginInfo)
287 : {
288 0 : if (!mInst || !mInst->CanFireNotifications())
289 0 : return NS_ERROR_FAILURE;
290 :
291 0 : PluginDestructionGuard guard(mInst);
292 :
293 0 : nsNPAPIPlugin* plugin = mInst->GetPlugin();
294 0 : if (!plugin || !plugin->GetLibrary())
295 0 : return NS_ERROR_FAILURE;
296 :
297 0 : NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
298 :
299 0 : if (!pluginFunctions->newstream)
300 0 : return NS_ERROR_FAILURE;
301 :
302 : NPP npp;
303 0 : mInst->GetNPP(&npp);
304 :
305 : bool seekable;
306 : char* contentType;
307 0 : PRUint16 streamType = NP_NORMAL;
308 : NPError error;
309 :
310 0 : mNPStream.ndata = (void*) this;
311 0 : pluginInfo->GetURL(&mNPStream.url);
312 :
313 0 : pluginInfo->GetLength((PRUint32*)&(mNPStream.end));
314 0 : pluginInfo->GetLastModified((PRUint32*)&(mNPStream.lastmodified));
315 0 : pluginInfo->IsSeekable(&seekable);
316 0 : pluginInfo->GetContentType(&contentType);
317 :
318 0 : if (!mResponseHeaders.IsEmpty()) {
319 0 : mResponseHeaderBuf = PL_strdup(mResponseHeaders.get());
320 0 : mNPStream.headers = mResponseHeaderBuf;
321 : }
322 :
323 0 : mStreamInfo = pluginInfo;
324 :
325 0 : NPPAutoPusher nppPusher(npp);
326 :
327 0 : NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->newstream)(npp, (char*)contentType, &mNPStream, seekable, &streamType), mInst);
328 :
329 0 : NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
330 : ("NPP NewStream called: this=%p, npp=%p, mime=%s, seek=%d, type=%d, return=%d, url=%s\n",
331 : this, npp, (char *)contentType, seekable, streamType, error, mNPStream.url));
332 :
333 0 : if (error != NPERR_NO_ERROR)
334 0 : return NS_ERROR_FAILURE;
335 :
336 0 : switch(streamType)
337 : {
338 : case NP_NORMAL:
339 0 : mStreamType = NP_NORMAL;
340 0 : break;
341 : case NP_ASFILEONLY:
342 0 : mStreamType = NP_ASFILEONLY;
343 0 : break;
344 : case NP_ASFILE:
345 0 : mStreamType = NP_ASFILE;
346 0 : break;
347 : case NP_SEEK:
348 0 : mStreamType = NP_SEEK;
349 : // Seekable streams should continue to exist even after OnStopRequest
350 : // is fired, so we AddRef ourself an extra time and Release when the
351 : // plugin calls NPN_DestroyStream (CleanUpStream). If the plugin never
352 : // calls NPN_DestroyStream the stream will be destroyed before the plugin
353 : // instance is destroyed.
354 0 : NS_ADDREF_THIS();
355 0 : break;
356 : default:
357 0 : return NS_ERROR_FAILURE;
358 : }
359 :
360 0 : mStreamStarted = true;
361 0 : return NS_OK;
362 : }
363 :
364 : void
365 0 : nsNPAPIPluginStreamListener::SuspendRequest()
366 : {
367 0 : NS_ASSERTION(!mIsSuspended,
368 : "Suspending a request that's already suspended!");
369 :
370 : nsCOMPtr<nsINPAPIPluginStreamInfo> pluginInfoNPAPI =
371 0 : do_QueryInterface(mStreamInfo);
372 :
373 0 : if (!pluginInfoNPAPI) {
374 : return;
375 : }
376 :
377 0 : nsresult rv = StartDataPump();
378 0 : if (NS_FAILED(rv))
379 : return;
380 :
381 0 : mIsSuspended = true;
382 :
383 0 : pluginInfoNPAPI->SuspendRequests();
384 : }
385 :
386 : void
387 0 : nsNPAPIPluginStreamListener::ResumeRequest()
388 : {
389 : nsCOMPtr<nsINPAPIPluginStreamInfo> pluginInfoNPAPI =
390 0 : do_QueryInterface(mStreamInfo);
391 :
392 0 : pluginInfoNPAPI->ResumeRequests();
393 :
394 0 : mIsSuspended = false;
395 0 : }
396 :
397 : nsresult
398 0 : nsNPAPIPluginStreamListener::StartDataPump()
399 : {
400 : nsresult rv;
401 0 : mDataPumpTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
402 0 : NS_ENSURE_SUCCESS(rv, rv);
403 :
404 : // Start pumping data to the plugin every 100ms until it obeys and
405 : // eats the data.
406 0 : return mDataPumpTimer->InitWithCallback(this, 100,
407 0 : nsITimer::TYPE_REPEATING_SLACK);
408 : }
409 :
410 : void
411 0 : nsNPAPIPluginStreamListener::StopDataPump()
412 : {
413 0 : if (mDataPumpTimer) {
414 0 : mDataPumpTimer->Cancel();
415 0 : mDataPumpTimer = nsnull;
416 : }
417 0 : }
418 :
419 : // Return true if a javascript: load that was started while the plugin
420 : // was being initialized is still in progress.
421 : bool
422 0 : nsNPAPIPluginStreamListener::PluginInitJSLoadInProgress()
423 : {
424 0 : if (!mInst)
425 0 : return false;
426 :
427 0 : nsTArray<nsNPAPIPluginStreamListener*> *streamListeners = mInst->StreamListeners();
428 0 : for (unsigned int i = 0; i < streamListeners->Length(); i++) {
429 0 : if (streamListeners->ElementAt(i)->mIsPluginInitJSStream) {
430 0 : return true;
431 : }
432 : }
433 :
434 0 : return false;
435 : }
436 :
437 : // This method is called when there's more data available off the
438 : // network, but it's also called from our data pump when we're feeding
439 : // the plugin data that we already got off the network, but the plugin
440 : // was unable to consume it at the point it arrived. In the case when
441 : // the plugin pump calls this method, the input argument will be null,
442 : // and the length will be the number of bytes available in our
443 : // internal buffer.
444 : NS_IMETHODIMP
445 0 : nsNPAPIPluginStreamListener::OnDataAvailable(nsIPluginStreamInfo* pluginInfo,
446 : nsIInputStream* input,
447 : PRUint32 length)
448 : {
449 0 : if (!length || !mInst || !mInst->CanFireNotifications())
450 0 : return NS_ERROR_FAILURE;
451 :
452 0 : PluginDestructionGuard guard(mInst);
453 :
454 : // Just in case the caller switches plugin info on us.
455 0 : mStreamInfo = pluginInfo;
456 :
457 0 : nsNPAPIPlugin* plugin = mInst->GetPlugin();
458 0 : if (!plugin || !plugin->GetLibrary())
459 0 : return NS_ERROR_FAILURE;
460 :
461 0 : NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
462 :
463 : // check out if plugin implements NPP_Write call
464 0 : if (!pluginFunctions->write)
465 0 : return NS_ERROR_FAILURE; // it'll cancel necko transaction
466 :
467 0 : if (!mStreamBuffer) {
468 : // To optimize the mem usage & performance we have to allocate
469 : // mStreamBuffer here in first ODA when length of data available
470 : // in input stream is known. mStreamBuffer will be freed in DTOR.
471 : // we also have to remember the size of that buff to make safe
472 : // consecutive Read() calls form input stream into our buff.
473 :
474 : PRUint32 contentLength;
475 0 : pluginInfo->GetLength(&contentLength);
476 :
477 0 : mStreamBufferSize = NS_MAX(length, contentLength);
478 :
479 : // Limit the size of the initial buffer to MAX_PLUGIN_NECKO_BUFFER
480 : // (16k). This buffer will grow if needed, as in the case where
481 : // we're getting data faster than the plugin can process it.
482 : mStreamBufferSize = NS_MIN(mStreamBufferSize,
483 0 : PRUint32(MAX_PLUGIN_NECKO_BUFFER));
484 :
485 0 : mStreamBuffer = (char*) PR_Malloc(mStreamBufferSize);
486 0 : if (!mStreamBuffer)
487 0 : return NS_ERROR_OUT_OF_MEMORY;
488 : }
489 :
490 : // prepare NPP_ calls params
491 : NPP npp;
492 0 : mInst->GetNPP(&npp);
493 :
494 : PRInt32 streamPosition;
495 0 : pluginInfo->GetStreamOffset(&streamPosition);
496 0 : PRInt32 streamOffset = streamPosition;
497 :
498 0 : if (input) {
499 0 : streamOffset += length;
500 :
501 : // Set new stream offset for the next ODA call regardless of how
502 : // following NPP_Write call will behave we pretend to consume all
503 : // data from the input stream. It's possible that current steam
504 : // position will be overwritten from NPP_RangeRequest call made
505 : // from NPP_Write, so we cannot call SetStreamOffset after
506 : // NPP_Write.
507 : //
508 : // Note: there is a special case when data flow should be
509 : // temporarily stopped if NPP_WriteReady returns 0 (bug #89270)
510 0 : pluginInfo->SetStreamOffset(streamOffset);
511 :
512 : // set new end in case the content is compressed
513 : // initial end is less than end of decompressed stream
514 : // and some plugins (e.g. acrobat) can fail.
515 0 : if ((PRInt32)mNPStream.end < streamOffset)
516 0 : mNPStream.end = streamOffset;
517 : }
518 :
519 0 : nsresult rv = NS_OK;
520 0 : while (NS_SUCCEEDED(rv) && length > 0) {
521 0 : if (input && length) {
522 0 : if (mStreamBufferSize < mStreamBufferByteCount + length && mIsSuspended) {
523 : // We're in the ::OnDataAvailable() call that we might get
524 : // after suspending a request, or we suspended the request
525 : // from within this ::OnDataAvailable() call while there's
526 : // still data in the input, and we don't have enough space to
527 : // store what we got off the network. Reallocate our internal
528 : // buffer.
529 0 : mStreamBufferSize = mStreamBufferByteCount + length;
530 0 : char *buf = (char*)PR_Realloc(mStreamBuffer, mStreamBufferSize);
531 0 : if (!buf)
532 0 : return NS_ERROR_OUT_OF_MEMORY;
533 :
534 0 : mStreamBuffer = buf;
535 : }
536 :
537 : PRUint32 bytesToRead =
538 0 : NS_MIN(length, mStreamBufferSize - mStreamBufferByteCount);
539 :
540 0 : PRUint32 amountRead = 0;
541 : rv = input->Read(mStreamBuffer + mStreamBufferByteCount, bytesToRead,
542 0 : &amountRead);
543 0 : NS_ENSURE_SUCCESS(rv, rv);
544 :
545 0 : if (amountRead == 0) {
546 : NS_NOTREACHED("input->Read() returns no data, it's almost impossible "
547 0 : "to get here");
548 :
549 0 : break;
550 : }
551 :
552 0 : mStreamBufferByteCount += amountRead;
553 0 : length -= amountRead;
554 : } else {
555 : // No input, nothing to read. Set length to 0 so that we don't
556 : // keep iterating through this outer loop any more.
557 :
558 0 : length = 0;
559 : }
560 :
561 : // Temporary pointer to the beginning of the data we're writing as
562 : // we loop and feed the plugin data.
563 0 : char *ptrStreamBuffer = mStreamBuffer;
564 :
565 : // it is possible plugin's NPP_Write() returns 0 byte consumed. We
566 : // use zeroBytesWriteCount to count situation like this and break
567 : // the loop
568 0 : PRInt32 zeroBytesWriteCount = 0;
569 :
570 : // mStreamBufferByteCount tells us how many bytes there are in the
571 : // buffer. WriteReady returns to us how many bytes the plugin is
572 : // ready to handle.
573 0 : while (mStreamBufferByteCount > 0) {
574 : PRInt32 numtowrite;
575 0 : if (pluginFunctions->writeready) {
576 0 : NPPAutoPusher nppPusher(npp);
577 :
578 0 : NS_TRY_SAFE_CALL_RETURN(numtowrite, (*pluginFunctions->writeready)(npp, &mNPStream), mInst);
579 0 : NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY,
580 : ("NPP WriteReady called: this=%p, npp=%p, "
581 : "return(towrite)=%d, url=%s\n",
582 : this, npp, numtowrite, mNPStream.url));
583 :
584 0 : if (!mStreamStarted) {
585 : // The plugin called NPN_DestroyStream() from within
586 : // NPP_WriteReady(), kill the stream.
587 :
588 0 : return NS_BINDING_ABORTED;
589 : }
590 :
591 : // if WriteReady returned 0, the plugin is not ready to handle
592 : // the data, suspend the stream (if it isn't already
593 : // suspended).
594 : //
595 : // Also suspend the stream if the stream we're loading is not
596 : // a javascript: URL load that was initiated during plugin
597 : // initialization and there currently is such a stream
598 : // loading. This is done to work around a Windows Media Player
599 : // plugin bug where it can't deal with being fed data for
600 : // other streams while it's waiting for data from the
601 : // javascript: URL loads it requests during
602 : // initialization. See bug 386493 for more details.
603 :
604 0 : if (numtowrite <= 0 ||
605 0 : (!mIsPluginInitJSStream && PluginInitJSLoadInProgress())) {
606 0 : if (!mIsSuspended) {
607 0 : SuspendRequest();
608 : }
609 :
610 : // Break out of the inner loop, but keep going through the
611 : // outer loop in case there's more data to read from the
612 : // input stream.
613 :
614 : break;
615 : }
616 :
617 0 : numtowrite = NS_MIN(numtowrite, mStreamBufferByteCount);
618 : } else {
619 : // if WriteReady is not supported by the plugin, just write
620 : // the whole buffer
621 0 : numtowrite = mStreamBufferByteCount;
622 : }
623 :
624 0 : NPPAutoPusher nppPusher(npp);
625 :
626 0 : PRInt32 writeCount = 0; // bytes consumed by plugin instance
627 0 : NS_TRY_SAFE_CALL_RETURN(writeCount, (*pluginFunctions->write)(npp, &mNPStream, streamPosition, numtowrite, ptrStreamBuffer), mInst);
628 :
629 0 : NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY,
630 : ("NPP Write called: this=%p, npp=%p, pos=%d, len=%d, "
631 : "buf=%s, return(written)=%d, url=%s\n",
632 : this, npp, streamPosition, numtowrite,
633 : ptrStreamBuffer, writeCount, mNPStream.url));
634 :
635 0 : if (!mStreamStarted) {
636 : // The plugin called NPN_DestroyStream() from within
637 : // NPP_Write(), kill the stream.
638 0 : return NS_BINDING_ABORTED;
639 : }
640 :
641 0 : if (writeCount > 0) {
642 0 : NS_ASSERTION(writeCount <= mStreamBufferByteCount,
643 : "Plugin read past the end of the available data!");
644 :
645 0 : writeCount = NS_MIN(writeCount, mStreamBufferByteCount);
646 0 : mStreamBufferByteCount -= writeCount;
647 :
648 0 : streamPosition += writeCount;
649 :
650 0 : zeroBytesWriteCount = 0;
651 :
652 0 : if (mStreamBufferByteCount > 0) {
653 : // This alignment code is most likely bogus, but we'll leave
654 : // it in for now in case it matters for some plugins on some
655 : // architectures. Who knows...
656 0 : if (writeCount % sizeof(PRWord)) {
657 : // memmove will take care about alignment
658 : memmove(mStreamBuffer, ptrStreamBuffer + writeCount,
659 0 : mStreamBufferByteCount);
660 0 : ptrStreamBuffer = mStreamBuffer;
661 : } else {
662 : // if aligned we can use ptrStreamBuffer += to eliminate
663 : // memmove()
664 0 : ptrStreamBuffer += writeCount;
665 : }
666 : }
667 0 : } else if (writeCount == 0) {
668 : // if NPP_Write() returns writeCount == 0 lets say 3 times in
669 : // a row, suspend the request and continue feeding the plugin
670 : // the data we got so far. Once that data is consumed, we'll
671 : // resume the request.
672 0 : if (mIsSuspended || ++zeroBytesWriteCount == 3) {
673 0 : if (!mIsSuspended) {
674 0 : SuspendRequest();
675 : }
676 :
677 : // Break out of the for loop, but keep going through the
678 : // while loop in case there's more data to read from the
679 : // input stream.
680 :
681 : break;
682 : }
683 : } else {
684 : // Something's really wrong, kill the stream.
685 0 : rv = NS_ERROR_FAILURE;
686 :
687 : break;
688 : }
689 : } // end of inner while loop
690 :
691 0 : if (mStreamBufferByteCount && mStreamBuffer != ptrStreamBuffer) {
692 0 : memmove(mStreamBuffer, ptrStreamBuffer, mStreamBufferByteCount);
693 : }
694 : }
695 :
696 0 : if (streamPosition != streamOffset) {
697 : // The plugin didn't consume all available data, or consumed some
698 : // of our cached data while we're pumping cached data. Adjust the
699 : // plugin info's stream offset to match reality, except if the
700 : // plugin info's stream offset was set by a re-entering
701 : // NPN_RequestRead() call.
702 :
703 : PRInt32 postWriteStreamPosition;
704 0 : pluginInfo->GetStreamOffset(&postWriteStreamPosition);
705 :
706 0 : if (postWriteStreamPosition == streamOffset) {
707 0 : pluginInfo->SetStreamOffset(streamPosition);
708 : }
709 : }
710 :
711 0 : return rv;
712 : }
713 :
714 : NS_IMETHODIMP
715 0 : nsNPAPIPluginStreamListener::OnFileAvailable(nsIPluginStreamInfo* pluginInfo,
716 : const char* fileName)
717 : {
718 0 : if (!mInst || !mInst->CanFireNotifications())
719 0 : return NS_ERROR_FAILURE;
720 :
721 0 : PluginDestructionGuard guard(mInst);
722 :
723 0 : nsNPAPIPlugin* plugin = mInst->GetPlugin();
724 0 : if (!plugin || !plugin->GetLibrary())
725 0 : return NS_ERROR_FAILURE;
726 :
727 0 : NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
728 :
729 0 : if (!pluginFunctions->asfile)
730 0 : return NS_ERROR_FAILURE;
731 :
732 : NPP npp;
733 0 : mInst->GetNPP(&npp);
734 :
735 0 : NS_TRY_SAFE_CALL_VOID((*pluginFunctions->asfile)(npp, &mNPStream, fileName), mInst);
736 :
737 0 : NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
738 : ("NPP StreamAsFile called: this=%p, npp=%p, url=%s, file=%s\n",
739 : this, npp, mNPStream.url, fileName));
740 :
741 0 : return NS_OK;
742 : }
743 :
744 : NS_IMETHODIMP
745 0 : nsNPAPIPluginStreamListener::OnStopBinding(nsIPluginStreamInfo* pluginInfo,
746 : nsresult status)
747 : {
748 0 : StopDataPump();
749 :
750 0 : if (NS_FAILED(status)) {
751 : // The stream was destroyed, or died for some reason. Make sure we
752 : // cancel the underlying request.
753 : nsCOMPtr<nsINPAPIPluginStreamInfo> pluginInfoNPAPI =
754 0 : do_QueryInterface(mStreamInfo);
755 :
756 0 : if (pluginInfoNPAPI) {
757 0 : pluginInfoNPAPI->CancelRequests(status);
758 : }
759 : }
760 :
761 0 : if (!mInst || !mInst->CanFireNotifications())
762 0 : return NS_ERROR_FAILURE;
763 :
764 : // check if the stream is of seekable type and later its destruction
765 : // see bug 91140
766 0 : nsresult rv = NS_OK;
767 0 : NPReason reason = NS_FAILED(status) ? NPRES_NETWORK_ERR : NPRES_DONE;
768 0 : if (mRedirectDenied) {
769 0 : reason = NPRES_USER_BREAK;
770 : }
771 0 : if (mStreamType != NP_SEEK ||
772 : (NP_SEEK == mStreamType && NS_BINDING_ABORTED == status)) {
773 0 : rv = CleanUpStream(reason);
774 : }
775 :
776 0 : return rv;
777 : }
778 :
779 : NS_IMETHODIMP
780 0 : nsNPAPIPluginStreamListener::GetStreamType(PRInt32 *result)
781 : {
782 0 : *result = mStreamType;
783 0 : return NS_OK;
784 : }
785 :
786 : NS_IMETHODIMP
787 0 : nsNPAPIPluginStreamListener::Notify(nsITimer *aTimer)
788 : {
789 0 : NS_ASSERTION(aTimer == mDataPumpTimer, "Uh, wrong timer?");
790 :
791 0 : PRInt32 oldStreamBufferByteCount = mStreamBufferByteCount;
792 :
793 0 : nsresult rv = OnDataAvailable(mStreamInfo, nsnull, mStreamBufferByteCount);
794 :
795 0 : if (NS_FAILED(rv)) {
796 : // We ran into an error, no need to keep firing this timer then.
797 0 : aTimer->Cancel();
798 0 : return NS_OK;
799 : }
800 :
801 0 : if (mStreamBufferByteCount != oldStreamBufferByteCount &&
802 : ((mStreamStarted && mStreamBufferByteCount < 1024) ||
803 : mStreamBufferByteCount == 0)) {
804 : // The plugin read some data and we've got less than 1024 bytes in
805 : // our buffer (or its empty and the stream is already
806 : // done). Resume the request so that we get more data off the
807 : // network.
808 0 : ResumeRequest();
809 : // Necko will pump data now that we've resumed the request.
810 0 : StopDataPump();
811 : }
812 :
813 0 : return NS_OK;
814 : }
815 :
816 : NS_IMETHODIMP
817 0 : nsNPAPIPluginStreamListener::StatusLine(const char* line)
818 : {
819 0 : mResponseHeaders.Append(line);
820 0 : mResponseHeaders.Append('\n');
821 0 : return NS_OK;
822 : }
823 :
824 : NS_IMETHODIMP
825 0 : nsNPAPIPluginStreamListener::NewResponseHeader(const char* headerName,
826 : const char* headerValue)
827 : {
828 0 : mResponseHeaders.Append(headerName);
829 0 : mResponseHeaders.Append(": ");
830 0 : mResponseHeaders.Append(headerValue);
831 0 : mResponseHeaders.Append('\n');
832 0 : return NS_OK;
833 : }
834 :
835 : bool
836 0 : nsNPAPIPluginStreamListener::HandleRedirectNotification(nsIChannel *oldChannel, nsIChannel *newChannel,
837 : nsIAsyncVerifyRedirectCallback* callback)
838 : {
839 0 : nsCOMPtr<nsIHttpChannel> oldHttpChannel = do_QueryInterface(oldChannel);
840 0 : nsCOMPtr<nsIHttpChannel> newHttpChannel = do_QueryInterface(newChannel);
841 0 : if (!oldHttpChannel || !newHttpChannel) {
842 0 : return false;
843 : }
844 :
845 0 : if (!mInst || !mInst->CanFireNotifications()) {
846 0 : return false;
847 : }
848 :
849 0 : nsNPAPIPlugin* plugin = mInst->GetPlugin();
850 0 : if (!plugin || !plugin->GetLibrary()) {
851 0 : return false;
852 : }
853 :
854 0 : NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
855 0 : if (!pluginFunctions->urlredirectnotify) {
856 0 : return false;
857 : }
858 :
859 : // A non-null closure is required for redirect handling support.
860 0 : if (mNPStream.notifyData) {
861 : PRUint32 status;
862 0 : if (NS_SUCCEEDED(oldHttpChannel->GetResponseStatus(&status))) {
863 0 : nsCOMPtr<nsIURI> uri;
864 0 : if (NS_SUCCEEDED(newHttpChannel->GetURI(getter_AddRefs(uri))) && uri) {
865 0 : nsCAutoString spec;
866 0 : if (NS_SUCCEEDED(uri->GetAsciiSpec(spec))) {
867 : // At this point the plugin will be responsible for making the callback
868 : // so save the callback object.
869 0 : mHTTPRedirectCallback = callback;
870 :
871 : NPP npp;
872 0 : mInst->GetNPP(&npp);
873 : #if defined(XP_WIN) || defined(XP_OS2)
874 : NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlredirectnotify)(npp, spec.get(), static_cast<int32_t>(status), mNPStream.notifyData), mInst);
875 : #else
876 0 : (*pluginFunctions->urlredirectnotify)(npp, spec.get(), static_cast<int32_t>(status), mNPStream.notifyData);
877 : #endif
878 0 : return true;
879 : }
880 : }
881 : }
882 : }
883 :
884 0 : callback->OnRedirectVerifyCallback(NS_ERROR_FAILURE);
885 0 : return true;
886 : }
887 :
888 : void
889 0 : nsNPAPIPluginStreamListener::URLRedirectResponse(NPBool allow)
890 : {
891 0 : if (mHTTPRedirectCallback) {
892 0 : mHTTPRedirectCallback->OnRedirectVerifyCallback(allow ? NS_OK : NS_ERROR_FAILURE);
893 0 : mRedirectDenied = allow ? false : true;
894 0 : mHTTPRedirectCallback = nsnull;
895 : }
896 0 : }
|