Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/uriloader/exthandler/unix/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 49 kB image not shown  

Quellcode-Bibliothek nsOSHelperAppService.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include <sys/types.h>
#include <sys/stat.h>

#include "nsOSHelperAppService.h"
#include "nsMIMEInfoUnix.h"
#ifdef MOZ_WIDGET_GTK
#  include "nsGNOMERegistry.h"
#  ifdef MOZ_BUILD_APP_IS_BROWSER
#    include "nsIToolkitShellService.h"
#    include "nsIGNOMEShellService.h"
#  endif
#endif
#include "nsISupports.h"
#include "nsString.h"
#include "nsReadableUtils.h"
#include "nsUnicharUtils.h"
#include "nsIFileStreams.h"
#include "nsILineInputStream.h"
#include "nsIFile.h"
#include "nsIProcess.h"
#include "nsLocalFile.h"
#include "nsNetCID.h"
#include "nsXPCOM.h"
#include "nsComponentManagerUtils.h"
#include "nsCRT.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsXULAppAPI.h"
#include "ContentHandlerService.h"
#include "prenv.h"  // for PR_GetEnv()
#include "mozilla/EnumeratedRange.h"
#include "mozilla/Preferences.h"
#include "mozilla/ClearOnShutdown.h"
#include "nsMimeTypes.h"
#include <mutex>

using namespace mozilla;

