content/base/src/nsSyncLoadService.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     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 /*
     7  * A service that provides methods for synchronously loading a DOM in various ways.
     8  */
    10 #include "nsSyncLoadService.h"
    11 #include "nsCOMPtr.h"
    12 #include "nsIChannel.h"
    13 #include "nsIChannelEventSink.h"
    14 #include "nsIAsyncVerifyRedirectCallback.h"
    15 #include "nsIInterfaceRequestor.h"
    16 #include "nsString.h"
    17 #include "nsWeakReference.h"
    18 #include "nsIDocument.h"
    19 #include "nsIDOMDocument.h"
    20 #include "nsIPrincipal.h"
    21 #include "nsContentUtils.h" // for kLoadAsData
    22 #include "nsThreadUtils.h"
    23 #include "nsNetUtil.h"
    24 #include "nsAutoPtr.h"
    25 #include "nsStreamUtils.h"
    26 #include "nsCrossSiteListenerProxy.h"
    27 #include <algorithm>
    29 /**
    30  * This class manages loading a single XML document
    31  */
    33 class nsSyncLoader : public nsIStreamListener,
    34                      public nsIChannelEventSink,
    35                      public nsIInterfaceRequestor,
    36                      public nsSupportsWeakReference
    37 {
    38 public:
    39     nsSyncLoader() : mLoading(false) {}
    40     virtual ~nsSyncLoader();
    42     NS_DECL_ISUPPORTS
    44     nsresult LoadDocument(nsIChannel* aChannel, nsIPrincipal *aLoaderPrincipal,
    45                           bool aChannelIsSync, bool aForceToXML,
    46                           nsIDOMDocument** aResult);
    48     NS_FORWARD_NSISTREAMLISTENER(mListener->)
    49     NS_DECL_NSIREQUESTOBSERVER
    51     NS_DECL_NSICHANNELEVENTSINK
    53     NS_DECL_NSIINTERFACEREQUESTOR
    55 private:
    56     nsresult PushAsyncStream(nsIStreamListener* aListener);
    57     nsresult PushSyncStream(nsIStreamListener* aListener);
    59     nsCOMPtr<nsIChannel> mChannel;
    60     nsCOMPtr<nsIStreamListener> mListener;
    61     bool mLoading;
    62     nsresult mAsyncLoadStatus;
    63 };
    65 class nsForceXMLListener : public nsIStreamListener
    66 {
    67 public:
    68     nsForceXMLListener(nsIStreamListener* aListener);
    69     virtual ~nsForceXMLListener();
    71     NS_DECL_ISUPPORTS
    72     NS_FORWARD_NSISTREAMLISTENER(mListener->)
    73     NS_DECL_NSIREQUESTOBSERVER
    75 private:
    76     nsCOMPtr<nsIStreamListener> mListener;
    77 };
    79 nsForceXMLListener::nsForceXMLListener(nsIStreamListener* aListener)
    80     : mListener(aListener)
    81 {
    82 }
    84 nsForceXMLListener::~nsForceXMLListener()
    85 {
    86 }
    88 NS_IMPL_ISUPPORTS(nsForceXMLListener,
    89                   nsIStreamListener,
    90                   nsIRequestObserver)
    92 NS_IMETHODIMP
    93 nsForceXMLListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
    94 {
    95     nsresult status;
    96     aRequest->GetStatus(&status);
    97     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
    98     if (channel && NS_SUCCEEDED(status)) {
    99       channel->SetContentType(NS_LITERAL_CSTRING("text/xml"));
   100     }
   102     return mListener->OnStartRequest(aRequest, aContext);
   103 }
   105 NS_IMETHODIMP
   106 nsForceXMLListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
   107                                   nsresult aStatusCode)
   108 {
   109     return mListener->OnStopRequest(aRequest, aContext, aStatusCode);
   110 }
   112 nsSyncLoader::~nsSyncLoader()
   113 {
   114     if (mLoading && mChannel) {
   115         mChannel->Cancel(NS_BINDING_ABORTED);
   116     }
   117 }
   119 NS_IMPL_ISUPPORTS(nsSyncLoader,
   120                   nsIStreamListener,
   121                   nsIRequestObserver,
   122                   nsIChannelEventSink,
   123                   nsIInterfaceRequestor,
   124                   nsISupportsWeakReference)
   126 nsresult
   127 nsSyncLoader::LoadDocument(nsIChannel* aChannel,
   128                            nsIPrincipal *aLoaderPrincipal,
   129                            bool aChannelIsSync,
   130                            bool aForceToXML,
   131                            nsIDOMDocument **aResult)
   132 {
   133     NS_ENSURE_ARG_POINTER(aResult);
   134     *aResult = nullptr;
   135     nsresult rv = NS_OK;
   137     nsCOMPtr<nsIURI> loaderUri;
   138     if (aLoaderPrincipal) {
   139         aLoaderPrincipal->GetURI(getter_AddRefs(loaderUri));
   140     }
   142     mChannel = aChannel;
   143     nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(mChannel);
   144     if (http) {
   145         http->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),     
   146                                NS_LITERAL_CSTRING("text/xml,application/xml,application/xhtml+xml,*/*;q=0.1"),
   147                                false);
   148         if (loaderUri) {
   149             http->SetReferrer(loaderUri);
   150         }
   151     }
   153     // Hook us up to listen to redirects and the like.
   154     // Do this before setting up the cross-site proxy since
   155     // that installs its own proxies.
   156     mChannel->SetNotificationCallbacks(this);
   158     // Get the loadgroup of the channel
   159     nsCOMPtr<nsILoadGroup> loadGroup;
   160     rv = aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
   161     NS_ENSURE_SUCCESS(rv, rv);
   163     // Create document
   164     nsCOMPtr<nsIDocument> document;
   165     rv = NS_NewXMLDocument(getter_AddRefs(document));
   166     NS_ENSURE_SUCCESS(rv, rv);
   168     // Start the document load. Do this before we attach the load listener
   169     // since we reset the document which drops all observers.
   170     nsCOMPtr<nsIStreamListener> listener;
   171     rv = document->StartDocumentLoad(kLoadAsData, mChannel, 
   172                                      loadGroup, nullptr, 
   173                                      getter_AddRefs(listener),
   174                                      true);
   175     NS_ENSURE_SUCCESS(rv, rv);
   177     if (aForceToXML) {
   178         nsCOMPtr<nsIStreamListener> forceListener =
   179             new nsForceXMLListener(listener);
   180         listener.swap(forceListener);
   181     }
   183     if (aLoaderPrincipal) {
   184         nsRefPtr<nsCORSListenerProxy> corsListener =
   185           new nsCORSListenerProxy(listener, aLoaderPrincipal, false);
   186         rv = corsListener->Init(mChannel);
   187         NS_ENSURE_SUCCESS(rv, rv);
   188         listener = corsListener;
   189     }
   191     if (aChannelIsSync) {
   192         rv = PushSyncStream(listener);
   193     }
   194     else {
   195         rv = PushAsyncStream(listener);
   196     }
   198     http = do_QueryInterface(mChannel);
   199     if (NS_SUCCEEDED(rv) && http) {
   200         bool succeeded;
   201         if (NS_FAILED(http->GetRequestSucceeded(&succeeded)) || !succeeded) {
   202             rv = NS_ERROR_FAILURE;
   203         }
   204     }
   205     mChannel = nullptr;
   207     // check that the load succeeded
   208     NS_ENSURE_SUCCESS(rv, rv);
   210     NS_ENSURE_TRUE(document->GetRootElement(), NS_ERROR_FAILURE);
   212     return CallQueryInterface(document, aResult);
   213 }
   215 nsresult
   216 nsSyncLoader::PushAsyncStream(nsIStreamListener* aListener)
   217 {
   218     mListener = aListener;
   220     mAsyncLoadStatus = NS_OK;
   222     // Start reading from the channel
   223     nsresult rv = mChannel->AsyncOpen(this, nullptr);
   225     if (NS_SUCCEEDED(rv)) {
   226         // process events until we're finished.
   227         mLoading = true;
   228         nsIThread *thread = NS_GetCurrentThread();
   229         while (mLoading && NS_SUCCEEDED(rv)) {
   230             bool processedEvent; 
   231             rv = thread->ProcessNextEvent(true, &processedEvent);
   232             if (NS_SUCCEEDED(rv) && !processedEvent)
   233                 rv = NS_ERROR_UNEXPECTED;
   234         }
   235     }
   237     mListener = nullptr;
   239     NS_ENSURE_SUCCESS(rv, rv);
   241     // Note that if AsyncOpen failed that's ok -- the only caller of
   242     // this method nulls out mChannel immediately after we return.
   244     return mAsyncLoadStatus;
   245 }
   247 nsresult
   248 nsSyncLoader::PushSyncStream(nsIStreamListener* aListener)
   249 {
   250     nsCOMPtr<nsIInputStream> in;
   251     nsresult rv = mChannel->Open(getter_AddRefs(in));
   252     NS_ENSURE_SUCCESS(rv, rv);
   254     mLoading = true;
   255     rv = nsSyncLoadService::PushSyncStreamToListener(in, aListener, mChannel);
   256     mLoading = false;
   258     return rv;
   259 }
   261 NS_IMETHODIMP
   262 nsSyncLoader::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
   263 {
   264     return mListener->OnStartRequest(aRequest, aContext);
   265 }
   267 NS_IMETHODIMP
   268 nsSyncLoader::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
   269                             nsresult aStatusCode)
   270 {
   271     if (NS_SUCCEEDED(mAsyncLoadStatus) && NS_FAILED(aStatusCode)) {
   272         mAsyncLoadStatus = aStatusCode;
   273     }
   274     nsresult rv = mListener->OnStopRequest(aRequest, aContext, aStatusCode);
   275     if (NS_SUCCEEDED(mAsyncLoadStatus) && NS_FAILED(rv)) {
   276         mAsyncLoadStatus = rv;
   277     }
   278     mLoading = false;
   280     return rv;
   281 }
   283 NS_IMETHODIMP
   284 nsSyncLoader::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
   285                                      nsIChannel *aNewChannel,
   286                                      uint32_t aFlags,
   287                                      nsIAsyncVerifyRedirectCallback *callback)
   288 {
   289     NS_PRECONDITION(aNewChannel, "Redirecting to null channel?");
   291     mChannel = aNewChannel;
   293     callback->OnRedirectVerifyCallback(NS_OK);
   294     return NS_OK;
   295 }
   297 NS_IMETHODIMP
   298 nsSyncLoader::GetInterface(const nsIID & aIID,
   299                            void **aResult)
   300 {
   301     return QueryInterface(aIID, aResult);
   302 }
   304 /* static */
   305 nsresult
   306 nsSyncLoadService::LoadDocument(nsIURI *aURI, nsIPrincipal *aLoaderPrincipal,
   307                                 nsILoadGroup *aLoadGroup, bool aForceToXML,
   308                                 nsIDOMDocument** aResult)
   309 {
   310     nsCOMPtr<nsIChannel> channel;
   311     nsresult rv = NS_NewChannel(getter_AddRefs(channel), aURI, nullptr,
   312                                 aLoadGroup);
   313     NS_ENSURE_SUCCESS(rv, rv);
   315     if (!aForceToXML) {
   316         channel->SetContentType(NS_LITERAL_CSTRING("text/xml"));
   317     }
   319     bool isChrome = false, isResource = false;
   320     bool isSync = (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) &&
   321                      isChrome) ||
   322                     (NS_SUCCEEDED(aURI->SchemeIs("resource", &isResource)) &&
   323                      isResource);
   325     nsRefPtr<nsSyncLoader> loader = new nsSyncLoader();
   326     return loader->LoadDocument(channel, aLoaderPrincipal, isSync,
   327                                 aForceToXML, aResult);
   329 }
   331 /* static */
   332 nsresult
   333 nsSyncLoadService::PushSyncStreamToListener(nsIInputStream* aIn,
   334                                             nsIStreamListener* aListener,
   335                                             nsIChannel* aChannel)
   336 {
   337     // Set up buffering stream
   338     nsresult rv;
   339     nsCOMPtr<nsIInputStream> bufferedStream;
   340     if (!NS_InputStreamIsBuffered(aIn)) {
   341         int64_t chunkSize;
   342         rv = aChannel->GetContentLength(&chunkSize);
   343         if (NS_FAILED(rv) || chunkSize < 1) {
   344             chunkSize = 4096;
   345         }
   346         chunkSize = std::min(int64_t(UINT16_MAX), chunkSize);
   348         rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), aIn,
   349                                        chunkSize);
   350         NS_ENSURE_SUCCESS(rv, rv);
   352         aIn = bufferedStream;
   353     }
   355     // Load
   356     rv = aListener->OnStartRequest(aChannel, nullptr);
   357     if (NS_SUCCEEDED(rv)) {
   358         uint64_t sourceOffset = 0;
   359         while (1) {
   360             uint64_t readCount = 0;
   361             rv = aIn->Available(&readCount);
   362             if (NS_FAILED(rv) || !readCount) {
   363                 if (rv == NS_BASE_STREAM_CLOSED) {
   364                     // End of file, but not an error
   365                     rv = NS_OK;
   366                 }
   367                 break;
   368             }
   370             if (readCount > UINT32_MAX)
   371                 readCount = UINT32_MAX;
   373             rv = aListener->OnDataAvailable(aChannel, nullptr, aIn,
   374                                             (uint32_t)std::min(sourceOffset, (uint64_t)UINT32_MAX),
   375                                             (uint32_t)readCount);
   376             if (NS_FAILED(rv)) {
   377                 break;
   378             }
   379             sourceOffset += readCount;
   380         }
   381     }
   382     if (NS_FAILED(rv)) {
   383         aChannel->Cancel(rv);
   384     }
   385     aListener->OnStopRequest(aChannel, nullptr, rv);
   387     return rv;
   388 }

mercurial