fixes bug 226071 "xremote: openURL doesn't work well when multiple apps with different capabilities are present" r=bz,blizzard sr=bryner a=asa

git-svn-id: svn://10.0.0.236/trunk@149593 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
darin%meer.net 2003-11-21 00:09:50 +00:00
parent 101b4e2a07
commit a34389b0de
14 changed files with 196 additions and 100 deletions

View File

@ -637,6 +637,12 @@ pref("signon.SignonFileName", "signons.txt");
pref("network.protocol-handler.external.mailto", true); // for mail
pref("network.protocol-handler.external.news" , true); // for news
// By default, all protocol handlers are exposed. This means that
// the browser will respond to openURL commands for all URL types.
// It will also try to open link clicks inside the browser before
// failing over to the system handlers.
pref("network.protocol-handler.expose-all", true);
// Default security warning dialogs to off
pref("security.warn_entering_secure", false);
pref("security.warn_entering_weak", false);

View File

@ -637,6 +637,12 @@ pref("signon.SignonFileName", "signons.txt");
pref("network.protocol-handler.external.mailto", true); // for mail
pref("network.protocol-handler.external.news" , true); // for news
// By default, all protocol handlers are exposed. This means that
// the browser will respond to openURL commands for all URL types.
// It will also try to open link clicks inside the browser before
// failing over to the system handlers.
pref("network.protocol-handler.expose-all", true);
// Default security warning dialogs to off
pref("security.warn_entering_secure", false);
pref("security.warn_entering_weak", false);

View File

@ -635,6 +635,11 @@ pref("signon.expireMasterPassword", false);
pref("network.protocol-handler.external.mailto", true); // for mail
pref("network.protocol-handler.external.news" , true); // for news
// By default, all protocol handlers are hidden. This means
// that calendar will not respond to X-remote openURL commands
// and it will also defer all link clicks to the user's browser.
pref("network.protocol-handler.expose-all", false);
// Default security warning dialogs to off
pref("security.warn_entering_secure", false);
pref("security.warn_entering_weak", false);

View File

@ -483,6 +483,11 @@ pref("network.protocol-handler.external.javascript", false);
pref("network.protocol-handler.external.ms-help", false);
pref("network.protocol-handler.external.vnd.ms.radio", false);
// By default, all protocol handlers are hidden. This means
// that composer will not respond to X-remote openURL commands
// and it will also defer all link clicks to the user's browser.
pref("network.protocol-handler.expose-all", false);
pref("network.hosts.smtp_server", "mail");
pref("network.hosts.pop_server", "mail");
pref("network.protocols.useSystemDefaults", false); // set to true if user links should use system default handlers

View File

