Home | History | Annotate | Download | only in download
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef CHROME_BROWSER_DOWNLOAD_DOWNLOAD_REQUEST_LIMITER_H_
      6 #define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_REQUEST_LIMITER_H_
      7 #pragma once
      8 
      9 #include <map>
     10 #include <string>
     11 #include <vector>
     12 
     13 #include "base/memory/ref_counted.h"
     14 #include "content/common/notification_observer.h"
     15 #include "content/common/notification_registrar.h"
     16 
     17 class DownloadRequestInfoBarDelegate;
     18 class NavigationController;
     19 class TabContents;
     20 
     21 // DownloadRequestLimiter is responsible for determining whether a download
     22 // should be allowed or not. It is designed to keep pages from downloading
     23 // multiple files without user interaction. DownloadRequestLimiter is invoked
     24 // from ResourceDispatcherHost any time a download begins
     25 // (CanDownloadOnIOThread). The request is processed on the UI thread, and the
     26 // request is notified (back on the IO thread) as to whether the download should
     27 // be allowed or denied.
     28 //
     29 // Invoking CanDownloadOnIOThread notifies the callback and may update the
     30 // download status. The following details the various states:
     31 // . Each NavigationController initially starts out allowing a download
     32 //   (ALLOW_ONE_DOWNLOAD).
     33 // . The first time CanDownloadOnIOThread is invoked the download is allowed and
     34 //   the state changes to PROMPT_BEFORE_DOWNLOAD.
     35 // . If the state is PROMPT_BEFORE_DOWNLOAD and the user clicks the mouse,
     36 //   presses enter, the space bar or navigates to another page the state is
     37 //   reset to ALLOW_ONE_DOWNLOAD.
     38 // . If a download is attempted and the state is PROMPT_BEFORE_DOWNLOAD the user
     39 //   is prompted as to whether the download is allowed or disallowed. The users
     40 //   choice stays until the user navigates to a different host. For example, if
     41 //   the user allowed the download, multiple downloads are allowed without any
     42 //   user intervention until the user navigates to a different host.
     43 class DownloadRequestLimiter
     44     : public base::RefCountedThreadSafe<DownloadRequestLimiter> {
     45  public:
     46   // Download status for a particular page. See class description for details.
     47   enum DownloadStatus {
     48     ALLOW_ONE_DOWNLOAD,
     49     PROMPT_BEFORE_DOWNLOAD,
     50     ALLOW_ALL_DOWNLOADS,
     51     DOWNLOADS_NOT_ALLOWED
     52   };
     53 
     54   // Max number of downloads before a "Prompt Before Download" Dialog is shown.
     55   static const size_t kMaxDownloadsAtOnce = 50;
     56 
     57   // The callback from CanDownloadOnIOThread. This is invoked on the io thread.
     58   class Callback {
     59    public:
     60     virtual void ContinueDownload() = 0;
     61     virtual void CancelDownload() = 0;
     62 
     63    protected:
     64     virtual ~Callback() {}
     65   };
     66 
     67   // TabDownloadState maintains the download state for a particular tab.
     68   // TabDownloadState prompts the user with an infobar as necessary.
     69   // TabDownloadState deletes itself (by invoking
     70   // DownloadRequestLimiter::Remove) as necessary.
     71   class TabDownloadState : public NotificationObserver {
     72    public:
     73     // Creates a new TabDownloadState. |controller| is the controller the
     74     // TabDownloadState tracks the state of and is the host for any dialogs that
     75     // are displayed. |originating_controller| is used to determine the host of
     76     // the initial download. If |originating_controller| is null, |controller|
     77     // is used. |originating_controller| is typically null, but differs from
     78     // |controller| in the case of a constrained popup requesting the download.
     79     TabDownloadState(DownloadRequestLimiter* host,
     80                      NavigationController* controller,
     81                      NavigationController* originating_controller);
     82     virtual ~TabDownloadState();
     83 
     84     // Status of the download.
     85     void set_download_status(DownloadRequestLimiter::DownloadStatus status) {
     86       status_ = status;
     87     }
     88     DownloadRequestLimiter::DownloadStatus download_status() const {
     89       return status_;
     90     }
     91 
     92     // Number of "ALLOWED" downloads.
     93     void increment_download_count() {
     94       download_count_++;
     95     }
     96     size_t download_count() const {
     97       return download_count_;
     98     }
     99 
    100     // Invoked when a user gesture occurs (mouse click, enter or space). This
    101     // may result in invoking Remove on DownloadRequestLimiter.
    102     void OnUserGesture();
    103 
    104     // Asks the user if they really want to allow the download.
    105     // See description above CanDownloadOnIOThread for details on lifetime of
    106     // callback.
    107     void PromptUserForDownload(TabContents* tab,
    108                                DownloadRequestLimiter::Callback* callback);
    109 
    110     // Are we showing a prompt to the user?
    111     bool is_showing_prompt() const { return (infobar_ != NULL); }
    112 
    113     // NavigationController we're tracking.
    114     NavigationController* controller() const { return controller_; }
    115 
    116     // Invoked from DownloadRequestDialogDelegate. Notifies the delegates and
    117     // changes the status appropriately. Virtual for testing.
    118     virtual void Cancel();
    119     virtual void Accept();
    120 
    121    protected:
    122     // Used for testing.
    123     TabDownloadState()
    124         : host_(NULL),
    125           controller_(NULL),
    126           status_(DownloadRequestLimiter::ALLOW_ONE_DOWNLOAD),
    127           download_count_(0),
    128           infobar_(NULL) {
    129     }
    130 
    131    private:
    132     // NotificationObserver method.
    133     virtual void Observe(NotificationType type,
    134                          const NotificationSource& source,
    135                          const NotificationDetails& details);
    136 
    137     // Notifies the callbacks as to whether the download is allowed or not.
    138     // Updates status_ appropriately.
    139     void NotifyCallbacks(bool allow);
    140 
    141     DownloadRequestLimiter* host_;
    142 
    143     NavigationController* controller_;
    144 
    145     // Host of the first page the download started on. This may be empty.
    146     std::string initial_page_host_;
    147 
    148     DownloadRequestLimiter::DownloadStatus status_;
    149 
    150     size_t download_count_;
    151 
    152     // Callbacks we need to notify. This is only non-empty if we're showing a
    153     // dialog.
    154     // See description above CanDownloadOnIOThread for details on lifetime of
    155     // callbacks.
    156     std::vector<DownloadRequestLimiter::Callback*> callbacks_;
    157 
    158     // Used to remove observers installed on NavigationController.
    159     NotificationRegistrar registrar_;
    160 
    161     // Handles showing the infobar to the user, may be null.
    162     DownloadRequestInfoBarDelegate* infobar_;
    163 
    164     DISALLOW_COPY_AND_ASSIGN(TabDownloadState);
    165   };
    166 
    167   DownloadRequestLimiter();
    168 
    169   // Returns the download status for a page. This does not change the state in
    170   // anyway.
    171   DownloadStatus GetDownloadStatus(TabContents* tab);
    172 
    173   // Updates the state of the page as necessary and notifies the callback.
    174   // WARNING: both this call and the callback are invoked on the io thread.
    175   //
    176   // DownloadRequestLimiter does not retain/release the Callback. It is up to
    177   // the caller to ensure the callback is valid until the request is complete.
    178   void CanDownloadOnIOThread(int render_process_host_id,
    179                              int render_view_id,
    180                              int request_id,
    181                              Callback* callback);
    182 
    183   // Invoked when the user presses the mouse, enter key or space bar. This may
    184   // change the download status for the page. See the class description for
    185   // details.
    186   void OnUserGesture(TabContents* tab);
    187 
    188  private:
    189   friend class base::RefCountedThreadSafe<DownloadRequestLimiter>;
    190   friend class DownloadRequestLimiterTest;
    191   friend class TabDownloadState;
    192 
    193   ~DownloadRequestLimiter();
    194 
    195   // For unit tests. If non-null this is used instead of creating a dialog.
    196   class TestingDelegate {
    197    public:
    198     virtual bool ShouldAllowDownload() = 0;
    199 
    200    protected:
    201     virtual ~TestingDelegate() {}
    202   };
    203   static void SetTestingDelegate(TestingDelegate* delegate);
    204 
    205   // Gets the download state for the specified controller. If the
    206   // TabDownloadState does not exist and |create| is true, one is created.
    207   // See TabDownloadState's constructor description for details on the two
    208   // controllers.
    209   //
    210   // The returned TabDownloadState is owned by the DownloadRequestLimiter and
    211   // deleted when no longer needed (the Remove method is invoked).
    212   TabDownloadState* GetDownloadState(
    213       NavigationController* controller,
    214       NavigationController* originating_controller,
    215       bool create);
    216 
    217   // CanDownloadOnIOThread invokes this on the UI thread. This determines the
    218   // tab and invokes CanDownloadImpl.
    219   void CanDownload(int render_process_host_id,
    220                    int render_view_id,
    221                    int request_id,
    222                    Callback* callback);
    223 
    224   // Does the work of updating the download status on the UI thread and
    225   // potentially prompting the user.
    226   void CanDownloadImpl(TabContents* originating_tab,
    227                        int request_id,
    228                        Callback* callback);
    229 
    230   // Invoked on the UI thread. Schedules a call to NotifyCallback on the io
    231   // thread.
    232   void ScheduleNotification(Callback* callback, bool allow);
    233 
    234   // Notifies the callback. This *must* be invoked on the IO thread.
    235   void NotifyCallback(Callback* callback, bool allow);
    236 
    237   // Removes the specified TabDownloadState from the internal map and deletes
    238   // it. This has the effect of resetting the status for the tab to
    239   // ALLOW_ONE_DOWNLOAD.
    240   void Remove(TabDownloadState* state);
    241 
    242   // Maps from tab to download state. The download state for a tab only exists
    243   // if the state is other than ALLOW_ONE_DOWNLOAD. Similarly once the state
    244   // transitions from anything but ALLOW_ONE_DOWNLOAD back to ALLOW_ONE_DOWNLOAD
    245   // the TabDownloadState is removed and deleted (by way of Remove).
    246   typedef std::map<NavigationController*, TabDownloadState*> StateMap;
    247   StateMap state_map_;
    248 
    249   static TestingDelegate* delegate_;
    250 
    251   DISALLOW_COPY_AND_ASSIGN(DownloadRequestLimiter);
    252 };
    253 
    254 #endif  // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_REQUEST_LIMITER_H_
    255