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
17class DownloadRequestInfoBarDelegate;
18class NavigationController;
19class 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.
43class 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