render_frame_host_impl.cc revision 0529e5d033099cbfc42635f6f6183833b09dff6e
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(DesktopNotificationHostMsg_RequestPermission, 292 OnRequestDesktopNotificationPermission) 293 IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_Show, 294 OnShowDesktopNotification) 295 IPC_MESSAGE_HANDLER(DesktopNotificationHostMsg_Cancel, 296 OnCancelDesktopNotification) 297 IPC_END_MESSAGE_MAP_EX() 298 299 if (!msg_is_ok) { 300 // The message had a handler, but its de-serialization failed. 301 // Kill the renderer. 302 RecordAction(base::UserMetricsAction("BadMessageTerminate_RFH")); 303 GetProcess()->ReceivedBadMessage(); 304 } 305 306 return handled; 307} 308 309void RenderFrameHostImpl::Init() { 310 GetProcess()->ResumeRequestsForView(routing_id_); 311} 312 313void RenderFrameHostImpl::OnAddMessageToConsole( 314 int32 level, 315 const base::string16& message, 316 int32 line_no, 317 const base::string16& source_id) { 318 if (delegate_->AddMessageToConsole(level, message, line_no, source_id)) 319 return; 320 321 // Pass through log level only on WebUI pages to limit console spew. 322 int32 resolved_level = 323 HasWebUIScheme(delegate_->GetMainFrameLastCommittedURL()) ? level : 0; 324 325 if (resolved_level >= ::logging::GetMinLogLevel()) { 326 logging::LogMessage("CONSOLE", line_no, resolved_level).stream() << "\"" << 327 message << "\", source: " << source_id << " (" << line_no << ")"; 328 } 329} 330 331void RenderFrameHostImpl::OnCreateChildFrame(int new_routing_id, 332 const std::string& frame_name) { 333 RenderFrameHostImpl* new_frame = frame_tree_->AddFrame( 334 frame_tree_node_, new_routing_id, frame_name); 335 if (delegate_) 336 delegate_->RenderFrameCreated(new_frame); 337} 338 339void RenderFrameHostImpl::OnDetach() { 340 frame_tree_->RemoveFrame(frame_tree_node_); 341} 342 343void RenderFrameHostImpl::OnFrameFocused() { 344 frame_tree_->SetFocusedFrame(frame_tree_node_); 345} 346 347void RenderFrameHostImpl::OnOpenURL( 348 const FrameHostMsg_OpenURL_Params& params) { 349 GURL validated_url(params.url); 350 GetProcess()->FilterURL(false, &validated_url); 351 352 frame_tree_node_->navigator()->RequestOpenURL( 353 this, validated_url, params.referrer, params.disposition, 354 params.should_replace_current_entry, params.user_gesture); 355} 356 357void RenderFrameHostImpl::OnDidStartProvisionalLoadForFrame( 358 int parent_routing_id, 359 const GURL& url) { 360 frame_tree_node_->navigator()->DidStartProvisionalLoad( 361 this, parent_routing_id, url); 362} 363 364void RenderFrameHostImpl::OnDidFailProvisionalLoadWithError( 365 const FrameHostMsg_DidFailProvisionalLoadWithError_Params& params) { 366 frame_tree_node_->navigator()->DidFailProvisionalLoadWithError(this, params); 367} 368 369void RenderFrameHostImpl::OnDidFailLoadWithError( 370 const GURL& url, 371 int error_code, 372 const base::string16& error_description) { 373 GURL validated_url(url); 374 GetProcess()->FilterURL(false, &validated_url); 375 376 frame_tree_node_->navigator()->DidFailLoadWithError( 377 this, validated_url, error_code, error_description); 378} 379 380void RenderFrameHostImpl::OnDidRedirectProvisionalLoad( 381 int32 page_id, 382 const GURL& source_url, 383 const GURL& target_url) { 384 frame_tree_node_->navigator()->DidRedirectProvisionalLoad( 385 this, page_id, source_url, target_url); 386} 387 388// Called when the renderer navigates. For every frame loaded, we'll get this 389// notification containing parameters identifying the navigation. 390// 391// Subframes are identified by the page transition type. For subframes loaded 392// as part of a wider page load, the page_id will be the same as for the top 393// level frame. If the user explicitly requests a subframe navigation, we will 394// get a new page_id because we need to create a new navigation entry for that 395// action. 396void RenderFrameHostImpl::OnNavigate(const IPC::Message& msg) { 397 // Read the parameters out of the IPC message directly to avoid making another 398 // copy when we filter the URLs. 399 PickleIterator iter(msg); 400 FrameHostMsg_DidCommitProvisionalLoad_Params validated_params; 401 if (!IPC::ParamTraits<FrameHostMsg_DidCommitProvisionalLoad_Params>:: 402 Read(&msg, &iter, &validated_params)) 403 return; 404 405 // If we're waiting for a cross-site beforeunload ack from this renderer and 406 // we receive a Navigate message from the main frame, then the renderer was 407 // navigating already and sent it before hearing the ViewMsg_Stop message. 408 // We do not want to cancel the pending navigation in this case, since the 409 // old page will soon be stopped. Instead, treat this as a beforeunload ack 410 // to allow the pending navigation to continue. 411 if (render_view_host_->is_waiting_for_beforeunload_ack_ && 412 render_view_host_->unload_ack_is_for_cross_site_transition_ && 413 PageTransitionIsMainFrame(validated_params.transition)) { 414 OnBeforeUnloadACK(true, send_before_unload_start_time_, 415 base::TimeTicks::Now()); 416 return; 417 } 418 419 // If we're waiting for an unload ack from this renderer and we receive a 420 // Navigate message, then the renderer was navigating before it received the 421 // unload request. It will either respond to the unload request soon or our 422 // timer will expire. Either way, we should ignore this message, because we 423 // have already committed to closing this renderer. 424 if (render_view_host_->IsWaitingForUnloadACK()) 425 return; 426 427 RenderProcessHost* process = GetProcess(); 428 429 // Attempts to commit certain off-limits URL should be caught more strictly 430 // than our FilterURL checks below. If a renderer violates this policy, it 431 // should be killed. 432 if (!CanCommitURL(validated_params.url)) { 433 VLOG(1) << "Blocked URL " << validated_params.url.spec(); 434 validated_params.url = GURL(kAboutBlankURL); 435 RecordAction(base::UserMetricsAction("CanCommitURL_BlockedAndKilled")); 436 // Kills the process. 437 process->ReceivedBadMessage(); 438 } 439 440 // Now that something has committed, we don't need to track whether the 441 // initial page has been accessed. 442 render_view_host_->has_accessed_initial_document_ = false; 443 444 // Without this check, an evil renderer can trick the browser into creating 445 // a navigation entry for a banned URL. If the user clicks the back button 446 // followed by the forward button (or clicks reload, or round-trips through 447 // session restore, etc), we'll think that the browser commanded the 448 // renderer to load the URL and grant the renderer the privileges to request 449 // the URL. To prevent this attack, we block the renderer from inserting 450 // banned URLs into the navigation controller in the first place. 451 process->FilterURL(false, &validated_params.url); 452 process->FilterURL(true, &validated_params.referrer.url); 453 for (std::vector<GURL>::iterator it(validated_params.redirects.begin()); 454 it != validated_params.redirects.end(); ++it) { 455 process->FilterURL(false, &(*it)); 456 } 457 process->FilterURL(true, &validated_params.searchable_form_url); 458 459 // Without this check, the renderer can trick the browser into using 460 // filenames it can't access in a future session restore. 461 if (!render_view_host_->CanAccessFilesOfPageState( 462 validated_params.page_state)) { 463 GetProcess()->ReceivedBadMessage(); 464 return; 465 } 466 467 frame_tree_node()->navigator()->DidNavigate(this, validated_params); 468} 469 470int RenderFrameHostImpl::GetEnabledBindings() { 471 return render_view_host_->GetEnabledBindings(); 472} 473 474void RenderFrameHostImpl::OnCrossSiteResponse( 475 const GlobalRequestID& global_request_id, 476 scoped_ptr<CrossSiteTransferringRequest> cross_site_transferring_request, 477 const std::vector<GURL>& transfer_url_chain, 478 const Referrer& referrer, 479 PageTransition page_transition, 480 bool should_replace_current_entry) { 481 frame_tree_node_->render_manager()->OnCrossSiteResponse( 482 this, global_request_id, cross_site_transferring_request.Pass(), 483 transfer_url_chain, referrer, page_transition, 484 should_replace_current_entry); 485} 486 487void RenderFrameHostImpl::SwapOut() { 488 // TODO(creis): Move swapped out state to RFH. Until then, only update it 489 // when swapping out the main frame. 490 if (!GetParent()) { 491 // If this RenderViewHost is not in the default state, it must have already 492 // gone through this, therefore just return. 493 if (render_view_host_->rvh_state_ != RenderViewHostImpl::STATE_DEFAULT) 494 return; 495 496 render_view_host_->SetState( 497 RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK); 498 render_view_host_->unload_event_monitor_timeout_->Start( 499 base::TimeDelta::FromMilliseconds( 500 RenderViewHostImpl::kUnloadTimeoutMS)); 501 } 502 503 if (render_view_host_->IsRenderViewLive()) 504 Send(new FrameMsg_SwapOut(routing_id_)); 505 506 if (!GetParent()) 507 delegate_->SwappedOut(this); 508 509 // Allow the navigation to proceed. 510 frame_tree_node_->render_manager()->SwappedOut(this); 511} 512 513void RenderFrameHostImpl::OnDidStartLoading(bool to_different_document) { 514 delegate_->DidStartLoading(this, to_different_document); 515} 516 517void RenderFrameHostImpl::OnDidStopLoading() { 518 delegate_->DidStopLoading(this); 519} 520 521void RenderFrameHostImpl::OnBeforeUnloadACK( 522 bool proceed, 523 const base::TimeTicks& renderer_before_unload_start_time, 524 const base::TimeTicks& renderer_before_unload_end_time) { 525 // TODO(creis): Support properly beforeunload on subframes. For now just 526 // pretend that the handler ran and allowed the navigation to proceed. 527 if (GetParent()) { 528 render_view_host_->is_waiting_for_beforeunload_ack_ = false; 529 frame_tree_node_->render_manager()->OnBeforeUnloadACK( 530 render_view_host_->unload_ack_is_for_cross_site_transition_, proceed, 531 renderer_before_unload_end_time); 532 return; 533 } 534 535 render_view_host_->decrement_in_flight_event_count(); 536 render_view_host_->StopHangMonitorTimeout(); 537 // If this renderer navigated while the beforeunload request was in flight, we 538 // may have cleared this state in OnNavigate, in which case we can ignore 539 // this message. 540 // However renderer might also be swapped out but we still want to proceed 541 // with navigation, otherwise it would block future navigations. This can 542 // happen when pending cross-site navigation is canceled by a second one just 543 // before OnNavigate while current RVH is waiting for commit but second 544 // navigation is started from the beginning. 545 if (!render_view_host_->is_waiting_for_beforeunload_ack_) { 546 return; 547 } 548 549 render_view_host_->is_waiting_for_beforeunload_ack_ = false; 550 551 base::TimeTicks before_unload_end_time; 552 if (!send_before_unload_start_time_.is_null() && 553 !renderer_before_unload_start_time.is_null() && 554 !renderer_before_unload_end_time.is_null()) { 555 // When passing TimeTicks across process boundaries, we need to compensate 556 // for any skew between the processes. Here we are converting the 557 // renderer's notion of before_unload_end_time to TimeTicks in the browser 558 // process. See comments in inter_process_time_ticks_converter.h for more. 559 InterProcessTimeTicksConverter converter( 560 LocalTimeTicks::FromTimeTicks(send_before_unload_start_time_), 561 LocalTimeTicks::FromTimeTicks(base::TimeTicks::Now()), 562 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_start_time), 563 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_end_time)); 564 LocalTimeTicks browser_before_unload_end_time = 565 converter.ToLocalTimeTicks( 566 RemoteTimeTicks::FromTimeTicks(renderer_before_unload_end_time)); 567 before_unload_end_time = browser_before_unload_end_time.ToTimeTicks(); 568 } 569 frame_tree_node_->render_manager()->OnBeforeUnloadACK( 570 render_view_host_->unload_ack_is_for_cross_site_transition_, proceed, 571 before_unload_end_time); 572 573 // If canceled, notify the delegate to cancel its pending navigation entry. 574 if (!proceed) 575 render_view_host_->GetDelegate()->DidCancelLoading(); 576} 577 578void RenderFrameHostImpl::OnSwapOutACK() { 579 OnSwappedOut(false); 580} 581 582void RenderFrameHostImpl::OnSwappedOut(bool timed_out) { 583 // For now, we only need to update the RVH state machine for top-level swaps. 584 // Subframe swaps (in --site-per-process) can just continue via RFHM. 585 if (!GetParent()) 586 render_view_host_->OnSwappedOut(timed_out); 587 else 588 frame_tree_node_->render_manager()->SwappedOut(this); 589} 590 591void RenderFrameHostImpl::OnContextMenu(const ContextMenuParams& params) { 592 // Validate the URLs in |params|. If the renderer can't request the URLs 593 // directly, don't show them in the context menu. 594 ContextMenuParams validated_params(params); 595 RenderProcessHost* process = GetProcess(); 596 597 // We don't validate |unfiltered_link_url| so that this field can be used 598 // when users want to copy the original link URL. 599 process->FilterURL(true, &validated_params.link_url); 600 process->FilterURL(true, &validated_params.src_url); 601 process->FilterURL(false, &validated_params.page_url); 602 process->FilterURL(true, &validated_params.frame_url); 603 604 delegate_->ShowContextMenu(this, validated_params); 605} 606 607void RenderFrameHostImpl::OnJavaScriptExecuteResponse( 608 int id, const base::ListValue& result) { 609 const base::Value* result_value; 610 if (!result.Get(0, &result_value)) { 611 // Programming error or rogue renderer. 612 NOTREACHED() << "Got bad arguments for OnJavaScriptExecuteResponse"; 613 return; 614 } 615 616 std::map<int, JavaScriptResultCallback>::iterator it = 617 javascript_callbacks_.find(id); 618 if (it != javascript_callbacks_.end()) { 619 it->second.Run(result_value); 620 javascript_callbacks_.erase(it); 621 } else { 622 NOTREACHED() << "Received script response for unknown request"; 623 } 624} 625 626void RenderFrameHostImpl::OnRunJavaScriptMessage( 627 const base::string16& message, 628 const base::string16& default_prompt, 629 const GURL& frame_url, 630 JavaScriptMessageType type, 631 IPC::Message* reply_msg) { 632 // While a JS message dialog is showing, tabs in the same process shouldn't 633 // process input events. 634 GetProcess()->SetIgnoreInputEvents(true); 635 render_view_host_->StopHangMonitorTimeout(); 636 delegate_->RunJavaScriptMessage(this, message, default_prompt, 637 frame_url, type, reply_msg); 638} 639 640void RenderFrameHostImpl::OnRunBeforeUnloadConfirm( 641 const GURL& frame_url, 642 const base::string16& message, 643 bool is_reload, 644 IPC::Message* reply_msg) { 645 // While a JS before unload dialog is showing, tabs in the same process 646 // shouldn't process input events. 647 GetProcess()->SetIgnoreInputEvents(true); 648 render_view_host_->StopHangMonitorTimeout(); 649 delegate_->RunBeforeUnloadConfirm(this, message, is_reload, reply_msg); 650} 651 652void RenderFrameHostImpl::OnRequestDesktopNotificationPermission( 653 const GURL& source_origin, int callback_context) { 654 base::Closure done_callback = base::Bind( 655 &RenderFrameHostImpl::DesktopNotificationPermissionRequestDone, 656 weak_ptr_factory_.GetWeakPtr(), callback_context); 657 GetContentClient()->browser()->RequestDesktopNotificationPermission( 658 source_origin, this, done_callback); 659} 660 661void RenderFrameHostImpl::OnShowDesktopNotification( 662 int notification_id, 663 const ShowDesktopNotificationHostMsgParams& params) { 664 base::Closure cancel_callback; 665 GetContentClient()->browser()->ShowDesktopNotification( 666 params, this, 667 new DesktopNotificationDelegateImpl(this, notification_id), 668 &cancel_callback); 669 cancel_notification_callbacks_[notification_id] = cancel_callback; 670} 671 672void RenderFrameHostImpl::OnCancelDesktopNotification(int notification_id) { 673 if (!cancel_notification_callbacks_.count(notification_id)) { 674 NOTREACHED(); 675 return; 676 } 677 cancel_notification_callbacks_[notification_id].Run(); 678 cancel_notification_callbacks_.erase(notification_id); 679} 680 681void RenderFrameHostImpl::SetPendingShutdown(const base::Closure& on_swap_out) { 682 render_view_host_->SetPendingShutdown(on_swap_out); 683} 684 685bool RenderFrameHostImpl::CanCommitURL(const GURL& url) { 686 // TODO(creis): We should also check for WebUI pages here. Also, when the 687 // out-of-process iframes implementation is ready, we should check for 688 // cross-site URLs that are not allowed to commit in this process. 689 690 // Give the client a chance to disallow URLs from committing. 691 return GetContentClient()->browser()->CanCommitURL(GetProcess(), url); 692} 693 694void RenderFrameHostImpl::Navigate(const FrameMsg_Navigate_Params& params) { 695 TRACE_EVENT0("frame_host", "RenderFrameHostImpl::Navigate"); 696 // Browser plugin guests are not allowed to navigate outside web-safe schemes, 697 // so do not grant them the ability to request additional URLs. 698 if (!GetProcess()->IsGuest()) { 699 ChildProcessSecurityPolicyImpl::GetInstance()->GrantRequestURL( 700 GetProcess()->GetID(), params.url); 701 if (params.url.SchemeIs(kDataScheme) && 702 params.base_url_for_data_url.SchemeIs(kFileScheme)) { 703 // If 'data:' is used, and we have a 'file:' base url, grant access to 704 // local files. 705 ChildProcessSecurityPolicyImpl::GetInstance()->GrantRequestURL( 706 GetProcess()->GetID(), params.base_url_for_data_url); 707 } 708 } 709 710 // Only send the message if we aren't suspended at the start of a cross-site 711 // request. 712 if (render_view_host_->navigations_suspended_) { 713 // Shouldn't be possible to have a second navigation while suspended, since 714 // navigations will only be suspended during a cross-site request. If a 715 // second navigation occurs, RenderFrameHostManager will cancel this pending 716 // RFH and create a new pending RFH. 717 DCHECK(!render_view_host_->suspended_nav_params_.get()); 718 render_view_host_->suspended_nav_params_.reset( 719 new FrameMsg_Navigate_Params(params)); 720 } else { 721 // Get back to a clean state, in case we start a new navigation without 722 // completing a RVH swap or unload handler. 723 render_view_host_->SetState(RenderViewHostImpl::STATE_DEFAULT); 724 725 Send(new FrameMsg_Navigate(routing_id_, params)); 726 } 727 728 // Force the throbber to start. We do this because Blink's "started 729 // loading" message will be received asynchronously from the UI of the 730 // browser. But we want to keep the throbber in sync with what's happening 731 // in the UI. For example, we want to start throbbing immediately when the 732 // user naivgates even if the renderer is delayed. There is also an issue 733 // with the throbber starting because the WebUI (which controls whether the 734 // favicon is displayed) happens synchronously. If the start loading 735 // messages was asynchronous, then the default favicon would flash in. 736 // 737 // Blink doesn't send throb notifications for JavaScript URLs, so we 738 // don't want to either. 739 if (!params.url.SchemeIs(kJavaScriptScheme)) 740 delegate_->DidStartLoading(this, true); 741} 742 743void RenderFrameHostImpl::NavigateToURL(const GURL& url) { 744 FrameMsg_Navigate_Params params; 745 params.page_id = -1; 746 params.pending_history_list_offset = -1; 747 params.current_history_list_offset = -1; 748 params.current_history_list_length = 0; 749 params.url = url; 750 params.transition = PAGE_TRANSITION_LINK; 751 params.navigation_type = FrameMsg_Navigate_Type::NORMAL; 752 Navigate(params); 753} 754 755void RenderFrameHostImpl::DispatchBeforeUnload(bool for_cross_site_transition) { 756 // TODO(creis): Support subframes. 757 if (!render_view_host_->IsRenderViewLive() || GetParent()) { 758 // We don't have a live renderer, so just skip running beforeunload. 759 render_view_host_->is_waiting_for_beforeunload_ack_ = true; 760 render_view_host_->unload_ack_is_for_cross_site_transition_ = 761 for_cross_site_transition; 762 base::TimeTicks now = base::TimeTicks::Now(); 763 OnBeforeUnloadACK(true, now, now); 764 return; 765 } 766 767 // This may be called more than once (if the user clicks the tab close button 768 // several times, or if she clicks the tab close button then the browser close 769 // button), and we only send the message once. 770 if (render_view_host_->is_waiting_for_beforeunload_ack_) { 771 // Some of our close messages could be for the tab, others for cross-site 772 // transitions. We always want to think it's for closing the tab if any 773 // of the messages were, since otherwise it might be impossible to close 774 // (if there was a cross-site "close" request pending when the user clicked 775 // the close button). We want to keep the "for cross site" flag only if 776 // both the old and the new ones are also for cross site. 777 render_view_host_->unload_ack_is_for_cross_site_transition_ = 778 render_view_host_->unload_ack_is_for_cross_site_transition_ && 779 for_cross_site_transition; 780 } else { 781 // Start the hang monitor in case the renderer hangs in the beforeunload 782 // handler. 783 render_view_host_->is_waiting_for_beforeunload_ack_ = true; 784 render_view_host_->unload_ack_is_for_cross_site_transition_ = 785 for_cross_site_transition; 786 // Increment the in-flight event count, to ensure that input events won't 787 // cancel the timeout timer. 788 render_view_host_->increment_in_flight_event_count(); 789 render_view_host_->StartHangMonitorTimeout( 790 TimeDelta::FromMilliseconds(RenderViewHostImpl::kUnloadTimeoutMS)); 791 send_before_unload_start_time_ = base::TimeTicks::Now(); 792 Send(new FrameMsg_BeforeUnload(routing_id_)); 793 } 794} 795 796void RenderFrameHostImpl::ExtendSelectionAndDelete(size_t before, 797 size_t after) { 798 Send(new FrameMsg_ExtendSelectionAndDelete(routing_id_, before, after)); 799} 800 801void RenderFrameHostImpl::JavaScriptDialogClosed( 802 IPC::Message* reply_msg, 803 bool success, 804 const base::string16& user_input, 805 bool dialog_was_suppressed) { 806 GetProcess()->SetIgnoreInputEvents(false); 807 bool is_waiting = render_view_host_->is_waiting_for_beforeunload_ack() || 808 render_view_host_->IsWaitingForUnloadACK(); 809 810 // If we are executing as part of (before)unload event handling, we don't 811 // want to use the regular hung_renderer_delay_ms_ if the user has agreed to 812 // leave the current page. In this case, use the regular timeout value used 813 // during the (before)unload handling. 814 if (is_waiting) { 815 render_view_host_->StartHangMonitorTimeout(TimeDelta::FromMilliseconds( 816 success ? RenderViewHostImpl::kUnloadTimeoutMS 817 : render_view_host_->hung_renderer_delay_ms_)); 818 } 819 820 FrameHostMsg_RunJavaScriptMessage::WriteReplyParams(reply_msg, 821 success, user_input); 822 Send(reply_msg); 823 824 // If we are waiting for an unload or beforeunload ack and the user has 825 // suppressed messages, kill the tab immediately; a page that's spamming 826 // alerts in onbeforeunload is presumably malicious, so there's no point in 827 // continuing to run its script and dragging out the process. 828 // This must be done after sending the reply since RenderView can't close 829 // correctly while waiting for a response. 830 if (is_waiting && dialog_was_suppressed) 831 render_view_host_->delegate_->RendererUnresponsive( 832 render_view_host_, 833 render_view_host_->is_waiting_for_beforeunload_ack(), 834 render_view_host_->IsWaitingForUnloadACK()); 835} 836 837void RenderFrameHostImpl::NotificationClosed(int notification_id) { 838 cancel_notification_callbacks_.erase(notification_id); 839} 840 841void RenderFrameHostImpl::DesktopNotificationPermissionRequestDone( 842 int callback_context) { 843 Send(new DesktopNotificationMsg_PermissionRequestDone( 844 routing_id_, callback_context)); 845} 846 847} // namespace content 848