| Index: content/browser/renderer_host/render_view_host_impl.cc
|
| diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
|
| index 3edbcc9fb6ad251ba86e33ad69c477e26dd10334..8ef3c4283265f6f027d9216e4acc8ecd7e5969ee 100644
|
| --- a/content/browser/renderer_host/render_view_host_impl.cc
|
| +++ b/content/browser/renderer_host/render_view_host_impl.cc
|
| @@ -41,6 +41,7 @@
|
| #include "content/public/browser/notification_details.h"
|
| #include "content/public/browser/notification_service.h"
|
| #include "content/public/browser/notification_types.h"
|
| +#include "content/public/browser/render_process_host.h"
|
| #include "content/public/browser/render_view_host_delegate.h"
|
| #include "content/public/browser/render_view_host_observer.h"
|
| #include "content/public/browser/user_metrics.h"
|
| @@ -419,14 +420,17 @@ void RenderViewHostImpl::SwapOut(int new_render_process_host_id,
|
|
|
| void RenderViewHostImpl::OnSwapOutACK() {
|
| // Stop the hang monitor now that the unload handler has finished.
|
| - StopHangMonitorTimeout();
|
| is_waiting_for_unload_ack_ = false;
|
| + StopHangMonitorTimeout();
|
| delegate_->SwappedOut(this);
|
| }
|
|
|
| void RenderViewHostImpl::WasSwappedOut() {
|
| - // Don't bother reporting hung state anymore.
|
| - StopHangMonitorTimeout();
|
| + // If we are still waiting on the unload handler to be run, we consider
|
| + // the process hung and we should terminate it if there are no other tabs
|
| + // using the process. If there are other views using this process, the
|
| + // unresponsive renderer timeout will catch it.
|
| + bool hung = is_waiting_for_unload_ack_;
|
|
|
| // Now that we're no longer the active RVH in the tab, start filtering out
|
| // most IPC messages. Usually the renderer will have stopped sending
|
| @@ -436,6 +440,43 @@ void RenderViewHostImpl::WasSwappedOut() {
|
| // still allow synchronous messages through).
|
| SetSwappedOut(true);
|
|
|
| + // Don't bother reporting hung state anymore.
|
| + StopHangMonitorTimeout();
|
| +
|
| + // If we are not running the renderer in process and no other tab is using
|
| + // the hung process, kill it, assuming it is a real process (unit tests don't
|
| + // have real processes).
|
| + if (hung) {
|
| + base::ProcessHandle process_handle = GetProcess()->GetHandle();
|
| + int views = 0;
|
| +
|
| + // Count the number of listeners for the process, which is equivalent to
|
| + // views using the process as of this writing.
|
| + content::RenderProcessHost::RenderWidgetHostsIterator iter(
|
| + GetProcess()->GetRenderWidgetHostsIterator());
|
| + for (; !iter.IsAtEnd(); iter.Advance())
|
| + ++views;
|
| +
|
| + if (!content::RenderProcessHost::run_renderer_in_process() &&
|
| + process_handle && views <= 1) {
|
| + // We expect the delegate for this RVH to be TabContents, as it is the
|
| + // only class that swaps out render view hosts on navigation.
|
| + DCHECK(delegate_->GetRenderViewType() == content::VIEW_TYPE_TAB_CONTENTS);
|
| +
|
| + // Kill the process only if TabContents sets SuddenTerminationAllowed,
|
| + // which indicates that the timer has expired.
|
| + // This is not the case if we load data URLs or about:blank. The reason
|
| + // is that there is no network requests and this code is hit without
|
| + // setting the unresponsiveness timer. This allows a corner case where a
|
| + // navigation to a data URL will leave a process running, if the
|
| + // beforeunload handler completes fine, but the unload handler hangs.
|
| + // At this time, the complexity to solve this edge case is not worthwhile.
|
| + if (SuddenTerminationAllowed()) {
|
| + base::KillProcess(process_handle, content::RESULT_CODE_HUNG, false);
|
| + }
|
| + }
|
| + }
|
| +
|
| // Inform the renderer that it can exit if no one else is using it.
|
| Send(new ViewMsg_WasSwappedOut(GetRoutingID()));
|
| }
|
| @@ -462,9 +503,9 @@ void RenderViewHostImpl::ClosePage() {
|
| }
|
|
|
| void RenderViewHostImpl::ClosePageIgnoringUnloadEvents() {
|
| - StopHangMonitorTimeout();
|
| is_waiting_for_beforeunload_ack_ = false;
|
| is_waiting_for_unload_ack_ = false;
|
| + StopHangMonitorTimeout();
|
|
|
| sudden_termination_allowed_ = true;
|
| delegate_->Close(this);
|
| @@ -1329,14 +1370,16 @@ void RenderViewHostImpl::OnMsgShouldCloseACK(
|
| bool proceed,
|
| const base::TimeTicks& renderer_before_unload_start_time,
|
| const base::TimeTicks& renderer_before_unload_end_time) {
|
| - StopHangMonitorTimeout();
|
| // If this renderer navigated while the beforeunload request was in flight, we
|
| // may have cleared this state in OnMsgNavigate, in which case we can ignore
|
| // this message.
|
| - if (!is_waiting_for_beforeunload_ack_ || is_swapped_out_)
|
| + if (!is_waiting_for_beforeunload_ack_ || is_swapped_out_) {
|
| + StopHangMonitorTimeout();
|
| return;
|
| + }
|
|
|
| is_waiting_for_beforeunload_ack_ = false;
|
| + StopHangMonitorTimeout();
|
|
|
| RenderViewHostDelegate::RendererManagement* management_delegate =
|
| delegate_->GetRendererManagementDelegate();
|
| @@ -1738,4 +1781,17 @@ void RenderViewHostImpl::ClearPowerSaveBlockers() {
|
| STLDeleteValues(&power_save_blockers_);
|
| }
|
|
|
| +// During cross-site navigation, we have timeouts on beforeunload and unload
|
| +// handler processing. If we are asked to stop that timeout monitoring while
|
| +// we have an outstanding handler processing, we must not stop it. If we do,
|
| +// then slow or unresponsive renderer process will hang the navigation as there
|
| +// will be no way for us to detect it. Stopping of the timer is most frequently
|
| +// caused by input events, which restart the hang detection timeout.
|
| +void RenderViewHostImpl::StopHangMonitorTimeout() {
|
| + if (is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_)
|
| + return;
|
| +
|
| + RenderWidgetHostImpl::StopHangMonitorTimeout();
|
| +}
|
| +
|
| } // namespace content
|
|
|