| 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 "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" | |
| 17 #include "base/stl_util.h" | 16 #include "base/stl_util.h" |
| 18 #include "base/string_util.h" | 17 #include "base/string_util.h" |
| 19 #include "base/time.h" | 18 #include "base/time.h" |
| 20 #include "base/utf_string_conversions.h" | 19 #include "base/utf_string_conversions.h" |
| 21 #include "base/values.h" | 20 #include "base/values.h" |
| 22 #if defined(OS_WIN) | 21 #if defined(OS_WIN) |
| 23 #include "base/win/windows_version.h" | 22 #include "base/win/windows_version.h" |
| 24 #endif | 23 #endif |
| 25 #include "content/browser/child_process_security_policy_impl.h" | 24 #include "content/browser/child_process_security_policy_impl.h" |
| 26 #include "content/browser/cross_site_request_manager.h" | 25 #include "content/browser/cross_site_request_manager.h" |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 142 waiting_for_drag_context_response_(false), | 141 waiting_for_drag_context_response_(false), |
| 143 enabled_bindings_(0), | 142 enabled_bindings_(0), |
| 144 guest_(false), | 143 guest_(false), |
| 145 pending_request_id_(-1), | 144 pending_request_id_(-1), |
| 146 navigations_suspended_(false), | 145 navigations_suspended_(false), |
| 147 suspended_nav_message_(NULL), | 146 suspended_nav_message_(NULL), |
| 148 is_swapped_out_(swapped_out), | 147 is_swapped_out_(swapped_out), |
| 149 run_modal_reply_msg_(NULL), | 148 run_modal_reply_msg_(NULL), |
| 150 is_waiting_for_beforeunload_ack_(false), | 149 is_waiting_for_beforeunload_ack_(false), |
| 151 is_waiting_for_unload_ack_(false), | 150 is_waiting_for_unload_ack_(false), |
| 152 has_timed_out_on_unload_(false), | |
| 153 unload_ack_is_for_cross_site_transition_(false), | 151 unload_ack_is_for_cross_site_transition_(false), |
| 154 are_javascript_messages_suppressed_(false), | 152 are_javascript_messages_suppressed_(false), |
| 155 sudden_termination_allowed_(false), | 153 sudden_termination_allowed_(false), |
| 156 session_storage_namespace_( | 154 session_storage_namespace_( |
| 157 static_cast<SessionStorageNamespaceImpl*>(session_storage)), | 155 static_cast<SessionStorageNamespaceImpl*>(session_storage)), |
| 158 save_accessibility_tree_for_testing_(false), | 156 save_accessibility_tree_for_testing_(false), |
| 159 send_accessibility_updated_notifications_(false), | 157 send_accessibility_updated_notifications_(false), |
| 160 render_view_termination_status_(base::TERMINATION_STATUS_STILL_RUNNING) { | 158 render_view_termination_status_(base::TERMINATION_STATUS_STILL_RUNNING) { |
| 161 if (!session_storage_namespace_) { | 159 if (!session_storage_namespace_) { |
| 162 DOMStorageContext* dom_storage_context = | 160 DOMStorageContext* dom_storage_context = |
| (...skipping 237 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 400 // (if there was a cross-site "close" request pending when the user clicked | 398 // (if there was a cross-site "close" request pending when the user clicked |
| 401 // the close button). We want to keep the "for cross site" flag only if | 399 // the close button). We want to keep the "for cross site" flag only if |
| 402 // both the old and the new ones are also for cross site. | 400 // both the old and the new ones are also for cross site. |
| 403 unload_ack_is_for_cross_site_transition_ = | 401 unload_ack_is_for_cross_site_transition_ = |
| 404 unload_ack_is_for_cross_site_transition_ && for_cross_site_transition; | 402 unload_ack_is_for_cross_site_transition_ && for_cross_site_transition; |
| 405 } else { | 403 } else { |
| 406 // Start the hang monitor in case the renderer hangs in the beforeunload | 404 // Start the hang monitor in case the renderer hangs in the beforeunload |
| 407 // handler. | 405 // handler. |
| 408 is_waiting_for_beforeunload_ack_ = true; | 406 is_waiting_for_beforeunload_ack_ = true; |
| 409 unload_ack_is_for_cross_site_transition_ = for_cross_site_transition; | 407 unload_ack_is_for_cross_site_transition_ = for_cross_site_transition; |
| 410 // Increment the in-flight event count, to ensure that input events won't | |
| 411 // cancel the timeout timer. | |
| 412 increment_in_flight_event_count(); | |
| 413 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); | 408 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); |
| 414 send_should_close_start_time_ = base::TimeTicks::Now(); | 409 send_should_close_start_time_ = base::TimeTicks::Now(); |
| 415 Send(new ViewMsg_ShouldClose(GetRoutingID())); | 410 Send(new ViewMsg_ShouldClose(GetRoutingID())); |
| 416 } | 411 } |
| 417 } | 412 } |
| 418 | 413 |
| 419 void RenderViewHostImpl::SwapOut(int new_render_process_host_id, | 414 void RenderViewHostImpl::SwapOut(int new_render_process_host_id, |
| 420 int new_request_id) { | 415 int new_request_id) { |
| 421 // This will be set back to false in OnSwapOutACK, just before we replace | 416 // This will be set back to false in OnSwapOutACK, just before we replace |
| 422 // this RVH with the pending RVH. | 417 // this RVH with the pending RVH. |
| 423 is_waiting_for_unload_ack_ = true; | 418 is_waiting_for_unload_ack_ = true; |
| 424 // Start the hang monitor in case the renderer hangs in the unload handler. | 419 // Start the hang monitor in case the renderer hangs in the unload handler. |
| 425 // Increment the in-flight event count, to ensure that input events won't | |
| 426 // cancel the timeout timer. | |
| 427 increment_in_flight_event_count(); | |
| 428 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); | 420 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); |
| 429 | 421 |
| 430 ViewMsg_SwapOut_Params params; | 422 ViewMsg_SwapOut_Params params; |
| 431 params.closing_process_id = GetProcess()->GetID(); | 423 params.closing_process_id = GetProcess()->GetID(); |
| 432 params.closing_route_id = GetRoutingID(); | 424 params.closing_route_id = GetRoutingID(); |
| 433 params.new_render_process_host_id = new_render_process_host_id; | 425 params.new_render_process_host_id = new_render_process_host_id; |
| 434 params.new_request_id = new_request_id; | 426 params.new_request_id = new_request_id; |
| 435 if (IsRenderViewLive()) { | 427 if (IsRenderViewLive()) { |
| 436 Send(new ViewMsg_SwapOut(GetRoutingID(), params)); | 428 Send(new ViewMsg_SwapOut(GetRoutingID(), params)); |
| 437 } else { | 429 } else { |
| 438 // This RenderViewHost doesn't have a live renderer, so just skip the unload | 430 // This RenderViewHost doesn't have a live renderer, so just skip the unload |
| 439 // event. We must notify the ResourceDispatcherHost on the IO thread, | 431 // event. We must notify the ResourceDispatcherHost on the IO thread, |
| 440 // which we will do through the RenderProcessHost's widget helper. | 432 // which we will do through the RenderProcessHost's widget helper. |
| 441 GetProcess()->SimulateSwapOutACK(params); | 433 GetProcess()->CrossSiteSwapOutACK(params); |
| 442 } | 434 } |
| 443 } | 435 } |
| 444 | 436 |
| 445 void RenderViewHostImpl::OnSwapOutACK(bool timed_out) { | 437 void RenderViewHostImpl::OnSwapOutACK() { |
| 446 // Stop the hang monitor now that the unload handler has finished. | 438 // Stop the hang monitor now that the unload handler has finished. |
| 447 decrement_in_flight_event_count(); | |
| 448 StopHangMonitorTimeout(); | 439 StopHangMonitorTimeout(); |
| 449 is_waiting_for_unload_ack_ = false; | 440 is_waiting_for_unload_ack_ = false; |
| 450 has_timed_out_on_unload_ = timed_out; | |
| 451 delegate_->SwappedOut(this); | 441 delegate_->SwappedOut(this); |
| 452 } | 442 } |
| 453 | 443 |
| 454 void RenderViewHostImpl::WasSwappedOut() { | 444 void RenderViewHostImpl::WasSwappedOut() { |
| 455 // Don't bother reporting hung state anymore. | 445 // Don't bother reporting hung state anymore. |
| 456 StopHangMonitorTimeout(); | 446 StopHangMonitorTimeout(); |
| 457 | 447 |
| 458 // If we have timed out on running the unload handler, we consider | |
| 459 // the process hung and we should terminate it if there are no other tabs | |
| 460 // using the process. If there are other views using this process, the | |
| 461 // unresponsive renderer timeout will catch it. | |
| 462 bool hung = has_timed_out_on_unload_; | |
| 463 | |
| 464 // Now that we're no longer the active RVH in the tab, start filtering out | 448 // Now that we're no longer the active RVH in the tab, start filtering out |
| 465 // most IPC messages. Usually the renderer will have stopped sending | 449 // most IPC messages. Usually the renderer will have stopped sending |
| 466 // messages as of OnSwapOutACK. However, we may have timed out waiting | 450 // messages as of OnSwapOutACK. However, we may have timed out waiting |
| 467 // for that message, and additional IPC messages may keep streaming in. | 451 // for that message, and additional IPC messages may keep streaming in. |
| 468 // We filter them out, as long as that won't cause problems (e.g., we | 452 // We filter them out, as long as that won't cause problems (e.g., we |
| 469 // still allow synchronous messages through). | 453 // still allow synchronous messages through). |
| 470 SetSwappedOut(true); | 454 SetSwappedOut(true); |
| 471 | 455 |
| 472 // If we are not running the renderer in process and no other tab is using | |
| 473 // the hung process, kill it, assuming it is a real process (unit tests don't | |
| 474 // have real processes). | |
| 475 if (hung) { | |
| 476 base::ProcessHandle process_handle = GetProcess()->GetHandle(); | |
| 477 int views = 0; | |
| 478 | |
| 479 // Count the number of widget hosts for the process, which is equivalent to | |
| 480 // views using the process as of this writing. | |
| 481 content::RenderProcessHost::RenderWidgetHostsIterator iter( | |
| 482 GetProcess()->GetRenderWidgetHostsIterator()); | |
| 483 for (; !iter.IsAtEnd(); iter.Advance()) | |
| 484 ++views; | |
| 485 | |
| 486 if (!content::RenderProcessHost::run_renderer_in_process() && | |
| 487 process_handle && views <= 1) { | |
| 488 // We expect the delegate for this RVH to be WebContents, as it is the | |
| 489 // only class that swaps out render view hosts on navigation. | |
| 490 DCHECK_EQ(delegate_->GetRenderViewType(), | |
| 491 content::VIEW_TYPE_WEB_CONTENTS); | |
| 492 | |
| 493 // Kill the process only if WebContents sets SuddenTerminationAllowed, | |
| 494 // which indicates that the timer has expired. | |
| 495 // This is not the case if we load data URLs or about:blank. The reason | |
| 496 // is that there is no network requests and this code is hit without | |
| 497 // setting the unresponsiveness timer. This allows a corner case where a | |
| 498 // navigation to a data URL will leave a process running, if the | |
| 499 // beforeunload handler completes fine, but the unload handler hangs. | |
| 500 // At this time, the complexity to solve this edge case is not worthwhile. | |
| 501 if (SuddenTerminationAllowed()) { | |
| 502 base::KillProcess(process_handle, content::RESULT_CODE_HUNG, false); | |
| 503 // Log a histogram point to help us diagnose how many of those kills | |
| 504 // we have performed. 1 is the enum value for RendererType Normal for | |
| 505 // the histogram. | |
| 506 UMA_HISTOGRAM_PERCENTAGE( | |
| 507 "BrowserRenderProcessHost.ChildKillsUnresponsive", 1); | |
| 508 } | |
| 509 } | |
| 510 } | |
| 511 | |
| 512 // Inform the renderer that it can exit if no one else is using it. | 456 // Inform the renderer that it can exit if no one else is using it. |
| 513 Send(new ViewMsg_WasSwappedOut(GetRoutingID())); | 457 Send(new ViewMsg_WasSwappedOut(GetRoutingID())); |
| 514 } | 458 } |
| 515 | 459 |
| 516 void RenderViewHostImpl::ClosePage() { | 460 void RenderViewHostImpl::ClosePage() { |
| 517 // Start the hang monitor in case the renderer hangs in the unload handler. | 461 // Start the hang monitor in case the renderer hangs in the unload handler. |
| 518 is_waiting_for_unload_ack_ = true; | 462 is_waiting_for_unload_ack_ = true; |
| 519 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); | 463 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); |
| 520 | 464 |
| 521 if (IsRenderViewLive()) { | 465 if (IsRenderViewLive()) { |
| 522 // Since we are sending an IPC message to the renderer, increase the event | |
| 523 // count to prevent the hang monitor timeout from being stopped by input | |
| 524 // event acknowledgements. | |
| 525 increment_in_flight_event_count(); | |
| 526 | |
| 527 // TODO(creis): Should this be moved to Shutdown? It may not be called for | 466 // TODO(creis): Should this be moved to Shutdown? It may not be called for |
| 528 // RenderViewHosts that have been swapped out. | 467 // RenderViewHosts that have been swapped out. |
| 529 content::NotificationService::current()->Notify( | 468 content::NotificationService::current()->Notify( |
| 530 content::NOTIFICATION_RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW, | 469 content::NOTIFICATION_RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW, |
| 531 content::Source<RenderViewHost>(this), | 470 content::Source<RenderViewHost>(this), |
| 532 content::NotificationService::NoDetails()); | 471 content::NotificationService::NoDetails()); |
| 533 | 472 |
| 534 Send(new ViewMsg_ClosePage(GetRoutingID())); | 473 Send(new ViewMsg_ClosePage(GetRoutingID())); |
| 535 } else { | 474 } else { |
| 536 // This RenderViewHost doesn't have a live renderer, so just skip the unload | 475 // This RenderViewHost doesn't have a live renderer, so just skip the unload |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 704 loop->Run(); | 643 loop->Run(); |
| 705 return observer.value()->DeepCopy(); | 644 return observer.value()->DeepCopy(); |
| 706 } | 645 } |
| 707 | 646 |
| 708 void RenderViewHostImpl::JavaScriptDialogClosed(IPC::Message* reply_msg, | 647 void RenderViewHostImpl::JavaScriptDialogClosed(IPC::Message* reply_msg, |
| 709 bool success, | 648 bool success, |
| 710 const string16& user_input) { | 649 const string16& user_input) { |
| 711 GetProcess()->SetIgnoreInputEvents(false); | 650 GetProcess()->SetIgnoreInputEvents(false); |
| 712 bool is_waiting = | 651 bool is_waiting = |
| 713 is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_; | 652 is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_; |
| 714 | |
| 715 // If we are executing as part of (before)unload event handling, we don't | |
| 716 // want to use the regular hung_renderer_delay_ms_ if the user has agreed to | |
| 717 // leave the current page. In this case, use the regular timeout value used | |
| 718 // during the (before)unload handling. | |
| 719 if (is_waiting) | 653 if (is_waiting) |
| 720 StartHangMonitorTimeout(TimeDelta::FromMilliseconds( | 654 StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS)); |
| 721 success ? kUnloadTimeoutMS : hung_renderer_delay_ms_)); | |
| 722 | 655 |
| 723 ViewHostMsg_RunJavaScriptMessage::WriteReplyParams(reply_msg, | 656 ViewHostMsg_RunJavaScriptMessage::WriteReplyParams(reply_msg, |
| 724 success, user_input); | 657 success, user_input); |
| 725 Send(reply_msg); | 658 Send(reply_msg); |
| 726 | 659 |
| 727 // If we are waiting for an unload or beforeunload ack and the user has | 660 // If we are waiting for an unload or beforeunload ack and the user has |
| 728 // suppressed messages, kill the tab immediately; a page that's spamming | 661 // suppressed messages, kill the tab immediately; a page that's spamming |
| 729 // alerts in onbeforeunload is presumably malicious, so there's no point in | 662 // alerts in onbeforeunload is presumably malicious, so there's no point in |
| 730 // continuing to run its script and dragging out the process. | 663 // continuing to run its script and dragging out the process. |
| 731 // This must be done after sending the reply since RenderView can't close | 664 // This must be done after sending the reply since RenderView can't close |
| (...skipping 688 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1420 } | 1353 } |
| 1421 | 1354 |
| 1422 void RenderViewHostImpl::OnUserGesture() { | 1355 void RenderViewHostImpl::OnUserGesture() { |
| 1423 delegate_->OnUserGesture(); | 1356 delegate_->OnUserGesture(); |
| 1424 } | 1357 } |
| 1425 | 1358 |
| 1426 void RenderViewHostImpl::OnMsgShouldCloseACK( | 1359 void RenderViewHostImpl::OnMsgShouldCloseACK( |
| 1427 bool proceed, | 1360 bool proceed, |
| 1428 const base::TimeTicks& renderer_before_unload_start_time, | 1361 const base::TimeTicks& renderer_before_unload_start_time, |
| 1429 const base::TimeTicks& renderer_before_unload_end_time) { | 1362 const base::TimeTicks& renderer_before_unload_end_time) { |
| 1430 decrement_in_flight_event_count(); | |
| 1431 StopHangMonitorTimeout(); | 1363 StopHangMonitorTimeout(); |
| 1432 // If this renderer navigated while the beforeunload request was in flight, we | 1364 // If this renderer navigated while the beforeunload request was in flight, we |
| 1433 // may have cleared this state in OnMsgNavigate, in which case we can ignore | 1365 // may have cleared this state in OnMsgNavigate, in which case we can ignore |
| 1434 // this message. | 1366 // this message. |
| 1435 if (!is_waiting_for_beforeunload_ack_ || is_swapped_out_) | 1367 if (!is_waiting_for_beforeunload_ack_ || is_swapped_out_) |
| 1436 return; | 1368 return; |
| 1437 | 1369 |
| 1438 is_waiting_for_beforeunload_ack_ = false; | 1370 is_waiting_for_beforeunload_ack_ = false; |
| 1439 | 1371 |
| 1440 RenderViewHostDelegate::RendererManagement* management_delegate = | 1372 RenderViewHostDelegate::RendererManagement* management_delegate = |
| (...skipping 21 matching lines...) Expand all Loading... |
| 1462 unload_ack_is_for_cross_site_transition_, proceed, | 1394 unload_ack_is_for_cross_site_transition_, proceed, |
| 1463 before_unload_end_time); | 1395 before_unload_end_time); |
| 1464 } | 1396 } |
| 1465 | 1397 |
| 1466 // If canceled, notify the delegate to cancel its pending navigation entry. | 1398 // If canceled, notify the delegate to cancel its pending navigation entry. |
| 1467 if (!proceed) | 1399 if (!proceed) |
| 1468 delegate_->DidCancelLoading(); | 1400 delegate_->DidCancelLoading(); |
| 1469 } | 1401 } |
| 1470 | 1402 |
| 1471 void RenderViewHostImpl::OnMsgClosePageACK() { | 1403 void RenderViewHostImpl::OnMsgClosePageACK() { |
| 1472 decrement_in_flight_event_count(); | |
| 1473 ClosePageIgnoringUnloadEvents(); | 1404 ClosePageIgnoringUnloadEvents(); |
| 1474 } | 1405 } |
| 1475 | 1406 |
| 1476 void RenderViewHostImpl::NotifyRendererUnresponsive() { | 1407 void RenderViewHostImpl::NotifyRendererUnresponsive() { |
| 1477 delegate_->RendererUnresponsive( | 1408 delegate_->RendererUnresponsive( |
| 1478 this, is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_); | 1409 this, is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_); |
| 1479 } | 1410 } |
| 1480 | 1411 |
| 1481 void RenderViewHostImpl::NotifyRendererResponsive() { | 1412 void RenderViewHostImpl::NotifyRendererResponsive() { |
| 1482 delegate_->RendererResponsive(this); | 1413 delegate_->RendererResponsive(this); |
| (...skipping 351 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1834 } | 1765 } |
| 1835 | 1766 |
| 1836 void RenderViewHostImpl::SetSwappedOut(bool is_swapped_out) { | 1767 void RenderViewHostImpl::SetSwappedOut(bool is_swapped_out) { |
| 1837 is_swapped_out_ = is_swapped_out; | 1768 is_swapped_out_ = is_swapped_out; |
| 1838 | 1769 |
| 1839 // Whenever we change swap out state, we should not be waiting for | 1770 // Whenever we change swap out state, we should not be waiting for |
| 1840 // beforeunload or unload acks. We clear them here to be safe, since they | 1771 // beforeunload or unload acks. We clear them here to be safe, since they |
| 1841 // can cause navigations to be ignored in OnMsgNavigate. | 1772 // can cause navigations to be ignored in OnMsgNavigate. |
| 1842 is_waiting_for_beforeunload_ack_ = false; | 1773 is_waiting_for_beforeunload_ack_ = false; |
| 1843 is_waiting_for_unload_ack_ = false; | 1774 is_waiting_for_unload_ack_ = false; |
| 1844 has_timed_out_on_unload_ = false; | |
| 1845 } | 1775 } |
| 1846 | 1776 |
| 1847 void RenderViewHostImpl::ClearPowerSaveBlockers() { | 1777 void RenderViewHostImpl::ClearPowerSaveBlockers() { |
| 1848 STLDeleteValues(&power_save_blockers_); | 1778 STLDeleteValues(&power_save_blockers_); |
| 1849 } | 1779 } |
| 1850 | 1780 |
| 1851 } // namespace content | 1781 } // namespace content |
| OLD | NEW |