| 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..570e7713df98e4a2b3f612926d33cdc5ee7ee4a6 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"
|
| @@ -14,9 +15,11 @@
|
| #include "content/browser/renderer_host/render_view_host_impl.h"
|
| #include "content/common/frame_messages.h"
|
| #include "content/public/browser/browser_thread.h"
|
| +#include "content/public/browser/content_browser_client.h"
|
| #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 +136,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 +192,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 (!CanCommitURL(validated_params.url)) {
|
| + 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_));
|
| @@ -221,4 +321,13 @@ void RenderFrameHostImpl::OnContextMenu(const ContextMenuParams& params) {
|
| delegate_->ShowContextMenu(this, validated_params);
|
| }
|
|
|
| +bool RenderFrameHostImpl::CanCommitURL(const GURL& url) {
|
| + // TODO(creis): We should also check for WebUI pages here. Also, when the
|
| + // out-of-process iframes implementation is ready, we should check for
|
| + // cross-site URLs that are not allowed to commit in this process.
|
| +
|
| + // Give the client a chance to disallow URLs from committing.
|
| + return GetContentClient()->browser()->CanCommitURL(GetProcess(), url);
|
| +}
|
| +
|
| } // namespace content
|
|
|