Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/uriloader/exthandler/nsExternalHelperAppService.h
Line
Count
Source (jump to first uncovered line)
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 "nsINamed.h"
20
#include "nsIStreamListener.h"
21
#include "nsIFile.h"
22
#include "nsIFileStreams.h"
23
#include "nsIOutputStream.h"
24
#include "nsString.h"
25
#include "nsIInterfaceRequestor.h"
26
#include "nsIInterfaceRequestorUtils.h"
27
#include "nsIChannel.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 MaybeCloseWindowHelper;
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 nsIBackgroundFileSaverObserver,
214
                                   public nsINamed
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_NSIBACKGROUNDFILESAVEROBSERVER
223
  NS_DECL_NSINAMED
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
0
  }
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
  RefPtr<MaybeCloseWindowHelper> mMaybeCloseWindowHelper;
298
299
  /**
300
   * The following field is set if we were processing an http channel that had
301
   * a content disposition header which specified the SUGGESTED file name we
302
   * should present to the user in the save to disk dialog. 
303
   */
304
  nsString mSuggestedFileName;
305
306
  /**
307
   * If set, this handler should forcibly save the file to disk regardless of
308
   * MIME info settings or anything else, without ever popping up the 
309
   * unknown content type handling dialog.
310
   */
311
  bool mForceSave;
312
  
313
  /**
314
   * The canceled flag is set if the user canceled the launching of this
315
   * application before we finished saving the data to a temp file.
316
   */
317
  bool mCanceled;
318
319
  /**
320
   * True if a stop request has been issued.
321
   */
322
  bool mStopRequestIssued; 
323
324
  bool mIsFileChannel;
325
326
  /**
327
   * One of the REASON_ constants from nsIHelperAppLauncherDialog. Indicates the
328
   * reason the dialog was shown (unknown content type, server requested it,
329
   * etc).
330
   */
331
  uint32_t mReason;
332
333
  /**
334
   * Track the executable-ness of the temporary file.
335
   */
336
  bool mTempFileIsExecutable;
337
338
  PRTime mTimeDownloadStarted;
339
  int64_t mContentLength;
340
  int64_t mProgress; /**< Number of bytes received (for sending progress notifications). */
341
342
  /**
343
   * When we are told to save the temp file to disk (in a more permament
344
   * location) before we are done writing the content to a temp file, then
345
   * we need to remember the final destination until we are ready to use it.
346
   */
347
  nsCOMPtr<nsIFile> mFinalFileDestination;
348
349
  uint32_t mBufferSize;
350
351
  /**
352
   * This object handles saving the data received from the network to a
353
   * temporary location first, and then move the file to its final location,
354
   * doing all the input/output on a background thread.
355
   */
356
  nsCOMPtr<nsIBackgroundFileSaver> mSaver;
357
358
  /**
359
   * Stores the SHA-256 hash associated with the file that we downloaded.
360
   */
361
  nsAutoCString mHash;
362
  /**
363
   * Stores the signature information of the downloaded file in an nsIArray of
364
   * nsIX509CertList of nsIX509Cert. If the file is unsigned this will be
365
   * empty.
366
   */
367
  nsCOMPtr<nsIArray> mSignatureInfo;
368
  /**
369
   * Stores the redirect information associated with the channel.
370
   */
371
  nsCOMPtr<nsIArray> mRedirects;
372
  /**
373
   * Creates the temporary file for the download and an output stream for it.
374
   * Upon successful return, both mTempFile and mSaver will be valid.
375
   */
376
  nsresult SetUpTempFile(nsIChannel * aChannel);
377
  /**
378
   * When we download a helper app, we are going to retarget all load
379
   * notifications into our own docloader and load group instead of
380
   * using the window which initiated the load....RetargetLoadNotifications
381
   * contains that information...
382
   */
383
  void RetargetLoadNotifications(nsIRequest *request);
