LCOV - code coverage report
Current view: top level - uriloader/exthandler - nsExternalHelperAppService.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 58 1134 5.1 %
Date: 2017-07-14 16:53:18 Functions: 8 76 10.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2             :  * vim:expandtab:shiftwidth=2:tabstop=2:cin:
       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/. */
       6             : 
       7             : #include "base/basictypes.h"
       8             : 
       9             : /* This must occur *after* base/basictypes.h to avoid typedefs conflicts. */
      10             : #include "mozilla/ArrayUtils.h"
      11             : #include "mozilla/Base64.h"
      12             : 
      13             : #include "mozilla/dom/ContentChild.h"
      14             : #include "mozilla/dom/TabChild.h"
      15             : #include "nsXULAppAPI.h"
      16             : 
      17             : #include "nsExternalHelperAppService.h"
      18             : #include "nsCExternalHandlerService.h"
      19             : #include "nsIURI.h"
      20             : #include "nsIURL.h"
      21             : #include "nsIFile.h"
      22             : #include "nsIFileURL.h"
      23             : #include "nsIChannel.h"
      24             : #include "nsIDirectoryService.h"
      25             : #include "nsAppDirectoryServiceDefs.h"
      26             : #include "nsICategoryManager.h"
      27             : #include "nsDependentSubstring.h"
      28             : #include "nsXPIDLString.h"
      29             : #include "nsUnicharUtils.h"
      30             : #include "nsIStringEnumerator.h"
      31             : #include "nsMemory.h"
      32             : #include "nsIStreamListener.h"
      33             : #include "nsIMIMEService.h"
      34             : #include "nsILoadGroup.h"
      35             : #include "nsIWebProgressListener.h"
      36             : #include "nsITransfer.h"
      37             : #include "nsReadableUtils.h"
      38             : #include "nsIRequest.h"
      39             : #include "nsDirectoryServiceDefs.h"
      40             : #include "nsIInterfaceRequestor.h"
      41             : #include "nsThreadUtils.h"
      42             : #include "nsAutoPtr.h"
      43             : #include "nsIMutableArray.h"
      44             : #include "nsIRedirectHistoryEntry.h"
      45             : 
      46             : // used to access our datastore of user-configured helper applications
      47             : #include "nsIHandlerService.h"
      48             : #include "nsIMIMEInfo.h"
      49             : #include "nsIRefreshURI.h" // XXX needed to redirect according to Refresh: URI
      50             : #include "nsIDocumentLoader.h" // XXX needed to get orig. channel and assoc. refresh uri
      51             : #include "nsIHelperAppLauncherDialog.h"
      52             : #include "nsIContentDispatchChooser.h"
      53             : #include "nsNetUtil.h"
      54             : #include "nsIPrivateBrowsingChannel.h"
      55             : #include "nsIIOService.h"
      56             : #include "nsNetCID.h"
      57             : 
      58             : #include "nsMimeTypes.h"
      59             : // used for header disposition information.
      60             : #include "nsIHttpChannel.h"
      61             : #include "nsIHttpChannelInternal.h"
      62             : #include "nsIEncodedChannel.h"
      63             : #include "nsIMultiPartChannel.h"
      64             : #include "nsIFileChannel.h"
      65             : #include "nsIObserverService.h" // so we can be a profile change observer
      66             : #include "nsIPropertyBag2.h" // for the 64-bit content length
      67             : 
      68             : #ifdef XP_MACOSX
      69             : #include "nsILocalFileMac.h"
      70             : #endif
      71             : 
      72             : #include "nsIPluginHost.h" // XXX needed for ext->type mapping (bug 233289)
      73             : #include "nsPluginHost.h"
      74             : #include "nsEscape.h"
      75             : 
      76             : #include "nsIStringBundle.h" // XXX needed to localize error msgs
      77             : #include "nsIPrompt.h"
      78             : 
      79             : #include "nsITextToSubURI.h" // to unescape the filename
      80             : #include "nsIMIMEHeaderParam.h"
      81             : 
      82             : #include "nsIWindowWatcher.h"
      83             : 
      84             : #include "nsIDownloadHistory.h" // to mark downloads as visited
      85             : #include "nsDocShellCID.h"
      86             : 
      87             : #include "nsCRT.h"
      88             : #include "nsLocalHandlerApp.h"
      89             : 
      90             : #include "nsIRandomGenerator.h"
      91             : 
      92             : #include "ContentChild.h"
      93             : #include "nsXULAppAPI.h"
      94             : #include "nsPIDOMWindow.h"
      95             : #include "nsIDocShellTreeOwner.h"
      96             : #include "nsIDocShellTreeItem.h"
      97             : #include "ExternalHelperAppChild.h"
      98             : 
      99             : #ifdef XP_WIN
     100             : #include "nsWindowsHelpers.h"
     101             : #endif
     102             : 
     103             : #ifdef MOZ_WIDGET_ANDROID
     104             : #include "FennecJNIWrappers.h"
     105             : #endif
     106             : 
     107             : #include "mozilla/SizePrintfMacros.h"
     108             : #include "mozilla/Preferences.h"
     109             : #include "mozilla/ipc/URIUtils.h"
     110             : 
     111             : using namespace mozilla;
     112             : using namespace mozilla::ipc;
     113             : 
     114             : // Download Folder location constants
     115             : #define NS_PREF_DOWNLOAD_DIR        "browser.download.dir"
     116             : #define NS_PREF_DOWNLOAD_FOLDERLIST "browser.download.folderList"
     117             : enum {
     118             :   NS_FOLDER_VALUE_DESKTOP = 0
     119             : , NS_FOLDER_VALUE_DOWNLOADS = 1
     120             : , NS_FOLDER_VALUE_CUSTOM = 2
     121             : };
     122             : 
     123             : LazyLogModule nsExternalHelperAppService::mLog("HelperAppService");
     124             : 
     125             : // Using level 3 here because the OSHelperAppServices use a log level
     126             : // of LogLevel::Debug (4), and we want less detailed output here
     127             : // Using 3 instead of LogLevel::Warning because we don't output warnings
     128             : #undef LOG
     129             : #define LOG(args) MOZ_LOG(nsExternalHelperAppService::mLog, mozilla::LogLevel::Info, args)
     130             : #define LOG_ENABLED() MOZ_LOG_TEST(nsExternalHelperAppService::mLog, mozilla::LogLevel::Info)
     131             : 
     132             : static const char NEVER_ASK_FOR_SAVE_TO_DISK_PREF[] =
     133             :   "browser.helperApps.neverAsk.saveToDisk";
     134             : static const char NEVER_ASK_FOR_OPEN_FILE_PREF[] =
     135             :   "browser.helperApps.neverAsk.openFile";
     136             : 
     137             : // Helper functions for Content-Disposition headers
     138             : 
     139             : /**
     140             :  * Given a URI fragment, unescape it
     141             :  * @param aFragment The string to unescape
     142             :  * @param aURI The URI from which this fragment is taken. Only its character set
     143             :  *             will be used.
     144             :  * @param aResult [out] Unescaped string.
     145             :  */
     146           0 : static nsresult UnescapeFragment(const nsACString& aFragment, nsIURI* aURI,
     147             :                                  nsAString& aResult)
     148             : {
     149             :   // First, we need a charset
     150           0 :   nsAutoCString originCharset;
     151           0 :   nsresult rv = aURI->GetOriginCharset(originCharset);
     152           0 :   NS_ENSURE_SUCCESS(rv, rv);
     153             : 
     154             :   // Now, we need the unescaper
     155           0 :   nsCOMPtr<nsITextToSubURI> textToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
     156           0 :   NS_ENSURE_SUCCESS(rv, rv);
     157             : 
     158           0 :   return textToSubURI->UnEscapeURIForUI(originCharset, aFragment, aResult);
     159             : }
     160             : 
     161             : /**
     162             :  * UTF-8 version of UnescapeFragment.
     163             :  * @param aFragment The string to unescape
     164             :  * @param aURI The URI from which this fragment is taken. Only its character set
     165             :  *             will be used.
     166             :  * @param aResult [out] Unescaped string, UTF-8 encoded.
     167             :  * @note It is safe to pass the same string for aFragment and aResult.
     168             :  * @note When this function fails, aResult will not be modified.
     169             :  */
     170           0 : static nsresult UnescapeFragment(const nsACString& aFragment, nsIURI* aURI,
     171             :                                  nsACString& aResult)
     172             : {
     173           0 :   nsAutoString result;
     174           0 :   nsresult rv = UnescapeFragment(aFragment, aURI, result);
     175           0 :   if (NS_SUCCEEDED(rv))
     176           0 :     CopyUTF16toUTF8(result, aResult);
     177           0 :   return rv;
     178             : }
     179             : 
     180             : /**
     181             :  * Given a channel, returns the filename and extension the channel has.
     182             :  * This uses the URL and other sources (nsIMultiPartChannel).
     183             :  * Also gives back whether the channel requested external handling (i.e.
     184             :  * whether Content-Disposition: attachment was sent)
     185             :  * @param aChannel The channel to extract the filename/extension from
     186             :  * @param aFileName [out] Reference to the string where the filename should be
     187             :  *        stored. Empty if it could not be retrieved.
     188             :  *        WARNING - this filename may contain characters which the OS does not
     189             :  *        allow as part of filenames!
     190             :  * @param aExtension [out] Reference to the string where the extension should
     191             :  *        be stored. Empty if it could not be retrieved. Stored in UTF-8.
     192             :  * @param aAllowURLExtension (optional) Get the extension from the URL if no
     193             :  *        Content-Disposition header is present. Default is true.
     194             :  * @retval true The server sent Content-Disposition:attachment or equivalent
     195             :  * @retval false Content-Disposition: inline or no content-disposition header
     196             :  *         was sent.
     197             :  */
     198           0 : static bool GetFilenameAndExtensionFromChannel(nsIChannel* aChannel,
     199             :                                                  nsString& aFileName,
     200             :                                                  nsCString& aExtension,
     201             :                                                  bool aAllowURLExtension = true)
     202             : {
     203           0 :   aExtension.Truncate();
     204             :   /*
     205             :    * If the channel is an http or part of a multipart channel and we
     206             :    * have a content disposition header set, then use the file name
     207             :    * suggested there as the preferred file name to SUGGEST to the
     208             :    * user.  we shouldn't actually use that without their
     209             :    * permission... otherwise just use our temp file
     210             :    */
     211           0 :   bool handleExternally = false;
     212             :   uint32_t disp;
     213           0 :   nsresult rv = aChannel->GetContentDisposition(&disp);
     214           0 :   if (NS_SUCCEEDED(rv))
     215             :   {
     216           0 :     aChannel->GetContentDispositionFilename(aFileName);
     217           0 :     if (disp == nsIChannel::DISPOSITION_ATTACHMENT)
     218           0 :       handleExternally = true;
     219             :   }
     220             : 
     221             :   // If the disposition header didn't work, try the filename from nsIURL
     222           0 :   nsCOMPtr<nsIURI> uri;
     223           0 :   aChannel->GetURI(getter_AddRefs(uri));
     224           0 :   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
     225           0 :   if (url && aFileName.IsEmpty())
     226             :   {
     227           0 :     if (aAllowURLExtension) {
     228           0 :       url->GetFileExtension(aExtension);
     229           0 :       UnescapeFragment(aExtension, url, aExtension);
     230             : 
     231             :       // Windows ignores terminating dots. So we have to as well, so
     232             :       // that our security checks do "the right thing"
     233             :       // In case the aExtension consisted only of the dot, the code below will
     234             :       // extract an aExtension from the filename
     235           0 :       aExtension.Trim(".", false);
     236             :     }
     237             : 
     238             :     // try to extract the file name from the url and use that as a first pass as the
     239             :     // leaf name of our temp file...
     240           0 :     nsAutoCString leafName;
     241           0 :     url->GetFileName(leafName);
     242           0 :     if (!leafName.IsEmpty())
     243             :     {
     244           0 :       rv = UnescapeFragment(leafName, url, aFileName);
     245           0 :       if (NS_FAILED(rv))
     246             :       {
     247           0 :         CopyUTF8toUTF16(leafName, aFileName); // use escaped name
     248             :       }
     249             :     }
     250             :   }
     251             : 
     252             :   // Extract Extension, if we have a filename; otherwise,
     253             :   // truncate the string
     254           0 :   if (aExtension.IsEmpty()) {
     255           0 :     if (!aFileName.IsEmpty())
     256             :     {
     257             :       // Windows ignores terminating dots. So we have to as well, so
     258             :       // that our security checks do "the right thing"
     259           0 :       aFileName.Trim(".", false);
     260             : 
     261             :       // XXX RFindCharInReadable!!
     262           0 :       nsAutoString fileNameStr(aFileName);
     263           0 :       int32_t idx = fileNameStr.RFindChar(char16_t('.'));
     264           0 :       if (idx != kNotFound)
     265           0 :         CopyUTF16toUTF8(StringTail(fileNameStr, fileNameStr.Length() - idx - 1), aExtension);
     266             :     }
     267             :   }
     268             : 
     269             : 
     270           0 :   return handleExternally;
     271             : }
     272             : 
     273             : /**
     274             :  * Obtains the directory to use.  This tends to vary per platform, and
     275             :  * needs to be consistent throughout our codepaths. For platforms where
     276             :  * helper apps use the downloads directory, this should be kept in
     277             :  * sync with DownloadIntegration.jsm.
     278             :  *
     279             :  * Optionally skip availability of the directory and storage.
     280             :  */
     281           0 : static nsresult GetDownloadDirectory(nsIFile **_directory,
     282             :                                      bool aSkipChecks = false)
     283             : {
     284           0 :   nsCOMPtr<nsIFile> dir;
     285             : #ifdef XP_MACOSX
     286             :   // On OS X, we first try to get the users download location, if it's set.
     287             :   switch (Preferences::GetInt(NS_PREF_DOWNLOAD_FOLDERLIST, -1)) {
     288             :     case NS_FOLDER_VALUE_DESKTOP:
     289             :       (void) NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(dir));
     290             :       break;
     291             :     case NS_FOLDER_VALUE_CUSTOM:
     292             :       {
     293             :         Preferences::GetComplex(NS_PREF_DOWNLOAD_DIR,
     294             :                                 NS_GET_IID(nsIFile),
     295             :                                 getter_AddRefs(dir));
     296             :         if (!dir) break;
     297             : 
     298             :         // If we're not checking for availability we're done.
     299             :         if (aSkipChecks) {
     300             :           dir.forget(_directory);
     301             :           return NS_OK;
     302             :         }
     303             : 
     304             :         // We have the directory, and now we need to make sure it exists
     305             :         bool dirExists = false;
     306             :         (void) dir->Exists(&dirExists);
     307             :         if (dirExists) break;
     308             : 
     309             :         nsresult rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0755);
     310             :         if (NS_FAILED(rv)) {
     311             :           dir = nullptr;
     312             :           break;
     313             :         }
     314             :       }
     315             :       break;
     316             :     case NS_FOLDER_VALUE_DOWNLOADS:
     317             :       // This is just the OS default location, so fall out
     318             :       break;
     319             :   }
     320             : 
     321             :   if (!dir) {
     322             :     // If not, we default to the OS X default download location.
     323             :     nsresult rv = NS_GetSpecialDirectory(NS_OSX_DEFAULT_DOWNLOAD_DIR,
     324             :                                          getter_AddRefs(dir));
     325             :     NS_ENSURE_SUCCESS(rv, rv);
     326             :   }
     327             : #elif defined(ANDROID)
     328             :   // We ask Java for the temporary download directory. The directory will be
     329             :   // different depending on whether we have the permission to write to the
     330             :   // public download directory or not.
     331             :   // In the case where we do not have the permission we will start the
     332             :   // download to the app cache directory and later move it to the final
     333             :   // destination after prompting for the permission.
     334             :   jni::String::LocalRef downloadDir;
     335             :   if (jni::IsFennec()) {
     336             :     downloadDir = java::DownloadsIntegration::GetTemporaryDownloadDirectory();
     337             :   }
     338             : 
     339             :   nsresult rv;
     340             :   if (downloadDir) {
     341             :     nsCOMPtr<nsIFile> ldir;
     342             :     rv = NS_NewNativeLocalFile(downloadDir->ToCString(),
     343             :                                true, getter_AddRefs(ldir));
     344             : 
     345             :     NS_ENSURE_SUCCESS(rv, rv);
     346             :     dir = do_QueryInterface(ldir);
     347             : 
     348             :     // If we're not checking for availability we're done.
     349             :     if (aSkipChecks) {
     350             :       dir.forget(_directory);
     351             :       return NS_OK;
     352             :     }
     353             :   }
     354             :   else {
     355             :     return NS_ERROR_FAILURE;
     356             :   }
     357             : #else
     358             :   // On all other platforms, we default to the systems temporary directory.
     359           0 :   nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dir));
     360           0 :   NS_ENSURE_SUCCESS(rv, rv);
     361             : 
     362             : #if defined(XP_UNIX)
     363             :   // Ensuring that only the current user can read the file names we end up
     364             :   // creating. Note that Creating directories with specified permission only
     365             :   // supported on Unix platform right now. That's why above if exists.
     366             : 
     367             :   uint32_t permissions;
     368           0 :   rv = dir->GetPermissions(&permissions);
     369           0 :   NS_ENSURE_SUCCESS(rv, rv);
     370             : 
     371           0 :   if (permissions != PR_IRWXU) {
     372           0 :     const char* userName = PR_GetEnv("USERNAME");
     373           0 :     if (!userName || !*userName) {
     374           0 :       userName = PR_GetEnv("USER");
     375             :     }
     376           0 :     if (!userName || !*userName) {
     377           0 :       userName = PR_GetEnv("LOGNAME");
     378             :     }
     379           0 :     if (!userName || !*userName) {
     380           0 :       userName = "mozillaUser";
     381             :     }
     382             : 
     383           0 :     nsAutoString userDir;
     384           0 :     userDir.AssignLiteral("mozilla_");
     385           0 :     userDir.AppendASCII(userName);
     386           0 :     userDir.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '_');
     387             : 
     388           0 :     int counter = 0;
     389             :     bool pathExists;
     390           0 :     nsCOMPtr<nsIFile> finalPath;
     391             : 
     392             :     while (true) {
     393           0 :       nsAutoString countedUserDir(userDir);
     394           0 :       countedUserDir.AppendInt(counter, 10);
     395           0 :       dir->Clone(getter_AddRefs(finalPath));
     396           0 :       finalPath->Append(countedUserDir);
     397             : 
     398           0 :       rv = finalPath->Exists(&pathExists);
     399           0 :       NS_ENSURE_SUCCESS(rv, rv);
     400             : 
     401           0 :       if (pathExists) {
     402             :         // If this path has the right permissions, use it.
     403           0 :         rv = finalPath->GetPermissions(&permissions);
     404           0 :         NS_ENSURE_SUCCESS(rv, rv);
     405             : 
     406             :         // Ensuring the path is writable by the current user.
     407             :         bool isWritable;
     408           0 :         rv = finalPath->IsWritable(&isWritable);
     409           0 :         NS_ENSURE_SUCCESS(rv, rv);
     410             : 
     411           0 :         if (permissions == PR_IRWXU && isWritable) {
     412           0 :           dir = finalPath;
     413           0 :           break;
     414             :         }
     415             :       }
     416             : 
     417           0 :       rv = finalPath->Create(nsIFile::DIRECTORY_TYPE, PR_IRWXU);
     418           0 :       if (NS_SUCCEEDED(rv)) {
     419           0 :         dir = finalPath;
     420           0 :         break;
     421             :       }
     422           0 :       else if (rv != NS_ERROR_FILE_ALREADY_EXISTS) {
     423             :         // Unexpected error.
     424           0 :         return rv;
     425             :       }
     426             : 
     427           0 :       counter++;
     428           0 :     }
     429             :   }
     430             : 
     431             : #endif
     432             : #endif
     433             : 
     434           0 :   NS_ASSERTION(dir, "Somehow we didn't get a download directory!");
     435           0 :   dir.forget(_directory);
     436           0 :   return NS_OK;
     437             : }
     438             : 
     439             : /**
     440             :  * Structure for storing extension->type mappings.
     441             :  * @see defaultMimeEntries
     442             :  */
     443             : struct nsDefaultMimeTypeEntry {
     444             :   const char* mMimeType;
     445             :   const char* mFileExtension;
     446             : };
     447             : 
     448             : /**
     449             :  * Default extension->mimetype mappings. These are not overridable.
     450             :  * If you add types here, make sure they are lowercase, or you'll regret it.
     451             :  */
     452             : static const nsDefaultMimeTypeEntry defaultMimeEntries[] =
     453             : {
     454             :   // The following are those extensions that we're asked about during startup,
     455             :   // sorted by order used
     456             :   { IMAGE_GIF, "gif" },
     457             :   { TEXT_XML, "xml" },
     458             :   { APPLICATION_RDF, "rdf" },
     459             :   { TEXT_XUL, "xul" },
     460             :   { IMAGE_PNG, "png" },
     461             :   // -- end extensions used during startup
     462             :   { TEXT_CSS, "css" },
     463             :   { IMAGE_JPEG, "jpeg" },
     464             :   { IMAGE_JPEG, "jpg" },
     465             :   { IMAGE_SVG_XML, "svg" },
     466             :   { TEXT_HTML, "html" },
     467             :   { TEXT_HTML, "htm" },
     468             :   { APPLICATION_XPINSTALL, "xpi" },
     469             :   { "application/xhtml+xml", "xhtml" },
     470             :   { "application/xhtml+xml", "xht" },
     471             :   { TEXT_PLAIN, "txt" },
     472             :   { APPLICATION_JSON, "json" },
     473             :   { APPLICATION_XJAVASCRIPT, "js" },
     474             :   { APPLICATION_XJAVASCRIPT, "jsm" },
     475             :   { VIDEO_OGG, "ogv" },
     476             :   { VIDEO_OGG, "ogg" },
     477             :   { APPLICATION_OGG, "ogg" },
     478             :   { AUDIO_OGG, "oga" },
     479             :   { AUDIO_OGG, "opus" },
     480             :   { APPLICATION_PDF, "pdf" },
     481             :   { VIDEO_WEBM, "webm" },
     482             :   { AUDIO_WEBM, "webm" },
     483             :   { IMAGE_ICO, "ico" },
     484             :   { TEXT_PLAIN, "properties" },
     485             :   { TEXT_PLAIN, "locale" },
     486             : #if defined(MOZ_WMF)
     487             :   { VIDEO_MP4, "mp4" },
     488             :   { AUDIO_MP4, "m4a" },
     489             :   { AUDIO_MP3, "mp3" },
     490             : #endif
     491             : #ifdef MOZ_RAW
     492             :   { VIDEO_RAW, "yuv" }
     493             : #endif
     494             : };
     495             : 
     496             : /**
     497             :  * This is a small private struct used to help us initialize some
     498             :  * default mime types.
     499             :  */
     500             : struct nsExtraMimeTypeEntry {
     501             :   const char* mMimeType; 
     502             :   const char* mFileExtensions;
     503             :   const char* mDescription;
     504             : };
     505             : 
     506             : #ifdef XP_MACOSX
     507             : #define MAC_TYPE(x) x
     508             : #else
     509             : #define MAC_TYPE(x) 0
     510             : #endif
     511             : 
     512             : /**
     513             :  * This table lists all of the 'extra' content types that we can deduce from particular
     514             :  * file extensions.  These entries also ensure that we provide a good descriptive name
     515             :  * when we encounter files with these content types and/or extensions.  These can be
     516             :  * overridden by user helper app prefs.
     517             :  * If you add types here, make sure they are lowercase, or you'll regret it.
     518             :  */
     519             : static const nsExtraMimeTypeEntry extraMimeEntries[] =
     520             : {
     521             : #if defined(XP_MACOSX) // don't define .bin on the mac...use internet config to look that up...
     522             :   { APPLICATION_OCTET_STREAM, "exe,com", "Binary File" },
     523             : #else
     524             :   { APPLICATION_OCTET_STREAM, "exe,com,bin", "Binary File" },
     525             : #endif
     526             :   { APPLICATION_GZIP2, "gz", "gzip" },
     527             :   { "application/x-arj", "arj", "ARJ file" },
     528             :   { "application/rtf", "rtf", "Rich Text Format File" },
     529             :   { APPLICATION_XPINSTALL, "xpi", "XPInstall Install" },
     530             :   { APPLICATION_PDF, "pdf", "Portable Document Format" },
     531             :   { APPLICATION_POSTSCRIPT, "ps,eps,ai", "Postscript File" },
     532             :   { APPLICATION_XJAVASCRIPT, "js", "Javascript Source File" },
     533             :   { APPLICATION_XJAVASCRIPT, "jsm", "Javascript Module Source File" },
     534             : #ifdef MOZ_WIDGET_ANDROID
     535             :   { "application/vnd.android.package-archive", "apk", "Android Package" },
     536             : #endif
     537             :   { IMAGE_ART, "art", "ART Image" },
     538             :   { IMAGE_BMP, "bmp", "BMP Image" },
     539             :   { IMAGE_GIF, "gif", "GIF Image" },
     540             :   { IMAGE_ICO, "ico,cur", "ICO Image" },
     541             :   { IMAGE_JPEG, "jpeg,jpg,jfif,pjpeg,pjp", "JPEG Image" },
     542             :   { IMAGE_PNG, "png", "PNG Image" },
     543             :   { IMAGE_APNG, "apng", "APNG Image" },
     544             :   { IMAGE_TIFF, "tiff,tif", "TIFF Image" },
     545             :   { IMAGE_XBM, "xbm", "XBM Image" },
     546             :   { IMAGE_SVG_XML, "svg", "Scalable Vector Graphics" },
     547             :   { MESSAGE_RFC822, "eml", "RFC-822 data" },
     548             :   { TEXT_PLAIN, "txt,text", "Text File" },
     549             :   { APPLICATION_JSON, "json", "JavaScript Object Notation" },
     550             :   { TEXT_VTT, "vtt", "Web Video Text Tracks" },
     551             :   { TEXT_CACHE_MANIFEST, "appcache", "Application Cache Manifest" },
     552             :   { TEXT_HTML, "html,htm,shtml,ehtml", "HyperText Markup Language" },
     553             :   { "application/xhtml+xml", "xhtml,xht", "Extensible HyperText Markup Language" },
     554             :   { APPLICATION_MATHML_XML, "mml", "Mathematical Markup Language" },
     555             :   { APPLICATION_RDF, "rdf", "Resource Description Framework" },
     556             :   { TEXT_XUL, "xul", "XML-Based User Interface Language" },
     557             :   { TEXT_XML, "xml,xsl,xbl", "Extensible Markup Language" },
     558             :   { TEXT_CSS, "css", "Style Sheet" },
     559             :   { TEXT_VCARD, "vcf,vcard", "Contact Information" },
     560             :   { VIDEO_OGG, "ogv", "Ogg Video" },
     561             :   { VIDEO_OGG, "ogg", "Ogg Video" },
     562             :   { APPLICATION_OGG, "ogg", "Ogg Video"},
     563             :   { AUDIO_OGG, "oga", "Ogg Audio" },
     564             :   { AUDIO_OGG, "opus", "Opus Audio" },
     565             : #ifdef MOZ_WIDGET_GONK
     566             :   { AUDIO_AMR, "amr", "Adaptive Multi-Rate Audio" },
     567             :   { AUDIO_FLAC, "flac", "FLAC Audio" },
     568             :   { VIDEO_AVI, "avi", "Audio Video Interleave" },
     569             :   { VIDEO_AVI, "divx", "Audio Video Interleave" },
     570             :   { VIDEO_MPEG_TS, "ts", "MPEG Transport Stream" },
     571             :   { VIDEO_MPEG_TS, "m2ts", "MPEG-2 Transport Stream" },
     572             :   { VIDEO_MATROSKA, "mkv", "MATROSKA VIDEO" },
     573             :   { AUDIO_MATROSKA, "mka", "MATROSKA AUDIO" },
     574             : #endif
     575             :   { VIDEO_WEBM, "webm", "Web Media Video" },
     576             :   { AUDIO_WEBM, "webm", "Web Media Audio" },
     577             :   { AUDIO_MP3, "mp3", "MPEG Audio" },
     578             :   { VIDEO_MP4, "mp4", "MPEG-4 Video" },
     579             :   { AUDIO_MP4, "m4a", "MPEG-4 Audio" },
     580             :   { VIDEO_RAW, "yuv", "Raw YUV Video" },
     581             :   { AUDIO_WAV, "wav", "Waveform Audio" },
     582             :   { VIDEO_3GPP, "3gpp,3gp", "3GPP Video" },
     583             :   { VIDEO_3GPP2,"3g2", "3GPP2 Video" },
     584             : #ifdef MOZ_WIDGET_GONK
     585             :   // The AUDIO_3GPP has to come after the VIDEO_3GPP entry because the Gallery
     586             :   // app on Firefox OS depends on the "3gp" extension mapping to the
     587             :   // "video/3gpp" MIME type.
     588             :   { AUDIO_3GPP, "3gpp,3gp", "3GPP Audio" },
     589             :   { AUDIO_3GPP2, "3g2", "3GPP2 Audio" },
     590             : #endif
     591             :   { AUDIO_MIDI, "mid", "Standard MIDI Audio" }
     592             : };
     593             : 
     594             : #undef MAC_TYPE
     595             : 
     596             : /**
     597             :  * File extensions for which decoding should be disabled.
     598             :  * NOTE: These MUST be lower-case and ASCII.
     599             :  */
     600             : static const nsDefaultMimeTypeEntry nonDecodableExtensions[] = {
     601             :   { APPLICATION_GZIP, "gz" }, 
     602             :   { APPLICATION_GZIP, "tgz" },
     603             :   { APPLICATION_ZIP, "zip" },
     604             :   { APPLICATION_COMPRESS, "z" },
     605             :   { APPLICATION_GZIP, "svgz" }
     606             : };
     607             : 
     608         468 : NS_IMPL_ISUPPORTS(
     609             :   nsExternalHelperAppService,
     610             :   nsIExternalHelperAppService,
     611             :   nsPIExternalAppLauncher,
     612             :   nsIExternalProtocolService,
     613             :   nsIMIMEService,
     614             :   nsIObserver,
     615             :   nsISupportsWeakReference)
     616             : 
     617           3 : nsExternalHelperAppService::nsExternalHelperAppService()
     618             : {
     619           3 : }
     620           3 : nsresult nsExternalHelperAppService::Init()
     621             : {
     622             :   // Add an observer for profile change
     623           6 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     624           3 :   if (!obs)
     625           0 :     return NS_ERROR_FAILURE;
     626             : 
     627           3 :   nsresult rv = obs->AddObserver(this, "profile-before-change", true);
     628           3 :   NS_ENSURE_SUCCESS(rv, rv);
     629           3 :   return obs->AddObserver(this, "last-pb-context-exited", true);
     630             : }
     631             : 
     632           0 : nsExternalHelperAppService::~nsExternalHelperAppService()
     633             : {
     634           0 : }
     635             : 
     636             : 
     637             : nsresult
     638           0 : nsExternalHelperAppService::DoContentContentProcessHelper(const nsACString& aMimeContentType,
     639             :                                                           nsIRequest *aRequest,
     640             :                                                           nsIInterfaceRequestor *aContentContext,
     641             :                                                           bool aForceSave,
     642             :                                                           nsIInterfaceRequestor *aWindowContext,
     643             :                                                           nsIStreamListener ** aStreamListener)
     644             : {
     645           0 :   nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(aContentContext);
     646           0 :   NS_ENSURE_STATE(window);
     647             : 
     648             :   // We need to get a hold of a ContentChild so that we can begin forwarding
     649             :   // this data to the parent.  In the HTTP case, this is unfortunate, since
     650             :   // we're actually passing data from parent->child->parent wastefully, but
     651             :   // the Right Fix will eventually be to short-circuit those channels on the
     652             :   // parent side based on some sort of subscription concept.
     653             :   using mozilla::dom::ContentChild;
     654             :   using mozilla::dom::ExternalHelperAppChild;
     655           0 :   ContentChild *child = ContentChild::GetSingleton();
     656           0 :   if (!child) {
     657           0 :     return NS_ERROR_FAILURE;
     658             :   }
     659             : 
     660           0 :   nsCString disp;
     661           0 :   nsCOMPtr<nsIURI> uri;
     662           0 :   int64_t contentLength = -1;
     663           0 :   bool wasFileChannel = false;
     664           0 :   uint32_t contentDisposition = -1;
     665           0 :   nsAutoString fileName;
     666             : 
     667           0 :   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     668           0 :   if (channel) {
     669           0 :     channel->GetURI(getter_AddRefs(uri));
     670           0 :     channel->GetContentLength(&contentLength);
     671           0 :     channel->GetContentDisposition(&contentDisposition);
     672           0 :     channel->GetContentDispositionFilename(fileName);
     673           0 :     channel->GetContentDispositionHeader(disp);
     674             : 
     675           0 :     nsCOMPtr<nsIFileChannel> fileChan(do_QueryInterface(aRequest));
     676           0 :     wasFileChannel = fileChan != nullptr;
     677             :   }
     678             : 
     679             : 
     680           0 :   nsCOMPtr<nsIURI> referrer;
     681           0 :   NS_GetReferrerFromChannel(channel, getter_AddRefs(referrer));
     682             : 
     683           0 :   OptionalURIParams uriParams, referrerParams;
     684           0 :   SerializeURI(uri, uriParams);
     685           0 :   SerializeURI(referrer, referrerParams);
     686             : 
     687             :   // Now we build a protocol for forwarding our data to the parent.  The
     688             :   // protocol will act as a listener on the child-side and create a "real"
     689             :   // helperAppService listener on the parent-side, via another call to
     690             :   // DoContent.
     691             :   mozilla::dom::PExternalHelperAppChild *pc =
     692           0 :     child->SendPExternalHelperAppConstructor(uriParams,
     693           0 :                                               nsCString(aMimeContentType),
     694             :                                               disp, contentDisposition,
     695             :                                               fileName, aForceSave,
     696             :                                               contentLength, wasFileChannel,
     697             :                                               referrerParams,
     698           0 :                                               mozilla::dom::TabChild::GetFrom(window));
     699           0 :   ExternalHelperAppChild *childListener = static_cast<ExternalHelperAppChild *>(pc);
     700             : 
     701           0 :   NS_ADDREF(*aStreamListener = childListener);
     702             : 
     703           0 :   uint32_t reason = nsIHelperAppLauncherDialog::REASON_CANTHANDLE;
     704             : 
     705             :   RefPtr<nsExternalAppHandler> handler =
     706           0 :     new nsExternalAppHandler(nullptr, EmptyCString(), aContentContext, aWindowContext, this,
     707           0 :                              fileName, reason, aForceSave);
     708           0 :   if (!handler) {
     709           0 :     return NS_ERROR_OUT_OF_MEMORY;
     710             :   }
     711             : 
     712           0 :   childListener->SetHandler(handler);
     713           0 :   return NS_OK;
     714             : }
     715             : 
     716           0 : NS_IMETHODIMP nsExternalHelperAppService::DoContent(const nsACString& aMimeContentType,
     717             :                                                     nsIRequest *aRequest,
     718             :                                                     nsIInterfaceRequestor *aContentContext,
     719             :                                                     bool aForceSave,
     720             :                                                     nsIInterfaceRequestor *aWindowContext,
     721             :                                                     nsIStreamListener ** aStreamListener)
     722             : {
     723           0 :   if (XRE_IsContentProcess()) {
     724           0 :     return DoContentContentProcessHelper(aMimeContentType, aRequest, aContentContext,
     725           0 :                                          aForceSave, aWindowContext, aStreamListener);
     726             :   }
     727             : 
     728           0 :   nsAutoString fileName;
     729           0 :   nsAutoCString fileExtension;
     730           0 :   uint32_t reason = nsIHelperAppLauncherDialog::REASON_CANTHANDLE;
     731           0 :   uint32_t contentDisposition = -1;
     732             : 
     733             :   // Get the file extension and name that we will need later
     734           0 :   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     735           0 :   nsCOMPtr<nsIURI> uri;
     736           0 :   int64_t contentLength = -1;
     737           0 :   if (channel) {
     738           0 :     channel->GetURI(getter_AddRefs(uri));
     739           0 :     channel->GetContentLength(&contentLength);
     740           0 :     channel->GetContentDisposition(&contentDisposition);
     741           0 :     channel->GetContentDispositionFilename(fileName);
     742             : 
     743             :     // Check if we have a POST request, in which case we don't want to use
     744             :     // the url's extension
     745           0 :     bool allowURLExt = true;
     746           0 :     nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(channel);
     747           0 :     if (httpChan) {
     748           0 :       nsAutoCString requestMethod;
     749           0 :       Unused << httpChan->GetRequestMethod(requestMethod);
     750           0 :       allowURLExt = !requestMethod.EqualsLiteral("POST");
     751             :     }
     752             : 
     753             :     // Check if we had a query string - we don't want to check the URL
     754             :     // extension if a query is present in the URI
     755             :     // If we already know we don't want to check the URL extension, don't
     756             :     // bother checking the query
     757           0 :     if (uri && allowURLExt) {
     758           0 :       nsCOMPtr<nsIURL> url = do_QueryInterface(uri);
     759             : 
     760           0 :       if (url) {
     761           0 :         nsAutoCString query;
     762             : 
     763             :         // We only care about the query for HTTP and HTTPS URLs
     764             :         nsresult rv;
     765             :         bool isHTTP, isHTTPS;
     766           0 :         rv = uri->SchemeIs("http", &isHTTP);
     767           0 :         if (NS_FAILED(rv)) {
     768           0 :           isHTTP = false;
     769             :         }
     770           0 :         rv = uri->SchemeIs("https", &isHTTPS);
     771           0 :         if (NS_FAILED(rv)) {
     772           0 :           isHTTPS = false;
     773             :         }
     774           0 :         if (isHTTP || isHTTPS) {
     775           0 :           url->GetQuery(query);
     776             :         }
     777             : 
     778             :         // Only get the extension if the query is empty; if it isn't, then the
     779             :         // extension likely belongs to a cgi script and isn't helpful
     780           0 :         allowURLExt = query.IsEmpty();
     781             :       }
     782             :     }
     783             :     // Extract name & extension
     784           0 :     bool isAttachment = GetFilenameAndExtensionFromChannel(channel, fileName,
     785             :                                                              fileExtension,
     786           0 :                                                              allowURLExt);
     787           0 :     LOG(("Found extension '%s' (filename is '%s', handling attachment: %i)",
     788             :          fileExtension.get(), NS_ConvertUTF16toUTF8(fileName).get(),
     789             :          isAttachment));
     790           0 :     if (isAttachment) {
     791           0 :       reason = nsIHelperAppLauncherDialog::REASON_SERVERREQUEST;
     792             :     }
     793             :   }
     794             : 
     795           0 :   LOG(("HelperAppService::DoContent: mime '%s', extension '%s'\n",
     796             :        PromiseFlatCString(aMimeContentType).get(), fileExtension.get()));
     797             : 
     798             :   // We get the mime service here even though we're the default implementation
     799             :   // of it, so it's possible to override only the mime service and not need to
     800             :   // reimplement the whole external helper app service itself.
     801           0 :   nsCOMPtr<nsIMIMEService> mimeSvc(do_GetService(NS_MIMESERVICE_CONTRACTID));
     802           0 :   NS_ENSURE_TRUE(mimeSvc, NS_ERROR_FAILURE);
     803             : 
     804             :   // Try to find a mime object by looking at the mime type/extension
     805           0 :   nsCOMPtr<nsIMIMEInfo> mimeInfo;
     806           0 :   if (aMimeContentType.Equals(APPLICATION_GUESS_FROM_EXT, nsCaseInsensitiveCStringComparator())) {
     807           0 :     nsAutoCString mimeType;
     808           0 :     if (!fileExtension.IsEmpty()) {
     809           0 :       mimeSvc->GetFromTypeAndExtension(EmptyCString(), fileExtension, getter_AddRefs(mimeInfo));
     810           0 :       if (mimeInfo) {
     811           0 :         mimeInfo->GetMIMEType(mimeType);
     812             : 
     813           0 :         LOG(("OS-Provided mime type '%s' for extension '%s'\n", 
     814             :              mimeType.get(), fileExtension.get()));
     815             :       }
     816             :     }
     817             : 
     818           0 :     if (fileExtension.IsEmpty() || mimeType.IsEmpty()) {
     819             :       // Extension lookup gave us no useful match
     820           0 :       mimeSvc->GetFromTypeAndExtension(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM), fileExtension,
     821           0 :                                        getter_AddRefs(mimeInfo));
     822           0 :       mimeType.AssignLiteral(APPLICATION_OCTET_STREAM);
     823             :     }
     824             : 
     825           0 :     if (channel) {
     826           0 :       channel->SetContentType(mimeType);
     827             :     }
     828             : 
     829             :     // Don't overwrite SERVERREQUEST
     830           0 :     if (reason == nsIHelperAppLauncherDialog::REASON_CANTHANDLE) {
     831           0 :       reason = nsIHelperAppLauncherDialog::REASON_TYPESNIFFED;
     832             :     }
     833             :   } else {
     834           0 :     mimeSvc->GetFromTypeAndExtension(aMimeContentType, fileExtension,
     835           0 :                                      getter_AddRefs(mimeInfo));
     836             :   } 
     837           0 :   LOG(("Type/Ext lookup found 0x%p\n", mimeInfo.get()));
     838             : 
     839             :   // No mimeinfo -> we can't continue. probably OOM.
     840           0 :   if (!mimeInfo) {
     841           0 :     return NS_ERROR_OUT_OF_MEMORY;
     842             :   }
     843             : 
     844           0 :   *aStreamListener = nullptr;
     845             :   // We want the mimeInfo's primary extension to pass it to
     846             :   // nsExternalAppHandler
     847           0 :   nsAutoCString buf;
     848           0 :   mimeInfo->GetPrimaryExtension(buf);
     849             : 
     850             :   // NB: ExternalHelperAppParent depends on this listener always being an
     851             :   // nsExternalAppHandler. If this changes, make sure to update that code.
     852             :   nsExternalAppHandler * handler = new nsExternalAppHandler(mimeInfo,
     853             :                                                             buf,
     854             :                                                             aContentContext,
     855             :                                                             aWindowContext,
     856             :                                                             this,
     857             :                                                             fileName,
     858             :                                                             reason,
     859           0 :                                                             aForceSave);
     860           0 :   if (!handler) {
     861           0 :     return NS_ERROR_OUT_OF_MEMORY;
     862             :   }
     863             : 
     864           0 :   NS_ADDREF(*aStreamListener = handler);
     865           0 :   return NS_OK;
     866             : }
     867             : 
     868           0 : NS_IMETHODIMP nsExternalHelperAppService::ApplyDecodingForExtension(const nsACString& aExtension,
     869             :                                                                     const nsACString& aEncodingType,
     870             :                                                                     bool *aApplyDecoding)
     871             : {
     872           0 :   *aApplyDecoding = true;
     873             :   uint32_t i;
     874           0 :   for(i = 0; i < ArrayLength(nonDecodableExtensions); ++i) {
     875           0 :     if (aExtension.LowerCaseEqualsASCII(nonDecodableExtensions[i].mFileExtension) &&
     876           0 :         aEncodingType.LowerCaseEqualsASCII(nonDecodableExtensions[i].mMimeType)) {
     877           0 :       *aApplyDecoding = false;
     878           0 :       break;
     879             :     }
     880             :   }
     881           0 :   return NS_OK;
     882             : }
     883             : 
     884           0 : nsresult nsExternalHelperAppService::GetFileTokenForPath(const char16_t * aPlatformAppPath,
     885             :                                                          nsIFile ** aFile)
     886             : {
     887           0 :   nsDependentString platformAppPath(aPlatformAppPath);
     888             :   // First, check if we have an absolute path
     889           0 :   nsIFile* localFile = nullptr;
     890           0 :   nsresult rv = NS_NewLocalFile(platformAppPath, true, &localFile);
     891           0 :   if (NS_SUCCEEDED(rv)) {
     892           0 :     *aFile = localFile;
     893             :     bool exists;
     894           0 :     if (NS_FAILED((*aFile)->Exists(&exists)) || !exists) {
     895           0 :       NS_RELEASE(*aFile);
     896           0 :       return NS_ERROR_FILE_NOT_FOUND;
     897             :     }
     898           0 :     return NS_OK;
     899             :   }
     900             : 
     901             : 
     902             :   // Second, check if file exists in mozilla program directory
     903           0 :   rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR, aFile);
     904           0 :   if (NS_SUCCEEDED(rv)) {
     905           0 :     rv = (*aFile)->Append(platformAppPath);
     906           0 :     if (NS_SUCCEEDED(rv)) {
     907           0 :       bool exists = false;
     908           0 :       rv = (*aFile)->Exists(&exists);
     909           0 :       if (NS_SUCCEEDED(rv) && exists)
     910           0 :         return NS_OK;
     911             :     }
     912           0 :     NS_RELEASE(*aFile);
     913             :   }
     914             : 
     915             : 
     916           0 :   return NS_ERROR_NOT_AVAILABLE;
     917             : }
     918             : 
     919             : //////////////////////////////////////////////////////////////////////////////////////////////////////
     920             : // begin external protocol service default implementation...
     921             : //////////////////////////////////////////////////////////////////////////////////////////////////////
     922           0 : NS_IMETHODIMP nsExternalHelperAppService::ExternalProtocolHandlerExists(const char * aProtocolScheme,
     923             :                                                                         bool * aHandlerExists)
     924             : {
     925           0 :   nsCOMPtr<nsIHandlerInfo> handlerInfo;
     926           0 :   nsresult rv = GetProtocolHandlerInfo(nsDependentCString(aProtocolScheme), 
     927           0 :                                        getter_AddRefs(handlerInfo));
     928           0 :   NS_ENSURE_SUCCESS(rv, rv);
     929             : 
     930             :   // See if we have any known possible handler apps for this
     931           0 :   nsCOMPtr<nsIMutableArray> possibleHandlers;
     932           0 :   handlerInfo->GetPossibleApplicationHandlers(getter_AddRefs(possibleHandlers));
     933             : 
     934             :   uint32_t length;
     935           0 :   possibleHandlers->GetLength(&length);
     936           0 :   if (length) {
     937           0 :     *aHandlerExists = true;
     938           0 :     return NS_OK;
     939             :   }
     940             : 
     941             :   // if not, fall back on an os-based handler
     942           0 :   return OSProtocolHandlerExists(aProtocolScheme, aHandlerExists);
     943             : }
     944             : 
     945           0 : NS_IMETHODIMP nsExternalHelperAppService::IsExposedProtocol(const char * aProtocolScheme, bool * aResult)
     946             : {
     947             :   // check the per protocol setting first.  it always takes precedence.
     948             :   // if not set, then use the global setting.
     949             : 
     950           0 :   nsAutoCString prefName("network.protocol-handler.expose.");
     951           0 :   prefName += aProtocolScheme;
     952             :   bool val;
     953           0 :   if (NS_SUCCEEDED(Preferences::GetBool(prefName.get(), &val))) {
     954           0 :     *aResult = val;
     955           0 :     return NS_OK;
     956             :   }
     957             : 
     958             :   // by default, no protocol is exposed.  i.e., by default all link clicks must
     959             :   // go through the external protocol service.  most applications override this
     960             :   // default behavior.
     961           0 :   *aResult =
     962           0 :     Preferences::GetBool("network.protocol-handler.expose-all", false);
     963             : 
     964           0 :   return NS_OK;
     965             : }
     966             : 
     967           0 : NS_IMETHODIMP nsExternalHelperAppService::LoadUrl(nsIURI * aURL)
     968             : {
     969           0 :   return LoadURI(aURL, nullptr);
     970             : }
     971             : 
     972             : static const char kExternalProtocolPrefPrefix[]  = "network.protocol-handler.external.";
     973             : static const char kExternalProtocolDefaultPref[] = "network.protocol-handler.external-default";
     974             : 
     975             : NS_IMETHODIMP 
     976           0 : nsExternalHelperAppService::LoadURI(nsIURI *aURI,
     977             :                                     nsIInterfaceRequestor *aWindowContext)
     978             : {
     979           0 :   NS_ENSURE_ARG_POINTER(aURI);
     980             : 
     981           0 :   if (XRE_IsContentProcess()) {
     982           0 :     URIParams uri;
     983           0 :     SerializeURI(aURI, uri);
     984             : 
     985           0 :     nsCOMPtr<nsITabChild> tabChild(do_GetInterface(aWindowContext));
     986           0 :     mozilla::dom::ContentChild::GetSingleton()->
     987           0 :       SendLoadURIExternal(uri, static_cast<dom::TabChild*>(tabChild.get()));
     988           0 :     return NS_OK;
     989             :   }
     990             : 
     991           0 :   nsAutoCString spec;
     992           0 :   aURI->GetSpec(spec);
     993             : 
     994           0 :   if (spec.Find("%00") != -1)
     995           0 :     return NS_ERROR_MALFORMED_URI;
     996             : 
     997           0 :   spec.ReplaceSubstring("\"", "%22");
     998           0 :   spec.ReplaceSubstring("`", "%60");
     999             :   
    1000           0 :   nsCOMPtr<nsIIOService> ios(do_GetIOService());
    1001           0 :   nsCOMPtr<nsIURI> uri;
    1002           0 :   nsresult rv = ios->NewURI(spec, nullptr, nullptr, getter_AddRefs(uri));
    1003           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1004             : 
    1005           0 :   nsAutoCString scheme;
    1006           0 :   uri->GetScheme(scheme);
    1007           0 :   if (scheme.IsEmpty())
    1008           0 :     return NS_OK; // must have a scheme
    1009             : 
    1010             :   // Deny load if the prefs say to do so
    1011           0 :   nsAutoCString externalPref(kExternalProtocolPrefPrefix);
    1012           0 :   externalPref += scheme;
    1013           0 :   bool allowLoad  = false;
    1014           0 :   if (NS_FAILED(Preferences::GetBool(externalPref.get(), &allowLoad))) {
    1015             :     // no scheme-specific value, check the default
    1016           0 :     if (NS_FAILED(Preferences::GetBool(kExternalProtocolDefaultPref,
    1017             :                                        &allowLoad))) {
    1018           0 :       return NS_OK; // missing default pref
    1019             :     }
    1020             :   }
    1021             : 
    1022           0 :   if (!allowLoad) {
    1023           0 :     return NS_OK; // explicitly denied
    1024             :   }
    1025             : 
    1026           0 :   nsCOMPtr<nsIHandlerInfo> handler;
    1027           0 :   rv = GetProtocolHandlerInfo(scheme, getter_AddRefs(handler));
    1028           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1029             : 
    1030             :   nsHandlerInfoAction preferredAction;
    1031           0 :   handler->GetPreferredAction(&preferredAction);
    1032           0 :   bool alwaysAsk = true;
    1033           0 :   handler->GetAlwaysAskBeforeHandling(&alwaysAsk);
    1034             : 
    1035             :   // if we are not supposed to ask, and the preferred action is to use
    1036             :   // a helper app or the system default, we just launch the URI.
    1037           0 :   if (!alwaysAsk && (preferredAction == nsIHandlerInfo::useHelperApp ||
    1038           0 :                      preferredAction == nsIHandlerInfo::useSystemDefault))
    1039           0 :     return handler->LaunchWithURI(uri, aWindowContext);
    1040             :   
    1041             :   nsCOMPtr<nsIContentDispatchChooser> chooser =
    1042           0 :     do_CreateInstance("@mozilla.org/content-dispatch-chooser;1", &rv);
    1043           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1044             :   
    1045           0 :   return chooser->Ask(handler, aWindowContext, uri,
    1046           0 :                       nsIContentDispatchChooser::REASON_CANNOT_HANDLE);
    1047             : }
    1048             : 
    1049           0 : NS_IMETHODIMP nsExternalHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval)
    1050             : {
    1051             :   // this method should only be implemented by each OS specific implementation of this service.
    1052           0 :   return NS_ERROR_NOT_IMPLEMENTED;
    1053             : }
    1054             : 
    1055             : 
    1056             : //////////////////////////////////////////////////////////////////////////////////////////////////////
    1057             : // Methods related to deleting temporary files on exit
    1058             : //////////////////////////////////////////////////////////////////////////////////////////////////////
    1059             : 
    1060             : /* static */
    1061             : nsresult
    1062           0 : nsExternalHelperAppService::DeleteTemporaryFileHelper(nsIFile * aTemporaryFile,
    1063             :                                                       nsCOMArray<nsIFile> &aFileList)
    1064             : {
    1065           0 :   bool isFile = false;
    1066             : 
    1067             :   // as a safety measure, make sure the nsIFile is really a file and not a directory object.
    1068           0 :   aTemporaryFile->IsFile(&isFile);
    1069           0 :   if (!isFile) return NS_OK;
    1070             : 
    1071           0 :   aFileList.AppendObject(aTemporaryFile);
    1072             : 
    1073           0 :   return NS_OK;
    1074             : }
    1075             : 
    1076             : NS_IMETHODIMP
    1077           0 : nsExternalHelperAppService::DeleteTemporaryFileOnExit(nsIFile* aTemporaryFile)
    1078             : {
    1079           0 :   return DeleteTemporaryFileHelper(aTemporaryFile, mTemporaryFilesList);
    1080             : }
    1081             : 
    1082             : NS_IMETHODIMP
    1083           0 : nsExternalHelperAppService::DeleteTemporaryPrivateFileWhenPossible(nsIFile* aTemporaryFile)
    1084             : {
    1085           0 :   return DeleteTemporaryFileHelper(aTemporaryFile, mTemporaryPrivateFilesList);
    1086             : }
    1087             : 
    1088           0 : void nsExternalHelperAppService::ExpungeTemporaryFilesHelper(nsCOMArray<nsIFile> &fileList)
    1089             : {
    1090           0 :   int32_t numEntries = fileList.Count();
    1091             :   nsIFile* localFile;
    1092           0 :   for (int32_t index = 0; index < numEntries; index++)
    1093             :   {
    1094           0 :     localFile = fileList[index];
    1095           0 :     if (localFile) {
    1096             :       // First make the file writable, since the temp file is probably readonly.
    1097           0 :       localFile->SetPermissions(0600);
    1098           0 :       localFile->Remove(false);
    1099             :     }
    1100             :   }
    1101             : 
    1102           0 :   fileList.Clear();
    1103           0 : }
    1104             : 
    1105           0 : void nsExternalHelperAppService::ExpungeTemporaryFiles()
    1106             : {
    1107           0 :   ExpungeTemporaryFilesHelper(mTemporaryFilesList);
    1108           0 : }
    1109             : 
    1110           0 : void nsExternalHelperAppService::ExpungeTemporaryPrivateFiles()
    1111             : {
    1112           0 :   ExpungeTemporaryFilesHelper(mTemporaryPrivateFilesList);
    1113           0 : }
    1114             : 
    1115             : static const char kExternalWarningPrefPrefix[] = 
    1116             :   "network.protocol-handler.warn-external.";
    1117             : static const char kExternalWarningDefaultPref[] = 
    1118             :   "network.protocol-handler.warn-external-default";
    1119             : 
    1120             : NS_IMETHODIMP
    1121           0 : nsExternalHelperAppService::GetProtocolHandlerInfo(const nsACString &aScheme,
    1122             :                                                    nsIHandlerInfo **aHandlerInfo)
    1123             : {
    1124             :   // XXX enterprise customers should be able to turn this support off with a
    1125             :   // single master pref (maybe use one of the "exposed" prefs here?)
    1126             : 
    1127             :   bool exists;
    1128           0 :   nsresult rv = GetProtocolHandlerInfoFromOS(aScheme, &exists, aHandlerInfo);
    1129           0 :   if (NS_FAILED(rv)) {
    1130             :     // Either it knows nothing, or we ran out of memory
    1131           0 :     return NS_ERROR_FAILURE;
    1132             :   }
    1133             :   
    1134           0 :   nsCOMPtr<nsIHandlerService> handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID);
    1135           0 :   if (handlerSvc) {
    1136           0 :     bool hasHandler = false;
    1137           0 :     (void) handlerSvc->Exists(*aHandlerInfo, &hasHandler);
    1138           0 :     if (hasHandler) {
    1139           0 :       rv = handlerSvc->FillHandlerInfo(*aHandlerInfo, EmptyCString());
    1140           0 :       if (NS_SUCCEEDED(rv))
    1141           0 :         return NS_OK;
    1142             :     }
    1143             :   }
    1144             :   
    1145           0 :   return SetProtocolHandlerDefaults(*aHandlerInfo, exists);
    1146             : }
    1147             : 
    1148             : NS_IMETHODIMP
    1149           0 : nsExternalHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
    1150             :                                                          bool *found,
    1151             :                                                          nsIHandlerInfo **aHandlerInfo)
    1152             : {
    1153             :   // intended to be implemented by the subclass
    1154           0 :   return NS_ERROR_NOT_IMPLEMENTED;
    1155             : }
    1156             : 
    1157             : NS_IMETHODIMP
    1158           0 : nsExternalHelperAppService::SetProtocolHandlerDefaults(nsIHandlerInfo *aHandlerInfo,
    1159             :                                                        bool aOSHandlerExists)
    1160             : {
    1161             :   // this type isn't in our database, so we've only got an OS default handler,
    1162             :   // if one exists
    1163             : 
    1164           0 :   if (aOSHandlerExists) {
    1165             :     // we've got a default, so use it
    1166           0 :     aHandlerInfo->SetPreferredAction(nsIHandlerInfo::useSystemDefault);
    1167             : 
    1168             :     // whether or not to ask the user depends on the warning preference
    1169           0 :     nsAutoCString scheme;
    1170           0 :     aHandlerInfo->GetType(scheme);
    1171             :     
    1172           0 :     nsAutoCString warningPref(kExternalWarningPrefPrefix);
    1173           0 :     warningPref += scheme;
    1174             :     bool warn;
    1175           0 :     if (NS_FAILED(Preferences::GetBool(warningPref.get(), &warn))) {
    1176             :       // no scheme-specific value, check the default
    1177           0 :       warn = Preferences::GetBool(kExternalWarningDefaultPref, true);
    1178             :     }
    1179           0 :     aHandlerInfo->SetAlwaysAskBeforeHandling(warn);
    1180             :   } else {
    1181             :     // If no OS default existed, we set the preferred action to alwaysAsk. 
    1182             :     // This really means not initialized (i.e. there's no available handler)
    1183             :     // to all the code...
    1184           0 :     aHandlerInfo->SetPreferredAction(nsIHandlerInfo::alwaysAsk);
    1185             :   }
    1186             : 
    1187           0 :   return NS_OK;
    1188             : }
    1189             :  
    1190             : // XPCOM profile change observer
    1191             : NS_IMETHODIMP
    1192           0 : nsExternalHelperAppService::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData )
    1193             : {
    1194           0 :   if (!strcmp(aTopic, "profile-before-change")) {
    1195           0 :     ExpungeTemporaryFiles();
    1196           0 :   } else if (!strcmp(aTopic, "last-pb-context-exited")) {
    1197           0 :     ExpungeTemporaryPrivateFiles();
    1198             :   }
    1199           0 :   return NS_OK;
    1200             : }
    1201             : 
    1202             : //////////////////////////////////////////////////////////////////////////////////////////////////////
    1203             : // begin external app handler implementation 
    1204             : //////////////////////////////////////////////////////////////////////////////////////////////////////
    1205             : 
    1206           0 : NS_IMPL_ADDREF(nsExternalAppHandler)
    1207           0 : NS_IMPL_RELEASE(nsExternalAppHandler)
    1208             : 
    1209           0 : NS_INTERFACE_MAP_BEGIN(nsExternalAppHandler)
    1210           0 :    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
    1211           0 :    NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
    1212           0 :    NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
    1213           0 :    NS_INTERFACE_MAP_ENTRY(nsIHelperAppLauncher)
    1214           0 :    NS_INTERFACE_MAP_ENTRY(nsICancelable)
    1215           0 :    NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
    1216           0 :    NS_INTERFACE_MAP_ENTRY(nsIBackgroundFileSaverObserver)
    1217           0 : NS_INTERFACE_MAP_END_THREADSAFE
    1218             : 
    1219           0 : nsExternalAppHandler::nsExternalAppHandler(nsIMIMEInfo * aMIMEInfo,
    1220             :                                            const nsACString& aTempFileExtension,
    1221             :                                            nsIInterfaceRequestor* aContentContext,
    1222             :                                            nsIInterfaceRequestor* aWindowContext,
    1223             :                                            nsExternalHelperAppService *aExtProtSvc,
    1224             :                                            const nsAString& aSuggestedFilename,
    1225           0 :                                            uint32_t aReason, bool aForceSave)
    1226             : : mMimeInfo(aMIMEInfo)
    1227             : , mContentContext(aContentContext)
    1228             : , mWindowContext(aWindowContext)
    1229             : , mWindowToClose(nullptr)
    1230             : , mSuggestedFileName(aSuggestedFilename)
    1231             : , mForceSave(aForceSave)
    1232             : , mCanceled(false)
    1233             : , mShouldCloseWindow(false)
    1234             : , mStopRequestIssued(false)
    1235             : , mReason(aReason)
    1236             : , mContentLength(-1)
    1237             : , mProgress(0)
    1238             : , mSaver(nullptr)
    1239             : , mDialogProgressListener(nullptr)
    1240             : , mTransfer(nullptr)
    1241             : , mRequest(nullptr)
    1242           0 : , mExtProtSvc(aExtProtSvc)
    1243             : {
    1244             : 
    1245             :   // make sure the extention includes the '.'
    1246           0 :   if (!aTempFileExtension.IsEmpty() && aTempFileExtension.First() != '.')
    1247           0 :     mTempFileExtension = char16_t('.');
    1248           0 :   AppendUTF8toUTF16(aTempFileExtension, mTempFileExtension);
    1249             : 
    1250             :   // replace platform specific path separator and illegal characters to avoid any confusion
    1251           0 :   mSuggestedFileName.ReplaceChar(KNOWN_PATH_SEPARATORS FILE_ILLEGAL_CHARACTERS, '_');
    1252           0 :   mTempFileExtension.ReplaceChar(KNOWN_PATH_SEPARATORS FILE_ILLEGAL_CHARACTERS, '_');
    1253             : 
    1254             :   // Remove unsafe bidi characters which might have spoofing implications (bug 511521).
    1255             :   const char16_t unsafeBidiCharacters[] = {
    1256             :     char16_t(0x061c), // Arabic Letter Mark
    1257             :     char16_t(0x200e), // Left-to-Right Mark
    1258             :     char16_t(0x200f), // Right-to-Left Mark
    1259             :     char16_t(0x202a), // Left-to-Right Embedding
    1260             :     char16_t(0x202b), // Right-to-Left Embedding
    1261             :     char16_t(0x202c), // Pop Directional Formatting
    1262             :     char16_t(0x202d), // Left-to-Right Override
    1263             :     char16_t(0x202e), // Right-to-Left Override
    1264             :     char16_t(0x2066), // Left-to-Right Isolate
    1265             :     char16_t(0x2067), // Right-to-Left Isolate
    1266             :     char16_t(0x2068), // First Strong Isolate
    1267             :     char16_t(0x2069), // Pop Directional Isolate
    1268             :     char16_t(0)
    1269           0 :   };
    1270           0 :   mSuggestedFileName.ReplaceChar(unsafeBidiCharacters, '_');
    1271           0 :   mTempFileExtension.ReplaceChar(unsafeBidiCharacters, '_');
    1272             : 
    1273             :   // Make sure extension is correct.
    1274           0 :   EnsureSuggestedFileName();
    1275             : 
    1276           0 :   mBufferSize = Preferences::GetUint("network.buffer.cache.size", 4096);
    1277           0 : }
    1278             : 
    1279           0 : nsExternalAppHandler::~nsExternalAppHandler()
    1280             : {
    1281           0 :   MOZ_ASSERT(!mSaver, "Saver should hold a reference to us until deleted");
    1282           0 : }
    1283             : 
    1284             : void
    1285           0 : nsExternalAppHandler::DidDivertRequest(nsIRequest *request)
    1286             : {
    1287           0 :   MOZ_ASSERT(XRE_IsContentProcess(), "in child process");
    1288             :   // Remove our request from the child loadGroup
    1289           0 :   RetargetLoadNotifications(request);
    1290           0 : }
    1291             : 
    1292           0 : NS_IMETHODIMP nsExternalAppHandler::SetWebProgressListener(nsIWebProgressListener2 * aWebProgressListener)
    1293             : {
    1294             :   // This is always called by nsHelperDlg.js. Go ahead and register the
    1295             :   // progress listener. At this point, we don't have mTransfer.
    1296           0 :   mDialogProgressListener = aWebProgressListener;
    1297           0 :   return NS_OK;
    1298             : }
    1299             : 
    1300           0 : NS_IMETHODIMP nsExternalAppHandler::GetTargetFile(nsIFile** aTarget)
    1301             : {
    1302           0 :   if (mFinalFileDestination)
    1303           0 :     *aTarget = mFinalFileDestination;
    1304             :   else
    1305           0 :     *aTarget = mTempFile;
    1306             : 
    1307           0 :   NS_IF_ADDREF(*aTarget);
    1308           0 :   return NS_OK;
    1309             : }
    1310             : 
    1311           0 : NS_IMETHODIMP nsExternalAppHandler::GetTargetFileIsExecutable(bool *aExec)
    1312             : {
    1313             :   // Use the real target if it's been set
    1314           0 :   if (mFinalFileDestination)
    1315           0 :     return mFinalFileDestination->IsExecutable(aExec);
    1316             : 
    1317             :   // Otherwise, use the stored executable-ness of the temporary
    1318           0 :   *aExec = mTempFileIsExecutable;
    1319           0 :   return NS_OK;
    1320             : }
    1321             : 
    1322           0 : NS_IMETHODIMP nsExternalAppHandler::GetTimeDownloadStarted(PRTime* aTime)
    1323             : {
    1324           0 :   *aTime = mTimeDownloadStarted;
    1325           0 :   return NS_OK;
    1326             : }
    1327             : 
    1328           0 : NS_IMETHODIMP nsExternalAppHandler::GetContentLength(int64_t *aContentLength)
    1329             : {
    1330           0 :   *aContentLength = mContentLength;
    1331           0 :   return NS_OK;
    1332             : }
    1333             : 
    1334           0 : void nsExternalAppHandler::RetargetLoadNotifications(nsIRequest *request)
    1335             : {
    1336             :   // we are going to run the downloading of the helper app in our own little docloader / load group context. 
    1337             :   // so go ahead and force the creation of a load group and doc loader for us to use...
    1338           0 :   nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
    1339           0 :   if (!aChannel)
    1340           0 :     return;
    1341             : 
    1342             :   // we need to store off the original (pre redirect!) channel that initiated the load. We do
    1343             :   // this so later on, we can pass any refresh urls associated with the original channel back to the 
    1344             :   // window context which started the whole process. More comments about that are listed below....
    1345             :   // HACK ALERT: it's pretty bogus that we are getting the document channel from the doc loader. 
    1346             :   // ideally we should be able to just use mChannel (the channel we are extracting content from) or
    1347             :   // the default load channel associated with the original load group. Unfortunately because
    1348             :   // a redirect may have occurred, the doc loader is the only one with a ptr to the original channel 
    1349             :   // which is what we really want....
    1350             : 
    1351             :   // Note that we need to do this before removing aChannel from the loadgroup,
    1352             :   // since that would mess with the original channel on the loader.
    1353             :   nsCOMPtr<nsIDocumentLoader> origContextLoader =
    1354           0 :     do_GetInterface(mContentContext);
    1355           0 :   if (origContextLoader) {
    1356           0 :     origContextLoader->GetDocumentChannel(getter_AddRefs(mOriginalChannel));
    1357             :   }
    1358             : 
    1359           0 :   bool isPrivate = NS_UsePrivateBrowsing(aChannel);
    1360             : 
    1361           0 :   nsCOMPtr<nsILoadGroup> oldLoadGroup;
    1362           0 :   aChannel->GetLoadGroup(getter_AddRefs(oldLoadGroup));
    1363             : 
    1364           0 :   if(oldLoadGroup) {
    1365           0 :     oldLoadGroup->RemoveRequest(request, nullptr, NS_BINDING_RETARGETED);
    1366             :   }
    1367             :       
    1368           0 :   aChannel->SetLoadGroup(nullptr);
    1369           0 :   aChannel->SetNotificationCallbacks(nullptr);
    1370             : 
    1371           0 :   nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(aChannel);
    1372           0 :   if (pbChannel) {
    1373           0 :     pbChannel->SetPrivate(isPrivate);
    1374             :   }
    1375             : }
    1376             : 
    1377             : /**
    1378             :  * Make mTempFileExtension contain an extension exactly when its previous value
    1379             :  * is different from mSuggestedFileName's extension, so that it can be appended
    1380             :  * to mSuggestedFileName and form a valid, useful leaf name.
    1381             :  * This is required so that the (renamed) temporary file has the correct extension
    1382             :  * after downloading to make sure the OS will launch the application corresponding
    1383             :  * to the MIME type (which was used to calculate mTempFileExtension).  This prevents
    1384             :  * a cgi-script named foobar.exe that returns application/zip from being named
    1385             :  * foobar.exe and executed as an executable file. It also blocks content that
    1386             :  * a web site might provide with a content-disposition header indicating
    1387             :  * filename="foobar.exe" from being downloaded to a file with extension .exe
    1388             :  * and executed.
    1389             :  */
    1390           0 : void nsExternalAppHandler::EnsureSuggestedFileName()
    1391             : {
    1392             :   // Make sure there is a mTempFileExtension (not "" or ".").
    1393             :   // Remember that mTempFileExtension will always have the leading "."
    1394             :   // (the check for empty is just to be safe).
    1395           0 :   if (mTempFileExtension.Length() > 1)
    1396             :   {
    1397             :     // Get mSuggestedFileName's current extension.
    1398           0 :     nsAutoString fileExt;
    1399           0 :     int32_t pos = mSuggestedFileName.RFindChar('.');
    1400           0 :     if (pos != kNotFound)
    1401           0 :       mSuggestedFileName.Right(fileExt, mSuggestedFileName.Length() - pos);
    1402             : 
    1403             :     // Now, compare fileExt to mTempFileExtension.
    1404           0 :     if (fileExt.Equals(mTempFileExtension, nsCaseInsensitiveStringComparator()))
    1405             :     {
    1406             :       // Matches -> mTempFileExtension can be empty
    1407           0 :       mTempFileExtension.Truncate();
    1408             :     }
    1409             :   }
    1410           0 : }
    1411             : 
    1412           0 : nsresult nsExternalAppHandler::SetUpTempFile(nsIChannel * aChannel)
    1413             : {
    1414             :   // First we need to try to get the destination directory for the temporary
    1415             :   // file.
    1416           0 :   nsresult rv = GetDownloadDirectory(getter_AddRefs(mTempFile));
    1417           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1418             : 
    1419             :   // At this point, we do not have a filename for the temp file.  For security
    1420             :   // purposes, this cannot be predictable, so we must use a cryptographic
    1421             :   // quality PRNG to generate one.
    1422             :   // We will request raw random bytes, and transform that to a base64 string,
    1423             :   // as all characters from the base64 set are acceptable for filenames.  For
    1424             :   // each three bytes of random data, we will get four bytes of ASCII.  Request
    1425             :   // a bit more, to be safe, and truncate to the length we want in the end.
    1426             : 
    1427           0 :   const uint32_t wantedFileNameLength = 8;
    1428             :   const uint32_t requiredBytesLength =
    1429           0 :     static_cast<uint32_t>((wantedFileNameLength + 1) / 4 * 3);
    1430             : 
    1431             :   nsCOMPtr<nsIRandomGenerator> rg =
    1432           0 :     do_GetService("@mozilla.org/security/random-generator;1", &rv);
    1433           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1434             : 
    1435             :   uint8_t *buffer;
    1436           0 :   rv = rg->GenerateRandomBytes(requiredBytesLength, &buffer);
    1437           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1438             : 
    1439           0 :   nsAutoCString tempLeafName;
    1440           0 :   nsDependentCSubstring randomData(reinterpret_cast<const char*>(buffer), requiredBytesLength);
    1441           0 :   rv = Base64Encode(randomData, tempLeafName);
    1442           0 :   free(buffer);
    1443           0 :   buffer = nullptr;
    1444           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1445             : 
    1446           0 :   tempLeafName.Truncate(wantedFileNameLength);
    1447             : 
    1448             :   // Base64 characters are alphanumeric (a-zA-Z0-9) and '+' and '/', so we need
    1449             :   // to replace illegal characters -- notably '/'
    1450           0 :   tempLeafName.ReplaceChar(KNOWN_PATH_SEPARATORS FILE_ILLEGAL_CHARACTERS, '_');
    1451             : 
    1452             :   // now append our extension.
    1453           0 :   nsAutoCString ext;
    1454           0 :   mMimeInfo->GetPrimaryExtension(ext);
    1455           0 :   if (!ext.IsEmpty()) {
    1456           0 :     ext.ReplaceChar(KNOWN_PATH_SEPARATORS FILE_ILLEGAL_CHARACTERS, '_');
    1457           0 :     if (ext.First() != '.')
    1458           0 :       tempLeafName.Append('.');
    1459           0 :     tempLeafName.Append(ext);
    1460             :   }
    1461             : 
    1462             :   // We need to temporarily create a dummy file with the correct
    1463             :   // file extension to determine the executable-ness, so do this before adding
    1464             :   // the extra .part extension.
    1465           0 :   nsCOMPtr<nsIFile> dummyFile;
    1466           0 :   rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(dummyFile));
    1467           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1468             : 
    1469             :   // Set the file name without .part
    1470           0 :   rv = dummyFile->Append(NS_ConvertUTF8toUTF16(tempLeafName));
    1471           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1472           0 :   rv = dummyFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
    1473           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1474             : 
    1475             :   // Store executable-ness then delete
    1476           0 :   dummyFile->IsExecutable(&mTempFileIsExecutable);
    1477           0 :   dummyFile->Remove(false);
    1478             : 
    1479             :   // Add an additional .part to prevent the OS from running this file in the
    1480             :   // default application.
    1481           0 :   tempLeafName.AppendLiteral(".part");
    1482             : 
    1483           0 :   rv = mTempFile->Append(NS_ConvertUTF8toUTF16(tempLeafName));
    1484             :   // make this file unique!!!
    1485           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1486           0 :   rv = mTempFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
    1487           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1488             : 
    1489             :   // Now save the temp leaf name, minus the ".part" bit, so we can use it later.
    1490             :   // This is a bit broken in the case when createUnique actually had to append
    1491             :   // some numbers, because then we now have a filename like foo.bar-1.part and
    1492             :   // we'll end up with foo.bar-1.bar as our final filename if we end up using
    1493             :   // this.  But the other options are all bad too....  Ideally we'd have a way
    1494             :   // to tell createUnique to put its unique marker before the extension that
    1495             :   // comes before ".part" or something.
    1496           0 :   rv = mTempFile->GetLeafName(mTempLeafName);
    1497           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1498             : 
    1499           0 :   NS_ENSURE_TRUE(StringEndsWith(mTempLeafName, NS_LITERAL_STRING(".part")),
    1500             :                  NS_ERROR_UNEXPECTED);
    1501             : 
    1502             :   // Strip off the ".part" from mTempLeafName
    1503           0 :   mTempLeafName.Truncate(mTempLeafName.Length() - ArrayLength(".part") + 1);
    1504             : 
    1505           0 :   MOZ_ASSERT(!mSaver, "Output file initialization called more than once!");
    1506           0 :   mSaver = do_CreateInstance(NS_BACKGROUNDFILESAVERSTREAMLISTENER_CONTRACTID,
    1507           0 :                              &rv);
    1508           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1509             : 
    1510           0 :   rv = mSaver->SetObserver(this);
    1511           0 :   if (NS_FAILED(rv)) {
    1512           0 :     mSaver = nullptr;
    1513           0 :     return rv;
    1514             :   }
    1515             : 
    1516           0 :   rv = mSaver->EnableSha256();
    1517           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1518             : 
    1519           0 :   rv = mSaver->EnableSignatureInfo();
    1520           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1521           0 :   LOG(("Enabled hashing and signature verification"));
    1522             : 
    1523           0 :   rv = mSaver->SetTarget(mTempFile, false);
    1524           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1525             : 
    1526           0 :   return rv;
    1527             : }
    1528             : 
    1529             : void
    1530           0 : nsExternalAppHandler::MaybeApplyDecodingForExtension(nsIRequest *aRequest)
    1531             : {
    1532           0 :   MOZ_ASSERT(aRequest);
    1533             : 
    1534           0 :   nsCOMPtr<nsIEncodedChannel> encChannel = do_QueryInterface(aRequest);
    1535           0 :   if (!encChannel) {
    1536           0 :     return;
    1537             :   }
    1538             : 
    1539             :   // Turn off content encoding conversions if needed
    1540           0 :   bool applyConversion = true;
    1541             : 
    1542             :   // First, check to see if conversion is already disabled.  If so, we
    1543             :   // have nothing to do here.
    1544           0 :   encChannel->GetApplyConversion(&applyConversion);
    1545           0 :   if (!applyConversion) {
    1546           0 :     return;
    1547             :   }
    1548             : 
    1549           0 :   nsCOMPtr<nsIURL> sourceURL(do_QueryInterface(mSourceUrl));
    1550           0 :   if (sourceURL)
    1551             :   {
    1552           0 :     nsAutoCString extension;
    1553           0 :     sourceURL->GetFileExtension(extension);
    1554           0 :     if (!extension.IsEmpty())
    1555             :     {
    1556           0 :       nsCOMPtr<nsIUTF8StringEnumerator> encEnum;
    1557           0 :       encChannel->GetContentEncodings(getter_AddRefs(encEnum));
    1558           0 :       if (encEnum)
    1559             :       {
    1560             :         bool hasMore;
    1561           0 :         nsresult rv = encEnum->HasMore(&hasMore);
    1562           0 :         if (NS_SUCCEEDED(rv) && hasMore)
    1563             :         {
    1564           0 :           nsAutoCString encType;
    1565           0 :           rv = encEnum->GetNext(encType);
    1566           0 :           if (NS_SUCCEEDED(rv) && !encType.IsEmpty())
    1567             :           {
    1568           0 :             MOZ_ASSERT(mExtProtSvc);
    1569           0 :             mExtProtSvc->ApplyDecodingForExtension(extension, encType,
    1570           0 :                                                    &applyConversion);
    1571             :           }
    1572             :         }
    1573             :       }
    1574             :     }
    1575             :   }
    1576             : 
    1577           0 :   encChannel->SetApplyConversion( applyConversion );
    1578           0 :   return;
    1579             : }
    1580             : 
    1581           0 : NS_IMETHODIMP nsExternalAppHandler::OnStartRequest(nsIRequest *request, nsISupports * aCtxt)
    1582             : {
    1583           0 :   NS_PRECONDITION(request, "OnStartRequest without request?");
    1584             : 
    1585             :   // Set mTimeDownloadStarted here as the download has already started and
    1586             :   // we want to record the start time before showing the filepicker.
    1587           0 :   mTimeDownloadStarted = PR_Now();
    1588             : 
    1589           0 :   mRequest = request;
    1590             : 
    1591           0 :   nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
    1592             : 
    1593             :   nsresult rv;
    1594             : 
    1595           0 :   nsCOMPtr<nsIFileChannel> fileChan(do_QueryInterface(request));
    1596           0 :   mIsFileChannel = fileChan != nullptr;
    1597           0 :   if (!mIsFileChannel) {
    1598             :     // It's possible that this request came from the child process and the
    1599             :     // file channel actually lives there. If this returns true, then our
    1600             :     // mSourceUrl will be an nsIFileURL anyway.
    1601           0 :     nsCOMPtr<dom::nsIExternalHelperAppParent> parent(do_QueryInterface(request));
    1602           0 :     mIsFileChannel = parent && parent->WasFileChannel();
    1603             :   }
    1604             : 
    1605             :   // Get content length
    1606           0 :   if (aChannel) {
    1607           0 :     aChannel->GetContentLength(&mContentLength);
    1608             :   }
    1609             : 
    1610           0 :   nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(request, &rv));
    1611             :   // Determine whether a new window was opened specifically for this request
    1612           0 :   if (props) {
    1613           0 :     bool tmp = false;
    1614           0 :     props->GetPropertyAsBool(NS_LITERAL_STRING("docshell.newWindowTarget"),
    1615           0 :                              &tmp);
    1616           0 :     mShouldCloseWindow = tmp;
    1617             :   }
    1618             : 
    1619             :   // Now get the URI
    1620           0 :   if (aChannel) {
    1621           0 :     aChannel->GetURI(getter_AddRefs(mSourceUrl));
    1622             :   }
    1623             : 
    1624             :   // retarget all load notifications to our docloader instead of the original window's docloader...
    1625           0 :   RetargetLoadNotifications(request);
    1626             : 
    1627             :   // Check to see if there is a refresh header on the original channel.
    1628           0 :   if (mOriginalChannel) {
    1629           0 :     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mOriginalChannel));
    1630           0 :     if (httpChannel) {
    1631           0 :       nsAutoCString refreshHeader;
    1632           0 :       Unused << httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("refresh"),
    1633           0 :                                                refreshHeader);
    1634           0 :       if (!refreshHeader.IsEmpty()) {
    1635           0 :         mShouldCloseWindow = false;
    1636             :       }
    1637             :     }
    1638             :   }
    1639             : 
    1640             :   // Close the underlying DOMWindow if there is no refresh header
    1641             :   // and it was opened specifically for the download
    1642           0 :   MaybeCloseWindow();
    1643             : 
    1644             :   // In an IPC setting, we're allowing the child process, here, to make
    1645             :   // decisions about decoding the channel (e.g. decompression).  It will
    1646             :   // still forward the decoded (uncompressed) data back to the parent.
    1647             :   // Con: Uncompressed data means more IPC overhead.
    1648             :   // Pros: ExternalHelperAppParent doesn't need to implement nsIEncodedChannel.
    1649             :   //       Parent process doesn't need to expect CPU time on decompression.
    1650           0 :   MaybeApplyDecodingForExtension(aChannel);
    1651             : 
    1652             :   // At this point, the child process has done everything it can usefully do
    1653             :   // for OnStartRequest.
    1654           0 :   if (XRE_IsContentProcess()) {
    1655           0 :     return NS_OK;
    1656             :   }
    1657             : 
    1658           0 :   rv = SetUpTempFile(aChannel);
    1659           0 :   if (NS_FAILED(rv)) {
    1660           0 :     nsresult transferError = rv;
    1661             : 
    1662           0 :     rv = CreateFailedTransfer(aChannel && NS_UsePrivateBrowsing(aChannel));
    1663           0 :     if (NS_FAILED(rv)) {
    1664           0 :       LOG(("Failed to create transfer to report failure."
    1665             :            "Will fallback to prompter!"));
    1666             :     }
    1667             : 
    1668           0 :     mCanceled = true;
    1669           0 :     request->Cancel(transferError);
    1670             : 
    1671           0 :     nsAutoString path;
    1672           0 :     if (mTempFile)
    1673           0 :       mTempFile->GetPath(path);
    1674             : 
    1675           0 :     SendStatusChange(kWriteError, transferError, request, path);
    1676             : 
    1677           0 :     return NS_OK;
    1678             :   }
    1679             : 
    1680             :   // Inform channel it is open on behalf of a download to throttle it during
    1681             :   // page loads and prevent its caching.
    1682           0 :   nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(aChannel);
    1683           0 :   if (httpInternal) {
    1684           0 :     rv = httpInternal->SetChannelIsForDownload(true);
    1685           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    1686             :   }
    1687             : 
    1688             :   // now that the temp file is set up, find out if we need to invoke a dialog
    1689             :   // asking the user what they want us to do with this content...
    1690             : 
    1691             :   // We can get here for three reasons: "can't handle", "sniffed type", or
    1692             :   // "server sent content-disposition:attachment".  In the first case we want
    1693             :   // to honor the user's "always ask" pref; in the other two cases we want to
    1694             :   // honor it only if the default action is "save".  Opening attachments in
    1695             :   // helper apps by default breaks some websites (especially if the attachment
    1696             :   // is one part of a multipart document).  Opening sniffed content in helper
    1697             :   // apps by default introduces security holes that we'd rather not have.
    1698             : 
    1699             :   // So let's find out whether the user wants to be prompted.  If he does not,
    1700             :   // check mReason and the preferred action to see what we should do.
    1701             : 
    1702           0 :   bool alwaysAsk = true;
    1703           0 :   mMimeInfo->GetAlwaysAskBeforeHandling(&alwaysAsk);
    1704           0 :   if (alwaysAsk) {
    1705             :     // But we *don't* ask if this mimeInfo didn't come from
    1706             :     // our user configuration datastore and the user has said
    1707             :     // at some point in the distant past that they don't
    1708             :     // want to be asked.  The latter fact would have been
    1709             :     // stored in pref strings back in the old days.
    1710             : 
    1711           0 :     bool mimeTypeIsInDatastore = false;
    1712           0 :     nsCOMPtr<nsIHandlerService> handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID);
    1713           0 :     if (handlerSvc) {
    1714           0 :       handlerSvc->Exists(mMimeInfo, &mimeTypeIsInDatastore);
    1715             :     }
    1716           0 :     if (!handlerSvc || !mimeTypeIsInDatastore) {
    1717           0 :       nsAutoCString MIMEType;
    1718           0 :       mMimeInfo->GetMIMEType(MIMEType);
    1719           0 :       if (!GetNeverAskFlagFromPref(NEVER_ASK_FOR_SAVE_TO_DISK_PREF, MIMEType.get())) {
    1720             :         // Don't need to ask after all.
    1721           0 :         alwaysAsk = false;
    1722             :         // Make sure action matches pref (save to disk).
    1723           0 :         mMimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
    1724           0 :       } else if (!GetNeverAskFlagFromPref(NEVER_ASK_FOR_OPEN_FILE_PREF, MIMEType.get())) {
    1725             :         // Don't need to ask after all.
    1726           0 :         alwaysAsk = false;
    1727             :       }
    1728             :     }
    1729             :   }
    1730             : 
    1731           0 :   int32_t action = nsIMIMEInfo::saveToDisk;
    1732           0 :   mMimeInfo->GetPreferredAction( &action );
    1733             : 
    1734             :   // OK, now check why we're here
    1735           0 :   if (!alwaysAsk && mReason != nsIHelperAppLauncherDialog::REASON_CANTHANDLE) {
    1736             :     // Force asking if we're not saving.  See comment back when we fetched the
    1737             :     // alwaysAsk boolean for details.
    1738           0 :     alwaysAsk = (action != nsIMIMEInfo::saveToDisk);
    1739             :   }
    1740             : 
    1741             :   // if we were told that we _must_ save to disk without asking, all the stuff
    1742             :   // before this is irrelevant; override it
    1743           0 :   if (mForceSave) {
    1744           0 :     alwaysAsk = false;
    1745           0 :     action = nsIMIMEInfo::saveToDisk;
    1746             :   }
    1747             :   
    1748           0 :   if (alwaysAsk)
    1749             :   {
    1750             :     // Display the dialog
    1751           0 :     mDialog = do_CreateInstance(NS_HELPERAPPLAUNCHERDLG_CONTRACTID, &rv);
    1752           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1753             : 
    1754             :     // this will create a reference cycle (the dialog holds a reference to us as
    1755             :     // nsIHelperAppLauncher), which will be broken in Cancel or CreateTransfer.
    1756           0 :     rv = mDialog->Show(this, GetDialogParent(), mReason);
    1757             : 
    1758             :     // what do we do if the dialog failed? I guess we should call Cancel and abort the load....
    1759             :   }
    1760             :   else
    1761             :   {
    1762             : 
    1763             :     // We need to do the save/open immediately, then.
    1764             : #ifdef XP_WIN
    1765             :     /* We need to see whether the file we've got here could be
    1766             :      * executable.  If it could, we had better not try to open it!
    1767             :      * We can skip this check, though, if we have a setting to open in a
    1768             :      * helper app.
    1769             :      * This code mirrors the code in
    1770             :      * nsExternalAppHandler::LaunchWithApplication so that what we
    1771             :      * test here is as close as possible to what will really be
    1772             :      * happening if we decide to execute
    1773             :      */
    1774             :     nsCOMPtr<nsIHandlerApp> prefApp;
    1775             :     mMimeInfo->GetPreferredApplicationHandler(getter_AddRefs(prefApp));
    1776             :     if (action != nsIMIMEInfo::useHelperApp || !prefApp) {
    1777             :       nsCOMPtr<nsIFile> fileToTest;
    1778             :       GetTargetFile(getter_AddRefs(fileToTest));
    1779             :       if (fileToTest) {
    1780             :         bool isExecutable;
    1781             :         rv = fileToTest->IsExecutable(&isExecutable);
    1782             :         if (NS_FAILED(rv) || isExecutable) {  // checking NS_FAILED, because paranoia is good
    1783             :           action = nsIMIMEInfo::saveToDisk;
    1784             :         }
    1785             :       } else {   // Paranoia is good here too, though this really should not happen
    1786             :         NS_WARNING("GetDownloadInfo returned a null file after the temp file has been set up! ");
    1787             :         action = nsIMIMEInfo::saveToDisk;
    1788             :       }
    1789             :     }
    1790             : 
    1791             : #endif
    1792           0 :     if (action == nsIMIMEInfo::useHelperApp ||
    1793           0 :         action == nsIMIMEInfo::useSystemDefault) {
    1794           0 :         rv = LaunchWithApplication(nullptr, false);
    1795             :     } else {
    1796           0 :         rv = SaveToDisk(nullptr, false);
    1797             :     }
    1798             :   }
    1799             : 
    1800           0 :   return NS_OK;
    1801             : }
    1802             : 
    1803             : // Convert error info into proper message text and send OnStatusChange
    1804             : // notification to the dialog progress listener or nsITransfer implementation.
    1805           0 : void nsExternalAppHandler::SendStatusChange(ErrorType type, nsresult rv, nsIRequest *aRequest, const nsString& path)
    1806             : {
    1807           0 :     nsAutoString msgId;
    1808           0 :     switch (rv) {
    1809             :     case NS_ERROR_OUT_OF_MEMORY:
    1810             :         // No memory
    1811           0 :         msgId.AssignLiteral("noMemory");
    1812           0 :         break;
    1813             : 
    1814             :     case NS_ERROR_FILE_DISK_FULL:
    1815             :     case NS_ERROR_FILE_NO_DEVICE_SPACE:
    1816             :         // Out of space on target volume.
    1817           0 :         msgId.AssignLiteral("diskFull");
    1818           0 :         break;
    1819             : 
    1820             :     case NS_ERROR_FILE_READ_ONLY:
    1821             :         // Attempt to write to read/only file.
    1822           0 :         msgId.AssignLiteral("readOnly");
    1823           0 :         break;
    1824             : 
    1825             :     case NS_ERROR_FILE_ACCESS_DENIED:
    1826           0 :         if (type == kWriteError) {
    1827             :           // Attempt to write without sufficient permissions.
    1828             : #if defined(ANDROID)
    1829             :           // On Android (and Gonk), this means the SD card is present but
    1830             :           // unavailable (read-only).
    1831             :           msgId.AssignLiteral("SDAccessErrorCardReadOnly");
    1832             : #else
    1833           0 :           msgId.AssignLiteral("accessError");
    1834             : #endif
    1835             :         } else {
    1836           0 :           msgId.AssignLiteral("launchError");
    1837             :         }
    1838           0 :         break;
    1839             : 
    1840             :     case NS_ERROR_FILE_NOT_FOUND:
    1841             :     case NS_ERROR_FILE_TARGET_DOES_NOT_EXIST:
    1842             :     case NS_ERROR_FILE_UNRECOGNIZED_PATH:
    1843             :         // Helper app not found, let's verify this happened on launch
    1844           0 :         if (type == kLaunchError) {
    1845           0 :           msgId.AssignLiteral("helperAppNotFound");
    1846           0 :           break;
    1847             :         }
    1848             : #if defined(ANDROID)
    1849             :         else if (type == kWriteError) {
    1850             :           // On Android (and Gonk), this means the SD card is missing (not in
    1851             :           // SD slot).
    1852             :           msgId.AssignLiteral("SDAccessErrorCardMissing");
    1853             :           break;
    1854             :         }
    1855             : #endif
    1856             :         MOZ_FALLTHROUGH;
    1857             : 
    1858             :     default:
    1859             :         // Generic read/write/launch error message.
    1860           0 :         switch (type) {
    1861             :         case kReadError:
    1862           0 :           msgId.AssignLiteral("readError");
    1863           0 :           break;
    1864             :         case kWriteError:
    1865           0 :           msgId.AssignLiteral("writeError");
    1866           0 :           break;
    1867             :         case kLaunchError:
    1868           0 :           msgId.AssignLiteral("launchError");
    1869           0 :           break;
    1870             :         }
    1871           0 :         break;
    1872             :     }
    1873             : 
    1874           0 :     MOZ_LOG(nsExternalHelperAppService::mLog, LogLevel::Error,
    1875             :         ("Error: %s, type=%i, listener=0x%p, transfer=0x%p, rv=0x%08" PRIX32 "\n",
    1876             :          NS_LossyConvertUTF16toASCII(msgId).get(), type, mDialogProgressListener.get(), mTransfer.get(),
    1877             :          static_cast<uint32_t>(rv)));
    1878             : 
    1879           0 :     MOZ_LOG(nsExternalHelperAppService::mLog, LogLevel::Error,
    1880             :         ("       path='%s'\n", NS_ConvertUTF16toUTF8(path).get()));
    1881             : 
    1882             :     // Get properties file bundle and extract status string.
    1883             :     nsCOMPtr<nsIStringBundleService> stringService =
    1884           0 :         mozilla::services::GetStringBundleService();
    1885           0 :     if (stringService) {
    1886           0 :         nsCOMPtr<nsIStringBundle> bundle;
    1887           0 :         if (NS_SUCCEEDED(stringService->CreateBundle("chrome://global/locale/nsWebBrowserPersist.properties",
    1888             :                          getter_AddRefs(bundle)))) {
    1889           0 :             nsXPIDLString msgText;
    1890           0 :             const char16_t *strings[] = { path.get() };
    1891           0 :             if (NS_SUCCEEDED(bundle->FormatStringFromName(msgId.get(), strings, 1,
    1892             :                                                           getter_Copies(msgText)))) {
    1893           0 :               if (mDialogProgressListener) {
    1894             :                 // We have a listener, let it handle the error.
    1895           0 :                 mDialogProgressListener->OnStatusChange(nullptr, (type == kReadError) ? aRequest : nullptr, rv, msgText);
    1896           0 :               } else if (mTransfer) {
    1897           0 :                 mTransfer->OnStatusChange(nullptr, (type == kReadError) ? aRequest : nullptr, rv, msgText);
    1898           0 :               } else if (XRE_IsParentProcess()) {
    1899             :                 // We don't have a listener.  Simply show the alert ourselves.
    1900             :                 nsresult qiRv;
    1901           0 :                 nsCOMPtr<nsIPrompt> prompter(do_GetInterface(GetDialogParent(), &qiRv));
    1902           0 :                 nsXPIDLString title;
    1903           0 :                 bundle->FormatStringFromName(u"title",
    1904             :                                              strings,
    1905             :                                              1,
    1906           0 :                                              getter_Copies(title));
    1907             : 
    1908           0 :                 MOZ_LOG(nsExternalHelperAppService::mLog, LogLevel::Debug,
    1909             :                        ("mContentContext=0x%p, prompter=0x%p, qi rv=0x%08"
    1910             :                         PRIX32 ", title='%s', msg='%s'",
    1911             :                        mContentContext.get(),
    1912             :                        prompter.get(),
    1913             :                         static_cast<uint32_t>(qiRv),
    1914             :                        NS_ConvertUTF16toUTF8(title).get(),
    1915             :                        NS_ConvertUTF16toUTF8(msgText).get()));
    1916             : 
    1917             :                 // If we didn't have a prompter we will try and get a window
    1918             :                 // instead, get it's docshell and use it to alert the user.
    1919           0 :                 if (!prompter) {
    1920           0 :                   nsCOMPtr<nsPIDOMWindowOuter> window(do_GetInterface(GetDialogParent()));
    1921           0 :                   if (!window || !window->GetDocShell()) {
    1922           0 :                     return;
    1923             :                   }
    1924             : 
    1925           0 :                   prompter = do_GetInterface(window->GetDocShell(), &qiRv);
    1926             : 
    1927           0 :                   MOZ_LOG(nsExternalHelperAppService::mLog, LogLevel::Debug,
    1928             :                          ("No prompter from mContentContext, using DocShell, " \
    1929             :                           "window=0x%p, docShell=0x%p, " \
    1930             :                           "prompter=0x%p, qi rv=0x%08" PRIX32,
    1931             :                           window.get(),
    1932             :                           window->GetDocShell(),
    1933             :                           prompter.get(),
    1934             :                           static_cast<uint32_t>(qiRv)));
    1935             : 
    1936             :                   // If we still don't have a prompter, there's nothing else we
    1937             :                   // can do so just return.
    1938           0 :                   if (!prompter) {
    1939           0 :                     MOZ_LOG(nsExternalHelperAppService::mLog, LogLevel::Error,
    1940             :                            ("No prompter from DocShell, no way to alert user"));
    1941           0 :                     return;
    1942             :                   }
    1943             :                 }
    1944             : 
    1945             :                 // We should always have a prompter at this point.
    1946           0 :                 prompter->Alert(title, msgText);
    1947             :               }
    1948             :             }
    1949             :         }
    1950             :     }
    1951             : }
    1952             : 
    1953             : NS_IMETHODIMP
    1954           0 : nsExternalAppHandler::OnDataAvailable(nsIRequest *request, nsISupports * aCtxt,
    1955             :                                       nsIInputStream * inStr,
    1956             :                                       uint64_t sourceOffset, uint32_t count)
    1957             : {
    1958           0 :   nsresult rv = NS_OK;
    1959             :   // first, check to see if we've been canceled....
    1960           0 :   if (mCanceled || !mSaver) {
    1961             :     // then go cancel our underlying channel too
    1962           0 :     return request->Cancel(NS_BINDING_ABORTED);
    1963             :   }
    1964             : 
    1965             :   // read the data out of the stream and write it to the temp file.
    1966           0 :   if (count > 0) {
    1967           0 :     mProgress += count;
    1968             : 
    1969           0 :     nsCOMPtr<nsIStreamListener> saver = do_QueryInterface(mSaver);
    1970           0 :     rv = saver->OnDataAvailable(request, aCtxt, inStr, sourceOffset, count);
    1971           0 :     if (NS_SUCCEEDED(rv)) {
    1972             :       // Send progress notification.
    1973           0 :       if (mTransfer) {
    1974           0 :         mTransfer->OnProgressChange64(nullptr, request, mProgress,
    1975             :                                       mContentLength, mProgress,
    1976           0 :                                       mContentLength);
    1977             :       }
    1978             :     } else {
    1979             :       // An error occurred, notify listener.
    1980           0 :       nsAutoString tempFilePath;
    1981           0 :       if (mTempFile) {
    1982           0 :         mTempFile->GetPath(tempFilePath);
    1983             :       }
    1984           0 :       SendStatusChange(kReadError, rv, request, tempFilePath);
    1985             : 
    1986             :       // Cancel the download.
    1987           0 :       Cancel(rv);
    1988             :     }
    1989             :   }
    1990           0 :   return rv;
    1991             : }
    1992             : 
    1993           0 : NS_IMETHODIMP nsExternalAppHandler::OnStopRequest(nsIRequest *request, nsISupports *aCtxt,
    1994             :                                                   nsresult aStatus)
    1995             : {
    1996           0 :   LOG(("nsExternalAppHandler::OnStopRequest\n"
    1997             :        "  mCanceled=%d, mTransfer=0x%p, aStatus=0x%08" PRIX32 "\n",
    1998             :        mCanceled, mTransfer.get(), static_cast<uint32_t>(aStatus)));
    1999             : 
    2000           0 :   mStopRequestIssued = true;
    2001             : 
    2002             :   // Cancel if the request did not complete successfully.
    2003           0 :   if (!mCanceled && NS_FAILED(aStatus)) {
    2004             :     // Send error notification.
    2005           0 :     nsAutoString tempFilePath;
    2006           0 :     if (mTempFile)
    2007           0 :       mTempFile->GetPath(tempFilePath);
    2008           0 :     SendStatusChange( kReadError, aStatus, request, tempFilePath );
    2009             : 
    2010           0 :     Cancel(aStatus);
    2011             :   }
    2012             : 
    2013             :   // first, check to see if we've been canceled....
    2014           0 :   if (mCanceled || !mSaver) {
    2015           0 :     return NS_OK;
    2016             :   }
    2017             : 
    2018           0 :   return mSaver->Finish(NS_OK);
    2019             : }
    2020             : 
    2021             : NS_IMETHODIMP
    2022           0 : nsExternalAppHandler::OnTargetChange(nsIBackgroundFileSaver *aSaver,
    2023             :                                      nsIFile *aTarget)
    2024             : {
    2025           0 :   return NS_OK;
    2026             : }
    2027             : 
    2028             : NS_IMETHODIMP
    2029           0 : nsExternalAppHandler::OnSaveComplete(nsIBackgroundFileSaver *aSaver,
    2030             :                                      nsresult aStatus)
    2031             : {
    2032           0 :   LOG(("nsExternalAppHandler::OnSaveComplete\n"
    2033             :        "  aSaver=0x%p, aStatus=0x%08" PRIX32 ", mCanceled=%d, mTransfer=0x%p\n",
    2034             :        aSaver, static_cast<uint32_t>(aStatus), mCanceled, mTransfer.get()));
    2035             : 
    2036           0 :   if (!mCanceled) {
    2037             :     // Save the hash and signature information
    2038           0 :     (void)mSaver->GetSha256Hash(mHash);
    2039           0 :     (void)mSaver->GetSignatureInfo(getter_AddRefs(mSignatureInfo));
    2040             : 
    2041             :     // Free the reference that the saver keeps on us, even if we couldn't get
    2042             :     // the hash.
    2043           0 :     mSaver = nullptr;
    2044             : 
    2045             :     // Save the redirect information.
    2046           0 :     nsCOMPtr<nsIChannel> channel = do_QueryInterface(mRequest);
    2047           0 :     if (channel) {
    2048           0 :       nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
    2049           0 :       if (loadInfo) {
    2050           0 :         nsresult rv = NS_OK;
    2051             :         nsCOMPtr<nsIMutableArray> redirectChain =
    2052           0 :           do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
    2053           0 :         NS_ENSURE_SUCCESS(rv, rv);
    2054           0 :         LOG(("nsExternalAppHandler: Got %" PRIuSIZE " redirects\n",
    2055             :              loadInfo->RedirectChain().Length()));
    2056           0 :         for (nsIRedirectHistoryEntry* entry : loadInfo->RedirectChain()) {
    2057           0 :           redirectChain->AppendElement(entry, false);
    2058             :         }
    2059           0 :         mRedirects = redirectChain;
    2060             :       }
    2061             :     }
    2062             : 
    2063           0 :     if (NS_FAILED(aStatus)) {
    2064           0 :       nsAutoString path;
    2065           0 :       mTempFile->GetPath(path);
    2066             : 
    2067             :       // It may happen when e10s is enabled that there will be no transfer
    2068             :       // object available to communicate status as expected by the system.
    2069             :       // Let's try and create a temporary transfer object to take care of this
    2070             :       // for us, we'll fall back to using the prompt service if we absolutely
    2071             :       // have to.
    2072           0 :       if (!mTransfer) {
    2073             :         // We don't care if this fails.
    2074           0 :         CreateFailedTransfer(channel && NS_UsePrivateBrowsing(channel));
    2075             :       }
    2076             : 
    2077           0 :       SendStatusChange(kWriteError, aStatus, nullptr, path);
    2078           0 :       if (!mCanceled)
    2079           0 :         Cancel(aStatus);
    2080           0 :       return NS_OK;
    2081             :     }
    2082             :   }
    2083             : 
    2084             :   // Notify the transfer object that we are done if the user has chosen an
    2085             :   // action. If the user hasn't chosen an action, the progress listener
    2086             :   // (nsITransfer) will be notified in CreateTransfer.
    2087           0 :   if (mTransfer) {
    2088           0 :     NotifyTransfer(aStatus);
    2089             :   }
    2090             : 
    2091           0 :   return NS_OK;
    2092             : }
    2093             : 
    2094           0 : void nsExternalAppHandler::NotifyTransfer(nsresult aStatus)
    2095             : {
    2096           0 :   MOZ_ASSERT(NS_IsMainThread(), "Must notify on main thread");
    2097           0 :   MOZ_ASSERT(mTransfer, "We must have an nsITransfer");
    2098             : 
    2099           0 :   LOG(("Notifying progress listener"));
    2100             : 
    2101           0 :   if (NS_SUCCEEDED(aStatus)) {
    2102           0 :     (void)mTransfer->SetSha256Hash(mHash);
    2103           0 :     (void)mTransfer->SetSignatureInfo(mSignatureInfo);
    2104           0 :     (void)mTransfer->SetRedirects(mRedirects);
    2105           0 :     (void)mTransfer->OnProgressChange64(nullptr, nullptr, mProgress,
    2106           0 :       mContentLength, mProgress, mContentLength);
    2107             :   }
    2108             : 
    2109           0 :   (void)mTransfer->OnStateChange(nullptr, nullptr,
    2110             :     nsIWebProgressListener::STATE_STOP |
    2111             :     nsIWebProgressListener::STATE_IS_REQUEST |
    2112           0 :     nsIWebProgressListener::STATE_IS_NETWORK, aStatus);
    2113             : 
    2114             :   // This nsITransfer object holds a reference to us (we are its observer), so
    2115             :   // we need to release the reference to break a reference cycle (and therefore
    2116             :   // to prevent leaking).  We do this even if the previous calls failed.
    2117           0 :   mTransfer = nullptr;
    2118           0 : }
    2119             : 
    2120           0 : NS_IMETHODIMP nsExternalAppHandler::GetMIMEInfo(nsIMIMEInfo ** aMIMEInfo)
    2121             : {
    2122           0 :   *aMIMEInfo = mMimeInfo;
    2123           0 :   NS_ADDREF(*aMIMEInfo);
    2124           0 :   return NS_OK;
    2125             : }
    2126             : 
    2127           0 : NS_IMETHODIMP nsExternalAppHandler::GetSource(nsIURI ** aSourceURI)
    2128             : {
    2129           0 :   NS_ENSURE_ARG(aSourceURI);
    2130           0 :   *aSourceURI = mSourceUrl;
    2131           0 :   NS_IF_ADDREF(*aSourceURI);
    2132           0 :   return NS_OK;
    2133             : }
    2134             : 
    2135           0 : NS_IMETHODIMP nsExternalAppHandler::GetSuggestedFileName(nsAString& aSuggestedFileName)
    2136             : {
    2137           0 :   aSuggestedFileName = mSuggestedFileName;
    2138           0 :   return NS_OK;
    2139             : }
    2140             : 
    2141           0 : nsresult nsExternalAppHandler::CreateTransfer()
    2142             : {
    2143           0 :   LOG(("nsExternalAppHandler::CreateTransfer"));
    2144             : 
    2145           0 :   MOZ_ASSERT(NS_IsMainThread(), "Must create transfer on main thread");
    2146             :   // We are back from the helper app dialog (where the user chooses to save or
    2147             :   // open), but we aren't done processing the load. in this case, throw up a
    2148             :   // progress dialog so the user can see what's going on.
    2149             :   // Also, release our reference to mDialog. We don't need it anymore, and we
    2150             :   // need to break the reference cycle.
    2151           0 :   mDialog = nullptr;
    2152           0 :   if (!mDialogProgressListener) {
    2153           0 :     NS_WARNING("The dialog should nullify the dialog progress listener");
    2154             :   }
    2155             :   nsresult rv;
    2156             : 
    2157             :   // We must be able to create an nsITransfer object. If not, it doesn't matter
    2158             :   // much that we can't launch the helper application or save to disk. Work on
    2159             :   // a local copy rather than mTransfer until we know we succeeded, to make it
    2160             :   // clearer that this function is re-entrant.
    2161           0 :   nsCOMPtr<nsITransfer> transfer = do_CreateInstance(
    2162           0 :     NS_TRANSFER_CONTRACTID, &rv);
    2163           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2164             : 
    2165             :   // Initialize the download
    2166           0 :   nsCOMPtr<nsIURI> target;
    2167           0 :   rv = NS_NewFileURI(getter_AddRefs(target), mFinalFileDestination);
    2168           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2169             : 
    2170           0 :   nsCOMPtr<nsIChannel> channel = do_QueryInterface(mRequest);
    2171             : 
    2172           0 :   rv = transfer->Init(mSourceUrl, target, EmptyString(),
    2173             :                        mMimeInfo, mTimeDownloadStarted, mTempFile, this,
    2174           0 :                        channel && NS_UsePrivateBrowsing(channel));
    2175           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2176             : 
    2177             :   // Now let's add the download to history
    2178           0 :   nsCOMPtr<nsIDownloadHistory> dh(do_GetService(NS_DOWNLOADHISTORY_CONTRACTID));
    2179           0 :   if (dh) {
    2180           0 :     if (channel && !NS_UsePrivateBrowsing(channel)) {
    2181           0 :       nsCOMPtr<nsIURI> referrer;
    2182           0 :       NS_GetReferrerFromChannel(channel, getter_AddRefs(referrer));
    2183             : 
    2184           0 :       dh->AddDownload(mSourceUrl, referrer, mTimeDownloadStarted, target);
    2185             :     }
    2186             :   }
    2187             : 
    2188             :   // If we were cancelled since creating the transfer, just return. It is
    2189             :   // always ok to return NS_OK if we are cancelled. Callers of this function
    2190             :   // must call Cancel if CreateTransfer fails, but there's no need to cancel
    2191             :   // twice.
    2192           0 :   if (mCanceled) {
    2193           0 :     return NS_OK;
    2194             :   }
    2195           0 :   rv = transfer->OnStateChange(nullptr, mRequest,
    2196             :     nsIWebProgressListener::STATE_START |
    2197             :     nsIWebProgressListener::STATE_IS_REQUEST |
    2198           0 :     nsIWebProgressListener::STATE_IS_NETWORK, NS_OK);
    2199           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2200             : 
    2201           0 :   if (mCanceled) {
    2202           0 :     return NS_OK;
    2203             :   }
    2204             : 
    2205           0 :   mRequest = nullptr;
    2206             :   // Finally, save the transfer to mTransfer.
    2207           0 :   mTransfer = transfer;
    2208           0 :   transfer = nullptr;
    2209             : 
    2210             :   // While we were bringing up the progress dialog, we actually finished
    2211             :   // processing the url. If that's the case then mStopRequestIssued will be
    2212             :   // true and OnSaveComplete has been called.
    2213           0 :   if (mStopRequestIssued && !mSaver && mTransfer) {
    2214           0 :     NotifyTransfer(NS_OK);
    2215             :   }
    2216             : 
    2217           0 :   return rv;
    2218             : }
    2219             : 
    2220           0 : nsresult nsExternalAppHandler::CreateFailedTransfer(bool aIsPrivateBrowsing)
    2221             : {
    2222             :   nsresult rv;
    2223             :   nsCOMPtr<nsITransfer> transfer =
    2224           0 :     do_CreateInstance(NS_TRANSFER_CONTRACTID, &rv);
    2225           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2226             : 
    2227             :   // If we don't have a download directory we're kinda screwed but it's OK
    2228             :   // we'll still report the error via the prompter.
    2229           0 :   nsCOMPtr<nsIFile> pseudoFile;
    2230           0 :   rv = GetDownloadDirectory(getter_AddRefs(pseudoFile), true);
    2231           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2232             : 
    2233             :   // Append the default suggested filename. If the user restarts the transfer
    2234             :   // we will re-trigger a filename check anyway to ensure that it is unique.
    2235           0 :   rv = pseudoFile->Append(mSuggestedFileName);
    2236           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2237             : 
    2238           0 :   nsCOMPtr<nsIURI> pseudoTarget;
    2239           0 :   rv = NS_NewFileURI(getter_AddRefs(pseudoTarget), pseudoFile);
    2240           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2241             : 
    2242           0 :   rv = transfer->Init(mSourceUrl, pseudoTarget, EmptyString(),
    2243             :                       mMimeInfo, mTimeDownloadStarted, nullptr, this,
    2244           0 :                       aIsPrivateBrowsing);
    2245           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2246             : 
    2247             :   // Our failed transfer is ready.
    2248           0 :   mTransfer = transfer.forget();
    2249             : 
    2250           0 :   return NS_OK;
    2251             : }
    2252             : 
    2253           0 : nsresult nsExternalAppHandler::SaveDestinationAvailable(nsIFile * aFile)
    2254             : {
    2255           0 :   if (aFile)
    2256           0 :     ContinueSave(aFile);
    2257             :   else
    2258           0 :     Cancel(NS_BINDING_ABORTED);
    2259             : 
    2260           0 :   return NS_OK;
    2261             : }
    2262             : 
    2263           0 : void nsExternalAppHandler::RequestSaveDestination(const nsString& aDefaultFile, const nsString& aFileExtension)
    2264             : {
    2265             :   // Display the dialog
    2266             :   // XXX Convert to use file picker? No, then embeddors could not do any sort of
    2267             :   // "AutoDownload" w/o showing a prompt
    2268           0 :   nsresult rv = NS_OK;
    2269           0 :   if (!mDialog) {
    2270             :     // Get helper app launcher dialog.
    2271           0 :     mDialog = do_CreateInstance(NS_HELPERAPPLAUNCHERDLG_CONTRACTID, &rv);
    2272           0 :     if (rv != NS_OK) {
    2273           0 :       Cancel(NS_BINDING_ABORTED);
    2274           0 :       return;
    2275             :     }
    2276             :   }
    2277             : 
    2278             :   // we want to explicitly unescape aDefaultFile b4 passing into the dialog. we can't unescape
    2279             :   // it because the dialog is implemented by a JS component which doesn't have a window so no unescape routine is defined...
    2280             : 
    2281             :   // Now, be sure to keep |this| alive, and the dialog
    2282             :   // If we don't do this, users that close the helper app dialog while the file
    2283             :   // picker is up would cause Cancel() to be called, and the dialog would be
    2284             :   // released, which would release this object too, which would crash.
    2285             :   // See Bug 249143
    2286           0 :   RefPtr<nsExternalAppHandler> kungFuDeathGrip(this);
    2287           0 :   nsCOMPtr<nsIHelperAppLauncherDialog> dlg(mDialog);
    2288             : 
    2289           0 :   rv = dlg->PromptForSaveToFileAsync(this,
    2290           0 :                                      GetDialogParent(),
    2291             :                                      aDefaultFile.get(),
    2292             :                                      aFileExtension.get(),
    2293           0 :                                      mForceSave);
    2294           0 :   if (NS_FAILED(rv)) {
    2295           0 :     Cancel(NS_BINDING_ABORTED);
    2296             :   }
    2297             : }
    2298             : 
    2299             : // SaveToDisk should only be called by the helper app dialog which allows
    2300             : // the user to say launch with application or save to disk. It doesn't actually
    2301             : // perform the save, it just prompts for the destination file name.
    2302           0 : NS_IMETHODIMP nsExternalAppHandler::SaveToDisk(nsIFile * aNewFileLocation, bool aRememberThisPreference)
    2303             : {
    2304           0 :   if (mCanceled)
    2305           0 :     return NS_OK;
    2306             : 
    2307           0 :   mMimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
    2308             : 
    2309           0 :   if (!aNewFileLocation) {
    2310           0 :     if (mSuggestedFileName.IsEmpty())
    2311           0 :       RequestSaveDestination(mTempLeafName, mTempFileExtension);
    2312             :     else
    2313             :     {
    2314           0 :       nsAutoString fileExt;
    2315           0 :       int32_t pos = mSuggestedFileName.RFindChar('.');
    2316           0 :       if (pos >= 0)
    2317           0 :         mSuggestedFileName.Right(fileExt, mSuggestedFileName.Length() - pos);
    2318           0 :       if (fileExt.IsEmpty())
    2319           0 :         fileExt = mTempFileExtension;
    2320             : 
    2321           0 :       RequestSaveDestination(mSuggestedFileName, fileExt);
    2322             :     }
    2323             :   } else {
    2324           0 :     ContinueSave(aNewFileLocation);
    2325             :   }
    2326             : 
    2327           0 :   return NS_OK;
    2328             : }
    2329           0 : nsresult nsExternalAppHandler::ContinueSave(nsIFile * aNewFileLocation)
    2330             : {
    2331           0 :   if (mCanceled)
    2332           0 :     return NS_OK;
    2333             : 
    2334           0 :   NS_PRECONDITION(aNewFileLocation, "Must be called with a non-null file");
    2335             : 
    2336           0 :   nsresult rv = NS_OK;
    2337           0 :   nsCOMPtr<nsIFile> fileToUse = do_QueryInterface(aNewFileLocation);
    2338           0 :   mFinalFileDestination = do_QueryInterface(fileToUse);
    2339             : 
    2340             :   // Move what we have in the final directory, but append .part
    2341             :   // to it, to indicate that it's unfinished.  Do not call SetTarget on the
    2342             :   // saver if we are done (Finish has been called) but OnSaverComplete has not
    2343             :   // been called.
    2344           0 :   if (mFinalFileDestination && mSaver && !mStopRequestIssued)
    2345             :   {
    2346           0 :     nsCOMPtr<nsIFile> movedFile;
    2347           0 :     mFinalFileDestination->Clone(getter_AddRefs(movedFile));
    2348           0 :     if (movedFile) {
    2349             :       // Get the old leaf name and append .part to it
    2350           0 :       nsAutoString name;
    2351           0 :       mFinalFileDestination->GetLeafName(name);
    2352           0 :       name.AppendLiteral(".part");
    2353           0 :       movedFile->SetLeafName(name);
    2354             : 
    2355           0 :       rv = mSaver->SetTarget(movedFile, true);
    2356           0 :       if (NS_FAILED(rv)) {
    2357           0 :         nsAutoString path;
    2358           0 :         mTempFile->GetPath(path);
    2359           0 :         SendStatusChange(kWriteError, rv, nullptr, path);
    2360           0 :         Cancel(rv);
    2361           0 :         return NS_OK;
    2362             :       }
    2363             : 
    2364           0 :       mTempFile = movedFile;
    2365             :     }
    2366             :   }
    2367             : 
    2368             :   // The helper app dialog has told us what to do and we have a final file
    2369             :   // destination.
    2370           0 :   rv = CreateTransfer();
    2371             :   // If we fail to create the transfer, Cancel.
    2372           0 :   if (NS_FAILED(rv)) {
    2373           0 :     Cancel(rv);
    2374           0 :     return rv;
    2375             :   }
    2376             : 
    2377             :   // now that the user has chosen the file location to save to, it's okay to fire the refresh tag
    2378             :   // if there is one. We don't want to do this before the save as dialog goes away because this dialog
    2379             :   // is modal and we do bad things if you try to load a web page in the underlying window while a modal
    2380             :   // dialog is still up.
    2381           0 :   ProcessAnyRefreshTags();
    2382             : 
    2383           0 :   return NS_OK;
    2384             : }
    2385             : 
    2386             : 
    2387             : // LaunchWithApplication should only be called by the helper app dialog which
    2388             : // allows the user to say launch with application or save to disk. It doesn't
    2389             : // actually perform launch with application.
    2390           0 : NS_IMETHODIMP nsExternalAppHandler::LaunchWithApplication(nsIFile * aApplication, bool aRememberThisPreference)
    2391             : {
    2392           0 :   if (mCanceled)
    2393           0 :     return NS_OK;
    2394             : 
    2395             :   // user has chosen to launch using an application, fire any refresh tags now...
    2396           0 :   ProcessAnyRefreshTags(); 
    2397             :   
    2398           0 :   if (mMimeInfo && aApplication) {
    2399             :     PlatformLocalHandlerApp_t *handlerApp =
    2400           0 :       new PlatformLocalHandlerApp_t(EmptyString(), aApplication);
    2401           0 :     mMimeInfo->SetPreferredApplicationHandler(handlerApp);
    2402             :   }
    2403             : 
    2404             :   // Now check if the file is local, in which case we won't bother with saving
    2405             :   // it to a temporary directory and just launch it from where it is
    2406           0 :   nsCOMPtr<nsIFileURL> fileUrl(do_QueryInterface(mSourceUrl));
    2407           0 :   if (fileUrl && mIsFileChannel) {
    2408           0 :     Cancel(NS_BINDING_ABORTED);
    2409           0 :     nsCOMPtr<nsIFile> file;
    2410           0 :     nsresult rv = fileUrl->GetFile(getter_AddRefs(file));
    2411             : 
    2412           0 :     if (NS_SUCCEEDED(rv)) {
    2413           0 :       rv = mMimeInfo->LaunchWithFile(file);
    2414           0 :       if (NS_SUCCEEDED(rv))
    2415           0 :         return NS_OK;
    2416             :     }
    2417           0 :     nsAutoString path;
    2418           0 :     if (file)
    2419           0 :       file->GetPath(path);
    2420             :     // If we get here, an error happened
    2421           0 :     SendStatusChange(kLaunchError, rv, nullptr, path);
    2422           0 :     return rv;
    2423             :   }
    2424             : 
    2425             :   // Now that the user has elected to launch the downloaded file with a helper
    2426             :   // app, we're justified in removing the 'salted' name.  We'll rename to what
    2427             :   // was specified in mSuggestedFileName after the download is done prior to
    2428             :   // launching the helper app.  So that any existing file of that name won't be
    2429             :   // overwritten we call CreateUnique().  Also note that we use the same
    2430             :   // directory as originally downloaded so the download can be renamed in place
    2431             :   // later.
    2432           0 :   nsCOMPtr<nsIFile> fileToUse;
    2433           0 :   (void) GetDownloadDirectory(getter_AddRefs(fileToUse));
    2434             : 
    2435           0 :   if (mSuggestedFileName.IsEmpty()) {
    2436             :     // Keep using the leafname of the temp file, since we're just starting a helper
    2437           0 :     mSuggestedFileName = mTempLeafName;
    2438             :   }
    2439             : 
    2440             : #ifdef XP_WIN
    2441             :   fileToUse->Append(mSuggestedFileName + mTempFileExtension);
    2442             : #else
    2443           0 :   fileToUse->Append(mSuggestedFileName);  
    2444             : #endif
    2445             : 
    2446           0 :   nsresult rv = fileToUse->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
    2447           0 :   if(NS_SUCCEEDED(rv)) {
    2448           0 :     mFinalFileDestination = do_QueryInterface(fileToUse);
    2449             :     // launch the progress window now that the user has picked the desired action.
    2450           0 :     rv = CreateTransfer();
    2451           0 :     if (NS_FAILED(rv)) {
    2452           0 :       Cancel(rv);
    2453             :     }
    2454             :   } else {
    2455             :     // Cancel the download and report an error.  We do not want to end up in
    2456             :     // a state where it appears that we have a normal download that is
    2457             :     // pointing to a file that we did not actually create.
    2458           0 :     nsAutoString path;
    2459           0 :     mTempFile->GetPath(path);
    2460           0 :     SendStatusChange(kWriteError, rv, nullptr, path);
    2461           0 :     Cancel(rv);
    2462             :   }
    2463           0 :   return rv;
    2464             : }
    2465             : 
    2466           0 : NS_IMETHODIMP nsExternalAppHandler::Cancel(nsresult aReason)
    2467             : {
    2468           0 :   NS_ENSURE_ARG(NS_FAILED(aReason));
    2469             : 
    2470           0 :   if (mCanceled) {
    2471           0 :     return NS_OK;
    2472             :   }
    2473           0 :   mCanceled = true;
    2474             : 
    2475           0 :   if (mSaver) {
    2476             :     // We are still writing to the target file.  Give the saver a chance to
    2477             :     // close the target file, then notify the transfer object if necessary in
    2478             :     // the OnSaveComplete callback.
    2479           0 :     mSaver->Finish(aReason);
    2480           0 :     mSaver = nullptr;
    2481             :   } else {
    2482           0 :     if (mStopRequestIssued && mTempFile) {
    2483             :       // This branch can only happen when the user cancels the helper app dialog
    2484             :       // when the request has completed. The temp file has to be removed here,
    2485             :       // because mSaver has been released at that time with the temp file left.
    2486           0 :       (void)mTempFile->Remove(false);
    2487             :     }
    2488             : 
    2489             :     // Notify the transfer object that the download has been canceled, if the
    2490             :     // user has already chosen an action and we didn't notify already.
    2491           0 :     if (mTransfer) {
    2492           0 :       NotifyTransfer(aReason);
    2493             :     }
    2494             :   }
    2495             : 
    2496             :   // Break our reference cycle with the helper app dialog (set up in
    2497             :   // OnStartRequest)
    2498           0 :   mDialog = nullptr;
    2499             : 
    2500           0 :   mRequest = nullptr;
    2501             : 
    2502             :   // Release the listener, to break the reference cycle with it (we are the
    2503             :   // observer of the listener).
    2504           0 :   mDialogProgressListener = nullptr;
    2505             : 
    2506           0 :   return NS_OK;
    2507             : }
    2508             : 
    2509           0 : void nsExternalAppHandler::ProcessAnyRefreshTags()
    2510             : {
    2511             :    // one last thing, try to see if the original window context supports a refresh interface...
    2512             :    // Sometimes, when you download content that requires an external handler, there is
    2513             :    // a refresh header associated with the download. This refresh header points to a page
    2514             :    // the content provider wants the user to see after they download the content. How do we
    2515             :    // pass this refresh information back to the caller? For now, try to get the refresh URI
    2516             :    // interface. If the window context where the request originated came from supports this
    2517             :    // then we can force it to process the refresh information (if there is any) from this channel.
    2518           0 :    if (mContentContext && mOriginalChannel) {
    2519           0 :      nsCOMPtr<nsIRefreshURI> refreshHandler (do_GetInterface(mContentContext));
    2520           0 :      if (refreshHandler) {
    2521           0 :         refreshHandler->SetupRefreshURI(mOriginalChannel);
    2522             :      }
    2523           0 :      mOriginalChannel = nullptr;
    2524             :    }
    2525           0 : }
    2526             : 
    2527           0 : bool nsExternalAppHandler::GetNeverAskFlagFromPref(const char * prefName, const char * aContentType)
    2528             : {
    2529             :   // Search the obsolete pref strings.
    2530           0 :   nsAdoptingCString prefCString = Preferences::GetCString(prefName);
    2531           0 :   if (prefCString.IsEmpty()) {
    2532             :     // Default is true, if not found in the pref string.
    2533           0 :     return true;
    2534             :   }
    2535             : 
    2536           0 :   NS_UnescapeURL(prefCString);
    2537           0 :   nsACString::const_iterator start, end;
    2538           0 :   prefCString.BeginReading(start);
    2539           0 :   prefCString.EndReading(end);
    2540           0 :   return !CaseInsensitiveFindInReadable(nsDependentCString(aContentType),
    2541           0 :                                         start, end);
    2542             : }
    2543             : 
    2544           0 : nsresult nsExternalAppHandler::MaybeCloseWindow()
    2545             : {
    2546           0 :   nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(mContentContext);
    2547           0 :   NS_ENSURE_STATE(window);
    2548             : 
    2549           0 :   if (mShouldCloseWindow) {
    2550             :     // Reset the window context to the opener window so that the dependent
    2551             :     // dialogs have a parent
    2552           0 :     nsCOMPtr<nsPIDOMWindowOuter> opener = window->GetOpener();
    2553             : 
    2554           0 :     if (opener && !opener->Closed()) {
    2555           0 :       mContentContext = do_GetInterface(opener);
    2556             : 
    2557             :       // Now close the old window.  Do it on a timer so that we don't run
    2558             :       // into issues trying to close the window before it has fully opened.
    2559           0 :       NS_ASSERTION(!mTimer, "mTimer was already initialized once!");
    2560           0 :       mTimer = do_CreateInstance("@mozilla.org/timer;1");
    2561           0 :       if (!mTimer) {
    2562           0 :         return NS_ERROR_FAILURE;
    2563             :       }
    2564             : 
    2565           0 :       mTimer->InitWithCallback(this, 0, nsITimer::TYPE_ONE_SHOT);
    2566           0 :       mWindowToClose = window;
    2567             :     }
    2568             :   }
    2569             : 
    2570           0 :   return NS_OK;
    2571             : }
    2572             : 
    2573             : NS_IMETHODIMP
    2574           0 : nsExternalAppHandler::Notify(nsITimer* timer)
    2575             : {
    2576           0 :   NS_ASSERTION(mWindowToClose, "No window to close after timer fired");
    2577             : 
    2578           0 :   mWindowToClose->Close();
    2579           0 :   mWindowToClose = nullptr;
    2580           0 :   mTimer = nullptr;
    2581             : 
    2582           0 :   return NS_OK;
    2583             : }
    2584             : //////////////////////////////////////////////////////////////////////////////////////////////////////////////
    2585             : // The following section contains our nsIMIMEService implementation and related methods.
    2586             : //
    2587             : //////////////////////////////////////////////////////////////////////////////////////////////////////////////
    2588             : 
    2589             : // nsIMIMEService methods
    2590           2 : NS_IMETHODIMP nsExternalHelperAppService::GetFromTypeAndExtension(const nsACString& aMIMEType, const nsACString& aFileExt, nsIMIMEInfo **_retval) 
    2591             : {
    2592           2 :   NS_PRECONDITION(!aMIMEType.IsEmpty() ||
    2593             :                   !aFileExt.IsEmpty(), 
    2594             :                   "Give me something to work with");
    2595           2 :   LOG(("Getting mimeinfo from type '%s' ext '%s'\n",
    2596             :         PromiseFlatCString(aMIMEType).get(), PromiseFlatCString(aFileExt).get()));
    2597             : 
    2598           2 :   *_retval = nullptr;
    2599             : 
    2600             :   // OK... we need a type. Get one.
    2601           4 :   nsAutoCString typeToUse(aMIMEType);
    2602           2 :   if (typeToUse.IsEmpty()) {
    2603           0 :     nsresult rv = GetTypeFromExtension(aFileExt, typeToUse);
    2604           0 :     if (NS_FAILED(rv))
    2605           0 :       return NS_ERROR_NOT_AVAILABLE;
    2606             :   }
    2607             : 
    2608             :   // We promise to only send lower case mime types to the OS
    2609           2 :   ToLowerCase(typeToUse);
    2610             : 
    2611             :   // (1) Ask the OS for a mime info
    2612             :   bool found;
    2613           2 :   *_retval = GetMIMEInfoFromOS(typeToUse, aFileExt, &found).take();
    2614           2 :   LOG(("OS gave back 0x%p - found: %i\n", *_retval, found));
    2615             :   // If we got no mimeinfo, something went wrong. Probably lack of memory.
    2616           2 :   if (!*_retval)
    2617           0 :     return NS_ERROR_OUT_OF_MEMORY;
    2618             : 
    2619             :   // (2) Now, let's see if we can find something in our datastore
    2620             :   // This will not overwrite the OS information that interests us
    2621             :   // (i.e. default application, default app. description)
    2622             :   nsresult rv;
    2623           4 :   nsCOMPtr<nsIHandlerService> handlerSvc = do_GetService(NS_HANDLERSERVICE_CONTRACTID);
    2624           2 :   if (handlerSvc) {
    2625           2 :     bool hasHandler = false;
    2626           2 :     (void) handlerSvc->Exists(*_retval, &hasHandler);
    2627           2 :     if (hasHandler) {
    2628           2 :       rv = handlerSvc->FillHandlerInfo(*_retval, EmptyCString());
    2629           2 :       LOG(("Data source: Via type: retval 0x%08" PRIx32 "\n", static_cast<uint32_t>(rv)));
    2630             :     } else {
    2631           0 :       rv = NS_ERROR_NOT_AVAILABLE;
    2632             :     }
    2633             :  
    2634           2 :     found = found || NS_SUCCEEDED(rv);
    2635             : 
    2636           2 :     if (!found || NS_FAILED(rv)) {
    2637             :       // No type match, try extension match
    2638           0 :       if (!aFileExt.IsEmpty()) {
    2639           0 :         nsAutoCString overrideType;
    2640           0 :         rv = handlerSvc->GetTypeFromExtension(aFileExt, overrideType);
    2641           0 :         if (NS_SUCCEEDED(rv) && !overrideType.IsEmpty()) {
    2642             :           // We can't check handlerSvc->Exists() here, because we have a
    2643             :           // overideType. That's ok, it just results in some console noise.
    2644             :           // (If there's no handler for the override type, it throws)
    2645           0 :           rv = handlerSvc->FillHandlerInfo(*_retval, overrideType);
    2646           0 :           LOG(("Data source: Via ext: retval 0x%08" PRIx32 "\n",
    2647             :                static_cast<uint32_t>(rv)));
    2648           0 :           found = found || NS_SUCCEEDED(rv);
    2649             :         }
    2650             :       }
    2651             :     }
    2652             :   }
    2653             : 
    2654             :   // (3) No match yet. Ask extras.
    2655           2 :   if (!found) {
    2656           0 :     rv = NS_ERROR_FAILURE;
    2657             : #ifdef XP_WIN
    2658             :     /* XXX Gross hack to wallpaper over the most common Win32
    2659             :      * extension issues caused by the fix for bug 116938.  See bug
    2660             :      * 120327, comment 271 for why this is needed.  Not even sure we
    2661             :      * want to remove this once we have fixed all this stuff to work
    2662             :      * right; any info we get from extras on this type is pretty much
    2663             :      * useless....
    2664             :      */
    2665             :     if (!typeToUse.Equals(APPLICATION_OCTET_STREAM, nsCaseInsensitiveCStringComparator()))
    2666             : #endif
    2667           0 :       rv = FillMIMEInfoForMimeTypeFromExtras(typeToUse, *_retval);
    2668           0 :     LOG(("Searched extras (by type), rv 0x%08" PRIX32 "\n", static_cast<uint32_t>(rv)));
    2669             :     // If that didn't work out, try file extension from extras
    2670           0 :     if (NS_FAILED(rv) && !aFileExt.IsEmpty()) {
    2671           0 :       rv = FillMIMEInfoForExtensionFromExtras(aFileExt, *_retval);
    2672           0 :       LOG(("Searched extras (by ext), rv 0x%08" PRIX32 "\n", static_cast<uint32_t>(rv)));
    2673             :     }
    2674             :     // If that still didn't work, set the file description to "ext File"
    2675           0 :     if (NS_FAILED(rv) && !aFileExt.IsEmpty()) {
    2676             :       // XXXzpao This should probably be localized
    2677           0 :       nsAutoCString desc(aFileExt);
    2678           0 :       desc.AppendLiteral(" File");
    2679           0 :       (*_retval)->SetDescription(NS_ConvertASCIItoUTF16(desc));
    2680           0 :       LOG(("Falling back to 'File' file description\n"));
    2681             :     }
    2682             :   }
    2683             : 
    2684             :   // Finally, check if we got a file extension and if yes, if it is an
    2685             :   // extension on the mimeinfo, in which case we want it to be the primary one
    2686           2 :   if (!aFileExt.IsEmpty()) {
    2687           2 :     bool matches = false;
    2688           2 :     (*_retval)->ExtensionExists(aFileExt, &matches);
    2689           2 :     LOG(("Extension '%s' matches mime info: %i\n", PromiseFlatCString(aFileExt).get(), matches));
    2690           2 :     if (matches)
    2691           2 :       (*_retval)->SetPrimaryExtension(aFileExt);
    2692             :   }
    2693             : 
    2694           2 :   if (LOG_ENABLED()) {
    2695           0 :     nsAutoCString type;
    2696           0 :     (*_retval)->GetMIMEType(type);
    2697             : 
    2698           0 :     nsAutoCString ext;
    2699           0 :     (*_retval)->GetPrimaryExtension(ext);
    2700           0 :     LOG(("MIME Info Summary: Type '%s', Primary Ext '%s'\n", type.get(), ext.get()));
    2701             :   }
    2702             : 
    2703           2 :   return NS_OK;
    2704             : }
    2705             : 
    2706             : NS_IMETHODIMP
    2707         124 : nsExternalHelperAppService::GetTypeFromExtension(const nsACString& aFileExt,
    2708             :                                                  nsACString& aContentType)
    2709             : {
    2710             :   // OK. We want to try the following sources of mimetype information, in this order:
    2711             :   // 1. defaultMimeEntries array
    2712             :   // 2. OS-provided information
    2713             :   // 3. our "extras" array
    2714             :   // 4. Information from plugins
    2715             :   // 5. The "ext-to-type-mapping" category
    2716             :   // Note that, we are intentionally not looking at the handler service, because
    2717             :   // that can be affected by websites, which leads to undesired behavior.
    2718             : 
    2719             :   // Early return if called with an empty extension parameter
    2720         124 :   if (aFileExt.IsEmpty()) {
    2721           0 :     return NS_ERROR_NOT_AVAILABLE;
    2722             :   }
    2723             : 
    2724             :   // First of all, check our default entries
    2725        1192 :   for (auto& entry : defaultMimeEntries) {
    2726        1192 :     if (aFileExt.LowerCaseEqualsASCII(entry.mFileExtension)) {
    2727         124 :       aContentType = entry.mMimeType;
    2728         124 :       return NS_OK;
    2729             :     }
    2730             :   }
    2731             : 
    2732             :   // Ask OS.
    2733           0 :   if (GetMIMETypeFromOSForExtension(aFileExt, aContentType)) {
    2734           0 :     return NS_OK;
    2735             :   }
    2736             : 
    2737             :   // Check extras array.
    2738           0 :   bool found = GetTypeFromExtras(aFileExt, aContentType);
    2739           0 :   if (found) {
    2740           0 :     return NS_OK;
    2741             :   }
    2742             : 
    2743             :   // Try the plugins
    2744           0 :   RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
    2745           0 :   if (pluginHost &&
    2746           0 :       pluginHost->HavePluginForExtension(aFileExt, aContentType)) {
    2747           0 :     return NS_OK;
    2748             :   }
    2749             : 
    2750             :   // Let's see if an extension added something
    2751             :   nsCOMPtr<nsICategoryManager> catMan(
    2752           0 :     do_GetService("@mozilla.org/categorymanager;1"));
    2753           0 :   if (catMan) {
    2754             :     // The extension in the category entry is always stored as lowercase
    2755           0 :     nsAutoCString lowercaseFileExt(aFileExt);
    2756           0 :     ToLowerCase(lowercaseFileExt);
    2757             :     // Read the MIME type from the category entry, if available
    2758           0 :     nsXPIDLCString type;
    2759           0 :     nsresult rv = catMan->GetCategoryEntry("ext-to-type-mapping",
    2760             :                                            lowercaseFileExt.get(),
    2761           0 :                                            getter_Copies(type));
    2762           0 :     if (NS_SUCCEEDED(rv)) {
    2763           0 :       aContentType = type;
    2764           0 :       return NS_OK;
    2765             :     }
    2766             :   }
    2767             : 
    2768           0 :   return NS_ERROR_NOT_AVAILABLE;
    2769             : }
    2770             : 
    2771           0 : NS_IMETHODIMP nsExternalHelperAppService::GetPrimaryExtension(const nsACString& aMIMEType, const nsACString& aFileExt, nsACString& _retval)
    2772             : {
    2773           0 :   NS_ENSURE_ARG(!aMIMEType.IsEmpty());
    2774             : 
    2775           0 :   nsCOMPtr<nsIMIMEInfo> mi;
    2776           0 :   nsresult rv = GetFromTypeAndExtension(aMIMEType, aFileExt, getter_AddRefs(mi));
    2777           0 :   if (NS_FAILED(rv))
    2778           0 :     return rv;
    2779             : 
    2780           0 :   return mi->GetPrimaryExtension(_retval);
    2781             : }
    2782             : 
    2783           0 : NS_IMETHODIMP nsExternalHelperAppService::GetTypeFromURI(nsIURI *aURI, nsACString& aContentType) 
    2784             : {
    2785           0 :   NS_ENSURE_ARG_POINTER(aURI);
    2786           0 :   nsresult rv = NS_ERROR_NOT_AVAILABLE;
    2787           0 :   aContentType.Truncate();
    2788             : 
    2789             :   // First look for a file to use.  If we have one, we just use that.
    2790           0 :   nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(aURI);
    2791           0 :   if (fileUrl) {
    2792           0 :     nsCOMPtr<nsIFile> file;
    2793           0 :     rv = fileUrl->GetFile(getter_AddRefs(file));
    2794           0 :     if (NS_SUCCEEDED(rv)) {
    2795           0 :       rv = GetTypeFromFile(file, aContentType);
    2796           0 :       if (NS_SUCCEEDED(rv)) {
    2797             :         // we got something!
    2798           0 :         return rv;
    2799             :       }
    2800             :     }
    2801             :   }
    2802             : 
    2803             :   // Now try to get an nsIURL so we don't have to do our own parsing
    2804           0 :   nsCOMPtr<nsIURL> url = do_QueryInterface(aURI);
    2805           0 :   if (url) {
    2806           0 :     nsAutoCString ext;
    2807           0 :     rv = url->GetFileExtension(ext);
    2808           0 :     if (NS_FAILED(rv))
    2809           0 :       return rv;
    2810           0 :     if (ext.IsEmpty())
    2811           0 :       return NS_ERROR_NOT_AVAILABLE;
    2812             : 
    2813           0 :     UnescapeFragment(ext, url, ext);
    2814             : 
    2815           0 :     return GetTypeFromExtension(ext, aContentType);
    2816             :   }
    2817             :     
    2818             :   // no url, let's give the raw spec a shot
    2819           0 :   nsAutoCString specStr;
    2820           0 :   rv = aURI->GetSpec(specStr);
    2821           0 :   if (NS_FAILED(rv))
    2822           0 :     return rv;
    2823           0 :   UnescapeFragment(specStr, aURI, specStr);
    2824             : 
    2825             :   // find the file extension (if any)
    2826           0 :   int32_t extLoc = specStr.RFindChar('.');
    2827           0 :   int32_t specLength = specStr.Length();
    2828           0 :   if (-1 != extLoc &&
    2829           0 :       extLoc != specLength - 1 &&
    2830             :       // nothing over 20 chars long can be sanely considered an
    2831             :       // extension.... Dat dere would be just data.
    2832           0 :       specLength - extLoc < 20) 
    2833             :   {
    2834           0 :     return GetTypeFromExtension(Substring(specStr, extLoc + 1), aContentType);
    2835             :   }
    2836             : 
    2837             :   // We found no information; say so.
    2838           0 :   return NS_ERROR_NOT_AVAILABLE;
    2839             : }
    2840             : 
    2841         125 : NS_IMETHODIMP nsExternalHelperAppService::GetTypeFromFile(nsIFile* aFile, nsACString& aContentType)
    2842             : {
    2843         125 :   NS_ENSURE_ARG_POINTER(aFile);
    2844             :   nsresult rv;
    2845             : 
    2846             :   // Get the Extension
    2847         250 :   nsAutoString fileName;
    2848         125 :   rv = aFile->GetLeafName(fileName);
    2849         125 :   if (NS_FAILED(rv)) return rv;
    2850             :  
    2851         250 :   nsAutoCString fileExt;
    2852         125 :   if (!fileName.IsEmpty())
    2853             :   {
    2854         125 :     int32_t len = fileName.Length(); 
    2855         621 :     for (int32_t i = len; i >= 0; i--) 
    2856             :     {
    2857         620 :       if (fileName[i] == char16_t('.'))
    2858             :       {
    2859         124 :         CopyUTF16toUTF8(fileName.get() + i + 1, fileExt);
    2860         124 :         break;
    2861             :       }
    2862             :     }
    2863             :   }
    2864             : 
    2865         125 :   if (fileExt.IsEmpty())
    2866           1 :     return NS_ERROR_FAILURE;
    2867             : 
    2868         124 :   return GetTypeFromExtension(fileExt, aContentType);
    2869             : }
    2870             : 
    2871           0 : nsresult nsExternalHelperAppService::FillMIMEInfoForMimeTypeFromExtras(
    2872             :   const nsACString& aContentType, nsIMIMEInfo * aMIMEInfo)
    2873             : {
    2874           0 :   NS_ENSURE_ARG( aMIMEInfo );
    2875             : 
    2876           0 :   NS_ENSURE_ARG( !aContentType.IsEmpty() );
    2877             : 
    2878             :   // Look for default entry with matching mime type.
    2879           0 :   nsAutoCString MIMEType(aContentType);
    2880           0 :   ToLowerCase(MIMEType);
    2881           0 :   int32_t numEntries = ArrayLength(extraMimeEntries);
    2882           0 :   for (int32_t index = 0; index < numEntries; index++)
    2883             :   {
    2884           0 :       if ( MIMEType.Equals(extraMimeEntries[index].mMimeType) )
    2885             :       {
    2886             :           // This is the one. Set attributes appropriately.
    2887           0 :           aMIMEInfo->SetFileExtensions(nsDependentCString(extraMimeEntries[index].mFileExtensions));
    2888           0 :           aMIMEInfo->SetDescription(NS_ConvertASCIItoUTF16(extraMimeEntries[index].mDescription));
    2889           0 :           return NS_OK;
    2890             :       }
    2891             :   }
    2892             : 
    2893           0 :   return NS_ERROR_NOT_AVAILABLE;
    2894             : }
    2895             : 
    2896           0 : nsresult nsExternalHelperAppService::FillMIMEInfoForExtensionFromExtras(
    2897             :   const nsACString& aExtension, nsIMIMEInfo * aMIMEInfo)
    2898             : {
    2899           0 :   nsAutoCString type;
    2900           0 :   bool found = GetTypeFromExtras(aExtension, type);
    2901           0 :   if (!found)
    2902           0 :     return NS_ERROR_NOT_AVAILABLE;
    2903           0 :   return FillMIMEInfoForMimeTypeFromExtras(type, aMIMEInfo);
    2904             : }
    2905             : 
    2906           0 : bool nsExternalHelperAppService::GetTypeFromExtras(const nsACString& aExtension, nsACString& aMIMEType)
    2907             : {
    2908           0 :   NS_ASSERTION(!aExtension.IsEmpty(), "Empty aExtension parameter!");
    2909             : 
    2910             :   // Look for default entry with matching extension.
    2911           0 :   nsDependentCString::const_iterator start, end, iter;
    2912           0 :   int32_t numEntries = ArrayLength(extraMimeEntries);
    2913           0 :   for (int32_t index = 0; index < numEntries; index++)
    2914             :   {
    2915           0 :       nsDependentCString extList(extraMimeEntries[index].mFileExtensions);
    2916           0 :       extList.BeginReading(start);
    2917           0 :       extList.EndReading(end);
    2918           0 :       iter = start;
    2919           0 :       while (start != end)
    2920             :       {
    2921           0 :           FindCharInReadable(',', iter, end);
    2922           0 :           if (Substring(start, iter).Equals(aExtension,
    2923           0 :                                             nsCaseInsensitiveCStringComparator()))
    2924             :           {
    2925           0 :               aMIMEType = extraMimeEntries[index].mMimeType;
    2926           0 :               return true;
    2927             :           }
    2928           0 :           if (iter != end) {
    2929           0 :             ++iter;
    2930             :           }
    2931           0 :           start = iter;
    2932             :       }
    2933             :   }
    2934             : 
    2935           0 :   return false;
    2936             : }
    2937             : 
    2938             : bool
    2939           0 : nsExternalHelperAppService::GetMIMETypeFromOSForExtension(const nsACString& aExtension, nsACString& aMIMEType)
    2940             : {
    2941           0 :   bool found = false;
    2942           0 :   nsCOMPtr<nsIMIMEInfo> mimeInfo = GetMIMEInfoFromOS(EmptyCString(), aExtension, &found);
    2943           0 :   return found && mimeInfo && NS_SUCCEEDED(mimeInfo->GetMIMEType(aMIMEType));
    2944             : }

Generated by: LCOV version 1.13