download_request_limiter.h revision 010d83a9304c5a91596085d917d248abff47903a
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
21class HostContentSettingsMap;
22class DownloadRequestInfoBarDelegate;
23
24namespace content {
25class NavigationController;
26class 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.
51class 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                             int request_id,
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                   int request_id,
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                       int request_id,
228                       const std::string& request_method,
229                       const Callback& callback);
230
231  // Invoked when decision to download has been made.
232  void OnCanDownloadDecided(int render_process_host_id,
233                            int render_view_id,
234                            int request_id,
235                            const std::string& request_method,
236                            const Callback& orig_callback,
237                            bool allow);
238
239  // Invoked on the UI thread. Schedules a call to NotifyCallback on the io
240  // thread.
241  void ScheduleNotification(const Callback& callback, bool allow);
242
243  // Removes the specified TabDownloadState from the internal map and deletes
244  // it. This has the effect of resetting the status for the tab to
245  // ALLOW_ONE_DOWNLOAD.
246  void Remove(TabDownloadState* state, content::WebContents* contents);
247
248  static HostContentSettingsMap* content_settings_;
249  static HostContentSettingsMap* GetContentSettings(
250      content::WebContents* contents);
251
252  // Maps from tab to download state. The download state for a tab only exists
253  // if the state is other than ALLOW_ONE_DOWNLOAD. Similarly once the state
254  // transitions from anything but ALLOW_ONE_DOWNLOAD back to ALLOW_ONE_DOWNLOAD
255  // the TabDownloadState is removed and deleted (by way of Remove).
256  typedef std::map<content::WebContents*, TabDownloadState*> StateMap;
257  StateMap state_map_;
258
259  // Weak ptr factory used when |CanDownload| asks the delegate asynchronously
260  // about the download.
261  base::WeakPtrFactory<DownloadRequestLimiter> factory_;
262
263  DISALLOW_COPY_AND_ASSIGN(DownloadRequestLimiter);
264};
265
266#endif  // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_REQUEST_LIMITER_H_
267