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 : * Sean Echevarria <sean@beatnik.com>
24 : * HÃ¥kan Waara <hwaara@chello.se>
25 : * Josh Aas <josh@mozilla.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "nsPluginStreamListenerPeer.h"
42 : #include "nsIStreamConverterService.h"
43 : #include "nsIHttpChannel.h"
44 : #include "nsIHttpChannelInternal.h"
45 : #include "nsIFileChannel.h"
46 : #include "nsICachingChannel.h"
47 : #include "nsMimeTypes.h"
48 : #include "nsISupportsPrimitives.h"
49 : #include "nsNetCID.h"
50 : #include "nsPluginLogging.h"
51 : #include "nsIURI.h"
52 : #include "nsIURL.h"
53 : #include "nsPluginHost.h"
54 : #include "nsIByteRangeRequest.h"
55 : #include "nsIMultiPartChannel.h"
56 : #include "nsIInputStreamTee.h"
57 : #include "nsPrintfCString.h"
58 : #include "nsIScriptGlobalObject.h"
59 : #include "nsIDocument.h"
60 : #include "nsIWebNavigation.h"
61 : #include "nsContentUtils.h"
62 : #include "nsNetUtil.h"
63 : #include "nsPluginNativeWindow.h"
64 : #include "sampler.h"
65 :
66 : #define MAGIC_REQUEST_CONTEXT 0x01020304
67 :
68 : // nsPluginByteRangeStreamListener
69 :
70 : class nsPluginByteRangeStreamListener
71 : : public nsIStreamListener
72 : , public nsIInterfaceRequestor
73 : {
74 : public:
75 : nsPluginByteRangeStreamListener(nsIWeakReference* aWeakPtr);
76 : virtual ~nsPluginByteRangeStreamListener();
77 :
78 : NS_DECL_ISUPPORTS
79 : NS_DECL_NSIREQUESTOBSERVER
80 : NS_DECL_NSISTREAMLISTENER
81 : NS_DECL_NSIINTERFACEREQUESTOR
82 :
83 : private:
84 : nsCOMPtr<nsIStreamListener> mStreamConverter;
85 : nsWeakPtr mWeakPtrPluginStreamListenerPeer;
86 : bool mRemoveMagicNumber;
87 : };
88 :
89 0 : NS_IMPL_ISUPPORTS3(nsPluginByteRangeStreamListener,
90 : nsIRequestObserver,
91 : nsIStreamListener,
92 : nsIInterfaceRequestor)
93 :
94 0 : nsPluginByteRangeStreamListener::nsPluginByteRangeStreamListener(nsIWeakReference* aWeakPtr)
95 : {
96 0 : mWeakPtrPluginStreamListenerPeer = aWeakPtr;
97 0 : mRemoveMagicNumber = false;
98 0 : }
99 :
100 0 : nsPluginByteRangeStreamListener::~nsPluginByteRangeStreamListener()
101 : {
102 0 : mStreamConverter = 0;
103 0 : mWeakPtrPluginStreamListenerPeer = 0;
104 0 : }
105 :
106 : /**
107 : * Unwrap any byte-range requests so that we can check whether the base channel
108 : * is being tracked properly.
109 : */
110 : static nsCOMPtr<nsIRequest>
111 0 : GetBaseRequest(nsIRequest* r)
112 : {
113 0 : nsCOMPtr<nsIMultiPartChannel> mp = do_QueryInterface(r);
114 0 : if (!mp)
115 0 : return r;
116 :
117 0 : nsCOMPtr<nsIChannel> base;
118 0 : mp->GetBaseChannel(getter_AddRefs(base));
119 0 : return already_AddRefed<nsIRequest>(base.forget());
120 : }
121 :
122 : NS_IMETHODIMP
123 0 : nsPluginByteRangeStreamListener::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
124 : {
125 : nsresult rv;
126 :
127 0 : nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
128 0 : if (!finalStreamListener)
129 0 : return NS_ERROR_FAILURE;
130 :
131 : nsPluginStreamListenerPeer *pslp =
132 0 : static_cast<nsPluginStreamListenerPeer*>(finalStreamListener.get());
133 :
134 0 : NS_ASSERTION(pslp->mRequests.IndexOfObject(GetBaseRequest(request)) != -1,
135 : "Untracked byte-range request?");
136 :
137 0 : nsCOMPtr<nsIStreamConverterService> serv = do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
138 0 : if (NS_SUCCEEDED(rv)) {
139 0 : rv = serv->AsyncConvertData(MULTIPART_BYTERANGES,
140 : "*/*",
141 : finalStreamListener,
142 : nsnull,
143 0 : getter_AddRefs(mStreamConverter));
144 0 : if (NS_SUCCEEDED(rv)) {
145 0 : rv = mStreamConverter->OnStartRequest(request, ctxt);
146 0 : if (NS_SUCCEEDED(rv))
147 0 : return rv;
148 : }
149 : }
150 0 : mStreamConverter = 0;
151 :
152 0 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
153 0 : if (!httpChannel) {
154 0 : return NS_ERROR_FAILURE;
155 : }
156 :
157 0 : PRUint32 responseCode = 0;
158 0 : rv = httpChannel->GetResponseStatus(&responseCode);
159 0 : if (NS_FAILED(rv)) {
160 0 : return NS_ERROR_FAILURE;
161 : }
162 :
163 0 : if (responseCode != 200) {
164 0 : bool bWantsAllNetworkStreams = false;
165 : rv = pslp->GetPluginInstance()->GetValueFromPlugin(NPPVpluginWantsAllNetworkStreams,
166 0 : &bWantsAllNetworkStreams);
167 : // If the call returned an error code make sure we still use our default value.
168 0 : if (NS_FAILED(rv)) {
169 0 : bWantsAllNetworkStreams = false;
170 : }
171 :
172 0 : if (!bWantsAllNetworkStreams){
173 0 : return NS_ERROR_FAILURE;
174 : }
175 : }
176 :
177 : // if server cannot continue with byte range (206 status) and sending us whole object (200 status)
178 : // reset this seekable stream & try serve it to plugin instance as a file
179 0 : mStreamConverter = finalStreamListener;
180 0 : mRemoveMagicNumber = true;
181 :
182 0 : rv = pslp->ServeStreamAsFile(request, ctxt);
183 0 : return rv;
184 : }
185 :
186 : NS_IMETHODIMP
187 0 : nsPluginByteRangeStreamListener::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
188 : nsresult status)
189 : {
190 0 : if (!mStreamConverter)
191 0 : return NS_ERROR_FAILURE;
192 :
193 0 : nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
194 0 : if (!finalStreamListener)
195 0 : return NS_ERROR_FAILURE;
196 :
197 : nsPluginStreamListenerPeer *pslp =
198 0 : static_cast<nsPluginStreamListenerPeer*>(finalStreamListener.get());
199 0 : bool found = pslp->mRequests.RemoveObject(request);
200 0 : if (!found) {
201 0 : NS_ERROR("OnStopRequest received for untracked byte-range request!");
202 : }
203 :
204 0 : if (mRemoveMagicNumber) {
205 : // remove magic number from container
206 0 : nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(ctxt);
207 0 : if (container) {
208 0 : PRUint32 magicNumber = 0;
209 0 : container->GetData(&magicNumber);
210 0 : if (magicNumber == MAGIC_REQUEST_CONTEXT) {
211 : // to allow properly finish nsPluginStreamListenerPeer->OnStopRequest()
212 : // set it to something that is not the magic number.
213 0 : container->SetData(0);
214 : }
215 : } else {
216 0 : NS_WARNING("Bad state of nsPluginByteRangeStreamListener");
217 : }
218 : }
219 :
220 0 : return mStreamConverter->OnStopRequest(request, ctxt, status);
221 : }
222 :
223 : // CachedFileHolder
224 :
225 0 : CachedFileHolder::CachedFileHolder(nsIFile* cacheFile)
226 0 : : mFile(cacheFile)
227 : {
228 0 : NS_ASSERTION(mFile, "Empty CachedFileHolder");
229 0 : }
230 :
231 0 : CachedFileHolder::~CachedFileHolder()
232 : {
233 0 : mFile->Remove(false);
234 0 : }
235 :
236 : void
237 0 : CachedFileHolder::AddRef()
238 : {
239 0 : ++mRefCnt;
240 0 : NS_LOG_ADDREF(this, mRefCnt, "CachedFileHolder", sizeof(*this));
241 0 : }
242 :
243 : void
244 0 : CachedFileHolder::Release()
245 : {
246 0 : --mRefCnt;
247 0 : NS_LOG_RELEASE(this, mRefCnt, "CachedFileHolder");
248 0 : if (0 == mRefCnt)
249 0 : delete this;
250 0 : }
251 :
252 :
253 : NS_IMETHODIMP
254 0 : nsPluginByteRangeStreamListener::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
255 : nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
256 : {
257 0 : if (!mStreamConverter)
258 0 : return NS_ERROR_FAILURE;
259 :
260 0 : nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
261 0 : if (!finalStreamListener)
262 0 : return NS_ERROR_FAILURE;
263 :
264 0 : return mStreamConverter->OnDataAvailable(request, ctxt, inStr, sourceOffset, count);
265 : }
266 :
267 : NS_IMETHODIMP
268 0 : nsPluginByteRangeStreamListener::GetInterface(const nsIID& aIID, void** result)
269 : {
270 : // Forward interface requests to our parent
271 0 : nsCOMPtr<nsIInterfaceRequestor> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
272 0 : if (!finalStreamListener)
273 0 : return NS_ERROR_FAILURE;
274 :
275 0 : return finalStreamListener->GetInterface(aIID, result);
276 : }
277 :
278 :
279 : // nsPRUintKey
280 :
281 0 : class nsPRUintKey : public nsHashKey {
282 : protected:
283 : PRUint32 mKey;
284 : public:
285 0 : nsPRUintKey(PRUint32 key) : mKey(key) {}
286 :
287 0 : PRUint32 HashCode() const {
288 0 : return mKey;
289 : }
290 :
291 0 : bool Equals(const nsHashKey *aKey) const {
292 0 : return mKey == ((const nsPRUintKey*)aKey)->mKey;
293 : }
294 0 : nsHashKey *Clone() const {
295 0 : return new nsPRUintKey(mKey);
296 : }
297 : PRUint32 GetValue() { return mKey; }
298 : };
299 :
300 : // nsPluginStreamListenerPeer
301 :
302 0 : NS_IMPL_ISUPPORTS8(nsPluginStreamListenerPeer,
303 : nsIStreamListener,
304 : nsIRequestObserver,
305 : nsIHttpHeaderVisitor,
306 : nsISupportsWeakReference,
307 : nsIPluginStreamInfo,
308 : nsINPAPIPluginStreamInfo,
309 : nsIInterfaceRequestor,
310 : nsIChannelEventSink)
311 :
312 0 : nsPluginStreamListenerPeer::nsPluginStreamListenerPeer()
313 : {
314 0 : mStreamType = NP_NORMAL;
315 0 : mStartBinding = false;
316 0 : mAbort = false;
317 0 : mRequestFailed = false;
318 :
319 0 : mPendingRequests = 0;
320 0 : mHaveFiredOnStartRequest = false;
321 0 : mDataForwardToRequest = nsnull;
322 :
323 0 : mSeekable = false;
324 0 : mModified = 0;
325 0 : mStreamOffset = 0;
326 0 : mStreamComplete = 0;
327 0 : }
328 :
329 0 : nsPluginStreamListenerPeer::~nsPluginStreamListenerPeer()
330 : {
331 : #ifdef PLUGIN_LOGGING
332 0 : PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
333 : ("nsPluginStreamListenerPeer::dtor this=%p, url=%s\n",this, mURLSpec.get()));
334 : #endif
335 :
336 0 : if (mPStreamListener) {
337 0 : mPStreamListener->SetStreamListenerPeer(this);
338 : }
339 :
340 : // close FD of mFileCacheOutputStream if it's still open
341 : // or we won't be able to remove the cache file
342 0 : if (mFileCacheOutputStream)
343 0 : mFileCacheOutputStream = nsnull;
344 :
345 0 : delete mDataForwardToRequest;
346 :
347 0 : if (mPluginInstance)
348 0 : mPluginInstance->FileCachedStreamListeners()->RemoveElement(this);
349 0 : }
350 :
351 : // Called as a result of GetURL and PostURL
352 0 : nsresult nsPluginStreamListenerPeer::Initialize(nsIURI *aURL,
353 : nsNPAPIPluginInstance *aInstance,
354 : nsIPluginStreamListener* aListener)
355 : {
356 : #ifdef PLUGIN_LOGGING
357 0 : nsCAutoString urlSpec;
358 0 : if (aURL != nsnull) aURL->GetAsciiSpec(urlSpec);
359 :
360 0 : PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
361 : ("nsPluginStreamListenerPeer::Initialize instance=%p, url=%s\n", aInstance, urlSpec.get()));
362 :
363 0 : PR_LogFlush();
364 : #endif
365 :
366 0 : mURL = aURL;
367 :
368 0 : mPluginInstance = aInstance;
369 :
370 0 : mPStreamListener = static_cast<nsNPAPIPluginStreamListener*>(aListener);
371 0 : mPStreamListener->SetStreamListenerPeer(this);
372 :
373 0 : mPendingRequests = 1;
374 :
375 0 : mDataForwardToRequest = new nsHashtable(16, false);
376 0 : if (!mDataForwardToRequest)
377 0 : return NS_ERROR_FAILURE;
378 :
379 0 : return NS_OK;
380 : }
381 :
382 : /* Called by NewEmbeddedPluginStream() - if this is called, we weren't
383 : * able to load the plugin, so we need to load it later once we figure
384 : * out the mimetype. In order to load it later, we need the plugin
385 : * instance owner.
386 : */
387 0 : nsresult nsPluginStreamListenerPeer::InitializeEmbedded(nsIURI *aURL,
388 : nsNPAPIPluginInstance* aInstance,
389 : nsObjectLoadingContent *aContent)
390 : {
391 : #ifdef PLUGIN_LOGGING
392 0 : nsCAutoString urlSpec;
393 0 : aURL->GetSpec(urlSpec);
394 :
395 0 : PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
396 : ("nsPluginStreamListenerPeer::InitializeEmbedded url=%s\n", urlSpec.get()));
397 :
398 0 : PR_LogFlush();
399 : #endif
400 :
401 0 : mURL = aURL;
402 :
403 0 : if (aInstance) {
404 0 : NS_ASSERTION(mPluginInstance == nsnull, "nsPluginStreamListenerPeer::InitializeEmbedded mPluginInstance != nsnull");
405 0 : mPluginInstance = aInstance;
406 : } else {
407 0 : mContent = aContent;
408 : }
409 :
410 0 : mPendingRequests = 1;
411 :
412 0 : mDataForwardToRequest = new nsHashtable(16, false);
413 0 : if (!mDataForwardToRequest)
414 0 : return NS_ERROR_FAILURE;
415 :
416 0 : return NS_OK;
417 : }
418 :
419 : // Called by NewFullPagePluginStream()
420 0 : nsresult nsPluginStreamListenerPeer::InitializeFullPage(nsIURI* aURL, nsNPAPIPluginInstance *aInstance)
421 : {
422 0 : PLUGIN_LOG(PLUGIN_LOG_NORMAL,
423 : ("nsPluginStreamListenerPeer::InitializeFullPage instance=%p\n",aInstance));
424 :
425 0 : NS_ASSERTION(mPluginInstance == nsnull, "nsPluginStreamListenerPeer::InitializeFullPage mPluginInstance != nsnull");
426 0 : mPluginInstance = aInstance;
427 :
428 0 : mURL = aURL;
429 :
430 0 : mDataForwardToRequest = new nsHashtable(16, false);
431 0 : if (!mDataForwardToRequest)
432 0 : return NS_ERROR_FAILURE;
433 :
434 0 : mPendingRequests = 1;
435 :
436 0 : return NS_OK;
437 : }
438 :
439 : // SetupPluginCacheFile is called if we have to save the stream to disk.
440 : // the most likely cause for this is either there is no disk cache available
441 : // or the stream is coming from a https server.
442 : //
443 : // These files will be deleted when the host is destroyed.
444 : //
445 : // TODO? What if we fill up the the dest dir?
446 : nsresult
447 0 : nsPluginStreamListenerPeer::SetupPluginCacheFile(nsIChannel* channel)
448 : {
449 0 : nsresult rv = NS_OK;
450 :
451 0 : bool useExistingCacheFile = false;
452 0 : nsRefPtr<nsPluginHost> pluginHost = dont_AddRef(nsPluginHost::GetInst());
453 :
454 : // Look for an existing cache file for the URI.
455 0 : nsTArray< nsRefPtr<nsNPAPIPluginInstance> > *instances = pluginHost->InstanceArray();
456 0 : for (PRUint32 i = 0; i < instances->Length(); i++) {
457 : // most recent streams are at the end of list
458 0 : nsTArray<nsPluginStreamListenerPeer*> *streamListeners = instances->ElementAt(i)->FileCachedStreamListeners();
459 0 : for (PRInt32 i = streamListeners->Length() - 1; i >= 0; --i) {
460 0 : nsPluginStreamListenerPeer *lp = streamListeners->ElementAt(i);
461 0 : if (lp && lp->mLocalCachedFileHolder) {
462 0 : useExistingCacheFile = lp->UseExistingPluginCacheFile(this);
463 0 : if (useExistingCacheFile) {
464 0 : mLocalCachedFileHolder = lp->mLocalCachedFileHolder;
465 0 : break;
466 : }
467 : }
468 0 : if (useExistingCacheFile)
469 0 : break;
470 : }
471 : }
472 :
473 : // Create a new cache file if one could not be found.
474 0 : if (!useExistingCacheFile) {
475 0 : nsCOMPtr<nsIFile> pluginTmp;
476 0 : rv = nsPluginHost::GetPluginTempDir(getter_AddRefs(pluginTmp));
477 0 : if (NS_FAILED(rv)) {
478 0 : return rv;
479 : }
480 :
481 : // Get the filename from the channel
482 0 : nsCOMPtr<nsIURI> uri;
483 0 : rv = channel->GetURI(getter_AddRefs(uri));
484 0 : if (NS_FAILED(rv)) return rv;
485 :
486 0 : nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
487 0 : if (!url)
488 0 : return NS_ERROR_FAILURE;
489 :
490 0 : nsCAutoString filename;
491 0 : url->GetFileName(filename);
492 0 : if (NS_FAILED(rv))
493 0 : return rv;
494 :
495 : // Create a file to save our stream into. Should we scramble the name?
496 0 : filename.Insert(NS_LITERAL_CSTRING("plugin-"), 0);
497 0 : rv = pluginTmp->AppendNative(filename);
498 0 : if (NS_FAILED(rv))
499 0 : return rv;
500 :
501 : // Yes, make it unique.
502 0 : rv = pluginTmp->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
503 0 : if (NS_FAILED(rv))
504 0 : return rv;
505 :
506 : // create a file output stream to write to...
507 0 : nsCOMPtr<nsIOutputStream> outstream;
508 0 : rv = NS_NewLocalFileOutputStream(getter_AddRefs(mFileCacheOutputStream), pluginTmp, -1, 00600);
509 0 : if (NS_FAILED(rv))
510 0 : return rv;
511 :
512 : // save the file.
513 0 : mLocalCachedFileHolder = new CachedFileHolder(pluginTmp);
514 : }
515 :
516 : // add this listenerPeer to list of stream peers for this instance
517 0 : mPluginInstance->FileCachedStreamListeners()->AppendElement(this);
518 :
519 0 : return rv;
520 : }
521 :
522 : NS_IMETHODIMP
523 0 : nsPluginStreamListenerPeer::OnStartRequest(nsIRequest *request,
524 : nsISupports* aContext)
525 : {
526 0 : nsresult rv = NS_OK;
527 0 : SAMPLE_LABEL("nsPluginStreamListenerPeer", "OnStartRequest");
528 :
529 0 : if (mRequests.IndexOfObject(GetBaseRequest(request)) == -1) {
530 0 : NS_ASSERTION(mRequests.Count() == 0,
531 : "Only our initial stream should be unknown!");
532 0 : TrackRequest(request);
533 : }
534 :
535 0 : if (mHaveFiredOnStartRequest) {
536 0 : return NS_OK;
537 : }
538 :
539 0 : mHaveFiredOnStartRequest = true;
540 :
541 0 : nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
542 0 : NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
543 :
544 : // deal with 404 (Not Found) HTTP response,
545 : // just return, this causes the request to be ignored.
546 0 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
547 0 : if (httpChannel) {
548 0 : PRUint32 responseCode = 0;
549 0 : rv = httpChannel->GetResponseStatus(&responseCode);
550 0 : if (NS_FAILED(rv)) {
551 : // NPP_Notify() will be called from OnStopRequest
552 : // in nsNPAPIPluginStreamListener::CleanUpStream
553 : // return error will cancel this request
554 : // ...and we also need to tell the plugin that
555 0 : mRequestFailed = true;
556 0 : return NS_ERROR_FAILURE;
557 : }
558 :
559 0 : if (responseCode > 206) { // not normal
560 0 : bool bWantsAllNetworkStreams = false;
561 :
562 : // We don't always have an instance here already, but if we do, check
563 : // to see if it wants all streams.
564 0 : if (mPluginInstance) {
565 : rv = mPluginInstance->GetValueFromPlugin(NPPVpluginWantsAllNetworkStreams,
566 0 : &bWantsAllNetworkStreams);
567 : // If the call returned an error code make sure we still use our default value.
568 0 : if (NS_FAILED(rv)) {
569 0 : bWantsAllNetworkStreams = false;
570 : }
571 : }
572 :
573 0 : if (!bWantsAllNetworkStreams) {
574 0 : mRequestFailed = true;
575 0 : return NS_ERROR_FAILURE;
576 : }
577 : }
578 : }
579 :
580 : // Get the notification callbacks from the channel and save it as
581 : // week ref we'll use it in nsPluginStreamInfo::RequestRead() when
582 : // we'll create channel for byte range request.
583 0 : nsCOMPtr<nsIInterfaceRequestor> callbacks;
584 0 : channel->GetNotificationCallbacks(getter_AddRefs(callbacks));
585 0 : if (callbacks)
586 0 : mWeakPtrChannelCallbacks = do_GetWeakReference(callbacks);
587 :
588 0 : nsCOMPtr<nsILoadGroup> loadGroup;
589 0 : channel->GetLoadGroup(getter_AddRefs(loadGroup));
590 0 : if (loadGroup)
591 0 : mWeakPtrChannelLoadGroup = do_GetWeakReference(loadGroup);
592 :
593 : PRInt32 length;
594 0 : rv = channel->GetContentLength(&length);
595 :
596 : // it's possible for the server to not send a Content-Length.
597 : // we should still work in this case.
598 0 : if (NS_FAILED(rv) || length == -1) {
599 : // check out if this is file channel
600 0 : nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel);
601 0 : if (fileChannel) {
602 : // file does not exist
603 0 : mRequestFailed = true;
604 0 : return NS_ERROR_FAILURE;
605 : }
606 0 : mLength = 0;
607 : }
608 : else {
609 0 : mLength = length;
610 : }
611 :
612 0 : nsCAutoString aContentType; // XXX but we already got the type above!
613 0 : rv = channel->GetContentType(aContentType);
614 0 : if (NS_FAILED(rv))
615 0 : return rv;
616 :
617 0 : nsCOMPtr<nsIURI> aURL;
618 0 : rv = channel->GetURI(getter_AddRefs(aURL));
619 0 : if (NS_FAILED(rv))
620 0 : return rv;
621 :
622 0 : aURL->GetSpec(mURLSpec);
623 :
624 0 : if (!aContentType.IsEmpty())
625 0 : mContentType = aContentType;
626 :
627 : #ifdef PLUGIN_LOGGING
628 0 : PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NOISY,
629 : ("nsPluginStreamListenerPeer::OnStartRequest this=%p request=%p mime=%s, url=%s\n",
630 : this, request, aContentType.get(), mURLSpec.get()));
631 :
632 0 : PR_LogFlush();
633 : #endif
634 :
635 : // If we don't have an instance yet it means we weren't able to load
636 : // a plugin previously because we didn't have the mimetype. Try again
637 : // if we have a mime type now.
638 0 : if (!mPluginInstance && mContent && !aContentType.IsEmpty()) {
639 0 : nsObjectLoadingContent *olc = static_cast<nsObjectLoadingContent*>(mContent.get());
640 0 : rv = olc->InstantiatePluginInstance(aContentType.get(), aURL.get());
641 0 : if (NS_SUCCEEDED(rv)) {
642 0 : rv = olc->GetPluginInstance(getter_AddRefs(mPluginInstance));
643 0 : if (NS_FAILED(rv)) {
644 0 : return rv;
645 : }
646 : }
647 : }
648 :
649 : // Set up the stream listener...
650 0 : rv = SetUpStreamListener(request, aURL);
651 0 : if (NS_FAILED(rv)) return rv;
652 :
653 0 : return rv;
654 : }
655 :
656 0 : NS_IMETHODIMP nsPluginStreamListenerPeer::OnProgress(nsIRequest *request,
657 : nsISupports* aContext,
658 : PRUint64 aProgress,
659 : PRUint64 aProgressMax)
660 : {
661 0 : nsresult rv = NS_OK;
662 0 : return rv;
663 : }
664 :
665 0 : NS_IMETHODIMP nsPluginStreamListenerPeer::OnStatus(nsIRequest *request,
666 : nsISupports* aContext,
667 : nsresult aStatus,
668 : const PRUnichar* aStatusArg)
669 : {
670 0 : return NS_OK;
671 : }
672 :
673 : NS_IMETHODIMP
674 0 : nsPluginStreamListenerPeer::GetContentType(char** result)
675 : {
676 0 : *result = const_cast<char*>(mContentType.get());
677 0 : return NS_OK;
678 : }
679 :
680 :
681 : NS_IMETHODIMP
682 0 : nsPluginStreamListenerPeer::IsSeekable(bool* result)
683 : {
684 0 : *result = mSeekable;
685 0 : return NS_OK;
686 : }
687 :
688 : NS_IMETHODIMP
689 0 : nsPluginStreamListenerPeer::GetLength(PRUint32* result)
690 : {
691 0 : *result = mLength;
692 0 : return NS_OK;
693 : }
694 :
695 : NS_IMETHODIMP
696 0 : nsPluginStreamListenerPeer::GetLastModified(PRUint32* result)
697 : {
698 0 : *result = mModified;
699 0 : return NS_OK;
700 : }
701 :
702 : NS_IMETHODIMP
703 0 : nsPluginStreamListenerPeer::GetURL(const char** result)
704 : {
705 0 : *result = mURLSpec.get();
706 0 : return NS_OK;
707 : }
708 :
709 : void
710 0 : nsPluginStreamListenerPeer::MakeByteRangeString(NPByteRange* aRangeList, nsACString &rangeRequest,
711 : PRInt32 *numRequests)
712 : {
713 0 : rangeRequest.Truncate();
714 0 : *numRequests = 0;
715 : //the string should look like this: bytes=500-700,601-999
716 0 : if (!aRangeList)
717 0 : return;
718 :
719 0 : PRInt32 requestCnt = 0;
720 0 : nsCAutoString string("bytes=");
721 :
722 0 : for (NPByteRange * range = aRangeList; range != nsnull; range = range->next) {
723 : // XXX zero length?
724 0 : if (!range->length)
725 0 : continue;
726 :
727 : // XXX needs to be fixed for negative offsets
728 0 : string.AppendInt(range->offset);
729 0 : string.Append("-");
730 0 : string.AppendInt(range->offset + range->length - 1);
731 0 : if (range->next)
732 0 : string += ",";
733 :
734 0 : requestCnt++;
735 : }
736 :
737 : // get rid of possible trailing comma
738 0 : string.Trim(",", false);
739 :
740 0 : rangeRequest = string;
741 0 : *numRequests = requestCnt;
742 : return;
743 : }
744 :
745 : NS_IMETHODIMP
746 0 : nsPluginStreamListenerPeer::RequestRead(NPByteRange* rangeList)
747 : {
748 0 : nsCAutoString rangeString;
749 : PRInt32 numRequests;
750 :
751 0 : MakeByteRangeString(rangeList, rangeString, &numRequests);
752 :
753 0 : if (numRequests == 0)
754 0 : return NS_ERROR_FAILURE;
755 :
756 0 : nsresult rv = NS_OK;
757 :
758 0 : nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryReferent(mWeakPtrChannelCallbacks);
759 0 : nsCOMPtr<nsILoadGroup> loadGroup = do_QueryReferent(mWeakPtrChannelLoadGroup);
760 0 : nsCOMPtr<nsIChannel> channel;
761 0 : rv = NS_NewChannel(getter_AddRefs(channel), mURL, nsnull, loadGroup, callbacks);
762 0 : if (NS_FAILED(rv))
763 0 : return rv;
764 :
765 0 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
766 0 : if (!httpChannel)
767 0 : return NS_ERROR_FAILURE;
768 :
769 0 : httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, false);
770 :
771 0 : mAbort = true; // instruct old stream listener to cancel
772 : // the request on the next ODA.
773 :
774 0 : nsCOMPtr<nsIStreamListener> converter;
775 :
776 0 : if (numRequests == 1) {
777 0 : converter = this;
778 : // set current stream offset equal to the first offset in the range list
779 : // it will work for single byte range request
780 : // for multy range we'll reset it in ODA
781 0 : SetStreamOffset(rangeList->offset);
782 : } else {
783 : nsWeakPtr weakpeer =
784 0 : do_GetWeakReference(static_cast<nsISupportsWeakReference*>(this));
785 : nsPluginByteRangeStreamListener *brrListener =
786 0 : new nsPluginByteRangeStreamListener(weakpeer);
787 0 : if (brrListener)
788 0 : converter = brrListener;
789 : else
790 0 : return NS_ERROR_OUT_OF_MEMORY;
791 : }
792 :
793 0 : mPendingRequests += numRequests;
794 :
795 0 : nsCOMPtr<nsISupportsPRUint32> container = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
796 0 : if (NS_FAILED(rv))
797 0 : return rv;
798 0 : rv = container->SetData(MAGIC_REQUEST_CONTEXT);
799 0 : if (NS_FAILED(rv))
800 0 : return rv;
801 :
802 0 : rv = channel->AsyncOpen(converter, container);
803 0 : if (NS_SUCCEEDED(rv))
804 0 : TrackRequest(channel);
805 0 : return rv;
806 : }
807 :
808 : NS_IMETHODIMP
809 0 : nsPluginStreamListenerPeer::GetStreamOffset(PRInt32* result)
810 : {
811 0 : *result = mStreamOffset;
812 0 : return NS_OK;
813 : }
814 :
815 : NS_IMETHODIMP
816 0 : nsPluginStreamListenerPeer::SetStreamOffset(PRInt32 value)
817 : {
818 0 : mStreamOffset = value;
819 0 : return NS_OK;
820 : }
821 :
822 0 : nsresult nsPluginStreamListenerPeer::ServeStreamAsFile(nsIRequest *request,
823 : nsISupports* aContext)
824 : {
825 0 : if (!mPluginInstance)
826 0 : return NS_ERROR_FAILURE;
827 :
828 : // mPluginInstance->Stop calls mPStreamListener->CleanUpStream(), so stream will be properly clean up
829 0 : mPluginInstance->Stop();
830 0 : mPluginInstance->Start();
831 0 : nsCOMPtr<nsIPluginInstanceOwner> owner;
832 0 : mPluginInstance->GetOwner(getter_AddRefs(owner));
833 0 : if (owner) {
834 0 : NPWindow* window = nsnull;
835 0 : owner->GetWindow(window);
836 : #if defined(MOZ_WIDGET_GTK2) || defined(MOZ_WIDGET_QT)
837 : // Should call GetPluginPort() here.
838 : // This part is copied from nsPluginInstanceOwner::GetPluginPort().
839 0 : nsCOMPtr<nsIWidget> widget;
840 0 : ((nsPluginNativeWindow*)window)->GetPluginWidget(getter_AddRefs(widget));
841 0 : if (widget) {
842 0 : window->window = widget->GetNativeData(NS_NATIVE_PLUGIN_PORT);
843 : }
844 : #endif
845 0 : owner->CallSetWindow();
846 : }
847 :
848 0 : mSeekable = false;
849 0 : mPStreamListener->OnStartBinding(this);
850 0 : mStreamOffset = 0;
851 :
852 : // force the plugin to use stream as file
853 0 : mStreamType = NP_ASFILE;
854 :
855 : // then check it out if browser cache is not available
856 0 : nsCOMPtr<nsICachingChannel> cacheChannel = do_QueryInterface(request);
857 0 : if (!(cacheChannel && (NS_SUCCEEDED(cacheChannel->SetCacheAsFile(true))))) {
858 0 : nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
859 0 : if (channel) {
860 0 : SetupPluginCacheFile(channel);
861 : }
862 : }
863 :
864 : // unset mPendingRequests
865 0 : mPendingRequests = 0;
866 :
867 0 : return NS_OK;
868 : }
869 :
870 : bool
871 0 : nsPluginStreamListenerPeer::UseExistingPluginCacheFile(nsPluginStreamListenerPeer* psi)
872 : {
873 0 : NS_ENSURE_TRUE(psi, false);
874 :
875 0 : if (psi->mLength == mLength &&
876 : psi->mModified == mModified &&
877 : mStreamComplete &&
878 0 : mURLSpec.Equals(psi->mURLSpec))
879 : {
880 0 : return true;
881 : }
882 0 : return false;
883 : }
884 :
885 0 : NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(nsIRequest *request,
886 : nsISupports* aContext,
887 : nsIInputStream *aIStream,
888 : PRUint32 sourceOffset,
889 : PRUint32 aLength)
890 : {
891 0 : NS_ASSERTION(mRequests.IndexOfObject(GetBaseRequest(request)) != -1,
892 : "Received OnDataAvailable for untracked request.");
893 :
894 0 : if (mRequestFailed)
895 0 : return NS_ERROR_FAILURE;
896 :
897 0 : if (mAbort) {
898 0 : PRUint32 magicNumber = 0; // set it to something that is not the magic number.
899 0 : nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext);
900 0 : if (container)
901 0 : container->GetData(&magicNumber);
902 :
903 0 : if (magicNumber != MAGIC_REQUEST_CONTEXT) {
904 : // this is not one of our range requests
905 0 : mAbort = false;
906 0 : return NS_BINDING_ABORTED;
907 : }
908 : }
909 :
910 0 : nsresult rv = NS_OK;
911 :
912 0 : if (!mPStreamListener)
913 0 : return NS_ERROR_FAILURE;
914 :
915 0 : const char * url = nsnull;
916 0 : GetURL(&url);
917 :
918 0 : PLUGIN_LOG(PLUGIN_LOG_NOISY,
919 : ("nsPluginStreamListenerPeer::OnDataAvailable this=%p request=%p, offset=%d, length=%d, url=%s\n",
920 : this, request, sourceOffset, aLength, url ? url : "no url set"));
921 :
922 : // if the plugin has requested an AsFileOnly stream, then don't
923 : // call OnDataAvailable
924 0 : if (mStreamType != NP_ASFILEONLY) {
925 : // get the absolute offset of the request, if one exists.
926 0 : nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request);
927 0 : if (brr) {
928 0 : if (!mDataForwardToRequest)
929 0 : return NS_ERROR_FAILURE;
930 :
931 0 : PRInt64 absoluteOffset64 = LL_ZERO;
932 0 : brr->GetStartRange(&absoluteOffset64);
933 :
934 : // XXX handle 64-bit for real
935 0 : PRInt32 absoluteOffset = (PRInt32)PRInt64(absoluteOffset64);
936 :
937 : // we need to track how much data we have forwarded to the
938 : // plugin.
939 :
940 : // FIXME: http://bugzilla.mozilla.org/show_bug.cgi?id=240130
941 : //
942 : // Why couldn't this be tracked on the plugin info, and not in a
943 : // *hash table*?
944 0 : nsPRUintKey key(absoluteOffset);
945 : PRInt32 amtForwardToPlugin =
946 0 : NS_PTR_TO_INT32(mDataForwardToRequest->Get(&key));
947 0 : mDataForwardToRequest->Put(&key, NS_INT32_TO_PTR(amtForwardToPlugin + aLength));
948 :
949 0 : SetStreamOffset(absoluteOffset + amtForwardToPlugin);
950 : }
951 :
952 0 : nsCOMPtr<nsIInputStream> stream = aIStream;
953 :
954 : // if we are caching the file ourselves to disk, we want to 'tee' off
955 : // the data as the plugin read from the stream. We do this by the magic
956 : // of an input stream tee.
957 :
958 0 : if (mFileCacheOutputStream) {
959 0 : rv = NS_NewInputStreamTee(getter_AddRefs(stream), aIStream, mFileCacheOutputStream);
960 0 : if (NS_FAILED(rv))
961 0 : return rv;
962 : }
963 :
964 0 : rv = mPStreamListener->OnDataAvailable(this,
965 : stream,
966 0 : aLength);
967 :
968 : // if a plugin returns an error, the peer must kill the stream
969 : // else the stream and PluginStreamListener leak
970 0 : if (NS_FAILED(rv))
971 0 : request->Cancel(rv);
972 : }
973 : else
974 : {
975 : // if we don't read from the stream, OnStopRequest will never be called
976 0 : char* buffer = new char[aLength];
977 0 : PRUint32 amountRead, amountWrote = 0;
978 0 : rv = aIStream->Read(buffer, aLength, &amountRead);
979 :
980 : // if we are caching this to disk ourselves, lets write the bytes out.
981 0 : if (mFileCacheOutputStream) {
982 0 : while (amountWrote < amountRead && NS_SUCCEEDED(rv)) {
983 0 : rv = mFileCacheOutputStream->Write(buffer, amountRead, &amountWrote);
984 : }
985 : }
986 0 : delete [] buffer;
987 : }
988 0 : return rv;
989 : }
990 :
991 0 : NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
992 : nsISupports* aContext,
993 : nsresult aStatus)
994 : {
995 0 : nsresult rv = NS_OK;
996 :
997 0 : nsCOMPtr<nsIMultiPartChannel> mp = do_QueryInterface(request);
998 0 : if (!mp) {
999 0 : bool found = mRequests.RemoveObject(request);
1000 0 : if (!found) {
1001 0 : NS_ERROR("Received OnStopRequest for untracked request.");
1002 : }
1003 : }
1004 :
1005 0 : PLUGIN_LOG(PLUGIN_LOG_NOISY,
1006 : ("nsPluginStreamListenerPeer::OnStopRequest this=%p aStatus=%d request=%p\n",
1007 : this, aStatus, request));
1008 :
1009 : // for ByteRangeRequest we're just updating the mDataForwardToRequest hash and return.
1010 0 : nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request);
1011 0 : if (brr) {
1012 0 : PRInt64 absoluteOffset64 = LL_ZERO;
1013 0 : brr->GetStartRange(&absoluteOffset64);
1014 : // XXX support 64-bit offsets
1015 0 : PRInt32 absoluteOffset = (PRInt32)PRInt64(absoluteOffset64);
1016 :
1017 0 : nsPRUintKey key(absoluteOffset);
1018 :
1019 : // remove the request from our data forwarding count hash.
1020 0 : mDataForwardToRequest->Remove(&key);
1021 :
1022 :
1023 0 : PLUGIN_LOG(PLUGIN_LOG_NOISY,
1024 : (" ::OnStopRequest for ByteRangeRequest Started=%d\n",
1025 : absoluteOffset));
1026 : } else {
1027 : // if this is not byte range request and
1028 : // if we are writting the stream to disk ourselves,
1029 : // close & tear it down here
1030 0 : mFileCacheOutputStream = nsnull;
1031 : }
1032 :
1033 : // if we still have pending stuff to do, lets not close the plugin socket.
1034 0 : if (--mPendingRequests > 0)
1035 0 : return NS_OK;
1036 :
1037 : // we keep our connections around...
1038 0 : nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext);
1039 0 : if (container) {
1040 0 : PRUint32 magicNumber = 0; // set it to something that is not the magic number.
1041 0 : container->GetData(&magicNumber);
1042 0 : if (magicNumber == MAGIC_REQUEST_CONTEXT) {
1043 : // this is one of our range requests
1044 0 : return NS_OK;
1045 : }
1046 : }
1047 :
1048 0 : if (!mPStreamListener)
1049 0 : return NS_ERROR_FAILURE;
1050 :
1051 0 : nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
1052 0 : if (!channel)
1053 0 : return NS_ERROR_FAILURE;
1054 : // Set the content type to ensure we don't pass null to the plugin
1055 0 : nsCAutoString aContentType;
1056 0 : rv = channel->GetContentType(aContentType);
1057 0 : if (NS_FAILED(rv) && !mRequestFailed)
1058 0 : return rv;
1059 :
1060 0 : if (!aContentType.IsEmpty())
1061 0 : mContentType = aContentType;
1062 :
1063 : // set error status if stream failed so we notify the plugin
1064 0 : if (mRequestFailed)
1065 0 : aStatus = NS_ERROR_FAILURE;
1066 :
1067 0 : if (NS_FAILED(aStatus)) {
1068 : // on error status cleanup the stream
1069 : // and return w/o OnFileAvailable()
1070 0 : mPStreamListener->OnStopBinding(this, aStatus);
1071 0 : return NS_OK;
1072 : }
1073 :
1074 : // call OnFileAvailable if plugin requests stream type StreamType_AsFile or StreamType_AsFileOnly
1075 0 : if (mStreamType >= NP_ASFILE) {
1076 0 : nsCOMPtr<nsIFile> localFile;
1077 0 : if (mLocalCachedFileHolder)
1078 0 : localFile = mLocalCachedFileHolder->file();
1079 : else {
1080 0 : nsCOMPtr<nsICachingChannel> cacheChannel = do_QueryInterface(request);
1081 0 : if (cacheChannel) {
1082 0 : cacheChannel->GetCacheFile(getter_AddRefs(localFile));
1083 : } else {
1084 : // see if it is a file channel.
1085 0 : nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(request);
1086 0 : if (fileChannel) {
1087 0 : fileChannel->GetFile(getter_AddRefs(localFile));
1088 : }
1089 : }
1090 : }
1091 :
1092 0 : if (localFile) {
1093 0 : OnFileAvailable(localFile);
1094 : }
1095 : }
1096 :
1097 0 : if (mStartBinding) {
1098 : // On start binding has been called
1099 0 : mPStreamListener->OnStopBinding(this, aStatus);
1100 : } else {
1101 : // OnStartBinding hasn't been called, so complete the action.
1102 0 : mPStreamListener->OnStartBinding(this);
1103 0 : mPStreamListener->OnStopBinding(this, aStatus);
1104 : }
1105 :
1106 0 : if (NS_SUCCEEDED(aStatus)) {
1107 0 : mStreamComplete = true;
1108 : }
1109 :
1110 0 : return NS_OK;
1111 : }
1112 :
1113 0 : nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request,
1114 : nsIURI* aURL)
1115 : {
1116 0 : nsresult rv = NS_OK;
1117 :
1118 : // If we don't yet have a stream listener, we need to get
1119 : // one from the plugin.
1120 : // NOTE: this should only happen when a stream was NOT created
1121 : // with GetURL or PostURL (i.e. it's the initial stream we
1122 : // send to the plugin as determined by the SRC or DATA attribute)
1123 0 : if (!mPStreamListener) {
1124 0 : if (!mPluginInstance) {
1125 0 : return NS_ERROR_FAILURE;
1126 : }
1127 :
1128 0 : nsCOMPtr<nsIPluginStreamListener> streamListener;
1129 : rv = mPluginInstance->NewStreamListener(nsnull, nsnull,
1130 0 : getter_AddRefs(streamListener));
1131 0 : if (NS_FAILED(rv) || !streamListener) {
1132 0 : return NS_ERROR_FAILURE;
1133 : }
1134 :
1135 0 : mPStreamListener = static_cast<nsNPAPIPluginStreamListener*>(streamListener.get());
1136 : }
1137 :
1138 0 : mPStreamListener->SetStreamListenerPeer(this);
1139 :
1140 0 : bool useLocalCache = false;
1141 :
1142 : // get httpChannel to retrieve some info we need for nsIPluginStreamInfo setup
1143 0 : nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
1144 0 : nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
1145 :
1146 : /*
1147 : * Assumption
1148 : * By the time nsPluginStreamListenerPeer::OnDataAvailable() gets
1149 : * called, all the headers have been read.
1150 : */
1151 0 : if (httpChannel) {
1152 : // Reassemble the HTTP response status line and provide it to our
1153 : // listener. Would be nice if we could get the raw status line,
1154 : // but nsIHttpChannel doesn't currently provide that.
1155 : // Status code: required; the status line isn't useful without it.
1156 : PRUint32 statusNum;
1157 0 : if (NS_SUCCEEDED(httpChannel->GetResponseStatus(&statusNum)) &&
1158 : statusNum < 1000) {
1159 : // HTTP version: provide if available. Defaults to empty string.
1160 0 : nsCString ver;
1161 : nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
1162 0 : do_QueryInterface(channel);
1163 0 : if (httpChannelInternal) {
1164 : PRUint32 major, minor;
1165 0 : if (NS_SUCCEEDED(httpChannelInternal->GetResponseVersion(&major,
1166 : &minor))) {
1167 0 : ver = nsPrintfCString("/%lu.%lu", major, minor);
1168 : }
1169 : }
1170 :
1171 : // Status text: provide if available. Defaults to "OK".
1172 0 : nsCString statusText;
1173 0 : if (NS_FAILED(httpChannel->GetResponseStatusText(statusText))) {
1174 0 : statusText = "OK";
1175 : }
1176 :
1177 : // Assemble everything and pass to listener.
1178 : nsPrintfCString status(100, "HTTP%s %lu %s", ver.get(), statusNum,
1179 0 : statusText.get());
1180 0 : static_cast<nsIHTTPHeaderListener*>(mPStreamListener)->StatusLine(status.get());
1181 : }
1182 :
1183 : // Also provide all HTTP response headers to our listener.
1184 0 : httpChannel->VisitResponseHeaders(this);
1185 :
1186 0 : mSeekable = false;
1187 : // first we look for a content-encoding header. If we find one, we tell the
1188 : // plugin that stream is not seekable, because the plugin always sees
1189 : // uncompressed data, so it can't make meaningful range requests on a
1190 : // compressed entity. Also, we force the plugin to use
1191 : // nsPluginStreamType_AsFile stream type and we have to save decompressed
1192 : // file into local plugin cache, because necko cache contains original
1193 : // compressed file.
1194 0 : nsCAutoString contentEncoding;
1195 0 : if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Encoding"),
1196 : contentEncoding))) {
1197 0 : useLocalCache = true;
1198 : } else {
1199 : // set seekability (seekable if the stream has a known length and if the
1200 : // http server accepts byte ranges).
1201 : PRUint32 length;
1202 0 : GetLength(&length);
1203 0 : if (length) {
1204 0 : nsCAutoString range;
1205 0 : if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("accept-ranges"), range)) &&
1206 0 : range.Equals(NS_LITERAL_CSTRING("bytes"), nsCaseInsensitiveCStringComparator())) {
1207 0 : mSeekable = true;
1208 : }
1209 : }
1210 : }
1211 :
1212 : // we require a content len
1213 : // get Last-Modified header for plugin info
1214 0 : nsCAutoString lastModified;
1215 0 : if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("last-modified"), lastModified)) &&
1216 0 : !lastModified.IsEmpty()) {
1217 : PRTime time64;
1218 0 : PR_ParseTimeString(lastModified.get(), true, &time64); //convert string time to integer time
1219 :
1220 : // Convert PRTime to unix-style time_t, i.e. seconds since the epoch
1221 : double fpTime;
1222 0 : LL_L2D(fpTime, time64);
1223 0 : mModified = (PRUint32)(fpTime * 1e-6 + 0.5);
1224 : }
1225 : }
1226 :
1227 0 : rv = mPStreamListener->OnStartBinding(this);
1228 :
1229 0 : mStartBinding = true;
1230 :
1231 0 : if (NS_FAILED(rv))
1232 0 : return rv;
1233 :
1234 0 : mPStreamListener->GetStreamType(&mStreamType);
1235 :
1236 0 : if (!useLocalCache && mStreamType >= NP_ASFILE) {
1237 : // check it out if this is not a file channel.
1238 0 : nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(request);
1239 0 : if (!fileChannel) {
1240 : // and browser cache is not available
1241 0 : nsCOMPtr<nsICachingChannel> cacheChannel = do_QueryInterface(request);
1242 0 : if (!(cacheChannel && (NS_SUCCEEDED(cacheChannel->SetCacheAsFile(true))))) {
1243 0 : useLocalCache = true;
1244 : }
1245 : }
1246 : }
1247 :
1248 0 : if (useLocalCache) {
1249 0 : SetupPluginCacheFile(channel);
1250 : }
1251 :
1252 0 : return NS_OK;
1253 : }
1254 :
1255 : nsresult
1256 0 : nsPluginStreamListenerPeer::OnFileAvailable(nsIFile* aFile)
1257 : {
1258 : nsresult rv;
1259 0 : if (!mPStreamListener)
1260 0 : return NS_ERROR_FAILURE;
1261 :
1262 0 : nsCAutoString path;
1263 0 : rv = aFile->GetNativePath(path);
1264 0 : if (NS_FAILED(rv)) return rv;
1265 :
1266 0 : if (path.IsEmpty()) {
1267 0 : NS_WARNING("empty path");
1268 0 : return NS_OK;
1269 : }
1270 :
1271 0 : rv = mPStreamListener->OnFileAvailable(this, path.get());
1272 0 : return rv;
1273 : }
1274 :
1275 : NS_IMETHODIMP
1276 0 : nsPluginStreamListenerPeer::VisitHeader(const nsACString &header, const nsACString &value)
1277 : {
1278 0 : return mPStreamListener->NewResponseHeader(PromiseFlatCString(header).get(),
1279 0 : PromiseFlatCString(value).get());
1280 : }
1281 :
1282 : nsresult
1283 0 : nsPluginStreamListenerPeer::GetInterfaceGlobal(const nsIID& aIID, void** result)
1284 : {
1285 0 : if (!mPluginInstance) {
1286 0 : return NS_ERROR_FAILURE;
1287 : }
1288 :
1289 0 : nsCOMPtr<nsIPluginInstanceOwner> owner;
1290 0 : mPluginInstance->GetOwner(getter_AddRefs(owner));
1291 0 : if (owner) {
1292 0 : nsCOMPtr<nsIDocument> doc;
1293 0 : nsresult rv = owner->GetDocument(getter_AddRefs(doc));
1294 0 : if (NS_SUCCEEDED(rv) && doc) {
1295 0 : nsPIDOMWindow *window = doc->GetWindow();
1296 0 : if (window) {
1297 0 : nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window);
1298 0 : nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryInterface(webNav);
1299 0 : return ir->GetInterface(aIID, result);
1300 : }
1301 : }
1302 : }
1303 :
1304 0 : return NS_ERROR_FAILURE;
1305 : }
1306 :
1307 : NS_IMETHODIMP
1308 0 : nsPluginStreamListenerPeer::GetInterface(const nsIID& aIID, void** result)
1309 : {
1310 : // Provide nsIChannelEventSink ourselves, otherwise let our document's
1311 : // script global object owner provide the interface.
1312 0 : if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
1313 0 : return QueryInterface(aIID, result);
1314 : }
1315 :
1316 0 : return GetInterfaceGlobal(aIID, result);
1317 : }
1318 :
1319 : /**
1320 : * Proxy class which forwards async redirect notifications back to the necko
1321 : * callback, keeping nsPluginStreamListenerPeer::mRequests in sync with
1322 : * which channel is active.
1323 : */
1324 : class ChannelRedirectProxyCallback : public nsIAsyncVerifyRedirectCallback
1325 : {
1326 : public:
1327 0 : ChannelRedirectProxyCallback(nsINPAPIPluginStreamInfo* listener,
1328 : nsIAsyncVerifyRedirectCallback* parent,
1329 : nsIChannel* oldChannel,
1330 : nsIChannel* newChannel)
1331 0 : : mWeakListener(do_GetWeakReference(listener))
1332 : , mParent(parent)
1333 : , mOldChannel(oldChannel)
1334 0 : , mNewChannel(newChannel)
1335 : {
1336 0 : }
1337 :
1338 : ChannelRedirectProxyCallback() {}
1339 0 : virtual ~ChannelRedirectProxyCallback() {}
1340 :
1341 : NS_DECL_ISUPPORTS
1342 :
1343 0 : NS_IMETHODIMP OnRedirectVerifyCallback(nsresult result)
1344 : {
1345 0 : if (NS_SUCCEEDED(result)) {
1346 0 : nsCOMPtr<nsINPAPIPluginStreamInfo> listener = do_QueryReferent(mWeakListener);
1347 0 : if (listener)
1348 0 : listener->ReplaceRequest(mOldChannel, mNewChannel);
1349 : }
1350 0 : return mParent->OnRedirectVerifyCallback(result);
1351 : }
1352 :
1353 : private:
1354 : nsWeakPtr mWeakListener;
1355 : nsCOMPtr<nsIAsyncVerifyRedirectCallback> mParent;
1356 : nsCOMPtr<nsIChannel> mOldChannel;
1357 : nsCOMPtr<nsIChannel> mNewChannel;
1358 : };
1359 :
1360 0 : NS_IMPL_ISUPPORTS1(ChannelRedirectProxyCallback, nsIAsyncVerifyRedirectCallback)
1361 :
1362 :
1363 : NS_IMETHODIMP
1364 0 : nsPluginStreamListenerPeer::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel,
1365 : PRUint32 flags, nsIAsyncVerifyRedirectCallback* callback)
1366 : {
1367 : // Disallow redirects if we don't have a stream listener.
1368 0 : if (!mPStreamListener) {
1369 0 : return NS_ERROR_FAILURE;
1370 : }
1371 :
1372 : nsCOMPtr<nsIAsyncVerifyRedirectCallback> proxyCallback =
1373 0 : new ChannelRedirectProxyCallback(this, callback, oldChannel, newChannel);
1374 :
1375 : // Give NPAPI a chance to control redirects.
1376 0 : bool notificationHandled = mPStreamListener->HandleRedirectNotification(oldChannel, newChannel, proxyCallback);
1377 0 : if (notificationHandled) {
1378 0 : return NS_OK;
1379 : }
1380 :
1381 : // Don't allow cross-origin 307 POST redirects.
1382 0 : nsCOMPtr<nsIHttpChannel> oldHttpChannel(do_QueryInterface(oldChannel));
1383 0 : if (oldHttpChannel) {
1384 : PRUint32 responseStatus;
1385 0 : nsresult rv = oldHttpChannel->GetResponseStatus(&responseStatus);
1386 0 : if (NS_FAILED(rv)) {
1387 0 : return rv;
1388 : }
1389 0 : if (responseStatus == 307) {
1390 0 : nsCAutoString method;
1391 0 : rv = oldHttpChannel->GetRequestMethod(method);
1392 0 : if (NS_FAILED(rv)) {
1393 0 : return rv;
1394 : }
1395 0 : if (method.EqualsLiteral("POST")) {
1396 0 : rv = nsContentUtils::CheckSameOrigin(oldChannel, newChannel);
1397 0 : if (NS_FAILED(rv)) {
1398 0 : return rv;
1399 : }
1400 : }
1401 : }
1402 : }
1403 :
1404 : // Fall back to channel event sink for window.
1405 0 : nsCOMPtr<nsIChannelEventSink> channelEventSink;
1406 0 : nsresult rv = GetInterfaceGlobal(NS_GET_IID(nsIChannelEventSink), getter_AddRefs(channelEventSink));
1407 0 : if (NS_FAILED(rv)) {
1408 0 : return rv;
1409 : }
1410 :
1411 0 : return channelEventSink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, proxyCallback);
1412 : }
|