| 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 801184144ae9217e57361928d4138cb82ef1738a..74a91dd1c4c965acfd3c7b191e67459c8ea3c5a3 100644
|
| --- a/content/browser/renderer_host/render_view_host_impl.cc
|
| +++ b/content/browser/renderer_host/render_view_host_impl.cc
|
| @@ -35,6 +35,7 @@
|
| #include "content/browser/host_zoom_map_impl.h"
|
| #include "content/browser/loader/resource_dispatcher_host_impl.h"
|
| #include "content/browser/renderer_host/dip_util.h"
|
| +#include "content/browser/renderer_host/input/timeout_monitor.h"
|
| #include "content/browser/renderer_host/media/audio_renderer_host.h"
|
| #include "content/browser/renderer_host/render_process_host_impl.h"
|
| #include "content/browser/renderer_host/render_view_host_delegate.h"
|
| @@ -150,6 +151,16 @@ void DismissVirtualKeyboardTask() {
|
| // RenderViewHost, public:
|
|
|
| // static
|
| +bool RenderViewHostImpl::IsRVHStateActive(RenderViewHostImplState rvh_state) {
|
| + if (rvh_state == STATE_DEFAULT ||
|
| + rvh_state == STATE_WAITING_FOR_UNLOAD_ACK ||
|
| + rvh_state == STATE_WAITING_FOR_COMMIT ||
|
| + rvh_state == STATE_WAITING_FOR_CLOSE)
|
| + return true;
|
| + return false;
|
| +}
|
| +
|
| +// static
|
| RenderViewHost* RenderViewHost::FromID(int render_process_id,
|
| int render_view_id) {
|
| return RenderViewHostImpl::FromID(render_process_id, render_view_id);
|
| @@ -186,32 +197,35 @@ RenderViewHostImpl::RenderViewHostImpl(
|
| instance->GetProcess(),
|
| routing_id,
|
| hidden),
|
| + frames_ref_count_(0),
|
| delegate_(delegate),
|
| instance_(static_cast<SiteInstanceImpl*>(instance)),
|
| waiting_for_drag_context_response_(false),
|
| enabled_bindings_(0),
|
| navigations_suspended_(false),
|
| has_accessed_initial_document_(false),
|
| - is_swapped_out_(swapped_out),
|
| main_frame_id_(-1),
|
| main_frame_routing_id_(main_frame_routing_id),
|
| run_modal_reply_msg_(NULL),
|
| run_modal_opener_id_(MSG_ROUTING_NONE),
|
| is_waiting_for_beforeunload_ack_(false),
|
| - is_waiting_for_unload_ack_(false),
|
| - has_timed_out_on_unload_(false),
|
| unload_ack_is_for_cross_site_transition_(false),
|
| are_javascript_messages_suppressed_(false),
|
| sudden_termination_allowed_(false),
|
| render_view_termination_status_(base::TERMINATION_STATUS_STILL_RUNNING),
|
| - virtual_keyboard_requested_(false) {
|
| + virtual_keyboard_requested_(false),
|
| + weak_factory_(this) {
|
| DCHECK(instance_.get());
|
| CHECK(delegate_); // http://crbug.com/82827
|
|
|
| GetProcess()->EnableSendQueue();
|
|
|
| - if (!swapped_out)
|
| + if (swapped_out) {
|
| + rvh_state_ = STATE_SWAPPED_OUT;
|
| + } else {
|
| + rvh_state_ = STATE_DEFAULT;
|
| instance_->increment_active_view_count();
|
| + }
|
|
|
| if (ResourceDispatcherHostImpl::Get()) {
|
| BrowserThread::PostTask(
|
| @@ -224,6 +238,9 @@ RenderViewHostImpl::RenderViewHostImpl(
|
| #if defined(OS_ANDROID)
|
| media_player_manager_.reset(BrowserMediaPlayerManager::Create(this));
|
| #endif
|
| +
|
| + unload_event_monitor_timeout_.reset(new TimeoutMonitor(base::Bind(
|
| + &RenderViewHostImpl::OnSwappedOut, weak_factory_.GetWeakPtr(), true)));
|
| }
|
|
|
| RenderViewHostImpl::~RenderViewHostImpl() {
|
| @@ -243,7 +260,7 @@ RenderViewHostImpl::~RenderViewHostImpl() {
|
|
|
| // If this was swapped out, it already decremented the active view
|
| // count of the SiteInstance it belongs to.
|
| - if (!is_swapped_out_)
|
| + if (IsRVHStateActive(rvh_state_))
|
| instance_->decrement_active_view_count();
|
| }
|
|
|
| @@ -294,7 +311,7 @@ bool RenderViewHostImpl::CreateRenderView(
|
| params.frame_name = frame_name;
|
| // Ensure the RenderView sets its opener correctly.
|
| params.opener_route_id = opener_route_id;
|
| - params.swapped_out = is_swapped_out_;
|
| + params.swapped_out = !IsRVHStateActive(rvh_state_);
|
| params.hidden = is_hidden();
|
| params.next_page_id = next_page_id;
|
| GetWebScreenInfo(¶ms.screen_info);
|
| @@ -593,7 +610,7 @@ void RenderViewHostImpl::Navigate(const ViewMsg_Navigate_Params& params) {
|
| } else {
|
| // Get back to a clean state, in case we start a new navigation without
|
| // completing a RVH swap or unload handler.
|
| - SetSwappedOut(false);
|
| + SetState(STATE_DEFAULT);
|
|
|
| Send(new ViewMsg_Navigate(GetRoutingID(), params));
|
| }
|
| @@ -636,7 +653,7 @@ void RenderViewHostImpl::SetNavigationsSuspended(
|
| // There's navigation message params waiting to be sent. Now that we're not
|
| // suspended anymore, resume navigation by sending them. If we were swapped
|
| // out, we should also stop filtering out the IPC messages now.
|
| - SetSwappedOut(false);
|
| + SetState(STATE_DEFAULT);
|
|
|
| DCHECK(!proceed_time.is_null());
|
| suspended_nav_params_->browser_navigation_start = proceed_time;
|
| @@ -717,22 +734,14 @@ void RenderViewHostImpl::SuppressDialogsUntilSwapOut() {
|
| }
|
|
|
| void RenderViewHostImpl::SwapOut() {
|
| - // This will be set back to false in OnSwapOutACK, just before we replace
|
| - // this RVH with the pending RVH.
|
| - is_waiting_for_unload_ack_ = true;
|
| - // Start the hang monitor in case the renderer hangs in the unload handler.
|
| - // Increment the in-flight event count, to ensure that input events won't
|
| - // cancel the timeout timer.
|
| - increment_in_flight_event_count();
|
| - StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
|
| + SetState(STATE_WAITING_FOR_UNLOAD_ACK);
|
| + unload_event_monitor_timeout_->Start(
|
| + base::TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
|
|
|
| if (IsRenderViewLive()) {
|
| Send(new ViewMsg_SwapOut(GetRoutingID()));
|
| - } else {
|
| - // This RenderViewHost doesn't have a live renderer, so just skip the unload
|
| - // event.
|
| - OnSwappedOut(true);
|
| }
|
| + delegate_->SwappedOut(this);
|
| }
|
|
|
| void RenderViewHostImpl::OnSwapOutACK() {
|
| @@ -740,36 +749,11 @@ void RenderViewHostImpl::OnSwapOutACK() {
|
| }
|
|
|
| void RenderViewHostImpl::OnSwappedOut(bool timed_out) {
|
| - // Stop the hang monitor now that the unload handler has finished.
|
| - decrement_in_flight_event_count();
|
| - StopHangMonitorTimeout();
|
| - is_waiting_for_unload_ack_ = false;
|
| - has_timed_out_on_unload_ = timed_out;
|
| - delegate_->SwappedOut(this);
|
| -}
|
| -
|
| -void RenderViewHostImpl::WasSwappedOut() {
|
| - // Don't bother reporting hung state anymore.
|
| - StopHangMonitorTimeout();
|
| -
|
| - // If we have timed out on running the unload handler, 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 = has_timed_out_on_unload_;
|
| -
|
| - // 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
|
| - // messages as of OnSwapOutACK. However, we may have timed out waiting
|
| - // for that message, and additional IPC messages may keep streaming in.
|
| - // We filter them out, as long as that won't cause problems (e.g., we
|
| - // still allow synchronous messages through).
|
| - SetSwappedOut(true);
|
| -
|
| - // If we are not running the renderer in process and no other tab is using
|
| - // the hung process, consider it eligible to be killed, assuming it is a real
|
| - // process (unit tests don't have real processes).
|
| - if (hung) {
|
| + // Ignore spurious swap out ack.
|
| + if (!IsWaitingForUnloadACK())
|
| + return;
|
| + unload_event_monitor_timeout_->Stop();
|
| + if (timed_out) {
|
| base::ProcessHandle process_handle = GetProcess()->GetHandle();
|
| int views = 0;
|
|
|
| @@ -806,13 +790,50 @@ void RenderViewHostImpl::WasSwappedOut() {
|
| }
|
| }
|
|
|
| - // Inform the renderer that it can exit if no one else is using it.
|
| + switch (rvh_state_) {
|
| + case STATE_WAITING_FOR_UNLOAD_ACK:
|
| + SetState(STATE_WAITING_FOR_COMMIT);
|
| + break;
|
| + case STATE_PENDING_SWAP_OUT:
|
| + SetState(STATE_SWAPPED_OUT);
|
| + break;
|
| + case STATE_PENDING_SHUTDOWN:
|
| + DCHECK(!pending_shutdown_on_swap_out_.is_null());
|
| + pending_shutdown_on_swap_out_.Run();
|
| + break;
|
| + default:
|
| + NOTREACHED();
|
| + }
|
| +}
|
| +
|
| +void RenderViewHostImpl::WasSwappedOut(
|
| + const base::Closure& pending_delete_on_swap_out) {
|
| Send(new ViewMsg_WasSwappedOut(GetRoutingID()));
|
| + if (rvh_state_ == STATE_WAITING_FOR_UNLOAD_ACK) {
|
| + if (instance_->active_view_count())
|
| + SetState(STATE_PENDING_SWAP_OUT);
|
| + else
|
| + SetPendingShutdown(pending_delete_on_swap_out);
|
| + } else if (rvh_state_ == STATE_WAITING_FOR_COMMIT) {
|
| + SetState(STATE_SWAPPED_OUT);
|
| + } else if (rvh_state_ == STATE_DEFAULT) {
|
| + // When the RenderView is not live, the RenderFrameHostManager will call
|
| + // CommitPending directly, without calling SwapOut on the old RVH. This will
|
| + // cause WasSwappedOut to be called directly on the live old RVH.
|
| + DCHECK(!IsRenderViewLive());
|
| + SetState(STATE_SWAPPED_OUT);
|
| + } else {
|
| + NOTREACHED();
|
| + }
|
| +}
|
| +
|
| +void RenderViewHostImpl::SetPendingShutdown(const base::Closure& on_swap_out) {
|
| + pending_shutdown_on_swap_out_ = on_swap_out;
|
| + SetState(STATE_PENDING_SHUTDOWN);
|
| }
|
|
|
| void RenderViewHostImpl::ClosePage() {
|
| - // Start the hang monitor in case the renderer hangs in the unload handler.
|
| - is_waiting_for_unload_ack_ = true;
|
| + SetState(STATE_WAITING_FOR_CLOSE);
|
| StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
|
|
|
| if (IsRenderViewLive()) {
|
| @@ -839,7 +860,6 @@ void RenderViewHostImpl::ClosePage() {
|
| void RenderViewHostImpl::ClosePageIgnoringUnloadEvents() {
|
| StopHangMonitorTimeout();
|
| is_waiting_for_beforeunload_ack_ = false;
|
| - is_waiting_for_unload_ack_ = false;
|
|
|
| sudden_termination_allowed_ = true;
|
| delegate_->Close(this);
|
| @@ -1012,8 +1032,7 @@ void RenderViewHostImpl::JavaScriptDialogClosed(
|
| bool success,
|
| const base::string16& user_input) {
|
| GetProcess()->SetIgnoreInputEvents(false);
|
| - bool is_waiting =
|
| - is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_;
|
| + bool is_waiting = is_waiting_for_beforeunload_ack_ || IsWaitingForUnloadACK();
|
|
|
| // If we are executing as part of (before)unload event handling, we don't
|
| // want to use the regular hung_renderer_delay_ms_ if the user has agreed to
|
| @@ -1036,7 +1055,7 @@ void RenderViewHostImpl::JavaScriptDialogClosed(
|
| // correctly while waiting for a response.
|
| if (is_waiting && are_javascript_messages_suppressed_)
|
| delegate_->RendererUnresponsive(
|
| - this, is_waiting_for_beforeunload_ack_, is_waiting_for_unload_ack_);
|
| + this, is_waiting_for_beforeunload_ack_, IsWaitingForUnloadACK());
|
| }
|
|
|
| void RenderViewHostImpl::DragSourceEndedAt(
|
| @@ -1192,7 +1211,7 @@ bool RenderViewHostImpl::OnMessageReceived(const IPC::Message& msg) {
|
|
|
| // Filter out most IPC messages if this renderer is swapped out.
|
| // We still want to handle certain ACKs to keep our state consistent.
|
| - if (is_swapped_out_) {
|
| + if (IsSwappedOut()) {
|
| if (!SwappedOutMessages::CanHandleWhileSwappedOut(msg)) {
|
| // If this is a synchronous message and we decided not to handle it,
|
| // we must send an error reply, or else the renderer will be stuck
|
| @@ -1359,7 +1378,7 @@ void RenderViewHostImpl::OnShowView(int route_id,
|
| WindowOpenDisposition disposition,
|
| const gfx::Rect& initial_pos,
|
| bool user_gesture) {
|
| - if (!is_swapped_out_) {
|
| + if (IsRVHStateActive(rvh_state_)) {
|
| delegate_->ShowCreatedWindow(
|
| route_id, disposition, initial_pos, user_gesture);
|
| }
|
| @@ -1368,13 +1387,13 @@ void RenderViewHostImpl::OnShowView(int route_id,
|
|
|
| void RenderViewHostImpl::OnShowWidget(int route_id,
|
| const gfx::Rect& initial_pos) {
|
| - if (!is_swapped_out_)
|
| + if (IsRVHStateActive(rvh_state_))
|
| delegate_->ShowCreatedWidget(route_id, initial_pos);
|
| Send(new ViewMsg_Move_ACK(route_id));
|
| }
|
|
|
| void RenderViewHostImpl::OnShowFullscreenWidget(int route_id) {
|
| - if (!is_swapped_out_)
|
| + if (IsRVHStateActive(rvh_state_))
|
| delegate_->ShowCreatedFullscreenWidget(route_id);
|
| Send(new ViewMsg_Move_ACK(route_id));
|
| }
|
| @@ -1470,7 +1489,7 @@ void RenderViewHostImpl::OnNavigate(const IPC::Message& msg) {
|
| // unload request. It will either respond to the unload request soon or our
|
| // timer will expire. Either way, we should ignore this message, because we
|
| // have already committed to closing this renderer.
|
| - if (is_waiting_for_unload_ack_)
|
| + if (IsWaitingForUnloadACK())
|
| return;
|
|
|
| // Cache the main frame id, so we can use it for creating the frame tree
|
| @@ -1557,7 +1576,7 @@ void RenderViewHostImpl::OnUpdateEncoding(const std::string& encoding_name) {
|
| }
|
|
|
| void RenderViewHostImpl::OnUpdateTargetURL(int32 page_id, const GURL& url) {
|
| - if (!is_swapped_out_)
|
| + if (IsRVHStateActive(rvh_state_))
|
| delegate_->UpdateTargetURL(page_id, url);
|
|
|
| // Send a notification back to the renderer that we are ready to
|
| @@ -1578,7 +1597,7 @@ void RenderViewHostImpl::OnClose() {
|
| }
|
|
|
| void RenderViewHostImpl::OnRequestMove(const gfx::Rect& pos) {
|
| - if (!is_swapped_out_)
|
| + if (IsRVHStateActive(rvh_state_))
|
| delegate_->RequestMove(pos);
|
| Send(new ViewMsg_Move_ACK(GetRoutingID()));
|
| }
|
| @@ -1817,7 +1836,7 @@ void RenderViewHostImpl::OnShouldCloseACK(
|
| // If this renderer navigated while the beforeunload request was in flight, we
|
| // may have cleared this state in OnNavigate, in which case we can ignore
|
| // this message.
|
| - if (!is_waiting_for_beforeunload_ack_ || is_swapped_out_)
|
| + if (!is_waiting_for_beforeunload_ack_ || rvh_state_ != STATE_DEFAULT)
|
| return;
|
|
|
| is_waiting_for_beforeunload_ack_ = false;
|
| @@ -1860,7 +1879,7 @@ void RenderViewHostImpl::OnClosePageACK() {
|
|
|
| void RenderViewHostImpl::NotifyRendererUnresponsive() {
|
| delegate_->RendererUnresponsive(
|
| - this, is_waiting_for_beforeunload_ack_, is_waiting_for_unload_ack_);
|
| + this, is_waiting_for_beforeunload_ack_, IsWaitingForUnloadACK());
|
| }
|
|
|
| void RenderViewHostImpl::NotifyRendererResponsive() {
|
| @@ -1965,6 +1984,13 @@ void RenderViewHostImpl::ToggleSpeechInput() {
|
| Send(new InputTagSpeechMsg_ToggleSpeechInput(GetRoutingID()));
|
| }
|
|
|
| +bool RenderViewHostImpl::IsWaitingForUnloadACK() const {
|
| + return rvh_state_ == STATE_WAITING_FOR_UNLOAD_ACK ||
|
| + rvh_state_ == STATE_WAITING_FOR_CLOSE ||
|
| + rvh_state_ == STATE_PENDING_SHUTDOWN ||
|
| + rvh_state_ == STATE_PENDING_SWAP_OUT;
|
| +}
|
| +
|
| bool RenderViewHostImpl::CanCommitURL(const GURL& url) {
|
| // TODO(creis): We should also check for WebUI pages here. Also, when the
|
| // out-of-process iframes implementation is ready, we should check for
|
| @@ -1986,7 +2012,7 @@ WebPreferences RenderViewHostImpl::GetWebkitPreferences() {
|
|
|
| void RenderViewHostImpl::DisownOpener() {
|
| // This should only be called when swapped out.
|
| - DCHECK(is_swapped_out_);
|
| + DCHECK(IsSwappedOut());
|
|
|
| Send(new ViewMsg_DisownOpener(GetRoutingID()));
|
| }
|
| @@ -2067,7 +2093,7 @@ void RenderViewHostImpl::NotifyMoveOrResizeStarted() {
|
|
|
| void RenderViewHostImpl::OnAccessibilityEvents(
|
| const std::vector<AccessibilityHostMsg_EventParams>& params) {
|
| - if (view_ && !is_swapped_out_) {
|
| + if (view_ && IsRVHStateActive(rvh_state_)) {
|
| view_->CreateBrowserAccessibilityManagerIfNeeded();
|
| BrowserAccessibilityManager* manager =
|
| view_->GetBrowserAccessibilityManager();
|
| @@ -2099,7 +2125,7 @@ void RenderViewHostImpl::OnAccessibilityEvents(
|
|
|
| void RenderViewHostImpl::OnAccessibilityLocationChanges(
|
| const std::vector<AccessibilityHostMsg_LocationChangeParams>& params) {
|
| - if (view_ && !is_swapped_out_) {
|
| + if (view_ && IsRVHStateActive(rvh_state_)) {
|
| view_->CreateBrowserAccessibilityManagerIfNeeded();
|
| BrowserAccessibilityManager* manager =
|
| view_->GetBrowserAccessibilityManager();
|
| @@ -2204,22 +2230,25 @@ void RenderViewHostImpl::OnShowPopup(
|
| }
|
| #endif
|
|
|
| -void RenderViewHostImpl::SetSwappedOut(bool is_swapped_out) {
|
| +void RenderViewHostImpl::SetState(RenderViewHostImplState rvh_state) {
|
| // We update the number of RenderViews in a SiteInstance when the
|
| - // swapped out status of this RenderView gets flipped.
|
| - if (is_swapped_out_ && !is_swapped_out)
|
| + // swapped out status of this RenderView gets flipped to/from live.
|
| + if (!IsRVHStateActive(rvh_state_) && IsRVHStateActive(rvh_state))
|
| instance_->increment_active_view_count();
|
| - else if (!is_swapped_out_ && is_swapped_out)
|
| + else if (IsRVHStateActive(rvh_state_) && !IsRVHStateActive(rvh_state))
|
| instance_->decrement_active_view_count();
|
|
|
| - is_swapped_out_ = is_swapped_out;
|
| + // Whenever we change the RVH state to and from live or swapped out state, we
|
| + // should not be waiting for beforeunload or unload acks. We clear them here
|
| + // to be safe, since they can cause navigations to be ignored in OnNavigate.
|
| + if (rvh_state == STATE_DEFAULT ||
|
| + rvh_state == STATE_SWAPPED_OUT ||
|
| + rvh_state_ == STATE_DEFAULT ||
|
| + rvh_state_ == STATE_SWAPPED_OUT) {
|
| + is_waiting_for_beforeunload_ack_ = false;
|
| + }
|
| + rvh_state_ = rvh_state;
|
|
|
| - // Whenever we change swap out state, we should not be waiting for
|
| - // beforeunload or unload acks. We clear them here to be safe, since they
|
| - // can cause navigations to be ignored in OnNavigate.
|
| - is_waiting_for_beforeunload_ack_ = false;
|
| - is_waiting_for_unload_ack_ = false;
|
| - has_timed_out_on_unload_ = false;
|
| }
|
|
|
| bool RenderViewHostImpl::CanAccessFilesOfPageState(
|
|
|