Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(571)

Side by Side Diff: chrome/browser/download/download_request_limiter.cc

Issue 10412061: Fix crashes in DownloadRequestLimiter when extension popups/bubbles initiate downloads automatically (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: comment Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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;
OLDNEW
« no previous file with comments | « chrome/browser/download/download_request_limiter.h ('k') | chrome/browser/download/download_request_limiter_observer.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698