@ -118,10 +118,8 @@
#include "nsIDocument.h"
#include "nsITextToSubURI.h"
#ifdef MOZ_THUNDERBIRD
#include "nsIExternalProtocolService.h"
#include "nsCExternalHandlerService.h"
#endif
#ifdef NS_DEBUG
/**
@ -561,39 +559,30 @@ nsWebShell::OnLinkClickSync(nsIContent *aContent,
nsIDocShell** aDocShell,
nsIRequest** aRequest)
{
#ifdef MOZ_THUNDERBIRD
// XXX ugly thunderbird hack to force all url clicks to go to the system default app
// I promise this will be removed once we figure out a better way.
nsCAutoString scheme;
aURI->GetScheme(scheme);
nsCAutoString spec;
aURI->GetSpec(spec);
static const char kMailToURI[] = "mailto";
static const char kNewsURI[] = "news";
static const char kSnewsURI[] = "snews";
static const char kNntpURI[] = "nntp";
static const char kImapURI[] = "imap";
static const char kAddbookURI[] = "addbook";
static const char kPopURI[] = "pop";
static const char kMailboxURI[] = "mailbox";
if (scheme.EqualsIgnoreCase(kMailToURI) || scheme.EqualsIgnoreCase(kNewsURI) || scheme.EqualsIgnoreCase(kSnewsURI) ||
scheme.EqualsIgnoreCase(kNntpURI) || scheme.EqualsIgnoreCase(kImapURI) || scheme.EqualsIgnoreCase(kAddbookURI) ||
scheme.EqualsIgnoreCase(kPopURI) || scheme.EqualsIgnoreCase(kMailboxURI))
{
// we can handle all mail schemes
} else
PRBool earlyReturn = PR_FALSE;
{
// we don't handle this type, the the registered handler take it
nsresult rv = NS_OK;
nsCOMPtr<nsIExternalProtocolService> extProtService = do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv,rv);
return extProtService->LoadUrl(aURI);
}
#endif
// defer to an external protocol handler if necessary...
nsCOMPtr<nsIExternalProtocolService> extProtService = do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
if (extProtService) {
nsCAutoString scheme;
aURI->GetScheme(scheme);
if (!scheme.IsEmpty()) {
// if the URL scheme does not correspond to an exposed protocol, then we
// need to hand this link click over to the external protocol handler.
PRBool isExposed;
nsresult rv = extProtService->IsExposedProtocol(scheme.get(), &isExposed);
if (NS_SUCCEEDED(rv) && !isExposed) {
rv = extProtService->LoadUrl(aURI);
if (NS_SUCCEEDED(rv))
earlyReturn = PR_TRUE;
else
NS_WARNING("failed to launch external protocol handler");
}
}
}
}
if (earlyReturn)
return NS_OK;
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aContent));
NS_ENSURE_TRUE(node, NS_ERROR_UNEXPECTED);

View File

@ -230,6 +230,18 @@ function messagePaneOnClick(event)
if (href.search(needABrowser) == -1)
return;
// however, if the protocol should not be loaded internally, then we should
// not put up a new browser window. we should just let the usual processing
// take place.
try {
var extProtService = Components.classes["@mozilla.org/uriloader/external-protocol-service;1"].getService();
extProtService = extProtService.QueryInterface(Components.interfaces.nsIExternalProtocolService);
var scheme = href.substring(0, href.indexOf(":"));
if (!extProtService.isExposedProtocol(scheme))
return;
}
catch (ex) {} // ignore errors, and just assume that we can proceed.
// if you get here, the user did a simple left click on a link
// that we know should be in a browser window.
// since we are in the message pane, send it to the top most browser window

View File

@ -504,9 +504,22 @@ pref("network.protocol-handler.external.javascript", false);
pref("network.protocol-handler.external.ms-help", false);
pref("network.protocol-handler.external.vnd.ms.radio", false);
// 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
// application. For example, a non-exposed protocol would not be loaded by the
// application in response to a link click or a X-remote openURL command.
// Instead, it would be deferred to the system's external protocol handler.
// Only internal/built-in protocol handlers can be marked as exposed.
// This pref controls the default settings. Per protocol settings can be used
// to override this value.
pref("network.protocol-handler.expose-all", true);
// Example: make IMAP an exposed protocol
// pref("network.protocol-handler.expose.imap", true);
pref("network.hosts.smtp_server", "mail");
pref("network.hosts.pop_server", "mail");
pref("network.protocols.useSystemDefaults", false); // set to true if user links should use system default handlers
// <http>
pref("network.http.version", "1.1"); // default

View File

@ -910,6 +910,40 @@ NS_IMETHODIMP nsExternalHelperAppService::ExternalProtocolHandlerExists(const ch
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsExternalHelperAppService::IsExposedProtocol(const char * aProtocolScheme, PRBool * aResult)
{
// by default, no protocol is exposed. i.e., by default all link clicks must
// go through the external protocol service. most applications override this
// default behavior.
*aResult = PR_FALSE;
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
if (prefs)
{
PRBool val;
nsresult rv;
// check the per protocol setting first. it always takes precidence.
// if not set, then use the global setting.
nsCAutoString name;
name = NS_LITERAL_CSTRING("network.protocol-handler.expose.")
+ nsDependentCString(aProtocolScheme);
rv = prefs->GetBoolPref(name.get(), &val);
if (NS_SUCCEEDED(rv))
{
*aResult = val;
}
else
{
rv = prefs->GetBoolPref("network.protocol-handler.expose-all", &val);
if (NS_SUCCEEDED(rv) && val)
*aResult = PR_TRUE;
}
}
return NS_OK;
}
NS_IMETHODIMP nsExternalHelperAppService::LoadUrl(nsIURI * aURL)
{
// this method should only be implemented by each OS specific implementation of this service.

View File

@ -43,6 +43,18 @@ interface nsIExternalProtocolService : nsISupports
*/
boolean externalProtocolHandlerExists(in string aProtocolScheme);
/**
* Check whether a handler for a specific protocol is "exposed" as a visible
* feature of the current application.
*
* 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
* application. For example, a non-exposed protocol would not be loaded by the
* application in response to a link click or a X-remote openURL command.
* Instead, it would be deferred to the system's external protocol handler.
*/
boolean isExposedProtocol(in string aProtocolScheme);
/**
* Used to load a url via an external protocol handler (if one exists)
* @param aURL The url to load

View File

@ -1,3 +1,4 @@
/* vim:set ts=8 sw=2 et cindent: */
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
@ -98,54 +99,6 @@ XRemoteClient::Init (void)
return NS_OK;
}
NS_IMETHODIMP
XRemoteClient::SendCommand (const char *aCommand, PRBool *aWindowFound)
{
PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommand"));
*aWindowFound = PR_TRUE;
// find the remote window
Window window = FindWindow();
// no window? let the caller know.
if (!window) {
*aWindowFound = PR_FALSE;
return NS_OK;
}
// make sure we get the right events on that window
XSelectInput(mDisplay, window,
(PropertyChangeMask|StructureNotifyMask));
nsresult rv;
PRBool destroyed = PR_FALSE;
// get the lock on the window
rv = GetLock(window, &destroyed);
if (NS_FAILED(rv))
return NS_ERROR_FAILURE;
// send our command
rv = DoSendCommand(window, aCommand, &destroyed);
// if the window was destroyed, don't bother trying to free the
// lock.
if (destroyed)
return NS_ERROR_FAILURE;
// doesn't really matter what this returns
FreeLock(window);
// if we failed above we had to free the lock - return the error
// now.
if (NS_FAILED(rv))
return NS_ERROR_FAILURE;
return NS_OK;
}
NS_IMETHODIMP
XRemoteClient::Shutdown (void)
{
@ -164,32 +117,36 @@ XRemoteClient::Shutdown (void)
return NS_OK;
}
Window
XRemoteClient::FindWindow(void)
NS_IMETHODIMP
XRemoteClient::SendCommand (const char *aCommand, PRBool *aWindowFound)
{
PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("XRemoteClient::SendCommand"));
Window root = RootWindowOfScreen(DefaultScreenOfDisplay(mDisplay));
Window root2, parent, *kids;
unsigned int nkids;
Window result = 0;
int i;
*aWindowFound = PR_FALSE;
if (!XQueryTree(mDisplay, root, &root2, &parent, &kids, &nkids)) {
PR_LOG(sRemoteLm, PR_LOG_DEBUG,
("XQueryTree failed in XRemoteClient::FindWindow"));
return 0;
("XQueryTree failed in XRemoteClient::SendCommand"));
return NS_OK;
}
if (!(kids && nkids)) {
PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("root window has no children"));
return 0;
return NS_OK;
}
nsresult rv = NS_OK;
for (i=nkids-1; i >= 0; i--) {
Atom type;
int format;
unsigned long nitems, bytesafter;
unsigned char *data_return = 0;
Window w;
Window w, result = 0;
w = kids[i];
// find the inner window with WM_STATE on it
w = CheckWindow(w);
@ -224,11 +181,8 @@ XRemoteClient::FindWindow(void)
// if the IDs are equal then this is the window we want. if
// they aren't fall through to the next loop iteration.
if (!strcmp(logname, (const char *)data_return)) {
XFree(data_return);
if (!strcmp(logname, (const char *)data_return))
result = w;
break;
}
XFree(data_return);
}
@ -238,14 +192,40 @@ XRemoteClient::FindWindow(void)
// it.
else {
result = w;
break;
}
if (result) {
// ok, let the caller know that we at least found a window.
*aWindowFound = PR_TRUE;
// make sure we get the right events on that window
XSelectInput(mDisplay, result,
(PropertyChangeMask|StructureNotifyMask));
PRBool destroyed = PR_FALSE;
// get the lock on the window
rv = GetLock(result, &destroyed);
if (NS_SUCCEEDED(rv)) {
// send our command
rv = DoSendCommand(result, aCommand, &destroyed);
// if the window was destroyed, don't bother trying to free the
// lock.
if (!destroyed)
FreeLock(result); // doesn't really matter what this returns
// if accepted then we're done...
if (NS_SUCCEEDED(rv))
break;
}
}
}
}
PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("FindWindow returning 0x%lx\n", result));
return result;
PR_LOG(sRemoteLm, PR_LOG_DEBUG, ("SendCommand returning 0x%x\n", rv));
return rv;
}
Window

