tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsSyncLoadService.cpp (11126B)


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