OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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/frame_host/render_frame_host_impl.h" | 5 #include "content/browser/frame_host/render_frame_host_impl.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
9 #include "base/containers/hash_tables.h" | 9 #include "base/containers/hash_tables.h" |
10 #include "base/lazy_instance.h" | 10 #include "base/lazy_instance.h" |
(...skipping 689 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
700 | 700 |
701 // If we're waiting for a cross-site beforeunload ack from this renderer and | 701 // If we're waiting for a cross-site beforeunload ack from this renderer and |
702 // we receive a Navigate message from the main frame, then the renderer was | 702 // we receive a Navigate message from the main frame, then the renderer was |
703 // navigating already and sent it before hearing the FrameMsg_Stop message. | 703 // navigating already and sent it before hearing the FrameMsg_Stop message. |
704 // We do not want to cancel the pending navigation in this case, since the | 704 // We do not want to cancel the pending navigation in this case, since the |
705 // old page will soon be stopped. Instead, treat this as a beforeunload ack | 705 // old page will soon be stopped. Instead, treat this as a beforeunload ack |
706 // to allow the pending navigation to continue. | 706 // to allow the pending navigation to continue. |
707 if (is_waiting_for_beforeunload_ack_ && | 707 if (is_waiting_for_beforeunload_ack_ && |
708 unload_ack_is_for_cross_site_transition_ && | 708 unload_ack_is_for_cross_site_transition_ && |
709 ui::PageTransitionIsMainFrame(validated_params.transition)) { | 709 ui::PageTransitionIsMainFrame(validated_params.transition)) { |
710 OnBeforeUnloadACK(true, send_before_unload_start_time_, | 710 base::TimeTicks approx_renderer_start_time = send_before_unload_start_time_; |
carlosk
2014/10/21 13:07:09
I'm making this copy because it was unsafe to pass
Charlie Reis
2014/10/21 18:21:48
"Could be reset anytime" doesn't seem relevant her
carlosk
2014/10/22 09:08:56
Yeah I agree with that: it isn't really required.
| |
711 base::TimeTicks::Now()); | 711 OnBeforeUnloadACK(true, approx_renderer_start_time, base::TimeTicks::Now()); |
712 return; | 712 return; |
713 } | 713 } |
714 | 714 |
715 // If we're waiting for an unload ack from this renderer and we receive a | 715 // If we're waiting for an unload ack from this renderer and we receive a |
716 // Navigate message, then the renderer was navigating before it received the | 716 // Navigate message, then the renderer was navigating before it received the |
717 // unload request. It will either respond to the unload request soon or our | 717 // unload request. It will either respond to the unload request soon or our |
718 // timer will expire. Either way, we should ignore this message, because we | 718 // timer will expire. Either way, we should ignore this message, because we |
719 // have already committed to closing this renderer. | 719 // have already committed to closing this renderer. |
720 if (IsWaitingForUnloadACK()) | 720 if (IsWaitingForUnloadACK()) |
721 return; | 721 return; |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
823 if (!GetParent()) | 823 if (!GetParent()) |
824 delegate_->SwappedOut(this); | 824 delegate_->SwappedOut(this); |
825 } | 825 } |
826 | 826 |
827 void RenderFrameHostImpl::OnBeforeUnloadACK( | 827 void RenderFrameHostImpl::OnBeforeUnloadACK( |
828 bool proceed, | 828 bool proceed, |
829 const base::TimeTicks& renderer_before_unload_start_time, | 829 const base::TimeTicks& renderer_before_unload_start_time, |
830 const base::TimeTicks& renderer_before_unload_end_time) { | 830 const base::TimeTicks& renderer_before_unload_end_time) { |
831 TRACE_EVENT_ASYNC_END0( | 831 TRACE_EVENT_ASYNC_END0( |
832 "navigation", "RenderFrameHostImpl::BeforeUnload", this); | 832 "navigation", "RenderFrameHostImpl::BeforeUnload", this); |
833 // TODO(creis): Support beforeunload on subframes. For now just pretend that | 833 DCHECK(!GetParent()); |
834 // the handler ran and allowed the navigation to proceed. | |
835 if (GetParent()) { | |
836 is_waiting_for_beforeunload_ack_ = false; | |
837 frame_tree_node_->render_manager()->OnBeforeUnloadACK( | |
838 unload_ack_is_for_cross_site_transition_, proceed, | |
839 renderer_before_unload_end_time); | |
840 return; | |
841 } | |
842 | |
843 render_view_host_->decrement_in_flight_event_count(); | 834 render_view_host_->decrement_in_flight_event_count(); |
844 render_view_host_->StopHangMonitorTimeout(); | 835 render_view_host_->StopHangMonitorTimeout(); |
845 // If this renderer navigated while the beforeunload request was in flight, we | 836 // If this renderer navigated while the beforeunload request was in flight, we |
846 // may have cleared this state in OnDidCommitProvisionalLoad, in which case we | 837 // may have cleared this state in OnDidCommitProvisionalLoad, in which case we |
847 // can ignore this message. | 838 // can ignore this message. |
848 // However renderer might also be swapped out but we still want to proceed | 839 // However renderer might also be swapped out but we still want to proceed |
849 // with navigation, otherwise it would block future navigations. This can | 840 // with navigation, otherwise it would block future navigations. This can |
850 // happen when pending cross-site navigation is canceled by a second one just | 841 // happen when pending cross-site navigation is canceled by a second one just |
851 // before OnDidCommitProvisionalLoad while current RVH is waiting for commit | 842 // before OnDidCommitProvisionalLoad while current RVH is waiting for commit |
852 // but second navigation is started from the beginning. | 843 // but second navigation is started from the beginning. |
853 if (!is_waiting_for_beforeunload_ack_) { | 844 if (!is_waiting_for_beforeunload_ack_) { |
854 return; | 845 return; |
855 } | 846 } |
847 DCHECK(!send_before_unload_start_time_.is_null()); | |
856 | 848 |
857 is_waiting_for_beforeunload_ack_ = false; | 849 // Sets a default value for before_unload_end_time so that the browser |
858 | 850 // survives a hacked renderer. |
859 base::TimeTicks before_unload_end_time; | 851 base::TimeTicks before_unload_end_time = renderer_before_unload_end_time; |
carlosk
2014/10/21 13:07:09
So finally, this initialization was not the correc
| |
860 if (!send_before_unload_start_time_.is_null() && | 852 if (!renderer_before_unload_start_time.is_null() && |
861 !renderer_before_unload_start_time.is_null() && | |
862 !renderer_before_unload_end_time.is_null()) { | 853 !renderer_before_unload_end_time.is_null()) { |
863 // When passing TimeTicks across process boundaries, we need to compensate | 854 // When passing TimeTicks across process boundaries, we need to compensate |
864 // for any skew between the processes. Here we are converting the | 855 // for any skew between the processes. Here we are converting the |
865 // renderer's notion of before_unload_end_time to TimeTicks in the browser | 856 // renderer's notion of before_unload_end_time to TimeTicks in the browser |
866 // process. See comments in inter_process_time_ticks_converter.h for more. | 857 // process. See comments in inter_process_time_ticks_converter.h for more. |
858 base::TimeTicks receive_before_unload_ack_time = base::TimeTicks::Now(); | |
867 InterProcessTimeTicksConverter converter( | 859 InterProcessTimeTicksConverter converter( |
868 LocalTimeTicks::FromTimeTicks(send_before_unload_start_time_), | 860 LocalTimeTicks::FromTimeTicks(send_before_unload_start_time_), |
869 LocalTimeTicks::FromTimeTicks(base::TimeTicks::Now()), | 861 LocalTimeTicks::FromTimeTicks(receive_before_unload_ack_time), |
870 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_start_time), | 862 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_start_time), |
871 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_end_time)); | 863 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_end_time)); |
872 LocalTimeTicks browser_before_unload_end_time = | 864 LocalTimeTicks browser_before_unload_end_time = |
873 converter.ToLocalTimeTicks( | 865 converter.ToLocalTimeTicks( |
874 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_end_time)); | 866 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_end_time)); |
875 before_unload_end_time = browser_before_unload_end_time.ToTimeTicks(); | 867 before_unload_end_time = browser_before_unload_end_time.ToTimeTicks(); |
876 | 868 |
877 // Collect UMA on the inter-process skew. | 869 // Collect UMA on the inter-process skew. |
878 bool is_skew_additive = false; | 870 bool is_skew_additive = false; |
879 if (converter.IsSkewAdditiveForMetrics()) { | 871 if (converter.IsSkewAdditiveForMetrics()) { |
880 is_skew_additive = true; | 872 is_skew_additive = true; |
881 base::TimeDelta skew = converter.GetSkewForMetrics(); | 873 base::TimeDelta skew = converter.GetSkewForMetrics(); |
882 if (skew >= base::TimeDelta()) { | 874 if (skew >= base::TimeDelta()) { |
883 UMA_HISTOGRAM_TIMES( | 875 UMA_HISTOGRAM_TIMES( |
884 "InterProcessTimeTicks.BrowserBehind_RendererToBrowser", skew); | 876 "InterProcessTimeTicks.BrowserBehind_RendererToBrowser", skew); |
885 } else { | 877 } else { |
886 UMA_HISTOGRAM_TIMES( | 878 UMA_HISTOGRAM_TIMES( |
887 "InterProcessTimeTicks.BrowserAhead_RendererToBrowser", -skew); | 879 "InterProcessTimeTicks.BrowserAhead_RendererToBrowser", -skew); |
888 } | 880 } |
889 } | 881 } |
890 UMA_HISTOGRAM_BOOLEAN( | 882 UMA_HISTOGRAM_BOOLEAN( |
891 "InterProcessTimeTicks.IsSkewAdditive_RendererToBrowser", | 883 "InterProcessTimeTicks.IsSkewAdditive_RendererToBrowser", |
892 is_skew_additive); | 884 is_skew_additive); |
893 | 885 |
886 base::TimeDelta on_before_unload_overhead_time = | |
887 (receive_before_unload_ack_time - send_before_unload_start_time_) - | |
888 (renderer_before_unload_end_time - renderer_before_unload_start_time); | |
889 UMA_HISTOGRAM_TIMES("Navigation.OnBeforeUnloadOverheadTime", | |
890 on_before_unload_overhead_time); | |
891 | |
894 frame_tree_node_->navigator()->LogBeforeUnloadTime( | 892 frame_tree_node_->navigator()->LogBeforeUnloadTime( |
895 renderer_before_unload_start_time, renderer_before_unload_end_time); | 893 renderer_before_unload_start_time, renderer_before_unload_end_time); |
896 } | 894 } |
895 // Resets beforeunload waiting state. | |
Charlie Reis
2014/10/21 18:21:48
nit: There should have been a blank line before th
carlosk
2014/10/22 09:08:56
Acknowledged.
| |
896 is_waiting_for_beforeunload_ack_ = false; | |
897 send_before_unload_start_time_ = base::TimeTicks(); | |
carlosk
2014/10/21 13:07:09
I moved the state reset down here so that both var
Charlie Reis
2014/10/21 18:21:48
Acknowledged.
| |
898 | |
897 frame_tree_node_->render_manager()->OnBeforeUnloadACK( | 899 frame_tree_node_->render_manager()->OnBeforeUnloadACK( |
898 unload_ack_is_for_cross_site_transition_, proceed, | 900 unload_ack_is_for_cross_site_transition_, proceed, |
899 before_unload_end_time); | 901 before_unload_end_time); |
900 | 902 |
901 // If canceled, notify the delegate to cancel its pending navigation entry. | 903 // If canceled, notify the delegate to cancel its pending navigation entry. |
902 if (!proceed) | 904 if (!proceed) |
903 render_view_host_->GetDelegate()->DidCancelLoading(); | 905 render_view_host_->GetDelegate()->DidCancelLoading(); |
904 } | 906 } |
905 | 907 |
906 bool RenderFrameHostImpl::IsWaitingForUnloadACK() const { | 908 bool RenderFrameHostImpl::IsWaitingForUnloadACK() const { |
(...skipping 310 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1217 // Whenever we change the RFH state to and from active or swapped out state, | 1219 // Whenever we change the RFH state to and from active or swapped out state, |
1218 // we should not be waiting for beforeunload or close acks. We clear them | 1220 // we should not be waiting for beforeunload or close acks. We clear them |
1219 // here to be safe, since they can cause navigations to be ignored in | 1221 // here to be safe, since they can cause navigations to be ignored in |
1220 // OnDidCommitProvisionalLoad. | 1222 // OnDidCommitProvisionalLoad. |
1221 // TODO(creis): Move is_waiting_for_beforeunload_ack_ into the state machine. | 1223 // TODO(creis): Move is_waiting_for_beforeunload_ack_ into the state machine. |
1222 if (rfh_state == STATE_DEFAULT || | 1224 if (rfh_state == STATE_DEFAULT || |
1223 rfh_state == STATE_SWAPPED_OUT || | 1225 rfh_state == STATE_SWAPPED_OUT || |
1224 rfh_state_ == STATE_DEFAULT || | 1226 rfh_state_ == STATE_DEFAULT || |
1225 rfh_state_ == STATE_SWAPPED_OUT) { | 1227 rfh_state_ == STATE_SWAPPED_OUT) { |
1226 is_waiting_for_beforeunload_ack_ = false; | 1228 is_waiting_for_beforeunload_ack_ = false; |
1229 send_before_unload_start_time_ = base::TimeTicks(); | |
1227 render_view_host_->is_waiting_for_close_ack_ = false; | 1230 render_view_host_->is_waiting_for_close_ack_ = false; |
1228 } | 1231 } |
1229 rfh_state_ = rfh_state; | 1232 rfh_state_ = rfh_state; |
1230 } | 1233 } |
1231 | 1234 |
1232 bool RenderFrameHostImpl::CanCommitURL(const GURL& url) { | 1235 bool RenderFrameHostImpl::CanCommitURL(const GURL& url) { |
1233 // TODO(creis): We should also check for WebUI pages here. Also, when the | 1236 // TODO(creis): We should also check for WebUI pages here. Also, when the |
1234 // out-of-process iframes implementation is ready, we should check for | 1237 // out-of-process iframes implementation is ready, we should check for |
1235 // cross-site URLs that are not allowed to commit in this process. | 1238 // cross-site URLs that are not allowed to commit in this process. |
1236 | 1239 |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1301 | 1304 |
1302 void RenderFrameHostImpl::OpenURL(const FrameHostMsg_OpenURL_Params& params) { | 1305 void RenderFrameHostImpl::OpenURL(const FrameHostMsg_OpenURL_Params& params) { |
1303 OnOpenURL(params); | 1306 OnOpenURL(params); |
1304 } | 1307 } |
1305 | 1308 |
1306 void RenderFrameHostImpl::Stop() { | 1309 void RenderFrameHostImpl::Stop() { |
1307 Send(new FrameMsg_Stop(routing_id_)); | 1310 Send(new FrameMsg_Stop(routing_id_)); |
1308 } | 1311 } |
1309 | 1312 |
1310 void RenderFrameHostImpl::DispatchBeforeUnload(bool for_cross_site_transition) { | 1313 void RenderFrameHostImpl::DispatchBeforeUnload(bool for_cross_site_transition) { |
1314 // TODO(creis): Support beforeunload on subframes. For now just pretend that | |
1315 // the handler ran and allowed the navigation to proceed. | |
1316 if (GetParent() || !IsRenderFrameLive()) { | |
1317 // We don't have a live renderer, so just skip running beforeunload. | |
1318 frame_tree_node_->render_manager()->OnBeforeUnloadACK( | |
1319 for_cross_site_transition, true, base::TimeTicks::Now()); | |
1320 return; | |
1321 } | |
1311 TRACE_EVENT_ASYNC_BEGIN0( | 1322 TRACE_EVENT_ASYNC_BEGIN0( |
1312 "navigation", "RenderFrameHostImpl::BeforeUnload", this); | 1323 "navigation", "RenderFrameHostImpl::BeforeUnload", this); |
1313 // TODO(creis): Support subframes. | |
1314 if (GetParent() || !IsRenderFrameLive()) { | |
1315 // We don't have a live renderer, so just skip running beforeunload. | |
1316 is_waiting_for_beforeunload_ack_ = true; | |
1317 unload_ack_is_for_cross_site_transition_ = for_cross_site_transition; | |
1318 base::TimeTicks now = base::TimeTicks::Now(); | |
1319 OnBeforeUnloadACK(true, now, now); | |
1320 return; | |
1321 } | |
1322 | 1324 |
1323 // This may be called more than once (if the user clicks the tab close button | 1325 // This may be called more than once (if the user clicks the tab close button |
1324 // several times, or if she clicks the tab close button then the browser close | 1326 // several times, or if she clicks the tab close button then the browser close |
1325 // button), and we only send the message once. | 1327 // button), and we only send the message once. |
1326 if (is_waiting_for_beforeunload_ack_) { | 1328 if (is_waiting_for_beforeunload_ack_) { |
1327 // Some of our close messages could be for the tab, others for cross-site | 1329 // Some of our close messages could be for the tab, others for cross-site |
1328 // transitions. We always want to think it's for closing the tab if any | 1330 // transitions. We always want to think it's for closing the tab if any |
1329 // of the messages were, since otherwise it might be impossible to close | 1331 // of the messages were, since otherwise it might be impossible to close |
1330 // (if there was a cross-site "close" request pending when the user clicked | 1332 // (if there was a cross-site "close" request pending when the user clicked |
1331 // the close button). We want to keep the "for cross site" flag only if | 1333 // the close button). We want to keep the "for cross site" flag only if |
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1562 // Clear any state if a pending navigation is canceled or preempted. | 1564 // Clear any state if a pending navigation is canceled or preempted. |
1563 if (suspended_nav_params_) | 1565 if (suspended_nav_params_) |
1564 suspended_nav_params_.reset(); | 1566 suspended_nav_params_.reset(); |
1565 | 1567 |
1566 TRACE_EVENT_ASYNC_END0("navigation", | 1568 TRACE_EVENT_ASYNC_END0("navigation", |
1567 "RenderFrameHostImpl navigation suspended", this); | 1569 "RenderFrameHostImpl navigation suspended", this); |
1568 navigations_suspended_ = false; | 1570 navigations_suspended_ = false; |
1569 } | 1571 } |
1570 | 1572 |
1571 } // namespace content | 1573 } // namespace content |
OLD | NEW |