• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • 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 "components/content_settings/core/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.
set_download_status(DownloadRequestLimiter::DownloadStatus status)89     void set_download_status(DownloadRequestLimiter::DownloadStatus status) {
90       status_ = status;
91     }
download_status()92     DownloadRequestLimiter::DownloadStatus download_status() const {
93       return status_;
94     }
95 
96     // Number of "ALLOWED" downloads.
increment_download_count()97     void increment_download_count() {
98       download_count_++;
99     }
download_count()100     size_t download_count() const {
101       return download_count_;
102     }
103 
104     // content::WebContentsObserver overrides.
105     virtual void AboutToNavigateRenderView(
106         content::RenderViewHost* render_view_host) OVERRIDE;
107     // Invoked when a user gesture occurs (mouse click, enter or space). This
108     // may result in invoking Remove on DownloadRequestLimiter.
109     virtual void DidGetUserGesture() OVERRIDE;
110     virtual void WebContentsDestroyed() OVERRIDE;
111 
112     // Asks the user if they really want to allow the download.
113     // See description above CanDownloadOnIOThread for details on lifetime of
114     // callback.
115     void PromptUserForDownload(
116         const DownloadRequestLimiter::Callback& callback);
117 
118     // Invoked from DownloadRequestDialogDelegate. Notifies the delegates and
119     // changes the status appropriately. Virtual for testing.
120     virtual void Cancel();
121     virtual void CancelOnce();
122     virtual void Accept();
123 
124    protected:
125     // Used for testing.
126     TabDownloadState();
127 
128    private:
129     // Are we showing a prompt to the user?  Determined by whether
130     // we have an outstanding weak pointer--weak pointers are only
131     // given to the info bar delegate or permission bubble request.
132     bool is_showing_prompt() const;
133 
134     // content::NotificationObserver method.
135     virtual void Observe(int type,
136                          const content::NotificationSource& source,
137                          const content::NotificationDetails& details) OVERRIDE;
138 
139     // Remember to either block or allow automatic downloads from this origin.
140     void SetContentSetting(ContentSetting setting);
141 
142     // Notifies the callbacks as to whether the download is allowed or not.
143     // Updates status_ appropriately.
144     void NotifyCallbacks(bool allow);
145 
146     content::WebContents* web_contents_;
147 
148     DownloadRequestLimiter* host_;
149 
150     // Host of the first page the download started on. This may be empty.
151     std::string initial_page_host_;
152 
153     DownloadRequestLimiter::DownloadStatus status_;
154 
155     size_t download_count_;
156 
157     // Callbacks we need to notify. This is only non-empty if we're showing a
158     // dialog.
159     // See description above CanDownloadOnIOThread for details on lifetime of
160     // callbacks.
161     std::vector<DownloadRequestLimiter::Callback> callbacks_;
162 
163     // Used to remove observers installed on NavigationController.
164     content::NotificationRegistrar registrar_;
165 
166     // Weak pointer factory for generating a weak pointer to pass to the
167     // infobar.  User responses to the throttling prompt will be returned
168     // through this channel, and it can be revoked if the user prompt result
169     // becomes moot.
170     base::WeakPtrFactory<DownloadRequestLimiter::TabDownloadState> factory_;
171 
172     DISALLOW_COPY_AND_ASSIGN(TabDownloadState);
173   };
174 
175   static void SetContentSettingsForTesting(HostContentSettingsMap* settings);
176 
177   DownloadRequestLimiter();
178 
179   // Returns the download status for a page. This does not change the state in
180   // anyway.
181   DownloadStatus GetDownloadStatus(content::WebContents* tab);
182 
183   // Updates the state of the page as necessary and notifies the callback.
184   // WARNING: both this call and the callback are invoked on the io thread.
185   void CanDownloadOnIOThread(int render_process_host_id,
186                              int render_view_id,
187                              const GURL& url,
188                              const std::string& request_method,
189                              const Callback& callback);
190 
191  private:
192   FRIEND_TEST_ALL_PREFIXES(DownloadTest, DownloadResourceThrottleCancels);
193   friend class base::RefCountedThreadSafe<DownloadRequestLimiter>;
194   friend class DownloadRequestLimiterTest;
195   friend class TabDownloadState;
196 
197   ~DownloadRequestLimiter();
198 
199   // Gets the download state for the specified controller. If the
200   // TabDownloadState does not exist and |create| is true, one is created.
201   // See TabDownloadState's constructor description for details on the two
202   // controllers.
203   //
204   // The returned TabDownloadState is owned by the DownloadRequestLimiter and
205   // deleted when no longer needed (the Remove method is invoked).
206   TabDownloadState* GetDownloadState(
207       content::WebContents* web_contents,
208       content::WebContents* originating_web_contents,
209       bool create);
210 
211   // CanDownloadOnIOThread invokes this on the UI thread. This determines the
212   // tab and invokes CanDownloadImpl.
213   void CanDownload(int render_process_host_id,
214                    int render_view_id,
215                    const GURL& url,
216                    const std::string& request_method,
217                    const Callback& callback);
218 
219   // Does the work of updating the download status on the UI thread and
220   // potentially prompting the user.
221   void CanDownloadImpl(content::WebContents* originating_contents,
222                        const std::string& request_method,
223                        const Callback& callback);
224 
225   // Invoked when decision to download has been made.
226   void OnCanDownloadDecided(int render_process_host_id,
227                             int render_view_id,
228                             const std::string& request_method,
229                             const Callback& orig_callback,
230                             bool allow);
231 
232   // Invoked on the UI thread. Schedules a call to NotifyCallback on the io
233   // thread.
234   void ScheduleNotification(const Callback& callback, bool allow);
235 
236   // Removes the specified TabDownloadState from the internal map and deletes
237   // it. This has the effect of resetting the status for the tab to
238   // ALLOW_ONE_DOWNLOAD.
239   void Remove(TabDownloadState* state, content::WebContents* contents);
240 
241   static HostContentSettingsMap* content_settings_;
242   static HostContentSettingsMap* GetContentSettings(
243       content::WebContents* contents);
244 
245   // Maps from tab to download state. The download state for a tab only exists
246   // if the state is other than ALLOW_ONE_DOWNLOAD. Similarly once the state
247   // transitions from anything but ALLOW_ONE_DOWNLOAD back to ALLOW_ONE_DOWNLOAD
248   // the TabDownloadState is removed and deleted (by way of Remove).
249   typedef std::map<content::WebContents*, TabDownloadState*> StateMap;
250   StateMap state_map_;
251 
252   // Weak ptr factory used when |CanDownload| asks the delegate asynchronously
253   // about the download.
254   base::WeakPtrFactory<DownloadRequestLimiter> factory_;
255 
256   DISALLOW_COPY_AND_ASSIGN(DownloadRequestLimiter);
257 };
258 
259 #endif  // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_REQUEST_LIMITER_H_
260