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