LCOV - code coverage report
Current view: top level - dom/base - nsSyncLoadService.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 28 153 18.3 %
Date: 2017-07-14 16:53:18 Functions: 1 25 4.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13