michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsExternalHelperAppService_h__ michael@0: #define nsExternalHelperAppService_h__ michael@0: michael@0: #ifdef MOZ_LOGGING michael@0: #define FORCE_PR_LOG michael@0: #endif michael@0: #include "prlog.h" michael@0: #include "prtime.h" michael@0: michael@0: #include "nsIExternalHelperAppService.h" michael@0: #include "nsIExternalProtocolService.h" michael@0: #include "nsIWebProgressListener2.h" michael@0: #include "nsIHelperAppLauncherDialog.h" michael@0: michael@0: #include "nsIMIMEInfo.h" michael@0: #include "nsIMIMEService.h" michael@0: #include "nsIStreamListener.h" michael@0: #include "nsIFile.h" michael@0: #include "nsIFileStreams.h" michael@0: #include "nsIOutputStream.h" michael@0: #include "nsString.h" michael@0: #include "nsIInterfaceRequestor.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsIChannel.h" michael@0: #include "nsITimer.h" michael@0: #include "nsIBackgroundFileSaver.h" michael@0: michael@0: #include "nsIHandlerService.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIObserver.h" michael@0: #include "nsCOMArray.h" michael@0: #include "nsWeakReference.h" michael@0: #include "nsIPrompt.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "necko-config.h" michael@0: michael@0: class nsExternalAppHandler; michael@0: class nsIMIMEInfo; michael@0: class nsITransfer; michael@0: class nsIDOMWindow; michael@0: michael@0: /** michael@0: * The helper app service. Responsible for handling content that Mozilla michael@0: * itself can not handle michael@0: */ michael@0: class nsExternalHelperAppService michael@0: : public nsIExternalHelperAppService, michael@0: public nsPIExternalAppLauncher, michael@0: public nsIExternalProtocolService, michael@0: public nsIMIMEService, michael@0: public nsIObserver, michael@0: public nsSupportsWeakReference michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIEXTERNALHELPERAPPSERVICE michael@0: NS_DECL_NSPIEXTERNALAPPLAUNCHER michael@0: NS_DECL_NSIEXTERNALPROTOCOLSERVICE michael@0: NS_DECL_NSIMIMESERVICE michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: nsExternalHelperAppService(); michael@0: virtual ~nsExternalHelperAppService(); michael@0: michael@0: /** michael@0: * Initializes internal state. Will be called automatically when michael@0: * this service is first instantiated. michael@0: */ michael@0: NS_HIDDEN_(nsresult) Init(); michael@0: michael@0: /** michael@0: * Given a mimetype and an extension, looks up a mime info from the OS. michael@0: * The mime type is given preference. This function follows the same rules michael@0: * as nsIMIMEService::GetFromTypeAndExtension. michael@0: * This is supposed to be overridden by the platform-specific michael@0: * nsOSHelperAppService! michael@0: * @param aFileExt The file extension; may be empty. UTF-8 encoded. michael@0: * @param [out] aFound michael@0: * Should be set to true if the os has a mapping, to michael@0: * false otherwise. Must not be null. michael@0: * @return A MIMEInfo. This function must return a MIMEInfo object if it michael@0: * can allocate one. The only justifiable reason for not michael@0: * returning one is an out-of-memory error. michael@0: * If null, the value of aFound is unspecified. michael@0: */ michael@0: virtual already_AddRefed GetMIMEInfoFromOS(const nsACString& aMIMEType, michael@0: const nsACString& aFileExt, michael@0: bool * aFound) = 0; michael@0: michael@0: /** michael@0: * Given a string identifying an application, create an nsIFile representing michael@0: * it. This function should look in $PATH for the application. michael@0: * The base class implementation will first try to interpret platformAppPath michael@0: * as an absolute path, and if that fails it will look for a file next to the michael@0: * mozilla executable. Subclasses can override this method if they want a michael@0: * different behaviour. michael@0: * @param platformAppPath A platform specific path to an application that we michael@0: * got out of the rdf data source. This can be a mac michael@0: * file spec, a unix path or a windows path depending michael@0: * on the platform michael@0: * @param aFile [out] An nsIFile representation of that platform michael@0: * application path. michael@0: */ michael@0: virtual nsresult GetFileTokenForPath(const char16_t * platformAppPath, michael@0: nsIFile ** aFile); michael@0: michael@0: virtual NS_HIDDEN_(nsresult) OSProtocolHandlerExists(const char *aScheme, michael@0: bool *aExists) = 0; michael@0: michael@0: protected: michael@0: /** michael@0: * Searches the "extra" array of MIMEInfo objects for an object michael@0: * with a specific type. If found, it will modify the passed-in michael@0: * MIMEInfo. Otherwise, it will return an error and the MIMEInfo michael@0: * will be untouched. michael@0: * @param aContentType The type to search for. michael@0: * @param aMIMEInfo [inout] The mime info, if found michael@0: */ michael@0: NS_HIDDEN_(nsresult) FillMIMEInfoForMimeTypeFromExtras( michael@0: const nsACString& aContentType, nsIMIMEInfo * aMIMEInfo); michael@0: /** michael@0: * Searches the "extra" array of MIMEInfo objects for an object michael@0: * with a specific extension. michael@0: * michael@0: * Does not change the MIME Type of the MIME Info. michael@0: * michael@0: * @see FillMIMEInfoForMimeTypeFromExtras michael@0: */ michael@0: NS_HIDDEN_(nsresult) FillMIMEInfoForExtensionFromExtras( michael@0: const nsACString& aExtension, nsIMIMEInfo * aMIMEInfo); michael@0: michael@0: /** michael@0: * Searches the "extra" array for a MIME type, and gets its extension. michael@0: * @param aExtension The extension to search for michael@0: * @param aMIMEType [out] The found MIME type. michael@0: * @return true if the extension was found, false otherwise. michael@0: */ michael@0: NS_HIDDEN_(bool) GetTypeFromExtras(const nsACString& aExtension, michael@0: nsACString& aMIMEType); michael@0: michael@0: #ifdef PR_LOGGING michael@0: /** michael@0: * NSPR Logging Module. Usage: set NSPR_LOG_MODULES=HelperAppService:level, michael@0: * where level should be 2 for errors, 3 for debug messages from the cross- michael@0: * platform nsExternalHelperAppService, and 4 for os-specific debug messages. michael@0: */ michael@0: static PRLogModuleInfo* mLog; michael@0: michael@0: #endif michael@0: // friend, so that it can access the nspr log module. michael@0: friend class nsExternalAppHandler; michael@0: michael@0: /** michael@0: * Helper function for ExpungeTemporaryFiles and ExpungeTemporaryPrivateFiles michael@0: */ michael@0: static void ExpungeTemporaryFilesHelper(nsCOMArray &fileList); michael@0: /** michael@0: * Helper function for DeleteTemporaryFileOnExit and DeleteTemporaryPrivateFileWhenPossible michael@0: */ michael@0: static nsresult DeleteTemporaryFileHelper(nsIFile* aTemporaryFile, michael@0: nsCOMArray &aFileList); michael@0: /** michael@0: * Functions related to the tempory file cleanup service provided by michael@0: * nsExternalHelperAppService michael@0: */ michael@0: void ExpungeTemporaryFiles(); michael@0: /** michael@0: * Functions related to the tempory file cleanup service provided by michael@0: * nsExternalHelperAppService (for the temporary files added during michael@0: * the private browsing mode) michael@0: */ michael@0: void ExpungeTemporaryPrivateFiles(); michael@0: michael@0: #ifdef NECKO_PROTOCOL_rtsp michael@0: /** michael@0: * Launch video app for rtsp protocol. This function is supported only on Gonk michael@0: * for now. michael@0: */ michael@0: static void LaunchVideoAppForRtsp(nsIURI* aURI); michael@0: #endif michael@0: michael@0: /** michael@0: * Array for the files that should be deleted michael@0: */ michael@0: nsCOMArray mTemporaryFilesList; michael@0: /** michael@0: * Array for the files that should be deleted (for the temporary files michael@0: * added during the private browsing mode) michael@0: */ michael@0: nsCOMArray mTemporaryPrivateFilesList; michael@0: }; michael@0: michael@0: /** michael@0: * An external app handler is just a small little class that presents itself as michael@0: * a nsIStreamListener. It saves the incoming data into a temp file. The handler michael@0: * is bound to an application when it is created. When it receives an michael@0: * OnStopRequest it launches the application using the temp file it has michael@0: * stored the data into. We create a handler every time we have to process michael@0: * data using a helper app. michael@0: */ michael@0: class nsExternalAppHandler MOZ_FINAL : public nsIStreamListener, michael@0: public nsIHelperAppLauncher, michael@0: public nsITimerCallback, michael@0: public nsIBackgroundFileSaverObserver michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSISTREAMLISTENER michael@0: NS_DECL_NSIREQUESTOBSERVER michael@0: NS_DECL_NSIHELPERAPPLAUNCHER michael@0: NS_DECL_NSICANCELABLE michael@0: NS_DECL_NSITIMERCALLBACK michael@0: NS_DECL_NSIBACKGROUNDFILESAVEROBSERVER michael@0: michael@0: /** michael@0: * @param aMIMEInfo MIMEInfo object, representing the type of the michael@0: * content that should be handled michael@0: * @param aFileExtension The extension we need to append to our temp file, michael@0: * INCLUDING the ".". e.g. .mp3 michael@0: * @param aWindowContext Window context, as passed to DoContent michael@0: * @param mExtProtSvc nsExternalHelperAppService on creation michael@0: * @param aFileName The filename to use michael@0: * @param aReason A constant from nsIHelperAppLauncherDialog indicating michael@0: * why the request is handled by a helper app. michael@0: */ michael@0: nsExternalAppHandler(nsIMIMEInfo * aMIMEInfo, const nsCSubstring& aFileExtension, michael@0: nsIInterfaceRequestor * aWindowContext, michael@0: nsExternalHelperAppService * aExtProtSvc, michael@0: const nsAString& aFilename, michael@0: uint32_t aReason, bool aForceSave); michael@0: michael@0: ~nsExternalAppHandler(); michael@0: michael@0: /** michael@0: * Clean up after the request was diverted to the parent process. michael@0: */ michael@0: void DidDivertRequest(nsIRequest *request); michael@0: michael@0: protected: michael@0: nsCOMPtr mTempFile; michael@0: nsCOMPtr mSourceUrl; michael@0: nsString mTempFileExtension; michael@0: nsString mTempLeafName; michael@0: michael@0: /** michael@0: * The MIME Info for this load. Will never be null. michael@0: */ michael@0: nsCOMPtr mMimeInfo; michael@0: nsCOMPtr mWindowContext; michael@0: michael@0: /** michael@0: * Used to close the window on a timer, to avoid any exceptions that are michael@0: * thrown if we try to close the window before it's fully loaded. michael@0: */ michael@0: nsCOMPtr mWindowToClose; michael@0: nsCOMPtr mTimer; michael@0: michael@0: /** michael@0: * The following field is set if we were processing an http channel that had michael@0: * a content disposition header which specified the SUGGESTED file name we michael@0: * should present to the user in the save to disk dialog. michael@0: */ michael@0: nsString mSuggestedFileName; michael@0: michael@0: /** michael@0: * If set, this handler should forcibly save the file to disk regardless of michael@0: * MIME info settings or anything else, without ever popping up the michael@0: * unknown content type handling dialog. michael@0: */ michael@0: bool mForceSave; michael@0: michael@0: /** michael@0: * The canceled flag is set if the user canceled the launching of this michael@0: * application before we finished saving the data to a temp file. michael@0: */ michael@0: bool mCanceled; michael@0: michael@0: /** michael@0: * This is set based on whether the channel indicates that a new window michael@0: * was opened specifically for this download. If so, then we michael@0: * close it. michael@0: */ michael@0: bool mShouldCloseWindow; michael@0: michael@0: /** michael@0: * True if a stop request has been issued. michael@0: */ michael@0: bool mStopRequestIssued; michael@0: michael@0: bool mIsFileChannel; michael@0: michael@0: /** michael@0: * One of the REASON_ constants from nsIHelperAppLauncherDialog. Indicates the michael@0: * reason the dialog was shown (unknown content type, server requested it, michael@0: * etc). michael@0: */ michael@0: uint32_t mReason; michael@0: michael@0: /** michael@0: * Track the executable-ness of the temporary file. michael@0: */ michael@0: bool mTempFileIsExecutable; michael@0: michael@0: PRTime mTimeDownloadStarted; michael@0: int64_t mContentLength; michael@0: int64_t mProgress; /**< Number of bytes received (for sending progress notifications). */ michael@0: michael@0: /** michael@0: * When we are told to save the temp file to disk (in a more permament michael@0: * location) before we are done writing the content to a temp file, then michael@0: * we need to remember the final destination until we are ready to use it. michael@0: */ michael@0: nsCOMPtr mFinalFileDestination; michael@0: michael@0: uint32_t mBufferSize; michael@0: michael@0: /** michael@0: * This object handles saving the data received from the network to a michael@0: * temporary location first, and then move the file to its final location, michael@0: * doing all the input/output on a background thread. michael@0: */ michael@0: nsCOMPtr mSaver; michael@0: michael@0: /** michael@0: * Stores the SHA-256 hash associated with the file that we downloaded. michael@0: */ michael@0: nsAutoCString mHash; michael@0: /** michael@0: * Stores the signature information of the downloaded file in an nsIArray of michael@0: * nsIX509CertList of nsIX509Cert. If the file is unsigned this will be michael@0: * empty. michael@0: */ michael@0: nsCOMPtr mSignatureInfo; michael@0: /** michael@0: * Creates the temporary file for the download and an output stream for it. michael@0: * Upon successful return, both mTempFile and mSaver will be valid. michael@0: */ michael@0: nsresult SetUpTempFile(nsIChannel * aChannel); michael@0: /** michael@0: * When we download a helper app, we are going to retarget all load michael@0: * notifications into our own docloader and load group instead of michael@0: * using the window which initiated the load....RetargetLoadNotifications michael@0: * contains that information... michael@0: */ michael@0: void RetargetLoadNotifications(nsIRequest *request); michael@0: /** michael@0: * Once the user tells us how they want to dispose of the content michael@0: * create an nsITransfer so they know what's going on. If this fails, the michael@0: * caller MUST call Cancel. michael@0: */ michael@0: nsresult CreateTransfer(); michael@0: michael@0: /** michael@0: * If we fail to create the necessary temporary file to initiate a transfer michael@0: * we will report the failure by creating a failed nsITransfer. michael@0: */ michael@0: nsresult CreateFailedTransfer(bool aIsPrivateBrowsing); michael@0: michael@0: /* michael@0: * The following two functions are part of the split of SaveToDisk michael@0: * to make it async, and works as following: michael@0: * michael@0: * SaveToDisk -------> RequestSaveDestination michael@0: * . michael@0: * . michael@0: * v michael@0: * ContinueSave <------- SaveDestinationAvailable michael@0: */ michael@0: michael@0: /** michael@0: * This is called by SaveToDisk to decide what's the final michael@0: * file destination chosen by the user or by auto-download settings. michael@0: */ michael@0: void RequestSaveDestination(const nsAFlatString &aDefaultFile, michael@0: const nsAFlatString &aDefaultFileExt); michael@0: michael@0: /** michael@0: * When SaveToDisk is called, it possibly delegates to RequestSaveDestination michael@0: * to decide the file destination. ContinueSave must then be called when michael@0: * the final destination is finally known. michael@0: * @param aFile The file that was chosen as the final destination. michael@0: * Must not be null. michael@0: */ michael@0: nsresult ContinueSave(nsIFile* aFile); michael@0: michael@0: /** michael@0: * After we're done prompting the user for any information, if the original michael@0: * channel had a refresh url associated with it (which might point to a michael@0: * "thank you for downloading" kind of page, then process that....It is safe michael@0: * to invoke this method multiple times. We'll clear mOriginalChannel after michael@0: * it's called and this ensures we won't call it again.... michael@0: */ michael@0: void ProcessAnyRefreshTags(); michael@0: michael@0: /** michael@0: * Notify our nsITransfer object that we are done with the download. This is michael@0: * always called after the target file has been closed. michael@0: * michael@0: * @param aStatus michael@0: * NS_OK for success, or a failure code if the download failed. michael@0: * A partially downloaded file may still be available in this case. michael@0: */ michael@0: void NotifyTransfer(nsresult aStatus); michael@0: michael@0: /** michael@0: * Helper routine that searches a pref string for a given mime type michael@0: */ michael@0: bool GetNeverAskFlagFromPref(const char * prefName, const char * aContentType); michael@0: michael@0: /** michael@0: * Helper routine to ensure mSuggestedFileName is "correct"; michael@0: * this ensures that mTempFileExtension only contains an extension when it michael@0: * is different from mSuggestedFileName's extension. michael@0: */ michael@0: void EnsureSuggestedFileName(); michael@0: michael@0: typedef enum { kReadError, kWriteError, kLaunchError } ErrorType; michael@0: /** michael@0: * Utility function to send proper error notification to web progress listener michael@0: */ michael@0: void SendStatusChange(ErrorType type, nsresult aStatus, nsIRequest *aRequest, const nsAFlatString &path); michael@0: michael@0: /** michael@0: * Closes the window context if it does not have a refresh header michael@0: * and it never displayed content before the external helper app michael@0: * service was invoked. michael@0: */ michael@0: nsresult MaybeCloseWindow(); michael@0: michael@0: /** michael@0: * Set in nsHelperDlgApp.js. This is always null after the user has chosen an michael@0: * action. michael@0: */ michael@0: nsCOMPtr mDialogProgressListener; michael@0: /** michael@0: * Set once the user has chosen an action. This is null after the download michael@0: * has been canceled or completes. michael@0: */ michael@0: nsCOMPtr mTransfer; michael@0: michael@0: nsCOMPtr mOriginalChannel; /**< in the case of a redirect, this will be the pre-redirect channel. */ michael@0: nsCOMPtr mDialog; michael@0: michael@0: /** michael@0: * Keep request alive in case when helper non-modal dialog shown. michael@0: * Thus in OnStopRequest the mRequest will not be set to null (it will be set to null further). michael@0: */ michael@0: bool mKeepRequestAlive; michael@0: michael@0: /** michael@0: * The request that's being loaded. Initialized in OnStartRequest. michael@0: * Nulled out in OnStopRequest or once we know what we're doing michael@0: * with the data, whichever happens later. michael@0: */ michael@0: nsCOMPtr mRequest; michael@0: michael@0: nsRefPtr mExtProtSvc; michael@0: }; michael@0: michael@0: #endif // nsExternalHelperAppService_h__