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

Side by Side Diff: content/browser/renderer_host/render_view_host_impl.cc

Issue 10065028: Fixing a problem, where a hung renderer process is not killed when navigating away (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 8 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 "content/browser/renderer_host/render_view_host_impl.h" 5 #include "content/browser/renderer_host/render_view_host_impl.h"
6 6
7 #include <set> 7 #include <set>
8 #include <string> 8 #include <string>
9 #include <utility> 9 #include <utility>
10 #include <vector> 10 #include <vector>
11 11
12 #include "base/command_line.h" 12 #include "base/command_line.h"
13 #include "base/i18n/rtl.h" 13 #include "base/i18n/rtl.h"
14 #include "base/json/json_reader.h" 14 #include "base/json/json_reader.h"
15 #include "base/message_loop.h" 15 #include "base/message_loop.h"
16 #include "base/metrics/histogram.h"
16 #include "base/stl_util.h" 17 #include "base/stl_util.h"
17 #include "base/string_util.h" 18 #include "base/string_util.h"
18 #include "base/time.h" 19 #include "base/time.h"
19 #include "base/utf_string_conversions.h" 20 #include "base/utf_string_conversions.h"
20 #include "base/values.h" 21 #include "base/values.h"
21 #include "content/browser/child_process_security_policy_impl.h" 22 #include "content/browser/child_process_security_policy_impl.h"
22 #include "content/browser/cross_site_request_manager.h" 23 #include "content/browser/cross_site_request_manager.h"
23 #include "content/browser/gpu/gpu_surface_tracker.h" 24 #include "content/browser/gpu/gpu_surface_tracker.h"
24 #include "content/browser/host_zoom_map_impl.h" 25 #include "content/browser/host_zoom_map_impl.h"
25 #include "content/browser/in_process_webkit/dom_storage_context_impl.h" 26 #include "content/browser/in_process_webkit/dom_storage_context_impl.h"
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
135 waiting_for_drag_context_response_(false), 136 waiting_for_drag_context_response_(false),
136 enabled_bindings_(0), 137 enabled_bindings_(0),
137 guest_(false), 138 guest_(false),
138 pending_request_id_(-1), 139 pending_request_id_(-1),
139 navigations_suspended_(false), 140 navigations_suspended_(false),
140 suspended_nav_message_(NULL), 141 suspended_nav_message_(NULL),
141 is_swapped_out_(false), 142 is_swapped_out_(false),
142 run_modal_reply_msg_(NULL), 143 run_modal_reply_msg_(NULL),
143 is_waiting_for_beforeunload_ack_(false), 144 is_waiting_for_beforeunload_ack_(false),
144 is_waiting_for_unload_ack_(false), 145 is_waiting_for_unload_ack_(false),
146 has_timed_out_on_unload_(false),
145 unload_ack_is_for_cross_site_transition_(false), 147 unload_ack_is_for_cross_site_transition_(false),
146 are_javascript_messages_suppressed_(false), 148 are_javascript_messages_suppressed_(false),
147 sudden_termination_allowed_(false), 149 sudden_termination_allowed_(false),
148 session_storage_namespace_( 150 session_storage_namespace_(
149 static_cast<SessionStorageNamespaceImpl*>(session_storage)), 151 static_cast<SessionStorageNamespaceImpl*>(session_storage)),
150 save_accessibility_tree_for_testing_(false), 152 save_accessibility_tree_for_testing_(false),
151 send_accessibility_updated_notifications_(false), 153 send_accessibility_updated_notifications_(false),
152 render_view_termination_status_(base::TERMINATION_STATUS_STILL_RUNNING) { 154 render_view_termination_status_(base::TERMINATION_STATUS_STILL_RUNNING) {
153 if (!session_storage_namespace_) { 155 if (!session_storage_namespace_) {
154 DOMStorageContext* dom_storage_context = 156 DOMStorageContext* dom_storage_context =
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after
377 // (if there was a cross-site "close" request pending when the user clicked 379 // (if there was a cross-site "close" request pending when the user clicked
378 // the close button). We want to keep the "for cross site" flag only if 380 // the close button). We want to keep the "for cross site" flag only if
379 // both the old and the new ones are also for cross site. 381 // both the old and the new ones are also for cross site.
380 unload_ack_is_for_cross_site_transition_ = 382 unload_ack_is_for_cross_site_transition_ =
381 unload_ack_is_for_cross_site_transition_ && for_cross_site_transition; 383 unload_ack_is_for_cross_site_transition_ && for_cross_site_transition;
382 } else { 384 } else {
383 // Start the hang monitor in case the renderer hangs in the beforeunload 385 // Start the hang monitor in case the renderer hangs in the beforeunload
384 // handler. 386 // handler.
385 is_waiting_for_beforeunload_ack_ = true; 387 is_waiting_for_beforeunload_ack_ = true;
386 unload_ack_is_for_cross_site_transition_ = for_cross_site_transition; 388 unload_ack_is_for_cross_site_transition_ = for_cross_site_transition;
389 // Increment the in-flight event count, to ensure that input events won't
390 // cancel the timeout timer.
391 increment_in_flight_event_count();
387 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); 392 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
388 send_should_close_start_time_ = base::TimeTicks::Now(); 393 send_should_close_start_time_ = base::TimeTicks::Now();
389 Send(new ViewMsg_ShouldClose(GetRoutingID())); 394 Send(new ViewMsg_ShouldClose(GetRoutingID()));
390 } 395 }
391 } 396 }
392 397
393 void RenderViewHostImpl::SwapOut(int new_render_process_host_id, 398 void RenderViewHostImpl::SwapOut(int new_render_process_host_id,
394 int new_request_id) { 399 int new_request_id) {
395 // This will be set back to false in OnSwapOutACK, just before we replace 400 // This will be set back to false in OnSwapOutACK, just before we replace
396 // this RVH with the pending RVH. 401 // this RVH with the pending RVH.
397 is_waiting_for_unload_ack_ = true; 402 is_waiting_for_unload_ack_ = true;
398 // Start the hang monitor in case the renderer hangs in the unload handler. 403 // Start the hang monitor in case the renderer hangs in the unload handler.
404 // Increment the in-flight event count, to ensure that input events won't
405 // cancel the timeout timer.
406 increment_in_flight_event_count();
399 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); 407 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
400 408
401 ViewMsg_SwapOut_Params params; 409 ViewMsg_SwapOut_Params params;
402 params.closing_process_id = GetProcess()->GetID(); 410 params.closing_process_id = GetProcess()->GetID();
403 params.closing_route_id = GetRoutingID(); 411 params.closing_route_id = GetRoutingID();
404 params.new_render_process_host_id = new_render_process_host_id; 412 params.new_render_process_host_id = new_render_process_host_id;
405 params.new_request_id = new_request_id; 413 params.new_request_id = new_request_id;
406 if (IsRenderViewLive()) { 414 if (IsRenderViewLive()) {
407 Send(new ViewMsg_SwapOut(GetRoutingID(), params)); 415 Send(new ViewMsg_SwapOut(GetRoutingID(), params));
408 } else { 416 } else {
409 // This RenderViewHost doesn't have a live renderer, so just skip the unload 417 // This RenderViewHost doesn't have a live renderer, so just skip the unload
410 // event. We must notify the ResourceDispatcherHost on the IO thread, 418 // event. We must notify the ResourceDispatcherHost on the IO thread,
411 // which we will do through the RenderProcessHost's widget helper. 419 // which we will do through the RenderProcessHost's widget helper.
412 GetProcess()->CrossSiteSwapOutACK(params); 420 GetProcess()->SimulateSwapOutACK(params);
413 } 421 }
414 } 422 }
415 423
416 void RenderViewHostImpl::OnSwapOutACK() { 424 void RenderViewHostImpl::OnSwapOutACK(bool timed_out) {
417 // Stop the hang monitor now that the unload handler has finished. 425 // Stop the hang monitor now that the unload handler has finished.
426 decrement_in_flight_event_count();
418 StopHangMonitorTimeout(); 427 StopHangMonitorTimeout();
419 is_waiting_for_unload_ack_ = false; 428 is_waiting_for_unload_ack_ = false;
429 has_timed_out_on_unload_ = timed_out;
420 delegate_->SwappedOut(this); 430 delegate_->SwappedOut(this);
421 } 431 }
422 432
423 void RenderViewHostImpl::WasSwappedOut() { 433 void RenderViewHostImpl::WasSwappedOut() {
424 // Don't bother reporting hung state anymore. 434 // Don't bother reporting hung state anymore.
425 StopHangMonitorTimeout(); 435 StopHangMonitorTimeout();
426 436
437 // If we have timed out on running the unload handler, we consider
438 // the process hung and we should terminate it if there are no other tabs
439 // using the process. If there are other views using this process, the
440 // unresponsive renderer timeout will catch it.
441 bool hung = has_timed_out_on_unload_;
442
427 // Now that we're no longer the active RVH in the tab, start filtering out 443 // Now that we're no longer the active RVH in the tab, start filtering out
428 // most IPC messages. Usually the renderer will have stopped sending 444 // most IPC messages. Usually the renderer will have stopped sending
429 // messages as of OnSwapOutACK. However, we may have timed out waiting 445 // messages as of OnSwapOutACK. However, we may have timed out waiting
430 // for that message, and additional IPC messages may keep streaming in. 446 // for that message, and additional IPC messages may keep streaming in.
431 // We filter them out, as long as that won't cause problems (e.g., we 447 // We filter them out, as long as that won't cause problems (e.g., we
432 // still allow synchronous messages through). 448 // still allow synchronous messages through).
433 SetSwappedOut(true); 449 SetSwappedOut(true);
434 450
451 // If we are not running the renderer in process and no other tab is using
452 // the hung process, kill it, assuming it is a real process (unit tests don't
453 // have real processes).
454 if (hung) {
455 base::ProcessHandle process_handle = GetProcess()->GetHandle();
456 int views = 0;
457
458 // Count the number of widget hosts for the process, which is equivalent to
459 // views using the process as of this writing.
460 content::RenderProcessHost::RenderWidgetHostsIterator iter(
461 GetProcess()->GetRenderWidgetHostsIterator());
462 for (; !iter.IsAtEnd(); iter.Advance())
463 ++views;
464
465 if (!content::RenderProcessHost::run_renderer_in_process() &&
466 process_handle && views <= 1) {
467 // We expect the delegate for this RVH to be TabContents, as it is the
468 // only class that swaps out render view hosts on navigation.
469 DCHECK_EQ(delegate_->GetRenderViewType(),
470 content::VIEW_TYPE_TAB_CONTENTS);
471
472 // Kill the process only if TabContents sets SuddenTerminationAllowed,
473 // which indicates that the timer has expired.
474 // This is not the case if we load data URLs or about:blank. The reason
475 // is that there is no network requests and this code is hit without
476 // setting the unresponsiveness timer. This allows a corner case where a
477 // navigation to a data URL will leave a process running, if the
478 // beforeunload handler completes fine, but the unload handler hangs.
479 // At this time, the complexity to solve this edge case is not worthwhile.
480 if (SuddenTerminationAllowed()) {
481 base::KillProcess(process_handle, content::RESULT_CODE_HUNG, false);
482 // Log a histogram point to help us diagnose how many of those kills
483 // we have performed. 1 is the enum value for RendererType Normal for
484 // the histogram.
485 UMA_HISTOGRAM_PERCENTAGE(
486 "BrowserRenderProcessHost.ChildKillsUnresponsive", 1);
487 }
488 }
489 }
490
435 // Inform the renderer that it can exit if no one else is using it. 491 // Inform the renderer that it can exit if no one else is using it.
436 Send(new ViewMsg_WasSwappedOut(GetRoutingID())); 492 Send(new ViewMsg_WasSwappedOut(GetRoutingID()));
437 } 493 }
438 494
439 void RenderViewHostImpl::ClosePage() { 495 void RenderViewHostImpl::ClosePage() {
440 // Start the hang monitor in case the renderer hangs in the unload handler. 496 // Start the hang monitor in case the renderer hangs in the unload handler.
441 is_waiting_for_unload_ack_ = true; 497 is_waiting_for_unload_ack_ = true;
442 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); 498 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
443 499
444 if (IsRenderViewLive()) { 500 if (IsRenderViewLive()) {
501 // Since we are sending an IPC message to the renderer, increase the event
502 // count to prevent the hang monitor timeout from being stopped by input
503 // event acknowledgements.
504 increment_in_flight_event_count();
505
445 // TODO(creis): Should this be moved to Shutdown? It may not be called for 506 // TODO(creis): Should this be moved to Shutdown? It may not be called for
446 // RenderViewHosts that have been swapped out. 507 // RenderViewHosts that have been swapped out.
447 content::NotificationService::current()->Notify( 508 content::NotificationService::current()->Notify(
448 content::NOTIFICATION_RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW, 509 content::NOTIFICATION_RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW,
449 content::Source<RenderViewHost>(this), 510 content::Source<RenderViewHost>(this),
450 content::NotificationService::NoDetails()); 511 content::NotificationService::NoDetails());
451 512
452 Send(new ViewMsg_ClosePage(GetRoutingID())); 513 Send(new ViewMsg_ClosePage(GetRoutingID()));
453 } else { 514 } else {
454 // This RenderViewHost doesn't have a live renderer, so just skip the unload 515 // This RenderViewHost doesn't have a live renderer, so just skip the unload
(...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after
614 loop->Run(); 675 loop->Run();
615 return observer.value()->DeepCopy(); 676 return observer.value()->DeepCopy();
616 } 677 }
617 678
618 void RenderViewHostImpl::JavaScriptDialogClosed(IPC::Message* reply_msg, 679 void RenderViewHostImpl::JavaScriptDialogClosed(IPC::Message* reply_msg,
619 bool success, 680 bool success,
620 const string16& user_input) { 681 const string16& user_input) {
621 GetProcess()->SetIgnoreInputEvents(false); 682 GetProcess()->SetIgnoreInputEvents(false);
622 bool is_waiting = 683 bool is_waiting =
623 is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_; 684 is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_;
685
686 // If we are executing as part of (before)unload event handling, we don't
687 // want to use the regular hung_renderer_delay_ms_ if the user has agreed to
688 // leave the current page. In this case, use the regular timeout value used
689 // during the (before)unload handling.
624 if (is_waiting) 690 if (is_waiting)
625 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); 691 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(
692 success ? kUnloadTimeoutMS : hung_renderer_delay_ms_));
626 693
627 ViewHostMsg_RunJavaScriptMessage::WriteReplyParams(reply_msg, 694 ViewHostMsg_RunJavaScriptMessage::WriteReplyParams(reply_msg,
628 success, user_input); 695 success, user_input);
629 Send(reply_msg); 696 Send(reply_msg);
630 697
631 // If we are waiting for an unload or beforeunload ack and the user has 698 // If we are waiting for an unload or beforeunload ack and the user has
632 // suppressed messages, kill the tab immediately; a page that's spamming 699 // suppressed messages, kill the tab immediately; a page that's spamming
633 // alerts in onbeforeunload is presumably malicious, so there's no point in 700 // alerts in onbeforeunload is presumably malicious, so there's no point in
634 // continuing to run its script and dragging out the process. 701 // continuing to run its script and dragging out the process.
635 // This must be done after sending the reply since RenderView can't close 702 // This must be done after sending the reply since RenderView can't close
(...skipping 667 matching lines...) Expand 10 before | Expand all | Expand 10 after
1303 } 1370 }
1304 1371
1305 void RenderViewHostImpl::OnUserGesture() { 1372 void RenderViewHostImpl::OnUserGesture() {
1306 delegate_->OnUserGesture(); 1373 delegate_->OnUserGesture();
1307 } 1374 }
1308 1375
1309 void RenderViewHostImpl::OnMsgShouldCloseACK( 1376 void RenderViewHostImpl::OnMsgShouldCloseACK(
1310 bool proceed, 1377 bool proceed,
1311 const base::TimeTicks& renderer_before_unload_start_time, 1378 const base::TimeTicks& renderer_before_unload_start_time,
1312 const base::TimeTicks& renderer_before_unload_end_time) { 1379 const base::TimeTicks& renderer_before_unload_end_time) {
1380 decrement_in_flight_event_count();
1313 StopHangMonitorTimeout(); 1381 StopHangMonitorTimeout();
1314 // If this renderer navigated while the beforeunload request was in flight, we 1382 // If this renderer navigated while the beforeunload request was in flight, we
1315 // may have cleared this state in OnMsgNavigate, in which case we can ignore 1383 // may have cleared this state in OnMsgNavigate, in which case we can ignore
1316 // this message. 1384 // this message.
1317 if (!is_waiting_for_beforeunload_ack_ || is_swapped_out_) 1385 if (!is_waiting_for_beforeunload_ack_ || is_swapped_out_)
1318 return; 1386 return;
1319 1387
1320 is_waiting_for_beforeunload_ack_ = false; 1388 is_waiting_for_beforeunload_ack_ = false;
1321 1389
1322 RenderViewHostDelegate::RendererManagement* management_delegate = 1390 RenderViewHostDelegate::RendererManagement* management_delegate =
(...skipping 21 matching lines...) Expand all
1344 unload_ack_is_for_cross_site_transition_, proceed, 1412 unload_ack_is_for_cross_site_transition_, proceed,
1345 before_unload_end_time); 1413 before_unload_end_time);
1346 } 1414 }
1347 1415
1348 // If canceled, notify the delegate to cancel its pending navigation entry. 1416 // If canceled, notify the delegate to cancel its pending navigation entry.
1349 if (!proceed) 1417 if (!proceed)
1350 delegate_->DidCancelLoading(); 1418 delegate_->DidCancelLoading();
1351 } 1419 }
1352 1420
1353 void RenderViewHostImpl::OnMsgClosePageACK() { 1421 void RenderViewHostImpl::OnMsgClosePageACK() {
1422 decrement_in_flight_event_count();
1354 ClosePageIgnoringUnloadEvents(); 1423 ClosePageIgnoringUnloadEvents();
1355 } 1424 }
1356 1425
1357 void RenderViewHostImpl::NotifyRendererUnresponsive() { 1426 void RenderViewHostImpl::NotifyRendererUnresponsive() {
1358 delegate_->RendererUnresponsive( 1427 delegate_->RendererUnresponsive(
1359 this, is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_); 1428 this, is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_);
1360 } 1429 }
1361 1430
1362 void RenderViewHostImpl::NotifyRendererResponsive() { 1431 void RenderViewHostImpl::NotifyRendererResponsive() {
1363 delegate_->RendererResponsive(this); 1432 delegate_->RendererResponsive(this);
(...skipping 355 matching lines...) Expand 10 before | Expand all | Expand 10 after
1719 } 1788 }
1720 1789
1721 void RenderViewHostImpl::SetSwappedOut(bool is_swapped_out) { 1790 void RenderViewHostImpl::SetSwappedOut(bool is_swapped_out) {
1722 is_swapped_out_ = is_swapped_out; 1791 is_swapped_out_ = is_swapped_out;
1723 1792
1724 // Whenever we change swap out state, we should not be waiting for 1793 // Whenever we change swap out state, we should not be waiting for
1725 // beforeunload or unload acks. We clear them here to be safe, since they 1794 // beforeunload or unload acks. We clear them here to be safe, since they
1726 // can cause navigations to be ignored in OnMsgNavigate. 1795 // can cause navigations to be ignored in OnMsgNavigate.
1727 is_waiting_for_beforeunload_ack_ = false; 1796 is_waiting_for_beforeunload_ack_ = false;
1728 is_waiting_for_unload_ack_ = false; 1797 is_waiting_for_unload_ack_ = false;
1798 has_timed_out_on_unload_ = false;
1729 } 1799 }
1730 1800
1731 void RenderViewHostImpl::ClearPowerSaveBlockers() { 1801 void RenderViewHostImpl::ClearPowerSaveBlockers() {
1732 STLDeleteValues(&power_save_blockers_); 1802 STLDeleteValues(&power_save_blockers_);
1733 } 1803 }
1734 1804
1735 } // namespace content 1805 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/renderer_host/render_view_host_impl.h ('k') | content/browser/renderer_host/render_widget_helper.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698