dom/plugins/base/nsPluginStreamListenerPeer.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     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"
    32 #define MAGIC_REQUEST_CONTEXT 0x01020304
    34 // nsPluginByteRangeStreamListener
    36 class nsPluginByteRangeStreamListener
    37   : public nsIStreamListener
    38   , public nsIInterfaceRequestor
    39 {
    40 public:
    41   nsPluginByteRangeStreamListener(nsIWeakReference* aWeakPtr);
    42   virtual ~nsPluginByteRangeStreamListener();
    44   NS_DECL_ISUPPORTS
    45   NS_DECL_NSIREQUESTOBSERVER
    46   NS_DECL_NSISTREAMLISTENER
    47   NS_DECL_NSIINTERFACEREQUESTOR
    49 private:
    50   nsCOMPtr<nsIStreamListener> mStreamConverter;
    51   nsWeakPtr mWeakPtrPluginStreamListenerPeer;
    52   bool mRemoveMagicNumber;
    53 };
    55 NS_IMPL_ISUPPORTS(nsPluginByteRangeStreamListener,
    56                   nsIRequestObserver,
    57                   nsIStreamListener,
    58                   nsIInterfaceRequestor)
    60 nsPluginByteRangeStreamListener::nsPluginByteRangeStreamListener(nsIWeakReference* aWeakPtr)
    61 {
    62   mWeakPtrPluginStreamListenerPeer = aWeakPtr;
    63   mRemoveMagicNumber = false;
    64 }
    66 nsPluginByteRangeStreamListener::~nsPluginByteRangeStreamListener()
    67 {
    68   mStreamConverter = 0;
    69   mWeakPtrPluginStreamListenerPeer = 0;
    70 }
    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;
    83   nsCOMPtr<nsIChannel> base;
    84   mp->GetBaseChannel(getter_AddRefs(base));
    85   return already_AddRefed<nsIRequest>(base.forget());
    86 }
    88 NS_IMETHODIMP
    89 nsPluginByteRangeStreamListener::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
    90 {
    91   nsresult rv;
    93   nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
    94   if (!finalStreamListener)
    95     return NS_ERROR_FAILURE;
    97   nsPluginStreamListenerPeer *pslp =
    98     static_cast<nsPluginStreamListenerPeer*>(finalStreamListener.get());
   100   NS_ASSERTION(pslp->mRequests.IndexOfObject(GetBaseRequest(request)) != -1,
   101                "Untracked byte-range request?");
   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;
   118   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
   119   if (!httpChannel) {
   120     return NS_ERROR_FAILURE;
   121   }
   123   uint32_t responseCode = 0;
   124   rv = httpChannel->GetResponseStatus(&responseCode);
   125   if (NS_FAILED(rv)) {
   126     return NS_ERROR_FAILURE;
   127   }
   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     }
   138     if (!wantsAllNetworkStreams){
   139       return NS_ERROR_FAILURE;
   140     }
   141   }
   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;
   148   rv = pslp->ServeStreamAsFile(request, ctxt);
   149   return rv;
   150 }
   152 NS_IMETHODIMP
   153 nsPluginByteRangeStreamListener::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
   154                                                nsresult status)
   155 {
   156   if (!mStreamConverter)
   157     return NS_ERROR_FAILURE;
   159   nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
   160   if (!finalStreamListener)
   161     return NS_ERROR_FAILURE;
   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   }
   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   }
   186   return mStreamConverter->OnStopRequest(request, ctxt, status);
   187 }
   189 // CachedFileHolder
   191 CachedFileHolder::CachedFileHolder(nsIFile* cacheFile)
   192 : mFile(cacheFile)
   193 {
   194   NS_ASSERTION(mFile, "Empty CachedFileHolder");
   195 }
   197 CachedFileHolder::~CachedFileHolder()
   198 {
   199   mFile->Remove(false);
   200 }
   202 void
   203 CachedFileHolder::AddRef()
   204 {
   205   ++mRefCnt;
   206   NS_LOG_ADDREF(this, mRefCnt, "CachedFileHolder", sizeof(*this));
   207 }
   209 void
   210 CachedFileHolder::Release()
   211 {
   212   --mRefCnt;
   213   NS_LOG_RELEASE(this, mRefCnt, "CachedFileHolder");
   214   if (0 == mRefCnt)
   215     delete this;
   216 }
   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;
   228   nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
   229   if (!finalStreamListener)
   230     return NS_ERROR_FAILURE;
   232   return mStreamConverter->OnDataAvailable(request, ctxt, inStr, sourceOffset, count);
   233 }
   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;
   243   return finalStreamListener->GetInterface(aIID, result);
   244 }
   246 // nsPluginStreamListenerPeer
   248 NS_IMPL_ISUPPORTS(nsPluginStreamListenerPeer,
   249                   nsIStreamListener,
   250                   nsIRequestObserver,
   251                   nsIHttpHeaderVisitor,
   252                   nsISupportsWeakReference,
   253                   nsIInterfaceRequestor,
   254                   nsIChannelEventSink)
   256 nsPluginStreamListenerPeer::nsPluginStreamListenerPeer()
   257 {
   258   mStreamType = NP_NORMAL;
   259   mStartBinding = false;
   260   mAbort = false;
   261   mRequestFailed = false;
   263   mPendingRequests = 0;
   264   mHaveFiredOnStartRequest = false;
   265   mDataForwardToRequest = nullptr;
   267   mSeekable = false;
   268   mModified = 0;
   269   mStreamOffset = 0;
   270   mStreamComplete = 0;
   271 }
   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
   280   if (mPStreamListener) {
   281     mPStreamListener->SetStreamListenerPeer(nullptr);
   282   }
   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;
   289   delete mDataForwardToRequest;
   291   if (mPluginInstance)
   292     mPluginInstance->FileCachedStreamListeners()->RemoveElement(this);
   293 }
   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);
   305   PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_NORMAL,
   306          ("nsPluginStreamListenerPeer::Initialize instance=%p, url=%s\n", aInstance, urlSpec.get()));
   308   PR_LogFlush();
   309 #endif
   311   // Not gonna work out
   312   if (!aInstance) {
   313     return NS_ERROR_FAILURE;
   314   }
   316   mURL = aURL;
   318   NS_ASSERTION(mPluginInstance == nullptr,
   319                "nsPluginStreamListenerPeer::Initialize mPluginInstance != nullptr");
   320   mPluginInstance = aInstance;
   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   }
   330   mPendingRequests = 1;
   332   mDataForwardToRequest = new nsDataHashtable<nsUint32HashKey, uint32_t>();
   334   return NS_OK;
   335 }
   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;
   347   bool useExistingCacheFile = false;
   348   nsRefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
   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   }
   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     }
   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;
   382     nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
   383     if (!url)
   384       return NS_ERROR_FAILURE;
   386     nsAutoCString filename;
   387     url->GetFileName(filename);
   388     if (NS_FAILED(rv))
   389       return rv;
   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;
   397     // Yes, make it unique.
   398     rv = pluginTmp->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
   399     if (NS_FAILED(rv))
   400       return rv;
   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;
   408     // save the file.
   409     mLocalCachedFileHolder = new CachedFileHolder(pluginTmp);
   410   }
   412   // add this listenerPeer to list of stream peers for this instance
   413   mPluginInstance->FileCachedStreamListeners()->AppendElement(this);
   415   return rv;
   416 }
   418 NS_IMETHODIMP
   419 nsPluginStreamListenerPeer::OnStartRequest(nsIRequest *request,
   420                                            nsISupports* aContext)
   421 {
   422   nsresult rv = NS_OK;
   423   PROFILER_LABEL("nsPluginStreamListenerPeer", "OnStartRequest");
   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   }
   431   if (mHaveFiredOnStartRequest) {
   432     return NS_OK;
   433   }
   435   mHaveFiredOnStartRequest = true;
   437   nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
   438   NS_ENSURE_TRUE(channel, NS_ERROR_FAILURE);
   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     }
   455     if (responseCode > 206) { // not normal
   456       uint32_t wantsAllNetworkStreams = 0;
   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       }
   469       if (!wantsAllNetworkStreams) {
   470         mRequestFailed = true;
   471         return NS_ERROR_FAILURE;
   472       }
   473     }
   474   }
   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);
   484   nsCOMPtr<nsILoadGroup> loadGroup;
   485   channel->GetLoadGroup(getter_AddRefs(loadGroup));
   486   if (loadGroup)
   487     mWeakPtrChannelLoadGroup = do_GetWeakReference(loadGroup);
   489   int64_t length;
   490   rv = channel->GetContentLength(&length);
   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   }
   508   nsAutoCString aContentType; // XXX but we already got the type above!
   509   rv = channel->GetContentType(aContentType);
   510   if (NS_FAILED(rv))
   511     return rv;
   513   nsCOMPtr<nsIURI> aURL;
   514   rv = channel->GetURI(getter_AddRefs(aURL));
   515   if (NS_FAILED(rv))
   516     return rv;
   518   aURL->GetSpec(mURLSpec);
   520   if (!aContentType.IsEmpty())
   521     mContentType = aContentType;
   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()));
   528   PR_LogFlush();
   529 #endif
   531   // Set up the stream listener...
   532   rv = SetUpStreamListener(request, aURL);
   533   if (NS_FAILED(rv)) return rv;
   535   return rv;
   536 }
   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 }
   547 NS_IMETHODIMP nsPluginStreamListenerPeer::OnStatus(nsIRequest *request,
   548                                                    nsISupports* aContext,
   549                                                    nsresult aStatus,
   550                                                    const char16_t* aStatusArg)
   551 {
   552   return NS_OK;
   553 }
   555 nsresult
   556 nsPluginStreamListenerPeer::GetContentType(char** result)
   557 {
   558   *result = const_cast<char*>(mContentType.get());
   559   return NS_OK;
   560 }
   563 nsresult
   564 nsPluginStreamListenerPeer::IsSeekable(bool* result)
   565 {
   566   *result = mSeekable;
   567   return NS_OK;
   568 }
   570 nsresult
   571 nsPluginStreamListenerPeer::GetLength(uint32_t* result)
   572 {
   573   *result = mLength;
   574   return NS_OK;
   575 }
   577 nsresult
   578 nsPluginStreamListenerPeer::GetLastModified(uint32_t* result)
   579 {
   580   *result = mModified;
   581   return NS_OK;
   582 }
   584 nsresult
   585 nsPluginStreamListenerPeer::GetURL(const char** result)
   586 {
   587   *result = mURLSpec.get();
   588   return NS_OK;
   589 }
   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;
   601   int32_t requestCnt = 0;
   602   nsAutoCString string("bytes=");
   604   for (NPByteRange * range = aRangeList; range != nullptr; range = range->next) {
   605     // XXX zero length?
   606     if (!range->length)
   607       continue;
   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 += ",";
   616     requestCnt++;
   617   }
   619   // get rid of possible trailing comma
   620   string.Trim(",", false);
   622   rangeRequest = string;
   623   *numRequests  = requestCnt;
   624   return;
   625 }
   627 nsresult
   628 nsPluginStreamListenerPeer::RequestRead(NPByteRange* rangeList)
   629 {
   630   nsAutoCString rangeString;
   631   int32_t numRequests;
   633   MakeByteRangeString(rangeList, rangeString, &numRequests);
   635   if (numRequests == 0)
   636     return NS_ERROR_FAILURE;
   638   nsresult rv = NS_OK;
   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;
   647   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   648   if (!httpChannel)
   649     return NS_ERROR_FAILURE;
   651   httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, false);
   653   mAbort = true; // instruct old stream listener to cancel
   654   // the request on the next ODA.
   656   nsCOMPtr<nsIStreamListener> converter;
   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   }
   675   mPendingRequests += numRequests;
   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;
   684   rv = channel->AsyncOpen(converter, container);
   685   if (NS_SUCCEEDED(rv))
   686     TrackRequest(channel);
   687   return rv;
   688 }
   690 nsresult
   691 nsPluginStreamListenerPeer::GetStreamOffset(int32_t* result)
   692 {
   693   *result = mStreamOffset;
   694   return NS_OK;
   695 }
   697 nsresult
   698 nsPluginStreamListenerPeer::SetStreamOffset(int32_t value)
   699 {
   700   mStreamOffset = value;
   701   return NS_OK;
   702 }
   704 nsresult nsPluginStreamListenerPeer::ServeStreamAsFile(nsIRequest *request,
   705                                                        nsISupports* aContext)
   706 {
   707   if (!mPluginInstance)
   708     return NS_ERROR_FAILURE;
   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   }
   729   mSeekable = false;
   730   mPStreamListener->OnStartBinding(this);
   731   mStreamOffset = 0;
   733   // force the plugin to use stream as file
   734   mStreamType = NP_ASFILE;
   736   nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
   737   if (channel) {
   738     SetupPluginCacheFile(channel);
   739   }
   741   // unset mPendingRequests
   742   mPendingRequests = 0;
   744   return NS_OK;
   745 }
   747 bool
   748 nsPluginStreamListenerPeer::UseExistingPluginCacheFile(nsPluginStreamListenerPeer* psi)
   749 {
   750   NS_ENSURE_TRUE(psi, false);
   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 }
   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   }
   773   if (mRequestFailed)
   774     return NS_ERROR_FAILURE;
   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);
   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   }
   789   nsresult rv = NS_OK;
   791   if (!mPStreamListener)
   792     return NS_ERROR_FAILURE;
   794   const char * url = nullptr;
   795   GetURL(&url);
   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"));
   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;
   810       int64_t absoluteOffset64 = 0;
   811       brr->GetStartRange(&absoluteOffset64);
   813       // XXX handle 64-bit for real
   814       int32_t absoluteOffset = (int32_t)int64_t(absoluteOffset64);
   816       // we need to track how much data we have forwarded to the
   817       // plugin.
   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));
   826       SetStreamOffset(absoluteOffset + amtForwardToPlugin);
   827     }
   829     nsCOMPtr<nsIInputStream> stream = aIStream;
   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.
   835     if (mFileCacheOutputStream) {
   836       rv = NS_NewInputStreamTee(getter_AddRefs(stream), aIStream, mFileCacheOutputStream);
   837       if (NS_FAILED(rv))
   838         return rv;
   839     }
   841     rv =  mPStreamListener->OnDataAvailable(this,
   842                                             stream,
   843                                             aLength);
   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);
   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 }
   868 NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
   869                                                         nsISupports* aContext,
   870                                                         nsresult aStatus)
   871 {
   872   nsresult rv = NS_OK;
   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   }
   882   PLUGIN_LOG(PLUGIN_LOG_NOISY,
   883              ("nsPluginStreamListenerPeer::OnStopRequest this=%p aStatus=%d request=%p\n",
   884               this, aStatus, request));
   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);
   894     // remove the request from our data forwarding count hash.
   895     mDataForwardToRequest->Remove(absoluteOffset);
   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   }
   908   // if we still have pending stuff to do, lets not close the plugin socket.
   909   if (--mPendingRequests > 0)
   910     return NS_OK;
   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   }
   923   if (!mPStreamListener)
   924     return NS_ERROR_FAILURE;
   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;
   935   if (!aContentType.IsEmpty())
   936     mContentType = aContentType;
   938   // set error status if stream failed so we notify the plugin
   939   if (mRequestFailed)
   940     aStatus = NS_ERROR_FAILURE;
   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   }
   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     }
   962     if (localFile) {
   963       OnFileAvailable(localFile);
   964     }
   965   }
   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   }
   976   if (NS_SUCCEEDED(aStatus)) {
   977     mStreamComplete = true;
   978   }
   980   return NS_OK;
   981 }
   983 nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request,
   984                                                          nsIURI* aURL)
   985 {
   986   nsresult rv = NS_OK;
   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     }
   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;
  1005     mPStreamListener = static_cast<nsNPAPIPluginStreamListener*>(streamListener.get());
  1008   mPStreamListener->SetStreamListenerPeer(this);
  1010   bool useLocalCache = false;
  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);
  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);
  1041       // Status text: provide if available.  Defaults to "OK".
  1042       nsCString statusText;
  1043       if (NS_FAILED(httpChannel->GetResponseStatusText(statusText))) {
  1044         statusText = "OK";
  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());
  1053     // Also provide all HTTP response headers to our listener.
  1054     httpChannel->VisitResponseHeaders(this);
  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;
  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
  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);
  1096   rv = mPStreamListener->OnStartBinding(this);
  1098   mStartBinding = true;
  1100   if (NS_FAILED(rv))
  1101     return rv;
  1103   mPStreamListener->GetStreamType(&mStreamType);
  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;
  1113   if (useLocalCache) {
  1114     SetupPluginCacheFile(channel);
  1117   return NS_OK;
  1120 nsresult
  1121 nsPluginStreamListenerPeer::OnFileAvailable(nsIFile* aFile)
  1123   nsresult rv;
  1124   if (!mPStreamListener)
  1125     return NS_ERROR_FAILURE;
  1127   nsAutoCString path;
  1128   rv = aFile->GetNativePath(path);
  1129   if (NS_FAILED(rv)) return rv;
  1131   if (path.IsEmpty()) {
  1132     NS_WARNING("empty path");
  1133     return NS_OK;
  1136   rv = mPStreamListener->OnFileAvailable(this, path.get());
  1137   return rv;
  1140 NS_IMETHODIMP
  1141 nsPluginStreamListenerPeer::VisitHeader(const nsACString &header, const nsACString &value)
  1143   return mPStreamListener->NewResponseHeader(PromiseFlatCString(header).get(),
  1144                                              PromiseFlatCString(value).get());
  1147 nsresult
  1148 nsPluginStreamListenerPeer::GetInterfaceGlobal(const nsIID& aIID, void** result)
  1150   if (!mPluginInstance) {
  1151     return NS_ERROR_FAILURE;
  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);
  1168   return NS_ERROR_FAILURE;
  1171 NS_IMETHODIMP
  1172 nsPluginStreamListenerPeer::GetInterface(const nsIID& aIID, void** result)
  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);
  1180   return GetInterfaceGlobal(aIID, result);
  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
  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)
  1202   ChannelRedirectProxyCallback() {}
  1203   virtual ~ChannelRedirectProxyCallback() {}
  1205   NS_DECL_ISUPPORTS
  1207   NS_IMETHODIMP OnRedirectVerifyCallback(nsresult result)
  1209     if (NS_SUCCEEDED(result)) {
  1210       nsCOMPtr<nsIStreamListener> listener = do_QueryReferent(mWeakListener);
  1211       if (listener)
  1212         static_cast<nsPluginStreamListenerPeer*>(listener.get())->ReplaceRequest(mOldChannel, mNewChannel);
  1214     return mParent->OnRedirectVerifyCallback(result);
  1217 private:
  1218   nsWeakPtr mWeakListener;
  1219   nsCOMPtr<nsIAsyncVerifyRedirectCallback> mParent;
  1220   nsCOMPtr<nsIChannel> mOldChannel;
  1221   nsCOMPtr<nsIChannel> mNewChannel;
  1222 };
  1224 NS_IMPL_ISUPPORTS(ChannelRedirectProxyCallback, nsIAsyncVerifyRedirectCallback)
  1227 NS_IMETHODIMP
  1228 nsPluginStreamListenerPeer::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel,
  1229                                                    uint32_t flags, nsIAsyncVerifyRedirectCallback* callback)
  1231   // Disallow redirects if we don't have a stream listener.
  1232   if (!mPStreamListener) {
  1233     return NS_ERROR_FAILURE;
  1236   nsCOMPtr<nsIAsyncVerifyRedirectCallback> proxyCallback =
  1237     new ChannelRedirectProxyCallback(this, callback, oldChannel, newChannel);
  1239   // Give NPAPI a chance to control redirects.
  1240   bool notificationHandled = mPStreamListener->HandleRedirectNotification(oldChannel, newChannel, proxyCallback);
  1241   if (notificationHandled) {
  1242     return NS_OK;
  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;
  1253     if (responseStatus == 307) {
  1254       nsAutoCString method;
  1255       rv = oldHttpChannel->GetRequestMethod(method);
  1256       if (NS_FAILED(rv)) {
  1257         return rv;
  1259       if (method.EqualsLiteral("POST")) {
  1260         rv = nsContentUtils::CheckSameOrigin(oldChannel, newChannel);
  1261         if (NS_FAILED(rv)) {
  1262           return rv;
  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;
  1275   return channelEventSink->AsyncOnChannelRedirect(oldChannel, newChannel, flags, proxyCallback);

mercurial