1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef nsExternalHelperAppService_h__
7 #define nsExternalHelperAppService_h__
9 #include "mozilla/Logging.h"
12 #include "nsIExternalHelperAppService.h"
13 #include "nsIExternalProtocolService.h"
14 #include "nsIWebProgressListener2.h"
15 #include "nsIHelperAppLauncherDialog.h"
17 #include "nsILoadInfo.h"
18 #include "nsIMIMEInfo.h"
19 #include "nsIMIMEService.h"
21 #include "nsIStreamListener.h"
23 #include "nsIPermission.h"
25 #include "nsIInterfaceRequestor.h"
26 #include "nsIInterfaceRequestorUtils.h"
27 #include "nsIChannel.h"
28 #include "nsIBackgroundFileSaver.h"
31 #include "nsIObserver.h"
32 #include "nsCOMArray.h"
33 #include "nsWeakReference.h"
34 #include "mozilla/Attributes.h"
36 class nsExternalAppHandler
;
40 class MaybeCloseWindowHelper
;
42 #define EXTERNAL_APP_HANDLER_IID \
44 0x50eb7479, 0x71ff, 0x4ef8, { \
45 0xb3, 0x1e, 0x3b, 0x59, 0xc8, 0xab, 0xb9, 0x24 \
50 * The helper app service. Responsible for handling content that Mozilla
51 * itself can not handle
52 * Note that this is an abstract class - we depend on appropriate subclassing
53 * on a per-OS basis to implement some methods.
55 class nsExternalHelperAppService
: public nsIExternalHelperAppService
,
56 public nsPIExternalAppLauncher
,
57 public nsIExternalProtocolService
,
58 public nsIMIMEService
,
60 public nsSupportsWeakReference
{
63 NS_DECL_NSIEXTERNALHELPERAPPSERVICE
64 NS_DECL_NSPIEXTERNALAPPLAUNCHER
65 NS_DECL_NSIMIMESERVICE
68 nsExternalHelperAppService();
71 * Initializes internal state. Will be called automatically when
72 * this service is first instantiated.
74 [[nodiscard
]] nsresult
Init();
77 * nsIExternalProtocolService methods that we provide in this class. Other
78 * methods should be implemented by per-OS subclasses.
80 NS_IMETHOD
ExternalProtocolHandlerExists(const char* aProtocolScheme
,
81 bool* aHandlerExists
) override
;
82 NS_IMETHOD
IsExposedProtocol(const char* aProtocolScheme
,
83 bool* aResult
) override
;
84 NS_IMETHOD
GetProtocolHandlerInfo(const nsACString
& aScheme
,
85 nsIHandlerInfo
** aHandlerInfo
) override
;
86 NS_IMETHOD
LoadURI(nsIURI
* aURI
, nsIPrincipal
* aTriggeringPrincipal
,
87 mozilla::dom::BrowsingContext
* aBrowsingContext
,
88 bool aWasTriggeredExternally
) override
;
89 NS_IMETHOD
SetProtocolHandlerDefaults(nsIHandlerInfo
* aHandlerInfo
,
90 bool aOSHandlerExists
) override
;
93 * Given a string identifying an application, create an nsIFile representing
94 * it. This function should look in $PATH for the application.
95 * The base class implementation will first try to interpret platformAppPath
96 * as an absolute path, and if that fails it will look for a file next to the
97 * mozilla executable. Subclasses can override this method if they want a
98 * different behaviour.
99 * @param platformAppPath A platform specific path to an application that we
100 * got out of the rdf data source. This can be a mac
101 * file spec, a unix path or a windows path depending
103 * @param aFile [out] An nsIFile representation of that platform
106 virtual nsresult
GetFileTokenForPath(const char16_t
* platformAppPath
,
109 NS_IMETHOD
OSProtocolHandlerExists(const char* aScheme
, bool* aExists
) = 0;
112 * Given an extension, get a MIME type string. If not overridden by
113 * the OS-specific nsOSHelperAppService, will call into GetMIMEInfoFromOS
114 * with an empty mimetype.
115 * @return true if we successfully found a mimetype.
117 virtual bool GetMIMETypeFromOSForExtension(const nsACString
& aExtension
,
118 nsACString
& aMIMEType
);
120 static already_AddRefed
<nsExternalHelperAppService
> GetSingleton();
123 virtual ~nsExternalHelperAppService();
126 * Searches the "extra" array of MIMEInfo objects for an object
127 * with a specific type. If found, it will modify the passed-in
128 * MIMEInfo. Otherwise, it will return an error and the MIMEInfo
130 * @param aContentType The type to search for.
131 * @param aOverwriteDescription Whether to overwrite the description
132 * @param aMIMEInfo [inout] The mime info, if found
134 nsresult
FillMIMEInfoForMimeTypeFromExtras(const nsACString
& aContentType
,
135 bool aOverwriteDescription
,
136 nsIMIMEInfo
* aMIMEInfo
);
138 * Searches the "extra" array of MIMEInfo objects for an object
139 * with a specific extension.
141 * Does not change the MIME Type of the MIME Info.
143 * @see FillMIMEInfoForMimeTypeFromExtras
145 nsresult
FillMIMEInfoForExtensionFromExtras(const nsACString
& aExtension
,
146 nsIMIMEInfo
* aMIMEInfo
);
149 * Replace the primary extension of the mimeinfo object if it's in our
150 * list of forbidden extensions. This fixes up broken information
151 * provided to us by the OS.
153 bool MaybeReplacePrimaryExtension(const nsACString
& aPrimaryExtension
,
154 nsIMIMEInfo
* aMIMEInfo
);
157 * Searches the "extra" array for a MIME type, and gets its extension.
158 * @param aExtension The extension to search for
159 * @param aMIMEType [out] The found MIME type.
160 * @return true if the extension was found, false otherwise.
162 bool GetTypeFromExtras(const nsACString
& aExtension
, nsACString
& aMIMEType
);
165 * Logging Module. Usage: set MOZ_LOG=HelperAppService:level, where level
166 * should be 2 for errors, 3 for debug messages from the cross- platform
167 * nsExternalHelperAppService, and 4 for os-specific debug messages.
169 static mozilla::LazyLogModule mLog
;
171 // friend, so that it can access the nspr log module.
172 friend class nsExternalAppHandler
;
175 * Helper function for ExpungeTemporaryFiles and ExpungeTemporaryPrivateFiles
177 static void ExpungeTemporaryFilesHelper(nsCOMArray
<nsIFile
>& fileList
);
179 * Helper function for DeleteTemporaryFileOnExit and
180 * DeleteTemporaryPrivateFileWhenPossible
182 static nsresult
DeleteTemporaryFileHelper(nsIFile
* aTemporaryFile
,
183 nsCOMArray
<nsIFile
>& aFileList
);
185 * Functions related to the tempory file cleanup service provided by
186 * nsExternalHelperAppService
188 void ExpungeTemporaryFiles();
190 * Functions related to the tempory file cleanup service provided by
191 * nsExternalHelperAppService (for the temporary files added during
192 * the private browsing mode)
194 void ExpungeTemporaryPrivateFiles();
197 * Array for the files that should be deleted
199 nsCOMArray
<nsIFile
> mTemporaryFilesList
;
201 * Array for the files that should be deleted (for the temporary files
202 * added during the private browsing mode)
204 nsCOMArray
<nsIFile
> mTemporaryPrivateFilesList
;
207 nsresult
DoContentContentProcessHelper(
208 const nsACString
& aMimeContentType
, nsIRequest
* aRequest
,
209 mozilla::dom::BrowsingContext
* aContentContext
, bool aForceSave
,
210 nsIInterfaceRequestor
* aWindowContext
,
211 nsIStreamListener
** aStreamListener
);
215 * An external app handler is just a small little class that presents itself as
216 * a nsIStreamListener. It saves the incoming data into a temp file. The handler
217 * is bound to an application when it is created. When it receives an
218 * OnStopRequest it launches the application using the temp file it has
219 * stored the data into. We create a handler every time we have to process
220 * data using a helper app.
222 class nsExternalAppHandler final
: public nsIStreamListener
,
223 public nsIHelperAppLauncher
,
224 public nsIBackgroundFileSaverObserver
,
227 NS_DECL_THREADSAFE_ISUPPORTS
228 NS_DECL_NSISTREAMLISTENER
229 NS_DECL_NSIREQUESTOBSERVER
230 NS_DECL_NSIHELPERAPPLAUNCHER
231 NS_DECL_NSICANCELABLE
232 NS_DECL_NSIBACKGROUNDFILESAVEROBSERVER
235 NS_DECLARE_STATIC_IID_ACCESSOR(EXTERNAL_APP_HANDLER_IID
)
238 * @param aMIMEInfo MIMEInfo object, representing the type of the
239 * content that should be handled
240 * @param aFileExtension The extension we need to append to our temp file,
241 * INCLUDING the ".". e.g. .mp3
242 * @param aContentContext dom Window context, as passed to DoContent.
243 * @param aWindowContext Top level window context used in dialog parenting,
244 * as passed to DoContent. This parameter may be null,
245 * in which case dialogs will be parented to
247 * @param mExtProtSvc nsExternalHelperAppService on creation
248 * @param aFileName The filename to use
249 * @param aReason A constant from nsIHelperAppLauncherDialog
250 * indicating why the request is handled by a helper app.
252 nsExternalAppHandler(nsIMIMEInfo
* aMIMEInfo
, const nsACString
& aFileExtension
,
253 mozilla::dom::BrowsingContext
* aBrowsingContext
,
254 nsIInterfaceRequestor
* aWindowContext
,
255 nsExternalHelperAppService
* aExtProtSvc
,
256 const nsAString
& aFilename
, uint32_t aReason
,
260 * Clean up after the request was diverted to the parent process.
262 void DidDivertRequest(nsIRequest
* request
);
265 * Apply content conversions if needed.
267 void MaybeApplyDecodingForExtension(nsIRequest
* request
);
269 void SetShouldCloseWindow() { mShouldCloseWindow
= true; }
272 bool IsDownloadSpam(nsIChannel
* aChannel
);
274 ~nsExternalAppHandler();
276 nsCOMPtr
<nsIFile
> mTempFile
;
277 nsCOMPtr
<nsIURI
> mSourceUrl
;
278 nsString mTempFileExtension
;
279 nsString mTempLeafName
;
282 * The MIME Info for this load. Will never be null.
284 nsCOMPtr
<nsIMIMEInfo
> mMimeInfo
;
287 * The BrowsingContext associated with this request to handle content.
289 RefPtr
<mozilla::dom::BrowsingContext
> mBrowsingContext
;
292 * If set, the parent window helper app dialogs and file pickers
293 * should use in parenting. If null, we use mContentContext.
295 nsCOMPtr
<nsIInterfaceRequestor
> mWindowContext
;
298 * Used to close the window on a timer, to avoid any exceptions that are
299 * thrown if we try to close the window before it's fully loaded.
301 RefPtr
<MaybeCloseWindowHelper
> mMaybeCloseWindowHelper
;
304 * The following field is set if we were processing an http channel that had
305 * a content disposition header which specified the SUGGESTED file name we
306 * should present to the user in the save to disk dialog.
308 nsString mSuggestedFileName
;
311 * If set, this handler should forcibly save the file to disk regardless of
312 * MIME info settings or anything else, without ever popping up the
313 * unknown content type handling dialog.
318 * The canceled flag is set if the user canceled the launching of this
319 * application before we finished saving the data to a temp file.
324 * True if a stop request has been issued.
326 bool mStopRequestIssued
;
331 * True if the ExternalHelperAppChild told us that we should close the window
332 * if we handle the content as a download.
334 bool mShouldCloseWindow
;
337 * True if the file should be handled internally.
339 bool mHandleInternally
;
342 * One of the REASON_ constants from nsIHelperAppLauncherDialog. Indicates the
343 * reason the dialog was shown (unknown content type, server requested it,
349 * Indicates if the nsContentSecurityUtils rate this download as
350 * acceptable, potentialy unwanted or illigal request.
353 int32_t mDownloadClassification
;
356 * Track the executable-ness of the temporary file.
358 bool mTempFileIsExecutable
;
360 PRTime mTimeDownloadStarted
;
361 int64_t mContentLength
;
362 int64_t mProgress
; /**< Number of bytes received (for sending progress
366 * When we are told to save the temp file to disk (in a more permament
367 * location) before we are done writing the content to a temp file, then
368 * we need to remember the final destination until we are ready to use it.
370 nsCOMPtr
<nsIFile
> mFinalFileDestination
;
372 uint32_t mBufferSize
;
375 * This object handles saving the data received from the network to a
376 * temporary location first, and then move the file to its final location,
377 * doing all the input/output on a background thread.
379 nsCOMPtr
<nsIBackgroundFileSaver
> mSaver
;
382 * Stores the SHA-256 hash associated with the file that we downloaded.
386 * Stores the signature information of the downloaded file in an nsTArray of
387 * nsTArray of Array of bytes. If the file is unsigned this will be
390 nsTArray
<nsTArray
<nsTArray
<uint8_t>>> mSignatureInfo
;
392 * Stores the redirect information associated with the channel.
394 nsCOMPtr
<nsIArray
> mRedirects
;
396 * Get the dialog parent: the parent window that we can attach
397 * a dialog to when prompting the user for a download.
399 already_AddRefed
<nsIInterfaceRequestor
> GetDialogParent();
401 * Creates the temporary file for the download and an output stream for it.
402 * Upon successful return, both mTempFile and mSaver will be valid.
404 nsresult
SetUpTempFile(nsIChannel
* aChannel
);
406 * When we download a helper app, we are going to retarget all load
407 * notifications into our own docloader and load group instead of
408 * using the window which initiated the load....RetargetLoadNotifications
409 * contains that information...
411 void RetargetLoadNotifications(nsIRequest
* request
);
413 * Once the user tells us how they want to dispose of the content
414 * create an nsITransfer so they know what's going on. If this fails, the
415 * caller MUST call Cancel.
417 nsresult
CreateTransfer();
420 * If we fail to create the necessary temporary file to initiate a transfer
421 * we will report the failure by creating a failed nsITransfer.
423 nsresult
CreateFailedTransfer();
426 * The following two functions are part of the split of SaveToDisk
427 * to make it async, and works as following:
429 * SaveToDisk -------> RequestSaveDestination
433 * ContinueSave <------- SaveDestinationAvailable
437 * This is called by SaveToDisk to decide what's the final
438 * file destination chosen by the user or by auto-download settings.
440 void RequestSaveDestination(const nsString
& aDefaultFile
,
441 const nsString
& aDefaultFileExt
);
444 * When SaveToDisk is called, it possibly delegates to RequestSaveDestination
445 * to decide the file destination. ContinueSave must then be called when
446 * the final destination is finally known.
447 * @param aFile The file that was chosen as the final destination.
450 nsresult
ContinueSave(nsIFile
* aFile
);
453 * Notify our nsITransfer object that we are done with the download. This is
454 * always called after the target file has been closed.
457 * NS_OK for success, or a failure code if the download failed.
458 * A partially downloaded file may still be available in this case.
460 void NotifyTransfer(nsresult aStatus
);
463 * Helper routine that searches a pref string for a given mime type
465 bool GetNeverAskFlagFromPref(const char* prefName
, const char* aContentType
);
468 * Helper routine that checks whether we should enforce an extension
471 bool ShouldForceExtension(const nsString
& aFileExt
);
474 * Helper routine to ensure that mSuggestedFileName ends in the correct
475 * extension, in case the original extension contains invalid characters
476 * or if this download is for a mimetype where we enforce using a specific
477 * extension (image/, video/, and audio/ based mimetypes, and a few specific
480 * It also ensure that mTempFileExtension only contains an extension
481 * when it is different from mSuggestedFileName's extension.
483 void EnsureCorrectExtension(const nsString
& aFileExt
);
485 typedef enum { kReadError
, kWriteError
, kLaunchError
} ErrorType
;
487 * Utility function to send proper error notification to web progress listener
489 void SendStatusChange(ErrorType type
, nsresult aStatus
, nsIRequest
* aRequest
,
490 const nsString
& path
);
493 * Set in nsHelperDlgApp.js. This is always null after the user has chosen an
496 nsCOMPtr
<nsIWebProgressListener2
> mDialogProgressListener
;
498 * Set once the user has chosen an action. This is null after the download
499 * has been canceled or completes.
501 nsCOMPtr
<nsITransfer
> mTransfer
;
503 nsCOMPtr
<nsIHelperAppLauncherDialog
> mDialog
;
507 * The request that's being loaded. Initialized in OnStartRequest.
508 * Nulled out in OnStopRequest or once we know what we're doing
509 * with the data, whichever happens later.
511 nsCOMPtr
<nsIRequest
> mRequest
;
513 RefPtr
<nsExternalHelperAppService
> mExtProtSvc
;
515 NS_DEFINE_STATIC_IID_ACCESSOR(nsExternalAppHandler
, EXTERNAL_APP_HANDLER_IID
)
517 #endif // nsExternalHelperAppService_h__