• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • 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:
~Callback()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.
set_download_status(DownloadRequestLimiter::DownloadStatus status)85     void set_download_status(DownloadRequestLimiter::DownloadStatus status) {
86       status_ = status;
87     }
download_status()88     DownloadRequestLimiter::DownloadStatus download_status() const {
89       return status_;
90     }
91 
92     // Number of "ALLOWED" downloads.
increment_download_count()93     void increment_download_count() {
94       download_count_++;
95     }
download_count()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?
is_showing_prompt()111     bool is_showing_prompt() const { return (infobar_ != NULL); }
112 
113     // NavigationController we're tracking.
controller()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.
TabDownloadState()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:
~TestingDelegate()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