render_frame_host_impl.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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 3// found in the LICENSE file. 4 5#include "content/browser/frame_host/render_frame_host_impl.h" 6 7#include "base/bind.h" 8#include "base/containers/hash_tables.h" 9#include "base/lazy_instance.h" 10#include "base/metrics/user_metrics_action.h" 11#include "content/browser/child_process_security_policy_impl.h" 12#include "content/browser/frame_host/cross_process_frame_connector.h" 13#include "content/browser/frame_host/cross_site_transferring_request.h" 14#include "content/browser/frame_host/frame_tree.h" 15#include "content/browser/frame_host/frame_tree_node.h" 16#include "content/browser/frame_host/navigator.h" 17#include "content/browser/frame_host/render_frame_host_delegate.h" 18#include "content/browser/renderer_host/input/input_router.h" 19#include "content/browser/renderer_host/input/timeout_monitor.h" 20#include "content/browser/renderer_host/render_view_host_impl.h" 21#include "content/common/desktop_notification_messages.h" 22#include "content/common/frame_messages.h" 23#include "content/common/input_messages.h" 24#include "content/common/inter_process_time_ticks_converter.h" 25#include "content/common/swapped_out_messages.h" 26#include "content/public/browser/browser_thread.h" 27#include "content/public/browser/content_browser_client.h" 28#include "content/public/browser/desktop_notification_delegate.h" 29#include "content/public/browser/render_process_host.h" 30#include "content/public/browser/render_widget_host_view.h" 31#include "content/public/browser/user_metrics.h" 32#include "content/public/common/url_constants.h" 33#include "content/public/common/url_utils.h" 34#include "url/gurl.h" 35 36using base::TimeDelta; 37 38namespace content { 39 40namespace { 41 42// The (process id, routing id) pair that identifies one RenderFrame. 43typedef std::pair<int32, int32> RenderFrameHostID; 44typedef base::hash_map<RenderFrameHostID, RenderFrameHostImpl*> 45 RoutingIDFrameMap; 46base::LazyInstance<RoutingIDFrameMap> g_routing_id_frame_map = 47 LAZY_INSTANCE_INITIALIZER; 48 49class DesktopNotificationDelegateImpl : public DesktopNotificationDelegate { 50 public: 51 DesktopNotificationDelegateImpl(RenderFrameHost* render_frame_host, 52 int notification_id) 53 : render_process_id_(render_frame_host->GetProcess()->GetID()), 54 render_frame_id_(render_frame_host->GetRoutingID()), 55 notification_id_(notification_id) {} 56 57 virtual ~DesktopNotificationDelegateImpl() {} 58 59 virtual void NotificationDisplayed() OVERRIDE { 60 RenderFrameHost* rfh = 61 RenderFrameHost::FromID(render_process_id_, render_frame_id_); 62 if (!rfh) 63 return; 64 65 rfh->Send(new DesktopNotificationMsg_PostDisplay( 66 rfh->GetRoutingID(), notification_id_)); 67 } 68 69 virtual void NotificationError() OVERRIDE { 70 RenderFrameHost* rfh = 71 RenderFrameHost::FromID(render_process_id_, render_frame_id_); 72 if (!rfh) 73 return; 74 75 rfh->Send(new DesktopNotificationMsg_PostError( 76 rfh->GetRoutingID(), notification_id_)); 77 delete this; 78 } 79 80 virtual void NotificationClosed(bool by_user) OVERRIDE { 81 RenderFrameHost* rfh = 82 RenderFrameHost::FromID(render_process_id_, render_frame_id_); 83 if (!rfh) 84 return; 85 86 rfh->Send(new DesktopNotificationMsg_PostClose( 87 rfh->GetRoutingID(), notification_id_, by_user)); 88 static_cast<RenderFrameHostImpl*>(rfh)->NotificationClosed( 89 notification_id_); 90 delete this; 91 } 92 93 virtual void NotificationClick() OVERRIDE { 94 RenderFrameHost* rfh = 95 RenderFrameHost::FromID(render_process_id_, render_frame_id_); 96 if (!rfh) 97 return; 98 99 rfh->Send(new DesktopNotificationMsg_PostClick( 100 rfh->GetRoutingID(), notification_id_)); 101 } 102 103 private: 104 int render_process_id_; 105 int render_frame_id_; 106 int notification_id_; 107}; 108 109} // namespace 110 111RenderFrameHost* RenderFrameHost::FromID(int render_process_id, 112 int render_frame_id) { 113 return RenderFrameHostImpl::FromID(render_process_id, render_frame_id); 114} 115 116// static 117RenderFrameHostImpl* RenderFrameHostImpl::FromID( 118 int process_id, int routing_id) { 119 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 120 RoutingIDFrameMap* frames = g_routing_id_frame_map.Pointer(); 121 RoutingIDFrameMap::iterator it = frames->find( 122 RenderFrameHostID(process_id, routing_id)); 123 return it == frames->end() ? NULL : it->second; 124} 125 126RenderFrameHostImpl::RenderFrameHostImpl( 127 RenderViewHostImpl* render_view_host, 128 RenderFrameHostDelegate* delegate, 129 FrameTree* frame_tree, 130 FrameTreeNode* frame_tree_node, 131 int routing_id, 132 bool is_swapped_out) 133 : render_view_host_(render_view_host), 134 delegate_(delegate), 135 cross_process_frame_connector_(NULL), 136 frame_tree_(frame_tree), 137 frame_tree_node_(frame_tree_node), 138 routing_id_(routing_id), 139 is_swapped_out_(is_swapped_out), 140 weak_ptr_factory_(this) { 141 frame_tree_->RegisterRenderFrameHost(this); 142 GetProcess()->AddRoute(routing_id_, this); 143 g_routing_id_frame_map.Get().insert(std::make_pair( 144 RenderFrameHostID(GetProcess()->GetID(), routing_id_), 145 this)); 146} 147 148RenderFrameHostImpl::~RenderFrameHostImpl() { 149 GetProcess()->RemoveRoute(routing_id_); 150 g_routing_id_frame_map.Get().erase( 151 RenderFrameHostID(GetProcess()->GetID(), routing_id_)); 152 if (delegate_) 153 delegate_->RenderFrameDeleted(this); 154 155 // Notify the FrameTree that this RFH is going away, allowing it to shut down 156 // the corresponding RenderViewHost if it is no longer needed. 157 frame_tree_->UnregisterRenderFrameHost(this); 158} 159 160int RenderFrameHostImpl::GetRoutingID() { 161 return routing_id_; 162} 163 164SiteInstance* RenderFrameHostImpl::GetSiteInstance() { 165 return render_view_host_->GetSiteInstance(); 166} 167 168RenderProcessHost* RenderFrameHostImpl::GetProcess() { 169 // TODO(nasko): This should return its own process, once we have working 170 // cross-process navigation for subframes. 171 return render_view_host_->GetProcess(); 172} 173 174RenderFrameHost* RenderFrameHostImpl::GetParent() { 175 FrameTreeNode* parent_node = frame_tree_node_->parent(); 176 if (!parent_node) 177 return NULL; 178 return parent_node->current_frame_host(); 179} 180 181const std::string& RenderFrameHostImpl::GetFrameName() { 182 return frame_tree_node_->frame_name(); 183} 184 185bool RenderFrameHostImpl::IsCrossProcessSubframe() { 186 FrameTreeNode* parent_node = frame_tree_node_->parent(); 187 if (!parent_node) 188 return false; 189 return GetSiteInstance() != 190 parent_node->current_frame_host()->GetSiteInstance(); 191} 192 193GURL RenderFrameHostImpl::GetLastCommittedURL() { 194 return frame_tree_node_->current_url(); 195} 196 197gfx::NativeView RenderFrameHostImpl::GetNativeView() { 198 RenderWidgetHostView* view = render_view_host_->GetView(); 199 if (!view) 200 return NULL; 201 return view->GetNativeView(); 202} 203 204void RenderFrameHostImpl::ExecuteJavaScript( 205 const base::string16& javascript) { 206 Send(new FrameMsg_JavaScriptExecuteRequest(routing_id_, 207 javascript, 208 0, false)); 209} 210 211void RenderFrameHostImpl::ExecuteJavaScript( 212 const base::string16& javascript, 213 const JavaScriptResultCallback& callback) { 214 static int next_id = 1; 215 int key = next_id++; 216 Send(new FrameMsg_JavaScriptExecuteRequest(routing_id_, 217 javascript, 218 key, true)); 219 javascript_callbacks_.insert(std::make_pair(key, callback)); 220} 221 222RenderViewHost* RenderFrameHostImpl::GetRenderViewHost() { 223 return render_view_host_; 224} 225 226bool RenderFrameHostImpl::Send(IPC::Message* message) { 227 if (IPC_MESSAGE_ID_CLASS(message->type()) == InputMsgStart) { 228 return render_view_host_->input_router()->SendInput( 229 make_scoped_ptr(message)); 230 } 231 232 return GetProcess()->Send(message); 233} 234 235bool RenderFrameHostImpl::OnMessageReceived(const IPC::Message &msg) { 236 // Filter out most IPC messages if this renderer is swapped out. 237 // We still want to handle certain ACKs to keep our state consistent. 238 // TODO(nasko): Only check RenderViewHost state, as this object's own state 239 // isn't yet properly updated. Transition this check once the swapped out 240 // state is correct in RenderFrameHost itself. 241 if (render_view_host_->IsSwappedOut()) { 242 if (!SwappedOutMessages::CanHandleWhileSwappedOut(msg)) { 243 // If this is a synchronous message and we decided not to handle it, 244 // we must send an error reply, or else the renderer will be stuck 245 // and won't respond to future requests. 246 if (msg.is_sync()) { 247 IPC::Message* reply = IPC::SyncMessage::GenerateReply(&msg); 248 reply->set_reply_error(); 249 Send(reply); 250 } 251 // Don't continue looking for someone to handle it. 252 return true; 253 } 254 } 255 256 if (delegate_->OnMessageReceived(this, msg)) 257 return true; 258 259 if (cross_process_frame_connector_ && 260 cross_process_frame_connector_->OnMessageReceived(msg)) 261 return true; 262 263 bool handled = true; 264 bool msg_is_ok = true; 265 IPC_BEGIN_MESSAGE_MAP_EX(RenderFrameHostImpl, msg, msg_is_ok) 266 IPC_MESSAGE_HANDLER(FrameHostMsg_AddMessageToConsole, OnAddMessageToConsole) 267 IPC_MESSAGE_HANDLER(FrameHostMsg_Detach, OnDetach) 268 IPC_MESSAGE_HANDLER(FrameHostMsg_FrameFocused, OnFrameFocused) 269 IPC_MESSAGE_HANDLER(FrameHostMsg_DidStartProvisionalLoadForFrame, 270 OnDidStartProvisionalLoadForFrame) 271 IPC_MESSAGE_HANDLER(FrameHostMsg_DidFailProvisionalLoadWithError, 272 OnDidFailProvisionalLoadWithError) 273 IPC_MESSAGE_HANDLER(FrameHostMsg_DidRedirectProvisionalLoad, 274 OnDidRedirectProvisionalLoad) 275 IPC_MESSAGE_HANDLER(FrameHostMsg_DidFailLoadWithError, 276 OnDidFailLoadWithError) 277 IPC_MESSAGE_HANDLER_GENERIC(FrameHostMsg_DidCommitProvisionalLoad, 278 OnNavigate(msg)) 279 IPC_MESSAGE_HANDLER(FrameHostMsg_DidStartLoading, OnDidStartLoading) 280 IPC_MESSAGE_HANDLER(FrameHostMsg_DidStopLoading, OnDidStopLoading) 281 IPC_MESSAGE_HANDLER(FrameHostMsg_OpenURL, OnOpenURL) 282 IPC_MESSAGE_HANDLER(FrameHostMsg_BeforeUnload_ACK, OnBeforeUnloadACK) 283 IPC_MESSAGE_HANDLER(FrameHostMsg_SwapOut_ACK, OnSwapOutACK) 284 IPC_MESSAGE_HANDLER(FrameHostMsg_ContextMenu, OnContextMenu) 285 IPC_MESSAGE_HANDLER(FrameHostMsg_JavaScriptExecuteResponse, 286 OnJavaScriptExecuteResponse) 287 IPC_MESSAGE_HANDLER_DELAY_REPLY(FrameHostMsg_RunJavaScriptMessage, 288 OnRunJavaScriptMessage) 289 IPC_MESSAGE_HANDLER_DELAY_REPLY(FrameHostMsg_RunBeforeUnloadConfirm, 290 OnRunBeforeUnloadConfirm) 291 IPC_MESSAGE_HANDLER(FrameHostMsg_DidAccessInitialDocument, 292 OnDidAccessInitialDocument) 293 IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_RequestPermission, 294 OnRequestDesktopNotificationPermission) 295 IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_Show, 296 OnShowDesktopNotification) 297 IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_Cancel, 298 OnCancelDesktopNotification) 299 IPC_END_MESSAGE_MAP_EX() 300 301 if (!msg_is_ok) { 302 // The message had a handler, but its de-serialization failed. 303 // Kill the renderer. 304 RecordAction(base::UserMetricsAction("BadMessageTerminate_RFH")); 305 GetProcess()->ReceivedBadMessage(); 306 } 307 308 return handled; 309} 310 311void RenderFrameHostImpl::Init() { 312 GetProcess()->ResumeRequestsForView(routing_id_); 313} 314 315void RenderFrameHostImpl::OnAddMessageToConsole( 316 int32 level, 317 const base::string16& message, 318 int32 line_no, 319 const base::string16& source_id) { 320 if (delegate_->AddMessageToConsole(level, message, line_no, source_id)) 321 return; 322 323 // Pass through log level only on WebUI pages to limit console spew. 324 int32 resolved_level = 325 HasWebUIScheme(delegate_->GetMainFrameLastCommittedURL()) ? level : 0; 326 327 if (resolved_level >= ::logging::GetMinLogLevel()) { 328 logging::LogMessage("CONSOLE", line_no, resolved_level).stream() << "\"" << 329 message << "\", source: " << source_id << " (" << line_no << ")"; 330 } 331} 332 333void RenderFrameHostImpl::OnCreateChildFrame(int new_routing_id, 334 const std::string& frame_name) { 335 RenderFrameHostImpl* new_frame = frame_tree_->AddFrame( 336 frame_tree_node_, new_routing_id, frame_name); 337 if (delegate_) 338 delegate_->RenderFrameCreated(new_frame); 339} 340 341void RenderFrameHostImpl::OnDetach() { 342 frame_tree_->RemoveFrame(frame_tree_node_); 343} 344 345void RenderFrameHostImpl::OnFrameFocused() { 346 frame_tree_->SetFocusedFrame(frame_tree_node_); 347} 348 349void RenderFrameHostImpl::OnOpenURL( 350 const FrameHostMsg_OpenURL_Params& params) { 351 GURL validated_url(params.url); 352 GetProcess()->FilterURL(false, &validated_url); 353 354 frame_tree_node_->navigator()->RequestOpenURL( 355 this, validated_url, params.referrer, params.disposition, 356 params.should_replace_current_entry, params.user_gesture); 357} 358 359void RenderFrameHostImpl::OnDidStartProvisionalLoadForFrame( 360 int parent_routing_id, 361 const GURL& url) { 362 frame_tree_node_->navigator()->DidStartProvisionalLoad( 363 this, parent_routing_id, url); 364} 365 366void RenderFrameHostImpl::OnDidFailProvisionalLoadWithError( 367 const FrameHostMsg_DidFailProvisionalLoadWithError_Params& params) { 368 frame_tree_node_->navigator()->DidFailProvisionalLoadWithError(this, params); 369} 370 371void RenderFrameHostImpl::OnDidFailLoadWithError( 372 const GURL& url, 373 int error_code, 374 const base::string16& error_description) { 375 GURL validated_url(url); 376 GetProcess()->FilterURL(false, &validated_url); 377 378 frame_tree_node_->navigator()->DidFailLoadWithError( 379 this, validated_url, error_code, error_description); 380} 381 382void RenderFrameHostImpl::OnDidRedirectProvisionalLoad( 383 int32 page_id, 384 const GURL& source_url, 385 const GURL& target_url) { 386 frame_tree_node_->navigator()->DidRedirectProvisionalLoad( 387 this, page_id, source_url, target_url); 388} 389 390// Called when the renderer navigates. For every frame loaded, we'll get this 391// notification containing parameters identifying the navigation. 392// 393// Subframes are identified by the page transition type. For subframes loaded 394// as part of a wider page load, the page_id will be the same as for the top 395// level frame. If the user explicitly requests a subframe navigation, we will 396// get a new page_id because we need to create a new navigation entry for that 397// action. 398void RenderFrameHostImpl::OnNavigate(const IPC::Message& msg) { 399 // Read the parameters out of the IPC message directly to avoid making another 400 // copy when we filter the URLs. 401 PickleIterator iter(msg); 402 FrameHostMsg_DidCommitProvisionalLoad_Params validated_params; 403 if (!IPC::ParamTraits<FrameHostMsg_DidCommitProvisionalLoad_Params>:: 404 Read(&msg, &iter, &validated_params)) 405 return; 406 407 // If we're waiting for a cross-site beforeunload ack from this renderer and 408 // we receive a Navigate message from the main frame, then the renderer was 409 // navigating already and sent it before hearing the ViewMsg_Stop message. 410 // We do not want to cancel the pending navigation in this case, since the 411 // old page will soon be stopped. Instead, treat this as a beforeunload ack 412 // to allow the pending navigation to continue. 413 if (render_view_host_->is_waiting_for_beforeunload_ack_ && 414 render_view_host_->unload_ack_is_for_cross_site_transition_ && 415 PageTransitionIsMainFrame(validated_params.transition)) { 416 OnBeforeUnloadACK(true, send_before_unload_start_time_, 417 base::TimeTicks::Now()); 418 return; 419 } 420 421 // If we're waiting for an unload ack from this renderer and we receive a 422 // Navigate message, then the renderer was navigating before it received the 423 // unload request. It will either respond to the unload request soon or our 424 // timer will expire. Either way, we should ignore this message, because we 425 // have already committed to closing this renderer. 426 if (render_view_host_->IsWaitingForUnloadACK()) 427 return; 428 429 RenderProcessHost* process = GetProcess(); 430 431 // Attempts to commit certain off-limits URL should be caught more strictly 432 // than our FilterURL checks below. If a renderer violates this policy, it 433 // should be killed. 434 if (!CanCommitURL(validated_params.url)) { 435 VLOG(1) << "Blocked URL " << validated_params.url.spec(); 436 validated_params.url = GURL(kAboutBlankURL); 437 RecordAction(base::UserMetricsAction("CanCommitURL_BlockedAndKilled")); 438 // Kills the process. 439 process->ReceivedBadMessage(); 440 } 441 442 // Without this check, an evil renderer can trick the browser into creating 443 // a navigation entry for a banned URL. If the user clicks the back button 444 // followed by the forward button (or clicks reload, or round-trips through 445 // session restore, etc), we'll think that the browser commanded the 446 // renderer to load the URL and grant the renderer the privileges to request 447 // the URL. To prevent this attack, we block the renderer from inserting 448 // banned URLs into the navigation controller in the first place. 449 process->FilterURL(false, &validated_params.url); 450 process->FilterURL(true, &validated_params.referrer.url); 451 for (std::vector<GURL>::iterator it(validated_params.redirects.begin()); 452 it != validated_params.redirects.end(); ++it) { 453 process->FilterURL(false, &(*it)); 454 } 455 process->FilterURL(true, &validated_params.searchable_form_url); 456 457 // Without this check, the renderer can trick the browser into using 458 // filenames it can't access in a future session restore. 459 if (!render_view_host_->CanAccessFilesOfPageState( 460 validated_params.page_state)) { 461 GetProcess()->ReceivedBadMessage(); 462 return; 463 } 464 465 frame_tree_node()->navigator()->DidNavigate(this, validated_params); 466} 467 468int RenderFrameHostImpl::GetEnabledBindings() { 469 return render_view_host_->GetEnabledBindings(); 470} 471 472void RenderFrameHostImpl::OnCrossSiteResponse( 473 const GlobalRequestID& global_request_id, 474 scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request, 475 const std::vector<GURL>& transfer_url_chain, 476 const Referrer& referrer, 477 PageTransition page_transition, 478 bool should_replace_current_entry) { 479 frame_tree_node_->render_manager()->OnCrossSiteResponse( 480 this, global_request_id, cross_site_transferring_request.Pass(), 481 transfer_url_chain, referrer, page_transition, 482 should_replace_current_entry); 483} 484 485void RenderFrameHostImpl::SwapOut() { 486 // TODO(creis): Move swapped out state to RFH. Until then, only update it 487 // when swapping out the main frame. 488 if (!GetParent()) { 489 // If this RenderViewHost is not in the default state, it must have already 490 // gone through this, therefore just return. 491 if (render_view_host_->rvh_state_ != RenderViewHostImpl::STATE_DEFAULT) 492 return; 493 494 render_view_host_->SetState( 495 RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK); 496 render_view_host_->unload_event_monitor_timeout_->Start( 497 base::TimeDelta::FromMilliseconds( 498 RenderViewHostImpl::kUnloadTimeoutMS)); 499 } 500 501 if (render_view_host_->IsRenderViewLive()) 502 Send(new FrameMsg_SwapOut(routing_id_)); 503 504 if (!GetParent()) 505 delegate_->SwappedOut(this); 506 507 // Allow the navigation to proceed. 508 frame_tree_node_->render_manager()->SwappedOut(this); 509} 510 511void RenderFrameHostImpl::OnDidStartLoading(bool to_different_document) { 512 delegate_->DidStartLoading(this, to_different_document); 513} 514 515void RenderFrameHostImpl::OnDidStopLoading() { 516 delegate_->DidStopLoading(this); 517} 518 519void RenderFrameHostImpl::OnBeforeUnloadACK( 520 bool proceed, 521 const base::TimeTicks& renderer_before_unload_start_time, 522 const base::TimeTicks& renderer_before_unload_end_time) { 523 // TODO(creis): Support properly beforeunload on subframes. For now just 524 // pretend that the handler ran and allowed the navigation to proceed. 525 if (GetParent()) { 526 render_view_host_->is_waiting_for_beforeunload_ack_ = false; 527 frame_tree_node_->render_manager()->OnBeforeUnloadACK( 528 render_view_host_->unload_ack_is_for_cross_site_transition_, proceed, 529 renderer_before_unload_end_time); 530 return; 531 } 532 533 render_view_host_->decrement_in_flight_event_count(); 534 render_view_host_->StopHangMonitorTimeout(); 535 // If this renderer navigated while the beforeunload request was in flight, we 536 // may have cleared this state in OnNavigate, in which case we can ignore 537 // this message. 538 // However renderer might also be swapped out but we still want to proceed 539 // with navigation, otherwise it would block future navigations. This can 540 // happen when pending cross-site navigation is canceled by a second one just 541 // before OnNavigate while current RVH is waiting for commit but second 542 // navigation is started from the beginning. 543 if (!render_view_host_->is_waiting_for_beforeunload_ack_) { 544 return; 545 } 546 547 render_view_host_->is_waiting_for_beforeunload_ack_ = false; 548 549 base::TimeTicks before_unload_end_time; 550 if (!send_before_unload_start_time_.is_null() && 551 !renderer_before_unload_start_time.is_null() && 552 !renderer_before_unload_end_time.is_null()) { 553 // When passing TimeTicks across process boundaries, we need to compensate 554 // for any skew between the processes. Here we are converting the 555 // renderer's notion of before_unload_end_time to TimeTicks in the browser 556 // process. See comments in inter_process_time_ticks_converter.h for more. 557 InterProcessTimeTicksConverter converter( 558 LocalTimeTicks::FromTimeTicks(send_before_unload_start_time_), 559 LocalTimeTicks::FromTimeTicks(base::TimeTicks::Now()), 560 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_start_time), 561 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_end_time)); 562 LocalTimeTicks browser_before_unload_end_time = 563 converter.ToLocalTimeTicks( 564 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_end_time)); 565 before_unload_end_time = browser_before_unload_end_time.ToTimeTicks(); 566 } 567 frame_tree_node_->render_manager()->OnBeforeUnloadACK( 568 render_view_host_->unload_ack_is_for_cross_site_transition_, proceed, 569 before_unload_end_time); 570 571 // If canceled, notify the delegate to cancel its pending navigation entry. 572 if (!proceed) 573 render_view_host_->GetDelegate()->DidCancelLoading(); 574} 575 576void RenderFrameHostImpl::OnSwapOutACK() { 577 OnSwappedOut(false); 578} 579 580void RenderFrameHostImpl::OnSwappedOut(bool timed_out) { 581 // For now, we only need to update the RVH state machine for top-level swaps. 582 // Subframe swaps (in --site-per-process) can just continue via RFHM. 583 if (!GetParent()) 584 render_view_host_->OnSwappedOut(timed_out); 585 else 586 frame_tree_node_->render_manager()->SwappedOut(this); 587} 588 589void RenderFrameHostImpl::OnContextMenu(const ContextMenuParams& params) { 590 // Validate the URLs in |params|. If the renderer can't request the URLs 591 // directly, don't show them in the context menu. 592 ContextMenuParams validated_params(params); 593 RenderProcessHost* process = GetProcess(); 594 595 // We don't validate |unfiltered_link_url| so that this field can be used 596 // when users want to copy the original link URL. 597 process->FilterURL(true, &validated_params.link_url); 598 process->FilterURL(true, &validated_params.src_url); 599 process->FilterURL(false, &validated_params.page_url); 600 process->FilterURL(true, &validated_params.frame_url); 601 602 delegate_->ShowContextMenu(this, validated_params); 603} 604 605void RenderFrameHostImpl::OnJavaScriptExecuteResponse( 606 int id, const base::ListValue& result) { 607 const base::Value* result_value; 608 if (!result.Get(0, &result_value)) { 609 // Programming error or rogue renderer. 610 NOTREACHED() << "Got bad arguments for OnJavaScriptExecuteResponse"; 611 return; 612 } 613 614 std::map<int, JavaScriptResultCallback>::iterator it = 615 javascript_callbacks_.find(id); 616 if (it != javascript_callbacks_.end()) { 617 it->second.Run(result_value); 618 javascript_callbacks_.erase(it); 619 } else { 620 NOTREACHED() << "Received script response for unknown request"; 621 } 622} 623 624void RenderFrameHostImpl::OnRunJavaScriptMessage( 625 const base::string16& message, 626 const base::string16& default_prompt, 627 const GURL& frame_url, 628 JavaScriptMessageType type, 629 IPC::Message* reply_msg) { 630 // While a JS message dialog is showing, tabs in the same process shouldn't 631 // process input events. 632 GetProcess()->SetIgnoreInputEvents(true); 633 render_view_host_->StopHangMonitorTimeout(); 634 delegate_->RunJavaScriptMessage(this, message, default_prompt, 635 frame_url, type, reply_msg); 636} 637 638void RenderFrameHostImpl::OnRunBeforeUnloadConfirm( 639 const GURL& frame_url, 640 const base::string16& message, 641 bool is_reload, 642 IPC::Message* reply_msg) { 643 // While a JS before unload dialog is showing, tabs in the same process 644 // shouldn't process input events. 645 GetProcess()->SetIgnoreInputEvents(true); 646 render_view_host_->StopHangMonitorTimeout(); 647 delegate_->RunBeforeUnloadConfirm(this, message, is_reload, reply_msg); 648} 649 650void RenderFrameHostImpl::OnRequestDesktopNotificationPermission( 651 const GURL& source_origin, int callback_context) { 652 base::Closure done_callback = base::Bind( 653 &RenderFrameHostImpl::DesktopNotificationPermissionRequestDone, 654 weak_ptr_factory_.GetWeakPtr(), callback_context); 655 GetContentClient()->browser()->RequestDesktopNotificationPermission( 656 source_origin, this, done_callback); 657} 658 659void RenderFrameHostImpl::OnShowDesktopNotification( 660 int notification_id, 661 const ShowDesktopNotificationHostMsgParams& params) { 662 base::Closure cancel_callback; 663 GetContentClient()->browser()->ShowDesktopNotification( 664 params, this, 665 new DesktopNotificationDelegateImpl(this, notification_id), 666 &cancel_callback); 667 cancel_notification_callbacks_[notification_id] = cancel_callback; 668} 669 670void RenderFrameHostImpl::OnCancelDesktopNotification(int notification_id) { 671 if (!cancel_notification_callbacks_.count(notification_id)) { 672 NOTREACHED(); 673 return; 674 } 675 cancel_notification_callbacks_[notification_id].Run(); 676 cancel_notification_callbacks_.erase(notification_id); 677} 678 679void RenderFrameHostImpl::OnDidAccessInitialDocument() { 680 delegate_->DidAccessInitialDocument(); 681} 682 683void RenderFrameHostImpl::SetPendingShutdown(const base::Closure& on_swap_out) { 684 render_view_host_->SetPendingShutdown(on_swap_out); 685} 686 687bool RenderFrameHostImpl::CanCommitURL(const GURL& url) { 688 // TODO(creis): We should also check for WebUI pages here. Also, when the 689 // out-of-process iframes implementation is ready, we should check for 690 // cross-site URLs that are not allowed to commit in this process. 691 692 // Give the client a chance to disallow URLs from committing. 693 return GetContentClient()->browser()->CanCommitURL(GetProcess(), url); 694} 695 696void RenderFrameHostImpl::Navigate(const FrameMsg_Navigate_Params& params) { 697 TRACE_EVENT0("frame_host", "RenderFrameHostImpl::Navigate"); 698 // Browser plugin guests are not allowed to navigate outside web-safe schemes, 699 // so do not grant them the ability to request additional URLs. 700 if (!GetProcess()->IsGuest()) { 701 ChildProcessSecurityPolicyImpl::GetInstance()->GrantRequestURL( 702 GetProcess()->GetID(), params.url); 703 if (params.url.SchemeIs(kDataScheme) && 704 params.base_url_for_data_url.SchemeIs(kFileScheme)) { 705 // If 'data:' is used, and we have a 'file:' base url, grant access to 706 // local files. 707 ChildProcessSecurityPolicyImpl::GetInstance()->GrantRequestURL( 708 GetProcess()->GetID(), params.base_url_for_data_url); 709 } 710 } 711 712 // Only send the message if we aren't suspended at the start of a cross-site 713 // request. 714 if (render_view_host_->navigations_suspended_) { 715 // Shouldn't be possible to have a second navigation while suspended, since 716 // navigations will only be suspended during a cross-site request. If a 717 // second navigation occurs, RenderFrameHostManager will cancel this pending 718 // RFH and create a new pending RFH. 719 DCHECK(!render_view_host_->suspended_nav_params_.get()); 720 render_view_host_->suspended_nav_params_.reset( 721 new FrameMsg_Navigate_Params(params)); 722 } else { 723 // Get back to a clean state, in case we start a new navigation without 724 // completing a RVH swap or unload handler. 725 render_view_host_->SetState(RenderViewHostImpl::STATE_DEFAULT); 726 727 Send(new FrameMsg_Navigate(routing_id_, params)); 728 } 729 730 // Force the throbber to start. We do this because Blink's "started 731 // loading" message will be received asynchronously from the UI of the 732 // browser. But we want to keep the throbber in sync with what's happening 733 // in the UI. For example, we want to start throbbing immediately when the 734 // user naivgates even if the renderer is delayed. There is also an issue 735 // with the throbber starting because the WebUI (which controls whether the 736 // favicon is displayed) happens synchronously. If the start loading 737 // messages was asynchronous, then the default favicon would flash in. 738 // 739 // Blink doesn't send throb notifications for JavaScript URLs, so we 740 // don't want to either. 741 if (!params.url.SchemeIs(kJavaScriptScheme)) 742 delegate_->DidStartLoading(this, true); 743} 744 745void RenderFrameHostImpl::NavigateToURL(const GURL& url) { 746 FrameMsg_Navigate_Params params; 747 params.page_id = -1; 748 params.pending_history_list_offset = -1; 749 params.current_history_list_offset = -1; 750 params.current_history_list_length = 0; 751 params.url = url; 752 params.transition = PAGE_TRANSITION_LINK; 753 params.navigation_type = FrameMsg_Navigate_Type::NORMAL; 754 Navigate(params); 755} 756 757void RenderFrameHostImpl::DispatchBeforeUnload(bool for_cross_site_transition) { 758 // TODO(creis): Support subframes. 759 if (!render_view_host_->IsRenderViewLive() || GetParent()) { 760 // We don't have a live renderer, so just skip running beforeunload. 761 render_view_host_->is_waiting_for_beforeunload_ack_ = true; 762 render_view_host_->unload_ack_is_for_cross_site_transition_ = 763 for_cross_site_transition; 764 base::TimeTicks now = base::TimeTicks::Now(); 765 OnBeforeUnloadACK(true, now, now); 766 return; 767 } 768 769 // This may be called more than once (if the user clicks the tab close button 770 // several times, or if she clicks the tab close button then the browser close 771 // button), and we only send the message once. 772 if (render_view_host_->is_waiting_for_beforeunload_ack_) { 773 // Some of our close messages could be for the tab, others for cross-site 774 // transitions. We always want to think it's for closing the tab if any 775 // of the messages were, since otherwise it might be impossible to close 776 // (if there was a cross-site "close" request pending when the user clicked 777 // the close button). We want to keep the "for cross site" flag only if 778 // both the old and the new ones are also for cross site. 779 render_view_host_->unload_ack_is_for_cross_site_transition_ = 780 render_view_host_->unload_ack_is_for_cross_site_transition_ && 781 for_cross_site_transition; 782 } else { 783 // Start the hang monitor in case the renderer hangs in the beforeunload 784 // handler. 785 render_view_host_->is_waiting_for_beforeunload_ack_ = true; 786 render_view_host_->unload_ack_is_for_cross_site_transition_ = 787 for_cross_site_transition; 788 // Increment the in-flight event count, to ensure that input events won't 789 // cancel the timeout timer. 790 render_view_host_->increment_in_flight_event_count(); 791 render_view_host_->StartHangMonitorTimeout( 792 TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS)); 793 send_before_unload_start_time_ = base::TimeTicks::Now(); 794 Send(new FrameMsg_BeforeUnload(routing_id_)); 795 } 796} 797 798void RenderFrameHostImpl::ExtendSelectionAndDelete(size_t before, 799 size_t after) { 800 Send(new FrameMsg_ExtendSelectionAndDelete(routing_id_, before, after)); 801} 802 803void RenderFrameHostImpl::JavaScriptDialogClosed( 804 IPC::Message* reply_msg, 805 bool success, 806 const base::string16& user_input, 807 bool dialog_was_suppressed) { 808 GetProcess()->SetIgnoreInputEvents(false); 809 bool is_waiting = render_view_host_->is_waiting_for_beforeunload_ack() || 810 render_view_host_->IsWaitingForUnloadACK(); 811 812 // If we are executing as part of (before)unload event handling, we don't 813 // want to use the regular hung_renderer_delay_ms_ if the user has agreed to 814 // leave the current page. In this case, use the regular timeout value used 815 // during the (before)unload handling. 816 if (is_waiting) { 817 render_view_host_->StartHangMonitorTimeout(TimeDelta::FromMilliseconds( 818 success ? RenderViewHostImpl::kUnloadTimeoutMS 819 : render_view_host_->hung_renderer_delay_ms_)); 820 } 821 822 FrameHostMsg_RunJavaScriptMessage::WriteReplyParams(reply_msg, 823 success, user_input); 824 Send(reply_msg); 825 826 // If we are waiting for an unload or beforeunload ack and the user has 827 // suppressed messages, kill the tab immediately; a page that's spamming 828 // alerts in onbeforeunload is presumably malicious, so there's no point in 829 // continuing to run its script and dragging out the process. 830 // This must be done after sending the reply since RenderView can't close 831 // correctly while waiting for a response. 832 if (is_waiting && dialog_was_suppressed) 833 render_view_host_->delegate_->RendererUnresponsive( 834 render_view_host_, 835 render_view_host_->is_waiting_for_beforeunload_ack(), 836 render_view_host_->IsWaitingForUnloadACK()); 837} 838 839void RenderFrameHostImpl::NotificationClosed(int notification_id) { 840 cancel_notification_callbacks_.erase(notification_id); 841} 842 843void RenderFrameHostImpl::DesktopNotificationPermissionRequestDone( 844 int callback_context) { 845 Send(new DesktopNotificationMsg_PermissionRequestDone( 846 routing_id_, callback_context)); 847} 848 849} // namespace content 850