View File

@ -42,7 +42,6 @@ class XRemoteClient
private:
Window FindWindow (void);
Window CheckWindow (Window aWindow);
Window CheckChildren (Window aWindow);
nsresult GetLock (Window aWindow, PRBool *aDestroyed);
@ -63,5 +62,4 @@ private:
char *mLockData;
PRBool mInitialized;
};

View File

@ -42,6 +42,7 @@ REQUIRES = xpcom \
necko \
appshell \
rdf \
exthandler \
$(NULL)
CPPSRCS = \

View File

@ -47,6 +47,8 @@
#include <nsIURI.h>
#include <nsNetUtil.h>
#include <nsIWindowMediator.h>
#include <nsCExternalHandlerService.h>
#include <nsIExternalProtocolService.h>
NS_DEFINE_CID(kWindowCID, NS_WINDOW_CID);
@ -649,11 +651,41 @@ XRemoteService::GetComposeLocation(const char **_retval)
return NS_OK;
}
PRBool
XRemoteService::MayOpenURL(const nsCString &aURL)
{
// by default, we assume nothing can be loaded.
PRBool allowURL= PR_FALSE;
nsCOMPtr<nsIIOService> ios = do_GetIOService();
if (ios) {
nsCAutoString scheme;
ios->ExtractScheme(aURL, scheme);
if (!scheme.IsEmpty()) {
nsCOMPtr<nsIExternalProtocolService> extProtService =
do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
if (extProtService) {
// if the given URL scheme corresponds to an exposed protocol, then we
// can try to load it. otherwise, we must not.
PRBool isExposed;
nsresult rv = extProtService->IsExposedProtocol(scheme.get(), &isExposed);
if (NS_SUCCEEDED(rv) && isExposed)
allowURL = PR_TRUE; // ok, we can load this URL.
}
}
}
return allowURL;
}
nsresult
XRemoteService::OpenURL(nsCString &aArgument,
nsIDOMWindowInternal *aParent,
PRBool aOpenBrowser)
{
// check if we can handle this type of URL
if (!MayOpenURL(aArgument))
return NS_ERROR_ABORT;
// the eventual toplevel target of the load
nsCOMPtr<nsIDOMWindowInternal> finalWindow = aParent;

View File

@ -72,6 +72,9 @@ class XRemoteService : public nsIXRemoteService, public nsIObserver {
nsresult GetMailLocation(char **_retval);
nsresult GetComposeLocation(const char **_retval);
// returns true if the URL may be loaded.
PRBool MayOpenURL(const nsCString &aURL);
// remote command handlers
nsresult OpenURL(nsCString &aArgument,
nsIDOMWindowInternal *aParent,