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

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

Issue 2727253005: Move beforeunload hang timer duties to its own timer. (Closed)
Patch Set: rev 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 351 matching lines...) Expand 10 before | Expand all | Expand 10 after
362 enabled_bindings_ = parent_->GetEnabledBindings(); 362 enabled_bindings_ = parent_->GetEnabledBindings();
363 363
364 // New child frames should inherit the nav_entry_id of their parent. 364 // New child frames should inherit the nav_entry_id of their parent.
365 set_nav_entry_id( 365 set_nav_entry_id(
366 frame_tree_node_->parent()->current_frame_host()->nav_entry_id()); 366 frame_tree_node_->parent()->current_frame_host()->nav_entry_id());
367 } 367 }
368 368
369 SetUpMojoIfNeeded(); 369 SetUpMojoIfNeeded();
370 swapout_event_monitor_timeout_.reset(new TimeoutMonitor(base::Bind( 370 swapout_event_monitor_timeout_.reset(new TimeoutMonitor(base::Bind(
371 &RenderFrameHostImpl::OnSwappedOut, weak_ptr_factory_.GetWeakPtr()))); 371 &RenderFrameHostImpl::OnSwappedOut, weak_ptr_factory_.GetWeakPtr())));
372 beforeunload_timeout_.reset(
373 new TimeoutMonitor(base::Bind(&RenderFrameHostImpl::BeforeUnloadTimeout,
374 weak_ptr_factory_.GetWeakPtr())));
372 375
373 if (widget_routing_id != MSG_ROUTING_NONE) { 376 if (widget_routing_id != MSG_ROUTING_NONE) {
374 // TODO(avi): Once RenderViewHostImpl has-a RenderWidgetHostImpl, the main 377 // TODO(avi): Once RenderViewHostImpl has-a RenderWidgetHostImpl, the main
375 // render frame should probably start owning the RenderWidgetHostImpl, 378 // render frame should probably start owning the RenderWidgetHostImpl,
376 // so this logic checking for an already existing RWHI should be removed. 379 // so this logic checking for an already existing RWHI should be removed.
377 // https://crbug.com/545684 380 // https://crbug.com/545684
378 render_widget_host_ = 381 render_widget_host_ =
379 RenderWidgetHostImpl::FromID(GetProcess()->GetID(), widget_routing_id); 382 RenderWidgetHostImpl::FromID(GetProcess()->GetID(), widget_routing_id);
380 if (!render_widget_host_) { 383 if (!render_widget_host_) {
381 DCHECK(frame_tree_node->parent()); 384 DCHECK(frame_tree_node->parent());
(...skipping 1080 matching lines...) Expand 10 before | Expand all | Expand 10 after
1462 (receive_before_unload_ack_time - send_before_unload_start_time_) - 1465 (receive_before_unload_ack_time - send_before_unload_start_time_) -
1463 (renderer_before_unload_end_time - renderer_before_unload_start_time); 1466 (renderer_before_unload_end_time - renderer_before_unload_start_time);
1464 UMA_HISTOGRAM_TIMES("Navigation.OnBeforeUnloadOverheadTime", 1467 UMA_HISTOGRAM_TIMES("Navigation.OnBeforeUnloadOverheadTime",
1465 on_before_unload_overhead_time); 1468 on_before_unload_overhead_time);
1466 1469
1467 frame_tree_node_->navigator()->LogBeforeUnloadTime( 1470 frame_tree_node_->navigator()->LogBeforeUnloadTime(
1468 renderer_before_unload_start_time, renderer_before_unload_end_time); 1471 renderer_before_unload_start_time, renderer_before_unload_end_time);
1469 } 1472 }
1470 // Resets beforeunload waiting state. 1473 // Resets beforeunload waiting state.
1471 is_waiting_for_beforeunload_ack_ = false; 1474 is_waiting_for_beforeunload_ack_ = false;
1472 render_view_host_->GetWidget()->decrement_in_flight_event_count(); 1475 beforeunload_timeout_->Stop();
1473 render_view_host_->GetWidget()->StopHangMonitorTimeout();
1474 send_before_unload_start_time_ = base::TimeTicks(); 1476 send_before_unload_start_time_ = base::TimeTicks();
1475 1477
1476 // PlzNavigate: if the ACK is for a navigation, send it to the Navigator to 1478 // PlzNavigate: if the ACK is for a navigation, send it to the Navigator to
1477 // have the current navigation stop/proceed. Otherwise, send it to the 1479 // have the current navigation stop/proceed. Otherwise, send it to the
1478 // RenderFrameHostManager which handles closing. 1480 // RenderFrameHostManager which handles closing.
1479 if (IsBrowserSideNavigationEnabled() && unload_ack_is_for_navigation_) { 1481 if (IsBrowserSideNavigationEnabled() && unload_ack_is_for_navigation_) {
1480 // TODO(clamy): see if before_unload_end_time should be transmitted to the 1482 // TODO(clamy): see if before_unload_end_time should be transmitted to the
1481 // Navigator. 1483 // Navigator.
1482 frame_tree_node_->navigator()->OnBeforeUnloadACK( 1484 frame_tree_node_->navigator()->OnBeforeUnloadACK(
1483 frame_tree_node_, proceed); 1485 frame_tree_node_, proceed);
(...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after
1685 } 1687 }
1686 1688
1687 void RenderFrameHostImpl::OnRunBeforeUnloadConfirm( 1689 void RenderFrameHostImpl::OnRunBeforeUnloadConfirm(
1688 const GURL& frame_url, 1690 const GURL& frame_url,
1689 bool is_reload, 1691 bool is_reload,
1690 IPC::Message* reply_msg) { 1692 IPC::Message* reply_msg) {
1691 // While a JS beforeunload dialog is showing, tabs in the same process 1693 // While a JS beforeunload dialog is showing, tabs in the same process
1692 // shouldn't process input events. 1694 // shouldn't process input events.
1693 GetProcess()->SetIgnoreInputEvents(true); 1695 GetProcess()->SetIgnoreInputEvents(true);
1694 render_view_host_->GetWidget()->StopHangMonitorTimeout(); 1696 render_view_host_->GetWidget()->StopHangMonitorTimeout();
1697 beforeunload_timeout_->Stop();
1695 delegate_->RunBeforeUnloadConfirm(this, is_reload, reply_msg); 1698 delegate_->RunBeforeUnloadConfirm(this, is_reload, reply_msg);
1696 } 1699 }
1697 1700
1698 void RenderFrameHostImpl::OnRunFileChooser(const FileChooserParams& params) { 1701 void RenderFrameHostImpl::OnRunFileChooser(const FileChooserParams& params) {
1699 // Do not allow messages with absolute paths in them as this can permit a 1702 // Do not allow messages with absolute paths in them as this can permit a
1700 // renderer to coerce the browser to perform I/O on a renderer controlled 1703 // renderer to coerce the browser to perform I/O on a renderer controlled
1701 // path. 1704 // path.
1702 if (params.default_file_name != params.default_file_name.BaseName()) { 1705 if (params.default_file_name != params.default_file_name.BaseName()) {
1703 bad_message::ReceivedBadMessage(GetProcess(), 1706 bad_message::ReceivedBadMessage(GetProcess(),
1704 bad_message::RFH_FILE_CHOOSER_PATH); 1707 bad_message::RFH_FILE_CHOOSER_PATH);
(...skipping 680 matching lines...) Expand 10 before | Expand all | Expand 10 after
2385 } 2388 }
2386 2389
2387 void RenderFrameHostImpl::ResetWaitingState() { 2390 void RenderFrameHostImpl::ResetWaitingState() {
2388 DCHECK(is_active()); 2391 DCHECK(is_active());
2389 2392
2390 // Whenever we reset the RFH state, we should not be waiting for beforeunload 2393 // Whenever we reset the RFH state, we should not be waiting for beforeunload
2391 // or close acks. We clear them here to be safe, since they can cause 2394 // or close acks. We clear them here to be safe, since they can cause
2392 // navigations to be ignored in OnDidCommitProvisionalLoad. 2395 // navigations to be ignored in OnDidCommitProvisionalLoad.
2393 if (is_waiting_for_beforeunload_ack_) { 2396 if (is_waiting_for_beforeunload_ack_) {
2394 is_waiting_for_beforeunload_ack_ = false; 2397 is_waiting_for_beforeunload_ack_ = false;
2395 render_view_host_->GetWidget()->decrement_in_flight_event_count(); 2398 beforeunload_timeout_->Stop();
2396 render_view_host_->GetWidget()->StopHangMonitorTimeout();
2397 } 2399 }
2398 send_before_unload_start_time_ = base::TimeTicks(); 2400 send_before_unload_start_time_ = base::TimeTicks();
2399 render_view_host_->is_waiting_for_close_ack_ = false; 2401 render_view_host_->is_waiting_for_close_ack_ = false;
2400 } 2402 }
2401 2403
2402 bool RenderFrameHostImpl::CanCommitOrigin( 2404 bool RenderFrameHostImpl::CanCommitOrigin(
2403 const url::Origin& origin, 2405 const url::Origin& origin,
2404 const GURL& url) { 2406 const GURL& url) {
2405 // If the --disable-web-security flag is specified, all bets are off and the 2407 // If the --disable-web-security flag is specified, all bets are off and the
2406 // renderer process can send any origin it wishes. 2408 // renderer process can send any origin it wishes.
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after
2531 // Start the hang monitor in case the renderer hangs in the beforeunload 2533 // Start the hang monitor in case the renderer hangs in the beforeunload
2532 // handler. 2534 // handler.
2533 is_waiting_for_beforeunload_ack_ = true; 2535 is_waiting_for_beforeunload_ack_ = true;
2534 unload_ack_is_for_navigation_ = for_navigation; 2536 unload_ack_is_for_navigation_ = for_navigation;
2535 if (render_view_host_->GetDelegate()->IsJavaScriptDialogShowing()) { 2537 if (render_view_host_->GetDelegate()->IsJavaScriptDialogShowing()) {
2536 // If there is a JavaScript dialog up, don't bother sending the renderer 2538 // If there is a JavaScript dialog up, don't bother sending the renderer
2537 // the unload event because it is known unresponsive, waiting for the 2539 // the unload event because it is known unresponsive, waiting for the
2538 // reply from the dialog. 2540 // reply from the dialog.
2539 SimulateBeforeUnloadAck(); 2541 SimulateBeforeUnloadAck();
2540 } else { 2542 } else {
2541 // Increment the in-flight event count, to ensure that input events won't 2543 beforeunload_timeout_type_ =
2542 // cancel the timeout timer. 2544 RendererUnresponsiveType::RENDERER_UNRESPONSIVE_BEFORE_UNLOAD;
2543 render_view_host_->GetWidget()->increment_in_flight_event_count(); 2545 beforeunload_timeout_->Start(
2544 render_view_host_->GetWidget()->StartHangMonitorTimeout( 2546 TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS));
2545 TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS),
2546 blink::WebInputEvent::Undefined,
2547 RendererUnresponsiveType::RENDERER_UNRESPONSIVE_BEFORE_UNLOAD);
2548 send_before_unload_start_time_ = base::TimeTicks::Now(); 2547 send_before_unload_start_time_ = base::TimeTicks::Now();
2549 Send(new FrameMsg_BeforeUnload(routing_id_, is_reload)); 2548 Send(new FrameMsg_BeforeUnload(routing_id_, is_reload));
2550 } 2549 }
2551 } 2550 }
2552 } 2551 }
2553 2552
2554 void RenderFrameHostImpl::SimulateBeforeUnloadAck() { 2553 void RenderFrameHostImpl::SimulateBeforeUnloadAck() {
2555 DCHECK(is_waiting_for_beforeunload_ack_); 2554 DCHECK(is_waiting_for_beforeunload_ack_);
2556 base::TimeTicks approx_renderer_start_time = send_before_unload_start_time_; 2555 base::TimeTicks approx_renderer_start_time = send_before_unload_start_time_;
2557 OnBeforeUnloadACK(true, approx_renderer_start_time, base::TimeTicks::Now()); 2556 OnBeforeUnloadACK(true, approx_renderer_start_time, base::TimeTicks::Now());
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
2606 } 2605 }
2607 2606
2608 void RenderFrameHostImpl::JavaScriptDialogClosed( 2607 void RenderFrameHostImpl::JavaScriptDialogClosed(
2609 IPC::Message* reply_msg, 2608 IPC::Message* reply_msg,
2610 bool success, 2609 bool success,
2611 const base::string16& user_input, 2610 const base::string16& user_input,
2612 bool is_before_unload_dialog, 2611 bool is_before_unload_dialog,
2613 bool dialog_was_suppressed) { 2612 bool dialog_was_suppressed) {
2614 GetProcess()->SetIgnoreInputEvents(false); 2613 GetProcess()->SetIgnoreInputEvents(false);
2615 2614
2616 // If we are executing as part of beforeunload event handling, we don't 2615 // If executing as part of beforeunload event handling, resume the timeout
2617 // want to use the regular hung_renderer_delay_ms_ if the user has agreed to 2616 // that was suspended when the dialog was shown in OnRunBeforeUnloadConfirm().
2618 // leave the current page. In this case, use the regular timeout value used 2617 //
2619 // during the beforeunload handling. 2618 // If the user said to leave the current page, they're waiting, so use the
2619 // smaller onbeforeunload timeout. If the user said to stay, use the usual
2620 // hung page timeout, which is longer.
2620 if (is_before_unload_dialog) { 2621 if (is_before_unload_dialog) {
2621 RendererUnresponsiveType type = 2622 beforeunload_timeout_type_ =
2622 success ? RendererUnresponsiveType::RENDERER_UNRESPONSIVE_BEFORE_UNLOAD 2623 success ? RendererUnresponsiveType::RENDERER_UNRESPONSIVE_BEFORE_UNLOAD
2623 : RendererUnresponsiveType::RENDERER_UNRESPONSIVE_DIALOG_CLOSED; 2624 : RendererUnresponsiveType::RENDERER_UNRESPONSIVE_DIALOG_CLOSED;
2624 render_view_host_->GetWidget()->StartHangMonitorTimeout( 2625 beforeunload_timeout_->Start(
2625 success 2626 success
2626 ? TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS) 2627 ? TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS)
2627 : render_view_host_->GetWidget()->hung_renderer_delay(), 2628 : render_view_host_->GetWidget()->hung_renderer_delay());
Charlie Reis 2017/03/06 21:10:44 In the new approach where the beforeunload timer i
Avi (use Gerrit) 2017/03/06 22:11:17 We always get a beforeunload ACK, even when the us
Charlie Reis 2017/03/06 23:47:56 Great-- maybe we can use the shorter timeout for b
Avi (use Gerrit) 2017/03/06 23:59:15 Sure!
2628 blink::WebInputEvent::Undefined, type);
2629 } 2629 }
2630 2630
2631 SendJavaScriptDialogReply(reply_msg, success, user_input); 2631 SendJavaScriptDialogReply(reply_msg, success, user_input);
2632 2632
2633 // If we are waiting for a beforeunload ack and the user has suppressed 2633 // If we are waiting for a beforeunload ack and the user has suppressed
2634 // messages, kill the tab immediately; a page that's spamming alerts in 2634 // messages, kill the tab immediately. A page that's spamming is presumably
2635 // onbeforeunload is presumably malicious, so there's no point in continuing 2635 // malicious, so there's no point in continuing to run its script and dragging
2636 // to run its script and dragging out the process. This must be done after 2636 // out the process. This must be done after sending the reply since RenderView
2637 // sending the reply since RenderView can't close correctly while waiting for 2637 // can't close correctly while waiting for a response.
2638 // a response.
2639 if (is_before_unload_dialog && dialog_was_suppressed) { 2638 if (is_before_unload_dialog && dialog_was_suppressed) {
2640 render_view_host_->GetWidget()->delegate()->RendererUnresponsive( 2639 UMA_HISTOGRAM_ENUMERATION(
2641 render_view_host_->GetWidget(), 2640 "ChildProcess.HangRendererType",
2642 RendererUnresponsiveType::RENDERER_UNRESPONSIVE_DIALOG_SUPPRESSED); 2641 RendererUnresponsiveType::RENDERER_UNRESPONSIVE_DIALOG_SUPPRESSED,
2642 RendererUnresponsiveType::RENDERER_UNRESPONSIVE_MAX);
2643
2644 SimulateBeforeUnloadAck();
2643 } 2645 }
2644 } 2646 }
2645 2647
2646 void RenderFrameHostImpl::SendJavaScriptDialogReply( 2648 void RenderFrameHostImpl::SendJavaScriptDialogReply(
2647 IPC::Message* reply_msg, 2649 IPC::Message* reply_msg,
2648 bool success, 2650 bool success,
2649 const base::string16& user_input) { 2651 const base::string16& user_input) {
2650 FrameHostMsg_RunJavaScriptDialog::WriteReplyParams(reply_msg, success, 2652 FrameHostMsg_RunJavaScriptDialog::WriteReplyParams(reply_msg, success,
2651 user_input); 2653 user_input);
2652 Send(reply_msg); 2654 Send(reply_msg);
(...skipping 804 matching lines...) Expand 10 before | Expand all | Expand 10 after
3457 3459
3458 // There is no pending NavigationEntry in these cases, so pass 0 as the 3460 // There is no pending NavigationEntry in these cases, so pass 0 as the
3459 // pending_nav_entry_id. If the previous handle was a prematurely aborted 3461 // pending_nav_entry_id. If the previous handle was a prematurely aborted
3460 // navigation loaded via LoadDataWithBaseURL, propagate the entry id. 3462 // navigation loaded via LoadDataWithBaseURL, propagate the entry id.
3461 return NavigationHandleImpl::Create( 3463 return NavigationHandleImpl::Create(
3462 params.url, params.redirects, frame_tree_node_, is_renderer_initiated, 3464 params.url, params.redirects, frame_tree_node_, is_renderer_initiated,
3463 params.was_within_same_page, base::TimeTicks::Now(), 3465 params.was_within_same_page, base::TimeTicks::Now(),
3464 entry_id_for_data_nav, false); // started_from_context_menu 3466 entry_id_for_data_nav, false); // started_from_context_menu
3465 } 3467 }
3466 3468
3469 void RenderFrameHostImpl::BeforeUnloadTimeout() {
3470 if (render_view_host_->GetDelegate()->ShouldIgnoreUnresponsiveRenderer())
3471 return;
3472
3473 UMA_HISTOGRAM_ENUMERATION(
3474 "ChildProcess.HangRendererType", beforeunload_timeout_type_,
dtapuska 2017/03/06 18:23:46 Is this safe? Declaring the histogram twice? The m
Avi (use Gerrit) 2017/03/06 18:27:26 Wait.. you can't do this? I already did this in ht
Avi (use Gerrit) 2017/03/06 18:43:09 Ilya says in chat: "I see. That's fine, in terms
Avi (use Gerrit) 2017/03/06 23:59:15 WebContentsImpl::RendererUnresponsive is a bunch o
3475 RendererUnresponsiveType::RENDERER_UNRESPONSIVE_MAX);
3476
3477 SimulateBeforeUnloadAck();
3478 }
3479
3467 } // namespace content 3480 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698