Home | History | Annotate | Download | only in download
      1 // Copyright (c) 2012 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 
      8 #include <map>
      9 #include <string>
     10 #include <vector>
     11 
     12 #include "base/callback.h"
     13 #include "base/gtest_prod_util.h"
     14 #include "base/memory/ref_counted.h"
     15 #include "base/memory/weak_ptr.h"
     16 #include "chrome/common/content_settings.h"
     17 #include "content/public/browser/notification_observer.h"
     18 #include "content/public/browser/notification_registrar.h"
     19 #include "content/public/browser/web_contents_observer.h"
     20 
     21 class HostContentSettingsMap;
     22 class DownloadRequestInfoBarDelegate;
     23 
     24 namespace content {
     25 class NavigationController;
     26 class WebContents;
     27 }
     28 
     29 // DownloadRequestLimiter is responsible for determining whether a download
     30 // should be allowed or not. It is designed to keep pages from downloading
     31 // multiple files without user interaction. DownloadRequestLimiter is invoked
     32 // from ResourceDispatcherHost any time a download begins
     33 // (CanDownloadOnIOThread). The request is processed on the UI thread, and the
     34 // request is notified (back on the IO thread) as to whether the download should
     35 // be allowed or denied.
     36 //
     37 // Invoking CanDownloadOnIOThread notifies the callback and may update the
     38 // download status. The following details the various states:
     39 // . Each NavigationController initially starts out allowing a download
     40 //   (ALLOW_ONE_DOWNLOAD).
     41 // . The first time CanDownloadOnIOThread is invoked the download is allowed and
     42 //   the state changes to PROMPT_BEFORE_DOWNLOAD.
     43 // . If the state is PROMPT_BEFORE_DOWNLOAD and the user clicks the mouse,
     44 //   presses enter, the space bar or navigates to another page the state is
     45 //   reset to ALLOW_ONE_DOWNLOAD.
     46 // . If a download is attempted and the state is PROMPT_BEFORE_DOWNLOAD the user
     47 //   is prompted as to whether the download is allowed or disallowed. The users
     48 //   choice stays until the user navigates to a different host. For example, if
     49 //   the user allowed the download, multiple downloads are allowed without any
     50 //   user intervention until the user navigates to a different host.
     51 class DownloadRequestLimiter
     52     : public base::RefCountedThreadSafe<DownloadRequestLimiter> {
     53  public:
     54   // Download status for a particular page. See class description for details.
     55   enum DownloadStatus {
     56     ALLOW_ONE_DOWNLOAD,
     57     PROMPT_BEFORE_DOWNLOAD,
     58     ALLOW_ALL_DOWNLOADS,
     59     DOWNLOADS_NOT_ALLOWED
     60   };
     61 
     62   // Max number of downloads before a "Prompt Before Download" Dialog is shown.
     63   static const size_t kMaxDownloadsAtOnce = 50;
     64 
     65   // The callback from CanDownloadOnIOThread. This is invoked on the io thread.
     66   // The boolean parameter indicates whether or not the download is allowed.
     67   typedef base::Callback<void(bool /*allow*/)> Callback;
     68 
     69   // TabDownloadState maintains the download state for a particular tab.
     70   // TabDownloadState prompts the user with an infobar as necessary.
     71   // TabDownloadState deletes itself (by invoking
     72   // DownloadRequestLimiter::Remove) as necessary.
     73   // TODO(gbillock): just make this class implement PermissionBubbleRequest.
     74   class TabDownloadState : public content::NotificationObserver,
     75                            public content::WebContentsObserver {
     76    public:
     77     // Creates a new TabDownloadState. |controller| is the controller the
     78     // TabDownloadState tracks the state of and is the host for any dialogs that
     79     // are displayed. |originating_controller| is used to determine the host of
     80     // the initial download. If |originating_controller| is null, |controller|
     81     // is used. |originating_controller| is typically null, but differs from
     82     // |controller| in the case of a constrained popup requesting the download.
     83     TabDownloadState(DownloadRequestLimiter* host,
     84                      content::WebContents* web_contents,
     85                      content::WebContents* originating_web_contents);
     86     virtual ~TabDownloadState();
     87 
     88     // Status of the download.
     89     void set_download_status(DownloadRequestLimiter::DownloadStatus status) {
     90       status_ = status;
     91     }
     92     DownloadRequestLimiter::DownloadStatus download_status() const {
     93       return status_;
     94     }
     95 
     96     // Number of "ALLOWED" downloads.
     97     void increment_download_count() {
     98       download_count_++;
     99     }
    100     size_t download_count() const {
    101       return download_count_;
    102     }
    103 
    104     // Promote protected accessor to public.
    105     content::WebContents* web_contents() const {
    106       return content::WebContentsObserver::web_contents();
    107     }
    108 
    109     // content::WebContentsObserver overrides.
    110     virtual void AboutToNavigateRenderView(
    111         content::RenderViewHost* render_view_host) OVERRIDE;
    112     // Invoked when a user gesture occurs (mouse click, enter or space). This
    113     // may result in invoking Remove on DownloadRequestLimiter.
    114     virtual void DidGetUserGesture() OVERRIDE;
    115     virtual void WebContentsDestroyed() OVERRIDE;
    116 
    117     // Asks the user if they really want to allow the download.
    118     // See description above CanDownloadOnIOThread for details on lifetime of
    119     // callback.
    120     void PromptUserForDownload(
    121         const DownloadRequestLimiter::Callback& callback);
    122 
    123     // Invoked from DownloadRequestDialogDelegate. Notifies the delegates and
    124     // changes the status appropriately. Virtual for testing.
    125     virtual void Cancel();
    126     virtual void CancelOnce();
    127     virtual void Accept();
    128 
    129    protected:
    130     // Used for testing.
    131     TabDownloadState();
    132 
    133    private:
    134     // Are we showing a prompt to the user?  Determined by whether
    135     // we have an outstanding weak pointer--weak pointers are only
    136     // given to the info bar delegate or permission bubble request.
    137     bool is_showing_prompt() const;
    138 
    139     // content::NotificationObserver method.
    140     virtual void Observe(int type,
    141                          const content::NotificationSource& source,
    142                          const content::NotificationDetails& details) OVERRIDE;
    143 
    144     // Remember to either block or allow automatic downloads from this origin.
    145     void SetContentSetting(ContentSetting setting);
    146 
    147     // Notifies the callbacks as to whether the download is allowed or not.
    148     // Updates status_ appropriately.
    149     void NotifyCallbacks(bool allow);
    150 
    151     content::WebContents* web_contents_;
    152 
    153     DownloadRequestLimiter* host_;
    154 
    155     // Host of the first page the download started on. This may be empty.
    156     std::string initial_page_host_;
    157 
    158     DownloadRequestLimiter::DownloadStatus status_;
    159 
    160     size_t download_count_;
    161 
    162     // Callbacks we need to notify. This is only non-empty if we're showing a
    163     // dialog.
    164     // See description above CanDownloadOnIOThread for details on lifetime of
    165     // callbacks.
    166     std::vector<DownloadRequestLimiter::Callback> callbacks_;
    167 
    168     // Used to remove observers installed on NavigationController.
    169     content::NotificationRegistrar registrar_;
    170 
    171     // Weak pointer factory for generating a weak pointer to pass to the
    172     // infobar.  User responses to the throttling prompt will be returned
    173     // through this channel, and it can be revoked if the user prompt result
    174     // becomes moot.
    175     base::WeakPtrFactory<DownloadRequestLimiter::TabDownloadState> factory_;
    176 
    177     DISALLOW_COPY_AND_ASSIGN(TabDownloadState);
    178   };
    179 
    180   static void SetContentSettingsForTesting(HostContentSettingsMap* settings);
    181 
    182   DownloadRequestLimiter();
    183 
    184   // Returns the download status for a page. This does not change the state in
    185   // anyway.
    186   DownloadStatus GetDownloadStatus(content::WebContents* tab);
    187 
    188   // Updates the state of the page as necessary and notifies the callback.
    189   // WARNING: both this call and the callback are invoked on the io thread.
    190   void CanDownloadOnIOThread(int render_process_host_id,
    191                              int render_view_id,
    192                              const GURL& url,
    193                              const std::string& request_method,
    194                              const Callback& callback);
    195 
    196  private:
    197   FRIEND_TEST_ALL_PREFIXES(DownloadTest, DownloadResourceThrottleCancels);
    198   friend class base::RefCountedThreadSafe<DownloadRequestLimiter>;
    199   friend class DownloadRequestLimiterTest;
    200   friend class TabDownloadState;
    201 
    202   ~DownloadRequestLimiter();
    203 
    204   // Gets the download state for the specified controller. If the
    205   // TabDownloadState does not exist and |create| is true, one is created.
    206   // See TabDownloadState's constructor description for details on the two
    207   // controllers.
    208   //
    209   // The returned TabDownloadState is owned by the DownloadRequestLimiter and
    210   // deleted when no longer needed (the Remove method is invoked).
    211   TabDownloadState* GetDownloadState(
    212       content::WebContents* web_contents,
    213       content::WebContents* originating_web_contents,
    214       bool create);
    215 
    216   // CanDownloadOnIOThread invokes this on the UI thread. This determines the
    217   // tab and invokes CanDownloadImpl.
    218   void CanDownload(int render_process_host_id,
    219                    int render_view_id,
    220                    const GURL& url,
    221                    const std::string& request_method,
    222                    const 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(content::WebContents* originating_contents,
    227                        const std::string& request_method,
    228                        const Callback& callback);
    229 
    230   // Invoked when decision to download has been made.
    231   void OnCanDownloadDecided(int render_process_host_id,
    232                             int render_view_id,
    233                             const std::string& request_method,
    234                             const Callback& orig_callback,
    235                             bool allow);
    236 
    237   // Invoked on the UI thread. Schedules a call to NotifyCallback on the io
    238   // thread.
    239   void ScheduleNotification(const Callback& callback, bool allow);
    240 
    241   // Removes the specified TabDownloadState from the internal map and deletes
    242   // it. This has the effect of resetting the status for the tab to
    243   // ALLOW_ONE_DOWNLOAD.
    244   void Remove(TabDownloadState* state, content::WebContents* contents);
    245 
    246   static HostContentSettingsMap* content_settings_;
    247   static HostContentSettingsMap* GetContentSettings(
    248       content::WebContents* contents);
    249 
    250   // Maps from tab to download state. The download state for a tab only exists
    251   // if the state is other than ALLOW_ONE_DOWNLOAD. Similarly once the state
    252   // transitions from anything but ALLOW_ONE_DOWNLOAD back to ALLOW_ONE_DOWNLOAD
    253   // the TabDownloadState is removed and deleted (by way of Remove).
    254   typedef std::map<content::WebContents*, TabDownloadState*> StateMap;
    255   StateMap state_map_;
    256 
    257   // Weak ptr factory used when |CanDownload| asks the delegate asynchronously
    258   // about the download.
    259   base::WeakPtrFactory<DownloadRequestLimiter> factory_;
    260 
    261   DISALLOW_COPY_AND_ASSIGN(DownloadRequestLimiter);
    262 };
    263 
    264 #endif  // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_REQUEST_LIMITER_H_
    265