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 <stddef.h> 9 10 #include <map> 11 #include <set> 12 #include <string> 13 #include <vector> 14 15 #include "base/callback.h" 16 #include "base/gtest_prod_util.h" 17 #include "base/macros.h" 18 #include "base/memory/ref_counted.h" 19 #include "base/memory/weak_ptr.h" 20 #include "base/scoped_observer.h" 21 #include "components/content_settings/core/browser/content_settings_observer.h" 22 #include "components/content_settings/core/browser/host_content_settings_map.h" 23 #include "components/content_settings/core/common/content_settings.h" 24 #include "content/public/browser/web_contents.h" 25 #include "content/public/browser/web_contents_observer.h" 26 #include "url/origin.h" 27 28 namespace content { 29 class WebContents; 30 } 31 32 // DownloadRequestLimiter is responsible for determining whether a download 33 // should be allowed or not. It is designed to keep pages from downloading 34 // multiple files without user interaction. DownloadRequestLimiter is invoked 35 // from ResourceDispatcherHost any time a download begins 36 // (CanDownloadOnIOThread). The request is processed on the UI thread, and the 37 // request is notified (back on the IO thread) as to whether the download should 38 // be allowed or denied. 39 // 40 // Invoking CanDownloadOnIOThread notifies the callback and may update the 41 // download status. The following details the various states: 42 // . Each NavigationController initially starts out allowing a download 43 // (ALLOW_ONE_DOWNLOAD). 44 // . The first time CanDownloadOnIOThread is invoked the download is allowed and 45 // the state changes to PROMPT_BEFORE_DOWNLOAD. 46 // . If the state is PROMPT_BEFORE_DOWNLOAD and the user clicks the mouse, 47 // presses enter, the space bar or navigates to another page the state is 48 // reset to ALLOW_ONE_DOWNLOAD. 49 // . If a download is attempted and the state is PROMPT_BEFORE_DOWNLOAD the user 50 // is prompted as to whether the download is allowed or disallowed. The users 51 // choice stays until the user navigates to a different host. For example, if 52 // the user allowed the download, multiple downloads are allowed without any 53 // user intervention until the user navigates to a different host. 54 // 55 // The DownloadUiStatus indicates whether omnibox UI should be shown for the 56 // current download status. We do not show UI if there has not yet been 57 // a download attempt on the page regardless of the internal download status. 58 class DownloadRequestLimiter 59 : public base::RefCountedThreadSafe<DownloadRequestLimiter> { 60 public: 61 // Download status for a particular page. See class description for details. 62 enum DownloadStatus { 63 ALLOW_ONE_DOWNLOAD, 64 PROMPT_BEFORE_DOWNLOAD, 65 ALLOW_ALL_DOWNLOADS, 66 DOWNLOADS_NOT_ALLOWED 67 }; 68 69 // Download UI state given the current download status for a page. 70 enum DownloadUiStatus { 71 DOWNLOAD_UI_DEFAULT, 72 DOWNLOAD_UI_ALLOWED, 73 DOWNLOAD_UI_BLOCKED 74 }; 75 76 // Max number of downloads before a "Prompt Before Download" Dialog is shown. 77 static const size_t kMaxDownloadsAtOnce = 50; 78 79 // The callback from CanDownloadOnIOThread. This is invoked on the io thread. 80 // The boolean parameter indicates whether or not the download is allowed. 81 using Callback = base::OnceCallback<void(bool /*allow*/)>; 82 83 // TabDownloadState maintains the download state for a particular tab. 84 // TabDownloadState prompts the user with an infobar as necessary. 85 // TabDownloadState deletes itself (by invoking 86 // DownloadRequestLimiter::Remove) as necessary. 87 // TODO(gbillock): just make this class implement 88 // permissions::PermissionRequest. 89 class TabDownloadState : public content_settings::Observer, 90 public content::WebContentsObserver { 91 public: 92 // Creates a new TabDownloadState. |host| is DownloadRequestLimiter object 93 // that owns this object. This object will listen to all the navigations 94 // and downloads happening on the |web_contents| to determine the new 95 // download status. 96 TabDownloadState(DownloadRequestLimiter* host, 97 content::WebContents* web_contents); 98 ~TabDownloadState() override; 99 100 // Sets the current limiter state and the underlying automatic downloads 101 // content setting. Sends a notification that the content setting has been 102 // changed (if it has changed). 103 void SetDownloadStatusAndNotify(const url::Origin& request_origin, 104 DownloadStatus status); 105 106 // Status of the download. download_status()107 DownloadStatus download_status() const { return status_; } 108 109 // The omnibox UI to be showing (or none if we shouldn't show any). download_ui_status()110 DownloadUiStatus download_ui_status() const { return ui_status_; } 111 112 // Number of "ALLOWED" downloads. increment_download_count()113 void increment_download_count() { 114 download_count_++; 115 } download_count()116 size_t download_count() const { 117 return download_count_; 118 } 119 origin()120 const url::Origin& origin() const { return origin_; } 121 download_seen()122 bool download_seen() const { return download_seen_; } set_download_seen()123 void set_download_seen() { download_seen_ = true; } 124 125 // content::WebContentsObserver overrides. 126 void DidStartNavigation( 127 content::NavigationHandle* navigation_handle) override; 128 void DidFinishNavigation( 129 content::NavigationHandle* navigation_handle) override; 130 void DidGetUserInteraction(const blink::WebInputEvent& event) override; 131 void WebContentsDestroyed() override; 132 133 // Asks the user if they really want to allow the download. 134 // See description above CanDownloadOnIOThread for details on lifetime of 135 // callback. 136 void PromptUserForDownload(DownloadRequestLimiter::Callback callback, 137 const url::Origin& request_origin); 138 139 // Invoked from DownloadRequestDialogDelegate. Notifies the delegates and 140 // changes the status appropriately. Virtual for testing. 141 virtual void Cancel(const url::Origin& request_origin); 142 virtual void CancelOnce(const url::Origin& request_origin); 143 virtual void Accept(const url::Origin& request_origin); 144 145 DownloadStatus GetDownloadStatus(const url::Origin& request_origin); 146 147 protected: 148 // Used for testing. 149 TabDownloadState(); 150 151 private: 152 // Are we showing a prompt to the user? Determined by whether 153 // we have an outstanding weak pointer--weak pointers are only 154 // given to the info bar delegate or permission bubble request. 155 bool is_showing_prompt() const; 156 157 // This may result in invoking Remove on DownloadRequestLimiter. 158 void OnUserInteraction(); 159 160 // content_settings::Observer overrides. 161 void OnContentSettingChanged( 162 const ContentSettingsPattern& primary_pattern, 163 const ContentSettingsPattern& secondary_pattern, 164 ContentSettingsType content_type) override; 165 166 // Remember to either block or allow automatic downloads from 167 // |request_origin|. 168 void SetContentSetting(ContentSetting setting, 169 const url::Origin& request_origin); 170 171 // Notifies the callbacks as to whether the download is allowed or not. 172 // Returns false if it didn't notify all callbacks. 173 bool NotifyCallbacks(bool allow); 174 175 // Set the download limiter state and notify if it has changed. Callers must 176 // guarantee that |status| and |setting| correspond to each other. 177 void SetDownloadStatusAndNotifyImpl(const url::Origin& request_origin, 178 DownloadStatus status, 179 ContentSetting setting); 180 181 // Check if download is restricted (either requires prompting or is blocked) 182 // for the |navigation_handle|. 183 bool IsNavigationRestricted(content::NavigationHandle* navigation_handle); 184 185 content::WebContents* web_contents_; 186 187 DownloadRequestLimiter* host_; 188 189 // Host of the first page the download started on. This may be empty. 190 std::string initial_page_host_; 191 192 // Current tab status and UI status. Renderer initiated navigations will 193 // not change these values if the current tab state is restricted. 194 DownloadStatus status_; 195 DownloadUiStatus ui_status_; 196 197 // Origin for initiating the current download. The value was kept for 198 // updating the omnibox decoration. 199 url::Origin origin_; 200 201 size_t download_count_; 202 203 // True if a download has been seen on the current page load. 204 bool download_seen_; 205 206 // Callbacks we need to notify. This is only non-empty if we're showing a 207 // dialog. 208 // See description above CanDownloadOnIOThread for details on lifetime of 209 // callbacks. 210 std::vector<DownloadRequestLimiter::Callback> callbacks_; 211 212 // Origins that have non-default download state. 213 using DownloadStatusMap = std::map<url::Origin, DownloadStatus>; 214 DownloadStatusMap download_status_map_; 215 216 ScopedObserver<HostContentSettingsMap, content_settings::Observer> 217 observer_{this}; 218 219 // Weak pointer factory for generating a weak pointer to pass to the 220 // infobar. User responses to the throttling prompt will be returned 221 // through this channel, and it can be revoked if the user prompt result 222 // becomes moot. 223 base::WeakPtrFactory<DownloadRequestLimiter::TabDownloadState> factory_{ 224 this}; 225 226 DISALLOW_COPY_AND_ASSIGN(TabDownloadState); 227 }; 228 229 DownloadRequestLimiter(); 230 231 // Returns the download status for a page. This does not change the state in 232 // anyway. 233 DownloadStatus GetDownloadStatus(content::WebContents* web_contents); 234 235 // Returns the download UI status for a page for the purposes of showing an 236 // omnibox decoration. 237 DownloadUiStatus GetDownloadUiStatus(content::WebContents* web_contents); 238 239 // Returns the download origin that is associated with the current UI status 240 // for the purposes of showing an omnibox decoration. 241 GURL GetDownloadOrigin(content::WebContents* web_contents); 242 243 // Check if download can proceed and notifies the callback on UI thread. 244 void CanDownload(const content::WebContents::Getter& web_contents_getter, 245 const GURL& url, 246 const std::string& request_method, 247 base::Optional<url::Origin> request_initiator, 248 bool from_download_cross_origin_redirect, 249 Callback callback); 250 251 private: 252 FRIEND_TEST_ALL_PREFIXES(DownloadTest, DownloadResourceThrottleCancels); 253 FRIEND_TEST_ALL_PREFIXES(DownloadTest, 254 DownloadRequestLimiterDisallowsAnchorDownloadTag); 255 FRIEND_TEST_ALL_PREFIXES(DownloadTest, 256 CrossOriginRedirectDownloadFromAnchorDownload); 257 FRIEND_TEST_ALL_PREFIXES( 258 DownloadTest, 259 MultipleCrossOriginRedirectDownloadsFromAnchorDownload); 260 FRIEND_TEST_ALL_PREFIXES(DownloadTest, MultipleDownloadsFromIframeSrcdoc); 261 FRIEND_TEST_ALL_PREFIXES(ContentSettingBubbleControllerTest, Init); 262 FRIEND_TEST_ALL_PREFIXES(ContentSettingImageModelBrowserTest, 263 CreateBubbleModel); 264 friend class base::RefCountedThreadSafe<DownloadRequestLimiter>; 265 friend class BackgroundFetchBrowserTest; 266 friend class ContentSettingBubbleDialogTest; 267 friend class DownloadRequestLimiterTest; 268 friend class TabDownloadState; 269 270 ~DownloadRequestLimiter(); 271 272 // Gets the download state for the specified controller. If the 273 // TabDownloadState does not exist and |create| is true, one is created. 274 // See TabDownloadState's constructor description for details on the two 275 // controllers. 276 // 277 // The returned TabDownloadState is owned by the DownloadRequestLimiter and 278 // deleted when no longer needed (the Remove method is invoked). 279 TabDownloadState* GetDownloadState(content::WebContents* web_contents, 280 bool create); 281 282 // Does the work of updating the download status on the UI thread and 283 // potentially prompting the user. 284 void CanDownloadImpl(content::WebContents* originating_contents, 285 const std::string& request_method, 286 base::Optional<url::Origin> request_initiator, 287 bool from_download_cross_origin_redirect, 288 Callback callback); 289 290 // Invoked when decision to download has been made. 291 void OnCanDownloadDecided( 292 const content::WebContents::Getter& web_contents_getter, 293 const std::string& request_method, 294 base::Optional<url::Origin> request_initiator, 295 bool from_download_cross_origin_redirect, 296 Callback orig_callback, 297 bool allow); 298 299 // Removes the specified TabDownloadState from the internal map and deletes 300 // it. This has the effect of resetting the status for the tab to 301 // ALLOW_ONE_DOWNLOAD. 302 void Remove(TabDownloadState* state, content::WebContents* contents); 303 304 static HostContentSettingsMap* GetContentSettings( 305 content::WebContents* contents); 306 307 // Gets the content setting for a particular request initiator. 308 static ContentSetting GetAutoDownloadContentSetting( 309 content::WebContents* contents, 310 const GURL& request_initiator); 311 312 // Sets the callback for tests to know the result of OnCanDownloadDecided(). 313 using CanDownloadDecidedCallback = 314 base::RepeatingCallback<void(bool /*allow*/)>; 315 void SetOnCanDownloadDecidedCallbackForTesting( 316 CanDownloadDecidedCallback callback); 317 318 // TODO(bauerb): Change this to use WebContentsUserData. 319 // Maps from tab to download state. The download state for a tab only exists 320 // if the state is other than ALLOW_ONE_DOWNLOAD. Similarly once the state 321 // transitions from anything but ALLOW_ONE_DOWNLOAD back to ALLOW_ONE_DOWNLOAD 322 // the TabDownloadState is removed and deleted (by way of Remove). 323 typedef std::map<content::WebContents*, TabDownloadState*> StateMap; 324 StateMap state_map_; 325 326 CanDownloadDecidedCallback on_can_download_decided_callback_; 327 328 // Weak ptr factory used when |CanDownload| asks the delegate asynchronously 329 // about the download. 330 base::WeakPtrFactory<DownloadRequestLimiter> factory_{this}; 331 332 DISALLOW_COPY_AND_ASSIGN(DownloadRequestLimiter); 333 }; 334 335 #endif // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_REQUEST_LIMITER_H_ 336