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