Line data Source code
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/. */
5 :
6 : #ifndef nsExternalHelperAppService_h__
7 : #define nsExternalHelperAppService_h__
8 :
9 : #include "mozilla/Logging.h"
10 : #include "prtime.h"
11 :
12 : #include "nsIExternalHelperAppService.h"
13 : #include "nsIExternalProtocolService.h"
14 : #include "nsIWebProgressListener2.h"
15 : #include "nsIHelperAppLauncherDialog.h"
16 :
17 : #include "nsIMIMEInfo.h"
18 : #include "nsIMIMEService.h"
19 : #include "nsIStreamListener.h"
20 : #include "nsIFile.h"
21 : #include "nsIFileStreams.h"
22 : #include "nsIOutputStream.h"
23 : #include "nsString.h"
24 : #include "nsIInterfaceRequestor.h"
25 : #include "nsIInterfaceRequestorUtils.h"
26 : #include "nsIChannel.h"
27 : #include "nsITimer.h"
28 : #include "nsIBackgroundFileSaver.h"
29 :
30 : #include "nsIHandlerService.h"
31 : #include "nsCOMPtr.h"
32 : #include "nsIObserver.h"
33 : #include "nsCOMArray.h"
34 : #include "nsWeakReference.h"
35 : #include "nsIPrompt.h"
36 : #include "nsAutoPtr.h"
37 : #include "mozilla/Attributes.h"
38 :
39 : class nsExternalAppHandler;
40 : class nsIMIMEInfo;
41 : class nsITransfer;
42 : class nsPIDOMWindowOuter;
43 :
44 : /**
45 : * The helper app service. Responsible for handling content that Mozilla
46 : * itself can not handle
47 : */
48 : class nsExternalHelperAppService
49 : : public nsIExternalHelperAppService,
50 : public nsPIExternalAppLauncher,
51 : public nsIExternalProtocolService,
52 : public nsIMIMEService,
53 : public nsIObserver,
54 : public nsSupportsWeakReference
55 : {
56 : public:
57 : NS_DECL_ISUPPORTS
58 : NS_DECL_NSIEXTERNALHELPERAPPSERVICE
59 : NS_DECL_NSPIEXTERNALAPPLAUNCHER
60 : NS_DECL_NSIEXTERNALPROTOCOLSERVICE
61 : NS_DECL_NSIMIMESERVICE
62 : NS_DECL_NSIOBSERVER
63 :
64 : nsExternalHelperAppService();
65 :
66 : /**
67 : * Initializes internal state. Will be called automatically when
68 : * this service is first instantiated.
69 : */
70 : MOZ_MUST_USE nsresult Init();
71 :
72 : /**
73 : * Given a mimetype and an extension, looks up a mime info from the OS.
74 : * The mime type is given preference. This function follows the same rules
75 : * as nsIMIMEService::GetFromTypeAndExtension.
76 : * This is supposed to be overridden by the platform-specific
77 : * nsOSHelperAppService!
78 : * @param aFileExt The file extension; may be empty. UTF-8 encoded.
79 : * @param [out] aFound
80 : * Should be set to true if the os has a mapping, to
81 : * false otherwise. Must not be null.
82 : * @return A MIMEInfo. This function must return a MIMEInfo object if it
83 : * can allocate one. The only justifiable reason for not
84 : * returning one is an out-of-memory error.
85 : * If null, the value of aFound is unspecified.
86 : */
87 : virtual already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(const nsACString& aMIMEType,
88 : const nsACString& aFileExt,
89 : bool * aFound) = 0;
90 :
91 : /**
92 : * Given a string identifying an application, create an nsIFile representing
93 : * it. This function should look in $PATH for the application.
94 : * The base class implementation will first try to interpret platformAppPath
95 : * as an absolute path, and if that fails it will look for a file next to the
96 : * mozilla executable. Subclasses can override this method if they want a
97 : * different behaviour.
98 : * @param platformAppPath A platform specific path to an application that we
99 : * got out of the rdf data source. This can be a mac
100 : * file spec, a unix path or a windows path depending
101 : * on the platform
102 : * @param aFile [out] An nsIFile representation of that platform
103 : * application path.
104 : */
105 : virtual nsresult GetFileTokenForPath(const char16_t * platformAppPath,
106 : nsIFile ** aFile);
107 :
108 : virtual nsresult OSProtocolHandlerExists(const char *aScheme,
109 : bool *aExists) = 0;
110 :
111 : /**
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.
116 : */
117 : virtual bool GetMIMETypeFromOSForExtension(const nsACString& aExtension,
118 : nsACString& aMIMEType);
119 :
120 : protected:
121 : virtual ~nsExternalHelperAppService();
122 :
123 : /**
124 : * Searches the "extra" array of MIMEInfo objects for an object
125 : * with a specific type. If found, it will modify the passed-in
126 : * MIMEInfo. Otherwise, it will return an error and the MIMEInfo
127 : * will be untouched.
128 : * @param aContentType The type to search for.
129 : * @param aMIMEInfo [inout] The mime info, if found
130 : */
131 : nsresult FillMIMEInfoForMimeTypeFromExtras(
132 : const nsACString& aContentType, nsIMIMEInfo * aMIMEInfo);
133 : /**
134 : * Searches the "extra" array of MIMEInfo objects for an object
135 : * with a specific extension.
136 : *
137 : * Does not change the MIME Type of the MIME Info.
138 : *
139 : * @see FillMIMEInfoForMimeTypeFromExtras
140 : */
141 : nsresult FillMIMEInfoForExtensionFromExtras(
142 : const nsACString& aExtension, nsIMIMEInfo * aMIMEInfo);
143 :
144 : /**
145 : * Searches the "extra" array for a MIME type, and gets its extension.
146 : * @param aExtension The extension to search for
147 : * @param aMIMEType [out] The found MIME type.
148 : * @return true if the extension was found, false otherwise.
149 : */
150 : bool GetTypeFromExtras(const nsACString& aExtension,
151 : nsACString& aMIMEType);
152 :
153 : /**
154 : * Logging Module. Usage: set MOZ_LOG=HelperAppService:level, where level
155 : * should be 2 for errors, 3 for debug messages from the cross- platform
156 : * nsExternalHelperAppService, and 4 for os-specific debug messages.
157 : */
158 : static mozilla::LazyLogModule mLog;
159 :
160 : // friend, so that it can access the nspr log module.
161 : friend class nsExternalAppHandler;
162 :
163 : /**
164 : * Helper function for ExpungeTemporaryFiles and ExpungeTemporaryPrivateFiles
165 : */
166 : static void ExpungeTemporaryFilesHelper(nsCOMArray<nsIFile> &fileList);
167 : /**
168 : * Helper function for DeleteTemporaryFileOnExit and DeleteTemporaryPrivateFileWhenPossible
169 : */
170 : static nsresult DeleteTemporaryFileHelper(nsIFile* aTemporaryFile,
171 : nsCOMArray<nsIFile> &aFileList);
172 : /**
173 : * Functions related to the tempory file cleanup service provided by
174 : * nsExternalHelperAppService
175 : */
176 : void ExpungeTemporaryFiles();
177 : /**
178 : * Functions related to the tempory file cleanup service provided by
179 : * nsExternalHelperAppService (for the temporary files added during
180 : * the private browsing mode)
181 : */
182 : void ExpungeTemporaryPrivateFiles();
183 :
184 : /**
185 : * Array for the files that should be deleted
186 : */
187 : nsCOMArray<nsIFile> mTemporaryFilesList;
188 : /**
189 : * Array for the files that should be deleted (for the temporary files
190 : * added during the private browsing mode)
191 : */
192 : nsCOMArray<nsIFile> mTemporaryPrivateFilesList;
193 :
194 : private:
195 : nsresult DoContentContentProcessHelper(const nsACString& aMimeContentType,
196 : nsIRequest *aRequest,
197 : nsIInterfaceRequestor *aContentContext,
198 : bool aForceSave,
199 : nsIInterfaceRequestor *aWindowContext,
200 : nsIStreamListener ** aStreamListener);
201 : };
202 :
203 : /**
204 : * An external app handler is just a small little class that presents itself as
205 : * a nsIStreamListener. It saves the incoming data into a temp file. The handler
206 : * is bound to an application when it is created. When it receives an
207 : * OnStopRequest it launches the application using the temp file it has
208 : * stored the data into. We create a handler every time we have to process
209 : * data using a helper app.
210 : */
211 : class nsExternalAppHandler final : public nsIStreamListener,
212 : public nsIHelperAppLauncher,
213 : public nsITimerCallback,
214 : public nsIBackgroundFileSaverObserver
215 : {
216 : public:
217 : NS_DECL_THREADSAFE_ISUPPORTS
218 : NS_DECL_NSISTREAMLISTENER
219 : NS_DECL_NSIREQUESTOBSERVER
220 : NS_DECL_NSIHELPERAPPLAUNCHER
221 : NS_DECL_NSICANCELABLE
222 : NS_DECL_NSITIMERCALLBACK
223 : NS_DECL_NSIBACKGROUNDFILESAVEROBSERVER
224 :
225 : /**
226 : * @param aMIMEInfo MIMEInfo object, representing the type of the
227 : * content that should be handled
228 : * @param aFileExtension The extension we need to append to our temp file,
229 : * INCLUDING the ".". e.g. .mp3
230 : * @param aContentContext dom Window context, as passed to DoContent.
231 : * @param aWindowContext Top level window context used in dialog parenting,
232 : * as passed to DoContent. This parameter may be null,
233 : * in which case dialogs will be parented to
234 : * aContentContext.
235 : * @param mExtProtSvc nsExternalHelperAppService on creation
236 : * @param aFileName The filename to use
237 : * @param aReason A constant from nsIHelperAppLauncherDialog indicating
238 : * why the request is handled by a helper app.
239 : */
240 : nsExternalAppHandler(nsIMIMEInfo * aMIMEInfo, const nsACString& aFileExtension,
241 : nsIInterfaceRequestor * aContentContext,
242 : nsIInterfaceRequestor * aWindowContext,
243 : nsExternalHelperAppService * aExtProtSvc,
244 : const nsAString& aFilename,
245 : uint32_t aReason, bool aForceSave);
246 :
247 : /**
248 : * Clean up after the request was diverted to the parent process.
249 : */
250 : void DidDivertRequest(nsIRequest *request);
251 :
252 : /**
253 : * Apply content conversions if needed.
254 : */
255 : void MaybeApplyDecodingForExtension(nsIRequest *request);
256 :
257 : /**
258 : * Get the dialog parent. Public for ExternalHelperAppChild::OnStartRequest.
259 : */
260 0 : nsIInterfaceRequestor* GetDialogParent() {
261 0 : return mWindowContext ? mWindowContext : mContentContext;
262 : }
263 :
264 0 : void SetContentContext(nsIInterfaceRequestor* context) {
265 0 : MOZ_ASSERT(!mWindowContext);
266 0 : mContentContext = context;
267 0 : }
268 :
269 : protected:
270 : ~nsExternalAppHandler();
271 :
272 : nsCOMPtr<nsIFile> mTempFile;
273 : nsCOMPtr<nsIURI> mSourceUrl;
274 : nsString mTempFileExtension;
275 : nsString mTempLeafName;
276 :
277 : /**
278 : * The MIME Info for this load. Will never be null.
279 : */
280 : nsCOMPtr<nsIMIMEInfo> mMimeInfo;
281 :
282 : /**
283 : * The dom window associated with this request to handle content.
284 : */
285 : nsCOMPtr<nsIInterfaceRequestor> mContentContext;
286 :
287 : /**
288 : * If set, the parent window helper app dialogs and file pickers
289 : * should use in parenting. If null, we use mContentContext.
290 : */
291 : nsCOMPtr<nsIInterfaceRequestor> mWindowContext;
292 :
293 : /**
294 : * Used to close the window on a timer, to avoid any exceptions that are
295 : * thrown if we try to close the window before it's fully loaded.
296 : */
297 : nsCOMPtr<nsPIDOMWindowOuter> mWindowToClose;
298 : nsCOMPtr<nsITimer> mTimer;
299 :
300 : /**
301 : * The following field is set if we were processing an http channel that had
302 : * a content disposition header which specified the SUGGESTED file name we
303 : * should present to the user in the save to disk dialog.
304 : */
305 : nsString mSuggestedFileName;
306 :
307 : /**
308 : * If set, this handler should forcibly save the file to disk regardless of
309 : * MIME info settings or anything else, without ever popping up the
310 : * unknown content type handling dialog.
311 : */
312 : bool mForceSave;
313 :
314 : /**
315 : * The canceled flag is set if the user canceled the launching of this
316 : * application before we finished saving the data to a temp file.
317 : */
318 : bool mCanceled;
319 :
320 : /**
321 : * This is set based on whether the channel indicates that a new window
322 : * was opened specifically for this download. If so, then we
323 : * close it.
324 : */
325 : bool mShouldCloseWindow;
326 :
327 : /**
328 : * True if a stop request has been issued.
329 : */
330 : bool mStopRequestIssued;
331 :
332 : bool mIsFileChannel;
333 :
334 : /**
335 : * One of the REASON_ constants from nsIHelperAppLauncherDialog. Indicates the
336 : * reason the dialog was shown (unknown content type, server requested it,
337 : * etc).
338 : */
339 : uint32_t mReason;
340 :
341 : /**
342 : * Track the executable-ness of the temporary file.
343 : */
344 : bool mTempFileIsExecutable;
345 :
346 : PRTime mTimeDownloadStarted;
347 : int64_t mContentLength;
348 : int64_t mProgress; /**< Number of bytes received (for sending progress notifications). */
349 :
350 : /**
351 : * When we are told to save the temp file to disk (in a more permament
352 : * location) before we are done writing the content to a temp file, then
353 : * we need to remember the final destination until we are ready to use it.
354 : */
355 : nsCOMPtr<nsIFile> mFinalFileDestination;
356 :
357 : uint32_t mBufferSize;
358 :
359 : /**
360 : * This object handles saving the data received from the network to a
361 : * temporary location first, and then move the file to its final location,
362 : * doing all the input/output on a background thread.
363 : */
364 : nsCOMPtr<nsIBackgroundFileSaver> mSaver;
365 :
366 : /**
367 : * Stores the SHA-256 hash associated with the file that we downloaded.
368 : */
369 : nsAutoCString mHash;
370 : /**
371 : * Stores the signature information of the downloaded file in an nsIArray of
372 : * nsIX509CertList of nsIX509Cert. If the file is unsigned this will be
373 : * empty.
374 : */
375 : nsCOMPtr<nsIArray> mSignatureInfo;
376 : /**
377 : * Stores the redirect information associated with the channel.
378 : */
379 : nsCOMPtr<nsIArray> mRedirects;
380 : /**
381 : * Creates the temporary file for the download and an output stream for it.
382 : * Upon successful return, both mTempFile and mSaver will be valid.
383 : */
384 : nsresult SetUpTempFile(nsIChannel * aChannel);
385 : /**
386 : * When we download a helper app, we are going to retarget all load
387 : * notifications into our own docloader and load group instead of
388 : * using the window which initiated the load....RetargetLoadNotifications
389 : * contains that information...
390 : */
391 : void RetargetLoadNotifications(nsIRequest *request);
392 : /**
393 : * Once the user tells us how they want to dispose of the content
394 : * create an nsITransfer so they know what's going on. If this fails, the
395 : * caller MUST call Cancel.
396 : */
397 : nsresult CreateTransfer();
398 :
399 : /**
400 : * If we fail to create the necessary temporary file to initiate a transfer
401 : * we will report the failure by creating a failed nsITransfer.
402 : */
403 : nsresult CreateFailedTransfer(bool aIsPrivateBrowsing);
404 :
405 : /*
406 : * The following two functions are part of the split of SaveToDisk
407 : * to make it async, and works as following:
408 : *
409 : * SaveToDisk -------> RequestSaveDestination
410 : * .
411 : * .
412 : * v
413 : * ContinueSave <------- SaveDestinationAvailable
414 : */
415 :
416 : /**
417 : * This is called by SaveToDisk to decide what's the final
418 : * file destination chosen by the user or by auto-download settings.
419 : */
420 : void RequestSaveDestination(const nsString& aDefaultFile,
421 : const nsString& aDefaultFileExt);
422 :
423 : /**
424 : * When SaveToDisk is called, it possibly delegates to RequestSaveDestination
425 : * to decide the file destination. ContinueSave must then be called when
426 : * the final destination is finally known.
427 : * @param aFile The file that was chosen as the final destination.
428 : * Must not be null.
429 : */
430 : nsresult ContinueSave(nsIFile* aFile);
431 :
432 : /**
433 : * After we're done prompting the user for any information, if the original
434 : * channel had a refresh url associated with it (which might point to a
435 : * "thank you for downloading" kind of page, then process that....It is safe
436 : * to invoke this method multiple times. We'll clear mOriginalChannel after
437 : * it's called and this ensures we won't call it again....
438 : */
439 : void ProcessAnyRefreshTags();
440 :
441 : /**
442 : * Notify our nsITransfer object that we are done with the download. This is
443 : * always called after the target file has been closed.
444 : *
445 : * @param aStatus
446 : * NS_OK for success, or a failure code if the download failed.
447 : * A partially downloaded file may still be available in this case.
448 : */
449 : void NotifyTransfer(nsresult aStatus);
450 :
451 : /**
452 : * Helper routine that searches a pref string for a given mime type
453 : */
454 : bool GetNeverAskFlagFromPref(const char * prefName, const char * aContentType);
455 :
456 : /**
457 : * Helper routine to ensure mSuggestedFileName is "correct";
458 : * this ensures that mTempFileExtension only contains an extension when it
459 : * is different from mSuggestedFileName's extension.
460 : */
461 : void EnsureSuggestedFileName();
462 :
463 : typedef enum { kReadError, kWriteError, kLaunchError } ErrorType;
464 : /**
465 : * Utility function to send proper error notification to web progress listener
466 : */
467 : void SendStatusChange(ErrorType type, nsresult aStatus, nsIRequest *aRequest, const nsString& path);
468 :
469 : /**
470 : * Closes the window context if it does not have a refresh header
471 : * and it never displayed content before the external helper app
472 : * service was invoked.
473 : */
474 : nsresult MaybeCloseWindow();
475 :
476 : /**
477 : * Set in nsHelperDlgApp.js. This is always null after the user has chosen an
478 : * action.
479 : */
480 : nsCOMPtr<nsIWebProgressListener2> mDialogProgressListener;
481 : /**
482 : * Set once the user has chosen an action. This is null after the download
483 : * has been canceled or completes.
484 : */
485 : nsCOMPtr<nsITransfer> mTransfer;
486 :
487 : nsCOMPtr<nsIChannel> mOriginalChannel; /**< in the case of a redirect, this will be the pre-redirect channel. */
488 : nsCOMPtr<nsIHelperAppLauncherDialog> mDialog;
489 :
490 : /**
491 : * Keep request alive in case when helper non-modal dialog shown.
492 : * Thus in OnStopRequest the mRequest will not be set to null (it will be set to null further).
493 : */
494 : bool mKeepRequestAlive;
495 :
496 : /**
497 : * The request that's being loaded. Initialized in OnStartRequest.
498 : * Nulled out in OnStopRequest or once we know what we're doing
499 : * with the data, whichever happens later.
500 : */
501 : nsCOMPtr<nsIRequest> mRequest;
502 :
503 : RefPtr<nsExternalHelperAppService> mExtProtSvc;
504 : };
505 :
506 : #endif // nsExternalHelperAppService_h__
|