384
  /**
385
   * Once the user tells us how they want to dispose of the content
386
   * create an nsITransfer so they know what's going on. If this fails, the
387
   * caller MUST call Cancel.
388
   */
389
  nsresult CreateTransfer();
390
391
  /**
392
   * If we fail to create the necessary temporary file to initiate a transfer
393
   * we will report the failure by creating a failed nsITransfer.
394
   */
395
  nsresult CreateFailedTransfer(bool aIsPrivateBrowsing);
396
397
  /*
398
   * The following two functions are part of the split of SaveToDisk
399
   * to make it async, and works as following:
400
   *
401
   *    SaveToDisk    ------->   RequestSaveDestination
402
   *                                     .
403
   *                                     .
404
   *                                     v
405
   *    ContinueSave  <-------   SaveDestinationAvailable
406
   */
407
408
  /**
409
   * This is called by SaveToDisk to decide what's the final
410
   * file destination chosen by the user or by auto-download settings.
411
   */
412
  void RequestSaveDestination(const nsString& aDefaultFile,
413
                              const nsString& aDefaultFileExt);
414
415
  /**
416
   * When SaveToDisk is called, it possibly delegates to RequestSaveDestination
417
   * to decide the file destination. ContinueSave must then be called when
418
   * the final destination is finally known.
419
   * @param  aFile  The file that was chosen as the final destination.
420
   *                Must not be null.
421
   */
422
  nsresult ContinueSave(nsIFile* aFile);
423
424
  /**
425
   * After we're done prompting the user for any information, if the original
426
   * channel had a refresh url associated with it (which might point to a
427
   * "thank you for downloading" kind of page, then process that....It is safe
428
   * to invoke this method multiple times. We'll clear mOriginalChannel after
429
   * it's called and this ensures we won't call it again....
430
   */
431
  void ProcessAnyRefreshTags();
432
433
  /**
434
   * Notify our nsITransfer object that we are done with the download.  This is
435
   * always called after the target file has been closed.
436
   *
437
   * @param aStatus
438
   *        NS_OK for success, or a failure code if the download failed.
439
   *        A partially downloaded file may still be available in this case.
440
   */
441
  void NotifyTransfer(nsresult aStatus);
442
443
  /**
444
   * Helper routine that searches a pref string for a given mime type
445
   */
446
  bool GetNeverAskFlagFromPref(const char * prefName, const char * aContentType);
447
448
  /**
449
   * Helper routine to ensure mSuggestedFileName is "correct";
450
   * this ensures that mTempFileExtension only contains an extension when it
451
   * is different from mSuggestedFileName's extension.
452
   */
453
  void EnsureSuggestedFileName();
454
455
  typedef enum { kReadError, kWriteError, kLaunchError } ErrorType;
456
  /**
457
   * Utility function to send proper error notification to web progress listener
458
   */
459
  void SendStatusChange(ErrorType type, nsresult aStatus, nsIRequest *aRequest, const nsString& path);
460
461
  /**
462
   * Set in nsHelperDlgApp.js. This is always null after the user has chosen an
463
   * action.
464
   */
465
  nsCOMPtr<nsIWebProgressListener2> mDialogProgressListener;
466
  /**
467
   * Set once the user has chosen an action. This is null after the download
468
   * has been canceled or completes.
469
   */
470
  nsCOMPtr<nsITransfer> mTransfer;
471
472
  nsCOMPtr<nsIChannel> mOriginalChannel; /**< in the case of a redirect, this will be the pre-redirect channel. */
473
  nsCOMPtr<nsIHelperAppLauncherDialog> mDialog;
474
475
  /**
476
477
   * The request that's being loaded. Initialized in OnStartRequest.
478
   * Nulled out in OnStopRequest or once we know what we're doing
479
   * with the data, whichever happens later.
480
   */
481
  nsCOMPtr<nsIRequest> mRequest;
482
483
  RefPtr<nsExternalHelperAppService> mExtProtSvc;
484
};
485
486
#endif // nsExternalHelperAppService_h__