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