Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/download/download_request_limiter.h" | 5 #include "chrome/browser/download/download_request_limiter.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/stl_util.h" | 8 #include "base/stl_util.h" |
| 9 #include "chrome/browser/download/download_request_infobar_delegate.h" | 9 #include "chrome/browser/download/download_request_infobar_delegate.h" |
| 10 #include "chrome/browser/download/download_request_limiter_observer.h" | |
| 10 #include "chrome/browser/infobars/infobar_tab_helper.h" | 11 #include "chrome/browser/infobars/infobar_tab_helper.h" |
| 11 #include "chrome/browser/tab_contents/tab_util.h" | 12 #include "chrome/browser/tab_contents/tab_util.h" |
| 12 #include "chrome/browser/ui/blocked_content/blocked_content_tab_helper.h" | 13 #include "chrome/browser/ui/blocked_content/blocked_content_tab_helper.h" |
| 13 #include "chrome/browser/ui/blocked_content/blocked_content_tab_helper_delegate. h" | 14 #include "chrome/browser/ui/blocked_content/blocked_content_tab_helper_delegate. h" |
| 14 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" | 15 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" |
| 15 #include "content/public/browser/browser_thread.h" | 16 #include "content/public/browser/browser_thread.h" |
| 16 #include "content/public/browser/navigation_controller.h" | 17 #include "content/public/browser/navigation_controller.h" |
| 17 #include "content/public/browser/navigation_entry.h" | 18 #include "content/public/browser/navigation_entry.h" |
| 18 #include "content/public/browser/notification_source.h" | 19 #include "content/public/browser/notification_source.h" |
| 19 #include "content/public/browser/notification_types.h" | 20 #include "content/public/browser/notification_types.h" |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 77 WebContents* tab, | 78 WebContents* tab, |
| 78 const DownloadRequestLimiter::Callback& callback) { | 79 const DownloadRequestLimiter::Callback& callback) { |
| 79 callbacks_.push_back(callback); | 80 callbacks_.push_back(callback); |
| 80 | 81 |
| 81 if (is_showing_prompt()) | 82 if (is_showing_prompt()) |
| 82 return; // Already showing prompt. | 83 return; // Already showing prompt. |
| 83 | 84 |
| 84 if (DownloadRequestLimiter::delegate_) { | 85 if (DownloadRequestLimiter::delegate_) { |
| 85 NotifyCallbacks(DownloadRequestLimiter::delegate_->ShouldAllowDownload()); | 86 NotifyCallbacks(DownloadRequestLimiter::delegate_->ShouldAllowDownload()); |
| 86 } else { | 87 } else { |
| 87 InfoBarTabHelper* infobar_helper = | 88 TabContentsWrapper* tab_wrapper = |
| 88 TabContentsWrapper::GetCurrentWrapperForContents(tab)-> | 89 TabContentsWrapper::GetCurrentWrapperForContents(tab); |
| 89 infobar_tab_helper(); | 90 if (!tab_wrapper) { |
| 91 // If |tab| doesn't have a tab_wrapper, then it isn't what a user thinks | |
| 92 // of as a tab, it's actually a "raw" WebContents like those used for | |
| 93 // extension popups/bubbles and hosted apps etc. | |
| 94 // TODO(benjhayden): If this is an automatic download from an extension, | |
| 95 // it would be convenient for the extension author if we send a message to | |
| 96 // the extension's DevTools console (as we do for CSP) about how | |
| 97 // extensions should use chrome.downloads.download() (requires the | |
| 98 // "downloads" permission) to automatically download multiple files. | |
| 99 Cancel(); | |
| 100 return; | |
| 101 } | |
| 102 InfoBarTabHelper* infobar_helper = tab_wrapper->infobar_tab_helper(); | |
| 90 infobar_ = new DownloadRequestInfoBarDelegate(infobar_helper, this); | 103 infobar_ = new DownloadRequestInfoBarDelegate(infobar_helper, this); |
| 91 infobar_helper->AddInfoBar(infobar_); | 104 infobar_helper->AddInfoBar(infobar_); |
| 92 } | 105 } |
| 93 } | 106 } |
| 94 | 107 |
| 95 void DownloadRequestLimiter::TabDownloadState::Cancel() { | 108 void DownloadRequestLimiter::TabDownloadState::Cancel() { |
| 96 NotifyCallbacks(false); | 109 NotifyCallbacks(false); |
| 97 } | 110 } |
| 98 | 111 |
| 99 void DownloadRequestLimiter::TabDownloadState::Accept() { | 112 void DownloadRequestLimiter::TabDownloadState::Accept() { |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 204 } | 217 } |
| 205 | 218 |
| 206 DownloadRequestLimiter::~DownloadRequestLimiter() { | 219 DownloadRequestLimiter::~DownloadRequestLimiter() { |
| 207 // All the tabs should have closed before us, which sends notification and | 220 // All the tabs should have closed before us, which sends notification and |
| 208 // removes from state_map_. As such, there should be no pending callbacks. | 221 // removes from state_map_. As such, there should be no pending callbacks. |
| 209 DCHECK(state_map_.empty()); | 222 DCHECK(state_map_.empty()); |
| 210 } | 223 } |
| 211 | 224 |
| 212 DownloadRequestLimiter::DownloadStatus | 225 DownloadRequestLimiter::DownloadStatus |
| 213 DownloadRequestLimiter::GetDownloadStatus(WebContents* tab) { | 226 DownloadRequestLimiter::GetDownloadStatus(WebContents* tab) { |
| 214 TabDownloadState* state = GetDownloadState(&tab->GetController(), NULL, false) ; | 227 TabDownloadState* state = GetDownloadState( |
| 228 &tab->GetController(), NULL, false); | |
| 215 return state ? state->download_status() : ALLOW_ONE_DOWNLOAD; | 229 return state ? state->download_status() : ALLOW_ONE_DOWNLOAD; |
| 216 } | 230 } |
| 217 | 231 |
| 218 void DownloadRequestLimiter::CanDownloadOnIOThread( | 232 void DownloadRequestLimiter::CanDownloadOnIOThread( |
| 219 int render_process_host_id, | 233 int render_process_host_id, |
| 220 int render_view_id, | 234 int render_view_id, |
| 221 int request_id, | 235 int request_id, |
| 222 const std::string& request_method, | 236 const std::string& request_method, |
| 223 const Callback& callback) { | 237 const Callback& callback) { |
| 224 // This is invoked on the IO thread. Schedule the task to run on the UI | 238 // This is invoked on the IO thread. Schedule the task to run on the UI |
| 225 // thread so that we can query UI state. | 239 // thread so that we can query UI state. |
| 226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 227 BrowserThread::PostTask( | 241 BrowserThread::PostTask( |
| 228 BrowserThread::UI, FROM_HERE, | 242 BrowserThread::UI, FROM_HERE, |
| 229 base::Bind(&DownloadRequestLimiter::CanDownload, this, | 243 base::Bind(&DownloadRequestLimiter::CanDownload, this, |
| 230 render_process_host_id, render_view_id, request_id, | 244 render_process_host_id, render_view_id, request_id, |
| 231 request_method, callback)); | 245 request_method, callback)); |
| 232 } | 246 } |
| 233 | 247 |
| 234 void DownloadRequestLimiter::OnUserGesture(WebContents* tab) { | 248 void DownloadRequestLimiter::OnUserGesture(WebContents* tab) { |
| 235 TabDownloadState* state = | 249 TabDownloadState* state = GetDownloadState( |
| 236 GetDownloadState(&tab->GetController(), NULL, false); | 250 &tab->GetController(), NULL, false); |
| 237 if (!state) | 251 if (!state) |
| 238 return; | 252 return; |
| 239 | 253 |
| 240 state->OnUserGesture(); | 254 state->OnUserGesture(); |
| 241 } | 255 } |
| 242 | 256 |
| 243 // static | 257 // static |
| 244 void DownloadRequestLimiter::SetTestingDelegate(TestingDelegate* delegate) { | 258 void DownloadRequestLimiter::SetTestingDelegate(TestingDelegate* delegate) { |
| 245 delegate_ = delegate; | 259 delegate_ = delegate; |
| 246 } | 260 } |
| 247 | 261 |
| 248 DownloadRequestLimiter::TabDownloadState* DownloadRequestLimiter:: | 262 DownloadRequestLimiter::TabDownloadState* |
| 249 GetDownloadState(NavigationController* controller, | 263 DownloadRequestLimiter::GetDownloadState( |
| 250 NavigationController* originating_controller, | 264 NavigationController* controller, |
| 251 bool create) { | 265 NavigationController* originating_controller, |
| 266 bool create) { | |
| 252 DCHECK(controller); | 267 DCHECK(controller); |
| 253 StateMap::iterator i = state_map_.find(controller); | 268 StateMap::iterator i = state_map_.find(controller); |
| 254 if (i != state_map_.end()) | 269 if (i != state_map_.end()) |
| 255 return i->second; | 270 return i->second; |
| 256 | 271 |
| 257 if (!create) | 272 if (!create) |
| 258 return NULL; | 273 return NULL; |
| 259 | 274 |
| 260 TabDownloadState* state = | 275 TabDownloadState* state = |
| 261 new TabDownloadState(this, controller, originating_controller); | 276 new TabDownloadState(this, controller, originating_controller); |
| 262 state_map_[controller] = state; | 277 state_map_[controller] = state; |
| 263 return state; | 278 return state; |
| 264 } | 279 } |
| 265 | 280 |
| 266 void DownloadRequestLimiter::CanDownload(int render_process_host_id, | 281 void DownloadRequestLimiter::CanDownload(int render_process_host_id, |
| 267 int render_view_id, | 282 int render_view_id, |
| 268 int request_id, | 283 int request_id, |
| 269 const std::string& request_method, | 284 const std::string& request_method, |
| 270 const Callback& callback) { | 285 const Callback& callback) { |
| 271 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 286 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 272 | 287 |
| 273 WebContents* originating_tab = | 288 WebContents* originating_contents = |
| 274 tab_util::GetWebContentsByID(render_process_host_id, render_view_id); | 289 tab_util::GetWebContentsByID(render_process_host_id, render_view_id); |
| 275 if (!originating_tab) { | 290 if (!originating_contents) { |
| 276 // The tab was closed, don't allow the download. | 291 // The tab was closed, don't allow the download. |
| 277 ScheduleNotification(callback, false); | 292 ScheduleNotification(callback, false); |
| 278 return; | 293 return; |
| 279 } | 294 } |
| 280 | 295 |
| 281 CanDownloadImpl( | 296 CanDownloadImpl( |
| 282 TabContentsWrapper::GetCurrentWrapperForContents(originating_tab), | 297 originating_contents, |
| 283 request_id, | 298 request_id, |
| 284 request_method, | 299 request_method, |
| 285 callback); | 300 callback); |
| 286 } | 301 } |
| 287 | 302 |
| 288 void DownloadRequestLimiter::CanDownloadImpl( | 303 void DownloadRequestLimiter::CanDownloadImpl(WebContents* originating_contents, |
| 289 TabContentsWrapper* originating_tab, | 304 int request_id, |
| 290 int request_id, | 305 const std::string& request_method, |
| 291 const std::string& request_method, | 306 const Callback& callback) { |
| 292 const Callback& callback) { | 307 DCHECK(originating_contents); |
| 293 DCHECK(originating_tab); | 308 |
| 309 if (!ContainsKey(observer_map_, originating_contents)) { | |
| 310 // DownloadRequestLimiterObserver is self-deleting. We just remember it so | |
| 311 // that we don't create another one for this WebContents*. | |
| 312 // DRLO cannot be merged with TabDownloadState because TDS is keyed to a | |
| 313 // NavigationController while DRLO is keyed to a WebContents. There may be | |
| 314 // many WebContents per NavigationController. A NavigationController is | |
|
Avi (use Gerrit)
2012/05/24 20:48:44
Whoa. Where did you read that? It hasn't been that
benjhayden
2012/05/24 21:01:14
http://code.google.com/searchframe#OAMlx_jo-ck/src
| |
| 315 // closer to a user's concept of a "tab" than WebContents; we only use | |
| 316 // WebContents here to route OnUserGesture() signals. It might be more | |
| 317 // "standard" to make TabContentsWrapper create and own DRLO, but there is | |
| 318 // no TCW for extension popups/bubbles. | |
| 319 observer_map_[originating_contents] = new DownloadRequestLimiterObserver( | |
| 320 originating_contents, | |
| 321 base::Bind(&DownloadRequestLimiter::RemoveObserver, this, | |
| 322 originating_contents)); | |
| 323 } | |
| 294 | 324 |
| 295 // FYI: Chrome Frame overrides CanDownload in ExternalTabContainer in order | 325 // FYI: Chrome Frame overrides CanDownload in ExternalTabContainer in order |
| 296 // to cancel the download operation in chrome and let the host browser | 326 // to cancel the download operation in chrome and let the host browser |
| 297 // take care of it. | 327 // take care of it. |
| 298 WebContents* tab = originating_tab->web_contents(); | 328 if (originating_contents->GetDelegate() && |
| 299 if (tab->GetDelegate() && !tab->GetDelegate()->CanDownload( | 329 !originating_contents->GetDelegate()->CanDownload( |
| 300 tab->GetRenderViewHost(), request_id, request_method)) { | 330 originating_contents->GetRenderViewHost(), |
| 331 request_id, | |
| 332 request_method)) { | |
| 301 ScheduleNotification(callback, false); | 333 ScheduleNotification(callback, false); |
| 302 return; | 334 return; |
| 303 } | 335 } |
| 304 | 336 |
| 305 // If the tab requesting the download is a constrained popup that is not | 337 // If the tab requesting the download is a constrained popup that is not |
| 306 // shown, treat the request as if it came from the parent. | 338 // shown, treat the request as if it came from the parent. |
| 307 TabContentsWrapper* effective_wrapper = originating_tab; | 339 WebContents* effective_contents = originating_contents; |
| 308 if (effective_wrapper->blocked_content_tab_helper()->delegate()) { | 340 TabContentsWrapper* originating_wrapper = |
| 309 effective_wrapper = effective_wrapper->blocked_content_tab_helper()-> | 341 TabContentsWrapper::GetCurrentWrapperForContents(originating_contents); |
| 310 delegate()->GetConstrainingContentsWrapper(effective_wrapper); | 342 if (originating_wrapper && |
| 343 originating_wrapper->blocked_content_tab_helper()->delegate()) { | |
| 344 effective_contents = originating_wrapper->blocked_content_tab_helper()-> | |
| 345 delegate()->GetConstrainingContentsWrapper(originating_wrapper)-> | |
| 346 web_contents(); | |
| 311 } | 347 } |
| 312 | 348 |
| 313 TabDownloadState* state = GetDownloadState( | 349 TabDownloadState* state = GetDownloadState( |
| 314 &effective_wrapper->web_contents()->GetController(), | 350 &effective_contents->GetController(), |
| 315 &tab->GetController(), true); | 351 &originating_contents->GetController(), |
| 352 true); | |
| 316 switch (state->download_status()) { | 353 switch (state->download_status()) { |
| 317 case ALLOW_ALL_DOWNLOADS: | 354 case ALLOW_ALL_DOWNLOADS: |
| 318 if (state->download_count() && !(state->download_count() % | 355 if (state->download_count() && !(state->download_count() % |
| 319 DownloadRequestLimiter::kMaxDownloadsAtOnce)) | 356 DownloadRequestLimiter::kMaxDownloadsAtOnce)) |
| 320 state->set_download_status(PROMPT_BEFORE_DOWNLOAD); | 357 state->set_download_status(PROMPT_BEFORE_DOWNLOAD); |
| 321 ScheduleNotification(callback, true); | 358 ScheduleNotification(callback, true); |
| 322 state->increment_download_count(); | 359 state->increment_download_count(); |
| 323 break; | 360 break; |
| 324 | 361 |
| 325 case ALLOW_ONE_DOWNLOAD: | 362 case ALLOW_ONE_DOWNLOAD: |
| 326 state->set_download_status(PROMPT_BEFORE_DOWNLOAD); | 363 state->set_download_status(PROMPT_BEFORE_DOWNLOAD); |
| 327 ScheduleNotification(callback, true); | 364 ScheduleNotification(callback, true); |
| 328 break; | 365 break; |
| 329 | 366 |
| 330 case DOWNLOADS_NOT_ALLOWED: | 367 case DOWNLOADS_NOT_ALLOWED: |
| 331 ScheduleNotification(callback, false); | 368 ScheduleNotification(callback, false); |
| 332 break; | 369 break; |
| 333 | 370 |
| 334 case PROMPT_BEFORE_DOWNLOAD: | 371 case PROMPT_BEFORE_DOWNLOAD: |
| 335 state->PromptUserForDownload(effective_wrapper->web_contents(), callback); | 372 state->PromptUserForDownload(effective_contents, callback); |
| 336 state->increment_download_count(); | 373 state->increment_download_count(); |
| 337 break; | 374 break; |
| 338 | 375 |
| 339 default: | 376 default: |
| 340 NOTREACHED(); | 377 NOTREACHED(); |
| 341 } | 378 } |
| 342 } | 379 } |
| 343 | 380 |
| 344 void DownloadRequestLimiter::ScheduleNotification(const Callback& callback, | 381 void DownloadRequestLimiter::ScheduleNotification(const Callback& callback, |
| 345 bool allow) { | 382 bool allow) { |
| 346 BrowserThread::PostTask( | 383 BrowserThread::PostTask( |
| 347 BrowserThread::IO, FROM_HERE, base::Bind(callback, allow)); | 384 BrowserThread::IO, FROM_HERE, base::Bind(callback, allow)); |
| 348 } | 385 } |
| 349 | 386 |
| 350 void DownloadRequestLimiter::Remove(TabDownloadState* state) { | 387 void DownloadRequestLimiter::Remove(TabDownloadState* state) { |
| 351 DCHECK(ContainsKey(state_map_, state->controller())); | 388 DCHECK(ContainsKey(state_map_, state->controller())); |
| 352 state_map_.erase(state->controller()); | 389 state_map_.erase(state->controller()); |
| 353 delete state; | 390 delete state; |
| 354 } | 391 } |
| 355 | 392 |
| 393 void DownloadRequestLimiter::RemoveObserver( | |
| 394 WebContents* web_contents, | |
| 395 DownloadRequestLimiterObserver* observer) { | |
| 396 DCHECK(ContainsKey(observer_map_, web_contents)); | |
| 397 DCHECK(observer_map_[web_contents] == observer); | |
| 398 observer_map_.erase(web_contents); | |
| 399 } | |
| 400 | |
| 356 // static | 401 // static |
| 357 DownloadRequestLimiter::TestingDelegate* DownloadRequestLimiter::delegate_ = | 402 DownloadRequestLimiter::TestingDelegate* DownloadRequestLimiter::delegate_ = |
| 358 NULL; | 403 NULL; |
| OLD | NEW |