bug 173010 prompt before launching external protocol handler,r=mscott,sr=darin/jst

This commit is contained in:
dveditz%cruzio.com 2004-09-10 23:24:49 +00:00
parent 216e446106
commit 3ea4400949
4 changed files with 206 additions and 20 deletions

View File

@ -51,3 +51,7 @@ isprinting=The document cannot change while Printing or in Print Preview.
deniedPortAccess=Access to the port number given has been disabled for security reasons. deniedPortAccess=Access to the port number given has been disabled for security reasons.
proxyResolveFailure=The proxy server you have configured could not be found. Please check your proxy settings and try again. proxyResolveFailure=The proxy server you have configured could not be found. Please check your proxy settings and try again.
proxyConnectFailure=The connection was refused when attempting to contact the proxy server you have configured. Please check your proxy settings and try again. proxyConnectFailure=The connection was refused when attempting to contact the proxy server you have configured. Please check your proxy settings and try again.
externalProtocolTitle=External Protocol Request
externalProtocolPrompt=An external application must be launched to handle %1$S: links. Requested link:\n\n\n%2$S\n\n\nIf you were not expecting this request it may be an attempt to exploit a weakness in that other program. Cancel this request unless you are sure it is not malicious.\n
externalProtocolChkMsg=Remember my choice for all links of this type.
externalProtocolLaunchBtn=Launch application

View File

@ -109,6 +109,10 @@ pref("network.protocol-handler.external.vnd.ms.radio", false);
pref("network.protocol-handler.external.help", false); pref("network.protocol-handler.external.help", false);
pref("network.protocol-handler.external.disk", false); pref("network.protocol-handler.external.disk", false);
// Default action for unlisted external protocol handlers
// 0 == never load, 1 == always load, 2 == ask the user
pref("network.protocol-handler.external-default", 2);
// An exposed protocol handler is one that can be used in all contexts. A // An exposed protocol handler is one that can be used in all contexts. A
// non-exposed protocol handler is one that can only be used internally by the // non-exposed protocol handler is one that can only be used internally by the
// application. For example, a non-exposed protocol would not be loaded by the // application. For example, a non-exposed protocol would not be loaded by the

View File

@ -448,6 +448,10 @@ pref("network.protocol-handler.external.disk", false);
pref("network.protocol-handler.external.disks", false); pref("network.protocol-handler.external.disks", false);
pref("network.protocol-handler.external.afp", false); pref("network.protocol-handler.external.afp", false);
// Default action for unlisted external protocol handlers
// 0 == never load, 1 == always load, 2 == ask the user
pref("network.protocol-handler.external-default", 2);
// An exposed protocol handler is one that can be used in all contexts. A // An exposed protocol handler is one that can be used in all contexts. A
// non-exposed protocol handler is one that can only be used internally by the // non-exposed protocol handler is one that can only be used internally by the
// application. For example, a non-exposed protocol would not be loaded by the // application. For example, a non-exposed protocol would not be loaded by the

View File

