Index: content/browser/frame_host/render_frame_host_impl.cc |
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc |
index 98122c86f9b7a5903f14980a45c14a317587c07e..682228a20bf55f0999b68415b3a7eabe4b91112a 100644 |
--- a/content/browser/frame_host/render_frame_host_impl.cc |
+++ b/content/browser/frame_host/render_frame_host_impl.cc |
@@ -6,6 +6,7 @@ |
#include "base/containers/hash_tables.h" |
#include "base/lazy_instance.h" |
+#include "base/metrics/user_metrics_action.h" |
#include "content/browser/frame_host/cross_process_frame_connector.h" |
#include "content/browser/frame_host/frame_tree.h" |
#include "content/browser/frame_host/frame_tree_node.h" |
@@ -17,6 +18,7 @@ |
#include "content/public/browser/render_process_host.h" |
#include "content/public/browser/render_widget_host_view.h" |
#include "content/public/browser/user_metrics.h" |
+#include "content/public/common/url_constants.h" |
#include "url/gurl.h" |
namespace content { |
@@ -133,6 +135,8 @@ bool RenderFrameHostImpl::OnMessageReceived(const IPC::Message &msg) { |
OnDidFailProvisionalLoadWithError) |
IPC_MESSAGE_HANDLER(FrameHostMsg_DidRedirectProvisionalLoad, |
OnDidRedirectProvisionalLoad) |
+ IPC_MESSAGE_HANDLER_GENERIC(FrameHostMsg_DidCommitProvisionalLoad, |
+ OnNavigate(msg)) |
IPC_MESSAGE_HANDLER(FrameHostMsg_SwapOut_ACK, OnSwapOutACK) |
IPC_MESSAGE_HANDLER(FrameHostMsg_ContextMenu, OnContextMenu) |
IPC_END_MESSAGE_MAP_EX() |
@@ -187,6 +191,101 @@ void RenderFrameHostImpl::OnDidRedirectProvisionalLoad( |
this, page_id, source_url, target_url); |
} |
+// Called when the renderer navigates. For every frame loaded, we'll get this |
+// notification containing parameters identifying the navigation. |
+// |
+// Subframes are identified by the page transition type. For subframes loaded |
+// as part of a wider page load, the page_id will be the same as for the top |
+// level frame. If the user explicitly requests a subframe navigation, we will |
+// get a new page_id because we need to create a new navigation entry for that |
+// action. |
+void RenderFrameHostImpl::OnNavigate(const IPC::Message& msg) { |
+ // Read the parameters out of the IPC message directly to avoid making another |
+ // copy when we filter the URLs. |
+ PickleIterator iter(msg); |
+ FrameHostMsg_DidCommitProvisionalLoad_Params validated_params; |
+ if (!IPC::ParamTraits<FrameHostMsg_DidCommitProvisionalLoad_Params>:: |
+ Read(&msg, &iter, &validated_params)) |
+ return; |
+ |
+ // If we're waiting for a cross-site beforeunload ack from this renderer and |
+ // we receive a Navigate message from the main frame, then the renderer was |
+ // navigating already and sent it before hearing the ViewMsg_Stop message. |
+ // We do not want to cancel the pending navigation in this case, since the |
+ // old page will soon be stopped. Instead, treat this as a beforeunload ack |
+ // to allow the pending navigation to continue. |
+ if (render_view_host_->is_waiting_for_beforeunload_ack_ && |
+ render_view_host_->unload_ack_is_for_cross_site_transition_ && |
+ PageTransitionIsMainFrame(validated_params.transition)) { |
+ render_view_host_->OnShouldCloseACK( |
+ true, render_view_host_->send_should_close_start_time_, |
+ base::TimeTicks::Now()); |
+ return; |
+ } |
+ |
+ // If we're waiting for an unload ack from this renderer and we receive a |
+ // Navigate message, then the renderer was navigating before it received the |
+ // unload request. It will either respond to the unload request soon or our |
+ // timer will expire. Either way, we should ignore this message, because we |
+ // have already committed to closing this renderer. |
+ if (render_view_host_->is_waiting_for_unload_ack_) |
+ return; |
+ |
+ // Cache the main frame id, so we can use it for creating the frame tree |
+ // root node when needed. |
+ if (PageTransitionIsMainFrame(validated_params.transition)) { |
+ if (render_view_host_->main_frame_id_ == -1) { |
+ render_view_host_->main_frame_id_ = validated_params.frame_id; |
+ } else { |
+ // TODO(nasko): We plan to remove the usage of frame_id in navigation |
+ // and move to routing ids. This is in place to ensure that a |
+ // renderer is not misbehaving and sending us incorrect data. |
+ DCHECK_EQ(render_view_host_->main_frame_id_, validated_params.frame_id); |
+ } |
+ } |
+ RenderProcessHost* process = GetProcess(); |
+ |
+ // Attempts to commit certain off-limits URL should be caught more strictly |
+ // than our FilterURL checks below. If a renderer violates this policy, it |
+ // should be killed. |
+ if (!render_view_host_->CanCommitURL(validated_params.url)) { |
Charlie Reis
2014/02/05 23:30:37
Let's move CanCommitURL to RFH rather than calling
nasko
2014/02/06 01:55:13
Done.
|
+ VLOG(1) << "Blocked URL " << validated_params.url.spec(); |
+ validated_params.url = GURL(kAboutBlankURL); |
+ RecordAction(base::UserMetricsAction("CanCommitURL_BlockedAndKilled")); |
+ // Kills the process. |
+ process->ReceivedBadMessage(); |
+ } |
+ |
+ // Now that something has committed, we don't need to track whether the |
+ // initial page has been accessed. |
+ render_view_host_->has_accessed_initial_document_ = false; |
+ |
+ // Without this check, an evil renderer can trick the browser into creating |
+ // a navigation entry for a banned URL. If the user clicks the back button |
+ // followed by the forward button (or clicks reload, or round-trips through |
+ // session restore, etc), we'll think that the browser commanded the |
+ // renderer to load the URL and grant the renderer the privileges to request |
+ // the URL. To prevent this attack, we block the renderer from inserting |
+ // banned URLs into the navigation controller in the first place. |
+ process->FilterURL(false, &validated_params.url); |
+ process->FilterURL(true, &validated_params.referrer.url); |
+ for (std::vector<GURL>::iterator it(validated_params.redirects.begin()); |
+ it != validated_params.redirects.end(); ++it) { |
+ process->FilterURL(false, &(*it)); |
+ } |
+ process->FilterURL(true, &validated_params.searchable_form_url); |
+ |
+ // Without this check, the renderer can trick the browser into using |
+ // filenames it can't access in a future session restore. |
+ if (!render_view_host_->CanAccessFilesOfPageState( |
+ validated_params.page_state)) { |
+ GetProcess()->ReceivedBadMessage(); |
+ return; |
+ } |
+ |
+ frame_tree_node()->navigator()->DidNavigate(this, validated_params); |
+} |
+ |
void RenderFrameHostImpl::SwapOut() { |
if (render_view_host_->IsRenderViewLive()) { |
Send(new FrameMsg_SwapOut(routing_id_)); |