| Index: content/browser/renderer_host/render_view_host.cc
|
| diff --git a/content/browser/renderer_host/render_view_host.cc b/content/browser/renderer_host/render_view_host.cc
|
| index 47e54576fa06c3393618d8191ace65357d9fc77a..cbbf4f3505944c321dad9622a5966e07d77b8e03 100644
|
| --- a/content/browser/renderer_host/render_view_host.cc
|
| +++ b/content/browser/renderer_host/render_view_host.cc
|
| @@ -92,9 +92,10 @@ RenderViewHost::RenderViewHost(SiteInstance* instance,
|
| delegate_(delegate),
|
| waiting_for_drag_context_response_(false),
|
| enabled_bindings_(0),
|
| - pending_request_id_(0),
|
| + pending_request_id_(-1),
|
| navigations_suspended_(false),
|
| suspended_nav_message_(NULL),
|
| + is_swapped_out_(false),
|
| run_modal_reply_msg_(NULL),
|
| is_waiting_for_beforeunload_ack_(false),
|
| is_waiting_for_unload_ack_(false),
|
| @@ -258,11 +259,20 @@ void RenderViewHost::SetNavigationsSuspended(bool suspend) {
|
| navigations_suspended_ = suspend;
|
| if (!suspend && suspended_nav_message_.get()) {
|
| // There's a navigation message waiting to be sent. Now that we're not
|
| - // suspended anymore, resume navigation by sending it.
|
| + // suspended anymore, resume navigation by sending it. If we were swapped
|
| + // out, we should also stop filtering out the IPC messages now.
|
| + is_swapped_out_ = false;
|
| Send(suspended_nav_message_.release());
|
| }
|
| }
|
|
|
| +void RenderViewHost::CancelSuspendedNavigations() {
|
| + // Clear any state if a pending navigation is canceled or pre-empted.
|
| + if (suspended_nav_message_.get())
|
| + suspended_nav_message_.reset();
|
| + navigations_suspended_ = false;
|
| +}
|
| +
|
| void RenderViewHost::FirePageBeforeUnload(bool for_cross_site_transition) {
|
| if (!IsRenderViewLive()) {
|
| // This RenderViewHost doesn't have a live renderer, so just skip running
|
| @@ -295,45 +305,66 @@ void RenderViewHost::FirePageBeforeUnload(bool for_cross_site_transition) {
|
| }
|
| }
|
|
|
| -void RenderViewHost::ClosePage(bool for_cross_site_transition,
|
| - int new_render_process_host_id,
|
| - int new_request_id) {
|
| - // This will be set back to false in OnClosePageACK, just before we close the
|
| - // tab or replace it with a pending RVH. There are some cases (such as 204
|
| - // errors) where we'll continue to show this RVH.
|
| +void RenderViewHost::SwapOut(int new_render_process_host_id,
|
| + int new_request_id) {
|
| + // Start filtering IPC messages to avoid confusing the delegate. This will
|
| + // prevent any dialogs from appearing during unload handlers, but we've
|
| + // already decided to silence them in crbug.com/68780. We will set it back
|
| + // to false in SetNavigationsSuspended if we swap back in.
|
| + is_swapped_out_ = true;
|
| +
|
| + // 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.
|
| StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
|
|
|
| - ViewMsg_ClosePage_Params params;
|
| + ViewMsg_SwapOut_Params params;
|
| params.closing_process_id = process()->id();
|
| params.closing_route_id = routing_id();
|
| - params.for_cross_site_transition = for_cross_site_transition;
|
| params.new_render_process_host_id = new_render_process_host_id;
|
| params.new_request_id = new_request_id;
|
| if (IsRenderViewLive()) {
|
| - NotificationService::current()->Notify(
|
| - NotificationType::RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW,
|
| - Source<RenderViewHost>(this),
|
| - NotificationService::NoDetails());
|
| -
|
| - Send(new ViewMsg_ClosePage(routing_id(), params));
|
| + Send(new ViewMsg_SwapOut(routing_id(), params));
|
| } else {
|
| - // This RenderViewHost doesn't have a live renderer, so just skip closing
|
| - // the page. We must notify the ResourceDispatcherHost on the IO thread,
|
| + // This RenderViewHost doesn't have a live renderer, so just skip the unload
|
| + // event. We must notify the ResourceDispatcherHost on the IO thread,
|
| // which we will do through the RenderProcessHost's widget helper.
|
| - process()->CrossSiteClosePageACK(params);
|
| + process()->CrossSiteSwapOutACK(params);
|
| }
|
| }
|
|
|
| -void RenderViewHost::OnClosePageACK(bool for_cross_site_transition) {
|
| +void RenderViewHost::OnSwapOutACK() {
|
| + // Stop the hang monitor now that the unload handler has finished.
|
| StopHangMonitorTimeout();
|
| is_waiting_for_unload_ack_ = false;
|
| +}
|
| +
|
| +void RenderViewHost::WasSwappedOut() {
|
| + // Don't bother reporting hung state anymore.
|
| + StopHangMonitorTimeout();
|
| +
|
| + // Inform the renderer that it can exit if no one else is using it.
|
| + Send(new ViewMsg_WasSwappedOut(routing_id()));
|
| +}
|
| +
|
| +void RenderViewHost::ClosePage() {
|
| + // Start the hang monitor in case the renderer hangs in the unload handler.
|
| + is_waiting_for_unload_ack_ = true;
|
| + StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
|
|
|
| - // If this ClosePageACK is not for a cross-site transition, then it is for an
|
| - // attempt to close the tab. We have now finished the unload handler and can
|
| - // proceed with closing the tab.
|
| - if (!for_cross_site_transition) {
|
| + if (IsRenderViewLive()) {
|
| + // TODO(creis): Should this be moved to Shutdown? It may not be called for
|
| + // RenderViewHosts that have been swapped out.
|
| + NotificationService::current()->Notify(
|
| + NotificationType::RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW,
|
| + Source<RenderViewHost>(this),
|
| + NotificationService::NoDetails());
|
| +
|
| + Send(new ViewMsg_ClosePage(routing_id()));
|
| + } else {
|
| + // This RenderViewHost doesn't have a live renderer, so just skip the unload
|
| + // event and close the page.
|
| ClosePageIgnoringUnloadEvents();
|
| }
|
| }
|
| @@ -671,6 +702,12 @@ bool RenderViewHost::OnMessageReceived(const IPC::Message& msg) {
|
| if (!BrowserMessageFilter::CheckCanDispatchOnUI(msg, this))
|
| return true;
|
|
|
| + // Filter out most IPC messages if this renderer is swapped out.
|
| + // We still want to certain ACKs to keep our state consistent.
|
| + if (is_swapped_out_)
|
| + if (!CanHandleWhileSwappedOut(msg))
|
| + return true;
|
| +
|
| {
|
| // delegate_->OnMessageReceived can end up deleting |this|, in which case
|
| // the destructor for ObserverListBase::Iterator would access the deleted
|
| @@ -728,6 +765,7 @@ bool RenderViewHost::OnMessageReceived(const IPC::Message& msg) {
|
| IPC_MESSAGE_HANDLER(ViewHostMsg_TakeFocus, OnTakeFocus)
|
| IPC_MESSAGE_HANDLER(ViewHostMsg_AddMessageToConsole, OnAddMessageToConsole)
|
| IPC_MESSAGE_HANDLER(ViewHostMsg_ShouldClose_ACK, OnMsgShouldCloseACK)
|
| + IPC_MESSAGE_HANDLER(ViewHostMsg_ClosePage_ACK, OnMsgClosePageACK)
|
| IPC_MESSAGE_HANDLER(ViewHostMsg_SelectionChanged, OnMsgSelectionChanged)
|
| IPC_MESSAGE_HANDLER(ViewHostMsg_AccessibilityNotifications,
|
| OnAccessibilityNotifications)
|
| @@ -754,6 +792,56 @@ bool RenderViewHost::OnMessageReceived(const IPC::Message& msg) {
|
| return handled;
|
| }
|
|
|
| +bool RenderViewHost::CanHandleWhileSwappedOut(const IPC::Message& msg) {
|
| + // We drop most messages that arrive from a swapped out renderer. However,
|
| + // some are important (e.g., ACKs) for keeping the browser and renderer state
|
| + // consistent in case we later return to the renderer.
|
| + switch (msg.type()) {
|
| + // Sends an ACK.
|
| + case ViewHostMsg_ShowView::ID:
|
| + // Sends an ACK.
|
| + case ViewHostMsg_ShowWidget::ID:
|
| + // Sends an ACK.
|
| + case ViewHostMsg_ShowFullscreenWidget::ID:
|
| + // Updates browser state.
|
| + case ViewHostMsg_RenderViewReady::ID:
|
| + // Updates browser state.
|
| + case ViewHostMsg_RenderViewGone::ID:
|
| + // Updates the previous navigation entry.
|
| + case ViewHostMsg_UpdateState::ID:
|
| + // Sends an ACK.
|
| + case ViewHostMsg_UpdateTargetURL::ID:
|
| + // Updates browser state.
|
| + case ViewHostMsg_Snapshot::ID:
|
| + // We allow closing even if we are in the process of swapping out.
|
| + case ViewHostMsg_Close::ID:
|
| + // Sends an ACK.
|
| + case ViewHostMsg_RequestMove::ID:
|
| + // Updates browser state, and important for tests.
|
| + case ViewHostMsg_DomOperationResponse::ID:
|
| + // Suppresses dialog and sends an ACK.
|
| + case ViewHostMsg_RunJavaScriptMessage::ID:
|
| + // Suppresses dialog and sends an ACK.
|
| + case ViewHostMsg_RunBeforeUnloadConfirm::ID:
|
| + // Stops hang monitor.
|
| + case ViewHostMsg_ShouldClose_ACK::ID:
|
| + // We allow closing even if we are in the process of swapping out.
|
| + case ViewHostMsg_ClosePage_ACK::ID:
|
| + // Sends an ACK.
|
| + case ViewHostMsg_AccessibilityNotifications::ID:
|
| + // Updates browser state.
|
| + case ViewHostMsg_PaintAtSize_ACK::ID:
|
| + // Updates browser state.
|
| + case ViewHostMsg_HandleInputEvent_ACK::ID:
|
| + // Updates browser state.
|
| + case ViewHostMsg_UpdateRect::ID:
|
| + return true;
|
| + default:
|
| + break;
|
| + }
|
| + return false;
|
| +}
|
| +
|
| void RenderViewHost::Shutdown() {
|
| // If we are being run modally (see RunModal), then we need to cleanup.
|
| if (run_modal_reply_msg_) {
|
| @@ -797,7 +885,8 @@ void RenderViewHost::OnMsgShowView(int route_id,
|
| bool user_gesture) {
|
| RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
|
| if (view) {
|
| - view->ShowCreatedWindow(route_id, disposition, initial_pos, user_gesture);
|
| + if (!is_swapped_out_)
|
| + view->ShowCreatedWindow(route_id, disposition, initial_pos, user_gesture);
|
| Send(new ViewMsg_Move_ACK(route_id));
|
| }
|
| }
|
| @@ -806,7 +895,8 @@ void RenderViewHost::OnMsgShowWidget(int route_id,
|
| const gfx::Rect& initial_pos) {
|
| RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
|
| if (view) {
|
| - view->ShowCreatedWidget(route_id, initial_pos);
|
| + if (!is_swapped_out_)
|
| + view->ShowCreatedWidget(route_id, initial_pos);
|
| Send(new ViewMsg_Move_ACK(route_id));
|
| }
|
| }
|
| @@ -814,7 +904,8 @@ void RenderViewHost::OnMsgShowWidget(int route_id,
|
| void RenderViewHost::OnMsgShowFullscreenWidget(int route_id) {
|
| RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
|
| if (view) {
|
| - view->ShowCreatedFullscreenWidget(route_id);
|
| + if (!is_swapped_out_)
|
| + view->ShowCreatedFullscreenWidget(route_id);
|
| Send(new ViewMsg_Move_ACK(route_id));
|
| }
|
| }
|
| @@ -936,7 +1027,8 @@ void RenderViewHost::OnMsgUpdateEncoding(const std::string& encoding_name) {
|
|
|
| void RenderViewHost::OnMsgUpdateTargetURL(int32 page_id,
|
| const GURL& url) {
|
| - delegate_->UpdateTargetURL(page_id, url);
|
| + if (!is_swapped_out_)
|
| + delegate_->UpdateTargetURL(page_id, url);
|
|
|
| // Send a notification back to the renderer that we are ready to
|
| // receive more target urls.
|
| @@ -955,7 +1047,8 @@ void RenderViewHost::OnMsgClose() {
|
| }
|
|
|
| void RenderViewHost::OnMsgRequestMove(const gfx::Rect& pos) {
|
| - delegate_->RequestMove(pos);
|
| + if (!is_swapped_out_)
|
| + delegate_->RequestMove(pos);
|
| Send(new ViewMsg_Move_ACK(routing_id()));
|
| }
|
|
|
| @@ -1077,8 +1170,8 @@ void RenderViewHost::OnMsgRunJavaScriptMessage(
|
| // process input events.
|
| process()->set_ignore_input_events(true);
|
| StopHangMonitorTimeout();
|
| - delegate_->RunJavaScriptMessage(message, default_prompt, frame_url, flags,
|
| - reply_msg,
|
| + delegate_->RunJavaScriptMessage(this, message, default_prompt, frame_url,
|
| + flags, reply_msg,
|
| &are_javascript_messages_suppressed_);
|
| }
|
|
|
| @@ -1089,7 +1182,7 @@ void RenderViewHost::OnMsgRunBeforeUnloadConfirm(const GURL& frame_url,
|
| // shouldn't process input events.
|
| process()->set_ignore_input_events(true);
|
| StopHangMonitorTimeout();
|
| - delegate_->RunBeforeUnloadConfirm(message, reply_msg);
|
| + delegate_->RunBeforeUnloadConfirm(this, message, reply_msg);
|
| }
|
|
|
| void RenderViewHost::MediaPlayerActionAt(const gfx::Point& location,
|
| @@ -1110,7 +1203,7 @@ void RenderViewHost::OnMsgStartDragging(
|
| const gfx::Point& image_offset) {
|
| RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
|
| if (view)
|
| - view->StartDragging(drop_data, drag_operations_mask, image, image_offset);
|
| + view->StartDragging(drop_data, drag_operations_mask, image, image_offset);
|
| }
|
|
|
| void RenderViewHost::OnUpdateDragCursor(WebDragOperation current_op) {
|
| @@ -1167,7 +1260,7 @@ void RenderViewHost::OnMsgShouldCloseACK(bool proceed) {
|
| // 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_)
|
| + if (!is_waiting_for_beforeunload_ack_ || is_swapped_out_)
|
| return;
|
|
|
| is_waiting_for_beforeunload_ack_ = false;
|
| @@ -1184,6 +1277,10 @@ void RenderViewHost::OnMsgShouldCloseACK(bool proceed) {
|
| delegate_->DidCancelLoading();
|
| }
|
|
|
| +void RenderViewHost::OnMsgClosePageACK() {
|
| + ClosePageIgnoringUnloadEvents();
|
| +}
|
| +
|
| void RenderViewHost::WindowMoveOrResizeStarted() {
|
| Send(new ViewMsg_MoveOrResizeStarted(routing_id()));
|
| }
|
| @@ -1326,7 +1423,7 @@ void RenderViewHost::FilterURL(ChildProcessSecurityPolicy* policy,
|
|
|
| void RenderViewHost::OnAccessibilityNotifications(
|
| const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params) {
|
| - if (view())
|
| + if (view() && !is_swapped_out_)
|
| view()->OnAccessibilityNotifications(params);
|
|
|
| if (!params.empty()) {
|
|
|