1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/nsSyncLoadService.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,388 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 + * A service that provides methods for synchronously loading a DOM in various ways. 1.11 + */ 1.12 + 1.13 +#include "nsSyncLoadService.h" 1.14 +#include "nsCOMPtr.h" 1.15 +#include "nsIChannel.h" 1.16 +#include "nsIChannelEventSink.h" 1.17 +#include "nsIAsyncVerifyRedirectCallback.h" 1.18 +#include "nsIInterfaceRequestor.h" 1.19 +#include "nsString.h" 1.20 +#include "nsWeakReference.h" 1.21 +#include "nsIDocument.h" 1.22 +#include "nsIDOMDocument.h" 1.23 +#include "nsIPrincipal.h" 1.24 +#include "nsContentUtils.h" // for kLoadAsData 1.25 +#include "nsThreadUtils.h" 1.26 +#include "nsNetUtil.h" 1.27 +#include "nsAutoPtr.h" 1.28 +#include "nsStreamUtils.h" 1.29 +#include "nsCrossSiteListenerProxy.h" 1.30 +#include <algorithm> 1.31 + 1.32 +/** 1.33 + * This class manages loading a single XML document 1.34 + */ 1.35 + 1.36 +class nsSyncLoader : public nsIStreamListener, 1.37 + public nsIChannelEventSink, 1.38 + public nsIInterfaceRequestor, 1.39 + public nsSupportsWeakReference 1.40 +{ 1.41 +public: 1.42 + nsSyncLoader() : mLoading(false) {} 1.43 + virtual ~nsSyncLoader(); 1.44 + 1.45 + NS_DECL_ISUPPORTS 1.46 + 1.47 + nsresult LoadDocument(nsIChannel* aChannel, nsIPrincipal *aLoaderPrincipal, 1.48 + bool aChannelIsSync, bool aForceToXML, 1.49 + nsIDOMDocument** aResult); 1.50 + 1.51 + NS_FORWARD_NSISTREAMLISTENER(mListener->) 1.52 + NS_DECL_NSIREQUESTOBSERVER 1.53 + 1.54 + NS_DECL_NSICHANNELEVENTSINK 1.55 + 1.56 + NS_DECL_NSIINTERFACEREQUESTOR 1.57 + 1.58 +private: 1.59 + nsresult PushAsyncStream(nsIStreamListener* aListener); 1.60 + nsresult PushSyncStream(nsIStreamListener* aListener); 1.61 + 1.62 + nsCOMPtr<nsIChannel> mChannel; 1.63 + nsCOMPtr<nsIStreamListener> mListener; 1.64 + bool mLoading; 1.65 + nsresult mAsyncLoadStatus; 1.66 +}; 1.67 + 1.68 +class nsForceXMLListener : public nsIStreamListener 1.69 +{ 1.70 +public: 1.71 + nsForceXMLListener(nsIStreamListener* aListener); 1.72 + virtual ~nsForceXMLListener(); 1.73 + 1.74 + NS_DECL_ISUPPORTS 1.75 + NS_FORWARD_NSISTREAMLISTENER(mListener->) 1.76 + NS_DECL_NSIREQUESTOBSERVER 1.77 + 1.78 +private: 1.79 + nsCOMPtr<nsIStreamListener> mListener; 1.80 +}; 1.81 + 1.82 +nsForceXMLListener::nsForceXMLListener(nsIStreamListener* aListener) 1.83 + : mListener(aListener) 1.84 +{ 1.85 +} 1.86 + 1.87 +nsForceXMLListener::~nsForceXMLListener() 1.88 +{ 1.89 +} 1.90 + 1.91 +NS_IMPL_ISUPPORTS(nsForceXMLListener, 1.92 + nsIStreamListener, 1.93 + nsIRequestObserver) 1.94 + 1.95 +NS_IMETHODIMP 1.96 +nsForceXMLListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) 1.97 +{ 1.98 + nsresult status; 1.99 + aRequest->GetStatus(&status); 1.100 + nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); 1.101 + if (channel && NS_SUCCEEDED(status)) { 1.102 + channel->SetContentType(NS_LITERAL_CSTRING("text/xml")); 1.103 + } 1.104 + 1.105 + return mListener->OnStartRequest(aRequest, aContext); 1.106 +} 1.107 + 1.108 +NS_IMETHODIMP 1.109 +nsForceXMLListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext, 1.110 + nsresult aStatusCode) 1.111 +{ 1.112 + return mListener->OnStopRequest(aRequest, aContext, aStatusCode); 1.113 +} 1.114 + 1.115 +nsSyncLoader::~nsSyncLoader() 1.116 +{ 1.117 + if (mLoading && mChannel) { 1.118 + mChannel->Cancel(NS_BINDING_ABORTED); 1.119 + } 1.120 +} 1.121 + 1.122 +NS_IMPL_ISUPPORTS(nsSyncLoader, 1.123 + nsIStreamListener, 1.124 + nsIRequestObserver, 1.125 + nsIChannelEventSink, 1.126 + nsIInterfaceRequestor, 1.127 + nsISupportsWeakReference) 1.128 + 1.129 +nsresult 1.130 +nsSyncLoader::LoadDocument(nsIChannel* aChannel, 1.131 + nsIPrincipal *aLoaderPrincipal, 1.132 + bool aChannelIsSync, 1.133 + bool aForceToXML, 1.134 + nsIDOMDocument **aResult) 1.135 +{ 1.136 + NS_ENSURE_ARG_POINTER(aResult); 1.137 + *aResult = nullptr; 1.138 + nsresult rv = NS_OK; 1.139 + 1.140 + nsCOMPtr<nsIURI> loaderUri; 1.141 + if (aLoaderPrincipal) { 1.142 + aLoaderPrincipal->GetURI(getter_AddRefs(loaderUri)); 1.143 + } 1.144 + 1.145 + mChannel = aChannel; 1.146 + nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(mChannel); 1.147 + if (http) { 1.148 + http->SetRequestHeader(NS_LITERAL_CSTRING("Accept"), 1.149 + NS_LITERAL_CSTRING("text/xml,application/xml,application/xhtml+xml,*/*;q=0.1"), 1.150 + false); 1.151 + if (loaderUri) { 1.152 + http->SetReferrer(loaderUri); 1.153 + } 1.154 + } 1.155 + 1.156 + // Hook us up to listen to redirects and the like. 1.157 + // Do this before setting up the cross-site proxy since 1.158 + // that installs its own proxies. 1.159 + mChannel->SetNotificationCallbacks(this); 1.160 + 1.161 + // Get the loadgroup of the channel 1.162 + nsCOMPtr<nsILoadGroup> loadGroup; 1.163 + rv = aChannel->GetLoadGroup(getter_AddRefs(loadGroup)); 1.164 + NS_ENSURE_SUCCESS(rv, rv); 1.165 + 1.166 + // Create document 1.167 + nsCOMPtr<nsIDocument> document; 1.168 + rv = NS_NewXMLDocument(getter_AddRefs(document)); 1.169 + NS_ENSURE_SUCCESS(rv, rv); 1.170 + 1.171 + // Start the document load. Do this before we attach the load listener 1.172 + // since we reset the document which drops all observers. 1.173 + nsCOMPtr<nsIStreamListener> listener; 1.174 + rv = document->StartDocumentLoad(kLoadAsData, mChannel, 1.175 + loadGroup, nullptr, 1.176 + getter_AddRefs(listener), 1.177 + true); 1.178 + NS_ENSURE_SUCCESS(rv, rv); 1.179 + 1.180 + if (aForceToXML) { 1.181 + nsCOMPtr<nsIStreamListener> forceListener = 1.182 + new nsForceXMLListener(listener); 1.183 + listener.swap(forceListener); 1.184 + } 1.185 + 1.186 + if (aLoaderPrincipal) { 1.187 + nsRefPtr<nsCORSListenerProxy> corsListener = 1.188 + new nsCORSListenerProxy(listener, aLoaderPrincipal, false); 1.189 + rv = corsListener->Init(mChannel); 1.190 + NS_ENSURE_SUCCESS(rv, rv); 1.191 + listener = corsListener; 1.192 + } 1.193 + 1.194 + if (aChannelIsSync) { 1.195 + rv = PushSyncStream(listener); 1.196 + } 1.197 + else { 1.198 + rv = PushAsyncStream(listener); 1.199 + } 1.200 + 1.201 + http = do_QueryInterface(mChannel); 1.202 + if (NS_SUCCEEDED(rv) && http) { 1.203 + bool succeeded; 1.204 + if (NS_FAILED(http->GetRequestSucceeded(&succeeded)) || !succeeded) { 1.205 + rv = NS_ERROR_FAILURE; 1.206 + } 1.207 + } 1.208 + mChannel = nullptr; 1.209 + 1.210 + // check that the load succeeded 1.211 + NS_ENSURE_SUCCESS(rv, rv); 1.212 + 1.213 + NS_ENSURE_TRUE(document->GetRootElement(), NS_ERROR_FAILURE); 1.214 + 1.215 + return CallQueryInterface(document, aResult); 1.216 +} 1.217 + 1.218 +nsresult 1.219 +nsSyncLoader::PushAsyncStream(nsIStreamListener* aListener) 1.220 +{ 1.221 + mListener = aListener; 1.222 + 1.223 + mAsyncLoadStatus = NS_OK; 1.224 + 1.225 + // Start reading from the channel 1.226 + nsresult rv = mChannel->AsyncOpen(this, nullptr); 1.227 + 1.228 + if (NS_SUCCEEDED(rv)) { 1.229 + // process events until we're finished. 1.230 + mLoading = true; 1.231 + nsIThread *thread = NS_GetCurrentThread(); 1.232 + while (mLoading && NS_SUCCEEDED(rv)) { 1.233 + bool processedEvent; 1.234 + rv = thread->ProcessNextEvent(true, &processedEvent); 1.235 + if (NS_SUCCEEDED(rv) && !processedEvent) 1.236 + rv = NS_ERROR_UNEXPECTED; 1.237 + } 1.238 + } 1.239 + 1.240 + mListener = nullptr; 1.241 + 1.242 + NS_ENSURE_SUCCESS(rv, rv); 1.243 + 1.244 + // Note that if AsyncOpen failed that's ok -- the only caller of 1.245 + // this method nulls out mChannel immediately after we return. 1.246 + 1.247 + return mAsyncLoadStatus; 1.248 +} 1.249 + 1.250 +nsresult 1.251 +nsSyncLoader::PushSyncStream(nsIStreamListener* aListener) 1.252 +{ 1.253 + nsCOMPtr<nsIInputStream> in; 1.254 + nsresult rv = mChannel->Open(getter_AddRefs(in)); 1.255 + NS_ENSURE_SUCCESS(rv, rv); 1.256 + 1.257 + mLoading = true; 1.258 + rv = nsSyncLoadService::PushSyncStreamToListener(in, aListener, mChannel); 1.259 + mLoading = false; 1.260 + 1.261 + return rv; 1.262 +} 1.263 + 1.264 +NS_IMETHODIMP 1.265 +nsSyncLoader::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) 1.266 +{ 1.267 + return mListener->OnStartRequest(aRequest, aContext); 1.268 +} 1.269 + 1.270 +NS_IMETHODIMP 1.271 +nsSyncLoader::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext, 1.272 + nsresult aStatusCode) 1.273 +{ 1.274 + if (NS_SUCCEEDED(mAsyncLoadStatus) && NS_FAILED(aStatusCode)) { 1.275 + mAsyncLoadStatus = aStatusCode; 1.276 + } 1.277 + nsresult rv = mListener->OnStopRequest(aRequest, aContext, aStatusCode); 1.278 + if (NS_SUCCEEDED(mAsyncLoadStatus) && NS_FAILED(rv)) { 1.279 + mAsyncLoadStatus = rv; 1.280 + } 1.281 + mLoading = false; 1.282 + 1.283 + return rv; 1.284 +} 1.285 + 1.286 +NS_IMETHODIMP 1.287 +nsSyncLoader::AsyncOnChannelRedirect(nsIChannel *aOldChannel, 1.288 + nsIChannel *aNewChannel, 1.289 + uint32_t aFlags, 1.290 + nsIAsyncVerifyRedirectCallback *callback) 1.291 +{ 1.292 + NS_PRECONDITION(aNewChannel, "Redirecting to null channel?"); 1.293 + 1.294 + mChannel = aNewChannel; 1.295 + 1.296 + callback->OnRedirectVerifyCallback(NS_OK); 1.297 + return NS_OK; 1.298 +} 1.299 + 1.300 +NS_IMETHODIMP 1.301 +nsSyncLoader::GetInterface(const nsIID & aIID, 1.302 + void **aResult) 1.303 +{ 1.304 + return QueryInterface(aIID, aResult); 1.305 +} 1.306 + 1.307 +/* static */ 1.308 +nsresult 1.309 +nsSyncLoadService::LoadDocument(nsIURI *aURI, nsIPrincipal *aLoaderPrincipal, 1.310 + nsILoadGroup *aLoadGroup, bool aForceToXML, 1.311 + nsIDOMDocument** aResult) 1.312 +{ 1.313 + nsCOMPtr<nsIChannel> channel; 1.314 + nsresult rv = NS_NewChannel(getter_AddRefs(channel), aURI, nullptr, 1.315 + aLoadGroup); 1.316 + NS_ENSURE_SUCCESS(rv, rv); 1.317 + 1.318 + if (!aForceToXML) { 1.319 + channel->SetContentType(NS_LITERAL_CSTRING("text/xml")); 1.320 + } 1.321 + 1.322 + bool isChrome = false, isResource = false; 1.323 + bool isSync = (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && 1.324 + isChrome) || 1.325 + (NS_SUCCEEDED(aURI->SchemeIs("resource", &isResource)) && 1.326 + isResource); 1.327 + 1.328 + nsRefPtr<nsSyncLoader> loader = new nsSyncLoader(); 1.329 + return loader->LoadDocument(channel, aLoaderPrincipal, isSync, 1.330 + aForceToXML, aResult); 1.331 + 1.332 +} 1.333 + 1.334 +/* static */ 1.335 +nsresult 1.336 +nsSyncLoadService::PushSyncStreamToListener(nsIInputStream* aIn, 1.337 + nsIStreamListener* aListener, 1.338 + nsIChannel* aChannel) 1.339 +{ 1.340 + // Set up buffering stream 1.341 + nsresult rv; 1.342 + nsCOMPtr<nsIInputStream> bufferedStream; 1.343 + if (!NS_InputStreamIsBuffered(aIn)) { 1.344 + int64_t chunkSize; 1.345 + rv = aChannel->GetContentLength(&chunkSize); 1.346 + if (NS_FAILED(rv) || chunkSize < 1) { 1.347 + chunkSize = 4096; 1.348 + } 1.349 + chunkSize = std::min(int64_t(UINT16_MAX), chunkSize); 1.350 + 1.351 + rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), aIn, 1.352 + chunkSize); 1.353 + NS_ENSURE_SUCCESS(rv, rv); 1.354 + 1.355 + aIn = bufferedStream; 1.356 + } 1.357 + 1.358 + // Load 1.359 + rv = aListener->OnStartRequest(aChannel, nullptr); 1.360 + if (NS_SUCCEEDED(rv)) { 1.361 + uint64_t sourceOffset = 0; 1.362 + while (1) { 1.363 + uint64_t readCount = 0; 1.364 + rv = aIn->Available(&readCount); 1.365 + if (NS_FAILED(rv) || !readCount) { 1.366 + if (rv == NS_BASE_STREAM_CLOSED) { 1.367 + // End of file, but not an error 1.368 + rv = NS_OK; 1.369 + } 1.370 + break; 1.371 + } 1.372 + 1.373 + if (readCount > UINT32_MAX) 1.374 + readCount = UINT32_MAX; 1.375 + 1.376 + rv = aListener->OnDataAvailable(aChannel, nullptr, aIn, 1.377 + (uint32_t)std::min(sourceOffset, (uint64_t)UINT32_MAX), 1.378 + (uint32_t)readCount); 1.379 + if (NS_FAILED(rv)) { 1.380 + break; 1.381 + } 1.382 + sourceOffset += readCount; 1.383 + } 1.384 + } 1.385 + if (NS_FAILED(rv)) { 1.386 + aChannel->Cancel(rv); 1.387 + } 1.388 + aListener->OnStopRequest(aChannel, nullptr, rv); 1.389 + 1.390 + return rv; 1.391 +}