#define LOG(...) \
  MOZ_LOG(nsOSHelperAppService::sLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
#define LOG_ENABLED() \
  MOZ_LOG_TEST(nsOSHelperAppService::sLog, mozilla::LogLevel::Debug)

static nsresult FindSemicolon(nsAString::const_iterator& aSemicolon_iter,
                              const nsAString::const_iterator& aEnd_iter);
static nsresult ParseMIMEType(const nsAString::const_iterator& aStart_iter,
                              nsAString::const_iterator& aMajorTypeStart,
                              nsAString::const_iterator& aMajorTypeEnd,
                              nsAString::const_iterator& aMinorTypeStart,
                              nsAString::const_iterator& aMinorTypeEnd,
                              const nsAString::const_iterator& aEnd_iter);

inline bool IsNetscapeFormat(const nsACString& aBuffer);

nsOSHelperAppService::~nsOSHelperAppService() {}

/*
 * Take a command with all the mailcap escapes in it and unescape it
 * Ideally this needs the mime type, mime type options, and location of the
 * temporary file, but this last can't be got from here
 */

// static
nsresult nsOSHelperAppService::UnescapeCommand(const nsAString& aEscapedCommand,
                                               const nsAString& aMajorType,
                                               const nsAString& aMinorType,
                                               nsACString& aUnEscapedCommand) {
  LOG("-- UnescapeCommand");
  LOG("Command to escape: '%s'\n",
      NS_LossyConvertUTF16toASCII(aEscapedCommand).get());
  //  XXX This function will need to get the mime type and various stuff like
  //  that being passed in to work properly

  LOG(
      ("UnescapeCommand really needs some work -- it should actually do some "
       "unescaping\n"));

  CopyUTF16toUTF8(aEscapedCommand, aUnEscapedCommand);
  LOG("Escaped command: '%s'\n", PromiseFlatCString(aUnEscapedCommand).get());
  return NS_OK;
}

/* Put aSemicolon_iter at the first non-escaped semicolon after
 * aStart_iter but before aEnd_iter
 */


static nsresult FindSemicolon(nsAString::const_iterator& aSemicolon_iter,
                              const nsAString::const_iterator& aEnd_iter) {
  bool semicolonFound = false;
  while (aSemicolon_iter != aEnd_iter && !semicolonFound) {
    switch (*aSemicolon_iter) {
      case '\\':
        aSemicolon_iter.advance(2);
        break;
      case ';':
        semicolonFound = true;
        break;
      default:
        ++aSemicolon_iter;
        break;
    }
  }
  return NS_OK;
}

static nsresult ParseMIMEType(const nsAString::const_iterator& aStart_iter,
                              nsAString::const_iterator& aMajorTypeStart,
                              nsAString::const_iterator& aMajorTypeEnd,
                              nsAString::const_iterator& aMinorTypeStart,
                              nsAString::const_iterator& aMinorTypeEnd,
                              const nsAString::const_iterator& aEnd_iter) {
  nsAString::const_iterator iter(aStart_iter);

  // skip leading whitespace
  while (iter != aEnd_iter && nsCRT::IsAsciiSpace(*iter)) {
    ++iter;
  }

  if (iter == aEnd_iter) {
    return NS_ERROR_INVALID_ARG;
  }

  aMajorTypeStart = iter;

  // find major/minor separator ('/')
  while (iter != aEnd_iter && *iter != '/') {
    ++iter;
  }

  if (iter == aEnd_iter) {
    return NS_ERROR_INVALID_ARG;
  }

  aMajorTypeEnd = iter;

  // skip '/'
  ++iter;

  if (iter == aEnd_iter) {
    return NS_ERROR_INVALID_ARG;
  }

  aMinorTypeStart = iter;

  // find end of minor type, delimited by whitespace or ';'
  while (iter != aEnd_iter && !nsCRT::IsAsciiSpace(*iter) && *iter != ';') {
    ++iter;
  }

  aMinorTypeEnd = iter;

  return NS_OK;
}

// TODO: We should consider not only caching the file location but maybe the
// file contents too?
enum class FileKind {
  PrivateMimeTypes = 0,
  GlobalMimeTypes,
  PrivateMailCap,
  GlobalMailCap,

  Count,
};

struct FileLocationCache {
  struct Entry {
    bool mIsCached = false;
    nsresult mResult = NS_OK;
    nsString mLocation;

    void Clear() { *this = {}; }
  };

  EnumeratedArray<FileKind, Entry, size_t(FileKind::Count)> mEntries;

  static const char* PrefFor(FileKind aKind) {
    switch (aKind) {
      case FileKind::GlobalMimeTypes:
        return "helpers.global_mime_types_file";
      case FileKind::PrivateMimeTypes:
        return "helpers.private_mime_types_file";
      case FileKind::GlobalMailCap:
        return "helpers.global_mailcap_file";
      case FileKind::PrivateMailCap:
        return "helpers.private_mailcap_file";
      case FileKind::Count:
        break;
    }
    MOZ_ASSERT_UNREACHABLE("Unknown file kind");
    return "";
  }

  void Clear() {
    for (auto& entry : mEntries) {
      entry.Clear();
    }
  }

  static void PrefChangeCallback(const char*, void*) { Get().Clear(); }

  static FileLocationCache& Get() {
    static FileLocationCache sCache;
    static std::once_flag flag;
    std::call_once(flag, [&] {
      for (auto kind :
           MakeEnumeratedRange(FileKind::PrivateMimeTypes, FileKind::Count)) {
        Preferences::RegisterCallback(PrefChangeCallback,
                                      nsDependentCString(PrefFor(kind)));
      }
      RunOnShutdown([] {
        sCache.Clear();
        for (auto kind :
             MakeEnumeratedRange(FileKind::PrivateMimeTypes, FileKind::Count)) {
          Preferences::UnregisterCallback(PrefChangeCallback,
                                          nsDependentCString(PrefFor(kind)));
        }
      });
    });
    return sCache;
  }

  Entry& EntryFor(FileKind aKind) { return mEntries[aKind]; }
};

// The lookup order is:
//    1) user pref
//    2) env var (if any)
//    3) pref
static nsresult DoGetFileLocation(FileKind aKind, nsAString& aFileLocation) {
  LOG("-- GetFileLocation(%d)\n"int(aKind));

  aFileLocation.Truncate();

  const char* envVar = [&]() -> const char* {
    switch (aKind) {
      case FileKind::PrivateMailCap:
        return "PERSONAL_MAILCAP";
      case FileKind::GlobalMailCap:
        return "MAILCAP";
      default:
        return nullptr;
    }
  }();

  const char* prefName = FileLocationCache::PrefFor(aKind);

  if (envVar) {
    // If we have an env var we should check whether the pref is a user pref. If
    // we do not, we don't care.
    if (Preferences::HasUserValue(prefName) &&
        NS_SUCCEEDED(Preferences::GetString(prefName, aFileLocation))) {
      return NS_OK;
    }

    char* envValue = PR_GetEnv(envVar);
    if (envValue && *envValue) {
      // the pref is in the system charset and it's a filepath... The
      // natural way to do the charset conversion is by just initing
      // an nsIFile with the native path and asking it for the Unicode
      // version.
      nsCOMPtr<nsIFile> file;
      nsresult rv = NS_NewNativeLocalFile(nsDependentCString(envValue),
                                          getter_AddRefs(file));
      NS_ENSURE_SUCCESS(rv, rv);
      return file->GetPath(aFileLocation);
    }
  }

  return Preferences::GetString(prefName, aFileLocation);
}

static nsresult GetFileLocation(FileKind aKind, nsAString& aFileLocation) {
  MOZ_ASSERT(NS_IsMainThread());
  auto& entry = FileLocationCache::Get().EntryFor(aKind);
  if (!entry.mIsCached) {
    entry.mIsCached = true;
    entry.mResult = DoGetFileLocation(aKind, entry.mLocation);
  }
  aFileLocation = entry.mLocation;
  return entry.mResult;
}

/* Get the mime.types file names from prefs and look up info in them
   based on extension */

// static
nsresult nsOSHelperAppService::LookUpTypeAndDescription(
    const nsAString& aFileExtension, nsAString& aMajorType,
    nsAString& aMinorType, nsAString& aDescription, bool aUserData) {
  LOG("-- LookUpTypeAndDescription for extension '%s'\n",
      NS_LossyConvertUTF16toASCII(aFileExtension).get());
  nsAutoString mimeFileName;

  auto kind =
      aUserData ? FileKind::PrivateMimeTypes : FileKind::GlobalMimeTypes;
  nsresult rv = GetFileLocation(kind, mimeFileName);
  if (NS_SUCCEEDED(rv) && !mimeFileName.IsEmpty()) {
    rv = GetTypeAndDescriptionFromMimetypesFile(
        mimeFileName, aFileExtension, aMajorType, aMinorType, aDescription);
  } else {
    rv = NS_ERROR_NOT_AVAILABLE;
  }

  return rv;
}

inline bool IsNetscapeFormat(const nsACString& aBuffer) {
  return StringBeginsWith(
             aBuffer,
             nsLiteralCString(
                 "#--Netscape Communications Corporation MIME Information")) ||
         StringBeginsWith(aBuffer, "#--MCOM MIME Information"_ns);
}

/*
 * Create a file stream and line input stream for the filename.
 * Leaves the first line of the file in aBuffer and sets the format to
 *  true for netscape files and false for normail ones
 */

// static
nsresult nsOSHelperAppService::CreateInputStream(
    const nsAString& aFilename, nsIFileInputStream** aFileInputStream,
    nsILineInputStream** aLineInputStream, nsACString& aBuffer,
    bool* aNetscapeFormat, bool* aMore) {
  LOG("-- CreateInputStream");
  nsresult rv = NS_OK;

  nsCOMPtr<nsIFile> file;
  MOZ_TRY(NS_NewLocalFile(aFilename, getter_AddRefs(file)));

  nsCOMPtr<nsIFileInputStream> fileStream(
      do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
  if (NS_FAILED(rv)) return rv;
  rv = fileStream->Init(file, -1, -1, false);
  if (NS_FAILED(rv)) return rv;

  nsCOMPtr<nsILineInputStream> lineStream(do_QueryInterface(fileStream, &rv));

  if (NS_FAILED(rv)) {
    LOG("Interface trouble in stream land!");
    return rv;
  }

  rv = lineStream->ReadLine(aBuffer, aMore);
  if (NS_FAILED(rv)) {
    fileStream->Close();
    return rv;
  }

  *aNetscapeFormat = IsNetscapeFormat(aBuffer);

  *aFileInputStream = fileStream;
  NS_ADDREF(*aFileInputStream);
  *aLineInputStream = lineStream;
  NS_ADDREF(*aLineInputStream);

  return NS_OK;
}

/* Open the file, read the first line, decide what type of file it is,
   then get info based on extension */

// static
nsresult nsOSHelperAppService::GetTypeAndDescriptionFromMimetypesFile(
    const nsAString& aFilename, const nsAString& aFileExtension,
    nsAString& aMajorType, nsAString& aMinorType, nsAString& aDescription) {
  LOG("-- GetTypeAndDescriptionFromMimetypesFile\n");
  LOG("Getting type and description from types file '%s'\n",
      NS_LossyConvertUTF16toASCII(aFilename).get());
  LOG("Using extension '%s'\n",
      NS_LossyConvertUTF16toASCII(aFileExtension).get());
  nsCOMPtr<nsIFileInputStream> mimeFile;
  nsCOMPtr<nsILineInputStream> mimeTypes;
  bool netscapeFormat;
  nsAutoString buf;
  nsAutoCString cBuf;
  bool more = false;
  nsresult rv = CreateInputStream(aFilename, getter_AddRefs(mimeFile),
                                  getter_AddRefs(mimeTypes), cBuf,
                                  &netscapeFormat, &more);

  if (NS_FAILED(rv)) {
    return rv;
  }

  nsAutoString extensions;
  nsAutoStringN<101> entry;
  nsAString::const_iterator majorTypeStart, majorTypeEnd, minorTypeStart,
      minorTypeEnd, descriptionStart, descriptionEnd;

  do {
    CopyASCIItoUTF16(cBuf, buf);
    // read through, building up an entry.  If we finish an entry, check for
    // a match and return out of the loop if we match

    // skip comments and empty lines
    if (!buf.IsEmpty() && buf.First() != '#') {
      entry.Append(buf);
      if (entry.Last() == '\\') {
        entry.Truncate(entry.Length() - 1);
        entry.Append(char16_t(
            ' '));  // in case there is no trailing whitespace on this line
      } else {      // we have a full entry
        LOG("Current entry: '%s'\n", NS_LossyConvertUTF16toASCII(entry).get());
        if (netscapeFormat) {
          rv = ParseNetscapeMIMETypesEntry(
              entry, majorTypeStart, majorTypeEnd, minorTypeStart, minorTypeEnd,
              extensions, descriptionStart, descriptionEnd);
          if (NS_FAILED(rv)) {
            // We sometimes get things like RealPlayer appending
            // "normal" entries to "Netscape" .mime.types files.  Try
            // to handle that.  Bug 106381.
            LOG("Bogus entry; trying 'normal' mode\n");
            rv = ParseNormalMIMETypesEntry(
                entry, majorTypeStart, majorTypeEnd, minorTypeStart,
                minorTypeEnd, extensions, descriptionStart, descriptionEnd);
          }
        } else {
          rv = ParseNormalMIMETypesEntry(
              entry, majorTypeStart, majorTypeEnd, minorTypeStart, minorTypeEnd,
              extensions, descriptionStart, descriptionEnd);
          if (NS_FAILED(rv)) {
            // We sometimes get things like StarOffice prepending
            // "normal" entries to "Netscape" .mime.types files.  Try
            // to handle that.  Bug 136670.
            LOG("Bogus entry; trying 'Netscape' mode\n");
            rv = ParseNetscapeMIMETypesEntry(
                entry, majorTypeStart, majorTypeEnd, minorTypeStart,
                minorTypeEnd, extensions, descriptionStart, descriptionEnd);
          }
        }

        if (NS_SUCCEEDED(rv)) {  // entry parses
          nsAString::const_iterator start, end;
          extensions.BeginReading(start);
          extensions.EndReading(end);
          nsAString::const_iterator iter(start);

          while (start != end) {
            FindCharInReadable(',', iter, end);
            if (Substring(start, iter)
                    .Equals(aFileExtension,
                            nsCaseInsensitiveStringComparator)) {
              // it's a match.  Assign the type and description and run
              aMajorType.Assign(Substring(majorTypeStart, majorTypeEnd));
              aMinorType.Assign(Substring(minorTypeStart, minorTypeEnd));
              aDescription.Assign(Substring(descriptionStart, descriptionEnd));
              mimeFile->Close();
              return NS_OK;
            }
            if (iter != end) {
              ++iter;
            }
            start = iter;
          }
        } else {
          LOG("Failed to parse entry: %s\n",
              NS_LossyConvertUTF16toASCII(entry).get());
        }
        // truncate the entry for the next iteration
        entry.Truncate();
      }
    }
    if (!more) {
      rv = NS_ERROR_NOT_AVAILABLE;
      break;
    }
    // read the next line
    rv = mimeTypes->ReadLine(cBuf, &more);
  } while (NS_SUCCEEDED(rv));

  mimeFile->Close();
  return rv;
}

/* Get the mime.types file names from prefs and look up info in them
   based on mimetype  */

// static
nsresult nsOSHelperAppService::LookUpExtensionsAndDescription(
    const nsAString& aMajorType, const nsAString& aMinorType,
    nsAString& aFileExtensions, nsAString& aDescription) {
  LOG("-- LookUpExtensionsAndDescription for type '%s/%s'\n",
      NS_LossyConvertUTF16toASCII(aMajorType).get(),
      NS_LossyConvertUTF16toASCII(aMinorType).get());
  nsAutoString mimeFileName;

  nsresult rv = GetFileLocation(FileKind::PrivateMimeTypes, mimeFileName);
  if (NS_SUCCEEDED(rv) && !mimeFileName.IsEmpty()) {
    rv = GetExtensionsAndDescriptionFromMimetypesFile(
        mimeFileName, aMajorType, aMinorType, aFileExtensions, aDescription);
  } else {
    rv = NS_ERROR_NOT_AVAILABLE;
  }
  if (NS_FAILED(rv) || aFileExtensions.IsEmpty()) {
    rv = GetFileLocation(FileKind::GlobalMimeTypes, mimeFileName);
    if (NS_SUCCEEDED(rv) && !mimeFileName.IsEmpty()) {
      rv = GetExtensionsAndDescriptionFromMimetypesFile(
          mimeFileName, aMajorType, aMinorType, aFileExtensions, aDescription);
    } else {
      rv = NS_ERROR_NOT_AVAILABLE;
    }
  }
  return rv;
}

/* Open the file, read the first line, decide what type of file it is,
   then get info based on extension */

// static
nsresult nsOSHelperAppService::GetExtensionsAndDescriptionFromMimetypesFile(
    const nsAString& aFilename, const nsAString& aMajorType,
    const nsAString& aMinorType, nsAString& aFileExtensions,
    nsAString& aDescription) {
  LOG("-- GetExtensionsAndDescriptionFromMimetypesFile\n");
  LOG("Getting extensions and description from types file '%s'\n",
      NS_LossyConvertUTF16toASCII(aFilename).get());
  LOG("Using type '%s/%s'\n", NS_LossyConvertUTF16toASCII(aMajorType).get(),
      NS_LossyConvertUTF16toASCII(aMinorType).get());
  nsCOMPtr<nsIFileInputStream> mimeFile;
  nsCOMPtr<nsILineInputStream> mimeTypes;
  bool netscapeFormat;
  nsAutoCString cBuf;
  nsAutoString buf;
  bool more = false;
  nsresult rv = CreateInputStream(aFilename, getter_AddRefs(mimeFile),
                                  getter_AddRefs(mimeTypes), cBuf,
                                  &netscapeFormat, &more);
  if (NS_FAILED(rv)) {
    return rv;
  }

  nsAutoString extensions;
  nsAutoStringN<101> entry;
  nsAString::const_iterator majorTypeStart, majorTypeEnd, minorTypeStart,
      minorTypeEnd, descriptionStart, descriptionEnd;

  do {
    CopyASCIItoUTF16(cBuf, buf);
    // read through, building up an entry.  If we finish an entry, check for
    // a match and return out of the loop if we match

    // skip comments and empty lines
    if (!buf.IsEmpty() && buf.First() != '#') {
      entry.Append(buf);
      if (entry.Last() == '\\') {
        entry.Truncate(entry.Length() - 1);
        entry.Append(char16_t(
            ' '));  // in case there is no trailing whitespace on this line
      } else {      // we have a full entry
        LOG("Current entry: '%s'\n", NS_LossyConvertUTF16toASCII(entry).get());
        if (netscapeFormat) {
          rv = ParseNetscapeMIMETypesEntry(
              entry, majorTypeStart, majorTypeEnd, minorTypeStart, minorTypeEnd,
              extensions, descriptionStart, descriptionEnd);

          if (NS_FAILED(rv)) {
            // We sometimes get things like RealPlayer appending
            // "normal" entries to "Netscape" .mime.types files.  Try
            // to handle that.  Bug 106381.
            LOG("Bogus entry; trying 'normal' mode\n");
            rv = ParseNormalMIMETypesEntry(
                entry, majorTypeStart, majorTypeEnd, minorTypeStart,
                minorTypeEnd, extensions, descriptionStart, descriptionEnd);
          }
        } else {
          rv = ParseNormalMIMETypesEntry(
              entry, majorTypeStart, majorTypeEnd, minorTypeStart, minorTypeEnd,
              extensions, descriptionStart, descriptionEnd);

          if (NS_FAILED(rv)) {
            // We sometimes get things like StarOffice prepending
            // "normal" entries to "Netscape" .mime.types files.  Try
            // to handle that.  Bug 136670.
            LOG("Bogus entry; trying 'Netscape' mode\n");
            rv = ParseNetscapeMIMETypesEntry(
                entry, majorTypeStart, majorTypeEnd, minorTypeStart,
                minorTypeEnd, extensions, descriptionStart, descriptionEnd);
          }
        }

        if (NS_SUCCEEDED(rv) &&
            Substring(majorTypeStart, majorTypeEnd)
                .Equals(aMajorType, nsCaseInsensitiveStringComparator) &&
            Substring(minorTypeStart, minorTypeEnd)
                .Equals(aMinorType, nsCaseInsensitiveStringComparator)) {
          // it's a match
          aFileExtensions.Assign(extensions);
          aDescription.Assign(Substring(descriptionStart, descriptionEnd));
          mimeFile->Close();
          return NS_OK;
        }
        if (NS_FAILED(rv)) {
          LOG("Failed to parse entry: %s\n",
              NS_LossyConvertUTF16toASCII(entry).get());
        }

        entry.Truncate();
      }
    }
    if (!more) {
      rv = NS_ERROR_NOT_AVAILABLE;
      break;
    }
    // read the next line
    rv = mimeTypes->ReadLine(cBuf, &more);
  } while (NS_SUCCEEDED(rv));

  mimeFile->Close();
  return rv;
}

/*
 * This parses a Netscape format mime.types entry.  There are two
 * possible formats:
 *
 * type=foo/bar; options exts="baz" description="Some type"
 *
 * and
 *
 * type=foo/bar; options description="Some type" exts="baz"
 */

// static
nsresult nsOSHelperAppService::ParseNetscapeMIMETypesEntry(
    const nsAString& aEntry, nsAString::const_iterator& aMajorTypeStart,
    nsAString::const_iterator& aMajorTypeEnd,
    nsAString::const_iterator& aMinorTypeStart,
    nsAString::const_iterator& aMinorTypeEnd, nsAString& aExtensions,
    nsAString::const_iterator& aDescriptionStart,
    nsAString::const_iterator& aDescriptionEnd) {
  LOG("-- ParseNetscapeMIMETypesEntry\n");
  NS_ASSERTION(!aEntry.IsEmpty(),
               "Empty Netscape MIME types entry being parsed.");

  nsAString::const_iterator start_iter, end_iter, match_start, match_end;

  aEntry.BeginReading(start_iter);
  aEntry.EndReading(end_iter);

  // skip trailing whitespace
  do {
    --end_iter;
  } while (end_iter != start_iter && nsCRT::IsAsciiSpace(*end_iter));
  // if we're pointing to a quote, don't advance -- we don't want to
  // include the quote....
  if (*end_iter != '"') ++end_iter;
  match_start = start_iter;
  match_end = end_iter;

  // Get the major and minor types
  // First the major type
  if (!FindInReadable(u"type="_ns, match_start, match_end)) {
    return NS_ERROR_FAILURE;
  }

  match_start = match_end;

  while (match_end != end_iter && *match_end != '/') {
    ++match_end;
  }
  if (match_end == end_iter) {
    return NS_ERROR_FAILURE;
  }

  aMajorTypeStart = match_start;
  aMajorTypeEnd = match_end;

  // now the minor type
  if (++match_end == end_iter) {
    return NS_ERROR_FAILURE;
  }

  match_start = match_end;

  while (match_end != end_iter && !nsCRT::IsAsciiSpace(*match_end) &&
         *match_end != ';') {
    ++match_end;
  }
  if (match_end == end_iter) {
    return NS_ERROR_FAILURE;
  }

  aMinorTypeStart = match_start;
  aMinorTypeEnd = match_end;

  // ignore everything up to the end of the mime type from here on
  start_iter = match_end;

  // get the extensions
  match_start = match_end;
  match_end = end_iter;
  if (FindInReadable(u"exts="_ns, match_start, match_end)) {
    nsAString::const_iterator extStart, extEnd;

    if (match_end == end_iter ||
        (*match_end == '"' && ++match_end == end_iter)) {
      return NS_ERROR_FAILURE;
    }

    extStart = match_end;
    match_start = extStart;
    match_end = end_iter;
    if (FindInReadable(u"desc=\""_ns, match_start, match_end)) {
      // exts= before desc=, so we have to find the actual end of the extensions
      extEnd = match_start;
      if (extEnd == extStart) {
        return NS_ERROR_FAILURE;
      }

      do {
        --extEnd;
      } while (extEnd != extStart && nsCRT::IsAsciiSpace(*extEnd));

      if (extEnd != extStart && *extEnd == '"') {
        --extEnd;
      }
    } else {
      // desc= before exts=, so we can use end_iter as the end of the extensions
      extEnd = end_iter;
    }
    aExtensions = Substring(extStart, extEnd);
  } else {
    // no extensions
    aExtensions.Truncate();
  }

  // get the description
  match_start = start_iter;
  match_end = end_iter;
  if (FindInReadable(u"desc=\""_ns, match_start, match_end)) {
    aDescriptionStart = match_end;
    match_start = aDescriptionStart;
    match_end = end_iter;
    if (FindInReadable(u"exts="_ns, match_start, match_end)) {
      // exts= after desc=, so have to find actual end of description
      aDescriptionEnd = match_start;
      if (aDescriptionEnd == aDescriptionStart) {
        return NS_ERROR_FAILURE;
      }

      do {
        --aDescriptionEnd;
      } while (aDescriptionEnd != aDescriptionStart &&
               nsCRT::IsAsciiSpace(*aDescriptionEnd));
    } else {
      // desc= after exts=, so use end_iter for the description end
      aDescriptionEnd = end_iter;
    }
  } else {
    // no description
    aDescriptionStart = start_iter;
    aDescriptionEnd = start_iter;
  }

  return NS_OK;
}

/*
 * This parses a normal format mime.types entry.  The format is:
 *
 * major/minor    ext1 ext2 ext3
 */

// static
nsresult nsOSHelperAppService::ParseNormalMIMETypesEntry(
    const nsAString& aEntry, nsAString::const_iterator& aMajorTypeStart,
    nsAString::const_iterator& aMajorTypeEnd,
    nsAString::const_iterator& aMinorTypeStart,
    nsAString::const_iterator& aMinorTypeEnd, nsAString& aExtensions,
    nsAString::const_iterator& aDescriptionStart,
    nsAString::const_iterator& aDescriptionEnd) {
  LOG("-- ParseNormalMIMETypesEntry\n");
  NS_ASSERTION(!aEntry.IsEmpty(),
               "Empty Normal MIME types entry being parsed.");

  nsAString::const_iterator start_iter, end_iter, iter;

  aEntry.BeginReading(start_iter);
  aEntry.EndReading(end_iter);

  // no description
  aDescriptionStart = start_iter;
  aDescriptionEnd = start_iter;

  // skip leading whitespace
  while (start_iter != end_iter && nsCRT::IsAsciiSpace(*start_iter)) {
    ++start_iter;
  }
  if (start_iter == end_iter) {
    return NS_ERROR_FAILURE;
  }
  // skip trailing whitespace
  do {
    --end_iter;
  } while (end_iter != start_iter && nsCRT::IsAsciiSpace(*end_iter));

  ++end_iter;  // point to first whitespace char (or to end of string)
  iter = start_iter;

  // get the major type
  if (!FindCharInReadable('/', iter, end_iter)) return NS_ERROR_FAILURE;

  nsAString::const_iterator equals_sign_iter(start_iter);
  if (FindCharInReadable('=', equals_sign_iter, iter))
    return NS_ERROR_FAILURE;  // see bug 136670

  aMajorTypeStart = start_iter;
  aMajorTypeEnd = iter;

  // get the minor type
  if (++iter == end_iter) {
    return NS_ERROR_FAILURE;
  }
  start_iter = iter;

  while (iter != end_iter && !nsCRT::IsAsciiSpace(*iter)) {
    ++iter;
  }
  aMinorTypeStart = start_iter;
  aMinorTypeEnd = iter;

  // get the extensions
  aExtensions.Truncate();
  while (iter != end_iter) {
    while (iter != end_iter && nsCRT::IsAsciiSpace(*iter)) {
      ++iter;
    }

    start_iter = iter;
    while (iter != end_iter && !nsCRT::IsAsciiSpace(*iter)) {
      ++iter;
    }
    aExtensions.Append(Substring(start_iter, iter));
    if (iter != end_iter) {  // not the last extension
      aExtensions.Append(char16_t(','));
    }
  }

  return NS_OK;
}

// static
nsresult nsOSHelperAppService::LookUpHandlerAndDescription(
    const nsAString& aMajorType, const nsAString& aMinorType,
    nsAString& aHandler, nsAString& aDescription, nsAString& aMozillaFlags) {
  // The mailcap lookup is two-pass to handle the case of mailcap files
  // that have something like:
  //
  // text/*; emacs %s
  // text/rtf; soffice %s
  //
  // in that order.  We want to pick up "soffice" for text/rtf in such cases
  nsresult rv = DoLookUpHandlerAndDescription(
      aMajorType, aMinorType, aHandler, aDescription, aMozillaFlags, true);
  if (NS_FAILED(rv)) {
    rv = DoLookUpHandlerAndDescription(aMajorType, aMinorType, aHandler,
                                       aDescription, aMozillaFlags, false);
  }

  // maybe we have an entry for "aMajorType/*"?
  if (NS_FAILED(rv)) {
    rv = DoLookUpHandlerAndDescription(aMajorType, u"*"_ns, aHandler,
                                       aDescription, aMozillaFlags, true);
  }

  if (NS_FAILED(rv)) {
    rv = DoLookUpHandlerAndDescription(aMajorType, u"*"_ns, aHandler,
                                       aDescription, aMozillaFlags, false);
  }

  return rv;
}

// static
nsresult nsOSHelperAppService::DoLookUpHandlerAndDescription(
    const nsAString& aMajorType, const nsAString& aMinorType,
    nsAString& aHandler, nsAString& aDescription, nsAString& aMozillaFlags,
    bool aUserData) {
  LOG("-- LookUpHandlerAndDescription for type '%s/%s'\n",
      NS_LossyConvertUTF16toASCII(aMajorType).get(),
      NS_LossyConvertUTF16toASCII(aMinorType).get());
  nsAutoString mailcapFileName;

  const auto kind =
      aUserData ? FileKind::PrivateMailCap : FileKind::GlobalMailCap;
  nsresult rv = GetFileLocation(kind, mailcapFileName);
  if (NS_FAILED(rv) || mailcapFileName.IsEmpty()) {
    return NS_ERROR_NOT_AVAILABLE;
  }
  return GetHandlerAndDescriptionFromMailcapFile(mailcapFileName, aMajorType,
                                                 aMinorType, aHandler,
                                                 aDescription, aMozillaFlags);
}

// static
nsresult nsOSHelperAppService::GetHandlerAndDescriptionFromMailcapFile(
    const nsAString& aFilename, const nsAString& aMajorType,
    const nsAString& aMinorType, nsAString& aHandler, nsAString& aDescription,
    nsAString& aMozillaFlags) {
  LOG("-- GetHandlerAndDescriptionFromMailcapFile\n");
  LOG("Getting handler and description from mailcap file '%s'\n",
      NS_LossyConvertUTF16toASCII(aFilename).get());
  LOG("Using type '%s/%s'\n", NS_LossyConvertUTF16toASCII(aMajorType).get(),
      NS_LossyConvertUTF16toASCII(aMinorType).get());

  nsresult rv = NS_OK;
  bool more = false;

  nsCOMPtr<nsIFile> file;
  MOZ_TRY(NS_NewLocalFile(aFilename, getter_AddRefs(file)));

  nsCOMPtr<nsIFileInputStream> mailcapFile(
      do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
  if (NS_FAILED(rv)) return rv;
  rv = mailcapFile->Init(file, -1, -1, false);
  if (NS_FAILED(rv)) return rv;

  nsCOMPtr<nsILineInputStream> mailcap(do_QueryInterface(mailcapFile, &rv));

  if (NS_FAILED(rv)) {
    LOG("Interface trouble in stream land!");
    return rv;
  }

  nsAutoStringN<129> entry;
  nsAutoStringN<81> buffer;
  nsAutoCStringN<81> cBuffer;
  rv = mailcap->ReadLine(cBuffer, &more);
  if (NS_FAILED(rv)) {
    mailcapFile->Close();
    return rv;
  }

  do {  // return on end-of-file in the loop

    CopyASCIItoUTF16(cBuffer, buffer);
    if (!buffer.IsEmpty() && buffer.First() != '#') {
      entry.Append(buffer);
      if (entry.Last() == '\\') {  // entry continues on next line
        entry.Truncate(entry.Length() - 1);
        entry.Append(char16_t(
            ' '));  // in case there is no trailing whitespace on this line
      } else {      // we have a full entry in entry.  Check it for the type
        LOG("Current entry: '%s'\n", NS_LossyConvertUTF16toASCII(entry).get());

        nsAString::const_iterator semicolon_iter, start_iter, end_iter,
            majorTypeStart, majorTypeEnd, minorTypeStart, minorTypeEnd;
        entry.BeginReading(start_iter);
        entry.EndReading(end_iter);
        semicolon_iter = start_iter;
        FindSemicolon(semicolon_iter, end_iter);
        if (semicolon_iter !=
            end_iter) {  // we have something resembling a valid entry
          rv = ParseMIMEType(start_iter, majorTypeStart, majorTypeEnd,
                             minorTypeStart, minorTypeEnd, semicolon_iter);
          if (NS_SUCCEEDED(rv) &&
              Substring(majorTypeStart, majorTypeEnd)
                  .Equals(aMajorType, nsCaseInsensitiveStringComparator) &&
              Substring(minorTypeStart, minorTypeEnd)
                  .Equals(aMinorType, nsCaseInsensitiveStringComparator)) {
            // we have a match
            bool match = true;
            ++semicolon_iter;  // point at the first char past the semicolon
            start_iter = semicolon_iter;  // handler string starts here
            FindSemicolon(semicolon_iter, end_iter);
            while (start_iter != semicolon_iter &&
                   nsCRT::IsAsciiSpace(*start_iter)) {
              ++start_iter;
            }

            LOG("The real handler is: '%s'\n",
                NS_LossyConvertUTF16toASCII(
                    Substring(start_iter, semicolon_iter))
                    .get());

            // XXX ugly hack.  Just grab the executable name
            nsAString::const_iterator end_handler_iter = semicolon_iter;
            nsAString::const_iterator end_executable_iter = start_iter;
            while (end_executable_iter != end_handler_iter &&
                   !nsCRT::IsAsciiSpace(*end_executable_iter)) {
              ++end_executable_iter;
            }
            // XXX End ugly hack

            aHandler = Substring(start_iter, end_executable_iter);

            nsAString::const_iterator start_option_iter, end_optionname_iter,
                equal_sign_iter;
            bool equalSignFound;
            while (match && semicolon_iter != end_iter &&
                   ++semicolon_iter !=
                       end_iter) {  // there are options left and we still match
              start_option_iter = semicolon_iter;
              // skip over leading whitespace
              while (start_option_iter != end_iter &&
                     nsCRT::IsAsciiSpace(*start_option_iter)) {
                ++start_option_iter;
              }
              if (start_option_iter == end_iter) {  // nothing actually here
                break;
              }
              semicolon_iter = start_option_iter;
              FindSemicolon(semicolon_iter, end_iter);
              equal_sign_iter = start_option_iter;
              equalSignFound = false;
              while (equal_sign_iter != semicolon_iter && !equalSignFound) {
                switch (*equal_sign_iter) {
                  case '\\':
                    equal_sign_iter.advance(2);
                    break;
                  case '=':
                    equalSignFound = true;
                    break;
                  default:
                    ++equal_sign_iter;
                    break;
                }
              }
              end_optionname_iter = start_option_iter;
              // find end of option name
              while (end_optionname_iter != equal_sign_iter &&
                     !nsCRT::IsAsciiSpace(*end_optionname_iter)) {
                ++end_optionname_iter;
              }
              nsDependentSubstring optionName(start_option_iter,
                                              end_optionname_iter);
              if (equalSignFound) {
                // This is an option that has a name and value
                if (optionName.EqualsLiteral("description")) {
                  aDescription = Substring(++equal_sign_iter, semicolon_iter);
                } else if (optionName.EqualsLiteral("x-mozilla-flags")) {
                  aMozillaFlags = Substring(++equal_sign_iter, semicolon_iter);
                } else if (optionName.EqualsLiteral("test")) {
                  nsAutoCString testCommand;
                  rv = UnescapeCommand(
                      Substring(++equal_sign_iter, semicolon_iter), aMajorType,
                      aMinorType, testCommand);
                  if (NS_FAILED(rv)) continue;
                  nsCOMPtr<nsIProcess> process =
                      do_CreateInstance(NS_PROCESS_CONTRACTID, &rv);
                  if (NS_FAILED(rv)) continue;
                  nsCOMPtr<nsIFile> file;
                  rv =
                      NS_NewNativeLocalFile("/bin/sh"_ns, getter_AddRefs(file));
                  if (NS_FAILED(rv)) continue;
                  rv = process->Init(file);
                  if (NS_FAILED(rv)) continue;
                  const char* args[] = {"-c", testCommand.get()};
                  LOG("Running Test: %s\n", testCommand.get());
                  rv = process->Run(true, args, 2);
                  if (NS_FAILED(rv)) continue;
                  int32_t exitValue;
                  rv = process->GetExitValue(&exitValue);
                  if (NS_FAILED(rv)) continue;
                  LOG("Exit code: %d\n", exitValue);
                  if (exitValue) {
                    match = false;
                  }
                }
              } else {
                // This is an option that just has a name but no value (eg
                // "copiousoutput")
                if (optionName.EqualsLiteral("needsterminal")) {
                  match = false;
                }
              }
            }

            if (match) {  // we did not fail any test clauses; all is good
              // get out of here
              mailcapFile->Close();
              return NS_OK;
            }
            // pretend that this match never happened
            aDescription.Truncate();
            aMozillaFlags.Truncate();
            aHandler.Truncate();
          }
        }
        // zero out the entry for the next cycle
        entry.Truncate();
      }
    }
    if (!more) {
      rv = NS_ERROR_NOT_AVAILABLE;
      break;
    }
    rv = mailcap->ReadLine(cBuffer, &more);
  } while (NS_SUCCEEDED(rv));
  mailcapFile->Close();
  return rv;
}

nsresult nsOSHelperAppService::OSProtocolHandlerExists(
    const char* aProtocolScheme, bool* aHandlerExists) {
  nsresult rv = NS_OK;

  if (!XRE_IsContentProcess()) {
#ifdef MOZ_WIDGET_GTK
    // Check the GNOME registry for a protocol handler
    *aHandlerExists = nsGNOMERegistry::HandlerExists(aProtocolScheme);
#else
    *aHandlerExists = false;
#endif
  } else {
    *aHandlerExists = false;
    nsCOMPtr<nsIHandlerService> handlerSvc =
        do_GetService(NS_HANDLERSERVICE_CONTRACTID, &rv);
    if (NS_SUCCEEDED(rv) && handlerSvc) {
      rv = handlerSvc->ExistsForProtocolOS(nsCString(aProtocolScheme),
                                           aHandlerExists);
    }
  }

  return rv;
}

NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(
    const nsACString& aScheme, nsAString& _retval) {
#ifdef MOZ_WIDGET_GTK
  nsGNOMERegistry::GetAppDescForScheme(aScheme, _retval);
  return _retval.IsEmpty() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
#else
  return NS_ERROR_NOT_AVAILABLE;
#endif
}

NS_IMETHODIMP nsOSHelperAppService::IsCurrentAppOSDefaultForProtocol(
    const nsACString& aScheme, bool* _retval) {
  *_retval = false;
#if defined(MOZ_BUILD_APP_IS_BROWSER) && defined(MOZ_WIDGET_GTK)
  if (nsCOMPtr<nsIGNOMEShellService> shell =
          do_GetService(NS_TOOLKITSHELLSERVICE_CONTRACTID)) {
    return shell->IsDefaultForScheme(aScheme, _retval);
  }
#endif
  return NS_OK;
}

nsresult nsOSHelperAppService::GetFileTokenForPath(
    const char16_t* platformAppPath, nsIFile** aFile) {
  LOG("-- nsOSHelperAppService::GetFileTokenForPath: '%s'\n",
      NS_LossyConvertUTF16toASCII(platformAppPath).get());
  if (!*platformAppPath) {  // empty filename--return error
    NS_WARNING("Empty filename passed in.");
    return NS_ERROR_INVALID_ARG;
  }

  // first check if the base class implementation finds anything
  nsresult rv =
      nsExternalHelperAppService::GetFileTokenForPath(platformAppPath, aFile);
  if (NS_SUCCEEDED(rv)) return rv;
  // If the reason for failure was that the file doesn't exist, return too
  // (because it means the path was absolute, and so that we shouldn't search in
  // the path)
  if (rv == NS_ERROR_FILE_NOT_FOUND) return rv;

  // If we get here, we really should have a relative path.
  NS_ASSERTION(*platformAppPath != char16_t('/'), "Unexpected absolute path");

  bool exists = false;
  // ugly hack.  Walk the PATH variable...
  char* unixpath = PR_GetEnv("PATH");
  nsAutoCString path(unixpath);

  const char* start_iter = path.BeginReading(start_iter);
  const char* colon_iter = start_iter;
  const char* end_iter = path.EndReading(end_iter);

  nsCOMPtr<nsIFile> localFile = new nsLocalFile();
  while (start_iter != end_iter && !exists) {
    while (colon_iter != end_iter && *colon_iter != ':') {
      ++colon_iter;
    }
    rv = localFile->InitWithNativePath(Substring(start_iter, colon_iter));
    NS_ENSURE_SUCCESS(rv, rv);
    rv = localFile->AppendRelativePath(nsDependentString(platformAppPath));
    // Failing AppendRelativePath is a bad thing - it should basically always
    // succeed given a relative path. Show a warning if it does fail.
    // To prevent infinite loops when it does fail, return at this point.
    NS_ENSURE_SUCCESS(rv, rv);
    localFile->Exists(&exists);
    if (!exists) {
      if (colon_iter == end_iter) {
        break;
      }
      ++colon_iter;
      start_iter = colon_iter;
    }
  }

  if (exists) {
    rv = NS_OK;
  } else {
    rv = NS_ERROR_NOT_AVAILABLE;
  }

  *aFile = localFile;
  NS_IF_ADDREF(*aFile);

  return rv;
}

already_AddRefed<nsMIMEInfoBase> nsOSHelperAppService::GetFromExtension(
    const nsCString& aFileExt) {
  // if the extension is empty, return immediately
  if (aFileExt.IsEmpty()) {
    return nullptr;
  }

  LOG("Here we do an extension lookup for '%s'\n", aFileExt.get());

  nsAutoString majorType, minorType, mime_types_description,
      mailcap_description, handler, mozillaFlags;

  nsresult rv =
      LookUpTypeAndDescription(NS_ConvertUTF8toUTF16(aFileExt), majorType,
                               minorType, mime_types_description, true);

  if (NS_FAILED(rv) || majorType.IsEmpty()) {
#ifdef MOZ_WIDGET_GTK
    LOG("Looking in GNOME registry\n");
    RefPtr<nsMIMEInfoBase> gnomeInfo =
        nsGNOMERegistry::GetFromExtension(aFileExt);
    if (gnomeInfo) {
      LOG("Got MIMEInfo from GNOME registry\n");
      return gnomeInfo.forget();
    }
#endif

    rv = LookUpTypeAndDescription(NS_ConvertUTF8toUTF16(aFileExt), majorType,
                                  minorType, mime_types_description, false);
  }

  if (NS_FAILED(rv)) {
    return nullptr;
  }

  NS_LossyConvertUTF16toASCII asciiMajorType(majorType);
  NS_LossyConvertUTF16toASCII asciiMinorType(minorType);

  LOG("Type/Description results: majorType='%s', minorType='%s', "
      "description='%s'\n",
      asciiMajorType.get(), asciiMinorType.get(),
      NS_LossyConvertUTF16toASCII(mime_types_description).get());

  if (majorType.IsEmpty() && minorType.IsEmpty()) {
    // we didn't get a type mapping, so we can't do anything useful
    return nullptr;
  }

  nsAutoCString mimeType(asciiMajorType + "/"_ns + asciiMinorType);
  RefPtr<nsMIMEInfoUnix> mimeInfo = new nsMIMEInfoUnix(mimeType);

  mimeInfo->AppendExtension(aFileExt);
  rv = LookUpHandlerAndDescription(majorType, minorType, handler,
                                   mailcap_description, mozillaFlags);
  LOG("Handler/Description results: handler='%s', description='%s', "
      "mozillaFlags='%s'\n",
      NS_LossyConvertUTF16toASCII(handler).get(),
      NS_LossyConvertUTF16toASCII(mailcap_description).get(),
      NS_LossyConvertUTF16toASCII(mozillaFlags).get());
  mailcap_description.Trim(" \t\"");
  mozillaFlags.Trim(" \t");
  if (!mime_types_description.IsEmpty()) {
    mimeInfo->SetDescription(mime_types_description);
  } else {
    mimeInfo->SetDescription(mailcap_description);
  }

  if (NS_SUCCEEDED(rv) && handler.IsEmpty()) {
    rv = NS_ERROR_NOT_AVAILABLE;
  }

  if (NS_SUCCEEDED(rv)) {
    nsCOMPtr<nsIFile> handlerFile;
    rv = GetFileTokenForPath(handler.get(), getter_AddRefs(handlerFile));

    if (NS_SUCCEEDED(rv)) {
      mimeInfo->SetDefaultApplication(handlerFile);
      mimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
      mimeInfo->SetDefaultDescription(handler);
    }
  }

  if (NS_FAILED(rv)) {
    mimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
  }

  return mimeInfo.forget();
}

already_AddRefed<nsMIMEInfoBase> nsOSHelperAppService::GetFromType(
    const nsCString& aMIMEType) {
  // if the type is empty, return immediately
  if (aMIMEType.IsEmpty()) {
    return nullptr;
  }

  LOG("Here we do a mimetype lookup for '%s'\n", aMIMEType.get());

  // extract the major and minor types
  NS_ConvertASCIItoUTF16 mimeType(aMIMEType);
  nsAString::const_iterator start_iter, end_iter, majorTypeStart, majorTypeEnd,
      minorTypeStart, minorTypeEnd;

  mimeType.BeginReading(start_iter);
  mimeType.EndReading(end_iter);

  // XXX FIXME: add typeOptions parsing in here
  nsresult rv = ParseMIMEType(start_iter, majorTypeStart, majorTypeEnd,
                              minorTypeStart, minorTypeEnd, end_iter);

  if (NS_FAILED(rv)) {
    return nullptr;
  }

  nsDependentSubstring majorType(majorTypeStart, majorTypeEnd);
  nsDependentSubstring minorType(minorTypeStart, minorTypeEnd);

  // First check the user's private mailcap file
  nsAutoString mailcap_description, handler, mozillaFlags;
  DoLookUpHandlerAndDescription(majorType, minorType, handler,
                                mailcap_description, mozillaFlags, true);

  LOG("Private Handler/Description results: handler='%s', description='%s'\n",
      NS_LossyConvertUTF16toASCII(handler).get(),
      NS_LossyConvertUTF16toASCII(mailcap_description).get());

  // Now look up our extensions
  nsAutoString extensions, mime_types_description;
  LookUpExtensionsAndDescription(majorType, minorType, extensions,
                                 mime_types_description);

#ifdef MOZ_WIDGET_GTK
  if (handler.IsEmpty()) {
    RefPtr<nsMIMEInfoBase> gnomeInfo = nsGNOMERegistry::GetFromType(aMIMEType);
    if (gnomeInfo) {
      LOG("Got MIMEInfo from GNOME registry without extensions; setting them "
          "to %s\n",
          NS_LossyConvertUTF16toASCII(extensions).get());

      NS_ASSERTION(!gnomeInfo->HasExtensions(), "How'd that happen?");
      gnomeInfo->SetFileExtensions(NS_ConvertUTF16toUTF8(extensions));
      return gnomeInfo.forget();
    }
  }
#endif

  if (handler.IsEmpty()) {
    DoLookUpHandlerAndDescription(majorType, minorType, handler,
                                  mailcap_description, mozillaFlags, false);
  }

  if (handler.IsEmpty()) {
    DoLookUpHandlerAndDescription(majorType, u"*"_ns, handler,
                                  mailcap_description, mozillaFlags, true);
  }

  if (handler.IsEmpty()) {
    DoLookUpHandlerAndDescription(majorType, u"*"_ns, handler,
                                  mailcap_description, mozillaFlags, false);
  }

  LOG("Handler/Description results: handler='%s', description='%s', "
      "mozillaFlags='%s'\n",
      NS_LossyConvertUTF16toASCII(handler).get(),
      NS_LossyConvertUTF16toASCII(mailcap_description).get(),
      NS_LossyConvertUTF16toASCII(mozillaFlags).get());

  mailcap_description.Trim(" \t\"");
  mozillaFlags.Trim(" \t");

  if (handler.IsEmpty() && extensions.IsEmpty() &&
      mailcap_description.IsEmpty() && mime_types_description.IsEmpty()) {
    // No real useful info
    return nullptr;
  }

  RefPtr<nsMIMEInfoUnix> mimeInfo = new nsMIMEInfoUnix(aMIMEType);

  mimeInfo->SetFileExtensions(NS_ConvertUTF16toUTF8(extensions));
  if (!mime_types_description.IsEmpty()) {
    mimeInfo->SetDescription(mime_types_description);
  } else {
    mimeInfo->SetDescription(mailcap_description);
  }

  rv = NS_ERROR_NOT_AVAILABLE;
  nsCOMPtr<nsIFile> handlerFile;
  if (!handler.IsEmpty()) {
    rv = GetFileTokenForPath(handler.get(), getter_AddRefs(handlerFile));
  }

  if (NS_SUCCEEDED(rv)) {
    mimeInfo->SetDefaultApplication(handlerFile);
    mimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
    mimeInfo->SetDefaultDescription(handler);
  } else {
    mimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
  }

  return mimeInfo.forget();
}

nsresult nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aType,
                                                 const nsACString& aFileExt,
                                                 bool* aFound,
                                                 nsIMIMEInfo** aMIMEInfo) {
  *aFound = true;
  RefPtr<nsMIMEInfoBase> retval;
  // Fallback to lookup by extension when generic 'application/octet-stream'
  // content type is received.
  if (!aType.EqualsLiteral(APPLICATION_OCTET_STREAM)) {
    retval = GetFromType(PromiseFlatCString(aType));
  }
  bool hasDefault = false;
  if (retval) retval->GetHasDefaultHandler(&hasDefault);
  if (!retval || !hasDefault) {
    RefPtr<nsMIMEInfoBase> miByExt =
        GetFromExtension(PromiseFlatCString(aFileExt));
    // If we had no extension match, but a type match, use that
    if (!miByExt && retval) {
      retval.forget(aMIMEInfo);
      return NS_OK;
    }
    // If we had an extension match but no type match, set the mimetype and use
    // it
    if (!retval && miByExt) {
      if (!aType.IsEmpty()) miByExt->SetMIMEType(aType);
      miByExt.swap(retval);

      retval.forget(aMIMEInfo);
      return NS_OK;
    }
    // If we got nothing, make a new mimeinfo
    if (!retval) {
      *aFound = false;
      retval = new nsMIMEInfoUnix(aType);
      if (retval) {
        if (!aFileExt.IsEmpty()) retval->AppendExtension(aFileExt);
      }

      retval.forget(aMIMEInfo);
      return NS_OK;
    }

    // Copy the attributes of retval (mimeinfo from type) onto miByExt, to
    // return it
    // but reset to just collected mDefaultAppDescription (from ext)
    nsAutoString byExtDefault;
    miByExt->GetDefaultDescription(byExtDefault);
    retval->SetDefaultDescription(byExtDefault);
    retval->CopyBasicDataTo(miByExt);

    miByExt.swap(retval);
  }
  retval.forget(aMIMEInfo);
  return NS_OK;
}

NS_IMETHODIMP
nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString& aScheme,
                                                   bool* found,
                                                   nsIHandlerInfo** _retval) {
  NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!");

  nsresult rv =
      OSProtocolHandlerExists(nsPromiseFlatCString(aScheme).get(), found);
  if (NS_FAILED(rv)) return rv;

  nsMIMEInfoUnix* handlerInfo =
      new nsMIMEInfoUnix(aScheme, nsMIMEInfoBase::eProtocolInfo);
  NS_ENSURE_TRUE(handlerInfo, NS_ERROR_OUT_OF_MEMORY);
  NS_ADDREF(*_retval = handlerInfo);

  if (!*found) {
    // Code that calls this requires an object regardless if the OS has
    // something for us, so we return the empty object.
    return NS_OK;
  }

  nsAutoString desc;
  GetApplicationDescription(aScheme, desc);
  handlerInfo->SetDefaultDescription(desc);

  return NS_OK;
}

Messung V0.5
C=90 H=89 G=89

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.20Bemerkung:  (vorverarbeitet)  ¤

*Bot Zugriff






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.