/* -*- 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.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include "nsIBlockingNotification.h" #include "nsIURL.h" #include "nsIWebShell.h" #include "nsNetSupportDialog.h" #include "nsIEventQueueService.h" #include "nsIServiceManager.h" #include "nsXPComFactory.h" // Class IDs... static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID); static NS_DEFINE_CID(kNetSupportDialogCID, NS_NETSUPPORTDIALOG_CID); // Interface IDs... static NS_DEFINE_IID(kIEventQueueServiceIID, NS_IEVENTQUEUESERVICE_IID); static NS_DEFINE_IID(kIBlockingNotificationObserverIID, NS_IBLOCKINGNOTIFICATION_OBSERVER_IID); static NS_DEFINE_IID(kIBlockingNotificationIID, NS_IBLOCKINGNOTIFICATION_IID); // Forward declarations... class nsDefaultProtocolHelper; /*-------------------- Notification Event Class ----------------------------*/ struct NotificationEvent : public PLEvent { NotificationEvent(nsDefaultProtocolHelper *aSelf, nsIBlockingNotification *aCaller, nsIURI *aUrl, PRInt32 aCode, nsISupports *aExtraInfo); ~NotificationEvent(); PRStatus Fire(nsIEventQueue* aEventQ); static void PR_CALLBACK HandlePLEvent(PLEvent* aEvent); static void PR_CALLBACK DestroyPLEvent(PLEvent* aEvent); nsDefaultProtocolHelper *mSelf; nsIBlockingNotification *mCaller; nsIURI *mUrl; PRInt32 mCode; nsISupports *mExtraInfo; }; /*----------------------------- nsDefaultProtocolHelper ---------------------*/ class nsDefaultProtocolHelper : public nsIBlockingNotificationObserver { public: nsDefaultProtocolHelper(); /* nsISupports interface... */ NS_DECL_ISUPPORTS /* nsIBlockingNotificationObserver interface... */ NS_IMETHOD Notify(nsIBlockingNotification *aCaller, nsIURI *aUrl, PRThread *aThread, PRInt32 aCode, void *aExtraInfo); NS_IMETHOD CancelNotify(nsIURI *aUrl); /* class methods... */ nsresult HandleNotification(nsIBlockingNotification *aCaller, nsIURI *aUrl, PRInt32 aCode, void *aExtraInfo); protected: virtual ~nsDefaultProtocolHelper(); private: nsIEventQueueService *mEventQService; }; nsDefaultProtocolHelper::nsDefaultProtocolHelper() { NS_INIT_REFCNT(); /* * Cache the EventQueue service... * * Ignore failure since there is nothing that can be done... * Instead, all of the code that uses mEventQService *must* * check that it was initialized... */ (void) nsServiceManager::GetService(kEventQueueServiceCID, kIEventQueueServiceIID, (nsISupports **)&mEventQService); } nsDefaultProtocolHelper::~nsDefaultProtocolHelper() { if (mEventQService) { nsServiceManager::ReleaseService(kEventQueueServiceCID, mEventQService); mEventQService = nsnull; } } /* * Implementations of nsISupports interface methods... */ NS_IMPL_ADDREF(nsDefaultProtocolHelper); NS_IMPL_RELEASE(nsDefaultProtocolHelper); NS_IMPL_QUERY_INTERFACE(nsDefaultProtocolHelper, kIBlockingNotificationObserverIID); /* * Implementations of nsIBlockingNotificationObserver interface methods... */ NS_IMETHODIMP nsDefaultProtocolHelper::Notify(nsIBlockingNotification *aCaller, nsIURI *aUrl, PRThread *aThread, PRInt32 aCode, void *aExtraInfo) { nsresult rv; NotificationEvent *ev; /* * Initialize the return code to indicate that the notification was not * processed synchronously... */ rv = NS_NOTIFY_BLOCKED; /* * If no thread switch is necessary, then handle the notification * immediately... */ #if 0 // The mac netlib is on the same thread as the UI thread but you crash if you block right here PRThread* currentThread = PR_GetCurrentThread(); if ( currentThread == aThread) { rv = HandleNotification(aCaller, aUrl, aCode, (void *)aExtraInfo); } else #endif { /* * Post a message to the appropriate thread event queue to * handle the notification... */ nsIEventQueue *evQ = nsnull; /* locate the event queue for the thread... */ if (mEventQService) { mEventQService->GetThreadEventQueue(aThread, &evQ); } /* Create and dispatch the notification event... */ if (evQ) { ev = new NotificationEvent(this, aCaller, aUrl, aCode, (nsISupports *) aExtraInfo); if (ev) { PRStatus status; /* dispatch the event into the appropriate event queue... */ status = ev->Fire(evQ); if (PR_SUCCESS != status) { /* If the event was not dispatched, then clean up... */ NotificationEvent::DestroyPLEvent(ev); rv = NS_ERROR_FAILURE; } } else { /* allocation of the Notification event failed... */ rv = NS_ERROR_OUT_OF_MEMORY; } NS_IF_RELEASE(evQ); } else { /* No event queue was found! */ NS_ASSERTION(0, "No Event Queue is available!"); rv = NS_ERROR_FAILURE; } } return rv; } NS_IMETHODIMP nsDefaultProtocolHelper::CancelNotify(nsIURI *aUrl) { /* XXX: does not support interrupting a notification yet... */ return NS_ERROR_FAILURE; } nsresult nsDefaultProtocolHelper::HandleNotification(nsIBlockingNotification *aCaller, nsIURI *aUrl, PRInt32 aCode, void *aExtraInfo) { /* XXX this definition must match the one in network/protocol/http/mkhttp.c this will go away as netlib cleanup continues */ typedef struct _NET_AuthClosure { char * msg; char * user; char * pass; void * _private; } NET_AuthClosure; NET_AuthClosure * auth_closure = (NET_AuthClosure *) aExtraInfo; nsAutoString aText(auth_closure->msg); nsAutoString aUser; nsAutoString aPass(auth_closure->pass); // create a dialog PRBool bResult = PR_FALSE; nsString password, user; PRInt32 result; nsresult rv; #ifdef NECKO NS_WITH_SERVICE(nsIPrompt, dialog, kNetSupportDialogCID, &rv); #else NS_WITH_SERVICE(nsINetSupportDialogService, dialog, kNetSupportDialogCID, &rv); #endif if (NS_FAILED(rv)) return NS_ERROR_FAILURE; if ( dialog ) { #ifdef NECKO PRUnichar* usr; PRUnichar* pwd; rv = dialog->PromptUsernameAndPassword(aText.GetUnicode(), &usr, &pwd, &result); if (NS_SUCCEEDED(rv)) { aUser = usr; delete[] usr; aPass = pwd; delete[] pwd; } #else dialog->PromptUserAndPassword( aText, aUser, aPass, &result ); #endif } if ( result == 1 ) bResult = NS_NOTIFY_SUCCEEDED; else bResult = NS_ERROR_FAILURE; auth_closure->user = aUser.ToNewCString(); auth_closure->pass = aPass.ToNewCString(); aCaller->Resume(aUrl, (void *) auth_closure); return bResult; // delete aUser; // delete aPass; // delete aText; // dialog->Release(); return NS_NOTIFY_BLOCKED; } /*-------------------- Notification Event Class ----------------------------*/ NotificationEvent::NotificationEvent(nsDefaultProtocolHelper *aSelf, nsIBlockingNotification *aCaller, nsIURI *aUrl, PRInt32 aCode, nsISupports *aExtraInfo) { mSelf = aSelf; mCaller = aCaller; mUrl = aUrl; mCode = aCode; mExtraInfo = aExtraInfo; NS_IF_ADDREF(mSelf); NS_IF_ADDREF(mCaller); NS_IF_ADDREF(mUrl); // NS_IF_ADDREF(mExtraInfo); } NotificationEvent::~NotificationEvent() { NS_IF_RELEASE(mSelf); NS_IF_RELEASE(mCaller); NS_IF_RELEASE(mUrl); // NS_IF_RELEASE(mExtraInfo); } void PR_CALLBACK NotificationEvent::HandlePLEvent(PLEvent* aEvent) { NotificationEvent *ev = (NotificationEvent*)aEvent; (void)ev->mSelf->HandleNotification(ev->mCaller, ev->mUrl, ev->mCode, (void *)ev->mExtraInfo); } void PR_CALLBACK NotificationEvent::DestroyPLEvent(PLEvent* aEvent) { NotificationEvent *ev = (NotificationEvent*)aEvent; delete ev; } PRStatus NotificationEvent::Fire(nsIEventQueue* aEventQ) { PL_InitEvent(this, nsnull, (PLHandleEventProc) NotificationEvent::HandlePLEvent, (PLDestroyEventProc) NotificationEvent::DestroyPLEvent); PRStatus status = aEventQ->PostEvent(this); return status; } //---------------------------------------------------------------------- // Entry point to create nsAppShellService factory instances... NS_DEF_FACTORY(DefaultProtocolHelper,nsDefaultProtocolHelper) nsresult NS_NewDefaultProtocolHelperFactory(nsIFactory** aResult) { nsresult rv = NS_OK; nsIFactory* inst; inst = new nsDefaultProtocolHelperFactory; if (nsnull == inst) { rv = NS_ERROR_OUT_OF_MEMORY; } else { NS_ADDREF(inst); } *aResult = inst; return rv; }