LCOV - code coverage report
Current view: directory - uriloader/exthandler - nsExternalHelperAppService.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1070 613 57.3 %
Date: 2012-06-02 Functions: 71 47 66.2 %

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

Generated by: LCOV version 1.7