1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/uriloader/exthandler/win/nsOSHelperAppService.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,698 @@ 1.4 +/* -*- Mode: C++; tab-width: 3; 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 "nsOSHelperAppService.h" 1.12 +#include "nsISupports.h" 1.13 +#include "nsString.h" 1.14 +#include "nsXPIDLString.h" 1.15 +#include "nsIURL.h" 1.16 +#include "nsIMIMEInfo.h" 1.17 +#include "nsMIMEInfoWin.h" 1.18 +#include "nsMimeTypes.h" 1.19 +#include "nsILocalFileWin.h" 1.20 +#include "nsIProcess.h" 1.21 +#include "plstr.h" 1.22 +#include "nsAutoPtr.h" 1.23 +#include "nsNativeCharsetUtils.h" 1.24 +#include "nsIWindowsRegKey.h" 1.25 + 1.26 +// shellapi.h is needed to build with WIN32_LEAN_AND_MEAN 1.27 +#include <shellapi.h> 1.28 + 1.29 +#define LOG(args) PR_LOG(mLog, PR_LOG_DEBUG, args) 1.30 + 1.31 +// helper methods: forward declarations... 1.32 +static nsresult GetExtensionFrom4xRegistryInfo(const nsACString& aMimeType, 1.33 + nsString& aFileExtension); 1.34 +static nsresult GetExtensionFromWindowsMimeDatabase(const nsACString& aMimeType, 1.35 + nsString& aFileExtension); 1.36 + 1.37 +nsOSHelperAppService::nsOSHelperAppService() : 1.38 + nsExternalHelperAppService() 1.39 + , mAppAssoc(nullptr) 1.40 +{ 1.41 + CoInitialize(nullptr); 1.42 + CoCreateInstance(CLSID_ApplicationAssociationRegistration, nullptr, 1.43 + CLSCTX_INPROC, IID_IApplicationAssociationRegistration, 1.44 + (void**)&mAppAssoc); 1.45 +} 1.46 + 1.47 +nsOSHelperAppService::~nsOSHelperAppService() 1.48 +{ 1.49 + if (mAppAssoc) 1.50 + mAppAssoc->Release(); 1.51 + mAppAssoc = nullptr; 1.52 + CoUninitialize(); 1.53 +} 1.54 + 1.55 +// The windows registry provides a mime database key which lists a set of mime types and corresponding "Extension" values. 1.56 +// we can use this to look up our mime type to see if there is a preferred extension for the mime type. 1.57 +static nsresult GetExtensionFromWindowsMimeDatabase(const nsACString& aMimeType, 1.58 + nsString& aFileExtension) 1.59 +{ 1.60 + nsAutoString mimeDatabaseKey; 1.61 + mimeDatabaseKey.AssignLiteral("MIME\\Database\\Content Type\\"); 1.62 + 1.63 + AppendASCIItoUTF16(aMimeType, mimeDatabaseKey); 1.64 + 1.65 + nsCOMPtr<nsIWindowsRegKey> regKey = 1.66 + do_CreateInstance("@mozilla.org/windows-registry-key;1"); 1.67 + if (!regKey) 1.68 + return NS_ERROR_NOT_AVAILABLE; 1.69 + 1.70 + nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, 1.71 + mimeDatabaseKey, 1.72 + nsIWindowsRegKey::ACCESS_QUERY_VALUE); 1.73 + 1.74 + if (NS_SUCCEEDED(rv)) 1.75 + regKey->ReadStringValue(NS_LITERAL_STRING("Extension"), aFileExtension); 1.76 + 1.77 + return NS_OK; 1.78 +} 1.79 + 1.80 +// We have a serious problem!! I have this content type and the windows registry only gives me 1.81 +// helper apps based on extension. Right now, we really don't have a good place to go for 1.82 +// trying to figure out the extension for a particular mime type....One short term hack is to look 1.83 +// this information in 4.x (it's stored in the windows regsitry). 1.84 +static nsresult GetExtensionFrom4xRegistryInfo(const nsACString& aMimeType, 1.85 + nsString& aFileExtension) 1.86 +{ 1.87 + nsCOMPtr<nsIWindowsRegKey> regKey = 1.88 + do_CreateInstance("@mozilla.org/windows-registry-key;1"); 1.89 + if (!regKey) 1.90 + return NS_ERROR_NOT_AVAILABLE; 1.91 + 1.92 + nsresult rv = regKey-> 1.93 + Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, 1.94 + NS_LITERAL_STRING("Software\\Netscape\\Netscape Navigator\\Suffixes"), 1.95 + nsIWindowsRegKey::ACCESS_QUERY_VALUE); 1.96 + if (NS_FAILED(rv)) 1.97 + return NS_ERROR_NOT_AVAILABLE; 1.98 + 1.99 + rv = regKey->ReadStringValue(NS_ConvertASCIItoUTF16(aMimeType), 1.100 + aFileExtension); 1.101 + if (NS_FAILED(rv)) 1.102 + return NS_OK; 1.103 + 1.104 + aFileExtension.Insert(char16_t('.'), 0); 1.105 + 1.106 + // this may be a comma separated list of extensions...just take the 1.107 + // first one for now... 1.108 + 1.109 + int32_t pos = aFileExtension.FindChar(char16_t(',')); 1.110 + if (pos > 0) { 1.111 + // we have a comma separated list of types... 1.112 + // truncate everything after the first comma (including the comma) 1.113 + aFileExtension.Truncate(pos); 1.114 + } 1.115 + 1.116 + return NS_OK; 1.117 +} 1.118 + 1.119 +nsresult nsOSHelperAppService::OSProtocolHandlerExists(const char * aProtocolScheme, bool * aHandlerExists) 1.120 +{ 1.121 + // look up the protocol scheme in the windows registry....if we find a match then we have a handler for it... 1.122 + *aHandlerExists = false; 1.123 + if (aProtocolScheme && *aProtocolScheme) 1.124 + { 1.125 + // Vista: use new application association interface 1.126 + if (mAppAssoc) { 1.127 + wchar_t * pResult = nullptr; 1.128 + NS_ConvertASCIItoUTF16 scheme(aProtocolScheme); 1.129 + // We are responsible for freeing returned strings. 1.130 + HRESULT hr = mAppAssoc->QueryCurrentDefault(scheme.get(), 1.131 + AT_URLPROTOCOL, AL_EFFECTIVE, 1.132 + &pResult); 1.133 + if (SUCCEEDED(hr)) { 1.134 + CoTaskMemFree(pResult); 1.135 + *aHandlerExists = true; 1.136 + } 1.137 + return NS_OK; 1.138 + } 1.139 + 1.140 + HKEY hKey; 1.141 + LONG err = ::RegOpenKeyExW(HKEY_CLASSES_ROOT, 1.142 + NS_ConvertASCIItoUTF16(aProtocolScheme).get(), 1.143 + 0, 1.144 + KEY_QUERY_VALUE, 1.145 + &hKey); 1.146 + if (err == ERROR_SUCCESS) 1.147 + { 1.148 + err = ::RegQueryValueExW(hKey, L"URL Protocol", 1.149 + nullptr, nullptr, nullptr, nullptr); 1.150 + *aHandlerExists = (err == ERROR_SUCCESS); 1.151 + // close the key 1.152 + ::RegCloseKey(hKey); 1.153 + } 1.154 + } 1.155 + 1.156 + return NS_OK; 1.157 +} 1.158 + 1.159 +NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval) 1.160 +{ 1.161 + nsCOMPtr<nsIWindowsRegKey> regKey = 1.162 + do_CreateInstance("@mozilla.org/windows-registry-key;1"); 1.163 + if (!regKey) 1.164 + return NS_ERROR_NOT_AVAILABLE; 1.165 + 1.166 + NS_ConvertASCIItoUTF16 buf(aScheme); 1.167 + 1.168 + // Vista: use new application association interface 1.169 + if (mAppAssoc) { 1.170 + wchar_t * pResult = nullptr; 1.171 + // We are responsible for freeing returned strings. 1.172 + HRESULT hr = mAppAssoc->QueryCurrentDefault(buf.get(), 1.173 + AT_URLPROTOCOL, AL_EFFECTIVE, 1.174 + &pResult); 1.175 + if (SUCCEEDED(hr)) { 1.176 + nsCOMPtr<nsIFile> app; 1.177 + nsAutoString appInfo(pResult); 1.178 + CoTaskMemFree(pResult); 1.179 + if (NS_SUCCEEDED(GetDefaultAppInfo(appInfo, _retval, getter_AddRefs(app)))) 1.180 + return NS_OK; 1.181 + } 1.182 + return NS_ERROR_NOT_AVAILABLE; 1.183 + } 1.184 + 1.185 + nsCOMPtr<nsIFile> app; 1.186 + GetDefaultAppInfo(buf, _retval, getter_AddRefs(app)); 1.187 + 1.188 + if (!_retval.Equals(buf)) 1.189 + return NS_OK; 1.190 + 1.191 + // Fall back to full path 1.192 + buf.AppendLiteral("\\shell\\open\\command"); 1.193 + nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, 1.194 + buf, 1.195 + nsIWindowsRegKey::ACCESS_QUERY_VALUE); 1.196 + if (NS_FAILED(rv)) 1.197 + return NS_ERROR_NOT_AVAILABLE; 1.198 + 1.199 + rv = regKey->ReadStringValue(EmptyString(), _retval); 1.200 + 1.201 + return NS_SUCCEEDED(rv) ? NS_OK : NS_ERROR_NOT_AVAILABLE; 1.202 +} 1.203 + 1.204 +// GetMIMEInfoFromRegistry: This function obtains the values of some of the nsIMIMEInfo 1.205 +// attributes for the mimeType/extension associated with the input registry key. The default 1.206 +// entry for that key is the name of a registry key under HKEY_CLASSES_ROOT. The default 1.207 +// value for *that* key is the descriptive name of the type. The EditFlags value is a binary 1.208 +// value; the low order bit of the third byte of which indicates that the user does not need 1.209 +// to be prompted. 1.210 +// 1.211 +// This function sets only the Description attribute of the input nsIMIMEInfo. 1.212 +/* static */ 1.213 +nsresult nsOSHelperAppService::GetMIMEInfoFromRegistry(const nsAFlatString& fileType, nsIMIMEInfo *pInfo) 1.214 +{ 1.215 + nsresult rv = NS_OK; 1.216 + 1.217 + NS_ENSURE_ARG(pInfo); 1.218 + nsCOMPtr<nsIWindowsRegKey> regKey = 1.219 + do_CreateInstance("@mozilla.org/windows-registry-key;1"); 1.220 + if (!regKey) 1.221 + return NS_ERROR_NOT_AVAILABLE; 1.222 + 1.223 + rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, 1.224 + fileType, nsIWindowsRegKey::ACCESS_QUERY_VALUE); 1.225 + if (NS_FAILED(rv)) 1.226 + return NS_ERROR_FAILURE; 1.227 + 1.228 + // OK, the default value here is the description of the type. 1.229 + nsAutoString description; 1.230 + rv = regKey->ReadStringValue(EmptyString(), description); 1.231 + if (NS_SUCCEEDED(rv)) 1.232 + pInfo->SetDescription(description); 1.233 + 1.234 + return NS_OK; 1.235 +} 1.236 + 1.237 +///////////////////////////////////////////////////////////////////////////////////////////////// 1.238 +// method overrides used to gather information from the windows registry for 1.239 +// various mime types. 1.240 +//////////////////////////////////////////////////////////////////////////////////////////////// 1.241 + 1.242 +/// Looks up the type for the extension aExt and compares it to aType 1.243 +/* static */ bool 1.244 +nsOSHelperAppService::typeFromExtEquals(const char16_t* aExt, const char *aType) 1.245 +{ 1.246 + if (!aType) 1.247 + return false; 1.248 + nsAutoString fileExtToUse; 1.249 + if (aExt[0] != char16_t('.')) 1.250 + fileExtToUse = char16_t('.'); 1.251 + 1.252 + fileExtToUse.Append(aExt); 1.253 + 1.254 + bool eq = false; 1.255 + nsCOMPtr<nsIWindowsRegKey> regKey = 1.256 + do_CreateInstance("@mozilla.org/windows-registry-key;1"); 1.257 + if (!regKey) 1.258 + return eq; 1.259 + 1.260 + nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, 1.261 + fileExtToUse, 1.262 + nsIWindowsRegKey::ACCESS_QUERY_VALUE); 1.263 + if (NS_FAILED(rv)) 1.264 + return eq; 1.265 + 1.266 + nsAutoString type; 1.267 + rv = regKey->ReadStringValue(NS_LITERAL_STRING("Content Type"), type); 1.268 + if (NS_SUCCEEDED(rv)) 1.269 + eq = type.EqualsASCII(aType); 1.270 + 1.271 + return eq; 1.272 +} 1.273 + 1.274 +// Strip a handler command string of its quotes and parameters. 1.275 +static void CleanupHandlerPath(nsString& aPath) 1.276 +{ 1.277 + // Example command strings passed into this routine: 1.278 + 1.279 + // 1) C:\Program Files\Company\some.exe -foo -bar 1.280 + // 2) C:\Program Files\Company\some.dll 1.281 + // 3) C:\Windows\some.dll,-foo -bar 1.282 + // 4) C:\Windows\some.cpl,-foo -bar 1.283 + 1.284 + int32_t lastCommaPos = aPath.RFindChar(','); 1.285 + if (lastCommaPos != kNotFound) 1.286 + aPath.Truncate(lastCommaPos); 1.287 + 1.288 + aPath.AppendLiteral(" "); 1.289 + 1.290 + // case insensitive 1.291 + uint32_t index = aPath.Find(".exe ", true); 1.292 + if (index == kNotFound) 1.293 + index = aPath.Find(".dll ", true); 1.294 + if (index == kNotFound) 1.295 + index = aPath.Find(".cpl ", true); 1.296 + 1.297 + if (index != kNotFound) 1.298 + aPath.Truncate(index + 4); 1.299 + aPath.Trim(" ", true, true); 1.300 +} 1.301 + 1.302 +// Strip the windows host process bootstrap executable rundll32.exe 1.303 +// from a handler's command string if it exists. 1.304 +static void StripRundll32(nsString& aCommandString) 1.305 +{ 1.306 + // Example rundll formats: 1.307 + // C:\Windows\System32\rundll32.exe "path to dll" 1.308 + // rundll32.exe "path to dll" 1.309 + // C:\Windows\System32\rundll32.exe "path to dll", var var 1.310 + // rundll32.exe "path to dll", var var 1.311 + 1.312 + NS_NAMED_LITERAL_STRING(rundllSegment, "rundll32.exe "); 1.313 + NS_NAMED_LITERAL_STRING(rundllSegmentShort, "rundll32 "); 1.314 + 1.315 + // case insensitive 1.316 + int32_t strLen = rundllSegment.Length(); 1.317 + int32_t index = aCommandString.Find(rundllSegment, true); 1.318 + if (index == kNotFound) { 1.319 + strLen = rundllSegmentShort.Length(); 1.320 + index = aCommandString.Find(rundllSegmentShort, true); 1.321 + } 1.322 + 1.323 + if (index != kNotFound) { 1.324 + uint32_t rundllSegmentLength = index + strLen; 1.325 + aCommandString.Cut(0, rundllSegmentLength); 1.326 + } 1.327 +} 1.328 + 1.329 +// Returns the fully qualified path to an application handler based on 1.330 +// a parameterized command string. Note this routine should not be used 1.331 +// to launch the associated application as it strips parameters and 1.332 +// rundll.exe from the string. Designed for retrieving display information 1.333 +// on a particular handler. 1.334 +/* static */ bool nsOSHelperAppService::CleanupCmdHandlerPath(nsAString& aCommandHandler) 1.335 +{ 1.336 + nsAutoString handlerCommand(aCommandHandler); 1.337 + 1.338 + // Straight command path: 1.339 + // 1.340 + // %SystemRoot%\system32\NOTEPAD.EXE var 1.341 + // "C:\Program Files\iTunes\iTunes.exe" var var 1.342 + // C:\Program Files\iTunes\iTunes.exe var var 1.343 + // 1.344 + // Example rundll handlers: 1.345 + // 1.346 + // rundll32.exe "%ProgramFiles%\Win...ery\PhotoViewer.dll", var var 1.347 + // rundll32.exe "%ProgramFiles%\Windows Photo Gallery\PhotoViewer.dll" 1.348 + // C:\Windows\System32\rundll32.exe "path to dll", var var 1.349 + // %SystemRoot%\System32\rundll32.exe "%ProgramFiles%\Win...ery\Photo 1.350 + // Viewer.dll", var var 1.351 + 1.352 + // Expand environment variables so we have full path strings. 1.353 + uint32_t bufLength = ::ExpandEnvironmentStringsW(handlerCommand.get(), 1.354 + L"", 0); 1.355 + if (bufLength == 0) // Error 1.356 + return false; 1.357 + 1.358 + nsAutoArrayPtr<wchar_t> destination(new wchar_t[bufLength]); 1.359 + if (!destination) 1.360 + return false; 1.361 + if (!::ExpandEnvironmentStringsW(handlerCommand.get(), destination, 1.362 + bufLength)) 1.363 + return false; 1.364 + 1.365 + handlerCommand = static_cast<const wchar_t*>(destination); 1.366 + 1.367 + // Remove quotes around paths 1.368 + handlerCommand.StripChars("\""); 1.369 + 1.370 + // Strip windows host process bootstrap so we can get to the actual 1.371 + // handler. 1.372 + StripRundll32(handlerCommand); 1.373 + 1.374 + // Trim any command parameters so that we have a native path we can 1.375 + // initialize a local file with. 1.376 + CleanupHandlerPath(handlerCommand); 1.377 + 1.378 + aCommandHandler.Assign(handlerCommand); 1.379 + return true; 1.380 +} 1.381 + 1.382 +// The "real" name of a given helper app (as specified by the path to the 1.383 +// executable file held in various registry keys) is stored n the VERSIONINFO 1.384 +// block in the file's resources. We need to find the path to the executable 1.385 +// and then retrieve the "FileDescription" field value from the file. 1.386 +nsresult 1.387 +nsOSHelperAppService::GetDefaultAppInfo(const nsAString& aAppInfo, 1.388 + nsAString& aDefaultDescription, 1.389 + nsIFile** aDefaultApplication) 1.390 +{ 1.391 + nsAutoString handlerCommand; 1.392 + 1.393 + // If all else fails, use the file type key name, which will be 1.394 + // something like "pngfile" for .pngs, "WMVFile" for .wmvs, etc. 1.395 + aDefaultDescription = aAppInfo; 1.396 + *aDefaultApplication = nullptr; 1.397 + 1.398 + if (aAppInfo.IsEmpty()) 1.399 + return NS_ERROR_FAILURE; 1.400 + 1.401 + // aAppInfo may be a file, file path, program id, or 1.402 + // Applications reference - 1.403 + // c:\dir\app.exe 1.404 + // Applications\appfile.exe/dll (shell\open...) 1.405 + // ProgID.progid (shell\open...) 1.406 + 1.407 + nsAutoString handlerKeyName(aAppInfo); 1.408 + 1.409 + nsCOMPtr<nsIWindowsRegKey> chkKey = 1.410 + do_CreateInstance("@mozilla.org/windows-registry-key;1"); 1.411 + if (!chkKey) 1.412 + return NS_ERROR_FAILURE; 1.413 + 1.414 + nsresult rv = chkKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, 1.415 + handlerKeyName, 1.416 + nsIWindowsRegKey::ACCESS_QUERY_VALUE); 1.417 + if (NS_FAILED(rv)) { 1.418 + // It's a file system path to a handler 1.419 + handlerCommand.Assign(aAppInfo); 1.420 + } 1.421 + else { 1.422 + handlerKeyName.AppendLiteral("\\shell\\open\\command"); 1.423 + nsCOMPtr<nsIWindowsRegKey> regKey = 1.424 + do_CreateInstance("@mozilla.org/windows-registry-key;1"); 1.425 + if (!regKey) 1.426 + return NS_ERROR_FAILURE; 1.427 + 1.428 + nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, 1.429 + handlerKeyName, 1.430 + nsIWindowsRegKey::ACCESS_QUERY_VALUE); 1.431 + if (NS_FAILED(rv)) 1.432 + return NS_ERROR_FAILURE; 1.433 + 1.434 + // OK, the default value here is the description of the type. 1.435 + rv = regKey->ReadStringValue(EmptyString(), handlerCommand); 1.436 + if (NS_FAILED(rv)) { 1.437 + 1.438 + // Check if there is a DelegateExecute string 1.439 + nsAutoString delegateExecute; 1.440 + rv = regKey->ReadStringValue(NS_LITERAL_STRING("DelegateExecute"), delegateExecute); 1.441 + NS_ENSURE_SUCCESS(rv, rv); 1.442 + 1.443 + // Look for InProcServer32 1.444 + nsAutoString delegateExecuteRegPath; 1.445 + delegateExecuteRegPath.AssignLiteral("CLSID\\"); 1.446 + delegateExecuteRegPath.Append(delegateExecute); 1.447 + delegateExecuteRegPath.AppendLiteral("\\InProcServer32"); 1.448 + rv = chkKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, 1.449 + delegateExecuteRegPath, 1.450 + nsIWindowsRegKey::ACCESS_QUERY_VALUE); 1.451 + if (NS_SUCCEEDED(rv)) { 1.452 + rv = chkKey->ReadStringValue(EmptyString(), handlerCommand); 1.453 + } 1.454 + 1.455 + if (NS_FAILED(rv)) { 1.456 + // Look for LocalServer32 1.457 + delegateExecuteRegPath.AssignLiteral("CLSID\\"); 1.458 + delegateExecuteRegPath.Append(delegateExecute); 1.459 + delegateExecuteRegPath.AppendLiteral("\\LocalServer32"); 1.460 + rv = chkKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, 1.461 + delegateExecuteRegPath, 1.462 + nsIWindowsRegKey::ACCESS_QUERY_VALUE); 1.463 + NS_ENSURE_SUCCESS(rv, rv); 1.464 + rv = chkKey->ReadStringValue(EmptyString(), handlerCommand); 1.465 + NS_ENSURE_SUCCESS(rv, rv); 1.466 + } 1.467 + } 1.468 + } 1.469 + 1.470 + if (!CleanupCmdHandlerPath(handlerCommand)) 1.471 + return NS_ERROR_FAILURE; 1.472 + 1.473 + // XXX FIXME: If this fails, the UI will display the full command 1.474 + // string. 1.475 + // There are some rare cases this can happen - ["url.dll" -foo] 1.476 + // for example won't resolve correctly to the system dir. The 1.477 + // subsequent launch of the helper app will work though. 1.478 + nsCOMPtr<nsIFile> lf; 1.479 + NS_NewLocalFile(handlerCommand, true, getter_AddRefs(lf)); 1.480 + if (!lf) 1.481 + return NS_ERROR_FILE_NOT_FOUND; 1.482 + 1.483 + nsILocalFileWin* lfw = nullptr; 1.484 + CallQueryInterface(lf, &lfw); 1.485 + 1.486 + if (lfw) { 1.487 + // The "FileDescription" field contains the actual name of the application. 1.488 + lfw->GetVersionInfoField("FileDescription", aDefaultDescription); 1.489 + // QI addref'ed for us. 1.490 + *aDefaultApplication = lfw; 1.491 + } 1.492 + 1.493 + return NS_OK; 1.494 +} 1.495 + 1.496 +already_AddRefed<nsMIMEInfoWin> nsOSHelperAppService::GetByExtension(const nsAFlatString& aFileExt, const char *aTypeHint) 1.497 +{ 1.498 + if (aFileExt.IsEmpty()) 1.499 + return nullptr; 1.500 + 1.501 + // windows registry assumes your file extension is going to include the '.'. 1.502 + // so make sure it's there... 1.503 + nsAutoString fileExtToUse; 1.504 + if (aFileExt.First() != char16_t('.')) 1.505 + fileExtToUse = char16_t('.'); 1.506 + 1.507 + fileExtToUse.Append(aFileExt); 1.508 + 1.509 + // Try to get an entry from the windows registry. 1.510 + nsCOMPtr<nsIWindowsRegKey> regKey = 1.511 + do_CreateInstance("@mozilla.org/windows-registry-key;1"); 1.512 + if (!regKey) 1.513 + return nullptr; 1.514 + 1.515 + nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT, 1.516 + fileExtToUse, 1.517 + nsIWindowsRegKey::ACCESS_QUERY_VALUE); 1.518 + if (NS_FAILED(rv)) 1.519 + return nullptr; 1.520 + 1.521 + nsAutoCString typeToUse; 1.522 + if (aTypeHint && *aTypeHint) { 1.523 + typeToUse.Assign(aTypeHint); 1.524 + } 1.525 + else { 1.526 + nsAutoString temp; 1.527 + if (NS_FAILED(regKey->ReadStringValue(NS_LITERAL_STRING("Content Type"), 1.528 + temp)) || temp.IsEmpty()) { 1.529 + return nullptr; 1.530 + } 1.531 + // Content-Type is always in ASCII 1.532 + LossyAppendUTF16toASCII(temp, typeToUse); 1.533 + } 1.534 + 1.535 + nsRefPtr<nsMIMEInfoWin> mimeInfo = new nsMIMEInfoWin(typeToUse); 1.536 + 1.537 + // don't append the '.' 1.538 + mimeInfo->AppendExtension(NS_ConvertUTF16toUTF8(Substring(fileExtToUse, 1))); 1.539 + mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault); 1.540 + 1.541 + nsAutoString appInfo; 1.542 + bool found; 1.543 + 1.544 + // Retrieve the default application for this extension 1.545 + if (mAppAssoc) { 1.546 + // Vista: use the new application association COM interfaces 1.547 + // for resolving helpers. 1.548 + nsString assocType(fileExtToUse); 1.549 + wchar_t * pResult = nullptr; 1.550 + HRESULT hr = mAppAssoc->QueryCurrentDefault(assocType.get(), 1.551 + AT_FILEEXTENSION, AL_EFFECTIVE, 1.552 + &pResult); 1.553 + if (SUCCEEDED(hr)) { 1.554 + found = true; 1.555 + appInfo.Assign(pResult); 1.556 + CoTaskMemFree(pResult); 1.557 + } 1.558 + else { 1.559 + found = false; 1.560 + } 1.561 + } 1.562 + else 1.563 + { 1.564 + found = NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(), 1.565 + appInfo)); 1.566 + } 1.567 + 1.568 + // Bug 358297 - ignore the default handler, force the user to choose app 1.569 + if (appInfo.EqualsLiteral("XPSViewer.Document")) 1.570 + found = false; 1.571 + 1.572 + if (!found) { 1.573 + return nullptr; 1.574 + } 1.575 + 1.576 + // Get other nsIMIMEInfo fields from registry, if possible. 1.577 + nsAutoString defaultDescription; 1.578 + nsCOMPtr<nsIFile> defaultApplication; 1.579 + 1.580 + if (NS_FAILED(GetDefaultAppInfo(appInfo, defaultDescription, 1.581 + getter_AddRefs(defaultApplication)))) { 1.582 + return nullptr; 1.583 + } 1.584 + 1.585 + mimeInfo->SetDefaultDescription(defaultDescription); 1.586 + mimeInfo->SetDefaultApplicationHandler(defaultApplication); 1.587 + 1.588 + // Grab the general description 1.589 + GetMIMEInfoFromRegistry(appInfo, mimeInfo); 1.590 + 1.591 + return mimeInfo.forget(); 1.592 +} 1.593 + 1.594 +already_AddRefed<nsIMIMEInfo> nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType, const nsACString& aFileExt, bool *aFound) 1.595 +{ 1.596 + *aFound = true; 1.597 + 1.598 + const nsCString& flatType = PromiseFlatCString(aMIMEType); 1.599 + const nsCString& flatExt = PromiseFlatCString(aFileExt); 1.600 + 1.601 + nsAutoString fileExtension; 1.602 + /* XXX The Equals is a gross hack to wallpaper over the most common Win32 1.603 + * extension issues caused by the fix for bug 116938. See bug 1.604 + * 120327, comment 271 for why this is needed. Not even sure we 1.605 + * want to remove this once we have fixed all this stuff to work 1.606 + * right; any info we get from the OS on this type is pretty much 1.607 + * useless.... 1.608 + * We'll do extension-based lookup for this type later in this function. 1.609 + */ 1.610 + if (!aMIMEType.LowerCaseEqualsLiteral(APPLICATION_OCTET_STREAM)) { 1.611 + // (1) try to use the windows mime database to see if there is a mapping to a file extension 1.612 + // (2) try to see if we have some left over 4.x registry info we can peek at... 1.613 + GetExtensionFromWindowsMimeDatabase(aMIMEType, fileExtension); 1.614 + LOG(("Windows mime database: extension '%s'\n", fileExtension.get())); 1.615 + if (fileExtension.IsEmpty()) { 1.616 + GetExtensionFrom4xRegistryInfo(aMIMEType, fileExtension); 1.617 + LOG(("4.x Registry: extension '%s'\n", fileExtension.get())); 1.618 + } 1.619 + } 1.620 + // If we found an extension for the type, do the lookup 1.621 + nsRefPtr<nsMIMEInfoWin> mi; 1.622 + if (!fileExtension.IsEmpty()) 1.623 + mi = GetByExtension(fileExtension, flatType.get()); 1.624 + LOG(("Extension lookup on '%s' found: 0x%p\n", fileExtension.get(), mi.get())); 1.625 + 1.626 + bool hasDefault = false; 1.627 + if (mi) { 1.628 + mi->GetHasDefaultHandler(&hasDefault); 1.629 + // OK. We might have the case that |aFileExt| is a valid extension for the 1.630 + // mimetype we were given. In that case, we do want to append aFileExt 1.631 + // to the mimeinfo that we have. (E.g.: We are asked for video/mpeg and 1.632 + // .mpg, but the primary extension for video/mpeg is .mpeg. But because 1.633 + // .mpg is an extension for video/mpeg content, we want to append it) 1.634 + if (!aFileExt.IsEmpty() && typeFromExtEquals(NS_ConvertUTF8toUTF16(flatExt).get(), flatType.get())) { 1.635 + LOG(("Appending extension '%s' to mimeinfo, because its mimetype is '%s'\n", 1.636 + flatExt.get(), flatType.get())); 1.637 + bool extExist = false; 1.638 + mi->ExtensionExists(aFileExt, &extExist); 1.639 + if (!extExist) 1.640 + mi->AppendExtension(aFileExt); 1.641 + } 1.642 + } 1.643 + if (!mi || !hasDefault) { 1.644 + nsRefPtr<nsMIMEInfoWin> miByExt = 1.645 + GetByExtension(NS_ConvertUTF8toUTF16(aFileExt), flatType.get()); 1.646 + LOG(("Ext. lookup for '%s' found 0x%p\n", flatExt.get(), miByExt.get())); 1.647 + if (!miByExt && mi) 1.648 + return mi.forget(); 1.649 + if (miByExt && !mi) { 1.650 + return miByExt.forget(); 1.651 + } 1.652 + if (!miByExt && !mi) { 1.653 + *aFound = false; 1.654 + mi = new nsMIMEInfoWin(flatType); 1.655 + if (!aFileExt.IsEmpty()) { 1.656 + mi->AppendExtension(aFileExt); 1.657 + } 1.658 + 1.659 + return mi.forget(); 1.660 + } 1.661 + 1.662 + // if we get here, mi has no default app. copy from extension lookup. 1.663 + nsCOMPtr<nsIFile> defaultApp; 1.664 + nsAutoString desc; 1.665 + miByExt->GetDefaultDescription(desc); 1.666 + 1.667 + mi->SetDefaultDescription(desc); 1.668 + } 1.669 + return mi.forget(); 1.670 +} 1.671 + 1.672 +NS_IMETHODIMP 1.673 +nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme, 1.674 + bool *found, 1.675 + nsIHandlerInfo **_retval) 1.676 +{ 1.677 + NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!"); 1.678 + 1.679 + nsresult rv = OSProtocolHandlerExists(nsPromiseFlatCString(aScheme).get(), 1.680 + found); 1.681 + if (NS_FAILED(rv)) 1.682 + return rv; 1.683 + 1.684 + nsMIMEInfoWin *handlerInfo = 1.685 + new nsMIMEInfoWin(aScheme, nsMIMEInfoBase::eProtocolInfo); 1.686 + NS_ENSURE_TRUE(handlerInfo, NS_ERROR_OUT_OF_MEMORY); 1.687 + NS_ADDREF(*_retval = handlerInfo); 1.688 + 1.689 + if (!*found) { 1.690 + // Code that calls this requires an object regardless if the OS has 1.691 + // something for us, so we return the empty object. 1.692 + return NS_OK; 1.693 + } 1.694 + 1.695 + nsAutoString desc; 1.696 + GetApplicationDescription(aScheme, desc); 1.697 + handlerInfo->SetDefaultDescription(desc); 1.698 + 1.699 + return NS_OK; 1.700 +} 1.701 +