uriloader/exthandler/win/nsOSHelperAppService.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 * vim:set ts=2 sts=2 sw=2 et cin:
michael@0 3 *
michael@0 4 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 5 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 7
michael@0 8 #include "nsOSHelperAppService.h"
michael@0 9 #include "nsISupports.h"
michael@0 10 #include "nsString.h"
michael@0 11 #include "nsXPIDLString.h"
michael@0 12 #include "nsIURL.h"
michael@0 13 #include "nsIMIMEInfo.h"
michael@0 14 #include "nsMIMEInfoWin.h"
michael@0 15 #include "nsMimeTypes.h"
michael@0 16 #include "nsILocalFileWin.h"
michael@0 17 #include "nsIProcess.h"
michael@0 18 #include "plstr.h"
michael@0 19 #include "nsAutoPtr.h"
michael@0 20 #include "nsNativeCharsetUtils.h"
michael@0 21 #include "nsIWindowsRegKey.h"
michael@0 22
michael@0 23 // shellapi.h is needed to build with WIN32_LEAN_AND_MEAN
michael@0 24 #include <shellapi.h>
michael@0 25
michael@0 26 #define LOG(args) PR_LOG(mLog, PR_LOG_DEBUG, args)
michael@0 27
michael@0 28 // helper methods: forward declarations...
michael@0 29 static nsresult GetExtensionFrom4xRegistryInfo(const nsACString& aMimeType,
michael@0 30 nsString& aFileExtension);
michael@0 31 static nsresult GetExtensionFromWindowsMimeDatabase(const nsACString& aMimeType,
michael@0 32 nsString& aFileExtension);
michael@0 33
michael@0 34 nsOSHelperAppService::nsOSHelperAppService() :
michael@0 35 nsExternalHelperAppService()
michael@0 36 , mAppAssoc(nullptr)
michael@0 37 {
michael@0 38 CoInitialize(nullptr);
michael@0 39 CoCreateInstance(CLSID_ApplicationAssociationRegistration, nullptr,
michael@0 40 CLSCTX_INPROC, IID_IApplicationAssociationRegistration,
michael@0 41 (void**)&mAppAssoc);
michael@0 42 }
michael@0 43
michael@0 44 nsOSHelperAppService::~nsOSHelperAppService()
michael@0 45 {
michael@0 46 if (mAppAssoc)
michael@0 47 mAppAssoc->Release();
michael@0 48 mAppAssoc = nullptr;
michael@0 49 CoUninitialize();
michael@0 50 }
michael@0 51
michael@0 52 // The windows registry provides a mime database key which lists a set of mime types and corresponding "Extension" values.
michael@0 53 // we can use this to look up our mime type to see if there is a preferred extension for the mime type.
michael@0 54 static nsresult GetExtensionFromWindowsMimeDatabase(const nsACString& aMimeType,
michael@0 55 nsString& aFileExtension)
michael@0 56 {
michael@0 57 nsAutoString mimeDatabaseKey;
michael@0 58 mimeDatabaseKey.AssignLiteral("MIME\\Database\\Content Type\\");
michael@0 59
michael@0 60 AppendASCIItoUTF16(aMimeType, mimeDatabaseKey);
michael@0 61
michael@0 62 nsCOMPtr<nsIWindowsRegKey> regKey =
michael@0 63 do_CreateInstance("@mozilla.org/windows-registry-key;1");
michael@0 64 if (!regKey)
michael@0 65 return NS_ERROR_NOT_AVAILABLE;
michael@0 66
michael@0 67 nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
michael@0 68 mimeDatabaseKey,
michael@0 69 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
michael@0 70
michael@0 71 if (NS_SUCCEEDED(rv))
michael@0 72 regKey->ReadStringValue(NS_LITERAL_STRING("Extension"), aFileExtension);
michael@0 73
michael@0 74 return NS_OK;
michael@0 75 }
michael@0 76
michael@0 77 // We have a serious problem!! I have this content type and the windows registry only gives me
michael@0 78 // helper apps based on extension. Right now, we really don't have a good place to go for
michael@0 79 // trying to figure out the extension for a particular mime type....One short term hack is to look
michael@0 80 // this information in 4.x (it's stored in the windows regsitry).
michael@0 81 static nsresult GetExtensionFrom4xRegistryInfo(const nsACString& aMimeType,
michael@0 82 nsString& aFileExtension)
michael@0 83 {
michael@0 84 nsCOMPtr<nsIWindowsRegKey> regKey =
michael@0 85 do_CreateInstance("@mozilla.org/windows-registry-key;1");
michael@0 86 if (!regKey)
michael@0 87 return NS_ERROR_NOT_AVAILABLE;
michael@0 88
michael@0 89 nsresult rv = regKey->
michael@0 90 Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
michael@0 91 NS_LITERAL_STRING("Software\\Netscape\\Netscape Navigator\\Suffixes"),
michael@0 92 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
michael@0 93 if (NS_FAILED(rv))
michael@0 94 return NS_ERROR_NOT_AVAILABLE;
michael@0 95
michael@0 96 rv = regKey->ReadStringValue(NS_ConvertASCIItoUTF16(aMimeType),
michael@0 97 aFileExtension);
michael@0 98 if (NS_FAILED(rv))
michael@0 99 return NS_OK;
michael@0 100
michael@0 101 aFileExtension.Insert(char16_t('.'), 0);
michael@0 102
michael@0 103 // this may be a comma separated list of extensions...just take the
michael@0 104 // first one for now...
michael@0 105
michael@0 106 int32_t pos = aFileExtension.FindChar(char16_t(','));
michael@0 107 if (pos > 0) {
michael@0 108 // we have a comma separated list of types...
michael@0 109 // truncate everything after the first comma (including the comma)
michael@0 110 aFileExtension.Truncate(pos);
michael@0 111 }
michael@0 112
michael@0 113 return NS_OK;
michael@0 114 }
michael@0 115
michael@0 116 nsresult nsOSHelperAppService::OSProtocolHandlerExists(const char * aProtocolScheme, bool * aHandlerExists)
michael@0 117 {
michael@0 118 // look up the protocol scheme in the windows registry....if we find a match then we have a handler for it...
michael@0 119 *aHandlerExists = false;
michael@0 120 if (aProtocolScheme && *aProtocolScheme)
michael@0 121 {
michael@0 122 // Vista: use new application association interface
michael@0 123 if (mAppAssoc) {
michael@0 124 wchar_t * pResult = nullptr;
michael@0 125 NS_ConvertASCIItoUTF16 scheme(aProtocolScheme);
michael@0 126 // We are responsible for freeing returned strings.
michael@0 127 HRESULT hr = mAppAssoc->QueryCurrentDefault(scheme.get(),
michael@0 128 AT_URLPROTOCOL, AL_EFFECTIVE,
michael@0 129 &pResult);
michael@0 130 if (SUCCEEDED(hr)) {
michael@0 131 CoTaskMemFree(pResult);
michael@0 132 *aHandlerExists = true;
michael@0 133 }
michael@0 134 return NS_OK;
michael@0 135 }
michael@0 136
michael@0 137 HKEY hKey;
michael@0 138 LONG err = ::RegOpenKeyExW(HKEY_CLASSES_ROOT,
michael@0 139 NS_ConvertASCIItoUTF16(aProtocolScheme).get(),
michael@0 140 0,
michael@0 141 KEY_QUERY_VALUE,
michael@0 142 &hKey);
michael@0 143 if (err == ERROR_SUCCESS)
michael@0 144 {
michael@0 145 err = ::RegQueryValueExW(hKey, L"URL Protocol",
michael@0 146 nullptr, nullptr, nullptr, nullptr);
michael@0 147 *aHandlerExists = (err == ERROR_SUCCESS);
michael@0 148 // close the key
michael@0 149 ::RegCloseKey(hKey);
michael@0 150 }
michael@0 151 }
michael@0 152
michael@0 153 return NS_OK;
michael@0 154 }
michael@0 155
michael@0 156 NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval)
michael@0 157 {
michael@0 158 nsCOMPtr<nsIWindowsRegKey> regKey =
michael@0 159 do_CreateInstance("@mozilla.org/windows-registry-key;1");
michael@0 160 if (!regKey)
michael@0 161 return NS_ERROR_NOT_AVAILABLE;
michael@0 162
michael@0 163 NS_ConvertASCIItoUTF16 buf(aScheme);
michael@0 164
michael@0 165 // Vista: use new application association interface
michael@0 166 if (mAppAssoc) {
michael@0 167 wchar_t * pResult = nullptr;
michael@0 168 // We are responsible for freeing returned strings.
michael@0 169 HRESULT hr = mAppAssoc->QueryCurrentDefault(buf.get(),
michael@0 170 AT_URLPROTOCOL, AL_EFFECTIVE,
michael@0 171 &pResult);
michael@0 172 if (SUCCEEDED(hr)) {
michael@0 173 nsCOMPtr<nsIFile> app;
michael@0 174 nsAutoString appInfo(pResult);
michael@0 175 CoTaskMemFree(pResult);
michael@0 176 if (NS_SUCCEEDED(GetDefaultAppInfo(appInfo, _retval, getter_AddRefs(app))))
michael@0 177 return NS_OK;
michael@0 178 }
michael@0 179 return NS_ERROR_NOT_AVAILABLE;
michael@0 180 }
michael@0 181
michael@0 182 nsCOMPtr<nsIFile> app;
michael@0 183 GetDefaultAppInfo(buf, _retval, getter_AddRefs(app));
michael@0 184
michael@0 185 if (!_retval.Equals(buf))
michael@0 186 return NS_OK;
michael@0 187
michael@0 188 // Fall back to full path
michael@0 189 buf.AppendLiteral("\\shell\\open\\command");
michael@0 190 nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
michael@0 191 buf,
michael@0 192 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
michael@0 193 if (NS_FAILED(rv))
michael@0 194 return NS_ERROR_NOT_AVAILABLE;
michael@0 195
michael@0 196 rv = regKey->ReadStringValue(EmptyString(), _retval);
michael@0 197
michael@0 198 return NS_SUCCEEDED(rv) ? NS_OK : NS_ERROR_NOT_AVAILABLE;
michael@0 199 }
michael@0 200
michael@0 201 // GetMIMEInfoFromRegistry: This function obtains the values of some of the nsIMIMEInfo
michael@0 202 // attributes for the mimeType/extension associated with the input registry key. The default
michael@0 203 // entry for that key is the name of a registry key under HKEY_CLASSES_ROOT. The default
michael@0 204 // value for *that* key is the descriptive name of the type. The EditFlags value is a binary
michael@0 205 // value; the low order bit of the third byte of which indicates that the user does not need
michael@0 206 // to be prompted.
michael@0 207 //
michael@0 208 // This function sets only the Description attribute of the input nsIMIMEInfo.
michael@0 209 /* static */
michael@0 210 nsresult nsOSHelperAppService::GetMIMEInfoFromRegistry(const nsAFlatString& fileType, nsIMIMEInfo *pInfo)
michael@0 211 {
michael@0 212 nsresult rv = NS_OK;
michael@0 213
michael@0 214 NS_ENSURE_ARG(pInfo);
michael@0 215 nsCOMPtr<nsIWindowsRegKey> regKey =
michael@0 216 do_CreateInstance("@mozilla.org/windows-registry-key;1");
michael@0 217 if (!regKey)
michael@0 218 return NS_ERROR_NOT_AVAILABLE;
michael@0 219
michael@0 220 rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
michael@0 221 fileType, nsIWindowsRegKey::ACCESS_QUERY_VALUE);
michael@0 222 if (NS_FAILED(rv))
michael@0 223 return NS_ERROR_FAILURE;
michael@0 224
michael@0 225 // OK, the default value here is the description of the type.
michael@0 226 nsAutoString description;
michael@0 227 rv = regKey->ReadStringValue(EmptyString(), description);
michael@0 228 if (NS_SUCCEEDED(rv))
michael@0 229 pInfo->SetDescription(description);
michael@0 230
michael@0 231 return NS_OK;
michael@0 232 }
michael@0 233
michael@0 234 /////////////////////////////////////////////////////////////////////////////////////////////////
michael@0 235 // method overrides used to gather information from the windows registry for
michael@0 236 // various mime types.
michael@0 237 ////////////////////////////////////////////////////////////////////////////////////////////////
michael@0 238
michael@0 239 /// Looks up the type for the extension aExt and compares it to aType
michael@0 240 /* static */ bool
michael@0 241 nsOSHelperAppService::typeFromExtEquals(const char16_t* aExt, const char *aType)
michael@0 242 {
michael@0 243 if (!aType)
michael@0 244 return false;
michael@0 245 nsAutoString fileExtToUse;
michael@0 246 if (aExt[0] != char16_t('.'))
michael@0 247 fileExtToUse = char16_t('.');
michael@0 248
michael@0 249 fileExtToUse.Append(aExt);
michael@0 250
michael@0 251 bool eq = false;
michael@0 252 nsCOMPtr<nsIWindowsRegKey> regKey =
michael@0 253 do_CreateInstance("@mozilla.org/windows-registry-key;1");
michael@0 254 if (!regKey)
michael@0 255 return eq;
michael@0 256
michael@0 257 nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
michael@0 258 fileExtToUse,
michael@0 259 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
michael@0 260 if (NS_FAILED(rv))
michael@0 261 return eq;
michael@0 262
michael@0 263 nsAutoString type;
michael@0 264 rv = regKey->ReadStringValue(NS_LITERAL_STRING("Content Type"), type);
michael@0 265 if (NS_SUCCEEDED(rv))
michael@0 266 eq = type.EqualsASCII(aType);
michael@0 267
michael@0 268 return eq;
michael@0 269 }
michael@0 270
michael@0 271 // Strip a handler command string of its quotes and parameters.
michael@0 272 static void CleanupHandlerPath(nsString& aPath)
michael@0 273 {
michael@0 274 // Example command strings passed into this routine:
michael@0 275
michael@0 276 // 1) C:\Program Files\Company\some.exe -foo -bar
michael@0 277 // 2) C:\Program Files\Company\some.dll
michael@0 278 // 3) C:\Windows\some.dll,-foo -bar
michael@0 279 // 4) C:\Windows\some.cpl,-foo -bar
michael@0 280
michael@0 281 int32_t lastCommaPos = aPath.RFindChar(',');
michael@0 282 if (lastCommaPos != kNotFound)
michael@0 283 aPath.Truncate(lastCommaPos);
michael@0 284
michael@0 285 aPath.AppendLiteral(" ");
michael@0 286
michael@0 287 // case insensitive
michael@0 288 uint32_t index = aPath.Find(".exe ", true);
michael@0 289 if (index == kNotFound)
michael@0 290 index = aPath.Find(".dll ", true);
michael@0 291 if (index == kNotFound)
michael@0 292 index = aPath.Find(".cpl ", true);
michael@0 293
michael@0 294 if (index != kNotFound)
michael@0 295 aPath.Truncate(index + 4);
michael@0 296 aPath.Trim(" ", true, true);
michael@0 297 }
michael@0 298
michael@0 299 // Strip the windows host process bootstrap executable rundll32.exe
michael@0 300 // from a handler's command string if it exists.
michael@0 301 static void StripRundll32(nsString& aCommandString)
michael@0 302 {
michael@0 303 // Example rundll formats:
michael@0 304 // C:\Windows\System32\rundll32.exe "path to dll"
michael@0 305 // rundll32.exe "path to dll"
michael@0 306 // C:\Windows\System32\rundll32.exe "path to dll", var var
michael@0 307 // rundll32.exe "path to dll", var var
michael@0 308
michael@0 309 NS_NAMED_LITERAL_STRING(rundllSegment, "rundll32.exe ");
michael@0 310 NS_NAMED_LITERAL_STRING(rundllSegmentShort, "rundll32 ");
michael@0 311
michael@0 312 // case insensitive
michael@0 313 int32_t strLen = rundllSegment.Length();
michael@0 314 int32_t index = aCommandString.Find(rundllSegment, true);
michael@0 315 if (index == kNotFound) {
michael@0 316 strLen = rundllSegmentShort.Length();
michael@0 317 index = aCommandString.Find(rundllSegmentShort, true);
michael@0 318 }
michael@0 319
michael@0 320 if (index != kNotFound) {
michael@0 321 uint32_t rundllSegmentLength = index + strLen;
michael@0 322 aCommandString.Cut(0, rundllSegmentLength);
michael@0 323 }
michael@0 324 }
michael@0 325
michael@0 326 // Returns the fully qualified path to an application handler based on
michael@0 327 // a parameterized command string. Note this routine should not be used
michael@0 328 // to launch the associated application as it strips parameters and
michael@0 329 // rundll.exe from the string. Designed for retrieving display information
michael@0 330 // on a particular handler.
michael@0 331 /* static */ bool nsOSHelperAppService::CleanupCmdHandlerPath(nsAString& aCommandHandler)
michael@0 332 {
michael@0 333 nsAutoString handlerCommand(aCommandHandler);
michael@0 334
michael@0 335 // Straight command path:
michael@0 336 //
michael@0 337 // %SystemRoot%\system32\NOTEPAD.EXE var
michael@0 338 // "C:\Program Files\iTunes\iTunes.exe" var var
michael@0 339 // C:\Program Files\iTunes\iTunes.exe var var
michael@0 340 //
michael@0 341 // Example rundll handlers:
michael@0 342 //
michael@0 343 // rundll32.exe "%ProgramFiles%\Win...ery\PhotoViewer.dll", var var
michael@0 344 // rundll32.exe "%ProgramFiles%\Windows Photo Gallery\PhotoViewer.dll"
michael@0 345 // C:\Windows\System32\rundll32.exe "path to dll", var var
michael@0 346 // %SystemRoot%\System32\rundll32.exe "%ProgramFiles%\Win...ery\Photo
michael@0 347 // Viewer.dll", var var
michael@0 348
michael@0 349 // Expand environment variables so we have full path strings.
michael@0 350 uint32_t bufLength = ::ExpandEnvironmentStringsW(handlerCommand.get(),
michael@0 351 L"", 0);
michael@0 352 if (bufLength == 0) // Error
michael@0 353 return false;
michael@0 354
michael@0 355 nsAutoArrayPtr<wchar_t> destination(new wchar_t[bufLength]);
michael@0 356 if (!destination)
michael@0 357 return false;
michael@0 358 if (!::ExpandEnvironmentStringsW(handlerCommand.get(), destination,
michael@0 359 bufLength))
michael@0 360 return false;
michael@0 361
michael@0 362 handlerCommand = static_cast<const wchar_t*>(destination);
michael@0 363
michael@0 364 // Remove quotes around paths
michael@0 365 handlerCommand.StripChars("\"");
michael@0 366
michael@0 367 // Strip windows host process bootstrap so we can get to the actual
michael@0 368 // handler.
michael@0 369 StripRundll32(handlerCommand);
michael@0 370
michael@0 371 // Trim any command parameters so that we have a native path we can
michael@0 372 // initialize a local file with.
michael@0 373 CleanupHandlerPath(handlerCommand);
michael@0 374
michael@0 375 aCommandHandler.Assign(handlerCommand);
michael@0 376 return true;
michael@0 377 }
michael@0 378
michael@0 379 // The "real" name of a given helper app (as specified by the path to the
michael@0 380 // executable file held in various registry keys) is stored n the VERSIONINFO
michael@0 381 // block in the file's resources. We need to find the path to the executable
michael@0 382 // and then retrieve the "FileDescription" field value from the file.
michael@0 383 nsresult
michael@0 384 nsOSHelperAppService::GetDefaultAppInfo(const nsAString& aAppInfo,
michael@0 385 nsAString& aDefaultDescription,
michael@0 386 nsIFile** aDefaultApplication)
michael@0 387 {
michael@0 388 nsAutoString handlerCommand;
michael@0 389
michael@0 390 // If all else fails, use the file type key name, which will be
michael@0 391 // something like "pngfile" for .pngs, "WMVFile" for .wmvs, etc.
michael@0 392 aDefaultDescription = aAppInfo;
michael@0 393 *aDefaultApplication = nullptr;
michael@0 394
michael@0 395 if (aAppInfo.IsEmpty())
michael@0 396 return NS_ERROR_FAILURE;
michael@0 397
michael@0 398 // aAppInfo may be a file, file path, program id, or
michael@0 399 // Applications reference -
michael@0 400 // c:\dir\app.exe
michael@0 401 // Applications\appfile.exe/dll (shell\open...)
michael@0 402 // ProgID.progid (shell\open...)
michael@0 403
michael@0 404 nsAutoString handlerKeyName(aAppInfo);
michael@0 405
michael@0 406 nsCOMPtr<nsIWindowsRegKey> chkKey =
michael@0 407 do_CreateInstance("@mozilla.org/windows-registry-key;1");
michael@0 408 if (!chkKey)
michael@0 409 return NS_ERROR_FAILURE;
michael@0 410
michael@0 411 nsresult rv = chkKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
michael@0 412 handlerKeyName,
michael@0 413 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
michael@0 414 if (NS_FAILED(rv)) {
michael@0 415 // It's a file system path to a handler
michael@0 416 handlerCommand.Assign(aAppInfo);
michael@0 417 }
michael@0 418 else {
michael@0 419 handlerKeyName.AppendLiteral("\\shell\\open\\command");
michael@0 420 nsCOMPtr<nsIWindowsRegKey> regKey =
michael@0 421 do_CreateInstance("@mozilla.org/windows-registry-key;1");
michael@0 422 if (!regKey)
michael@0 423 return NS_ERROR_FAILURE;
michael@0 424
michael@0 425 nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
michael@0 426 handlerKeyName,
michael@0 427 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
michael@0 428 if (NS_FAILED(rv))
michael@0 429 return NS_ERROR_FAILURE;
michael@0 430
michael@0 431 // OK, the default value here is the description of the type.
michael@0 432 rv = regKey->ReadStringValue(EmptyString(), handlerCommand);
michael@0 433 if (NS_FAILED(rv)) {
michael@0 434
michael@0 435 // Check if there is a DelegateExecute string
michael@0 436 nsAutoString delegateExecute;
michael@0 437 rv = regKey->ReadStringValue(NS_LITERAL_STRING("DelegateExecute"), delegateExecute);
michael@0 438 NS_ENSURE_SUCCESS(rv, rv);
michael@0 439
michael@0 440 // Look for InProcServer32
michael@0 441 nsAutoString delegateExecuteRegPath;
michael@0 442 delegateExecuteRegPath.AssignLiteral("CLSID\\");
michael@0 443 delegateExecuteRegPath.Append(delegateExecute);
michael@0 444 delegateExecuteRegPath.AppendLiteral("\\InProcServer32");
michael@0 445 rv = chkKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
michael@0 446 delegateExecuteRegPath,
michael@0 447 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
michael@0 448 if (NS_SUCCEEDED(rv)) {
michael@0 449 rv = chkKey->ReadStringValue(EmptyString(), handlerCommand);
michael@0 450 }
michael@0 451
michael@0 452 if (NS_FAILED(rv)) {
michael@0 453 // Look for LocalServer32
michael@0 454 delegateExecuteRegPath.AssignLiteral("CLSID\\");
michael@0 455 delegateExecuteRegPath.Append(delegateExecute);
michael@0 456 delegateExecuteRegPath.AppendLiteral("\\LocalServer32");
michael@0 457 rv = chkKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
michael@0 458 delegateExecuteRegPath,
michael@0 459 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
michael@0 460 NS_ENSURE_SUCCESS(rv, rv);
michael@0 461 rv = chkKey->ReadStringValue(EmptyString(), handlerCommand);
michael@0 462 NS_ENSURE_SUCCESS(rv, rv);
michael@0 463 }
michael@0 464 }
michael@0 465 }
michael@0 466
michael@0 467 if (!CleanupCmdHandlerPath(handlerCommand))
michael@0 468 return NS_ERROR_FAILURE;
michael@0 469
michael@0 470 // XXX FIXME: If this fails, the UI will display the full command
michael@0 471 // string.
michael@0 472 // There are some rare cases this can happen - ["url.dll" -foo]
michael@0 473 // for example won't resolve correctly to the system dir. The
michael@0 474 // subsequent launch of the helper app will work though.
michael@0 475 nsCOMPtr<nsIFile> lf;
michael@0 476 NS_NewLocalFile(handlerCommand, true, getter_AddRefs(lf));
michael@0 477 if (!lf)
michael@0 478 return NS_ERROR_FILE_NOT_FOUND;
michael@0 479
michael@0 480 nsILocalFileWin* lfw = nullptr;
michael@0 481 CallQueryInterface(lf, &lfw);
michael@0 482
michael@0 483 if (lfw) {
michael@0 484 // The "FileDescription" field contains the actual name of the application.
michael@0 485 lfw->GetVersionInfoField("FileDescription", aDefaultDescription);
michael@0 486 // QI addref'ed for us.
michael@0 487 *aDefaultApplication = lfw;
michael@0 488 }
michael@0 489
michael@0 490 return NS_OK;
michael@0 491 }
michael@0 492
michael@0 493 already_AddRefed<nsMIMEInfoWin> nsOSHelperAppService::GetByExtension(const nsAFlatString& aFileExt, const char *aTypeHint)
michael@0 494 {
michael@0 495 if (aFileExt.IsEmpty())
michael@0 496 return nullptr;
michael@0 497
michael@0 498 // windows registry assumes your file extension is going to include the '.'.
michael@0 499 // so make sure it's there...
michael@0 500 nsAutoString fileExtToUse;
michael@0 501 if (aFileExt.First() != char16_t('.'))
michael@0 502 fileExtToUse = char16_t('.');
michael@0 503
michael@0 504 fileExtToUse.Append(aFileExt);
michael@0 505
michael@0 506 // Try to get an entry from the windows registry.
michael@0 507 nsCOMPtr<nsIWindowsRegKey> regKey =
michael@0 508 do_CreateInstance("@mozilla.org/windows-registry-key;1");
michael@0 509 if (!regKey)
michael@0 510 return nullptr;
michael@0 511
michael@0 512 nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
michael@0 513 fileExtToUse,
michael@0 514 nsIWindowsRegKey::ACCESS_QUERY_VALUE);
michael@0 515 if (NS_FAILED(rv))
michael@0 516 return nullptr;
michael@0 517
michael@0 518 nsAutoCString typeToUse;
michael@0 519 if (aTypeHint && *aTypeHint) {
michael@0 520 typeToUse.Assign(aTypeHint);
michael@0 521 }
michael@0 522 else {
michael@0 523 nsAutoString temp;
michael@0 524 if (NS_FAILED(regKey->ReadStringValue(NS_LITERAL_STRING("Content Type"),
michael@0 525 temp)) || temp.IsEmpty()) {
michael@0 526 return nullptr;
michael@0 527 }
michael@0 528 // Content-Type is always in ASCII
michael@0 529 LossyAppendUTF16toASCII(temp, typeToUse);
michael@0 530 }
michael@0 531
michael@0 532 nsRefPtr<nsMIMEInfoWin> mimeInfo = new nsMIMEInfoWin(typeToUse);
michael@0 533
michael@0 534 // don't append the '.'
michael@0 535 mimeInfo->AppendExtension(NS_ConvertUTF16toUTF8(Substring(fileExtToUse, 1)));
michael@0 536 mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault);
michael@0 537
michael@0 538 nsAutoString appInfo;
michael@0 539 bool found;
michael@0 540
michael@0 541 // Retrieve the default application for this extension
michael@0 542 if (mAppAssoc) {
michael@0 543 // Vista: use the new application association COM interfaces
michael@0 544 // for resolving helpers.
michael@0 545 nsString assocType(fileExtToUse);
michael@0 546 wchar_t * pResult = nullptr;
michael@0 547 HRESULT hr = mAppAssoc->QueryCurrentDefault(assocType.get(),
michael@0 548 AT_FILEEXTENSION, AL_EFFECTIVE,
michael@0 549 &pResult);
michael@0 550 if (SUCCEEDED(hr)) {
michael@0 551 found = true;
michael@0 552 appInfo.Assign(pResult);
michael@0 553 CoTaskMemFree(pResult);
michael@0 554 }
michael@0 555 else {
michael@0 556 found = false;
michael@0 557 }
michael@0 558 }
michael@0 559 else
michael@0 560 {
michael@0 561 found = NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(),
michael@0 562 appInfo));
michael@0 563 }
michael@0 564
michael@0 565 // Bug 358297 - ignore the default handler, force the user to choose app
michael@0 566 if (appInfo.EqualsLiteral("XPSViewer.Document"))
michael@0 567 found = false;
michael@0 568
michael@0 569 if (!found) {
michael@0 570 return nullptr;
michael@0 571 }
michael@0 572
michael@0 573 // Get other nsIMIMEInfo fields from registry, if possible.
michael@0 574 nsAutoString defaultDescription;
michael@0 575 nsCOMPtr<nsIFile> defaultApplication;
michael@0 576
michael@0 577 if (NS_FAILED(GetDefaultAppInfo(appInfo, defaultDescription,
michael@0 578 getter_AddRefs(defaultApplication)))) {
michael@0 579 return nullptr;
michael@0 580 }
michael@0 581
michael@0 582 mimeInfo->SetDefaultDescription(defaultDescription);
michael@0 583 mimeInfo->SetDefaultApplicationHandler(defaultApplication);
michael@0 584
michael@0 585 // Grab the general description
michael@0 586 GetMIMEInfoFromRegistry(appInfo, mimeInfo);
michael@0 587
michael@0 588 return mimeInfo.forget();
michael@0 589 }
michael@0 590
michael@0 591 already_AddRefed<nsIMIMEInfo> nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType, const nsACString& aFileExt, bool *aFound)
michael@0 592 {
michael@0 593 *aFound = true;
michael@0 594
michael@0 595 const nsCString& flatType = PromiseFlatCString(aMIMEType);
michael@0 596 const nsCString& flatExt = PromiseFlatCString(aFileExt);
michael@0 597
michael@0 598 nsAutoString fileExtension;
michael@0 599 /* XXX The Equals is a gross hack to wallpaper over the most common Win32
michael@0 600 * extension issues caused by the fix for bug 116938. See bug
michael@0 601 * 120327, comment 271 for why this is needed. Not even sure we
michael@0 602 * want to remove this once we have fixed all this stuff to work
michael@0 603 * right; any info we get from the OS on this type is pretty much
michael@0 604 * useless....
michael@0 605 * We'll do extension-based lookup for this type later in this function.
michael@0 606 */
michael@0 607 if (!aMIMEType.LowerCaseEqualsLiteral(APPLICATION_OCTET_STREAM)) {
michael@0 608 // (1) try to use the windows mime database to see if there is a mapping to a file extension
michael@0 609 // (2) try to see if we have some left over 4.x registry info we can peek at...
michael@0 610 GetExtensionFromWindowsMimeDatabase(aMIMEType, fileExtension);
michael@0 611 LOG(("Windows mime database: extension '%s'\n", fileExtension.get()));
michael@0 612 if (fileExtension.IsEmpty()) {
michael@0 613 GetExtensionFrom4xRegistryInfo(aMIMEType, fileExtension);
michael@0 614 LOG(("4.x Registry: extension '%s'\n", fileExtension.get()));
michael@0 615 }
michael@0 616 }
michael@0 617 // If we found an extension for the type, do the lookup
michael@0 618 nsRefPtr<nsMIMEInfoWin> mi;
michael@0 619 if (!fileExtension.IsEmpty())
michael@0 620 mi = GetByExtension(fileExtension, flatType.get());
michael@0 621 LOG(("Extension lookup on '%s' found: 0x%p\n", fileExtension.get(), mi.get()));
michael@0 622
michael@0 623 bool hasDefault = false;
michael@0 624 if (mi) {
michael@0 625 mi->GetHasDefaultHandler(&hasDefault);
michael@0 626 // OK. We might have the case that |aFileExt| is a valid extension for the
michael@0 627 // mimetype we were given. In that case, we do want to append aFileExt
michael@0 628 // to the mimeinfo that we have. (E.g.: We are asked for video/mpeg and
michael@0 629 // .mpg, but the primary extension for video/mpeg is .mpeg. But because
michael@0 630 // .mpg is an extension for video/mpeg content, we want to append it)
michael@0 631 if (!aFileExt.IsEmpty() && typeFromExtEquals(NS_ConvertUTF8toUTF16(flatExt).get(), flatType.get())) {
michael@0 632 LOG(("Appending extension '%s' to mimeinfo, because its mimetype is '%s'\n",
michael@0 633 flatExt.get(), flatType.get()));
michael@0 634 bool extExist = false;
michael@0 635 mi->ExtensionExists(aFileExt, &extExist);
michael@0 636 if (!extExist)
michael@0 637 mi->AppendExtension(aFileExt);
michael@0 638 }
michael@0 639 }
michael@0 640 if (!mi || !hasDefault) {
michael@0 641 nsRefPtr<nsMIMEInfoWin> miByExt =
michael@0 642 GetByExtension(NS_ConvertUTF8toUTF16(aFileExt), flatType.get());
michael@0 643 LOG(("Ext. lookup for '%s' found 0x%p\n", flatExt.get(), miByExt.get()));
michael@0 644 if (!miByExt && mi)
michael@0 645 return mi.forget();
michael@0 646 if (miByExt && !mi) {
michael@0 647 return miByExt.forget();
michael@0 648 }
michael@0 649 if (!miByExt && !mi) {
michael@0 650 *aFound = false;
michael@0 651 mi = new nsMIMEInfoWin(flatType);
michael@0 652 if (!aFileExt.IsEmpty()) {
michael@0 653 mi->AppendExtension(aFileExt);
michael@0 654 }
michael@0 655
michael@0 656 return mi.forget();
michael@0 657 }
michael@0 658
michael@0 659 // if we get here, mi has no default app. copy from extension lookup.
michael@0 660 nsCOMPtr<nsIFile> defaultApp;
michael@0 661 nsAutoString desc;
michael@0 662 miByExt->GetDefaultDescription(desc);
michael@0 663
michael@0 664 mi->SetDefaultDescription(desc);
michael@0 665 }
michael@0 666 return mi.forget();
michael@0 667 }
michael@0 668
michael@0 669 NS_IMETHODIMP
michael@0 670 nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
michael@0 671 bool *found,
michael@0 672 nsIHandlerInfo **_retval)
michael@0 673 {
michael@0 674 NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!");
michael@0 675
michael@0 676 nsresult rv = OSProtocolHandlerExists(nsPromiseFlatCString(aScheme).get(),
michael@0 677 found);
michael@0 678 if (NS_FAILED(rv))
michael@0 679 return rv;
michael@0 680
michael@0 681 nsMIMEInfoWin *handlerInfo =
michael@0 682 new nsMIMEInfoWin(aScheme, nsMIMEInfoBase::eProtocolInfo);
michael@0 683 NS_ENSURE_TRUE(handlerInfo, NS_ERROR_OUT_OF_MEMORY);
michael@0 684 NS_ADDREF(*_retval = handlerInfo);
michael@0 685
michael@0 686 if (!*found) {
michael@0 687 // Code that calls this requires an object regardless if the OS has
michael@0 688 // something for us, so we return the empty object.
michael@0 689 return NS_OK;
michael@0 690 }
michael@0 691
michael@0 692 nsAutoString desc;
michael@0 693 GetApplicationDescription(aScheme, desc);
michael@0 694 handlerInfo->SetDefaultDescription(desc);
michael@0 695
michael@0 696 return NS_OK;
michael@0 697 }
michael@0 698

mercurial