Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(94)

Side by Side Diff: content/browser/frame_host/render_frame_host_impl.cc

Issue 2738943002: Move beforeunload hang timer duties to its own timer. (Closed)
Patch Set: better test Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 <algorithm> 7 #include <algorithm>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
(...skipping 358 matching lines...) Expand 10 before | Expand all | Expand 10 after
369 enabled_bindings_ = parent_->GetEnabledBindings(); 369 enabled_bindings_ = parent_->GetEnabledBindings();
370 370
371 // New child frames should inherit the nav_entry_id of their parent. 371 // New child frames should inherit the nav_entry_id of their parent.
372 set_nav_entry_id( 372 set_nav_entry_id(
373 frame_tree_node_->parent()->current_frame_host()->nav_entry_id()); 373 frame_tree_node_->parent()->current_frame_host()->nav_entry_id());
374 } 374 }
375 375
376 SetUpMojoIfNeeded(); 376 SetUpMojoIfNeeded();
377 swapout_event_monitor_timeout_.reset(new TimeoutMonitor(base::Bind( 377 swapout_event_monitor_timeout_.reset(new TimeoutMonitor(base::Bind(
378 &RenderFrameHostImpl::OnSwappedOut, weak_ptr_factory_.GetWeakPtr()))); 378 &RenderFrameHostImpl::OnSwappedOut, weak_ptr_factory_.GetWeakPtr())));
379 beforeunload_timeout_.reset(
380 new TimeoutMonitor(base::Bind(&RenderFrameHostImpl::BeforeUnloadTimeout,
381 weak_ptr_factory_.GetWeakPtr())));
379 382
380 if (widget_routing_id != MSG_ROUTING_NONE) { 383 if (widget_routing_id != MSG_ROUTING_NONE) {
381 // TODO(avi): Once RenderViewHostImpl has-a RenderWidgetHostImpl, the main 384 // TODO(avi): Once RenderViewHostImpl has-a RenderWidgetHostImpl, the main
382 // render frame should probably start owning the RenderWidgetHostImpl, 385 // render frame should probably start owning the RenderWidgetHostImpl,
383 // so this logic checking for an already existing RWHI should be removed. 386 // so this logic checking for an already existing RWHI should be removed.
384 // https://crbug.com/545684 387 // https://crbug.com/545684
385 render_widget_host_ = 388 render_widget_host_ =
386 RenderWidgetHostImpl::FromID(GetProcess()->GetID(), widget_routing_id); 389 RenderWidgetHostImpl::FromID(GetProcess()->GetID(), widget_routing_id);
387 if (!render_widget_host_) { 390 if (!render_widget_host_) {
388 DCHECK(frame_tree_node->parent()); 391 DCHECK(frame_tree_node->parent());
(...skipping 1085 matching lines...) Expand 10 before | Expand all | Expand 10 after
1474 (receive_before_unload_ack_time - send_before_unload_start_time_) - 1477 (receive_before_unload_ack_time - send_before_unload_start_time_) -
1475 (renderer_before_unload_end_time - renderer_before_unload_start_time); 1478 (renderer_before_unload_end_time - renderer_before_unload_start_time);
1476 UMA_HISTOGRAM_TIMES("Navigation.OnBeforeUnloadOverheadTime", 1479 UMA_HISTOGRAM_TIMES("Navigation.OnBeforeUnloadOverheadTime",
1477 on_before_unload_overhead_time); 1480 on_before_unload_overhead_time);
1478 1481
1479 frame_tree_node_->navigator()->LogBeforeUnloadTime( 1482 frame_tree_node_->navigator()->LogBeforeUnloadTime(
1480 renderer_before_unload_start_time, renderer_before_unload_end_time); 1483 renderer_before_unload_start_time, renderer_before_unload_end_time);
1481 } 1484 }
1482 // Resets beforeunload waiting state. 1485 // Resets beforeunload waiting state.
1483 is_waiting_for_beforeunload_ack_ = false; 1486 is_waiting_for_beforeunload_ack_ = false;
1484 render_view_host_->GetWidget()->decrement_in_flight_event_count(); 1487 beforeunload_timeout_->Stop();
1485 render_view_host_->GetWidget()->StopHangMonitorTimeout();
1486 send_before_unload_start_time_ = base::TimeTicks(); 1488 send_before_unload_start_time_ = base::TimeTicks();
1487 1489
1488 // PlzNavigate: if the ACK is for a navigation, send it to the Navigator to 1490 // PlzNavigate: if the ACK is for a navigation, send it to the Navigator to
1489 // have the current navigation stop/proceed. Otherwise, send it to the 1491 // have the current navigation stop/proceed. Otherwise, send it to the
1490 // RenderFrameHostManager which handles closing. 1492 // RenderFrameHostManager which handles closing.
1491 if (IsBrowserSideNavigationEnabled() && unload_ack_is_for_navigation_) { 1493 if (IsBrowserSideNavigationEnabled() && unload_ack_is_for_navigation_) {
1492 // TODO(clamy): see if before_unload_end_time should be transmitted to the 1494 // TODO(clamy): see if before_unload_end_time should be transmitted to the
1493 // Navigator. 1495 // Navigator.
1494 frame_tree_node_->navigator()->OnBeforeUnloadACK( 1496 frame_tree_node_->navigator()->OnBeforeUnloadACK(
1495 frame_tree_node_, proceed); 1497 frame_tree_node_, proceed);
(...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after
1697 } 1699 }
1698 1700
1699 void RenderFrameHostImpl::OnRunBeforeUnloadConfirm( 1701 void RenderFrameHostImpl::OnRunBeforeUnloadConfirm(
1700 const GURL& frame_url, 1702 const GURL& frame_url,
1701 bool is_reload, 1703 bool is_reload,
1702 IPC::Message* reply_msg) { 1704 IPC::Message* reply_msg) {
1703 // While a JS beforeunload dialog is showing, tabs in the same process 1705 // While a JS beforeunload dialog is showing, tabs in the same process
1704 // shouldn't process input events. 1706 // shouldn't process input events.
1705 GetProcess()->SetIgnoreInputEvents(true); 1707 GetProcess()->SetIgnoreInputEvents(true);
1706 render_view_host_->GetWidget()->StopHangMonitorTimeout(); 1708 render_view_host_->GetWidget()->StopHangMonitorTimeout();
1709
1710 // The beforeunload dialog for this frame may have been triggered by a
1711 // browser-side request to this frame or a frame up in the frame hierarchy.
1712 // Stop any timers that are waiting.
1713 for (RenderFrameHostImpl* frame = this; frame; frame = frame->GetParent())
1714 frame->beforeunload_timeout_->Stop();
1715
1707 delegate_->RunBeforeUnloadConfirm(this, is_reload, reply_msg); 1716 delegate_->RunBeforeUnloadConfirm(this, is_reload, reply_msg);
1708 } 1717 }
1709 1718
1710 void RenderFrameHostImpl::OnRunFileChooser(const FileChooserParams& params) { 1719 void RenderFrameHostImpl::OnRunFileChooser(const FileChooserParams& params) {
1711 // Do not allow messages with absolute paths in them as this can permit a 1720 // Do not allow messages with absolute paths in them as this can permit a
1712 // renderer to coerce the browser to perform I/O on a renderer controlled 1721 // renderer to coerce the browser to perform I/O on a renderer controlled
1713 // path. 1722 // path.
1714 if (params.default_file_name != params.default_file_name.BaseName()) { 1723 if (params.default_file_name != params.default_file_name.BaseName()) {
1715 bad_message::ReceivedBadMessage(GetProcess(), 1724 bad_message::ReceivedBadMessage(GetProcess(),
1716 bad_message::RFH_FILE_CHOOSER_PATH); 1725 bad_message::RFH_FILE_CHOOSER_PATH);
(...skipping 696 matching lines...) Expand 10 before | Expand all | Expand 10 after
2413 } 2422 }
2414 2423
2415 void RenderFrameHostImpl::ResetWaitingState() { 2424 void RenderFrameHostImpl::ResetWaitingState() {
2416 DCHECK(is_active()); 2425 DCHECK(is_active());
2417 2426
2418 // Whenever we reset the RFH state, we should not be waiting for beforeunload 2427 // Whenever we reset the RFH state, we should not be waiting for beforeunload
2419 // or close acks. We clear them here to be safe, since they can cause 2428 // or close acks. We clear them here to be safe, since they can cause
2420 // navigations to be ignored in OnDidCommitProvisionalLoad. 2429 // navigations to be ignored in OnDidCommitProvisionalLoad.
2421 if (is_waiting_for_beforeunload_ack_) { 2430 if (is_waiting_for_beforeunload_ack_) {
2422 is_waiting_for_beforeunload_ack_ = false; 2431 is_waiting_for_beforeunload_ack_ = false;
2423 render_view_host_->GetWidget()->decrement_in_flight_event_count(); 2432 beforeunload_timeout_->Stop();
2424 render_view_host_->GetWidget()->StopHangMonitorTimeout();
2425 } 2433 }
2426 send_before_unload_start_time_ = base::TimeTicks(); 2434 send_before_unload_start_time_ = base::TimeTicks();
2427 render_view_host_->is_waiting_for_close_ack_ = false; 2435 render_view_host_->is_waiting_for_close_ack_ = false;
2428 } 2436 }
2429 2437
2430 bool RenderFrameHostImpl::CanCommitOrigin( 2438 bool RenderFrameHostImpl::CanCommitOrigin(
2431 const url::Origin& origin, 2439 const url::Origin& origin,
2432 const GURL& url) { 2440 const GURL& url) {
2433 // If the --disable-web-security flag is specified, all bets are off and the 2441 // If the --disable-web-security flag is specified, all bets are off and the
2434 // renderer process can send any origin it wishes. 2442 // renderer process can send any origin it wishes.
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
2560 // Start the hang monitor in case the renderer hangs in the beforeunload 2568 // Start the hang monitor in case the renderer hangs in the beforeunload
2561 // handler. 2569 // handler.
2562 is_waiting_for_beforeunload_ack_ = true; 2570 is_waiting_for_beforeunload_ack_ = true;
2563 unload_ack_is_for_navigation_ = for_navigation; 2571 unload_ack_is_for_navigation_ = for_navigation;
2564 if (render_view_host_->GetDelegate()->IsJavaScriptDialogShowing()) { 2572 if (render_view_host_->GetDelegate()->IsJavaScriptDialogShowing()) {
2565 // If there is a JavaScript dialog up, don't bother sending the renderer 2573 // If there is a JavaScript dialog up, don't bother sending the renderer
2566 // the unload event because it is known unresponsive, waiting for the 2574 // the unload event because it is known unresponsive, waiting for the
2567 // reply from the dialog. 2575 // reply from the dialog.
2568 SimulateBeforeUnloadAck(); 2576 SimulateBeforeUnloadAck();
2569 } else { 2577 } else {
2570 // Increment the in-flight event count, to ensure that input events won't 2578 beforeunload_timeout_->Start(
2571 // cancel the timeout timer. 2579 TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS));
2572 render_view_host_->GetWidget()->increment_in_flight_event_count();
2573 render_view_host_->GetWidget()->StartHangMonitorTimeout(
2574 TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS),
2575 blink::WebInputEvent::Undefined);
2576 send_before_unload_start_time_ = base::TimeTicks::Now(); 2580 send_before_unload_start_time_ = base::TimeTicks::Now();
2577 Send(new FrameMsg_BeforeUnload(routing_id_, is_reload)); 2581 Send(new FrameMsg_BeforeUnload(routing_id_, is_reload));
2578 } 2582 }
2579 } 2583 }
2580 } 2584 }
2581 2585
2582 void RenderFrameHostImpl::SimulateBeforeUnloadAck() { 2586 void RenderFrameHostImpl::SimulateBeforeUnloadAck() {
2583 DCHECK(is_waiting_for_beforeunload_ack_); 2587 DCHECK(is_waiting_for_beforeunload_ack_);
2584 base::TimeTicks approx_renderer_start_time = send_before_unload_start_time_; 2588 base::TimeTicks approx_renderer_start_time = send_before_unload_start_time_;
2585 OnBeforeUnloadACK(true, approx_renderer_start_time, base::TimeTicks::Now()); 2589 OnBeforeUnloadACK(true, approx_renderer_start_time, base::TimeTicks::Now());
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
2630 void RenderFrameHostImpl::DeleteSurroundingTextInCodePoints(int before, 2634 void RenderFrameHostImpl::DeleteSurroundingTextInCodePoints(int before,
2631 int after) { 2635 int after) {
2632 Send(new InputMsg_DeleteSurroundingTextInCodePoints(routing_id_, before, 2636 Send(new InputMsg_DeleteSurroundingTextInCodePoints(routing_id_, before,
2633 after)); 2637 after));
2634 } 2638 }
2635 2639
2636 void RenderFrameHostImpl::JavaScriptDialogClosed( 2640 void RenderFrameHostImpl::JavaScriptDialogClosed(
2637 IPC::Message* reply_msg, 2641 IPC::Message* reply_msg,
2638 bool success, 2642 bool success,
2639 const base::string16& user_input, 2643 const base::string16& user_input,
2640 bool is_before_unload_dialog,
2641 bool dialog_was_suppressed) { 2644 bool dialog_was_suppressed) {
2642 GetProcess()->SetIgnoreInputEvents(false); 2645 GetProcess()->SetIgnoreInputEvents(false);
2643 2646
2644 // If we are executing as part of beforeunload event handling, we don't
2645 // want to use the regular hung_renderer_delay_ms_ if the user has agreed to
2646 // leave the current page. In this case, use the regular timeout value used
2647 // during the beforeunload handling.
2648 if (is_before_unload_dialog) {
2649 render_view_host_->GetWidget()->StartHangMonitorTimeout(
2650 success
2651 ? TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS)
2652 : render_view_host_->GetWidget()->hung_renderer_delay(),
2653 blink::WebInputEvent::Undefined);
2654 }
2655
2656 SendJavaScriptDialogReply(reply_msg, success, user_input); 2647 SendJavaScriptDialogReply(reply_msg, success, user_input);
2657 2648
2658 // If we are waiting for a beforeunload ack and the user has suppressed 2649 // If executing as part of beforeunload event handling, there may have been
2659 // messages, kill the tab immediately; a page that's spamming alerts in 2650 // timers stopped in this frame or a frame up in the frame hierarchy. Restart
2660 // onbeforeunload is presumably malicious, so there's no point in continuing 2651 // any timers that were stopped in OnRunBeforeUnloadConfirm().
2661 // to run its script and dragging out the process. This must be done after 2652 for (RenderFrameHostImpl* frame = this; frame; frame = frame->GetParent()) {
2662 // sending the reply since RenderView can't close correctly while waiting for 2653 if (frame->is_waiting_for_beforeunload_ack_) {
2663 // a response. 2654 // If we are waiting for a beforeunload ack and the user has suppressed
2664 if (is_before_unload_dialog && dialog_was_suppressed) { 2655 // messages, kill the tab immediately. A page that's spamming is
2665 render_view_host_->GetWidget()->delegate()->RendererUnresponsive( 2656 // presumably malicious, so there's no point in continuing to run its
2666 render_view_host_->GetWidget()); 2657 // script and dragging out the process.
2658 if (dialog_was_suppressed) {
2659 frame->SimulateBeforeUnloadAck();
2660 } else {
2661 frame->beforeunload_timeout_->Start(
2662 TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS));
2663 }
2664 }
2667 } 2665 }
2668 } 2666 }
2669 2667
2670 void RenderFrameHostImpl::SendJavaScriptDialogReply( 2668 void RenderFrameHostImpl::SendJavaScriptDialogReply(
2671 IPC::Message* reply_msg, 2669 IPC::Message* reply_msg,
2672 bool success, 2670 bool success,
2673 const base::string16& user_input) { 2671 const base::string16& user_input) {
2674 FrameHostMsg_RunJavaScriptDialog::WriteReplyParams(reply_msg, success, 2672 FrameHostMsg_RunJavaScriptDialog::WriteReplyParams(reply_msg, success,
2675 user_input); 2673 user_input);
2676 Send(reply_msg); 2674 Send(reply_msg);
(...skipping 806 matching lines...) Expand 10 before | Expand all | Expand 10 after
3483 3481
3484 // There is no pending NavigationEntry in these cases, so pass 0 as the 3482 // There is no pending NavigationEntry in these cases, so pass 0 as the
3485 // pending_nav_entry_id. If the previous handle was a prematurely aborted 3483 // pending_nav_entry_id. If the previous handle was a prematurely aborted
3486 // navigation loaded via LoadDataWithBaseURL, propagate the entry id. 3484 // navigation loaded via LoadDataWithBaseURL, propagate the entry id.
3487 return NavigationHandleImpl::Create( 3485 return NavigationHandleImpl::Create(
3488 params.url, params.redirects, frame_tree_node_, is_renderer_initiated, 3486 params.url, params.redirects, frame_tree_node_, is_renderer_initiated,
3489 params.was_within_same_page, base::TimeTicks::Now(), 3487 params.was_within_same_page, base::TimeTicks::Now(),
3490 entry_id_for_data_nav, false); // started_from_context_menu 3488 entry_id_for_data_nav, false); // started_from_context_menu
3491 } 3489 }
3492 3490
3491 void RenderFrameHostImpl::BeforeUnloadTimeout() {
3492 if (render_view_host_->GetDelegate()->ShouldIgnoreUnresponsiveRenderer())
3493 return;
3494
3495 SimulateBeforeUnloadAck();
3496 }
3497
3493 #if defined(OS_ANDROID) 3498 #if defined(OS_ANDROID)
3494 base::android::ScopedJavaLocalRef<jobject> 3499 base::android::ScopedJavaLocalRef<jobject>
3495 RenderFrameHostImpl::GetJavaRenderFrameHost() { 3500 RenderFrameHostImpl::GetJavaRenderFrameHost() {
3496 RenderFrameHostAndroid* render_frame_host_android = 3501 RenderFrameHostAndroid* render_frame_host_android =
3497 static_cast<RenderFrameHostAndroid*>( 3502 static_cast<RenderFrameHostAndroid*>(
3498 GetUserData(kRenderFrameHostAndroidKey)); 3503 GetUserData(kRenderFrameHostAndroidKey));
3499 if (!render_frame_host_android) { 3504 if (!render_frame_host_android) {
3500 render_frame_host_android = new RenderFrameHostAndroid(this); 3505 render_frame_host_android = new RenderFrameHostAndroid(this);
3501 SetUserData(kRenderFrameHostAndroidKey, render_frame_host_android); 3506 SetUserData(kRenderFrameHostAndroidKey, render_frame_host_android);
3502 } 3507 }
3503 return render_frame_host_android->GetJavaObject(); 3508 return render_frame_host_android->GetJavaObject();
3504 } 3509 }
3505 #endif 3510 #endif
3506 3511
3507 } // namespace content 3512 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/frame_host/render_frame_host_impl.h ('k') | content/browser/frame_host/render_frame_host_impl_browsertest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698