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