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