Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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 | } |