Back to index

lightning-sunbird  0.9+nobinonly
nsSyncLoadService.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Peter Van der Beken <peterv@netscape.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsCOMPtr.h"
00040 #include "nsIChannel.h"
00041 #include "nsIDOMLoadListener.h"
00042 #include "nsIChannelEventSink.h"
00043 #include "nsIInterfaceRequestor.h"
00044 #include "nsIScriptContext.h"
00045 #include "nsISyncLoadDOMService.h"
00046 #include "nsString.h"
00047 #include "nsWeakReference.h"
00048 #include "jsapi.h"
00049 #include "nsIDocument.h"
00050 #include "nsIDOMDocument.h"
00051 #include "nsIDOMElement.h"
00052 #include "nsIDOMDOMImplementation.h"
00053 #include "nsIDOMEventReceiver.h"
00054 #include "nsIEventQueueService.h"
00055 #include "nsIJSContextStack.h"
00056 #include "nsIPrivateDOMImplementation.h"
00057 #include "nsIScriptGlobalObject.h"
00058 #include "nsIScriptSecurityManager.h"
00059 #include "nsContentCID.h"
00060 #include "nsContentUtils.h"
00061 #include "nsNetUtil.h"
00062 #include "nsIHttpChannel.h"
00063 #include "nsIScriptLoader.h"
00064 #include "nsIScriptLoaderObserver.h"
00065 #include "nsIXMLContentSink.h"
00066 #include "nsIContent.h"
00067 #include "nsAutoPtr.h"
00068 #include "nsLoadListenerProxy.h"
00069 
00070 static const char kLoadAsData[] = "loadAsData";
00071 
00072 static NS_DEFINE_CID(kXMLDocumentCID, NS_XMLDOCUMENT_CID);
00073 
00074 // This is ugly, but nsXBLContentSink.h isn't exported
00075 nsresult
00076 NS_NewXBLContentSink(nsIXMLContentSink** aResult,
00077                      nsIDocument* aDoc,
00078                      nsIURI* aURL,
00079                      nsISupports* aContainer);
00080 
00081 class nsSyncLoadService : public nsISyncLoadDOMService
00082 {
00083 public:
00084     nsSyncLoadService();
00085     virtual ~nsSyncLoadService();
00086     
00087     NS_DECL_ISUPPORTS
00088     NS_DECL_NSISYNCLOADDOMSERVICE
00089 
00090     static
00091     nsresult PushSyncStreamToListener(nsIInputStream* aIn,
00092                                       nsIStreamListener* aListener,
00093                                       nsIChannel* aChannel);
00094 };
00095 
00100 class nsSyncLoader : public nsIDOMLoadListener,
00101                      public nsIChannelEventSink,
00102                      public nsIInterfaceRequestor,
00103                      public nsSupportsWeakReference
00104 {
00105 public:
00106     nsSyncLoader();
00107     virtual ~nsSyncLoader();
00108 
00109     NS_DECL_ISUPPORTS
00110 
00111     nsresult LoadDocument(nsIChannel* aChannel, nsIURI *aLoaderURI,
00112                           PRBool aChannelIsSync, PRBool aForceToXML,
00113                           nsIDOMDocument** aResult);
00114 
00115     // nsIDOMEventListener
00116     NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
00117 
00118     // nsIDOMLoadListener
00119     NS_IMETHOD Load(nsIDOMEvent* aEvent);
00120     NS_IMETHOD BeforeUnload(nsIDOMEvent* aEvent);
00121     NS_IMETHOD Unload(nsIDOMEvent* aEvent);
00122     NS_IMETHOD Abort(nsIDOMEvent* aEvent);
00123     NS_IMETHOD Error(nsIDOMEvent* aEvent);
00124 
00125     NS_DECL_NSICHANNELEVENTSINK
00126 
00127     NS_DECL_NSIINTERFACEREQUESTOR
00128 
00129 protected:
00130     nsresult PushAsyncStream(nsIStreamListener* aListener);
00131     nsresult PushSyncStream(nsIStreamListener* aListener);
00132 
00133     nsCOMPtr<nsIChannel> mChannel;
00134     PRPackedBool mLoading;
00135     PRPackedBool mLoadSuccess;
00136 };
00137 
00138 class nsForceXMLListener : public nsIStreamListener
00139 {
00140 public:
00141     nsForceXMLListener(nsIStreamListener* aListener);
00142     virtual ~nsForceXMLListener();
00143 
00144     NS_DECL_ISUPPORTS
00145     NS_FORWARD_NSISTREAMLISTENER(mListener->)
00146     NS_DECL_NSIREQUESTOBSERVER
00147 
00148 private:
00149     nsCOMPtr<nsIStreamListener> mListener;
00150 };
00151 
00152 nsForceXMLListener::nsForceXMLListener(nsIStreamListener* aListener)
00153     : mListener(aListener)
00154 {
00155 }
00156 
00157 nsForceXMLListener::~nsForceXMLListener()
00158 {
00159 }
00160 
00161 NS_IMPL_ISUPPORTS2(nsForceXMLListener,
00162                    nsIStreamListener,
00163                    nsIRequestObserver)
00164 
00165 NS_IMETHODIMP
00166 nsForceXMLListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
00167 {
00168     nsresult status;
00169     aRequest->GetStatus(&status);
00170     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
00171     if (channel && NS_SUCCEEDED(status)) {
00172       channel->SetContentType(NS_LITERAL_CSTRING("text/xml"));
00173     }
00174 
00175     return mListener->OnStartRequest(aRequest, aContext);
00176 }
00177 
00178 NS_IMETHODIMP
00179 nsForceXMLListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
00180                                   nsresult aStatusCode)
00181 {
00182     return mListener->OnStopRequest(aRequest, aContext, aStatusCode);
00183 }
00184 
00185 nsSyncLoader::nsSyncLoader()
00186 {
00187 }
00188 
00189 nsSyncLoader::~nsSyncLoader()
00190 {
00191     if (mLoading && mChannel) {
00192         mChannel->Cancel(NS_BINDING_ABORTED);
00193     }
00194 }
00195 
00196 NS_IMPL_ISUPPORTS4(nsSyncLoader,
00197                    nsIDOMLoadListener,
00198                    nsIChannelEventSink,
00199                    nsIInterfaceRequestor,
00200                    nsISupportsWeakReference)
00201 
00202 nsresult
00203 nsSyncLoader::LoadDocument(nsIChannel* aChannel,
00204                            nsIURI *aLoaderURI,
00205                            PRBool aChannelIsSync,
00206                            PRBool aForceToXML,
00207                            nsIDOMDocument **aResult)
00208 {
00209     NS_ENSURE_ARG_POINTER(aResult);
00210     *aResult = nsnull;
00211     nsresult rv = NS_OK;
00212 
00213     mChannel = aChannel;
00214     nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(mChannel);
00215     if (http) {
00216         http->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),     
00217                                NS_LITERAL_CSTRING("text/xml,application/xml,application/xhtml+xml,*/*;q=0.1"),
00218                                PR_FALSE);
00219     }
00220 
00221     if (aLoaderURI) {
00222         nsCOMPtr<nsIURI> docURI;
00223         rv = aChannel->GetOriginalURI(getter_AddRefs(docURI));
00224         NS_ENSURE_SUCCESS(rv, rv);
00225 
00226         nsIScriptSecurityManager *securityManager =
00227             nsContentUtils::GetSecurityManager();
00228 
00229         rv = securityManager->CheckLoadURI(aLoaderURI, docURI,
00230                                            nsIScriptSecurityManager::STANDARD);
00231         NS_ENSURE_SUCCESS(rv, rv);
00232 
00233         rv = securityManager->CheckSameOriginURI(aLoaderURI, docURI);
00234         NS_ENSURE_SUCCESS(rv, rv);
00235     }
00236 
00237     // Get the loadgroup of the channel
00238     nsCOMPtr<nsILoadGroup> loadGroup;
00239     rv = aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
00240     NS_ENSURE_SUCCESS(rv, rv);
00241 
00242     // Create document
00243     nsCOMPtr<nsIDocument> document = do_CreateInstance(kXMLDocumentCID, &rv);
00244     NS_ENSURE_SUCCESS(rv, rv);
00245 
00246     // Start the document load. Do this before we attach the load listener
00247     // since we reset the document which drops all observers.
00248     nsCOMPtr<nsIStreamListener> listener;
00249     rv = document->StartDocumentLoad(kLoadAsData, mChannel, 
00250                                      loadGroup, nsnull, 
00251                                      getter_AddRefs(listener),
00252                                      PR_TRUE);
00253     NS_ENSURE_SUCCESS(rv, rv);
00254 
00255     if (aForceToXML) {
00256         nsCOMPtr<nsIStreamListener> forceListener =
00257             new nsForceXMLListener(listener);
00258         listener.swap(forceListener);
00259     }
00260 
00261     // Register as a load listener on the document
00262     nsCOMPtr<nsIDOMEventReceiver> target = do_QueryInterface(document);
00263     NS_ENSURE_TRUE(target, NS_ERROR_FAILURE);
00264 
00265     nsWeakPtr requestWeak = do_GetWeakReference(NS_STATIC_CAST(nsIDOMLoadListener*, this));
00266     nsLoadListenerProxy* proxy = new nsLoadListenerProxy(requestWeak);
00267     NS_ENSURE_TRUE(proxy, NS_ERROR_OUT_OF_MEMORY);
00268 
00269     // This will addref the proxy
00270     rv = target->AddEventListenerByIID(NS_STATIC_CAST(nsIDOMEventListener*, 
00271                                                       proxy), 
00272                                        NS_GET_IID(nsIDOMLoadListener));
00273     NS_ENSURE_SUCCESS(rv, rv);
00274     
00275     mLoadSuccess = PR_FALSE;
00276     if (aChannelIsSync) {
00277         rv = PushSyncStream(listener);
00278     }
00279     else {
00280         rv = PushAsyncStream(listener);
00281     }
00282 
00283     http = do_QueryInterface(mChannel);
00284     if (mLoadSuccess && http) {
00285         PRBool succeeded;
00286         mLoadSuccess = NS_SUCCEEDED(http->GetRequestSucceeded(&succeeded)) &&
00287                        succeeded;
00288     }
00289     mChannel = nsnull;
00290 
00291     // This will release the proxy. Don't use the errorvalue from this since
00292     // we're more interested in the errorvalue from the loading
00293     target->RemoveEventListenerByIID(NS_STATIC_CAST(nsIDOMEventListener*, 
00294                                                     proxy), 
00295                                      NS_GET_IID(nsIDOMLoadListener));
00296 
00297     // check that the load succeeded
00298     NS_ENSURE_SUCCESS(rv, rv);
00299 
00300     NS_ENSURE_TRUE(mLoadSuccess, NS_ERROR_FAILURE);
00301 
00302     NS_ENSURE_TRUE(document->GetRootContent(), NS_ERROR_FAILURE);
00303 
00304     return CallQueryInterface(document, aResult);
00305 }
00306 
00307 nsresult
00308 nsSyncLoader::PushAsyncStream(nsIStreamListener* aListener)
00309 {
00310     nsresult rv = NS_OK;
00311 
00312     // Set up a new eventqueue
00313     nsCOMPtr<nsIEventQueueService> service = 
00314         do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv);
00315     NS_ENSURE_SUCCESS(rv, rv);
00316 
00317     nsCOMPtr<nsIEventQueue> currentThreadQ;
00318     rv = service->PushThreadEventQueue(getter_AddRefs(currentThreadQ));
00319     NS_ENSURE_SUCCESS(rv, rv);
00320 
00321     // Hook us up to listen to redirects and the like
00322     mChannel->SetNotificationCallbacks(this);
00323 
00324     // Start reading from the channel
00325     rv = mChannel->AsyncOpen(aListener, nsnull);
00326 
00327     if (NS_SUCCEEDED(rv)) {
00328         mLoading = PR_TRUE;
00329 
00330         // process events until we're finished.
00331         PLEvent *event;
00332         while (mLoading && NS_SUCCEEDED(rv)) {
00333             rv = currentThreadQ->WaitForEvent(&event);
00334             NS_ASSERTION(NS_SUCCEEDED(rv), ": currentThreadQ->WaitForEvent failed...\n");
00335             if (NS_SUCCEEDED(rv)) {
00336                 rv = currentThreadQ->HandleEvent(event);
00337                 NS_ASSERTION(NS_SUCCEEDED(rv), ": currentThreadQ->HandleEvent failed...\n");
00338             }
00339         }
00340     }
00341 
00342     // Note that if AsyncOpen failed that's ok -- the only caller of
00343     // this method nulls out mChannel immediately after we return.
00344 
00345     service->PopThreadEventQueue(currentThreadQ);
00346     
00347     return rv;
00348 }
00349 
00350 nsresult
00351 nsSyncLoader::PushSyncStream(nsIStreamListener* aListener)
00352 {
00353     nsCOMPtr<nsIInputStream> in;
00354     nsresult rv = mChannel->Open(getter_AddRefs(in));
00355     NS_ENSURE_SUCCESS(rv, rv);
00356 
00357     mLoading = PR_TRUE;
00358     rv = nsSyncLoadService::PushSyncStreamToListener(in, aListener, mChannel);
00359 
00360     return rv;
00361 }
00362 
00363 // nsIDOMEventListener
00364 nsresult
00365 nsSyncLoader::HandleEvent(nsIDOMEvent* aEvent)
00366 {
00367     return NS_OK;
00368 }
00369 
00370 // nsIDOMLoadListener
00371 nsresult
00372 nsSyncLoader::Load(nsIDOMEvent* aEvent)
00373 {
00374     if (mLoading) {
00375         mLoading = PR_FALSE;
00376         mLoadSuccess = PR_TRUE;
00377     }
00378 
00379     return NS_OK;
00380 }
00381 
00382 nsresult
00383 nsSyncLoader::BeforeUnload(nsIDOMEvent* aEvent)
00384 {
00385     // Like, whatever.
00386 
00387     return NS_OK;
00388 }
00389 
00390 nsresult
00391 nsSyncLoader::Unload(nsIDOMEvent* aEvent)
00392 {
00393     return NS_OK;
00394 }
00395 
00396 nsresult
00397 nsSyncLoader::Abort(nsIDOMEvent* aEvent)
00398 {
00399     if (mLoading) {
00400         mLoading = PR_FALSE;
00401     }
00402 
00403     return NS_OK;
00404 }
00405 
00406 nsresult
00407 nsSyncLoader::Error(nsIDOMEvent* aEvent)
00408 {
00409     if (mLoading) {
00410         mLoading = PR_FALSE;
00411     }
00412 
00413     return NS_OK;
00414 }
00415 
00416 NS_IMETHODIMP
00417 nsSyncLoader::OnChannelRedirect(nsIChannel *aOldChannel,
00418                                 nsIChannel *aNewChannel,
00419                                 PRUint32    aFlags)
00420 {
00421     NS_PRECONDITION(aNewChannel, "Redirecting to null channel?");
00422 
00423     nsCOMPtr<nsIURI> oldURI;
00424     nsresult rv = aOldChannel->GetURI(getter_AddRefs(oldURI)); // The original URI
00425     NS_ENSURE_SUCCESS(rv, rv);
00426 
00427     nsCOMPtr<nsIURI> newURI;
00428     rv = aNewChannel->GetURI(getter_AddRefs(newURI)); // The new URI
00429     NS_ENSURE_SUCCESS(rv, rv);
00430 
00431     rv = nsContentUtils::GetSecurityManager()->CheckSameOriginURI(oldURI, newURI);
00432 
00433     NS_ENSURE_SUCCESS(rv, rv);
00434 
00435     mChannel = aNewChannel;
00436 
00437     return NS_OK;
00438 }
00439 
00440 NS_IMETHODIMP
00441 nsSyncLoader::GetInterface(const nsIID & aIID,
00442                            void **aResult)
00443 {
00444     return QueryInterface(aIID, aResult);
00445 }
00446 
00447 nsSyncLoadService::nsSyncLoadService()
00448 {
00449 }
00450 
00451 nsSyncLoadService::~nsSyncLoadService()
00452 {
00453 }
00454 
00455 NS_IMPL_ISUPPORTS1(nsSyncLoadService,
00456                    nsISyncLoadDOMService)
00457 
00458 NS_IMETHODIMP
00459 nsSyncLoadService::LoadDocument(nsIChannel* aChannel, nsIURI* aLoaderURI,
00460                                 nsIDOMDocument** aResult)
00461 {
00462     nsRefPtr<nsSyncLoader> loader = new nsSyncLoader();
00463     NS_ENSURE_TRUE(loader, NS_ERROR_OUT_OF_MEMORY);
00464 
00465     return loader->LoadDocument(aChannel, aLoaderURI, PR_FALSE, PR_FALSE,
00466                                 aResult);
00467 }
00468 
00469 NS_IMETHODIMP
00470 nsSyncLoadService::LoadDocumentAsXML(nsIChannel* aChannel, nsIURI* aLoaderURI,
00471                                      nsIDOMDocument** aResult)
00472 {
00473     nsRefPtr<nsSyncLoader> loader = new nsSyncLoader();
00474     NS_ENSURE_TRUE(loader, NS_ERROR_OUT_OF_MEMORY);
00475 
00476     return loader->LoadDocument(aChannel, aLoaderURI, PR_FALSE, PR_TRUE,
00477                                 aResult);
00478 }
00479 
00480 nsresult
00481 nsSyncLoadService::LoadLocalDocument(nsIChannel* aChannel, nsIURI* aLoaderURI,
00482                                      nsIDOMDocument** _retval)
00483 {
00484     nsSyncLoader* loader = new nsSyncLoader();
00485     NS_ENSURE_TRUE(loader, NS_ERROR_OUT_OF_MEMORY);
00486     NS_ADDREF(loader);
00487     nsresult rv = loader->LoadDocument(aChannel, aLoaderURI, PR_TRUE, PR_TRUE,
00488                                        _retval);
00489     NS_RELEASE(loader);
00490     return rv;
00491 }
00492 
00493 nsresult
00494 nsSyncLoadService::LoadLocalXBLDocument(nsIChannel* aChannel,
00495                                         nsIDOMDocument** _retval)
00496 {
00497     *_retval = nsnull;
00498 
00499     nsCOMPtr<nsIInputStream> in;
00500     nsresult rv = aChannel->Open(getter_AddRefs(in));
00501     if (NS_FAILED(rv)) {
00502         return rv;
00503     }
00504 
00505     // Get uri and loadgroup
00506     nsCOMPtr<nsIURI> docURI;
00507     rv = aChannel->GetURI(getter_AddRefs(docURI));
00508     NS_ENSURE_SUCCESS(rv, rv);
00509 
00510     nsCOMPtr<nsILoadGroup> loadGroup;
00511     rv = aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
00512     NS_ENSURE_SUCCESS(rv, rv);
00513 
00514     // Create document and contentsink and set them up.
00515     nsCOMPtr<nsIDocument> doc = do_CreateInstance(kXMLDocumentCID, &rv);
00516     NS_ENSURE_SUCCESS(rv, rv);
00517 
00518     nsCOMPtr<nsIXMLContentSink> xblSink;
00519     rv = NS_NewXBLContentSink(getter_AddRefs(xblSink), doc, docURI, nsnull);
00520     NS_ENSURE_SUCCESS(rv, rv);
00521 
00522     nsCOMPtr<nsIStreamListener> listener;
00523     rv = doc->StartDocumentLoad("loadAsInteractiveData",
00524                                 aChannel,
00525                                 loadGroup,
00526                                 nsnull,
00527                                 getter_AddRefs(listener),
00528                                 PR_TRUE,
00529                                 xblSink);
00530     NS_ENSURE_SUCCESS(rv, rv);
00531 
00532     rv = PushSyncStreamToListener(in, listener, aChannel);
00533 
00534     NS_ENSURE_SUCCESS(rv, rv);
00535 
00536     return CallQueryInterface(doc, _retval);
00537 }
00538 
00539 // static
00540 nsresult
00541 nsSyncLoadService::PushSyncStreamToListener(nsIInputStream* aIn,
00542                                             nsIStreamListener* aListener,
00543                                             nsIChannel* aChannel)
00544 {
00545     // Set up buffering stream
00546     nsCOMPtr<nsIInputStream> bufferedStream;
00547     nsresult rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
00548                                             aIn, 4096);
00549     NS_ENSURE_SUCCESS(rv, rv);
00550 
00551     // Load
00552     aListener->OnStartRequest(aChannel, nsnull);
00553     PRUint32 sourceOffset = 0;
00554     while (1) {
00555         PRUint32 readCount = 0;
00556         rv = bufferedStream->Available(&readCount);
00557         if (NS_FAILED(rv) || !readCount) {
00558             break;
00559         }
00560 
00561         rv = aListener->OnDataAvailable(aChannel, nsnull, bufferedStream,
00562                                         sourceOffset, readCount);
00563         if (NS_FAILED(rv)) {
00564             break;
00565         }
00566 
00567         sourceOffset += readCount;
00568     }
00569     aListener->OnStopRequest(aChannel, nsnull, rv);
00570     
00571     return rv;
00572 }
00573 
00574 nsresult
00575 NS_NewSyncLoadDOMService(nsISyncLoadDOMService** aResult)
00576 {
00577     *aResult = new nsSyncLoadService();
00578     NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
00579     NS_ADDREF(*aResult);
00580     return NS_OK;
00581 }