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

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: rebase 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 1081 matching lines...) Expand 10 before | Expand all | Expand 10 after
1463 (receive_before_unload_ack_time - send_before_unload_start_time_) - 1466 (receive_before_unload_ack_time - send_before_unload_start_time_) -
1464 (renderer_before_unload_end_time - renderer_before_unload_start_time); 1467 (renderer_before_unload_end_time - renderer_before_unload_start_time);
1465 UMA_HISTOGRAM_TIMES("Navigation.OnBeforeUnloadOverheadTime", 1468 UMA_HISTOGRAM_TIMES("Navigation.OnBeforeUnloadOverheadTime",
1466 on_before_unload_overhead_time); 1469 on_before_unload_overhead_time);
1467 1470
1468 frame_tree_node_->navigator()->LogBeforeUnloadTime( 1471 frame_tree_node_->navigator()->LogBeforeUnloadTime(
1469 renderer_before_unload_start_time, renderer_before_unload_end_time); 1472 renderer_before_unload_start_time, renderer_before_unload_end_time);
1470 } 1473 }
1471 // Resets beforeunload waiting state. 1474 // Resets beforeunload waiting state.
1472 is_waiting_for_beforeunload_ack_ = false; 1475 is_waiting_for_beforeunload_ack_ = false;
1473 render_view_host_->GetWidget()->decrement_in_flight_event_count(); 1476 beforeunload_timeout_->Stop();
1474 render_view_host_->GetWidget()->StopHangMonitorTimeout();
1475 send_before_unload_start_time_ = base::TimeTicks(); 1477 send_before_unload_start_time_ = base::TimeTicks();
1476 1478
1477 // PlzNavigate: if the ACK is for a navigation, send it to the Navigator to 1479 // PlzNavigate: if the ACK is for a navigation, send it to the Navigator to
1478 // have the current navigation stop/proceed. Otherwise, send it to the 1480 // have the current navigation stop/proceed. Otherwise, send it to the
1479 // RenderFrameHostManager which handles closing. 1481 // RenderFrameHostManager which handles closing.
1480 if (IsBrowserSideNavigationEnabled() && unload_ack_is_for_navigation_) { 1482 if (IsBrowserSideNavigationEnabled() && unload_ack_is_for_navigation_) {
1481 // TODO(clamy): see if before_unload_end_time should be transmitted to the 1483 // TODO(clamy): see if before_unload_end_time should be transmitted to the
1482 // Navigator. 1484 // Navigator.
1483 frame_tree_node_->navigator()->OnBeforeUnloadACK( 1485 frame_tree_node_->navigator()->OnBeforeUnloadACK(
1484 frame_tree_node_, proceed); 1486 frame_tree_node_, proceed);
(...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after
1686 } 1688 }
1687 1689
1688 void RenderFrameHostImpl::OnRunBeforeUnloadConfirm( 1690 void RenderFrameHostImpl::OnRunBeforeUnloadConfirm(
1689 const GURL& frame_url, 1691 const GURL& frame_url,
1690 bool is_reload, 1692 bool is_reload,
1691 IPC::Message* reply_msg) { 1693 IPC::Message* reply_msg) {
1692 // While a JS beforeunload dialog is showing, tabs in the same process 1694 // While a JS beforeunload dialog is showing, tabs in the same process
1693 // shouldn't process input events. 1695 // shouldn't process input events.
1694 GetProcess()->SetIgnoreInputEvents(true); 1696 GetProcess()->SetIgnoreInputEvents(true);
1695 render_view_host_->GetWidget()->StopHangMonitorTimeout(); 1697 render_view_host_->GetWidget()->StopHangMonitorTimeout();
1698
1699 // The beforeunload dialog for this frame may have been triggered by a
1700 // browser-side request to this frame or a frame up in the frame hierarchy.
1701 // Stop any timers that are waiting.
Charlie Reis 2017/03/14 17:19:29 Yeah, that sounds right.
Avi (use Gerrit) 2017/03/14 20:52:03 Acknowledged.
1702 for (RenderFrameHostImpl* frame = this; frame; frame = frame->GetParent())
Charlie Reis 2017/03/14 17:19:29 nit: This would need braces for a multi-line body,
Avi (use Gerrit) 2017/03/14 20:52:03 Acknowledged.
1703 if (frame->is_waiting_for_beforeunload_ack_)
Charlie Reis 2017/03/14 17:19:29 Is this check needed? I'm guessing that is_waitin
Avi (use Gerrit) 2017/03/14 20:52:03 If we don't have a dialog up, yes, is_waiting_for_
Avi (use Gerrit) 2017/03/14 20:58:58 FYI, before OldSpice, alerts were serialized in a
1704 frame->beforeunload_timeout_->Stop();
1705
1696 delegate_->RunBeforeUnloadConfirm(this, is_reload, reply_msg); 1706 delegate_->RunBeforeUnloadConfirm(this, is_reload, reply_msg);
1697 } 1707 }
1698 1708
1699 void RenderFrameHostImpl::OnRunFileChooser(const FileChooserParams& params) { 1709 void RenderFrameHostImpl::OnRunFileChooser(const FileChooserParams& params) {
1700 // Do not allow messages with absolute paths in them as this can permit a 1710 // Do not allow messages with absolute paths in them as this can permit a
1701 // renderer to coerce the browser to perform I/O on a renderer controlled 1711 // renderer to coerce the browser to perform I/O on a renderer controlled
1702 // path. 1712 // path.
1703 if (params.default_file_name != params.default_file_name.BaseName()) { 1713 if (params.default_file_name != params.default_file_name.BaseName()) {
1704 bad_message::ReceivedBadMessage(GetProcess(), 1714 bad_message::ReceivedBadMessage(GetProcess(),
1705 bad_message::RFH_FILE_CHOOSER_PATH); 1715 bad_message::RFH_FILE_CHOOSER_PATH);
(...skipping 680 matching lines...) Expand 10 before | Expand all | Expand 10 after
2386 } 2396 }
2387 2397
2388 void RenderFrameHostImpl::ResetWaitingState() { 2398 void RenderFrameHostImpl::ResetWaitingState() {
2389 DCHECK(is_active()); 2399 DCHECK(is_active());
2390 2400
2391 // Whenever we reset the RFH state, we should not be waiting for beforeunload 2401 // Whenever we reset the RFH state, we should not be waiting for beforeunload
2392 // or close acks. We clear them here to be safe, since they can cause 2402 // or close acks. We clear them here to be safe, since they can cause
2393 // navigations to be ignored in OnDidCommitProvisionalLoad. 2403 // navigations to be ignored in OnDidCommitProvisionalLoad.
2394 if (is_waiting_for_beforeunload_ack_) { 2404 if (is_waiting_for_beforeunload_ack_) {
2395 is_waiting_for_beforeunload_ack_ = false; 2405 is_waiting_for_beforeunload_ack_ = false;
2396 render_view_host_->GetWidget()->decrement_in_flight_event_count(); 2406 beforeunload_timeout_->Stop();
2397 render_view_host_->GetWidget()->StopHangMonitorTimeout();
2398 } 2407 }
2399 send_before_unload_start_time_ = base::TimeTicks(); 2408 send_before_unload_start_time_ = base::TimeTicks();
2400 render_view_host_->is_waiting_for_close_ack_ = false; 2409 render_view_host_->is_waiting_for_close_ack_ = false;
2401 } 2410 }
2402 2411
2403 bool RenderFrameHostImpl::CanCommitOrigin( 2412 bool RenderFrameHostImpl::CanCommitOrigin(
2404 const url::Origin& origin, 2413 const url::Origin& origin,
2405 const GURL& url) { 2414 const GURL& url) {
2406 // If the --disable-web-security flag is specified, all bets are off and the 2415 // If the --disable-web-security flag is specified, all bets are off and the
2407 // renderer process can send any origin it wishes. 2416 // renderer process can send any origin it wishes.
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after
2532 // Start the hang monitor in case the renderer hangs in the beforeunload 2541 // Start the hang monitor in case the renderer hangs in the beforeunload
2533 // handler. 2542 // handler.
2534 is_waiting_for_beforeunload_ack_ = true; 2543 is_waiting_for_beforeunload_ack_ = true;
2535 unload_ack_is_for_navigation_ = for_navigation; 2544 unload_ack_is_for_navigation_ = for_navigation;
2536 if (render_view_host_->GetDelegate()->IsJavaScriptDialogShowing()) { 2545 if (render_view_host_->GetDelegate()->IsJavaScriptDialogShowing()) {
2537 // If there is a JavaScript dialog up, don't bother sending the renderer 2546 // If there is a JavaScript dialog up, don't bother sending the renderer
2538 // the unload event because it is known unresponsive, waiting for the 2547 // the unload event because it is known unresponsive, waiting for the
2539 // reply from the dialog. 2548 // reply from the dialog.
2540 SimulateBeforeUnloadAck(); 2549 SimulateBeforeUnloadAck();
2541 } else { 2550 } else {
2542 // Increment the in-flight event count, to ensure that input events won't 2551 beforeunload_timeout_->Start(
2543 // cancel the timeout timer. 2552 TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS));
2544 render_view_host_->GetWidget()->increment_in_flight_event_count();
2545 render_view_host_->GetWidget()->StartHangMonitorTimeout(
2546 TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS),
2547 blink::WebInputEvent::Undefined);
2548 send_before_unload_start_time_ = base::TimeTicks::Now(); 2553 send_before_unload_start_time_ = base::TimeTicks::Now();
2549 Send(new FrameMsg_BeforeUnload(routing_id_, is_reload)); 2554 Send(new FrameMsg_BeforeUnload(routing_id_, is_reload));
2550 } 2555 }
2551 } 2556 }
2552 } 2557 }
2553 2558
2554 void RenderFrameHostImpl::SimulateBeforeUnloadAck() { 2559 void RenderFrameHostImpl::SimulateBeforeUnloadAck() {
2555 DCHECK(is_waiting_for_beforeunload_ack_); 2560 DCHECK(is_waiting_for_beforeunload_ack_);
2556 base::TimeTicks approx_renderer_start_time = send_before_unload_start_time_; 2561 base::TimeTicks approx_renderer_start_time = send_before_unload_start_time_;
2557 OnBeforeUnloadACK(true, approx_renderer_start_time, base::TimeTicks::Now()); 2562 OnBeforeUnloadACK(true, approx_renderer_start_time, base::TimeTicks::Now());
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
2602 void RenderFrameHostImpl::DeleteSurroundingTextInCodePoints(int before, 2607 void RenderFrameHostImpl::DeleteSurroundingTextInCodePoints(int before,
2603 int after) { 2608 int after) {
2604 Send(new InputMsg_DeleteSurroundingTextInCodePoints(routing_id_, before, 2609 Send(new InputMsg_DeleteSurroundingTextInCodePoints(routing_id_, before,
2605 after)); 2610 after));
2606 } 2611 }
2607 2612
2608 void RenderFrameHostImpl::JavaScriptDialogClosed( 2613 void RenderFrameHostImpl::JavaScriptDialogClosed(
2609 IPC::Message* reply_msg, 2614 IPC::Message* reply_msg,
2610 bool success, 2615 bool success,
2611 const base::string16& user_input, 2616 const base::string16& user_input,
2612 bool is_before_unload_dialog,
2613 bool dialog_was_suppressed) { 2617 bool dialog_was_suppressed) {
2614 GetProcess()->SetIgnoreInputEvents(false); 2618 GetProcess()->SetIgnoreInputEvents(false);
2615 2619
2616 // If we are executing as part of beforeunload event handling, we don't
2617 // want to use the regular hung_renderer_delay_ms_ if the user has agreed to
2618 // leave the current page. In this case, use the regular timeout value used
2619 // during the beforeunload handling.
2620 if (is_before_unload_dialog) {
2621 render_view_host_->GetWidget()->StartHangMonitorTimeout(
2622 success
2623 ? TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS)
2624 : render_view_host_->GetWidget()->hung_renderer_delay(),
2625 blink::WebInputEvent::Undefined);
2626 }
2627
2628 SendJavaScriptDialogReply(reply_msg, success, user_input); 2620 SendJavaScriptDialogReply(reply_msg, success, user_input);
2629 2621
2630 // If we are waiting for a beforeunload ack and the user has suppressed 2622 // If executing as part of beforeunload event handling, there may have been
2631 // messages, kill the tab immediately; a page that's spamming alerts in 2623 // timers stopped in this frame or a frame up in the frame hierarchy. Restart
2632 // onbeforeunload is presumably malicious, so there's no point in continuing 2624 // any timers that were stopped in OnRunBeforeUnloadConfirm().
2633 // to run its script and dragging out the process. This must be done after 2625 for (RenderFrameHostImpl* frame = this; frame; frame = frame->GetParent()) {
2634 // sending the reply since RenderView can't close correctly while waiting for 2626 if (frame->is_waiting_for_beforeunload_ack_) {
Charlie Reis 2017/03/14 17:19:29 Oh, I guess we can't remove this after all, since
Avi (use Gerrit) 2017/03/14 20:52:03 Yes.
Avi (use Gerrit) 2017/03/14 21:03:14 ** this belongs to this thread ** FYI, before Old
Charlie Reis 2017/03/15 17:22:02 Is that true with OOPIFs, though? In my example a
Avi (use Gerrit) 2017/03/15 19:57:38 Yes.
Charlie Reis 2017/03/15 21:34:20 Thanks-- that does explain it. And in particular,
2635 // a response. 2627 // If we are waiting for a beforeunload ack and the user has suppressed
2636 if (is_before_unload_dialog && dialog_was_suppressed) { 2628 // messages, kill the tab immediately. A page that's spamming is
2637 render_view_host_->GetWidget()->delegate()->RendererUnresponsive( 2629 // presumably malicious, so there's no point in continuing to run its
2638 render_view_host_->GetWidget()); 2630 // script and dragging out the process.
2631 if (dialog_was_suppressed) {
2632 frame->SimulateBeforeUnloadAck();
2633 } else {
2634 frame->beforeunload_timeout_->Start(
2635 TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS));
2636 }
2637 }
2639 } 2638 }
2640 } 2639 }
2641 2640
2642 void RenderFrameHostImpl::SendJavaScriptDialogReply( 2641 void RenderFrameHostImpl::SendJavaScriptDialogReply(
2643 IPC::Message* reply_msg, 2642 IPC::Message* reply_msg,
2644 bool success, 2643 bool success,
2645 const base::string16& user_input) { 2644 const base::string16& user_input) {
2646 FrameHostMsg_RunJavaScriptDialog::WriteReplyParams(reply_msg, success, 2645 FrameHostMsg_RunJavaScriptDialog::WriteReplyParams(reply_msg, success,
2647 user_input); 2646 user_input);
2648 Send(reply_msg); 2647 Send(reply_msg);
(...skipping 806 matching lines...) Expand 10 before | Expand all | Expand 10 after
3455 3454
3456 // There is no pending NavigationEntry in these cases, so pass 0 as the 3455 // There is no pending NavigationEntry in these cases, so pass 0 as the
3457 // pending_nav_entry_id. If the previous handle was a prematurely aborted 3456 // pending_nav_entry_id. If the previous handle was a prematurely aborted
3458 // navigation loaded via LoadDataWithBaseURL, propagate the entry id. 3457 // navigation loaded via LoadDataWithBaseURL, propagate the entry id.
3459 return NavigationHandleImpl::Create( 3458 return NavigationHandleImpl::Create(
3460 params.url, params.redirects, frame_tree_node_, is_renderer_initiated, 3459 params.url, params.redirects, frame_tree_node_, is_renderer_initiated,
3461 params.was_within_same_page, base::TimeTicks::Now(), 3460 params.was_within_same_page, base::TimeTicks::Now(),
3462 entry_id_for_data_nav, false); // started_from_context_menu 3461 entry_id_for_data_nav, false); // started_from_context_menu
3463 } 3462 }
3464 3463
3464 void RenderFrameHostImpl::BeforeUnloadTimeout() {
3465 if (render_view_host_->GetDelegate()->ShouldIgnoreUnresponsiveRenderer())
3466 return;
3467
3468 SimulateBeforeUnloadAck();
3469 }
3470
3465 } // namespace content 3471 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/frame_host/render_frame_host_impl.h ('k') | content/browser/renderer_host/render_widget_host_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698