1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/protocol/app/AppProtocolHandler.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,423 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set expandtab ts=2 sw=2 sts=2 cin: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "AppProtocolHandler.h" 1.11 +#include "nsBaseChannel.h" 1.12 +#include "nsJARChannel.h" 1.13 +#include "nsNetCID.h" 1.14 +#include "nsIAppsService.h" 1.15 +#include "nsCxPusher.h" 1.16 +#include "nsXULAppAPI.h" 1.17 + 1.18 +/** 1.19 + * This dummy channel implementation only provides enough functionality 1.20 + * to return a fake 404 error when the caller asks for an app:// URL 1.21 + * containing an unknown appId. 1.22 + */ 1.23 +class DummyChannel : public nsIJARChannel 1.24 + , nsRunnable 1.25 +{ 1.26 +public: 1.27 + NS_DECL_ISUPPORTS 1.28 + NS_DECL_NSIREQUEST 1.29 + NS_DECL_NSICHANNEL 1.30 + NS_DECL_NSIJARCHANNEL 1.31 + 1.32 + DummyChannel(); 1.33 + 1.34 + NS_IMETHODIMP Run(); 1.35 + 1.36 +private: 1.37 + bool mPending; 1.38 + uint32_t mSuspendCount; 1.39 + nsCOMPtr<nsISupports> mListenerContext; 1.40 + nsCOMPtr<nsIStreamListener> mListener; 1.41 + nsCOMPtr<nsILoadGroup> mLoadGroup; 1.42 + nsLoadFlags mLoadFlags; 1.43 +}; 1.44 + 1.45 +NS_IMPL_ISUPPORTS(DummyChannel, nsIRequest, nsIChannel, nsIJARChannel) 1.46 + 1.47 +DummyChannel::DummyChannel() : mPending(false) 1.48 + , mSuspendCount(0) 1.49 + , mLoadFlags(LOAD_NORMAL) 1.50 +{ 1.51 +} 1.52 + 1.53 +NS_IMETHODIMP DummyChannel::GetName(nsACString &result) 1.54 +{ 1.55 + result = "dummy_app_channel"; 1.56 + return NS_OK; 1.57 +} 1.58 + 1.59 +NS_IMETHODIMP DummyChannel::GetStatus(nsresult *aStatus) 1.60 +{ 1.61 + *aStatus = NS_ERROR_FILE_NOT_FOUND; 1.62 + return NS_OK; 1.63 +} 1.64 + 1.65 +NS_IMETHODIMP DummyChannel::IsPending(bool *aResult) 1.66 +{ 1.67 + *aResult = mPending; 1.68 + return NS_OK; 1.69 +} 1.70 + 1.71 +NS_IMETHODIMP DummyChannel::Suspend() 1.72 +{ 1.73 + mSuspendCount++; 1.74 + return NS_OK; 1.75 +} 1.76 + 1.77 +NS_IMETHODIMP DummyChannel::Resume() 1.78 +{ 1.79 + if (mSuspendCount <= 0) { 1.80 + return NS_ERROR_UNEXPECTED; 1.81 + } 1.82 + 1.83 + if (--mSuspendCount == 0) { 1.84 + NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL); 1.85 + } 1.86 + return NS_OK; 1.87 +} 1.88 + 1.89 +NS_IMETHODIMP DummyChannel::Open(nsIInputStream**) 1.90 +{ 1.91 + return NS_ERROR_NOT_IMPLEMENTED; 1.92 +} 1.93 + 1.94 +NS_IMETHODIMP DummyChannel::AsyncOpen(nsIStreamListener* aListener, nsISupports* aContext) 1.95 +{ 1.96 + mListener = aListener; 1.97 + mListenerContext = aContext; 1.98 + mPending = true; 1.99 + 1.100 + if (mLoadGroup) { 1.101 + mLoadGroup->AddRequest(this, aContext); 1.102 + } 1.103 + 1.104 + if (mSuspendCount == 0) { 1.105 + NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL); 1.106 + } 1.107 + 1.108 + return NS_OK; 1.109 +} 1.110 + 1.111 +// nsIJarChannel, needed for XHR to turn NS_ERROR_FILE_NOT_FOUND into 1.112 +// a 404 error. 1.113 +NS_IMETHODIMP DummyChannel::GetIsUnsafe(bool *aResult) 1.114 +{ 1.115 + *aResult = false; 1.116 + return NS_OK; 1.117 +} 1.118 + 1.119 +NS_IMETHODIMP DummyChannel::SetAppURI(nsIURI *aURI) 1.120 +{ 1.121 + return NS_ERROR_NOT_IMPLEMENTED; 1.122 +} 1.123 + 1.124 +NS_IMETHODIMP DummyChannel::Run() 1.125 +{ 1.126 + nsresult rv = mListener->OnStartRequest(this, mListenerContext); 1.127 + NS_ENSURE_SUCCESS(rv, rv); 1.128 + mPending = false; 1.129 + rv = mListener->OnStopRequest(this, mListenerContext, NS_ERROR_FILE_NOT_FOUND); 1.130 + NS_ENSURE_SUCCESS(rv, rv); 1.131 + if (mLoadGroup) { 1.132 + mLoadGroup->RemoveRequest(this, mListenerContext, NS_ERROR_FILE_NOT_FOUND); 1.133 + } 1.134 + 1.135 + mListener = nullptr; 1.136 + mListenerContext = nullptr; 1.137 + rv = SetNotificationCallbacks(nullptr); 1.138 + NS_ENSURE_SUCCESS(rv, rv); 1.139 + 1.140 + return NS_OK; 1.141 +} 1.142 + 1.143 +NS_IMETHODIMP DummyChannel::Cancel(nsresult) 1.144 +{ 1.145 + return NS_ERROR_NOT_IMPLEMENTED; 1.146 +} 1.147 + 1.148 +NS_IMETHODIMP DummyChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup) 1.149 +{ 1.150 + *aLoadGroup = mLoadGroup; 1.151 + NS_IF_ADDREF(*aLoadGroup); 1.152 + return NS_OK; 1.153 +} 1.154 + 1.155 +NS_IMETHODIMP DummyChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) 1.156 +{ 1.157 + mLoadGroup = aLoadGroup; 1.158 + return NS_OK; 1.159 +} 1.160 + 1.161 +NS_IMETHODIMP DummyChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) 1.162 +{ 1.163 + *aLoadFlags = mLoadFlags; 1.164 + return NS_OK; 1.165 +} 1.166 + 1.167 +NS_IMETHODIMP DummyChannel::SetLoadFlags(nsLoadFlags aLoadFlags) 1.168 +{ 1.169 + mLoadFlags = aLoadFlags; 1.170 + return NS_OK; 1.171 +} 1.172 + 1.173 +NS_IMETHODIMP DummyChannel::GetOriginalURI(nsIURI**) 1.174 +{ 1.175 + return NS_ERROR_NOT_IMPLEMENTED; 1.176 +} 1.177 + 1.178 +NS_IMETHODIMP DummyChannel::SetOriginalURI(nsIURI*) 1.179 +{ 1.180 + return NS_ERROR_NOT_IMPLEMENTED; 1.181 +} 1.182 + 1.183 +NS_IMETHODIMP DummyChannel::GetOwner(nsISupports**) 1.184 +{ 1.185 + return NS_ERROR_NOT_IMPLEMENTED; 1.186 +} 1.187 + 1.188 +NS_IMETHODIMP DummyChannel::SetOwner(nsISupports*) 1.189 +{ 1.190 + return NS_ERROR_NOT_IMPLEMENTED; 1.191 +} 1.192 + 1.193 +NS_IMETHODIMP DummyChannel::GetNotificationCallbacks(nsIInterfaceRequestor**) 1.194 +{ 1.195 + return NS_ERROR_NOT_IMPLEMENTED; 1.196 +} 1.197 + 1.198 +NS_IMETHODIMP DummyChannel::SetNotificationCallbacks(nsIInterfaceRequestor*) 1.199 +{ 1.200 + return NS_ERROR_NOT_IMPLEMENTED; 1.201 +} 1.202 + 1.203 +NS_IMETHODIMP DummyChannel::GetSecurityInfo(nsISupports**) 1.204 +{ 1.205 + return NS_ERROR_NOT_IMPLEMENTED; 1.206 +} 1.207 + 1.208 +NS_IMETHODIMP DummyChannel::GetContentType(nsACString&) 1.209 +{ 1.210 + return NS_ERROR_NOT_IMPLEMENTED; 1.211 +} 1.212 + 1.213 +NS_IMETHODIMP DummyChannel::SetContentType(const nsACString&) 1.214 +{ 1.215 + return NS_ERROR_NOT_IMPLEMENTED; 1.216 +} 1.217 + 1.218 +NS_IMETHODIMP DummyChannel::GetContentCharset(nsACString&) 1.219 +{ 1.220 + return NS_ERROR_NOT_IMPLEMENTED; 1.221 +} 1.222 + 1.223 +NS_IMETHODIMP DummyChannel::SetContentCharset(const nsACString&) 1.224 +{ 1.225 + return NS_ERROR_NOT_IMPLEMENTED; 1.226 +} 1.227 + 1.228 +NS_IMETHODIMP DummyChannel::GetContentLength(int64_t*) 1.229 +{ 1.230 + return NS_ERROR_NOT_IMPLEMENTED; 1.231 +} 1.232 + 1.233 +NS_IMETHODIMP DummyChannel::SetContentLength(int64_t) 1.234 +{ 1.235 + return NS_ERROR_NOT_IMPLEMENTED; 1.236 +} 1.237 + 1.238 +NS_IMETHODIMP DummyChannel::GetContentDisposition(uint32_t*) 1.239 +{ 1.240 + return NS_ERROR_NOT_IMPLEMENTED; 1.241 +} 1.242 + 1.243 +NS_IMETHODIMP DummyChannel::SetContentDisposition(uint32_t) 1.244 +{ 1.245 + return NS_ERROR_NOT_IMPLEMENTED; 1.246 +} 1.247 + 1.248 +NS_IMETHODIMP DummyChannel::GetURI(nsIURI**) 1.249 +{ 1.250 + return NS_ERROR_NOT_IMPLEMENTED; 1.251 +} 1.252 + 1.253 +NS_IMETHODIMP DummyChannel::GetContentDispositionFilename(nsAString&) 1.254 +{ 1.255 + return NS_ERROR_NOT_IMPLEMENTED; 1.256 +} 1.257 + 1.258 +NS_IMETHODIMP DummyChannel::SetContentDispositionFilename(nsAString const &) 1.259 +{ 1.260 + return NS_ERROR_NOT_IMPLEMENTED; 1.261 +} 1.262 + 1.263 +NS_IMETHODIMP DummyChannel::GetContentDispositionHeader(nsACString&) 1.264 +{ 1.265 + return NS_ERROR_NOT_IMPLEMENTED; 1.266 +} 1.267 + 1.268 +/** 1.269 + * app:// protocol implementation. 1.270 + */ 1.271 + 1.272 +AppProtocolHandler::AppProtocolHandler() { 1.273 +} 1.274 + 1.275 +AppProtocolHandler::~AppProtocolHandler() { 1.276 + mAppInfoCache.Clear(); 1.277 +} 1.278 + 1.279 +NS_IMPL_ISUPPORTS(AppProtocolHandler, nsIProtocolHandler) 1.280 + 1.281 +/* static */ 1.282 +nsresult 1.283 +AppProtocolHandler::Create(nsISupports* aOuter, 1.284 + const nsIID& aIID, 1.285 + void* *aResult) 1.286 +{ 1.287 + // Instantiate the service here since that intializes gJarHandler, which we 1.288 + // use indirectly (via our new JarChannel) in NewChannel. 1.289 + nsCOMPtr<nsIProtocolHandler> jarInitializer( 1.290 + do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "jar")); 1.291 + AppProtocolHandler* ph = new AppProtocolHandler(); 1.292 + if (ph == nullptr) { 1.293 + return NS_ERROR_OUT_OF_MEMORY; 1.294 + } 1.295 + NS_ADDREF(ph); 1.296 + nsresult rv = ph->QueryInterface(aIID, aResult); 1.297 + NS_RELEASE(ph); 1.298 + return rv; 1.299 +} 1.300 + 1.301 +NS_IMETHODIMP 1.302 +AppProtocolHandler::GetScheme(nsACString &aResult) 1.303 +{ 1.304 + aResult.AssignLiteral("app"); 1.305 + return NS_OK; 1.306 +} 1.307 + 1.308 +NS_IMETHODIMP 1.309 +AppProtocolHandler::GetDefaultPort(int32_t *aResult) 1.310 +{ 1.311 + // No ports for the app protocol. 1.312 + *aResult = -1; 1.313 + return NS_OK; 1.314 +} 1.315 + 1.316 +NS_IMETHODIMP 1.317 +AppProtocolHandler::GetProtocolFlags(uint32_t *aResult) 1.318 +{ 1.319 + *aResult = URI_NOAUTH | 1.320 + URI_DANGEROUS_TO_LOAD | 1.321 + URI_CROSS_ORIGIN_NEEDS_WEBAPPS_PERM; 1.322 + return NS_OK; 1.323 +} 1.324 + 1.325 +NS_IMETHODIMP 1.326 +AppProtocolHandler::NewURI(const nsACString &aSpec, 1.327 + const char *aCharset, // ignore charset info 1.328 + nsIURI *aBaseURI, 1.329 + nsIURI **result) 1.330 +{ 1.331 + nsresult rv; 1.332 + nsCOMPtr<nsIStandardURL> surl(do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv)); 1.333 + NS_ENSURE_SUCCESS(rv, rv); 1.334 + 1.335 + rv = surl->Init(nsIStandardURL::URLTYPE_STANDARD, -1, aSpec, aCharset, aBaseURI); 1.336 + NS_ENSURE_SUCCESS(rv, rv); 1.337 + 1.338 + nsCOMPtr<nsIURL> url(do_QueryInterface(surl, &rv)); 1.339 + NS_ENSURE_SUCCESS(rv, rv); 1.340 + 1.341 + url.forget(result); 1.342 + return NS_OK; 1.343 +} 1.344 + 1.345 +// We map app://ABCDEF/path/to/file.ext to 1.346 +// jar:file:///path/to/profile/webapps/ABCDEF/application.zip!/path/to/file.ext 1.347 +NS_IMETHODIMP 1.348 +AppProtocolHandler::NewChannel(nsIURI* aUri, nsIChannel* *aResult) 1.349 +{ 1.350 + NS_ENSURE_ARG_POINTER(aUri); 1.351 + nsRefPtr<nsJARChannel> channel = new nsJARChannel(); 1.352 + 1.353 + nsAutoCString host; 1.354 + nsresult rv = aUri->GetHost(host); 1.355 + NS_ENSURE_SUCCESS(rv, rv); 1.356 + 1.357 + nsAutoCString fileSpec; 1.358 + nsCOMPtr<nsIURL> url = do_QueryInterface(aUri); 1.359 + rv = url->GetFilePath(fileSpec); 1.360 + NS_ENSURE_SUCCESS(rv, rv); 1.361 + 1.362 + mozilla::dom::AppInfo *appInfo; 1.363 + 1.364 + if (!mAppInfoCache.Get(host, &appInfo)) { 1.365 + nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID); 1.366 + if (!appsService) { 1.367 + return NS_ERROR_FAILURE; 1.368 + } 1.369 + 1.370 + mozilla::AutoSafeJSContext cx; 1.371 + JS::RootedValue jsInfo(cx); 1.372 + rv = appsService->GetAppInfo(NS_ConvertUTF8toUTF16(host), &jsInfo); 1.373 + if (NS_FAILED(rv) || !jsInfo.isObject()) { 1.374 + // Return a DummyChannel. 1.375 + printf_stderr("!! Creating a dummy channel for %s (no appInfo)\n", host.get()); 1.376 + NS_IF_ADDREF(*aResult = new DummyChannel()); 1.377 + return NS_OK; 1.378 + } 1.379 + 1.380 + appInfo = new mozilla::dom::AppInfo(); 1.381 + JSAutoCompartment ac(cx, &jsInfo.toObject()); 1.382 + if (!appInfo->Init(cx, jsInfo) || appInfo->mPath.IsEmpty()) { 1.383 + // Return a DummyChannel. 1.384 + printf_stderr("!! Creating a dummy channel for %s (invalid appInfo)\n", host.get()); 1.385 + NS_IF_ADDREF(*aResult = new DummyChannel()); 1.386 + return NS_OK; 1.387 + } 1.388 + mAppInfoCache.Put(host, appInfo); 1.389 + } 1.390 + 1.391 + bool noRemote = (appInfo->mIsCoreApp || 1.392 + XRE_GetProcessType() == GeckoProcessType_Default); 1.393 + 1.394 + // In-parent and CoreApps can directly access files, so use jar:file:// 1.395 + nsAutoCString jarSpec(noRemote ? "jar:file://" 1.396 + : "jar:remoteopenfile://"); 1.397 + jarSpec += NS_ConvertUTF16toUTF8(appInfo->mPath) + 1.398 + NS_LITERAL_CSTRING("/application.zip!") + 1.399 + fileSpec; 1.400 + 1.401 + nsCOMPtr<nsIURI> jarURI; 1.402 + rv = NS_NewURI(getter_AddRefs(jarURI), 1.403 + jarSpec, nullptr, nullptr); 1.404 + NS_ENSURE_SUCCESS(rv, rv); 1.405 + 1.406 + rv = channel->Init(jarURI); 1.407 + NS_ENSURE_SUCCESS(rv, rv); 1.408 + 1.409 + rv = channel->SetAppURI(aUri); 1.410 + NS_ENSURE_SUCCESS(rv, rv); 1.411 + 1.412 + rv = channel->SetOriginalURI(aUri); 1.413 + NS_ENSURE_SUCCESS(rv, rv); 1.414 + 1.415 + channel.forget(aResult); 1.416 + return NS_OK; 1.417 +} 1.418 + 1.419 +NS_IMETHODIMP 1.420 +AppProtocolHandler::AllowPort(int32_t aPort, const char *aScheme, bool *aRetval) 1.421 +{ 1.422 + // No port allowed for this scheme. 1.423 + *aRetval = false; 1.424 + return NS_OK; 1.425 +} 1.426 +