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