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