@ -44,6 +44,12 @@
#include "nsReadableUtils.h" #include "nsReadableUtils.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsIServiceManager.h" #include "nsIServiceManager.h"
#include "nsIServiceManagerUtils.h"
#include "nsIInterfaceRequestor.h"
#include "nsIStringBundle.h"
#include "nsIPrefService.h"
#include "nsIPrompt.h"
#include "nsEventQueueUtils.h"
#include "nsIChannel.h" #include "nsIChannel.h"
#include "nsNetCID.h" #include "nsNetCID.h"
#include "netCore.h" #include "netCore.h"
@ -53,6 +59,8 @@
#include "nsIExternalProtocolService.h" #include "nsIExternalProtocolService.h"
static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID); static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID);
static const char kExternalProtocolPrefPrefix[] = "network.protocol-handler.external.";
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
@ -67,18 +75,21 @@ public:
NS_DECL_ISUPPORTS NS_DECL_ISUPPORTS
NS_DECL_NSICHANNEL NS_DECL_NSICHANNEL
NS_DECL_NSIREQUEST NS_DECL_NSIREQUEST
nsExtProtocolChannel(); nsExtProtocolChannel();
virtual ~nsExtProtocolChannel(); virtual ~nsExtProtocolChannel();
nsresult SetURI(nsIURI*); nsresult SetURI(nsIURI*);
protected: nsresult OpenURL();
nsCOMPtr<nsIURI> mUrl;
nsCOMPtr<nsIURI> mOriginalURI;
nsresult mStatus;
nsresult OpenURL(); private:
nsCOMPtr<nsIURI> mUrl;
nsCOMPtr<nsIURI> mOriginalURI;
nsresult mStatus;
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
PRBool PromptForScheme(nsIURI *aURI, nsACString& aScheme);
}; };
NS_IMPL_THREADSAFE_ADDREF(nsExtProtocolChannel) NS_IMPL_THREADSAFE_ADDREF(nsExtProtocolChannel)
@ -105,18 +116,21 @@ NS_IMETHODIMP nsExtProtocolChannel::GetLoadGroup(nsILoadGroup * *aLoadGroup)
NS_IMETHODIMP nsExtProtocolChannel::SetLoadGroup(nsILoadGroup * aLoadGroup) NS_IMETHODIMP nsExtProtocolChannel::SetLoadGroup(nsILoadGroup * aLoadGroup)
{ {
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP nsExtProtocolChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks) NS_IMETHODIMP nsExtProtocolChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
{ {
*aNotificationCallbacks = nsnull; NS_ENSURE_ARG_POINTER(aNotificationCallbacks);
*aNotificationCallbacks = mCallbacks;
NS_IF_ADDREF(*aNotificationCallbacks);
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP nsExtProtocolChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks) NS_IMETHODIMP nsExtProtocolChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
{ {
return NS_OK; // don't fail when trying to set this mCallbacks = aNotificationCallbacks;
return NS_OK; // don't fail when trying to set this
} }
NS_IMETHODIMP NS_IMETHODIMP
@ -152,6 +166,94 @@ nsresult nsExtProtocolChannel::SetURI(nsIURI* aURI)
return NS_OK; return NS_OK;
} }
PRBool nsExtProtocolChannel::PromptForScheme(nsIURI* aURI,
nsACString& aScheme)
{
// deny the load if we aren't able to ask but prefs say we should
nsresult rv;
if (!mCallbacks) {
NS_ERROR("Notification Callbacks not set!");
return PR_FALSE;
}
nsCOMPtr<nsIPrompt> prompt;
mCallbacks->GetInterface(NS_GET_IID(nsIPrompt), getter_AddRefs(prompt));
if (!prompt) {
NS_ERROR("No prompt interface on channel");
return PR_FALSE;
}
nsCOMPtr<nsIStringBundleService> sbSvc(do_GetService(NS_STRINGBUNDLE_CONTRACTID));
if (!sbSvc) {
NS_ERROR("Couldn't load StringBundleService");
return PR_FALSE;
}
nsCOMPtr<nsIStringBundle> appstrings;
rv = sbSvc->CreateBundle("chrome://global/locale/appstrings.properties",
getter_AddRefs(appstrings));
if (NS_FAILED(rv) || !appstrings) {
NS_ERROR("Failed to create appstrings.properties bundle");
return PR_FALSE;
}
nsCAutoString spec;
aURI->GetSpec(spec);
NS_ConvertUTF8toUTF16 uri(spec);
NS_ConvertUTF8toUTF16 scheme(aScheme);
nsXPIDLString title;
appstrings->GetStringFromName(NS_LITERAL_STRING("externalProtocolTitle").get(),
getter_Copies(title));
nsXPIDLString checkMsg;
appstrings->GetStringFromName(NS_LITERAL_STRING("externalProtocolChkMsg").get(),
getter_Copies(checkMsg));
nsXPIDLString launchBtn;
appstrings->GetStringFromName(NS_LITERAL_STRING("externalProtocolLaunchBtn").get(),
getter_Copies(launchBtn));
nsXPIDLString message;
const PRUnichar* msgArgs[] = { scheme.get(), uri.get() };
appstrings->FormatStringFromName(NS_LITERAL_STRING("externalProtocolPrompt").get(),
msgArgs,
NS_ARRAY_LENGTH(msgArgs),
getter_Copies(message));
if (scheme.IsEmpty() || uri.IsEmpty() || title.IsEmpty() ||
checkMsg.IsEmpty() || launchBtn.IsEmpty() || message.IsEmpty())
return PR_FALSE;
// all pieces assembled, now we can pose the dialog
PRBool allowLoad = PR_FALSE;
PRBool remember = PR_FALSE;
PRInt32 choice = 1; // assume "cancel" in case of failure
rv = prompt->ConfirmEx(title.get(), message.get(),
nsIPrompt::BUTTON_DELAY_ENABLE +
nsIPrompt::BUTTON_POS_1_DEFAULT +
(nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_0) +
(nsIPrompt::BUTTON_TITLE_CANCEL * nsIPrompt::BUTTON_POS_1),
launchBtn.get(), 0, 0, checkMsg.get(),
&remember, &choice);
if (NS_SUCCEEDED(rv))
{
if (choice == 0)
allowLoad = PR_TRUE;
if (remember)
{
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
if (prefs)
{
nsCAutoString prefname(kExternalProtocolPrefPrefix);
prefname += aScheme;
prefs->SetBoolPref(prefname.get(), allowLoad);
}
}
}
return allowLoad;
}
nsresult nsExtProtocolChannel::OpenURL() nsresult nsExtProtocolChannel::OpenURL()
{ {
nsCOMPtr<nsIExternalProtocolService> extProtService (do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID)); nsCOMPtr<nsIExternalProtocolService> extProtService (do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
@ -165,9 +267,47 @@ nsresult nsExtProtocolChannel::OpenURL()
extProtService->ExternalProtocolHandlerExists(urlScheme.get(), &haveHandler); extProtService->ExternalProtocolHandlerExists(urlScheme.get(), &haveHandler);
NS_ASSERTION(haveHandler, "Why do we have a channel for this url if we don't support the protocol?"); NS_ASSERTION(haveHandler, "Why do we have a channel for this url if we don't support the protocol?");
#endif #endif
return extProtService->LoadUrl(mUrl);
// Check that it's OK to hand this scheme off to the OS
PRBool allowLoad = PR_FALSE;
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
if (prefs)
{
// check whether it's explicitly approved or denied in prefs
nsCAutoString schemePref(kExternalProtocolPrefPrefix);
schemePref += urlScheme;
nsresult rv = prefs->GetBoolPref(schemePref.get(), &allowLoad);
if (NS_FAILED(rv))
{
// scheme not explicitly listed, what is the default action?
const PRInt32 kExternalProtocolNever = 0;
const PRInt32 kExternalProtocolAlways = 1;
const PRInt32 kExternalProtocolAsk = 2;
PRInt32 externalDefault = kExternalProtocolAsk;
prefs->GetIntPref("network.protocol-handler.external-default",
&externalDefault);
if (externalDefault == kExternalProtocolAlways)
{
// original behavior -- just do it
allowLoad = PR_TRUE;
}
else if (externalDefault == kExternalProtocolAsk)
{
allowLoad = PromptForScheme(mUrl, urlScheme);
}
}
}
if (allowLoad)
return extProtService->LoadUrl(mUrl);
} }
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -177,26 +317,60 @@ NS_IMETHODIMP nsExtProtocolChannel::Open(nsIInputStream **_retval)
return NS_ERROR_NO_CONTENT; // force caller to abort. return NS_ERROR_NO_CONTENT; // force caller to abort.
} }
static void *PR_CALLBACK handleExtProtoEvent(PLEvent *event)
{
nsExtProtocolChannel *channel =
NS_STATIC_CAST(nsExtProtocolChannel*, PL_GetEventOwner(event));
if (channel)
channel->OpenURL();
return nsnull;
}
static void PR_CALLBACK destroyExtProtoEvent(PLEvent *event)
{
nsExtProtocolChannel *channel =
NS_STATIC_CAST(nsExtProtocolChannel*, PL_GetEventOwner(event));
NS_IF_RELEASE(channel);
delete event;
}
NS_IMETHODIMP nsExtProtocolChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) NS_IMETHODIMP nsExtProtocolChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt)
{ {
OpenURL(); nsCOMPtr<nsIEventQueue> eventQ;
nsresult rv = NS_GetCurrentEventQ(getter_AddRefs(eventQ));
if (NS_FAILED(rv))
return rv;
PLEvent *event = new PLEvent;
if (event)
{
NS_ADDREF_THIS();
PL_InitEvent(event, this, handleExtProtoEvent, destroyExtProtoEvent);
rv = eventQ->PostEvent(event);
if (NS_FAILED(rv))
PL_DestroyEvent(event);
}
return NS_ERROR_NO_CONTENT; // force caller to abort. return NS_ERROR_NO_CONTENT; // force caller to abort.
} }
NS_IMETHODIMP nsExtProtocolChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) NS_IMETHODIMP nsExtProtocolChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
{ {
*aLoadFlags = 0; *aLoadFlags = 0;
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP nsExtProtocolChannel::SetLoadFlags(nsLoadFlags aLoadFlags) NS_IMETHODIMP nsExtProtocolChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
{ {
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP nsExtProtocolChannel::GetContentType(nsACString &aContentType) NS_IMETHODIMP nsExtProtocolChannel::GetContentType(nsACString &aContentType)
{ {
return NS_ERROR_NOT_IMPLEMENTED; return NS_ERROR_NOT_IMPLEMENTED;
} }
NS_IMETHODIMP nsExtProtocolChannel::SetContentType(const nsACString &aContentType) NS_IMETHODIMP nsExtProtocolChannel::SetContentType(const nsACString &aContentType)
@ -285,8 +459,8 @@ NS_IMETHODIMP nsExtProtocolChannel::Resume()
nsExternalProtocolHandler::nsExternalProtocolHandler() nsExternalProtocolHandler::nsExternalProtocolHandler()
{ {
m_schemeName = "default"; m_schemeName = "default";
m_extProtService = do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID); m_extProtService = do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
} }
@ -305,13 +479,13 @@ NS_INTERFACE_MAP_END_THREADSAFE
NS_IMETHODIMP nsExternalProtocolHandler::GetScheme(nsACString &aScheme) NS_IMETHODIMP nsExternalProtocolHandler::GetScheme(nsACString &aScheme)
{ {
aScheme = m_schemeName; aScheme = m_schemeName;
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP nsExternalProtocolHandler::GetDefaultPort(PRInt32 *aDefaultPort) NS_IMETHODIMP nsExternalProtocolHandler::GetDefaultPort(PRInt32 *aDefaultPort)
{ {
*aDefaultPort = 0; *aDefaultPort = 0;
return NS_OK; return NS_OK;
} }