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