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/. */
7 * A service that provides methods for synchronously loading a DOM in various ways.
10 #include "nsSyncLoadService.h"
12 #include "nsIChannel.h"
13 #include "nsIChannelEventSink.h"
14 #include "nsIAsyncVerifyRedirectCallback.h"
15 #include "nsIInterfaceRequestor.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"
30 * This class manages loading a single XML document
33 class nsSyncLoader
: public nsIStreamListener
,
34 public nsIChannelEventSink
,
35 public nsIInterfaceRequestor
,
36 public nsSupportsWeakReference
39 nsSyncLoader() : mLoading(false) {}
40 virtual ~nsSyncLoader();
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
56 nsresult
PushAsyncStream(nsIStreamListener
* aListener
);
57 nsresult
PushSyncStream(nsIStreamListener
* aListener
);
59 nsCOMPtr
<nsIChannel
> mChannel
;
60 nsCOMPtr
<nsIStreamListener
> mListener
;
62 nsresult mAsyncLoadStatus
;
65 class nsForceXMLListener
: public nsIStreamListener
68 nsForceXMLListener(nsIStreamListener
* aListener
);
69 virtual ~nsForceXMLListener();
72 NS_FORWARD_NSISTREAMLISTENER(mListener
->)
73 NS_DECL_NSIREQUESTOBSERVER
76 nsCOMPtr
<nsIStreamListener
> mListener
;
79 nsForceXMLListener::nsForceXMLListener(nsIStreamListener
* aListener
)
80 : mListener(aListener
)
84 nsForceXMLListener::~nsForceXMLListener()
88 NS_IMPL_ISUPPORTS2(nsForceXMLListener
,
93 nsForceXMLListener::OnStartRequest(nsIRequest
*aRequest
, nsISupports
*aContext
)
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
);
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
,
123 nsIInterfaceRequestor
,
124 nsISupportsWeakReference
)
127 nsSyncLoader::LoadDocument(nsIChannel
* aChannel
,
128 nsIPrincipal
*aLoaderPrincipal
,
131 nsIDOMDocument
**aResult
)
133 NS_ENSURE_ARG_POINTER(aResult
);
137 nsCOMPtr
<nsIURI
> loaderUri
;
138 if (aLoaderPrincipal
) {
139 aLoaderPrincipal
->GetURI(getter_AddRefs(loaderUri
));
143 nsCOMPtr
<nsIHttpChannel
> http
= do_QueryInterface(mChannel
);
145 http
->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
146 NS_LITERAL_CSTRING("text/xml,application/xml,application/xhtml+xml,*/*;q=0.1"),
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
);
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
,
173 getter_AddRefs(listener
),
175 NS_ENSURE_SUCCESS(rv
, rv
);
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
);
195 rv
= PushAsyncStream(listener
);
198 http
= do_QueryInterface(mChannel
);
199 if (NS_SUCCEEDED(rv
) && http
) {
201 if (NS_FAILED(http
->GetRequestSucceeded(&succeeded
)) || !succeeded
) {
202 rv
= NS_ERROR_FAILURE
;
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
);
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.
228 nsIThread
*thread
= NS_GetCurrentThread();
229 while (mLoading
&& NS_SUCCEEDED(rv
)) {
231 rv
= thread
->ProcessNextEvent(true, &processedEvent
);
232 if (NS_SUCCEEDED(rv
) && !processedEvent
)
233 rv
= NS_ERROR_UNEXPECTED
;
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
;
248 nsSyncLoader::PushSyncStream(nsIStreamListener
* aListener
)
250 nsCOMPtr
<nsIInputStream
> in
;
251 nsresult rv
= mChannel
->Open(getter_AddRefs(in
));
252 NS_ENSURE_SUCCESS(rv
, rv
);
255 rv
= nsSyncLoadService::PushSyncStreamToListener(in
, aListener
, mChannel
);
262 nsSyncLoader::OnStartRequest(nsIRequest
*aRequest
, nsISupports
*aContext
)
264 return mListener
->OnStartRequest(aRequest
, aContext
);
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
;
284 nsSyncLoader::AsyncOnChannelRedirect(nsIChannel
*aOldChannel
,
285 nsIChannel
*aNewChannel
,
287 nsIAsyncVerifyRedirectCallback
*callback
)
289 NS_PRECONDITION(aNewChannel
, "Redirecting to null channel?");
291 mChannel
= aNewChannel
;
293 callback
->OnRedirectVerifyCallback(NS_OK
);
298 nsSyncLoader::GetInterface(const nsIID
& aIID
,
301 return QueryInterface(aIID
, aResult
);
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,
313 NS_ENSURE_SUCCESS(rv
, rv
);
316 channel
->SetContentType(NS_LITERAL_CSTRING("text/xml"));
319 bool isChrome
= false, isResource
= false;
320 bool isSync
= (NS_SUCCEEDED(aURI
->SchemeIs("chrome", &isChrome
)) &&
322 (NS_SUCCEEDED(aURI
->SchemeIs("resource", &isResource
)) &&
325 nsRefPtr
<nsSyncLoader
> loader
= new nsSyncLoader();
326 return loader
->LoadDocument(channel
, aLoaderPrincipal
, isSync
,
327 aForceToXML
, aResult
);
333 nsSyncLoadService::PushSyncStreamToListener(nsIInputStream
* aIn
,
334 nsIStreamListener
* aListener
,
335 nsIChannel
* aChannel
)
337 // Set up buffering stream
339 nsCOMPtr
<nsIInputStream
> bufferedStream
;
340 if (!NS_InputStreamIsBuffered(aIn
)) {
342 rv
= aChannel
->GetContentLength(&chunkSize
);
343 if (NS_FAILED(rv
) || chunkSize
< 1) {
346 chunkSize
= std::min(int64_t(UINT16_MAX
), chunkSize
);
348 rv
= NS_NewBufferedInputStream(getter_AddRefs(bufferedStream
), aIn
,
350 NS_ENSURE_SUCCESS(rv
, rv
);
352 aIn
= bufferedStream
;
356 rv
= aListener
->OnStartRequest(aChannel
, nullptr);
357 if (NS_SUCCEEDED(rv
)) {
358 uint64_t sourceOffset
= 0;
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
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
);
379 sourceOffset
+= readCount
;
383 aChannel
->Cancel(rv
);
385 aListener
->OnStopRequest(aChannel
, nullptr, rv
);