Backed out 35 changesets (bug 941158, bug 972518, bug 959520, bug 986063, bug 948895...
[gecko.git] / content / base / src / nsSyncLoadService.cpp
blob9fb52f4535713a47c5db79d57f18b4dcbebf5aa4
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/. */
6 /*
7 * A service that provides methods for synchronously loading a DOM in various ways.
8 */
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 "nsString.h"
17 #include "nsWeakReference.h"
18 #include "nsIDocument.h"
19 #include "nsIDOMDocument.h"
20 #include "nsIPrincipal.h"
21 #include "nsContentUtils.h" // for kLoadAsData
22 #include "nsThreadUtils.h"
23 #include "nsNetUtil.h"
24 #include "nsAutoPtr.h"
25 #include "nsStreamUtils.h"
26 #include "nsCrossSiteListenerProxy.h"
27 #include <algorithm>
29 /**
30 * This class manages loading a single XML document
33 class nsSyncLoader : public nsIStreamListener,
34 public nsIChannelEventSink,
35 public nsIInterfaceRequestor,
36 public nsSupportsWeakReference
38 public:
39 nsSyncLoader() : mLoading(false) {}
40 virtual ~nsSyncLoader();
42 NS_DECL_ISUPPORTS
44 nsresult LoadDocument(nsIChannel* aChannel, nsIPrincipal *aLoaderPrincipal,
45 bool aChannelIsSync, bool aForceToXML,
46 nsIDOMDocument** aResult);
48 NS_FORWARD_NSISTREAMLISTENER(mListener->)
49 NS_DECL_NSIREQUESTOBSERVER
51 NS_DECL_NSICHANNELEVENTSINK
53 NS_DECL_NSIINTERFACEREQUESTOR
55 private:
56 nsresult PushAsyncStream(nsIStreamListener* aListener);
57 nsresult PushSyncStream(nsIStreamListener* aListener);
59 nsCOMPtr<nsIChannel> mChannel;
60 nsCOMPtr<nsIStreamListener> mListener;
61 bool mLoading;
62 nsresult mAsyncLoadStatus;
65 class nsForceXMLListener : public nsIStreamListener
67 public:
68 nsForceXMLListener(nsIStreamListener* aListener);
69 virtual ~nsForceXMLListener();
71 NS_DECL_ISUPPORTS
72 NS_FORWARD_NSISTREAMLISTENER(mListener->)
73 NS_DECL_NSIREQUESTOBSERVER
75 private:
76 nsCOMPtr<nsIStreamListener> mListener;
79 nsForceXMLListener::nsForceXMLListener(nsIStreamListener* aListener)
80 : mListener(aListener)
84 nsForceXMLListener::~nsForceXMLListener()
88 NS_IMPL_ISUPPORTS2(nsForceXMLListener,
89 nsIStreamListener,
90 nsIRequestObserver)
92 NS_IMETHODIMP
93 nsForceXMLListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
95 nsresult status;
96 aRequest->GetStatus(&status);
97 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
98 if (channel && NS_SUCCEEDED(status)) {
99 channel->SetContentType(NS_LITERAL_CSTRING("text/xml"));
102 return mListener->OnStartRequest(aRequest, aContext);
105 NS_IMETHODIMP
106 nsForceXMLListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
107 nsresult aStatusCode)
109 return mListener->OnStopRequest(aRequest, aContext, aStatusCode);
112 nsSyncLoader::~nsSyncLoader()
114 if (mLoading && mChannel) {
115 mChannel->Cancel(NS_BINDING_ABORTED);
119 NS_IMPL_ISUPPORTS5(nsSyncLoader,
120 nsIStreamListener,
121 nsIRequestObserver,
122 nsIChannelEventSink,
123 nsIInterfaceRequestor,
124 nsISupportsWeakReference)
126 nsresult
127 nsSyncLoader::LoadDocument(nsIChannel* aChannel,
128 nsIPrincipal *aLoaderPrincipal,
129 bool aChannelIsSync,
130 bool aForceToXML,
131 nsIDOMDocument **aResult)
133 NS_ENSURE_ARG_POINTER(aResult);
134 *aResult = nullptr;
135 nsresult rv = NS_OK;
137 nsCOMPtr<nsIURI> loaderUri;
138 if (aLoaderPrincipal) {
139 aLoaderPrincipal->GetURI(getter_AddRefs(loaderUri));
142 mChannel = aChannel;
143 nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(mChannel);
144 if (http) {
145 http->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
146 NS_LITERAL_CSTRING("text/xml,application/xml,application/xhtml+xml,*/*;q=0.1"),
147 false);
148 if (loaderUri) {
149 http->SetReferrer(loaderUri);
153 // Hook us up to listen to redirects and the like.
154 // Do this before setting up the cross-site proxy since
155 // that installs its own proxies.
156 mChannel->SetNotificationCallbacks(this);
158 // Get the loadgroup of the channel
159 nsCOMPtr<nsILoadGroup> loadGroup;
160 rv = aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
161 NS_ENSURE_SUCCESS(rv, rv);
163 // Create document
164 nsCOMPtr<nsIDocument> document;
165 rv = NS_NewXMLDocument(getter_AddRefs(document));
166 NS_ENSURE_SUCCESS(rv, rv);
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,
172 loadGroup, nullptr,
173 getter_AddRefs(listener),
174 true);
175 NS_ENSURE_SUCCESS(rv, rv);
177 if (aForceToXML) {
178 nsCOMPtr<nsIStreamListener> forceListener =
179 new nsForceXMLListener(listener);
180 listener.swap(forceListener);
183 if (aLoaderPrincipal) {
184 nsRefPtr<nsCORSListenerProxy> corsListener =
185 new nsCORSListenerProxy(listener, aLoaderPrincipal, false);
186 rv = corsListener->Init(mChannel);
187 NS_ENSURE_SUCCESS(rv, rv);
188 listener = corsListener;
191 if (aChannelIsSync) {
192 rv = PushSyncStream(listener);
194 else {
195 rv = PushAsyncStream(listener);
198 http = do_QueryInterface(mChannel);
199 if (NS_SUCCEEDED(rv) && http) {
200 bool succeeded;
201 if (NS_FAILED(http->GetRequestSucceeded(&succeeded)) || !succeeded) {
202 rv = NS_ERROR_FAILURE;
205 mChannel = nullptr;
207 // check that the load succeeded
208 NS_ENSURE_SUCCESS(rv, rv);
210 NS_ENSURE_TRUE(document->GetRootElement(), NS_ERROR_FAILURE);
212 return CallQueryInterface(document, aResult);
215 nsresult
216 nsSyncLoader::PushAsyncStream(nsIStreamListener* aListener)
218 mListener = aListener;
220 mAsyncLoadStatus = NS_OK;
222 // Start reading from the channel
223 nsresult rv = mChannel->AsyncOpen(this, nullptr);
225 if (NS_SUCCEEDED(rv)) {
226 // process events until we're finished.
227 mLoading = true;
228 nsIThread *thread = NS_GetCurrentThread();
229 while (mLoading && NS_SUCCEEDED(rv)) {
230 bool processedEvent;
231 rv = thread->ProcessNextEvent(true, &processedEvent);
232 if (NS_SUCCEEDED(rv) && !processedEvent)
233 rv = NS_ERROR_UNEXPECTED;
237 mListener = nullptr;
239 NS_ENSURE_SUCCESS(rv, rv);
241 // Note that if AsyncOpen failed that's ok -- the only caller of
242 // this method nulls out mChannel immediately after we return.
244 return mAsyncLoadStatus;
247 nsresult
248 nsSyncLoader::PushSyncStream(nsIStreamListener* aListener)
250 nsCOMPtr<nsIInputStream> in;
251 nsresult rv = mChannel->Open(getter_AddRefs(in));
252 NS_ENSURE_SUCCESS(rv, rv);
254 mLoading = true;
255 rv = nsSyncLoadService::PushSyncStreamToListener(in, aListener, mChannel);
256 mLoading = false;
258 return rv;
261 NS_IMETHODIMP
262 nsSyncLoader::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
264 return mListener->OnStartRequest(aRequest, aContext);
267 NS_IMETHODIMP
268 nsSyncLoader::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
269 nsresult aStatusCode)
271 if (NS_SUCCEEDED(mAsyncLoadStatus) && NS_FAILED(aStatusCode)) {
272 mAsyncLoadStatus = aStatusCode;
274 nsresult rv = mListener->OnStopRequest(aRequest, aContext, aStatusCode);
275 if (NS_SUCCEEDED(mAsyncLoadStatus) && NS_FAILED(rv)) {
276 mAsyncLoadStatus = rv;
278 mLoading = false;
280 return rv;
283 NS_IMETHODIMP
284 nsSyncLoader::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
285 nsIChannel *aNewChannel,
286 uint32_t aFlags,
287 nsIAsyncVerifyRedirectCallback *callback)
289 NS_PRECONDITION(aNewChannel, "Redirecting to null channel?");
291 mChannel = aNewChannel;
293 callback->OnRedirectVerifyCallback(NS_OK);
294 return NS_OK;
297 NS_IMETHODIMP
298 nsSyncLoader::GetInterface(const nsIID & aIID,
299 void **aResult)
301 return QueryInterface(aIID, aResult);
304 /* static */
305 nsresult
306 nsSyncLoadService::LoadDocument(nsIURI *aURI, nsIPrincipal *aLoaderPrincipal,
307 nsILoadGroup *aLoadGroup, bool aForceToXML,
308 nsIDOMDocument** aResult)
310 nsCOMPtr<nsIChannel> channel;
311 nsresult rv = NS_NewChannel(getter_AddRefs(channel), aURI, nullptr,
312 aLoadGroup);
313 NS_ENSURE_SUCCESS(rv, rv);
315 if (!aForceToXML) {
316 channel->SetContentType(NS_LITERAL_CSTRING("text/xml"));
319 bool isChrome = false, isResource = false;
320 bool isSync = (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) &&
321 isChrome) ||
322 (NS_SUCCEEDED(aURI->SchemeIs("resource", &isResource)) &&
323 isResource);
325 nsRefPtr<nsSyncLoader> loader = new nsSyncLoader();
326 return loader->LoadDocument(channel, aLoaderPrincipal, isSync,
327 aForceToXML, aResult);
331 /* static */
332 nsresult
333 nsSyncLoadService::PushSyncStreamToListener(nsIInputStream* aIn,
334 nsIStreamListener* aListener,
335 nsIChannel* aChannel)
337 // Set up buffering stream
338 nsresult rv;
339 nsCOMPtr<nsIInputStream> bufferedStream;
340 if (!NS_InputStreamIsBuffered(aIn)) {
341 int64_t chunkSize;
342 rv = aChannel->GetContentLength(&chunkSize);
343 if (NS_FAILED(rv) || chunkSize < 1) {
344 chunkSize = 4096;
346 chunkSize = std::min(int64_t(UINT16_MAX), chunkSize);
348 rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), aIn,
349 chunkSize);
350 NS_ENSURE_SUCCESS(rv, rv);
352 aIn = bufferedStream;
355 // Load
356 rv = aListener->OnStartRequest(aChannel, nullptr);
357 if (NS_SUCCEEDED(rv)) {
358 uint64_t sourceOffset = 0;
359 while (1) {
360 uint64_t readCount = 0;
361 rv = aIn->Available(&readCount);
362 if (NS_FAILED(rv) || !readCount) {
363 if (rv == NS_BASE_STREAM_CLOSED) {
364 // End of file, but not an error
365 rv = NS_OK;
367 break;
370 if (readCount > UINT32_MAX)
371 readCount = UINT32_MAX;
373 rv = aListener->OnDataAvailable(aChannel, nullptr, aIn,
374 (uint32_t)std::min(sourceOffset, (uint64_t)UINT32_MAX),
375 (uint32_t)readCount);
376 if (NS_FAILED(rv)) {
377 break;
379 sourceOffset += readCount;
382 if (NS_FAILED(rv)) {
383 aChannel->Cancel(rv);
385 aListener->OnStopRequest(aChannel, nullptr, rv);
387 return rv;