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