1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/uriloader/exthandler/nsExternalProtocolHandler.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,426 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * vim:set ts=2 sts=2 sw=2 et cin: 1.6 + * 1.7 + * This Source Code Form is subject to the terms of the Mozilla Public 1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.10 + 1.11 +#include "nsIURI.h" 1.12 +#include "nsIURL.h" 1.13 +#include "nsExternalProtocolHandler.h" 1.14 +#include "nsXPIDLString.h" 1.15 +#include "nsReadableUtils.h" 1.16 +#include "nsCOMPtr.h" 1.17 +#include "nsIServiceManager.h" 1.18 +#include "nsServiceManagerUtils.h" 1.19 +#include "nsIInterfaceRequestor.h" 1.20 +#include "nsIInterfaceRequestorUtils.h" 1.21 +#include "nsIStringBundle.h" 1.22 +#include "nsIPrefService.h" 1.23 +#include "nsIPrompt.h" 1.24 +#include "nsNetUtil.h" 1.25 +#include "nsExternalHelperAppService.h" 1.26 + 1.27 +// used to dispatch urls to default protocol handlers 1.28 +#include "nsCExternalHandlerService.h" 1.29 +#include "nsIExternalProtocolService.h" 1.30 + 1.31 +//////////////////////////////////////////////////////////////////////// 1.32 +// a stub channel implemenation which will map calls to AsyncRead and OpenInputStream 1.33 +// to calls in the OS for loading the url. 1.34 +//////////////////////////////////////////////////////////////////////// 1.35 + 1.36 +class nsExtProtocolChannel : public nsIChannel 1.37 +{ 1.38 +public: 1.39 + NS_DECL_THREADSAFE_ISUPPORTS 1.40 + NS_DECL_NSICHANNEL 1.41 + NS_DECL_NSIREQUEST 1.42 + 1.43 + nsExtProtocolChannel(); 1.44 + virtual ~nsExtProtocolChannel(); 1.45 + 1.46 + nsresult SetURI(nsIURI*); 1.47 + 1.48 +private: 1.49 + nsresult OpenURL(); 1.50 + void Finish(nsresult aResult); 1.51 + 1.52 + nsCOMPtr<nsIURI> mUrl; 1.53 + nsCOMPtr<nsIURI> mOriginalURI; 1.54 + nsresult mStatus; 1.55 + nsLoadFlags mLoadFlags; 1.56 + bool mWasOpened; 1.57 + 1.58 + nsCOMPtr<nsIInterfaceRequestor> mCallbacks; 1.59 + nsCOMPtr<nsILoadGroup> mLoadGroup; 1.60 +}; 1.61 + 1.62 +NS_IMPL_ADDREF(nsExtProtocolChannel) 1.63 +NS_IMPL_RELEASE(nsExtProtocolChannel) 1.64 + 1.65 +NS_INTERFACE_MAP_BEGIN(nsExtProtocolChannel) 1.66 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel) 1.67 + NS_INTERFACE_MAP_ENTRY(nsIChannel) 1.68 + NS_INTERFACE_MAP_ENTRY(nsIRequest) 1.69 +NS_INTERFACE_MAP_END_THREADSAFE 1.70 + 1.71 +nsExtProtocolChannel::nsExtProtocolChannel() : mStatus(NS_OK), 1.72 + mWasOpened(false) 1.73 +{ 1.74 +} 1.75 + 1.76 +nsExtProtocolChannel::~nsExtProtocolChannel() 1.77 +{} 1.78 + 1.79 +NS_IMETHODIMP nsExtProtocolChannel::GetLoadGroup(nsILoadGroup * *aLoadGroup) 1.80 +{ 1.81 + NS_IF_ADDREF(*aLoadGroup = mLoadGroup); 1.82 + return NS_OK; 1.83 +} 1.84 + 1.85 +NS_IMETHODIMP nsExtProtocolChannel::SetLoadGroup(nsILoadGroup * aLoadGroup) 1.86 +{ 1.87 + mLoadGroup = aLoadGroup; 1.88 + return NS_OK; 1.89 +} 1.90 + 1.91 +NS_IMETHODIMP nsExtProtocolChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks) 1.92 +{ 1.93 + NS_IF_ADDREF(*aCallbacks = mCallbacks); 1.94 + return NS_OK; 1.95 +} 1.96 + 1.97 +NS_IMETHODIMP nsExtProtocolChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) 1.98 +{ 1.99 + mCallbacks = aCallbacks; 1.100 + return NS_OK; 1.101 +} 1.102 + 1.103 +NS_IMETHODIMP 1.104 +nsExtProtocolChannel::GetSecurityInfo(nsISupports * *aSecurityInfo) 1.105 +{ 1.106 + *aSecurityInfo = nullptr; 1.107 + return NS_OK; 1.108 +} 1.109 + 1.110 +NS_IMETHODIMP nsExtProtocolChannel::GetOriginalURI(nsIURI* *aURI) 1.111 +{ 1.112 + NS_ADDREF(*aURI = mOriginalURI); 1.113 + return NS_OK; 1.114 +} 1.115 + 1.116 +NS_IMETHODIMP nsExtProtocolChannel::SetOriginalURI(nsIURI* aURI) 1.117 +{ 1.118 + NS_ENSURE_ARG_POINTER(aURI); 1.119 + mOriginalURI = aURI; 1.120 + return NS_OK; 1.121 +} 1.122 + 1.123 +NS_IMETHODIMP nsExtProtocolChannel::GetURI(nsIURI* *aURI) 1.124 +{ 1.125 + *aURI = mUrl; 1.126 + NS_IF_ADDREF(*aURI); 1.127 + return NS_OK; 1.128 +} 1.129 + 1.130 +nsresult nsExtProtocolChannel::SetURI(nsIURI* aURI) 1.131 +{ 1.132 + mUrl = aURI; 1.133 + return NS_OK; 1.134 +} 1.135 + 1.136 +nsresult nsExtProtocolChannel::OpenURL() 1.137 +{ 1.138 + nsresult rv = NS_ERROR_FAILURE; 1.139 + nsCOMPtr<nsIExternalProtocolService> extProtService (do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID)); 1.140 + 1.141 + if (extProtService) 1.142 + { 1.143 +#ifdef DEBUG 1.144 + nsAutoCString urlScheme; 1.145 + mUrl->GetScheme(urlScheme); 1.146 + bool haveHandler = false; 1.147 + extProtService->ExternalProtocolHandlerExists(urlScheme.get(), &haveHandler); 1.148 + NS_ASSERTION(haveHandler, "Why do we have a channel for this url if we don't support the protocol?"); 1.149 +#endif 1.150 + 1.151 + nsCOMPtr<nsIInterfaceRequestor> aggCallbacks; 1.152 + rv = NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup, 1.153 + getter_AddRefs(aggCallbacks)); 1.154 + if (NS_FAILED(rv)) { 1.155 + goto finish; 1.156 + } 1.157 + 1.158 + rv = extProtService->LoadURI(mUrl, aggCallbacks); 1.159 + if (NS_SUCCEEDED(rv)) { 1.160 + // despite success, we need to abort this channel, at the very least 1.161 + // to make it clear to the caller that no on{Start,Stop}Request 1.162 + // should be expected. 1.163 + rv = NS_ERROR_NO_CONTENT; 1.164 + } 1.165 + } 1.166 + 1.167 +finish: 1.168 + mCallbacks = 0; 1.169 + return rv; 1.170 +} 1.171 + 1.172 +NS_IMETHODIMP nsExtProtocolChannel::Open(nsIInputStream **_retval) 1.173 +{ 1.174 + return OpenURL(); 1.175 +} 1.176 + 1.177 +NS_IMETHODIMP nsExtProtocolChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) 1.178 +{ 1.179 + NS_ENSURE_ARG_POINTER(listener); 1.180 + NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); 1.181 + 1.182 + mWasOpened = true; 1.183 + 1.184 + return OpenURL(); 1.185 +} 1.186 + 1.187 +NS_IMETHODIMP nsExtProtocolChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) 1.188 +{ 1.189 + *aLoadFlags = mLoadFlags; 1.190 + return NS_OK; 1.191 +} 1.192 + 1.193 +NS_IMETHODIMP nsExtProtocolChannel::SetLoadFlags(nsLoadFlags aLoadFlags) 1.194 +{ 1.195 + mLoadFlags = aLoadFlags; 1.196 + return NS_OK; 1.197 +} 1.198 + 1.199 +NS_IMETHODIMP nsExtProtocolChannel::GetContentType(nsACString &aContentType) 1.200 +{ 1.201 + return NS_ERROR_NOT_IMPLEMENTED; 1.202 +} 1.203 + 1.204 +NS_IMETHODIMP nsExtProtocolChannel::SetContentType(const nsACString &aContentType) 1.205 +{ 1.206 + return NS_ERROR_FAILURE; 1.207 +} 1.208 + 1.209 +NS_IMETHODIMP nsExtProtocolChannel::GetContentCharset(nsACString &aContentCharset) 1.210 +{ 1.211 + return NS_ERROR_NOT_IMPLEMENTED; 1.212 +} 1.213 + 1.214 +NS_IMETHODIMP nsExtProtocolChannel::SetContentCharset(const nsACString &aContentCharset) 1.215 +{ 1.216 + return NS_ERROR_NOT_IMPLEMENTED; 1.217 +} 1.218 + 1.219 +NS_IMETHODIMP nsExtProtocolChannel::GetContentDisposition(uint32_t *aContentDisposition) 1.220 +{ 1.221 + return NS_ERROR_NOT_AVAILABLE; 1.222 +} 1.223 + 1.224 +NS_IMETHODIMP nsExtProtocolChannel::SetContentDisposition(uint32_t aContentDisposition) 1.225 +{ 1.226 + return NS_ERROR_NOT_AVAILABLE; 1.227 +} 1.228 + 1.229 +NS_IMETHODIMP nsExtProtocolChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename) 1.230 +{ 1.231 + return NS_ERROR_NOT_AVAILABLE; 1.232 +} 1.233 + 1.234 +NS_IMETHODIMP nsExtProtocolChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename) 1.235 +{ 1.236 + return NS_ERROR_NOT_AVAILABLE; 1.237 +} 1.238 + 1.239 +NS_IMETHODIMP nsExtProtocolChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader) 1.240 +{ 1.241 + return NS_ERROR_NOT_AVAILABLE; 1.242 +} 1.243 + 1.244 +NS_IMETHODIMP nsExtProtocolChannel::GetContentLength(int64_t * aContentLength) 1.245 +{ 1.246 + *aContentLength = -1; 1.247 + return NS_OK; 1.248 +} 1.249 + 1.250 +NS_IMETHODIMP 1.251 +nsExtProtocolChannel::SetContentLength(int64_t aContentLength) 1.252 +{ 1.253 + NS_NOTREACHED("SetContentLength"); 1.254 + return NS_ERROR_NOT_IMPLEMENTED; 1.255 +} 1.256 + 1.257 +NS_IMETHODIMP nsExtProtocolChannel::GetOwner(nsISupports * *aPrincipal) 1.258 +{ 1.259 + NS_NOTREACHED("GetOwner"); 1.260 + return NS_ERROR_NOT_IMPLEMENTED; 1.261 +} 1.262 + 1.263 +NS_IMETHODIMP nsExtProtocolChannel::SetOwner(nsISupports * aPrincipal) 1.264 +{ 1.265 + NS_NOTREACHED("SetOwner"); 1.266 + return NS_ERROR_NOT_IMPLEMENTED; 1.267 +} 1.268 + 1.269 +//////////////////////////////////////////////////////////////////////////////// 1.270 +// From nsIRequest 1.271 +//////////////////////////////////////////////////////////////////////////////// 1.272 + 1.273 +NS_IMETHODIMP nsExtProtocolChannel::GetName(nsACString &result) 1.274 +{ 1.275 + return mUrl->GetSpec(result); 1.276 +} 1.277 + 1.278 +NS_IMETHODIMP nsExtProtocolChannel::IsPending(bool *result) 1.279 +{ 1.280 + *result = false; 1.281 + return NS_OK; 1.282 +} 1.283 + 1.284 +NS_IMETHODIMP nsExtProtocolChannel::GetStatus(nsresult *status) 1.285 +{ 1.286 + *status = mStatus; 1.287 + return NS_OK; 1.288 +} 1.289 + 1.290 +NS_IMETHODIMP nsExtProtocolChannel::Cancel(nsresult status) 1.291 +{ 1.292 + mStatus = status; 1.293 + return NS_OK; 1.294 +} 1.295 + 1.296 +NS_IMETHODIMP nsExtProtocolChannel::Suspend() 1.297 +{ 1.298 + NS_NOTREACHED("Suspend"); 1.299 + return NS_ERROR_NOT_IMPLEMENTED; 1.300 +} 1.301 + 1.302 +NS_IMETHODIMP nsExtProtocolChannel::Resume() 1.303 +{ 1.304 + NS_NOTREACHED("Resume"); 1.305 + return NS_ERROR_NOT_IMPLEMENTED; 1.306 +} 1.307 + 1.308 +/////////////////////////////////////////////////////////////////////// 1.309 +// the default protocol handler implementation 1.310 +////////////////////////////////////////////////////////////////////// 1.311 + 1.312 +nsExternalProtocolHandler::nsExternalProtocolHandler() 1.313 +{ 1.314 + m_schemeName = "default"; 1.315 +} 1.316 + 1.317 + 1.318 +nsExternalProtocolHandler::~nsExternalProtocolHandler() 1.319 +{} 1.320 + 1.321 +NS_IMPL_ADDREF(nsExternalProtocolHandler) 1.322 +NS_IMPL_RELEASE(nsExternalProtocolHandler) 1.323 + 1.324 +NS_INTERFACE_MAP_BEGIN(nsExternalProtocolHandler) 1.325 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIProtocolHandler) 1.326 + NS_INTERFACE_MAP_ENTRY(nsIProtocolHandler) 1.327 + NS_INTERFACE_MAP_ENTRY(nsIExternalProtocolHandler) 1.328 + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 1.329 +NS_INTERFACE_MAP_END_THREADSAFE 1.330 + 1.331 +NS_IMETHODIMP nsExternalProtocolHandler::GetScheme(nsACString &aScheme) 1.332 +{ 1.333 + aScheme = m_schemeName; 1.334 + return NS_OK; 1.335 +} 1.336 + 1.337 +NS_IMETHODIMP nsExternalProtocolHandler::GetDefaultPort(int32_t *aDefaultPort) 1.338 +{ 1.339 + *aDefaultPort = 0; 1.340 + return NS_OK; 1.341 +} 1.342 + 1.343 +NS_IMETHODIMP 1.344 +nsExternalProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) 1.345 +{ 1.346 + // don't override anything. 1.347 + *_retval = false; 1.348 + return NS_OK; 1.349 +} 1.350 +// returns TRUE if the OS can handle this protocol scheme and false otherwise. 1.351 +bool nsExternalProtocolHandler::HaveExternalProtocolHandler(nsIURI * aURI) 1.352 +{ 1.353 + bool haveHandler = false; 1.354 + if (aURI) 1.355 + { 1.356 + nsAutoCString scheme; 1.357 + aURI->GetScheme(scheme); 1.358 + nsCOMPtr<nsIExternalProtocolService> extProtSvc(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID)); 1.359 + if (extProtSvc) 1.360 + extProtSvc->ExternalProtocolHandlerExists(scheme.get(), &haveHandler); 1.361 + } 1.362 + 1.363 + return haveHandler; 1.364 +} 1.365 + 1.366 +NS_IMETHODIMP nsExternalProtocolHandler::GetProtocolFlags(uint32_t *aUritype) 1.367 +{ 1.368 + // Make it norelative since it is a simple uri 1.369 + *aUritype = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE | 1.370 + URI_NON_PERSISTABLE | URI_DOES_NOT_RETURN_DATA; 1.371 + return NS_OK; 1.372 +} 1.373 + 1.374 +NS_IMETHODIMP nsExternalProtocolHandler::NewURI(const nsACString &aSpec, 1.375 + const char *aCharset, // ignore charset info 1.376 + nsIURI *aBaseURI, 1.377 + nsIURI **_retval) 1.378 +{ 1.379 + nsresult rv; 1.380 + nsCOMPtr<nsIURI> uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID, &rv); 1.381 + NS_ENSURE_SUCCESS(rv, rv); 1.382 + 1.383 + rv = uri->SetSpec(aSpec); 1.384 + NS_ENSURE_SUCCESS(rv, rv); 1.385 + 1.386 + NS_ADDREF(*_retval = uri); 1.387 + return NS_OK; 1.388 +} 1.389 + 1.390 +NS_IMETHODIMP nsExternalProtocolHandler::NewChannel(nsIURI *aURI, nsIChannel **_retval) 1.391 +{ 1.392 + // Only try to return a channel if we have a protocol handler for the url. 1.393 + // nsOSHelperAppService::LoadUriInternal relies on this to check trustedness 1.394 + // for some platforms at least. (win uses ::ShellExecute and unix uses 1.395 + // gnome_url_show.) 1.396 + bool haveExternalHandler = HaveExternalProtocolHandler(aURI); 1.397 + if (haveExternalHandler) 1.398 + { 1.399 + nsCOMPtr<nsIChannel> channel = new nsExtProtocolChannel(); 1.400 + if (!channel) return NS_ERROR_OUT_OF_MEMORY; 1.401 + 1.402 + ((nsExtProtocolChannel*) channel.get())->SetURI(aURI); 1.403 + channel->SetOriginalURI(aURI); 1.404 + 1.405 + if (_retval) 1.406 + { 1.407 + *_retval = channel; 1.408 + NS_IF_ADDREF(*_retval); 1.409 + return NS_OK; 1.410 + } 1.411 + } 1.412 + 1.413 + return NS_ERROR_UNKNOWN_PROTOCOL; 1.414 +} 1.415 + 1.416 +/////////////////////////////////////////////////////////////////////// 1.417 +// External protocol handler interface implementation 1.418 +////////////////////////////////////////////////////////////////////// 1.419 +NS_IMETHODIMP nsExternalProtocolHandler::ExternalAppExistsForScheme(const nsACString& aScheme, bool *_retval) 1.420 +{ 1.421 + nsCOMPtr<nsIExternalProtocolService> extProtSvc(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID)); 1.422 + if (extProtSvc) 1.423 + return extProtSvc->ExternalProtocolHandlerExists( 1.424 + PromiseFlatCString(aScheme).get(), _retval); 1.425 + 1.426 + // In case we don't have external protocol service. 1.427 + *_retval = false; 1.428 + return NS_OK; 1.429 +}