Index: content/browser/frame_host/render_frame_host_impl.cc |
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc |
index 585fb225282f1448ba825bd80d6f1509fe1bc9d8..02bcbbb6e7032f6e10a9a7f5f316244ffa1d8cc1 100644 |
--- a/content/browser/frame_host/render_frame_host_impl.cc |
+++ b/content/browser/frame_host/render_frame_host_impl.cc |
@@ -149,6 +149,12 @@ base::i18n::TextDirection WebTextDirectionToChromeTextDirection( |
} // namespace |
+// static |
+bool RenderFrameHostImpl::IsRFHStateActive(RenderFrameHostImplState rfh_state) { |
+ return rfh_state == STATE_DEFAULT; |
+} |
+ |
+// static |
RenderFrameHost* RenderFrameHost::FromID(int render_process_id, |
int render_frame_id) { |
return RenderFrameHostImpl::FromID(render_process_id, render_frame_id); |
@@ -177,9 +183,10 @@ RenderFrameHostImpl::RenderFrameHostImpl(RenderViewHostImpl* render_view_host, |
frame_tree_(frame_tree), |
frame_tree_node_(frame_tree_node), |
routing_id_(routing_id), |
- is_swapped_out_(is_swapped_out), |
render_frame_created_(false), |
navigations_suspended_(false), |
+ is_waiting_for_beforeunload_ack_(false), |
+ unload_ack_is_for_cross_site_transition_(false), |
weak_ptr_factory_(this) { |
frame_tree_->RegisterRenderFrameHost(this); |
GetProcess()->AddRoute(routing_id_, this); |
@@ -187,6 +194,13 @@ RenderFrameHostImpl::RenderFrameHostImpl(RenderViewHostImpl* render_view_host, |
RenderFrameHostID(GetProcess()->GetID(), routing_id_), |
this)); |
+ if (is_swapped_out) { |
+ rfh_state_ = STATE_SWAPPED_OUT; |
+ } else { |
+ rfh_state_ = STATE_DEFAULT; |
+ GetSiteInstance()->increment_active_frame_count(); |
+ } |
+ |
if (GetProcess()->GetServiceRegistry()) { |
RenderFrameSetupPtr setup; |
GetProcess()->GetServiceRegistry()->ConnectToRemoteService(&setup); |
@@ -196,6 +210,9 @@ RenderFrameHostImpl::RenderFrameHostImpl(RenderViewHostImpl* render_view_host, |
service_registry_.BindRemoteServiceProvider( |
service_provider.PassMessagePipe()); |
} |
+ |
+ swapout_event_monitor_timeout_.reset(new TimeoutMonitor(base::Bind( |
+ &RenderFrameHostImpl::OnSwappedOut, weak_ptr_factory_.GetWeakPtr()))); |
} |
RenderFrameHostImpl::~RenderFrameHostImpl() { |
@@ -208,6 +225,11 @@ RenderFrameHostImpl::~RenderFrameHostImpl() { |
FrameAccessibility::GetInstance()->OnRenderFrameHostDestroyed(this); |
+ // If this was swapped out, it already decremented the active frame count of |
+ // the SiteInstance it belongs to. |
+ if (IsRFHStateActive(rfh_state_)) |
+ GetSiteInstance()->decrement_active_frame_count(); |
+ |
// Notify the FrameTree that this RFH is going away, allowing it to shut down |
// the corresponding RenderViewHost if it is no longer needed. |
frame_tree_->UnregisterRenderFrameHost(this); |
@@ -217,7 +239,7 @@ int RenderFrameHostImpl::GetRoutingID() { |
return routing_id_; |
} |
-SiteInstance* RenderFrameHostImpl::GetSiteInstance() { |
+SiteInstanceImpl* RenderFrameHostImpl::GetSiteInstance() { |
return render_view_host_->GetSiteInstance(); |
} |
@@ -300,7 +322,7 @@ bool RenderFrameHostImpl::Send(IPC::Message* message) { |
// Route IPCs through the RenderFrameProxyHost when in swapped out state. |
// Note: For subframes in --site-per-process mode, we don't use swapped out |
// RenderFrameHosts. |
- if (frame_tree_node_->IsMainFrame() && render_view_host_->IsSwappedOut()) { |
+ if (frame_tree_node_->IsMainFrame() && is_swapped_out()) { |
DCHECK(render_frame_proxy_host_); |
return render_frame_proxy_host_->Send(message); |
} |
@@ -309,12 +331,9 @@ bool RenderFrameHostImpl::Send(IPC::Message* message) { |
} |
bool RenderFrameHostImpl::OnMessageReceived(const IPC::Message &msg) { |
- // Filter out most IPC messages if this renderer is swapped out. |
+ // Filter out most IPC messages if this frame is swapped out. |
// We still want to handle certain ACKs to keep our state consistent. |
- // TODO(nasko): Only check RenderViewHost state, as this object's own state |
- // isn't yet properly updated. Transition this check once the swapped out |
- // state is correct in RenderFrameHost itself. |
- if (render_view_host_->IsSwappedOut()) { |
+ if (is_swapped_out()) { |
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 |
@@ -669,8 +688,8 @@ void RenderFrameHostImpl::OnDidCommitProvisionalLoad(const IPC::Message& msg) { |
// We do not want to cancel the pending navigation in this case, since the |
// old page will soon be stopped. Instead, treat this as a beforeunload ack |
// to allow the pending navigation to continue. |
- if (render_view_host_->is_waiting_for_beforeunload_ack_ && |
- render_view_host_->unload_ack_is_for_cross_site_transition_ && |
+ if (is_waiting_for_beforeunload_ack_ && |
+ unload_ack_is_for_cross_site_transition_ && |
ui::PageTransitionIsMainFrame(validated_params.transition)) { |
OnBeforeUnloadACK(true, send_before_unload_start_time_, |
base::TimeTicks::Now()); |
@@ -682,7 +701,7 @@ void RenderFrameHostImpl::OnDidCommitProvisionalLoad(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 (render_view_host_->IsWaitingForUnloadACK()) |
+ if (IsWaitingForUnloadACK()) |
return; |
RenderProcessHost* process = GetProcess(); |
@@ -764,20 +783,14 @@ void RenderFrameHostImpl::SwapOut(RenderFrameProxyHost* proxy) { |
// to be fixed when RenderViewHostImpl::OnSwapOut moves to RenderFrameHost. |
TRACE_EVENT_ASYNC_BEGIN0("navigation", "RenderFrameHostImpl::SwapOut", this); |
- // TODO(creis): Move swapped out state to RFH. Until then, only update it |
- // when swapping out the main frame. |
- if (!GetParent()) { |
- // If this RenderViewHost is not in the default state, it must have already |
- // gone through this, therefore just return. |
- if (render_view_host_->rvh_state_ != RenderViewHostImpl::STATE_DEFAULT) |
- return; |
+ // If this RenderFrameHost is not in the default state, it must have already |
+ // gone through this, therefore just return. |
+ if (rfh_state_ != RenderFrameHostImpl::STATE_DEFAULT) |
+ return; |
- render_view_host_->SetState( |
- RenderViewHostImpl::STATE_PENDING_SWAP_OUT); |
- render_view_host_->unload_event_monitor_timeout_->Start( |
- base::TimeDelta::FromMilliseconds( |
- RenderViewHostImpl::kUnloadTimeoutMS)); |
- } |
+ SetState(RenderFrameHostImpl::STATE_PENDING_SWAP_OUT); |
+ swapout_event_monitor_timeout_->Start( |
+ base::TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS)); |
set_render_frame_proxy_host(proxy); |
@@ -786,8 +799,6 @@ void RenderFrameHostImpl::SwapOut(RenderFrameProxyHost* proxy) { |
if (!GetParent()) |
delegate_->SwappedOut(this); |
- else |
- set_swapped_out(true); |
} |
void RenderFrameHostImpl::OnBeforeUnloadACK( |
@@ -796,12 +807,12 @@ void RenderFrameHostImpl::OnBeforeUnloadACK( |
const base::TimeTicks& renderer_before_unload_end_time) { |
TRACE_EVENT_ASYNC_END0( |
"navigation", "RenderFrameHostImpl::BeforeUnload", this); |
- // TODO(creis): Support properly beforeunload on subframes. For now just |
- // pretend that the handler ran and allowed the navigation to proceed. |
+ // TODO(creis): Support beforeunload on subframes. For now just pretend that |
+ // the handler ran and allowed the navigation to proceed. |
if (GetParent()) { |
- render_view_host_->is_waiting_for_beforeunload_ack_ = false; |
+ is_waiting_for_beforeunload_ack_ = false; |
frame_tree_node_->render_manager()->OnBeforeUnloadACK( |
- render_view_host_->unload_ack_is_for_cross_site_transition_, proceed, |
+ unload_ack_is_for_cross_site_transition_, proceed, |
renderer_before_unload_end_time); |
return; |
} |
@@ -816,11 +827,11 @@ void RenderFrameHostImpl::OnBeforeUnloadACK( |
// happen when pending cross-site navigation is canceled by a second one just |
// before OnDidCommitProvisionalLoad while current RVH is waiting for commit |
// but second navigation is started from the beginning. |
- if (!render_view_host_->is_waiting_for_beforeunload_ack_) { |
+ if (!is_waiting_for_beforeunload_ack_) { |
return; |
} |
- render_view_host_->is_waiting_for_beforeunload_ack_ = false; |
+ is_waiting_for_beforeunload_ack_ = false; |
base::TimeTicks before_unload_end_time; |
if (!send_before_unload_start_time_.is_null() && |
@@ -858,7 +869,7 @@ void RenderFrameHostImpl::OnBeforeUnloadACK( |
is_skew_additive); |
} |
frame_tree_node_->render_manager()->OnBeforeUnloadACK( |
- render_view_host_->unload_ack_is_for_cross_site_transition_, proceed, |
+ unload_ack_is_for_cross_site_transition_, proceed, |
before_unload_end_time); |
// If canceled, notify the delegate to cancel its pending navigation entry. |
@@ -866,15 +877,36 @@ void RenderFrameHostImpl::OnBeforeUnloadACK( |
render_view_host_->GetDelegate()->DidCancelLoading(); |
} |
+bool RenderFrameHostImpl::IsWaitingForUnloadACK() const { |
+ return render_view_host_->is_waiting_for_close_ack_ || |
+ rfh_state_ == STATE_PENDING_SHUTDOWN || |
+ rfh_state_ == STATE_PENDING_SWAP_OUT; |
+} |
+ |
void RenderFrameHostImpl::OnSwapOutACK() { |
- OnSwappedOut(false); |
- TRACE_EVENT_ASYNC_END0("navigation", "RenderFrameHostImpl::SwapOut", this); |
+ OnSwappedOut(); |
} |
-void RenderFrameHostImpl::OnSwappedOut(bool timed_out) { |
- // For now, we only need to update the RVH state machine for top-level swaps. |
- if (!GetParent()) |
- render_view_host_->OnSwappedOut(timed_out); |
+void RenderFrameHostImpl::OnSwappedOut() { |
+ // Ignore spurious swap out ack. |
+ if (rfh_state_ != STATE_PENDING_SWAP_OUT && |
+ rfh_state_ != STATE_PENDING_SHUTDOWN) |
+ return; |
+ |
+ TRACE_EVENT_ASYNC_END0("navigation", "RenderFrameHostImpl::SwapOut", this); |
+ swapout_event_monitor_timeout_->Stop(); |
+ |
+ switch (rfh_state_) { |
+ 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 RenderFrameHostImpl::OnContextMenu(const ContextMenuParams& params) { |
@@ -931,7 +963,7 @@ void RenderFrameHostImpl::OnRunBeforeUnloadConfirm( |
const base::string16& message, |
bool is_reload, |
IPC::Message* reply_msg) { |
- // While a JS before unload dialog is showing, tabs in the same process |
+ // While a JS beforeunload dialog is showing, tabs in the same process |
// shouldn't process input events. |
GetProcess()->SetIgnoreInputEvents(true); |
render_view_host_->StopHangMonitorTimeout(); |
@@ -1034,7 +1066,7 @@ void RenderFrameHostImpl::OnAccessibilityEvents( |
AccessibilityMode accessibility_mode = delegate_->GetAccessibilityMode(); |
if ((accessibility_mode != AccessibilityModeOff) && view && |
- RenderViewHostImpl::IsRVHStateActive(render_view_host_->rvh_state())) { |
+ RenderFrameHostImpl::IsRFHStateActive(rfh_state())) { |
if (accessibility_mode & AccessibilityModeFlagPlatform) { |
GetOrCreateBrowserAccessibilityManager(); |
if (browser_accessibility_manager_) |
@@ -1105,8 +1137,7 @@ void RenderFrameHostImpl::OnAccessibilityLocationChanges( |
const std::vector<AccessibilityHostMsg_LocationChangeParams>& params) { |
RenderWidgetHostViewBase* view = static_cast<RenderWidgetHostViewBase*>( |
render_view_host_->GetView()); |
- if (view && |
- RenderViewHostImpl::IsRVHStateActive(render_view_host_->rvh_state())) { |
+ if (view && RenderFrameHostImpl::IsRFHStateActive(rfh_state())) { |
AccessibilityMode accessibility_mode = delegate_->GetAccessibilityMode(); |
if (accessibility_mode & AccessibilityModeFlagPlatform) { |
if (!browser_accessibility_manager_) { |
@@ -1145,8 +1176,39 @@ void RenderFrameHostImpl::OnHidePopup() { |
} |
#endif |
+void RenderFrameHostImpl::SetState(RenderFrameHostImplState rfh_state) { |
+ // We update the number of RenderFrameHosts in a SiteInstance when the swapped |
+ // out status of a RenderFrameHost gets flipped to/from active. |
+ if (!IsRFHStateActive(rfh_state_) && IsRFHStateActive(rfh_state)) |
+ GetSiteInstance()->increment_active_frame_count(); |
+ else if (IsRFHStateActive(rfh_state_) && !IsRFHStateActive(rfh_state)) |
+ GetSiteInstance()->decrement_active_frame_count(); |
+ |
+ // The active and swapped out state of the RVH is determined by its main |
+ // frame, since subframes should have their own widgets. |
+ if (frame_tree_node_->IsMainFrame()) { |
+ render_view_host_->set_is_active(IsRFHStateActive(rfh_state)); |
+ render_view_host_->set_is_swapped_out(rfh_state == STATE_SWAPPED_OUT); |
+ } |
+ |
+ // Whenever we change the RFH state to and from active or swapped out state, |
+ // we should not be waiting for beforeunload or close acks. We clear them |
+ // here to be safe, since they can cause navigations to be ignored in |
+ // OnDidCommitProvisionalLoad. |
+ // TODO(creis): Move is_waiting_for_beforeunload_ack_ into the state machine. |
+ if (rfh_state == STATE_DEFAULT || |
+ rfh_state == STATE_SWAPPED_OUT || |
+ rfh_state_ == STATE_DEFAULT || |
+ rfh_state_ == STATE_SWAPPED_OUT) { |
+ is_waiting_for_beforeunload_ack_ = false; |
+ render_view_host_->is_waiting_for_close_ack_ = false; |
+ } |
+ rfh_state_ = rfh_state; |
+} |
+ |
void RenderFrameHostImpl::SetPendingShutdown(const base::Closure& on_swap_out) { |
- render_view_host_->SetPendingShutdown(on_swap_out); |
+ pending_shutdown_on_swap_out_ = on_swap_out; |
+ SetState(STATE_PENDING_SHUTDOWN); |
} |
bool RenderFrameHostImpl::CanCommitURL(const GURL& url) { |
@@ -1185,8 +1247,8 @@ void RenderFrameHostImpl::Navigate(const FrameMsg_Navigate_Params& params) { |
suspended_nav_params_.reset(new FrameMsg_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. |
- render_view_host_->SetState(RenderViewHostImpl::STATE_DEFAULT); |
+ // completing a RFH swap or unload handler. |
+ SetState(RenderFrameHostImpl::STATE_DEFAULT); |
Send(new FrameMsg_Navigate(routing_id_, params)); |
} |
@@ -1229,9 +1291,8 @@ void RenderFrameHostImpl::DispatchBeforeUnload(bool for_cross_site_transition) { |
// TODO(creis): Support subframes. |
if (GetParent() || !IsRenderFrameLive()) { |
// We don't have a live renderer, so just skip running beforeunload. |
- render_view_host_->is_waiting_for_beforeunload_ack_ = true; |
- render_view_host_->unload_ack_is_for_cross_site_transition_ = |
- for_cross_site_transition; |
+ is_waiting_for_beforeunload_ack_ = true; |
+ unload_ack_is_for_cross_site_transition_ = for_cross_site_transition; |
base::TimeTicks now = base::TimeTicks::Now(); |
OnBeforeUnloadACK(true, now, now); |
return; |
@@ -1240,22 +1301,20 @@ void RenderFrameHostImpl::DispatchBeforeUnload(bool for_cross_site_transition) { |
// This may be called more than once (if the user clicks the tab close button |
// several times, or if she clicks the tab close button then the browser close |
// button), and we only send the message once. |
- if (render_view_host_->is_waiting_for_beforeunload_ack_) { |
+ if (is_waiting_for_beforeunload_ack_) { |
// Some of our close messages could be for the tab, others for cross-site |
// transitions. We always want to think it's for closing the tab if any |
// of the messages were, since otherwise it might be impossible to close |
// (if there was a cross-site "close" request pending when the user clicked |
// the close button). We want to keep the "for cross site" flag only if |
// both the old and the new ones are also for cross site. |
- render_view_host_->unload_ack_is_for_cross_site_transition_ = |
- render_view_host_->unload_ack_is_for_cross_site_transition_ && |
- for_cross_site_transition; |
+ unload_ack_is_for_cross_site_transition_ = |
+ unload_ack_is_for_cross_site_transition_ && for_cross_site_transition; |
} else { |
// Start the hang monitor in case the renderer hangs in the beforeunload |
// handler. |
- render_view_host_->is_waiting_for_beforeunload_ack_ = true; |
- render_view_host_->unload_ack_is_for_cross_site_transition_ = |
- for_cross_site_transition; |
+ is_waiting_for_beforeunload_ack_ = true; |
+ unload_ack_is_for_cross_site_transition_ = for_cross_site_transition; |
// Increment the in-flight event count, to ensure that input events won't |
// cancel the timeout timer. |
render_view_host_->increment_in_flight_event_count(); |
@@ -1281,8 +1340,7 @@ void RenderFrameHostImpl::JavaScriptDialogClosed( |
const base::string16& user_input, |
bool dialog_was_suppressed) { |
GetProcess()->SetIgnoreInputEvents(false); |
- bool is_waiting = render_view_host_->is_waiting_for_beforeunload_ack() || |
- render_view_host_->IsWaitingForUnloadACK(); |
+ 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 |
@@ -1305,10 +1363,7 @@ void RenderFrameHostImpl::JavaScriptDialogClosed( |
// This must be done after sending the reply since RenderView can't close |
// correctly while waiting for a response. |
if (is_waiting && dialog_was_suppressed) |
- render_view_host_->delegate_->RendererUnresponsive( |
- render_view_host_, |
- render_view_host_->is_waiting_for_beforeunload_ack(), |
- render_view_host_->IsWaitingForUnloadACK()); |
+ render_view_host_->delegate_->RendererUnresponsive(render_view_host_); |
} |
void RenderFrameHostImpl::NotificationClosed(int notification_id) { |
@@ -1451,7 +1506,7 @@ void RenderFrameHostImpl::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. |
- render_view_host_->SetState(RenderViewHostImpl::STATE_DEFAULT); |
+ SetState(RenderFrameHostImpl::STATE_DEFAULT); |
DCHECK(!proceed_time.is_null()); |
suspended_nav_params_->browser_navigation_start = proceed_time; |