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