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.

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

mercurial