1 /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
10 #include "nsOSHelperAppService.h"
11 #include "nsMIMEInfoUnix.h"
13 # include "nsGNOMERegistry.h"
14 # ifdef MOZ_BUILD_APP_IS_BROWSER
15 # include "nsIToolkitShellService.h"
16 # include "nsIGNOMEShellService.h"
19 #include "nsISupports.h"
21 #include "nsReadableUtils.h"
22 #include "nsUnicharUtils.h"
23 #include "nsIFileStreams.h"
24 #include "nsILineInputStream.h"
26 #include "nsIProcess.h"
29 #include "nsComponentManagerUtils.h"
31 #include "nsDirectoryServiceDefs.h"
32 #include "nsDirectoryServiceUtils.h"
33 #include "nsXULAppAPI.h"
34 #include "ContentHandlerService.h"
35 #include "prenv.h" // for PR_GetEnv()
36 #include "mozilla/EnumeratedRange.h"
37 #include "mozilla/Preferences.h"
38 #include "mozilla/ClearOnShutdown.h"
39 #include "nsMimeTypes.h"
42 using namespace mozilla
;
45 MOZ_LOG(nsOSHelperAppService::sLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
46 #define LOG_ENABLED() \
47 MOZ_LOG_TEST(nsOSHelperAppService::sLog, mozilla::LogLevel::Debug)
49 static nsresult
FindSemicolon(nsAString::const_iterator
& aSemicolon_iter
,
50 const nsAString::const_iterator
& aEnd_iter
);
51 static nsresult
ParseMIMEType(const nsAString::const_iterator
& aStart_iter
,
52 nsAString::const_iterator
& aMajorTypeStart
,
53 nsAString::const_iterator
& aMajorTypeEnd
,
54 nsAString::const_iterator
& aMinorTypeStart
,
55 nsAString::const_iterator
& aMinorTypeEnd
,
56 const nsAString::const_iterator
& aEnd_iter
);
58 inline bool IsNetscapeFormat(const nsACString
& aBuffer
);
60 nsOSHelperAppService::~nsOSHelperAppService() {}
63 * Take a command with all the mailcap escapes in it and unescape it
64 * Ideally this needs the mime type, mime type options, and location of the
65 * temporary file, but this last can't be got from here
68 nsresult
nsOSHelperAppService::UnescapeCommand(const nsAString
& aEscapedCommand
,
69 const nsAString
& aMajorType
,
70 const nsAString
& aMinorType
,
71 nsACString
& aUnEscapedCommand
) {
72 LOG("-- UnescapeCommand");
73 LOG("Command to escape: '%s'\n",
74 NS_LossyConvertUTF16toASCII(aEscapedCommand
).get());
75 // XXX This function will need to get the mime type and various stuff like
76 // that being passed in to work properly
79 ("UnescapeCommand really needs some work -- it should actually do some "
82 CopyUTF16toUTF8(aEscapedCommand
, aUnEscapedCommand
);
83 LOG("Escaped command: '%s'\n", PromiseFlatCString(aUnEscapedCommand
).get());
87 /* Put aSemicolon_iter at the first non-escaped semicolon after
88 * aStart_iter but before aEnd_iter
91 static nsresult
FindSemicolon(nsAString::const_iterator
& aSemicolon_iter
,
92 const nsAString::const_iterator
& aEnd_iter
) {
93 bool semicolonFound
= false;
94 while (aSemicolon_iter
!= aEnd_iter
&& !semicolonFound
) {
95 switch (*aSemicolon_iter
) {
97 aSemicolon_iter
.advance(2);
100 semicolonFound
= true;
110 static nsresult
ParseMIMEType(const nsAString::const_iterator
& aStart_iter
,
111 nsAString::const_iterator
& aMajorTypeStart
,
112 nsAString::const_iterator
& aMajorTypeEnd
,
113 nsAString::const_iterator
& aMinorTypeStart
,
114 nsAString::const_iterator
& aMinorTypeEnd
,
115 const nsAString::const_iterator
& aEnd_iter
) {
116 nsAString::const_iterator
iter(aStart_iter
);
118 // skip leading whitespace
119 while (iter
!= aEnd_iter
&& nsCRT::IsAsciiSpace(*iter
)) {
123 if (iter
== aEnd_iter
) {
124 return NS_ERROR_INVALID_ARG
;
127 aMajorTypeStart
= iter
;
129 // find major/minor separator ('/')
130 while (iter
!= aEnd_iter
&& *iter
!= '/') {
134 if (iter
== aEnd_iter
) {
135 return NS_ERROR_INVALID_ARG
;
138 aMajorTypeEnd
= iter
;
143 if (iter
== aEnd_iter
) {
144 return NS_ERROR_INVALID_ARG
;
147 aMinorTypeStart
= iter
;
149 // find end of minor type, delimited by whitespace or ';'
150 while (iter
!= aEnd_iter
&& !nsCRT::IsAsciiSpace(*iter
) && *iter
!= ';') {
154 aMinorTypeEnd
= iter
;
159 // TODO: We should consider not only caching the file location but maybe the
160 // file contents too?
161 enum class FileKind
{
162 PrivateMimeTypes
= 0,
170 struct FileLocationCache
{
172 bool mIsCached
= false;
173 nsresult mResult
= NS_OK
;
176 void Clear() { *this = {}; }
179 EnumeratedArray
<FileKind
, Entry
, size_t(FileKind::Count
)> mEntries
;
181 static const char* PrefFor(FileKind aKind
) {
183 case FileKind::GlobalMimeTypes
:
184 return "helpers.global_mime_types_file";
185 case FileKind::PrivateMimeTypes
:
186 return "helpers.private_mime_types_file";
187 case FileKind::GlobalMailCap
:
188 return "helpers.global_mailcap_file";
189 case FileKind::PrivateMailCap
:
190 return "helpers.private_mailcap_file";
191 case FileKind::Count
:
194 MOZ_ASSERT_UNREACHABLE("Unknown file kind");
199 for (auto& entry
: mEntries
) {
204 static void PrefChangeCallback(const char*, void*) { Get().Clear(); }
206 static FileLocationCache
& Get() {
207 static FileLocationCache sCache
;
208 static std::once_flag flag
;
209 std::call_once(flag
, [&] {
211 MakeEnumeratedRange(FileKind::PrivateMimeTypes
, FileKind::Count
)) {
212 Preferences::RegisterCallback(PrefChangeCallback
,
213 nsDependentCString(PrefFor(kind
)));
218 MakeEnumeratedRange(FileKind::PrivateMimeTypes
, FileKind::Count
)) {
219 Preferences::UnregisterCallback(PrefChangeCallback
,
220 nsDependentCString(PrefFor(kind
)));
227 Entry
& EntryFor(FileKind aKind
) { return mEntries
[aKind
]; }
230 // The lookup order is:
232 // 2) env var (if any)
234 static nsresult
DoGetFileLocation(FileKind aKind
, nsAString
& aFileLocation
) {
235 LOG("-- GetFileLocation(%d)\n", int(aKind
));
237 aFileLocation
.Truncate();
239 const char* envVar
= [&]() -> const char* {
241 case FileKind::PrivateMailCap
:
242 return "PERSONAL_MAILCAP";
243 case FileKind::GlobalMailCap
:
250 const char* prefName
= FileLocationCache::PrefFor(aKind
);
253 // If we have an env var we should check whether the pref is a user pref. If
254 // we do not, we don't care.
255 if (Preferences::HasUserValue(prefName
) &&
256 NS_SUCCEEDED(Preferences::GetString(prefName
, aFileLocation
))) {
260 char* envValue
= PR_GetEnv(envVar
);
261 if (envValue
&& *envValue
) {
262 // the pref is in the system charset and it's a filepath... The
263 // natural way to do the charset conversion is by just initing
264 // an nsIFile with the native path and asking it for the Unicode
267 nsCOMPtr
<nsIFile
> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
, &rv
));
268 NS_ENSURE_SUCCESS(rv
, rv
);
270 rv
= file
->InitWithNativePath(nsDependentCString(envValue
));
271 NS_ENSURE_SUCCESS(rv
, rv
);
273 return file
->GetPath(aFileLocation
);
277 return Preferences::GetString(prefName
, aFileLocation
);
280 static nsresult
GetFileLocation(FileKind aKind
, nsAString
& aFileLocation
) {
281 MOZ_ASSERT(NS_IsMainThread());
282 auto& entry
= FileLocationCache::Get().EntryFor(aKind
);
283 if (!entry
.mIsCached
) {
284 entry
.mIsCached
= true;
285 entry
.mResult
= DoGetFileLocation(aKind
, entry
.mLocation
);
287 aFileLocation
= entry
.mLocation
;
288 return entry
.mResult
;
291 /* Get the mime.types file names from prefs and look up info in them
292 based on extension */
294 nsresult
nsOSHelperAppService::LookUpTypeAndDescription(
295 const nsAString
& aFileExtension
, nsAString
& aMajorType
,
296 nsAString
& aMinorType
, nsAString
& aDescription
, bool aUserData
) {
297 LOG("-- LookUpTypeAndDescription for extension '%s'\n",
298 NS_LossyConvertUTF16toASCII(aFileExtension
).get());
299 nsAutoString mimeFileName
;
302 aUserData
? FileKind::PrivateMimeTypes
: FileKind::GlobalMimeTypes
;
303 nsresult rv
= GetFileLocation(kind
, mimeFileName
);
304 if (NS_SUCCEEDED(rv
) && !mimeFileName
.IsEmpty()) {
305 rv
= GetTypeAndDescriptionFromMimetypesFile(
306 mimeFileName
, aFileExtension
, aMajorType
, aMinorType
, aDescription
);
308 rv
= NS_ERROR_NOT_AVAILABLE
;
314 inline bool IsNetscapeFormat(const nsACString
& aBuffer
) {
315 return StringBeginsWith(
318 "#--Netscape Communications Corporation MIME Information")) ||
319 StringBeginsWith(aBuffer
, "#--MCOM MIME Information"_ns
);
323 * Create a file stream and line input stream for the filename.
324 * Leaves the first line of the file in aBuffer and sets the format to
325 * true for netscape files and false for normail ones
328 nsresult
nsOSHelperAppService::CreateInputStream(
329 const nsAString
& aFilename
, nsIFileInputStream
** aFileInputStream
,
330 nsILineInputStream
** aLineInputStream
, nsACString
& aBuffer
,
331 bool* aNetscapeFormat
, bool* aMore
) {
332 LOG("-- CreateInputStream");
335 nsCOMPtr
<nsIFile
> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
, &rv
));
336 if (NS_FAILED(rv
)) return rv
;
337 rv
= file
->InitWithPath(aFilename
);
338 if (NS_FAILED(rv
)) return rv
;
340 nsCOMPtr
<nsIFileInputStream
> fileStream(
341 do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID
, &rv
));
342 if (NS_FAILED(rv
)) return rv
;
343 rv
= fileStream
->Init(file
, -1, -1, false);
344 if (NS_FAILED(rv
)) return rv
;
346 nsCOMPtr
<nsILineInputStream
> lineStream(do_QueryInterface(fileStream
, &rv
));
349 LOG("Interface trouble in stream land!");
353 rv
= lineStream
->ReadLine(aBuffer
, aMore
);
359 *aNetscapeFormat
= IsNetscapeFormat(aBuffer
);
361 *aFileInputStream
= fileStream
;
362 NS_ADDREF(*aFileInputStream
);
363 *aLineInputStream
= lineStream
;
364 NS_ADDREF(*aLineInputStream
);
369 /* Open the file, read the first line, decide what type of file it is,
370 then get info based on extension */
372 nsresult
nsOSHelperAppService::GetTypeAndDescriptionFromMimetypesFile(
373 const nsAString
& aFilename
, const nsAString
& aFileExtension
,
374 nsAString
& aMajorType
, nsAString
& aMinorType
, nsAString
& aDescription
) {
375 LOG("-- GetTypeAndDescriptionFromMimetypesFile\n");
376 LOG("Getting type and description from types file '%s'\n",
377 NS_LossyConvertUTF16toASCII(aFilename
).get());
378 LOG("Using extension '%s'\n",
379 NS_LossyConvertUTF16toASCII(aFileExtension
).get());
380 nsCOMPtr
<nsIFileInputStream
> mimeFile
;
381 nsCOMPtr
<nsILineInputStream
> mimeTypes
;
386 nsresult rv
= CreateInputStream(aFilename
, getter_AddRefs(mimeFile
),
387 getter_AddRefs(mimeTypes
), cBuf
,
388 &netscapeFormat
, &more
);
394 nsAutoString extensions
;
395 nsAutoStringN
<101> entry
;
396 nsAString::const_iterator majorTypeStart
, majorTypeEnd
, minorTypeStart
,
397 minorTypeEnd
, descriptionStart
, descriptionEnd
;
400 CopyASCIItoUTF16(cBuf
, buf
);
401 // read through, building up an entry. If we finish an entry, check for
402 // a match and return out of the loop if we match
404 // skip comments and empty lines
405 if (!buf
.IsEmpty() && buf
.First() != '#') {
407 if (entry
.Last() == '\\') {
408 entry
.Truncate(entry
.Length() - 1);
409 entry
.Append(char16_t(
410 ' ')); // in case there is no trailing whitespace on this line
411 } else { // we have a full entry
412 LOG("Current entry: '%s'\n", NS_LossyConvertUTF16toASCII(entry
).get());
413 if (netscapeFormat
) {
414 rv
= ParseNetscapeMIMETypesEntry(
415 entry
, majorTypeStart
, majorTypeEnd
, minorTypeStart
, minorTypeEnd
,
416 extensions
, descriptionStart
, descriptionEnd
);
418 // We sometimes get things like RealPlayer appending
419 // "normal" entries to "Netscape" .mime.types files. Try
420 // to handle that. Bug 106381.
421 LOG("Bogus entry; trying 'normal' mode\n");
422 rv
= ParseNormalMIMETypesEntry(
423 entry
, majorTypeStart
, majorTypeEnd
, minorTypeStart
,
424 minorTypeEnd
, extensions
, descriptionStart
, descriptionEnd
);
427 rv
= ParseNormalMIMETypesEntry(
428 entry
, majorTypeStart
, majorTypeEnd
, minorTypeStart
, minorTypeEnd
,
429 extensions
, descriptionStart
, descriptionEnd
);
431 // We sometimes get things like StarOffice prepending
432 // "normal" entries to "Netscape" .mime.types files. Try
433 // to handle that. Bug 136670.
434 LOG("Bogus entry; trying 'Netscape' mode\n");
435 rv
= ParseNetscapeMIMETypesEntry(
436 entry
, majorTypeStart
, majorTypeEnd
, minorTypeStart
,
437 minorTypeEnd
, extensions
, descriptionStart
, descriptionEnd
);
441 if (NS_SUCCEEDED(rv
)) { // entry parses
442 nsAString::const_iterator start
, end
;
443 extensions
.BeginReading(start
);
444 extensions
.EndReading(end
);
445 nsAString::const_iterator
iter(start
);
447 while (start
!= end
) {
448 FindCharInReadable(',', iter
, end
);
449 if (Substring(start
, iter
)
450 .Equals(aFileExtension
,
451 nsCaseInsensitiveStringComparator
)) {
452 // it's a match. Assign the type and description and run
453 aMajorType
.Assign(Substring(majorTypeStart
, majorTypeEnd
));
454 aMinorType
.Assign(Substring(minorTypeStart
, minorTypeEnd
));
455 aDescription
.Assign(Substring(descriptionStart
, descriptionEnd
));
465 LOG("Failed to parse entry: %s\n",
466 NS_LossyConvertUTF16toASCII(entry
).get());
468 // truncate the entry for the next iteration
473 rv
= NS_ERROR_NOT_AVAILABLE
;
476 // read the next line
477 rv
= mimeTypes
->ReadLine(cBuf
, &more
);
478 } while (NS_SUCCEEDED(rv
));
484 /* Get the mime.types file names from prefs and look up info in them
487 nsresult
nsOSHelperAppService::LookUpExtensionsAndDescription(
488 const nsAString
& aMajorType
, const nsAString
& aMinorType
,
489 nsAString
& aFileExtensions
, nsAString
& aDescription
) {
490 LOG("-- LookUpExtensionsAndDescription for type '%s/%s'\n",
491 NS_LossyConvertUTF16toASCII(aMajorType
).get(),
492 NS_LossyConvertUTF16toASCII(aMinorType
).get());
493 nsAutoString mimeFileName
;
495 nsresult rv
= GetFileLocation(FileKind::PrivateMimeTypes
, mimeFileName
);
496 if (NS_SUCCEEDED(rv
) && !mimeFileName
.IsEmpty()) {
497 rv
= GetExtensionsAndDescriptionFromMimetypesFile(
498 mimeFileName
, aMajorType
, aMinorType
, aFileExtensions
, aDescription
);
500 rv
= NS_ERROR_NOT_AVAILABLE
;
502 if (NS_FAILED(rv
) || aFileExtensions
.IsEmpty()) {
503 rv
= GetFileLocation(FileKind::GlobalMimeTypes
, mimeFileName
);
504 if (NS_SUCCEEDED(rv
) && !mimeFileName
.IsEmpty()) {
505 rv
= GetExtensionsAndDescriptionFromMimetypesFile(
506 mimeFileName
, aMajorType
, aMinorType
, aFileExtensions
, aDescription
);
508 rv
= NS_ERROR_NOT_AVAILABLE
;
514 /* Open the file, read the first line, decide what type of file it is,
515 then get info based on extension */
517 nsresult
nsOSHelperAppService::GetExtensionsAndDescriptionFromMimetypesFile(
518 const nsAString
& aFilename
, const nsAString
& aMajorType
,
519 const nsAString
& aMinorType
, nsAString
& aFileExtensions
,
520 nsAString
& aDescription
) {
521 LOG("-- GetExtensionsAndDescriptionFromMimetypesFile\n");
522 LOG("Getting extensions and description from types file '%s'\n",
523 NS_LossyConvertUTF16toASCII(aFilename
).get());
524 LOG("Using type '%s/%s'\n", NS_LossyConvertUTF16toASCII(aMajorType
).get(),
525 NS_LossyConvertUTF16toASCII(aMinorType
).get());
526 nsCOMPtr
<nsIFileInputStream
> mimeFile
;
527 nsCOMPtr
<nsILineInputStream
> mimeTypes
;
532 nsresult rv
= CreateInputStream(aFilename
, getter_AddRefs(mimeFile
),
533 getter_AddRefs(mimeTypes
), cBuf
,
534 &netscapeFormat
, &more
);
539 nsAutoString extensions
;
540 nsAutoStringN
<101> entry
;
541 nsAString::const_iterator majorTypeStart
, majorTypeEnd
, minorTypeStart
,
542 minorTypeEnd
, descriptionStart
, descriptionEnd
;
545 CopyASCIItoUTF16(cBuf
, buf
);
546 // read through, building up an entry. If we finish an entry, check for
547 // a match and return out of the loop if we match
549 // skip comments and empty lines
550 if (!buf
.IsEmpty() && buf
.First() != '#') {
552 if (entry
.Last() == '\\') {
553 entry
.Truncate(entry
.Length() - 1);
554 entry
.Append(char16_t(
555 ' ')); // in case there is no trailing whitespace on this line
556 } else { // we have a full entry
557 LOG("Current entry: '%s'\n", NS_LossyConvertUTF16toASCII(entry
).get());
558 if (netscapeFormat
) {
559 rv
= ParseNetscapeMIMETypesEntry(
560 entry
, majorTypeStart
, majorTypeEnd
, minorTypeStart
, minorTypeEnd
,
561 extensions
, descriptionStart
, descriptionEnd
);
564 // We sometimes get things like RealPlayer appending
565 // "normal" entries to "Netscape" .mime.types files. Try
566 // to handle that. Bug 106381.
567 LOG("Bogus entry; trying 'normal' mode\n");
568 rv
= ParseNormalMIMETypesEntry(
569 entry
, majorTypeStart
, majorTypeEnd
, minorTypeStart
,
570 minorTypeEnd
, extensions
, descriptionStart
, descriptionEnd
);
573 rv
= ParseNormalMIMETypesEntry(
574 entry
, majorTypeStart
, majorTypeEnd
, minorTypeStart
, minorTypeEnd
,
575 extensions
, descriptionStart
, descriptionEnd
);
578 // We sometimes get things like StarOffice prepending
579 // "normal" entries to "Netscape" .mime.types files. Try
580 // to handle that. Bug 136670.
581 LOG("Bogus entry; trying 'Netscape' mode\n");
582 rv
= ParseNetscapeMIMETypesEntry(
583 entry
, majorTypeStart
, majorTypeEnd
, minorTypeStart
,
584 minorTypeEnd
, extensions
, descriptionStart
, descriptionEnd
);
588 if (NS_SUCCEEDED(rv
) &&
589 Substring(majorTypeStart
, majorTypeEnd
)
590 .Equals(aMajorType
, nsCaseInsensitiveStringComparator
) &&
591 Substring(minorTypeStart
, minorTypeEnd
)
592 .Equals(aMinorType
, nsCaseInsensitiveStringComparator
)) {
594 aFileExtensions
.Assign(extensions
);
595 aDescription
.Assign(Substring(descriptionStart
, descriptionEnd
));
600 LOG("Failed to parse entry: %s\n",
601 NS_LossyConvertUTF16toASCII(entry
).get());
608 rv
= NS_ERROR_NOT_AVAILABLE
;
611 // read the next line
612 rv
= mimeTypes
->ReadLine(cBuf
, &more
);
613 } while (NS_SUCCEEDED(rv
));
620 * This parses a Netscape format mime.types entry. There are two
623 * type=foo/bar; options exts="baz" description="Some type"
627 * type=foo/bar; options description="Some type" exts="baz"
630 nsresult
nsOSHelperAppService::ParseNetscapeMIMETypesEntry(
631 const nsAString
& aEntry
, nsAString::const_iterator
& aMajorTypeStart
,
632 nsAString::const_iterator
& aMajorTypeEnd
,
633 nsAString::const_iterator
& aMinorTypeStart
,
634 nsAString::const_iterator
& aMinorTypeEnd
, nsAString
& aExtensions
,
635 nsAString::const_iterator
& aDescriptionStart
,
636 nsAString::const_iterator
& aDescriptionEnd
) {
637 LOG("-- ParseNetscapeMIMETypesEntry\n");
638 NS_ASSERTION(!aEntry
.IsEmpty(),
639 "Empty Netscape MIME types entry being parsed.");
641 nsAString::const_iterator start_iter
, end_iter
, match_start
, match_end
;
643 aEntry
.BeginReading(start_iter
);
644 aEntry
.EndReading(end_iter
);
646 // skip trailing whitespace
649 } while (end_iter
!= start_iter
&& nsCRT::IsAsciiSpace(*end_iter
));
650 // if we're pointing to a quote, don't advance -- we don't want to
651 // include the quote....
652 if (*end_iter
!= '"') ++end_iter
;
653 match_start
= start_iter
;
654 match_end
= end_iter
;
656 // Get the major and minor types
657 // First the major type
658 if (!FindInReadable(u
"type="_ns
, match_start
, match_end
)) {
659 return NS_ERROR_FAILURE
;
662 match_start
= match_end
;
664 while (match_end
!= end_iter
&& *match_end
!= '/') {
667 if (match_end
== end_iter
) {
668 return NS_ERROR_FAILURE
;
671 aMajorTypeStart
= match_start
;
672 aMajorTypeEnd
= match_end
;
674 // now the minor type
675 if (++match_end
== end_iter
) {
676 return NS_ERROR_FAILURE
;
679 match_start
= match_end
;
681 while (match_end
!= end_iter
&& !nsCRT::IsAsciiSpace(*match_end
) &&
685 if (match_end
== end_iter
) {
686 return NS_ERROR_FAILURE
;
689 aMinorTypeStart
= match_start
;
690 aMinorTypeEnd
= match_end
;
692 // ignore everything up to the end of the mime type from here on
693 start_iter
= match_end
;
695 // get the extensions
696 match_start
= match_end
;
697 match_end
= end_iter
;
698 if (FindInReadable(u
"exts="_ns
, match_start
, match_end
)) {
699 nsAString::const_iterator extStart
, extEnd
;
701 if (match_end
== end_iter
||
702 (*match_end
== '"' && ++match_end
== end_iter
)) {
703 return NS_ERROR_FAILURE
;
706 extStart
= match_end
;
707 match_start
= extStart
;
708 match_end
= end_iter
;
709 if (FindInReadable(u
"desc=\""_ns
, match_start
, match_end
)) {
710 // exts= before desc=, so we have to find the actual end of the extensions
711 extEnd
= match_start
;
712 if (extEnd
== extStart
) {
713 return NS_ERROR_FAILURE
;
718 } while (extEnd
!= extStart
&& nsCRT::IsAsciiSpace(*extEnd
));
720 if (extEnd
!= extStart
&& *extEnd
== '"') {
724 // desc= before exts=, so we can use end_iter as the end of the extensions
727 aExtensions
= Substring(extStart
, extEnd
);
730 aExtensions
.Truncate();
733 // get the description
734 match_start
= start_iter
;
735 match_end
= end_iter
;
736 if (FindInReadable(u
"desc=\""_ns
, match_start
, match_end
)) {
737 aDescriptionStart
= match_end
;
738 match_start
= aDescriptionStart
;
739 match_end
= end_iter
;
740 if (FindInReadable(u
"exts="_ns
, match_start
, match_end
)) {
741 // exts= after desc=, so have to find actual end of description
742 aDescriptionEnd
= match_start
;
743 if (aDescriptionEnd
== aDescriptionStart
) {
744 return NS_ERROR_FAILURE
;
749 } while (aDescriptionEnd
!= aDescriptionStart
&&
750 nsCRT::IsAsciiSpace(*aDescriptionEnd
));
752 // desc= after exts=, so use end_iter for the description end
753 aDescriptionEnd
= end_iter
;
757 aDescriptionStart
= start_iter
;
758 aDescriptionEnd
= start_iter
;
765 * This parses a normal format mime.types entry. The format is:
767 * major/minor ext1 ext2 ext3
770 nsresult
nsOSHelperAppService::ParseNormalMIMETypesEntry(
771 const nsAString
& aEntry
, nsAString::const_iterator
& aMajorTypeStart
,
772 nsAString::const_iterator
& aMajorTypeEnd
,
773 nsAString::const_iterator
& aMinorTypeStart
,
774 nsAString::const_iterator
& aMinorTypeEnd
, nsAString
& aExtensions
,
775 nsAString::const_iterator
& aDescriptionStart
,
776 nsAString::const_iterator
& aDescriptionEnd
) {
777 LOG("-- ParseNormalMIMETypesEntry\n");
778 NS_ASSERTION(!aEntry
.IsEmpty(),
779 "Empty Normal MIME types entry being parsed.");
781 nsAString::const_iterator start_iter
, end_iter
, iter
;
783 aEntry
.BeginReading(start_iter
);
784 aEntry
.EndReading(end_iter
);
787 aDescriptionStart
= start_iter
;
788 aDescriptionEnd
= start_iter
;
790 // skip leading whitespace
791 while (start_iter
!= end_iter
&& nsCRT::IsAsciiSpace(*start_iter
)) {
794 if (start_iter
== end_iter
) {
795 return NS_ERROR_FAILURE
;
797 // skip trailing whitespace
800 } while (end_iter
!= start_iter
&& nsCRT::IsAsciiSpace(*end_iter
));
802 ++end_iter
; // point to first whitespace char (or to end of string)
805 // get the major type
806 if (!FindCharInReadable('/', iter
, end_iter
)) return NS_ERROR_FAILURE
;
808 nsAString::const_iterator
equals_sign_iter(start_iter
);
809 if (FindCharInReadable('=', equals_sign_iter
, iter
))
810 return NS_ERROR_FAILURE
; // see bug 136670
812 aMajorTypeStart
= start_iter
;
813 aMajorTypeEnd
= iter
;
815 // get the minor type
816 if (++iter
== end_iter
) {
817 return NS_ERROR_FAILURE
;
821 while (iter
!= end_iter
&& !nsCRT::IsAsciiSpace(*iter
)) {
824 aMinorTypeStart
= start_iter
;
825 aMinorTypeEnd
= iter
;
827 // get the extensions
828 aExtensions
.Truncate();
829 while (iter
!= end_iter
) {
830 while (iter
!= end_iter
&& nsCRT::IsAsciiSpace(*iter
)) {
835 while (iter
!= end_iter
&& !nsCRT::IsAsciiSpace(*iter
)) {
838 aExtensions
.Append(Substring(start_iter
, iter
));
839 if (iter
!= end_iter
) { // not the last extension
840 aExtensions
.Append(char16_t(','));
848 nsresult
nsOSHelperAppService::LookUpHandlerAndDescription(
849 const nsAString
& aMajorType
, const nsAString
& aMinorType
,
850 nsAString
& aHandler
, nsAString
& aDescription
, nsAString
& aMozillaFlags
) {
851 // The mailcap lookup is two-pass to handle the case of mailcap files
852 // that have something like:
855 // text/rtf; soffice %s
857 // in that order. We want to pick up "soffice" for text/rtf in such cases
858 nsresult rv
= DoLookUpHandlerAndDescription(
859 aMajorType
, aMinorType
, aHandler
, aDescription
, aMozillaFlags
, true);
861 rv
= DoLookUpHandlerAndDescription(aMajorType
, aMinorType
, aHandler
,
862 aDescription
, aMozillaFlags
, false);
865 // maybe we have an entry for "aMajorType/*"?
867 rv
= DoLookUpHandlerAndDescription(aMajorType
, u
"*"_ns
, aHandler
,
868 aDescription
, aMozillaFlags
, true);
872 rv
= DoLookUpHandlerAndDescription(aMajorType
, u
"*"_ns
, aHandler
,
873 aDescription
, aMozillaFlags
, false);
880 nsresult
nsOSHelperAppService::DoLookUpHandlerAndDescription(
881 const nsAString
& aMajorType
, const nsAString
& aMinorType
,
882 nsAString
& aHandler
, nsAString
& aDescription
, nsAString
& aMozillaFlags
,
884 LOG("-- LookUpHandlerAndDescription for type '%s/%s'\n",
885 NS_LossyConvertUTF16toASCII(aMajorType
).get(),
886 NS_LossyConvertUTF16toASCII(aMinorType
).get());
887 nsAutoString mailcapFileName
;
890 aUserData
? FileKind::PrivateMailCap
: FileKind::GlobalMailCap
;
891 nsresult rv
= GetFileLocation(kind
, mailcapFileName
);
892 if (NS_FAILED(rv
) || mailcapFileName
.IsEmpty()) {
893 return NS_ERROR_NOT_AVAILABLE
;
895 return GetHandlerAndDescriptionFromMailcapFile(mailcapFileName
, aMajorType
,
896 aMinorType
, aHandler
,
897 aDescription
, aMozillaFlags
);
901 nsresult
nsOSHelperAppService::GetHandlerAndDescriptionFromMailcapFile(
902 const nsAString
& aFilename
, const nsAString
& aMajorType
,
903 const nsAString
& aMinorType
, nsAString
& aHandler
, nsAString
& aDescription
,
904 nsAString
& aMozillaFlags
) {
905 LOG("-- GetHandlerAndDescriptionFromMailcapFile\n");
906 LOG("Getting handler and description from mailcap file '%s'\n",
907 NS_LossyConvertUTF16toASCII(aFilename
).get());
908 LOG("Using type '%s/%s'\n", NS_LossyConvertUTF16toASCII(aMajorType
).get(),
909 NS_LossyConvertUTF16toASCII(aMinorType
).get());
914 nsCOMPtr
<nsIFile
> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
, &rv
));
915 if (NS_FAILED(rv
)) return rv
;
916 rv
= file
->InitWithPath(aFilename
);
917 if (NS_FAILED(rv
)) return rv
;
919 nsCOMPtr
<nsIFileInputStream
> mailcapFile(
920 do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID
, &rv
));
921 if (NS_FAILED(rv
)) return rv
;
922 rv
= mailcapFile
->Init(file
, -1, -1, false);
923 if (NS_FAILED(rv
)) return rv
;
925 nsCOMPtr
<nsILineInputStream
> mailcap(do_QueryInterface(mailcapFile
, &rv
));
928 LOG("Interface trouble in stream land!");
932 nsAutoStringN
<129> entry
;
933 nsAutoStringN
<81> buffer
;
934 nsAutoCStringN
<81> cBuffer
;
935 rv
= mailcap
->ReadLine(cBuffer
, &more
);
937 mailcapFile
->Close();
941 do { // return on end-of-file in the loop
943 CopyASCIItoUTF16(cBuffer
, buffer
);
944 if (!buffer
.IsEmpty() && buffer
.First() != '#') {
945 entry
.Append(buffer
);
946 if (entry
.Last() == '\\') { // entry continues on next line
947 entry
.Truncate(entry
.Length() - 1);
948 entry
.Append(char16_t(
949 ' ')); // in case there is no trailing whitespace on this line
950 } else { // we have a full entry in entry. Check it for the type
951 LOG("Current entry: '%s'\n", NS_LossyConvertUTF16toASCII(entry
).get());
953 nsAString::const_iterator semicolon_iter
, start_iter
, end_iter
,
954 majorTypeStart
, majorTypeEnd
, minorTypeStart
, minorTypeEnd
;
955 entry
.BeginReading(start_iter
);
956 entry
.EndReading(end_iter
);
957 semicolon_iter
= start_iter
;
958 FindSemicolon(semicolon_iter
, end_iter
);
959 if (semicolon_iter
!=
960 end_iter
) { // we have something resembling a valid entry
961 rv
= ParseMIMEType(start_iter
, majorTypeStart
, majorTypeEnd
,
962 minorTypeStart
, minorTypeEnd
, semicolon_iter
);
963 if (NS_SUCCEEDED(rv
) &&
964 Substring(majorTypeStart
, majorTypeEnd
)
965 .Equals(aMajorType
, nsCaseInsensitiveStringComparator
) &&
966 Substring(minorTypeStart
, minorTypeEnd
)
967 .Equals(aMinorType
, nsCaseInsensitiveStringComparator
)) {
970 ++semicolon_iter
; // point at the first char past the semicolon
971 start_iter
= semicolon_iter
; // handler string starts here
972 FindSemicolon(semicolon_iter
, end_iter
);
973 while (start_iter
!= semicolon_iter
&&
974 nsCRT::IsAsciiSpace(*start_iter
)) {
978 LOG("The real handler is: '%s'\n",
979 NS_LossyConvertUTF16toASCII(
980 Substring(start_iter
, semicolon_iter
))
983 // XXX ugly hack. Just grab the executable name
984 nsAString::const_iterator end_handler_iter
= semicolon_iter
;
985 nsAString::const_iterator end_executable_iter
= start_iter
;
986 while (end_executable_iter
!= end_handler_iter
&&
987 !nsCRT::IsAsciiSpace(*end_executable_iter
)) {
988 ++end_executable_iter
;
992 aHandler
= Substring(start_iter
, end_executable_iter
);
994 nsAString::const_iterator start_option_iter
, end_optionname_iter
,
997 while (match
&& semicolon_iter
!= end_iter
&&
999 end_iter
) { // there are options left and we still match
1000 start_option_iter
= semicolon_iter
;
1001 // skip over leading whitespace
1002 while (start_option_iter
!= end_iter
&&
1003 nsCRT::IsAsciiSpace(*start_option_iter
)) {
1004 ++start_option_iter
;
1006 if (start_option_iter
== end_iter
) { // nothing actually here
1009 semicolon_iter
= start_option_iter
;
1010 FindSemicolon(semicolon_iter
, end_iter
);
1011 equal_sign_iter
= start_option_iter
;
1012 equalSignFound
= false;
1013 while (equal_sign_iter
!= semicolon_iter
&& !equalSignFound
) {
1014 switch (*equal_sign_iter
) {
1016 equal_sign_iter
.advance(2);
1019 equalSignFound
= true;
1026 end_optionname_iter
= start_option_iter
;
1027 // find end of option name
1028 while (end_optionname_iter
!= equal_sign_iter
&&
1029 !nsCRT::IsAsciiSpace(*end_optionname_iter
)) {
1030 ++end_optionname_iter
;
1032 nsDependentSubstring
optionName(start_option_iter
,
1033 end_optionname_iter
);
1034 if (equalSignFound
) {
1035 // This is an option that has a name and value
1036 if (optionName
.EqualsLiteral("description")) {
1037 aDescription
= Substring(++equal_sign_iter
, semicolon_iter
);
1038 } else if (optionName
.EqualsLiteral("x-mozilla-flags")) {
1039 aMozillaFlags
= Substring(++equal_sign_iter
, semicolon_iter
);
1040 } else if (optionName
.EqualsLiteral("test")) {
1041 nsAutoCString testCommand
;
1042 rv
= UnescapeCommand(
1043 Substring(++equal_sign_iter
, semicolon_iter
), aMajorType
,
1044 aMinorType
, testCommand
);
1045 if (NS_FAILED(rv
)) continue;
1046 nsCOMPtr
<nsIProcess
> process
=
1047 do_CreateInstance(NS_PROCESS_CONTRACTID
, &rv
);
1048 if (NS_FAILED(rv
)) continue;
1049 nsCOMPtr
<nsIFile
> file(
1050 do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
, &rv
));
1051 if (NS_FAILED(rv
)) continue;
1052 rv
= file
->InitWithNativePath("/bin/sh"_ns
);
1053 if (NS_FAILED(rv
)) continue;
1054 rv
= process
->Init(file
);
1055 if (NS_FAILED(rv
)) continue;
1056 const char* args
[] = {"-c", testCommand
.get()};
1057 LOG("Running Test: %s\n", testCommand
.get());
1058 rv
= process
->Run(true, args
, 2);
1059 if (NS_FAILED(rv
)) continue;
1061 rv
= process
->GetExitValue(&exitValue
);
1062 if (NS_FAILED(rv
)) continue;
1063 LOG("Exit code: %d\n", exitValue
);
1069 // This is an option that just has a name but no value (eg
1071 if (optionName
.EqualsLiteral("needsterminal")) {
1077 if (match
) { // we did not fail any test clauses; all is good
1079 mailcapFile
->Close();
1082 // pretend that this match never happened
1083 aDescription
.Truncate();
1084 aMozillaFlags
.Truncate();
1085 aHandler
.Truncate();
1088 // zero out the entry for the next cycle
1093 rv
= NS_ERROR_NOT_AVAILABLE
;
1096 rv
= mailcap
->ReadLine(cBuffer
, &more
);
1097 } while (NS_SUCCEEDED(rv
));
1098 mailcapFile
->Close();
1102 nsresult
nsOSHelperAppService::OSProtocolHandlerExists(
1103 const char* aProtocolScheme
, bool* aHandlerExists
) {
1104 nsresult rv
= NS_OK
;
1106 if (!XRE_IsContentProcess()) {
1107 #ifdef MOZ_WIDGET_GTK
1108 // Check the GNOME registry for a protocol handler
1109 *aHandlerExists
= nsGNOMERegistry::HandlerExists(aProtocolScheme
);
1111 *aHandlerExists
= false;
1114 *aHandlerExists
= false;
1115 nsCOMPtr
<nsIHandlerService
> handlerSvc
=
1116 do_GetService(NS_HANDLERSERVICE_CONTRACTID
, &rv
);
1117 if (NS_SUCCEEDED(rv
) && handlerSvc
) {
1118 rv
= handlerSvc
->ExistsForProtocolOS(nsCString(aProtocolScheme
),
1126 NS_IMETHODIMP
nsOSHelperAppService::GetApplicationDescription(
1127 const nsACString
& aScheme
, nsAString
& _retval
) {
1128 #ifdef MOZ_WIDGET_GTK
1129 nsGNOMERegistry::GetAppDescForScheme(aScheme
, _retval
);
1130 return _retval
.IsEmpty() ? NS_ERROR_NOT_AVAILABLE
: NS_OK
;
1132 return NS_ERROR_NOT_AVAILABLE
;
1136 NS_IMETHODIMP
nsOSHelperAppService::IsCurrentAppOSDefaultForProtocol(
1137 const nsACString
& aScheme
, bool* _retval
) {
1139 #if defined(MOZ_BUILD_APP_IS_BROWSER) && defined(MOZ_WIDGET_GTK)
1140 if (nsCOMPtr
<nsIGNOMEShellService
> shell
=
1141 do_GetService(NS_TOOLKITSHELLSERVICE_CONTRACTID
)) {
1142 return shell
->IsDefaultForScheme(aScheme
, _retval
);
1148 nsresult
nsOSHelperAppService::GetFileTokenForPath(
1149 const char16_t
* platformAppPath
, nsIFile
** aFile
) {
1150 LOG("-- nsOSHelperAppService::GetFileTokenForPath: '%s'\n",
1151 NS_LossyConvertUTF16toASCII(platformAppPath
).get());
1152 if (!*platformAppPath
) { // empty filename--return error
1153 NS_WARNING("Empty filename passed in.");
1154 return NS_ERROR_INVALID_ARG
;
1157 // first check if the base class implementation finds anything
1159 nsExternalHelperAppService::GetFileTokenForPath(platformAppPath
, aFile
);
1160 if (NS_SUCCEEDED(rv
)) return rv
;
1161 // If the reason for failure was that the file doesn't exist, return too
1162 // (because it means the path was absolute, and so that we shouldn't search in
1164 if (rv
== NS_ERROR_FILE_NOT_FOUND
) return rv
;
1166 // If we get here, we really should have a relative path.
1167 NS_ASSERTION(*platformAppPath
!= char16_t('/'), "Unexpected absolute path");
1169 nsCOMPtr
<nsIFile
> localFile(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID
));
1171 if (!localFile
) return NS_ERROR_NOT_INITIALIZED
;
1173 bool exists
= false;
1174 // ugly hack. Walk the PATH variable...
1175 char* unixpath
= PR_GetEnv("PATH");
1176 nsAutoCString
path(unixpath
);
1178 const char* start_iter
= path
.BeginReading(start_iter
);
1179 const char* colon_iter
= start_iter
;
1180 const char* end_iter
= path
.EndReading(end_iter
);
1182 while (start_iter
!= end_iter
&& !exists
) {
1183 while (colon_iter
!= end_iter
&& *colon_iter
!= ':') {
1186 localFile
->InitWithNativePath(Substring(start_iter
, colon_iter
));
1187 rv
= localFile
->AppendRelativePath(nsDependentString(platformAppPath
));
1188 // Failing AppendRelativePath is a bad thing - it should basically always
1189 // succeed given a relative path. Show a warning if it does fail.
1190 // To prevent infinite loops when it does fail, return at this point.
1191 NS_ENSURE_SUCCESS(rv
, rv
);
1192 localFile
->Exists(&exists
);
1194 if (colon_iter
== end_iter
) {
1198 start_iter
= colon_iter
;
1205 rv
= NS_ERROR_NOT_AVAILABLE
;
1209 NS_IF_ADDREF(*aFile
);
1214 already_AddRefed
<nsMIMEInfoBase
> nsOSHelperAppService::GetFromExtension(
1215 const nsCString
& aFileExt
) {
1216 // if the extension is empty, return immediately
1217 if (aFileExt
.IsEmpty()) {
1221 LOG("Here we do an extension lookup for '%s'\n", aFileExt
.get());
1223 nsAutoString majorType
, minorType
, mime_types_description
,
1224 mailcap_description
, handler
, mozillaFlags
;
1227 LookUpTypeAndDescription(NS_ConvertUTF8toUTF16(aFileExt
), majorType
,
1228 minorType
, mime_types_description
, true);
1230 if (NS_FAILED(rv
) || majorType
.IsEmpty()) {
1231 #ifdef MOZ_WIDGET_GTK
1232 LOG("Looking in GNOME registry\n");
1233 RefPtr
<nsMIMEInfoBase
> gnomeInfo
=
1234 nsGNOMERegistry::GetFromExtension(aFileExt
);
1236 LOG("Got MIMEInfo from GNOME registry\n");
1237 return gnomeInfo
.forget();
1241 rv
= LookUpTypeAndDescription(NS_ConvertUTF8toUTF16(aFileExt
), majorType
,
1242 minorType
, mime_types_description
, false);
1245 if (NS_FAILED(rv
)) {
1249 NS_LossyConvertUTF16toASCII
asciiMajorType(majorType
);
1250 NS_LossyConvertUTF16toASCII
asciiMinorType(minorType
);
1252 LOG("Type/Description results: majorType='%s', minorType='%s', "
1253 "description='%s'\n",
1254 asciiMajorType
.get(), asciiMinorType
.get(),
1255 NS_LossyConvertUTF16toASCII(mime_types_description
).get());
1257 if (majorType
.IsEmpty() && minorType
.IsEmpty()) {
1258 // we didn't get a type mapping, so we can't do anything useful
1262 nsAutoCString
mimeType(asciiMajorType
+ "/"_ns
+ asciiMinorType
);
1263 RefPtr
<nsMIMEInfoUnix
> mimeInfo
= new nsMIMEInfoUnix(mimeType
);
1265 mimeInfo
->AppendExtension(aFileExt
);
1266 rv
= LookUpHandlerAndDescription(majorType
, minorType
, handler
,
1267 mailcap_description
, mozillaFlags
);
1268 LOG("Handler/Description results: handler='%s', description='%s', "
1269 "mozillaFlags='%s'\n",
1270 NS_LossyConvertUTF16toASCII(handler
).get(),
1271 NS_LossyConvertUTF16toASCII(mailcap_description
).get(),
1272 NS_LossyConvertUTF16toASCII(mozillaFlags
).get());
1273 mailcap_description
.Trim(" \t\"");
1274 mozillaFlags
.Trim(" \t");
1275 if (!mime_types_description
.IsEmpty()) {
1276 mimeInfo
->SetDescription(mime_types_description
);
1278 mimeInfo
->SetDescription(mailcap_description
);
1281 if (NS_SUCCEEDED(rv
) && handler
.IsEmpty()) {
1282 rv
= NS_ERROR_NOT_AVAILABLE
;
1285 if (NS_SUCCEEDED(rv
)) {
1286 nsCOMPtr
<nsIFile
> handlerFile
;
1287 rv
= GetFileTokenForPath(handler
.get(), getter_AddRefs(handlerFile
));
1289 if (NS_SUCCEEDED(rv
)) {
1290 mimeInfo
->SetDefaultApplication(handlerFile
);
1291 mimeInfo
->SetPreferredAction(nsIMIMEInfo::saveToDisk
);
1292 mimeInfo
->SetDefaultDescription(handler
);
1296 if (NS_FAILED(rv
)) {
1297 mimeInfo
->SetPreferredAction(nsIMIMEInfo::saveToDisk
);
1300 return mimeInfo
.forget();
1303 already_AddRefed
<nsMIMEInfoBase
> nsOSHelperAppService::GetFromType(
1304 const nsCString
& aMIMEType
) {
1305 // if the type is empty, return immediately
1306 if (aMIMEType
.IsEmpty()) {
1310 LOG("Here we do a mimetype lookup for '%s'\n", aMIMEType
.get());
1312 // extract the major and minor types
1313 NS_ConvertASCIItoUTF16
mimeType(aMIMEType
);
1314 nsAString::const_iterator start_iter
, end_iter
, majorTypeStart
, majorTypeEnd
,
1315 minorTypeStart
, minorTypeEnd
;
1317 mimeType
.BeginReading(start_iter
);
1318 mimeType
.EndReading(end_iter
);
1320 // XXX FIXME: add typeOptions parsing in here
1321 nsresult rv
= ParseMIMEType(start_iter
, majorTypeStart
, majorTypeEnd
,
1322 minorTypeStart
, minorTypeEnd
, end_iter
);
1324 if (NS_FAILED(rv
)) {
1328 nsDependentSubstring
majorType(majorTypeStart
, majorTypeEnd
);
1329 nsDependentSubstring
minorType(minorTypeStart
, minorTypeEnd
);
1331 // First check the user's private mailcap file
1332 nsAutoString mailcap_description
, handler
, mozillaFlags
;
1333 DoLookUpHandlerAndDescription(majorType
, minorType
, handler
,
1334 mailcap_description
, mozillaFlags
, true);
1336 LOG("Private Handler/Description results: handler='%s', description='%s'\n",
1337 NS_LossyConvertUTF16toASCII(handler
).get(),
1338 NS_LossyConvertUTF16toASCII(mailcap_description
).get());
1340 // Now look up our extensions
1341 nsAutoString extensions
, mime_types_description
;
1342 LookUpExtensionsAndDescription(majorType
, minorType
, extensions
,
1343 mime_types_description
);
1345 #ifdef MOZ_WIDGET_GTK
1346 if (handler
.IsEmpty()) {
1347 RefPtr
<nsMIMEInfoBase
> gnomeInfo
= nsGNOMERegistry::GetFromType(aMIMEType
);
1349 LOG("Got MIMEInfo from GNOME registry without extensions; setting them "
1351 NS_LossyConvertUTF16toASCII(extensions
).get());
1353 NS_ASSERTION(!gnomeInfo
->HasExtensions(), "How'd that happen?");
1354 gnomeInfo
->SetFileExtensions(NS_ConvertUTF16toUTF8(extensions
));
1355 return gnomeInfo
.forget();
1360 if (handler
.IsEmpty()) {
1361 DoLookUpHandlerAndDescription(majorType
, minorType
, handler
,
1362 mailcap_description
, mozillaFlags
, false);
1365 if (handler
.IsEmpty()) {
1366 DoLookUpHandlerAndDescription(majorType
, u
"*"_ns
, handler
,
1367 mailcap_description
, mozillaFlags
, true);
1370 if (handler
.IsEmpty()) {
1371 DoLookUpHandlerAndDescription(majorType
, u
"*"_ns
, handler
,
1372 mailcap_description
, mozillaFlags
, false);
1375 LOG("Handler/Description results: handler='%s', description='%s', "
1376 "mozillaFlags='%s'\n",
1377 NS_LossyConvertUTF16toASCII(handler
).get(),
1378 NS_LossyConvertUTF16toASCII(mailcap_description
).get(),
1379 NS_LossyConvertUTF16toASCII(mozillaFlags
).get());
1381 mailcap_description
.Trim(" \t\"");
1382 mozillaFlags
.Trim(" \t");
1384 if (handler
.IsEmpty() && extensions
.IsEmpty() &&
1385 mailcap_description
.IsEmpty() && mime_types_description
.IsEmpty()) {
1386 // No real useful info
1390 RefPtr
<nsMIMEInfoUnix
> mimeInfo
= new nsMIMEInfoUnix(aMIMEType
);
1392 mimeInfo
->SetFileExtensions(NS_ConvertUTF16toUTF8(extensions
));
1393 if (!mime_types_description
.IsEmpty()) {
1394 mimeInfo
->SetDescription(mime_types_description
);
1396 mimeInfo
->SetDescription(mailcap_description
);
1399 rv
= NS_ERROR_NOT_AVAILABLE
;
1400 nsCOMPtr
<nsIFile
> handlerFile
;
1401 if (!handler
.IsEmpty()) {
1402 rv
= GetFileTokenForPath(handler
.get(), getter_AddRefs(handlerFile
));
1405 if (NS_SUCCEEDED(rv
)) {
1406 mimeInfo
->SetDefaultApplication(handlerFile
);
1407 mimeInfo
->SetPreferredAction(nsIMIMEInfo::saveToDisk
);
1408 mimeInfo
->SetDefaultDescription(handler
);
1410 mimeInfo
->SetPreferredAction(nsIMIMEInfo::saveToDisk
);
1413 return mimeInfo
.forget();
1416 nsresult
nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString
& aType
,
1417 const nsACString
& aFileExt
,
1419 nsIMIMEInfo
** aMIMEInfo
) {
1421 RefPtr
<nsMIMEInfoBase
> retval
;
1422 // Fallback to lookup by extension when generic 'application/octet-stream'
1423 // content type is received.
1424 if (!aType
.EqualsLiteral(APPLICATION_OCTET_STREAM
)) {
1425 retval
= GetFromType(PromiseFlatCString(aType
));
1427 bool hasDefault
= false;
1428 if (retval
) retval
->GetHasDefaultHandler(&hasDefault
);
1429 if (!retval
|| !hasDefault
) {
1430 RefPtr
<nsMIMEInfoBase
> miByExt
=
1431 GetFromExtension(PromiseFlatCString(aFileExt
));
1432 // If we had no extension match, but a type match, use that
1433 if (!miByExt
&& retval
) {
1434 retval
.forget(aMIMEInfo
);
1437 // If we had an extension match but no type match, set the mimetype and use
1439 if (!retval
&& miByExt
) {
1440 if (!aType
.IsEmpty()) miByExt
->SetMIMEType(aType
);
1441 miByExt
.swap(retval
);
1443 retval
.forget(aMIMEInfo
);
1446 // If we got nothing, make a new mimeinfo
1449 retval
= new nsMIMEInfoUnix(aType
);
1451 if (!aFileExt
.IsEmpty()) retval
->AppendExtension(aFileExt
);
1454 retval
.forget(aMIMEInfo
);
1458 // Copy the attributes of retval (mimeinfo from type) onto miByExt, to
1460 // but reset to just collected mDefaultAppDescription (from ext)
1461 nsAutoString byExtDefault
;
1462 miByExt
->GetDefaultDescription(byExtDefault
);
1463 retval
->SetDefaultDescription(byExtDefault
);
1464 retval
->CopyBasicDataTo(miByExt
);
1466 miByExt
.swap(retval
);
1468 retval
.forget(aMIMEInfo
);
1473 nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString
& aScheme
,
1475 nsIHandlerInfo
** _retval
) {
1476 NS_ASSERTION(!aScheme
.IsEmpty(), "No scheme was specified!");
1479 OSProtocolHandlerExists(nsPromiseFlatCString(aScheme
).get(), found
);
1480 if (NS_FAILED(rv
)) return rv
;
1482 nsMIMEInfoUnix
* handlerInfo
=
1483 new nsMIMEInfoUnix(aScheme
, nsMIMEInfoBase::eProtocolInfo
);
1484 NS_ENSURE_TRUE(handlerInfo
, NS_ERROR_OUT_OF_MEMORY
);
1485 NS_ADDREF(*_retval
= handlerInfo
);
1488 // Code that calls this requires an object regardless if the OS has
1489 // something for us, so we return the empty object.
1494 GetApplicationDescription(aScheme
, desc
);
1495 handlerInfo
->SetDefaultDescription(desc
);