/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * Peter Van der Beken, peterv@netscape.com * -- original author. * */ #include "nsSyncLoader.h" #include "nsNetUtil.h" #include "nsLayoutCID.h" #include "nsIEventQueueService.h" #include "nsIDOMDocument.h" #include "nsIDOMDOMImplementation.h" #include "nsIDOMEventReceiver.h" #include "nsIXPConnect.h" #include "nsIDocShell.h" #include "nsIDocShellTreeItem.h" #include "nsIScriptContext.h" #include "nsIScriptGlobalObject.h" #include "nsIDOMWindowInternal.h" #include "nsAppShellCIDs.h" #include "nsIAppShellService.h" #include "nsIDocShellTreeOwner.h" #include "nsIInterfaceRequestor.h" #include "jsapi.h" static const char* kLoadAsData = "loadAsData"; static NS_DEFINE_CID(kIDOMDOMImplementationCID, NS_DOM_IMPLEMENTATION_CID); static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); static NS_DEFINE_CID(kAppShellServiceCID, NS_APPSHELL_SERVICE_CID); /* * This class exists to prevent a circular reference between * the loaded document and the nsSyncloader instance. The * request owns the document. While the document is loading, * the request is a load listener, held onto by the document. * The proxy class breaks the circularity by filling in as the * load listener and holding a weak reference to the request * object. */ class txLoadListenerProxy : public nsIDOMLoadListener { public: txLoadListenerProxy(nsWeakPtr aParent); virtual ~txLoadListenerProxy(); NS_DECL_ISUPPORTS // nsIDOMEventListener NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent); // nsIDOMLoadListener NS_IMETHOD Load(nsIDOMEvent* aEvent); NS_IMETHOD Unload(nsIDOMEvent* aEvent); NS_IMETHOD Abort(nsIDOMEvent* aEvent); NS_IMETHOD Error(nsIDOMEvent* aEvent); protected: nsWeakPtr mParent; }; txLoadListenerProxy::txLoadListenerProxy(nsWeakPtr aParent) { NS_INIT_ISUPPORTS(); mParent = aParent; } txLoadListenerProxy::~txLoadListenerProxy() { } NS_IMPL_ISUPPORTS1(txLoadListenerProxy, nsIDOMLoadListener) NS_IMETHODIMP txLoadListenerProxy::HandleEvent(nsIDOMEvent* aEvent) { nsCOMPtr listener = do_QueryReferent(mParent); if (listener) { return listener->HandleEvent(aEvent); } return NS_OK; } NS_IMETHODIMP txLoadListenerProxy::Load(nsIDOMEvent* aEvent) { nsCOMPtr listener = do_QueryReferent(mParent); if (listener) { return listener->Load(aEvent); } return NS_OK; } NS_IMETHODIMP txLoadListenerProxy::Unload(nsIDOMEvent* aEvent) { nsCOMPtr listener = do_QueryReferent(mParent); if (listener) { return listener->Unload(aEvent); } return NS_OK; } NS_IMETHODIMP txLoadListenerProxy::Abort(nsIDOMEvent* aEvent) { nsCOMPtr listener = do_QueryReferent(mParent); if (listener) { return listener->Abort(aEvent); } return NS_OK; } NS_IMETHODIMP txLoadListenerProxy::Error(nsIDOMEvent* aEvent) { nsCOMPtr listener = do_QueryReferent(mParent); if (listener) { return listener->Error(aEvent); } return NS_OK; } nsSyncLoader::nsSyncLoader() { NS_INIT_ISUPPORTS(); } nsSyncLoader::~nsSyncLoader() { //if (XML_HTTP_REQUEST_SENT == mStatus) { // Abort(); //} if (mChromeWindow) mChromeWindow->ExitModalEventLoop(NS_OK); } NS_IMPL_ISUPPORTS3(nsSyncLoader, nsISyncLoader, nsIDOMLoadListener, nsISupportsWeakReference) NS_IMETHODIMP nsSyncLoader::LoadDocument(nsIURI* documentURI, nsIDOMDocument **_retval) { nsresult rv = NS_OK; nsCOMPtr loadGroup; // If we have a base document, use it for the base URL and loadgroup //if (mBaseDocument) { // rv = mBaseDocument->GetDocumentLoadGroup(getter_AddRefs(loadGroup)); // if (NS_FAILED(rv)) return rv; //} //rv = NS_NewURI(getter_AddRefs(uri), url, mBaseURI); //if (NS_FAILED(rv)) return rv; nsCOMPtr channel; rv = NS_OpenURI(getter_AddRefs(channel), documentURI, nsnull, loadGroup); if (NS_FAILED(rv)) return rv; nsCOMPtr postDataStream; // Make sure we've been opened if (!channel) { return NS_ERROR_NOT_INITIALIZED; } // Get and initialize a DOMImplementation nsCOMPtr implementation = do_CreateInstance(kIDOMDOMImplementationCID, &rv); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; //if (mBaseURI) { // nsCOMPtr privImpl = do_QueryInterface(implementation); // if (privImpl) { // privImpl->Init(mBaseURI); // } //} // Create an empty document from it nsAutoString emptyStr; nsCOMPtr DOMDocument; rv = implementation->CreateDocument(emptyStr, emptyStr, nsnull, getter_AddRefs(DOMDocument)); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; // Register as a load listener on the document nsCOMPtr target = do_QueryInterface(DOMDocument); if (target) { nsWeakPtr requestWeak = getter_AddRefs(NS_GetWeakReference(NS_STATIC_CAST(nsIDOMLoadListener*, this))); txLoadListenerProxy* proxy = new txLoadListenerProxy(requestWeak); if (!proxy) return NS_ERROR_OUT_OF_MEMORY; // This will addref the proxy rv = target->AddEventListenerByIID(NS_STATIC_CAST(nsIDOMEventListener*, proxy), NS_GET_IID(nsIDOMLoadListener)); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; } // Tell the document to start loading nsCOMPtr listener; nsCOMPtr document = do_QueryInterface(DOMDocument); if (!document) { return NS_ERROR_FAILURE; } nsCOMPtr modalEventQueue; nsCOMPtr eventQService; nsCOMPtr cc; NS_WITH_SERVICE(nsIXPConnect, xpc, nsIXPConnect::GetCID(), &rv); if(NS_SUCCEEDED(rv)) { rv = xpc->GetCurrentNativeCallContext(getter_AddRefs(cc)); } JSContext* cx; if (NS_SUCCEEDED(rv) && cc) { rv = cc->GetJSContext(&cx); if (NS_FAILED(rv)) NS_ERROR_FAILURE; } else { NS_WITH_SERVICE(nsIAppShellService, appshellSvc, kAppShellServiceCID, &rv); if (NS_FAILED(rv)) return rv; nsCOMPtr junk; rv = appshellSvc->GetHiddenWindowAndJSContext(getter_AddRefs(junk), &cx); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; } if (NS_SUCCEEDED(rv)) { nsIScriptContext* scriptCX; // We can only do this if we're called from a DOM script context scriptCX = (nsIScriptContext*)JS_GetContextPrivate(cx); if (!scriptCX) return NS_OK; // Get the nsIDocShellTreeOwner associated with the window // containing this script context // XXX Need to find a better way to do this rather than // chaining through a bunch of getters and QIs nsCOMPtr global; global = dont_AddRef(scriptCX->GetGlobalObject()); if (!global) return NS_ERROR_FAILURE; nsCOMPtr docshell; rv = global->GetDocShell(getter_AddRefs(docshell)); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; nsCOMPtr item = do_QueryInterface(docshell); if (!item) return NS_ERROR_FAILURE; nsCOMPtr treeOwner; rv = item->GetTreeOwner(getter_AddRefs(treeOwner)); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; nsCOMPtr treeRequestor(do_GetInterface(treeOwner)); if (!treeRequestor) return NS_ERROR_FAILURE; treeRequestor->GetInterface(NS_GET_IID(nsIWebBrowserChrome), getter_AddRefs(mChromeWindow)); if (mChromeWindow) return NS_ERROR_FAILURE; eventQService = do_GetService(kEventQueueServiceCID); if(!eventQService || NS_FAILED(eventQService->PushThreadEventQueue(getter_AddRefs(modalEventQueue)))) { return NS_ERROR_FAILURE; } } rv = document->StartDocumentLoad(kLoadAsData, channel, nsnull, nsnull, getter_AddRefs(listener), PR_FALSE); if (NS_FAILED(rv)) { if (modalEventQueue) { eventQService->PopThreadEventQueue(modalEventQueue); } return NS_ERROR_FAILURE; } // Start reading from the channel rv = channel->AsyncOpen(listener, nsnull); if (NS_FAILED(rv)) { if (modalEventQueue) { eventQService->PopThreadEventQueue(modalEventQueue); } return NS_ERROR_FAILURE; } // Spin an event loop here and wait if (mChromeWindow) { rv = mChromeWindow->ShowAsModal(); eventQService->PopThreadEventQueue(modalEventQueue); if (NS_FAILED(rv)) return NS_ERROR_FAILURE; } *_retval = DOMDocument; NS_ADDREF(*_retval); return rv; } // nsIDOMEventListener nsresult nsSyncLoader::HandleEvent(nsIDOMEvent* aEvent) { return NS_OK; } // nsIDOMLoadListener nsresult nsSyncLoader::Load(nsIDOMEvent* aEvent) { if (mChromeWindow) { mChromeWindow->ExitModalEventLoop(NS_OK); mChromeWindow = 0; } return NS_OK; } nsresult nsSyncLoader::Unload(nsIDOMEvent* aEvent) { return NS_OK; } nsresult nsSyncLoader::Abort(nsIDOMEvent* aEvent) { if (mChromeWindow) { mChromeWindow->ExitModalEventLoop(NS_OK); mChromeWindow = 0; } return NS_OK; } nsresult nsSyncLoader::Error(nsIDOMEvent* aEvent) { if (mChromeWindow) { mChromeWindow->ExitModalEventLoop(NS_OK); mChromeWindow = 0; } return NS_OK; }