Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / renderer / render_frame_impl.cc
index f3a78fa..30d405f 100644 (file)
 #include <map>
 #include <string>
 
+#include "base/auto_reset.h"
 #include "base/command_line.h"
+#include "base/debug/alias.h"
+#include "base/debug/asan_invalid_access.h"
+#include "base/debug/dump_without_crashing.h"
+#include "base/i18n/char_iterator.h"
+#include "base/metrics/histogram.h"
+#include "base/process/kill.h"
+#include "base/process/process.h"
+#include "base/strings/string16.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "content/child/appcache/appcache_dispatcher.h"
 #include "content/child/plugin_messages.h"
 #include "content/child/quota_dispatcher.h"
 #include "content/child/request_extra_data.h"
+#include "content/child/service_worker/service_worker_network_provider.h"
 #include "content/child/service_worker/web_service_worker_provider_impl.h"
+#include "content/child/web_socket_stream_handle_impl.h"
+#include "content/child/web_url_request_util.h"
+#include "content/child/webmessageportchannel_impl.h"
+#include "content/child/websocket_bridge.h"
+#include "content/child/weburlresponse_extradata_impl.h"
+#include "content/common/clipboard_messages.h"
 #include "content/common/frame_messages.h"
+#include "content/common/input_messages.h"
+#include "content/common/service_worker/service_worker_types.h"
 #include "content/common/socket_stream_handle_data.h"
 #include "content/common/swapped_out_messages.h"
 #include "content/common/view_messages.h"
+#include "content/public/common/bindings_policy.h"
 #include "content/public/common/content_constants.h"
 #include "content/public/common/content_switches.h"
+#include "content/public/common/context_menu_params.h"
 #include "content/public/common/url_constants.h"
+#include "content/public/common/url_utils.h"
 #include "content/public/renderer/content_renderer_client.h"
+#include "content/public/renderer/context_menu_client.h"
 #include "content/public/renderer/document_state.h"
 #include "content/public/renderer/navigation_state.h"
+#include "content/public/renderer/render_frame_observer.h"
+#include "content/renderer/accessibility/renderer_accessibility.h"
+#include "content/renderer/accessibility/renderer_accessibility_complete.h"
+#include "content/renderer/accessibility/renderer_accessibility_focus_only.h"
 #include "content/renderer/browser_plugin/browser_plugin.h"
 #include "content/renderer/browser_plugin/browser_plugin_manager.h"
+#include "content/renderer/child_frame_compositing_helper.h"
+#include "content/renderer/context_menu_params_builder.h"
+#include "content/renderer/devtools/devtools_agent.h"
+#include "content/renderer/dom_automation_controller.h"
+#include "content/renderer/dom_utils.h"
+#include "content/renderer/geolocation_dispatcher.h"
+#include "content/renderer/history_controller.h"
+#include "content/renderer/history_serialization.h"
+#include "content/renderer/image_loading_helper.h"
+#include "content/renderer/ime_event_guard.h"
 #include "content/renderer/internal_document_state_data.h"
+#include "content/renderer/media/audio_renderer_mixer_manager.h"
+#include "content/renderer/media/media_stream_dispatcher.h"
+#include "content/renderer/media/media_stream_impl.h"
+#include "content/renderer/media/media_stream_renderer_factory.h"
+#include "content/renderer/media/midi_dispatcher.h"
+#include "content/renderer/media/render_media_log.h"
+#include "content/renderer/media/webcontentdecryptionmodule_impl.h"
+#include "content/renderer/media/webmediaplayer_impl.h"
+#include "content/renderer/media/webmediaplayer_ms.h"
+#include "content/renderer/media/webmediaplayer_params.h"
+#include "content/renderer/notification_permission_dispatcher.h"
+#include "content/renderer/notification_provider.h"
 #include "content/renderer/npapi/plugin_channel_host.h"
+#include "content/renderer/push_messaging_dispatcher.h"
+#include "content/renderer/render_frame_proxy.h"
+#include "content/renderer/render_process.h"
 #include "content/renderer/render_thread_impl.h"
 #include "content/renderer/render_view_impl.h"
+#include "content/renderer/render_widget_fullscreen_pepper.h"
 #include "content/renderer/renderer_webapplicationcachehost_impl.h"
+#include "content/renderer/renderer_webcolorchooser_impl.h"
+#include "content/renderer/screen_orientation/screen_orientation_dispatcher.h"
+#include "content/renderer/shared_worker_repository.h"
+#include "content/renderer/v8_value_converter_impl.h"
 #include "content/renderer/websharedworker_proxy.h"
+#include "media/base/audio_renderer_mixer_input.h"
+#include "net/base/data_url.h"
 #include "net/base/net_errors.h"
+#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/http/http_util.h"
+#include "third_party/WebKit/public/platform/WebStorageQuotaCallbacks.h"
 #include "third_party/WebKit/public/platform/WebString.h"
 #include "third_party/WebKit/public/platform/WebURL.h"
 #include "third_party/WebKit/public/platform/WebURLError.h"
 #include "third_party/WebKit/public/platform/WebURLResponse.h"
 #include "third_party/WebKit/public/platform/WebVector.h"
+#include "third_party/WebKit/public/web/WebColorSuggestion.h"
 #include "third_party/WebKit/public/web/WebDocument.h"
-#include "third_party/WebKit/public/web/WebFrame.h"
+#include "third_party/WebKit/public/web/WebGlyphCache.h"
+#include "third_party/WebKit/public/web/WebLocalFrame.h"
+#include "third_party/WebKit/public/web/WebMediaStreamRegistry.h"
 #include "third_party/WebKit/public/web/WebNavigationPolicy.h"
 #include "third_party/WebKit/public/web/WebPlugin.h"
 #include "third_party/WebKit/public/web/WebPluginParams.h"
+#include "third_party/WebKit/public/web/WebRange.h"
+#include "third_party/WebKit/public/web/WebScriptSource.h"
 #include "third_party/WebKit/public/web/WebSearchableFormData.h"
 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
-#include "third_party/WebKit/public/web/WebStorageQuotaCallbacks.h"
+#include "third_party/WebKit/public/web/WebSecurityPolicy.h"
+#include "third_party/WebKit/public/web/WebSurroundingText.h"
 #include "third_party/WebKit/public/web/WebUserGestureIndicator.h"
 #include "third_party/WebKit/public/web/WebView.h"
-#include "webkit/child/weburlresponse_extradata_impl.h"
+
+#if defined(ENABLE_PLUGINS)
+#include "content/renderer/npapi/webplugin_impl.h"
+#include "content/renderer/pepper/pepper_browser_connection.h"
+#include "content/renderer/pepper/pepper_plugin_instance_impl.h"
+#include "content/renderer/pepper/pepper_webplugin_impl.h"
+#include "content/renderer/pepper/plugin_module.h"
+#endif
 
 #if defined(ENABLE_WEBRTC)
 #include "content/renderer/media/rtc_peer_connection_handler.h"
 #endif
 
-using WebKit::WebDataSource;
-using WebKit::WebDocument;
-using WebKit::WebFrame;
-using WebKit::WebNavigationPolicy;
-using WebKit::WebPluginParams;
-using WebKit::WebReferrerPolicy;
-using WebKit::WebSearchableFormData;
-using WebKit::WebSecurityOrigin;
-using WebKit::WebServiceWorkerProvider;
-using WebKit::WebStorageQuotaCallbacks;
-using WebKit::WebString;
-using WebKit::WebURL;
-using WebKit::WebURLError;
-using WebKit::WebURLRequest;
-using WebKit::WebURLResponse;
-using WebKit::WebUserGestureIndicator;
-using WebKit::WebVector;
-using WebKit::WebView;
+#if defined(OS_ANDROID)
+#include <cpu-features.h>
+
+#include "content/common/gpu/client/context_provider_command_buffer.h"
+#include "content/renderer/android/synchronous_compositor_factory.h"
+#include "content/renderer/java/gin_java_bridge_dispatcher.h"
+#include "content/renderer/media/android/renderer_media_player_manager.h"
+#include "content/renderer/media/android/stream_texture_factory_impl.h"
+#include "content/renderer/media/android/webmediaplayer_android.h"
+#endif
+
+#if defined(ENABLE_BROWSER_CDMS)
+#include "content/renderer/media/crypto/renderer_cdm_manager.h"
+#endif
+
+using blink::WebContextMenuData;
+using blink::WebData;
+using blink::WebDataSource;
+using blink::WebDocument;
+using blink::WebElement;
+using blink::WebFrame;
+using blink::WebHistoryItem;
+using blink::WebHTTPBody;
+using blink::WebLocalFrame;
+using blink::WebMediaPlayer;
+using blink::WebMediaPlayerClient;
+using blink::WebNavigationPolicy;
+using blink::WebNavigationType;
+using blink::WebNode;
+using blink::WebPluginParams;
+using blink::WebRange;
+using blink::WebReferrerPolicy;
+using blink::WebScriptSource;
+using blink::WebSearchableFormData;
+using blink::WebSecurityOrigin;
+using blink::WebSecurityPolicy;
+using blink::WebServiceWorkerProvider;
+using blink::WebStorageQuotaCallbacks;
+using blink::WebString;
+using blink::WebURL;
+using blink::WebURLError;
+using blink::WebURLRequest;
+using blink::WebURLResponse;
+using blink::WebUserGestureIndicator;
+using blink::WebVector;
+using blink::WebView;
 using base::Time;
 using base::TimeDelta;
-using webkit_glue::WebURLResponseExtraDataImpl;
 
 namespace content {
 
 namespace {
 
-typedef std::map<WebKit::WebFrame*, RenderFrameImpl*> FrameMap;
-base::LazyInstance<FrameMap> g_child_frame_map = LAZY_INSTANCE_INITIALIZER;
+const char kDefaultAcceptHeader[] =
+    "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/"
+    "*;q=0.8";
+const char kAcceptHeader[] = "Accept";
+
+const size_t kExtraCharsBeforeAndAfterSelection = 100;
+
+typedef std::map<int, RenderFrameImpl*> RoutingIDFrameMap;
+static base::LazyInstance<RoutingIDFrameMap> g_routing_id_frame_map =
+    LAZY_INSTANCE_INITIALIZER;
+
+typedef std::map<blink::WebFrame*, RenderFrameImpl*> FrameMap;
+base::LazyInstance<FrameMap> g_frame_map = LAZY_INSTANCE_INITIALIZER;
+
+int64 ExtractPostId(const WebHistoryItem& item) {
+  if (item.isNull())
+    return -1;
+
+  if (item.httpBody().isNull())
+    return -1;
+
+  return item.httpBody().identifier();
+}
+
+WebURLResponseExtraDataImpl* GetExtraDataFromResponse(
+    const WebURLResponse& response) {
+  return static_cast<WebURLResponseExtraDataImpl*>(response.extraData());
+}
+
+void GetRedirectChain(WebDataSource* ds, std::vector<GURL>* result) {
+  // Replace any occurrences of swappedout:// with about:blank.
+  const WebURL& blank_url = GURL(url::kAboutBlankURL);
+  WebVector<WebURL> urls;
+  ds->redirectChain(urls);
+  result->reserve(urls.size());
+  for (size_t i = 0; i < urls.size(); ++i) {
+    if (urls[i] != GURL(kSwappedOutURL))
+      result->push_back(urls[i]);
+    else
+      result->push_back(blank_url);
+  }
+}
+
+// Returns the original request url. If there is no redirect, the original
+// url is the same as ds->request()->url(). If the WebDataSource belongs to a
+// frame was loaded by loadData, the original url will be ds->unreachableURL()
+static GURL GetOriginalRequestURL(WebDataSource* ds) {
+  // WebDataSource has unreachable URL means that the frame is loaded through
+  // blink::WebFrame::loadData(), and the base URL will be in the redirect
+  // chain. However, we never visited the baseURL. So in this case, we should
+  // use the unreachable URL as the original URL.
+  if (ds->hasUnreachableURL())
+    return ds->unreachableURL();
+
+  std::vector<GURL> redirects;
+  GetRedirectChain(ds, &redirects);
+  if (!redirects.empty())
+    return redirects.at(0);
+
+  return ds->originalRequest().url();
+}
+
+NOINLINE static void CrashIntentionally() {
+  // NOTE(shess): Crash directly rather than using NOTREACHED() so
+  // that the signature is easier to triage in crash reports.
+  volatile int* zero = NULL;
+  *zero = 0;
+}
+
+#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN)
+NOINLINE static void MaybeTriggerAsanError(const GURL& url) {
+  // NOTE(rogerm): We intentionally perform an invalid heap access here in
+  //     order to trigger an Address Sanitizer (ASAN) error report.
+  const char kCrashDomain[] = "crash";
+  const char kHeapOverflow[] = "/heap-overflow";
+  const char kHeapUnderflow[] = "/heap-underflow";
+  const char kUseAfterFree[] = "/use-after-free";
+#if defined(SYZYASAN)
+  const char kCorruptHeapBlock[] = "/corrupt-heap-block";
+  const char kCorruptHeap[] = "/corrupt-heap";
+#endif
+
+  if (!url.DomainIs(kCrashDomain, sizeof(kCrashDomain) - 1))
+    return;
+
+  if (!url.has_path())
+    return;
+
+  std::string crash_type(url.path());
+  if (crash_type == kHeapOverflow) {
+    base::debug::AsanHeapOverflow();
+  } else if (crash_type == kHeapUnderflow ) {
+    base::debug::AsanHeapUnderflow();
+  } else if (crash_type == kUseAfterFree) {
+    base::debug::AsanHeapUseAfterFree();
+#if defined(SYZYASAN)
+  } else if (crash_type == kCorruptHeapBlock) {
+    base::debug::AsanCorruptHeapBlock();
+  } else if (crash_type == kCorruptHeap) {
+    base::debug::AsanCorruptHeap();
+#endif
+  }
+}
+#endif  // ADDRESS_SANITIZER || SYZYASAN
+
+static void MaybeHandleDebugURL(const GURL& url) {
+  if (!url.SchemeIs(kChromeUIScheme))
+    return;
+  if (url == GURL(kChromeUICrashURL)) {
+    CrashIntentionally();
+  } else if (url == GURL(kChromeUIDumpURL)) {
+    // This URL will only correctly create a crash dump file if content is
+    // hosted in a process that has correctly called
+    // base::debug::SetDumpWithoutCrashingFunction.  Refer to the documentation
+    // of base::debug::DumpWithoutCrashing for more details.
+    base::debug::DumpWithoutCrashing();
+  } else if (url == GURL(kChromeUIKillURL)) {
+    base::KillProcess(base::GetCurrentProcessHandle(), 1, false);
+  } else if (url == GURL(kChromeUIHangURL)) {
+    for (;;) {
+      base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
+    }
+  } else if (url == GURL(kChromeUIShorthangURL)) {
+    base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(20));
+  }
+
+#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN)
+  MaybeTriggerAsanError(url);
+#endif  // ADDRESS_SANITIZER || SYZYASAN
+}
+
+// Returns false unless this is a top-level navigation.
+static bool IsTopLevelNavigation(WebFrame* frame) {
+  return frame->parent() == NULL;
+}
+
+// Returns false unless this is a top-level navigation that crosses origins.
+static bool IsNonLocalTopLevelNavigation(const GURL& url,
+                                         WebFrame* frame,
+                                         WebNavigationType type,
+                                         bool is_form_post) {
+  if (!IsTopLevelNavigation(frame))
+    return false;
+
+  // Navigations initiated within Webkit are not sent out to the external host
+  // in the following cases.
+  // 1. The url scheme is not http/https
+  // 2. The origin of the url and the opener is the same in which case the
+  //    opener relationship is maintained.
+  // 3. Reloads/form submits/back forward navigations
+  if (!url.SchemeIs(url::kHttpScheme) && !url.SchemeIs(url::kHttpsScheme))
+    return false;
+
+  if (type != blink::WebNavigationTypeReload &&
+      type != blink::WebNavigationTypeBackForward && !is_form_post) {
+    // The opener relationship between the new window and the parent allows the
+    // new window to script the parent and vice versa. This is not allowed if
+    // the origins of the two domains are different. This can be treated as a
+    // top level navigation and routed back to the host.
+    blink::WebFrame* opener = frame->opener();
+    if (!opener)
+      return true;
+
+    if (url.GetOrigin() != GURL(opener->document().url()).GetOrigin())
+      return true;
+  }
+  return false;
+}
 
 }  // namespace
 
@@ -102,6 +368,51 @@ RenderFrameImpl* RenderFrameImpl::Create(RenderViewImpl* render_view,
 }
 
 // static
+RenderFrameImpl* RenderFrameImpl::FromRoutingID(int32 routing_id) {
+  RoutingIDFrameMap::iterator iter =
+      g_routing_id_frame_map.Get().find(routing_id);
+  if (iter != g_routing_id_frame_map.Get().end())
+    return iter->second;
+  return NULL;
+}
+
+// static
+void RenderFrameImpl::CreateFrame(int routing_id, int parent_routing_id) {
+  // TODO(nasko): For now, this message is only sent for subframes, as the
+  // top level frame is created when the RenderView is created through the
+  // ViewMsg_New IPC.
+  CHECK_NE(MSG_ROUTING_NONE, parent_routing_id);
+
+  RenderFrameProxy* proxy = RenderFrameProxy::FromRoutingID(parent_routing_id);
+
+  // If the browser is sending a valid parent routing id, it should already be
+  // created and registered.
+  CHECK(proxy);
+  blink::WebRemoteFrame* parent_web_frame = proxy->web_frame();
+
+  // Create the RenderFrame and WebLocalFrame, linking the two.
+  RenderFrameImpl* render_frame =
+      RenderFrameImpl::Create(proxy->render_view(), routing_id);
+  blink::WebLocalFrame* web_frame =
+      parent_web_frame->createLocalChild("", render_frame);
+  render_frame->SetWebFrame(web_frame);
+  render_frame->Initialize();
+}
+
+// static
+RenderFrame* RenderFrame::FromWebFrame(blink::WebFrame* web_frame) {
+  return RenderFrameImpl::FromWebFrame(web_frame);
+}
+
+// static
+RenderFrameImpl* RenderFrameImpl::FromWebFrame(blink::WebFrame* web_frame) {
+  FrameMap::iterator iter = g_frame_map.Get().find(web_frame);
+  if (iter != g_frame_map.Get().end())
+    return iter->second;
+  return NULL;
+}
+
+// static
 void RenderFrameImpl::InstallCreateHook(
     RenderFrameImpl* (*create_render_frame_impl)(RenderViewImpl*, int32)) {
   CHECK(!g_create_render_frame_impl);
@@ -110,259 +421,1386 @@ void RenderFrameImpl::InstallCreateHook(
 
 // RenderFrameImpl ----------------------------------------------------------
 RenderFrameImpl::RenderFrameImpl(RenderViewImpl* render_view, int routing_id)
-    : render_view_(render_view),
+    : frame_(NULL),
+      render_view_(render_view->AsWeakPtr()),
       routing_id_(routing_id),
       is_swapped_out_(false),
-      is_detaching_(false) {
+      render_frame_proxy_(NULL),
+      is_detaching_(false),
+      cookie_jar_(this),
+      selection_text_offset_(0),
+      selection_range_(gfx::Range::InvalidRange()),
+      handling_select_range_(false),
+      notification_permission_dispatcher_(NULL),
+      notification_provider_(NULL),
+      web_user_media_client_(NULL),
+      midi_dispatcher_(NULL),
+#if defined(OS_ANDROID)
+      media_player_manager_(NULL),
+#endif
+#if defined(ENABLE_BROWSER_CDMS)
+      cdm_manager_(NULL),
+#endif
+#if defined(VIDEO_HOLE)
+      contains_media_player_(false),
+#endif
+      geolocation_dispatcher_(NULL),
+      push_messaging_dispatcher_(NULL),
+      screen_orientation_dispatcher_(NULL),
+      accessibility_mode_(AccessibilityModeOff),
+      renderer_accessibility_(NULL),
+      weak_factory_(this) {
+  std::pair<RoutingIDFrameMap::iterator, bool> result =
+      g_routing_id_frame_map.Get().insert(std::make_pair(routing_id_, this));
+  CHECK(result.second) << "Inserting a duplicate item.";
+
+  RenderThread::Get()->AddRoute(routing_id_, this);
+
+  render_view_->RegisterRenderFrame(this);
+
+#if defined(OS_ANDROID)
+  new GinJavaBridgeDispatcher(this);
+#endif
+
+#if defined(ENABLE_NOTIFICATIONS)
+  notification_provider_ = new NotificationProvider(this);
+#endif
 }
 
 RenderFrameImpl::~RenderFrameImpl() {
+  FOR_EACH_OBSERVER(RenderFrameObserver, observers_, RenderFrameGone());
+  FOR_EACH_OBSERVER(RenderFrameObserver, observers_, OnDestruct());
+
+#if defined(VIDEO_HOLE)
+  if (contains_media_player_)
+    render_view_->UnregisterVideoHoleFrame(this);
+#endif
+
+  render_view_->UnregisterRenderFrame(this);
+  g_routing_id_frame_map.Get().erase(routing_id_);
+  RenderThread::Get()->RemoveRoute(routing_id_);
 }
 
-int RenderFrameImpl::GetRoutingID() const {
-  return routing_id_;
+void RenderFrameImpl::SetWebFrame(blink::WebLocalFrame* web_frame) {
+  DCHECK(!frame_);
+
+  std::pair<FrameMap::iterator, bool> result = g_frame_map.Get().insert(
+      std::make_pair(web_frame, this));
+  CHECK(result.second) << "Inserting a duplicate item.";
+
+  frame_ = web_frame;
+}
+
+void RenderFrameImpl::Initialize() {
+#if defined(ENABLE_PLUGINS)
+  new PepperBrowserConnection(this);
+#endif
+  new SharedWorkerRepository(this);
+
+  if (!frame_->parent())
+    new ImageLoadingHelper(this);
+
+  // We delay calling this until we have the WebFrame so that any observer or
+  // embedder can call GetWebFrame on any RenderFrame.
+  GetContentClient()->renderer()->RenderFrameCreated(this);
+}
+
+RenderWidget* RenderFrameImpl::GetRenderWidget() {
+  return render_view_.get();
+}
+
+#if defined(ENABLE_PLUGINS)
+void RenderFrameImpl::PepperPluginCreated(RendererPpapiHost* host) {
+  FOR_EACH_OBSERVER(RenderFrameObserver, observers_,
+                    DidCreatePepperPlugin(host));
+}
+
+void RenderFrameImpl::PepperDidChangeCursor(
+    PepperPluginInstanceImpl* instance,
+    const blink::WebCursorInfo& cursor) {
+  // Update the cursor appearance immediately if the requesting plugin is the
+  // one which receives the last mouse event. Otherwise, the new cursor won't be
+  // picked up until the plugin gets the next input event. That is bad if, e.g.,
+  // the plugin would like to set an invisible cursor when there isn't any user
+  // input for a while.
+  if (instance == render_view_->pepper_last_mouse_event_target())
+    GetRenderWidget()->didChangeCursor(cursor);
+}
+
+void RenderFrameImpl::PepperDidReceiveMouseEvent(
+    PepperPluginInstanceImpl* instance) {
+  render_view_->set_pepper_last_mouse_event_target(instance);
+}
+
+void RenderFrameImpl::PepperTextInputTypeChanged(
+    PepperPluginInstanceImpl* instance) {
+  if (instance != render_view_->focused_pepper_plugin())
+    return;
+
+  GetRenderWidget()->UpdateTextInputState(
+      RenderWidget::NO_SHOW_IME, RenderWidget::FROM_NON_IME);
+  if (renderer_accessibility())
+    renderer_accessibility()->FocusedNodeChanged(WebNode());
+}
+
+void RenderFrameImpl::PepperCaretPositionChanged(
+    PepperPluginInstanceImpl* instance) {
+  if (instance != render_view_->focused_pepper_plugin())
+    return;
+  GetRenderWidget()->UpdateSelectionBounds();
+}
+
+void RenderFrameImpl::PepperCancelComposition(
+    PepperPluginInstanceImpl* instance) {
+  if (instance != render_view_->focused_pepper_plugin())
+    return;
+  Send(new InputHostMsg_ImeCancelComposition(render_view_->GetRoutingID()));;
+#if defined(OS_MACOSX) || defined(USE_AURA)
+  GetRenderWidget()->UpdateCompositionInfo(true);
+#endif
+}
+
+void RenderFrameImpl::PepperSelectionChanged(
+    PepperPluginInstanceImpl* instance) {
+  if (instance != render_view_->focused_pepper_plugin())
+    return;
+  SyncSelectionIfRequired();
+}
+
+RenderWidgetFullscreenPepper* RenderFrameImpl::CreatePepperFullscreenContainer(
+    PepperPluginInstanceImpl* plugin) {
+  GURL active_url;
+  if (render_view_->webview() && render_view_->webview()->mainFrame())
+    active_url = GURL(render_view_->webview()->mainFrame()->document().url());
+  RenderWidgetFullscreenPepper* widget = RenderWidgetFullscreenPepper::Create(
+      GetRenderWidget()->routing_id(), plugin, active_url,
+      GetRenderWidget()->screenInfo());
+  widget->show(blink::WebNavigationPolicyIgnore);
+  return widget;
+}
+
+bool RenderFrameImpl::IsPepperAcceptingCompositionEvents() const {
+  if (!render_view_->focused_pepper_plugin())
+    return false;
+  return render_view_->focused_pepper_plugin()->
+      IsPluginAcceptingCompositionEvents();
+}
+
+void RenderFrameImpl::PluginCrashed(const base::FilePath& plugin_path,
+                                   base::ProcessId plugin_pid) {
+  // TODO(jam): dispatch this IPC in RenderFrameHost and switch to use
+  // routing_id_ as a result.
+  Send(new FrameHostMsg_PluginCrashed(routing_id_, plugin_path, plugin_pid));
+}
+
+void RenderFrameImpl::SimulateImeSetComposition(
+    const base::string16& text,
+    const std::vector<blink::WebCompositionUnderline>& underlines,
+    int selection_start,
+    int selection_end) {
+  render_view_->OnImeSetComposition(
+      text, underlines, selection_start, selection_end);
+}
+
+void RenderFrameImpl::SimulateImeConfirmComposition(
+    const base::string16& text,
+    const gfx::Range& replacement_range) {
+  render_view_->OnImeConfirmComposition(text, replacement_range, false);
+}
+
+
+void RenderFrameImpl::OnImeSetComposition(
+    const base::string16& text,
+    const std::vector<blink::WebCompositionUnderline>& underlines,
+    int selection_start,
+    int selection_end) {
+  // When a PPAPI plugin has focus, we bypass WebKit.
+  if (!IsPepperAcceptingCompositionEvents()) {
+    pepper_composition_text_ = text;
+  } else {
+    // TODO(kinaba) currently all composition events are sent directly to
+    // plugins. Use DOM event mechanism after WebKit is made aware about
+    // plugins that support composition.
+    // The code below mimics the behavior of WebCore::Editor::setComposition.
+
+    // Empty -> nonempty: composition started.
+    if (pepper_composition_text_.empty() && !text.empty()) {
+      render_view_->focused_pepper_plugin()->HandleCompositionStart(
+          base::string16());
+    }
+    // Nonempty -> empty: composition canceled.
+    if (!pepper_composition_text_.empty() && text.empty()) {
+      render_view_->focused_pepper_plugin()->HandleCompositionEnd(
+          base::string16());
+    }
+    pepper_composition_text_ = text;
+    // Nonempty: composition is ongoing.
+    if (!pepper_composition_text_.empty()) {
+      render_view_->focused_pepper_plugin()->HandleCompositionUpdate(
+          pepper_composition_text_, underlines, selection_start,
+          selection_end);
+    }
+  }
+}
+
+void RenderFrameImpl::OnImeConfirmComposition(
+    const base::string16& text,
+    const gfx::Range& replacement_range,
+    bool keep_selection) {
+  // When a PPAPI plugin has focus, we bypass WebKit.
+  // Here, text.empty() has a special meaning. It means to commit the last
+  // update of composition text (see
+  // RenderWidgetHost::ImeConfirmComposition()).
+  const base::string16& last_text = text.empty() ? pepper_composition_text_
+                                                 : text;
+
+  // last_text is empty only when both text and pepper_composition_text_ is.
+  // Ignore it.
+  if (last_text.empty())
+    return;
+
+  if (!IsPepperAcceptingCompositionEvents()) {
+    base::i18n::UTF16CharIterator iterator(&last_text);
+    int32 i = 0;
+    while (iterator.Advance()) {
+      blink::WebKeyboardEvent char_event;
+      char_event.type = blink::WebInputEvent::Char;
+      char_event.timeStampSeconds = base::Time::Now().ToDoubleT();
+      char_event.modifiers = 0;
+      char_event.windowsKeyCode = last_text[i];
+      char_event.nativeKeyCode = last_text[i];
+
+      const int32 char_start = i;
+      for (; i < iterator.array_pos(); ++i) {
+        char_event.text[i - char_start] = last_text[i];
+        char_event.unmodifiedText[i - char_start] = last_text[i];
+      }
+
+      if (GetRenderWidget()->webwidget())
+        GetRenderWidget()->webwidget()->handleInputEvent(char_event);
+    }
+  } else {
+    // Mimics the order of events sent by WebKit.
+    // See WebCore::Editor::setComposition() for the corresponding code.
+    render_view_->focused_pepper_plugin()->HandleCompositionEnd(last_text);
+    render_view_->focused_pepper_plugin()->HandleTextInput(last_text);
+  }
+  pepper_composition_text_.clear();
+}
+
+#endif  // ENABLE_PLUGINS
+
+MediaStreamDispatcher* RenderFrameImpl::GetMediaStreamDispatcher() {
+  if (!web_user_media_client_)
+    InitializeUserMediaClient();
+  return web_user_media_client_ ?
+      web_user_media_client_->media_stream_dispatcher() : NULL;
 }
 
 bool RenderFrameImpl::Send(IPC::Message* message) {
-  if (is_detaching_ ||
-      ((is_swapped_out_ || render_view_->is_swapped_out()) &&
-       !SwappedOutMessages::CanSendWhileSwappedOut(message))) {
+  if (is_detaching_) {
     delete message;
     return false;
   }
+  if (frame_->parent() == NULL &&
+      (is_swapped_out_ || render_view_->is_swapped_out())) {
+    if (!SwappedOutMessages::CanSendWhileSwappedOut(message)) {
+      delete message;
+      return false;
+    }
+
+    // In most cases, send IPCs through the proxy when swapped out. In some
+    // calls the associated RenderViewImpl routing id is used to send
+    // messages, so don't use the proxy.
+    if (render_frame_proxy_ && message->routing_id() == routing_id_)
+      return render_frame_proxy_->Send(message);
+  }
 
   return RenderThread::Get()->Send(message);
 }
 
 bool RenderFrameImpl::OnMessageReceived(const IPC::Message& msg) {
-  // TODO(ajwong): Fill in with message handlers as various components
-  // are migrated over to understand frames.
-  return false;
+  // TODO(kenrb): document() should not be null, but as a transitional step
+  // we have RenderFrameProxy 'wrapping' a RenderFrameImpl, passing messages
+  // to this method. This happens for a top-level remote frame, where a
+  // document-less RenderFrame is replaced by a RenderFrameProxy but kept
+  // around and is still able to receive messages.
+  if (!frame_->document().isNull())
+    GetContentClient()->SetActiveURL(frame_->document().url());
+
+  ObserverListBase<RenderFrameObserver>::Iterator it(observers_);
+  RenderFrameObserver* observer;
+  while ((observer = it.GetNext()) != NULL) {
+    if (observer->OnMessageReceived(msg))
+      return true;
+  }
+
+  bool handled = true;
+  IPC_BEGIN_MESSAGE_MAP(RenderFrameImpl, msg)
+    IPC_MESSAGE_HANDLER(FrameMsg_Navigate, OnNavigate)
+    IPC_MESSAGE_HANDLER(FrameMsg_BeforeUnload, OnBeforeUnload)
+    IPC_MESSAGE_HANDLER(FrameMsg_SwapOut, OnSwapOut)
+    IPC_MESSAGE_HANDLER(FrameMsg_ContextMenuClosed, OnContextMenuClosed)
+    IPC_MESSAGE_HANDLER(FrameMsg_CustomContextMenuAction,
+                        OnCustomContextMenuAction)
+    IPC_MESSAGE_HANDLER(InputMsg_Undo, OnUndo)
+    IPC_MESSAGE_HANDLER(InputMsg_Redo, OnRedo)
+    IPC_MESSAGE_HANDLER(InputMsg_Cut, OnCut)
+    IPC_MESSAGE_HANDLER(InputMsg_Copy, OnCopy)
+    IPC_MESSAGE_HANDLER(InputMsg_Paste, OnPaste)
+    IPC_MESSAGE_HANDLER(InputMsg_PasteAndMatchStyle, OnPasteAndMatchStyle)
+    IPC_MESSAGE_HANDLER(InputMsg_Delete, OnDelete)
+    IPC_MESSAGE_HANDLER(InputMsg_SelectAll, OnSelectAll)
+    IPC_MESSAGE_HANDLER(InputMsg_SelectRange, OnSelectRange)
+    IPC_MESSAGE_HANDLER(InputMsg_Unselect, OnUnselect)
+    IPC_MESSAGE_HANDLER(InputMsg_Replace, OnReplace)
+    IPC_MESSAGE_HANDLER(InputMsg_ReplaceMisspelling, OnReplaceMisspelling)
+    IPC_MESSAGE_HANDLER(InputMsg_ExtendSelectionAndDelete,
+                        OnExtendSelectionAndDelete)
+    IPC_MESSAGE_HANDLER(InputMsg_SetCompositionFromExistingText,
+                        OnSetCompositionFromExistingText)
+    IPC_MESSAGE_HANDLER(FrameMsg_CSSInsertRequest, OnCSSInsertRequest)
+    IPC_MESSAGE_HANDLER(FrameMsg_JavaScriptExecuteRequest,
+                        OnJavaScriptExecuteRequest)
+    IPC_MESSAGE_HANDLER(FrameMsg_SetEditableSelectionOffsets,
+                        OnSetEditableSelectionOffsets)
+    IPC_MESSAGE_HANDLER(FrameMsg_SetupTransitionView, OnSetupTransitionView)
+    IPC_MESSAGE_HANDLER(FrameMsg_BeginExitTransition, OnBeginExitTransition)
+    IPC_MESSAGE_HANDLER(FrameMsg_Reload, OnReload)
+    IPC_MESSAGE_HANDLER(FrameMsg_TextSurroundingSelectionRequest,
+                        OnTextSurroundingSelectionRequest)
+    IPC_MESSAGE_HANDLER(FrameMsg_AddStyleSheetByURL,
+                        OnAddStyleSheetByURL)
+    IPC_MESSAGE_HANDLER(FrameMsg_SetAccessibilityMode,
+                        OnSetAccessibilityMode)
+#if defined(OS_MACOSX)
+    IPC_MESSAGE_HANDLER(InputMsg_CopyToFindPboard, OnCopyToFindPboard)
+#endif
+  IPC_END_MESSAGE_MAP()
+
+  return handled;
 }
 
-// WebKit::WebFrameClient implementation -------------------------------------
+void RenderFrameImpl::OnNavigate(const FrameMsg_Navigate_Params& params) {
+  MaybeHandleDebugURL(params.url);
+  if (!render_view_->webview())
+    return;
+
+  FOR_EACH_OBSERVER(
+      RenderViewObserver, render_view_->observers_, Navigate(params.url));
+
+  bool is_reload = RenderViewImpl::IsReload(params);
+  WebURLRequest::CachePolicy cache_policy =
+      WebURLRequest::UseProtocolCachePolicy;
+
+  // If this is a stale back/forward (due to a recent navigation the browser
+  // didn't know about), ignore it.
+  if (render_view_->IsBackForwardToStaleEntry(params, is_reload))
+    return;
+
+  // Swap this renderer back in if necessary.
+  if (render_view_->is_swapped_out_ &&
+      GetWebFrame() == render_view_->webview()->mainFrame()) {
+    // We marked the view as hidden when swapping the view out, so be sure to
+    // reset the visibility state before navigating to the new URL.
+    render_view_->webview()->setVisibilityState(
+        render_view_->visibilityState(), false);
+
+    // If this is an attempt to reload while we are swapped out, we should not
+    // reload swappedout://, but the previous page, which is stored in
+    // params.state.  Setting is_reload to false will treat this like a back
+    // navigation to accomplish that.
+    is_reload = false;
+    cache_policy = WebURLRequest::ReloadIgnoringCacheData;
+
+    // We refresh timezone when a view is swapped in since timezone
+    // can get out of sync when the system timezone is updated while
+    // the view is swapped out.
+    RenderThreadImpl::NotifyTimezoneChange();
+
+    render_view_->SetSwappedOut(false);
+    is_swapped_out_ = false;
+  }
+
+  if (params.should_clear_history_list) {
+    CHECK_EQ(params.pending_history_list_offset, -1);
+    CHECK_EQ(params.current_history_list_offset, -1);
+    CHECK_EQ(params.current_history_list_length, 0);
+  }
+  render_view_->history_list_offset_ = params.current_history_list_offset;
+  render_view_->history_list_length_ = params.current_history_list_length;
+  if (render_view_->history_list_length_ >= 0) {
+    render_view_->history_page_ids_.resize(
+        render_view_->history_list_length_, -1);
+  }
+  if (params.pending_history_list_offset >= 0 &&
+      params.pending_history_list_offset < render_view_->history_list_length_) {
+    render_view_->history_page_ids_[params.pending_history_list_offset] =
+        params.page_id;
+  }
+
+  GetContentClient()->SetActiveURL(params.url);
+
+  WebFrame* frame = frame_;
+  if (!params.frame_to_navigate.empty()) {
+    // TODO(nasko): Move this lookup to the browser process.
+    frame = render_view_->webview()->findFrameByName(
+        WebString::fromUTF8(params.frame_to_navigate));
+    CHECK(frame) << "Invalid frame name passed: " << params.frame_to_navigate;
+  }
+
+  if (is_reload && !render_view_->history_controller()->GetCurrentEntry()) {
+    // We cannot reload if we do not have any history state.  This happens, for
+    // example, when recovering from a crash.
+    is_reload = false;
+    cache_policy = WebURLRequest::ReloadIgnoringCacheData;
+  }
+
+  render_view_->pending_navigation_params_.reset(
+      new FrameMsg_Navigate_Params(params));
+
+  // If we are reloading, then WebKit will use the history state of the current
+  // page, so we should just ignore any given history state.  Otherwise, if we
+  // have history state, then we need to navigate to it, which corresponds to a
+  // back/forward navigation event.
+  if (is_reload) {
+    bool reload_original_url =
+        (params.navigation_type ==
+            FrameMsg_Navigate_Type::RELOAD_ORIGINAL_REQUEST_URL);
+    bool ignore_cache = (params.navigation_type ==
+                             FrameMsg_Navigate_Type::RELOAD_IGNORING_CACHE);
+
+    if (reload_original_url)
+      frame->reloadWithOverrideURL(params.url, true);
+    else
+      frame->reload(ignore_cache);
+  } else if (params.page_state.IsValid()) {
+    // We must know the page ID of the page we are navigating back to.
+    DCHECK_NE(params.page_id, -1);
+    scoped_ptr<HistoryEntry> entry =
+        PageStateToHistoryEntry(params.page_state);
+    if (entry) {
+      // Ensure we didn't save the swapped out URL in UpdateState, since the
+      // browser should never be telling us to navigate to swappedout://.
+      CHECK(entry->root().urlString() != WebString::fromUTF8(kSwappedOutURL));
+      render_view_->history_controller()->GoToEntry(entry.Pass(), cache_policy);
+    }
+  } else if (!params.base_url_for_data_url.is_empty()) {
+    // A loadData request with a specified base URL.
+    std::string mime_type, charset, data;
+    if (net::DataURL::Parse(params.url, &mime_type, &charset, &data)) {
+      frame->loadData(
+          WebData(data.c_str(), data.length()),
+          WebString::fromUTF8(mime_type),
+          WebString::fromUTF8(charset),
+          params.base_url_for_data_url,
+          params.history_url_for_data_url,
+          false);
+    } else {
+      CHECK(false) <<
+          "Invalid URL passed: " << params.url.possibly_invalid_spec();
+    }
+  } else {
+    // Navigate to the given URL.
+    WebURLRequest request(params.url);
+
+    // A session history navigation should have been accompanied by state.
+    CHECK_EQ(params.page_id, -1);
+
+    if (frame->isViewSourceModeEnabled())
+      request.setCachePolicy(WebURLRequest::ReturnCacheDataElseLoad);
+
+    if (params.referrer.url.is_valid()) {
+      WebString referrer = WebSecurityPolicy::generateReferrerHeader(
+          params.referrer.policy,
+          params.url,
+          WebString::fromUTF8(params.referrer.url.spec()));
+      if (!referrer.isEmpty())
+        request.setHTTPReferrer(referrer, params.referrer.policy);
+    }
+
+    if (!params.extra_headers.empty()) {
+      for (net::HttpUtil::HeadersIterator i(params.extra_headers.begin(),
+                                            params.extra_headers.end(), "\n");
+           i.GetNext(); ) {
+        request.addHTTPHeaderField(WebString::fromUTF8(i.name()),
+                                   WebString::fromUTF8(i.values()));
+      }
+    }
+
+    if (params.is_post) {
+      request.setHTTPMethod(WebString::fromUTF8("POST"));
+
+      // Set post data.
+      WebHTTPBody http_body;
+      http_body.initialize();
+      const char* data = NULL;
+      if (params.browser_initiated_post_data.size()) {
+        data = reinterpret_cast<const char*>(
+            &params.browser_initiated_post_data.front());
+      }
+      http_body.appendData(
+          WebData(data, params.browser_initiated_post_data.size()));
+      request.setHTTPBody(http_body);
+    }
+
+    // Record this before starting the load, we need a lower bound of this time
+    // to sanitize the navigationStart override set below.
+    base::TimeTicks renderer_navigation_start = base::TimeTicks::Now();
+    frame->loadRequest(request);
+
+    // The browser provides the navigation_start time to bootstrap the
+    // Navigation Timing information for the browser-initiated navigations. In
+    // case of cross-process navigations, this carries over the time of
+    // finishing the onbeforeunload handler of the previous page.
+    DCHECK(!params.browser_navigation_start.is_null());
+    if (frame->provisionalDataSource()) {
+      // |browser_navigation_start| is likely before this process existed, so we
+      // can't use InterProcessTimeTicksConverter. We need at least to ensure
+      // that the browser-side navigation start we set is not later than the one
+      // on the renderer side.
+      base::TimeTicks navigation_start = std::min(
+          params.browser_navigation_start, renderer_navigation_start);
+      double navigation_start_seconds =
+          (navigation_start - base::TimeTicks()).InSecondsF();
+      frame->provisionalDataSource()->setNavigationStartTime(
+          navigation_start_seconds);
+    }
+  }
+
+  // In case LoadRequest failed before DidCreateDataSource was called.
+  render_view_->pending_navigation_params_.reset();
+}
+
+void RenderFrameImpl::BindServiceRegistry(
+    mojo::ScopedMessagePipeHandle service_provider_handle) {
+  service_registry_.BindRemoteServiceProvider(service_provider_handle.Pass());
+}
+
+void RenderFrameImpl::OnBeforeUnload() {
+  // TODO(creis): Right now, this is only called on the main frame.  Make the
+  // browser process send dispatchBeforeUnloadEvent to every frame that needs
+  // it.
+  CHECK(!frame_->parent());
+
+  base::TimeTicks before_unload_start_time = base::TimeTicks::Now();
+  bool proceed = frame_->dispatchBeforeUnloadEvent();
+  base::TimeTicks before_unload_end_time = base::TimeTicks::Now();
+  Send(new FrameHostMsg_BeforeUnload_ACK(routing_id_, proceed,
+                                         before_unload_start_time,
+                                         before_unload_end_time));
+}
+
+void RenderFrameImpl::OnSwapOut(int proxy_routing_id) {
+  RenderFrameProxy* proxy = NULL;
+  bool is_site_per_process =
+      CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess);
+
+  // Only run unload if we're not swapped out yet, but send the ack either way.
+  if (!is_swapped_out_ || !render_view_->is_swapped_out_) {
+    // Swap this RenderFrame out so the frame can navigate to a page rendered by
+    // a different process.  This involves running the unload handler and
+    // clearing the page.  Once WasSwappedOut is called, we also allow this
+    // process to exit if there are no other active RenderFrames in it.
+
+    // Send an UpdateState message before we get swapped out. Create the
+    // RenderFrameProxy as well so its routing id is registered for receiving
+    // IPC messages.
+    render_view_->SyncNavigationState();
+    proxy = RenderFrameProxy::CreateProxyToReplaceFrame(this,
+                                                        proxy_routing_id);
+
+    // Synchronously run the unload handler before sending the ACK.
+    // TODO(creis): Call dispatchUnloadEvent unconditionally here to support
+    // unload on subframes as well.
+    if (!frame_->parent())
+      frame_->dispatchUnloadEvent();
+
+    // Swap out and stop sending any IPC messages that are not ACKs.
+    // TODO(nasko): Do we need RenderFrameImpl::is_swapped_out_ anymore?
+    if (!frame_->parent())
+      render_view_->SetSwappedOut(true);
+    is_swapped_out_ = true;
+
+    // Now that we're swapped out and filtering IPC messages, stop loading to
+    // ensure that no other in-progress navigation continues.  We do this here
+    // to avoid sending a DidStopLoading message to the browser process.
+    // TODO(creis): Should we be stopping all frames here and using
+    // StopAltErrorPageFetcher with RenderView::OnStop, or just stopping this
+    // frame?
+    if (!frame_->parent())
+      render_view_->OnStop();
+    else
+      frame_->stopLoading();
+
+    // Let subframes know that the frame is now rendered remotely, for the
+    // purposes of compositing and input events.
+    if (frame_->parent())
+      frame_->setIsRemote(true);
+
+    // Replace the page with a blank dummy URL. The unload handler will not be
+    // run a second time, thanks to a check in FrameLoader::stopLoading.
+    // TODO(creis): Need to add a better way to do this that avoids running the
+    // beforeunload handler. For now, we just run it a second time silently.
+    if (!is_site_per_process || frame_->parent() == NULL)
+      render_view_->NavigateToSwappedOutURL(frame_);
+
+    // Let WebKit know that this view is hidden so it can drop resources and
+    // stop compositing.
+    // TODO(creis): Support this for subframes as well.
+    if (!frame_->parent()) {
+      render_view_->webview()->setVisibilityState(
+          blink::WebPageVisibilityStateHidden, false);
+    }
+  }
+
+  // It is now safe to show modal dialogs again.
+  // TODO(creis): Deal with modal dialogs from subframes.
+  if (!frame_->parent())
+    render_view_->suppress_dialogs_until_swap_out_ = false;
+
+  Send(new FrameHostMsg_SwapOut_ACK(routing_id_));
+
+  // Now that all of the cleanup is complete and the browser side is notified,
+  // start using the RenderFrameProxy, if one is created.
+  if (proxy) {
+    if (frame_->parent()) {
+      frame_->swap(proxy->web_frame());
+      if (is_site_per_process) {
+        // TODO(nasko): delete the frame here, since we've replaced it with a
+        // proxy.
+      }
+    } else {
+      set_render_frame_proxy(proxy);
+    }
+  }
+}
+
+void RenderFrameImpl::OnContextMenuClosed(
+    const CustomContextMenuContext& custom_context) {
+  if (custom_context.request_id) {
+    // External request, should be in our map.
+    ContextMenuClient* client =
+        pending_context_menus_.Lookup(custom_context.request_id);
+    if (client) {
+      client->OnMenuClosed(custom_context.request_id);
+      pending_context_menus_.Remove(custom_context.request_id);
+    }
+  } else {
+    if (custom_context.link_followed.is_valid()) {
+        frame_->sendPings(
+            DomUtils::ExtractParentAnchorNode(context_menu_node_),
+            custom_context.link_followed);
+    }
+    // Internal request, forward to WebKit.
+    context_menu_node_.reset();
+  }
+}
+
+void RenderFrameImpl::OnCustomContextMenuAction(
+    const CustomContextMenuContext& custom_context,
+    unsigned action) {
+  if (custom_context.request_id) {
+    // External context menu request, look in our map.
+    ContextMenuClient* client =
+        pending_context_menus_.Lookup(custom_context.request_id);
+    if (client)
+      client->OnMenuAction(custom_context.request_id, action);
+  } else {
+    // Internal request, forward to WebKit.
+    render_view_->webview()->performCustomContextMenuAction(action);
+  }
+}
+
+void RenderFrameImpl::OnUndo() {
+  frame_->executeCommand(WebString::fromUTF8("Undo"), GetFocusedElement());
+}
+
+void RenderFrameImpl::OnRedo() {
+  frame_->executeCommand(WebString::fromUTF8("Redo"), GetFocusedElement());
+}
+
+void RenderFrameImpl::OnCut() {
+  base::AutoReset<bool> handling_select_range(&handling_select_range_, true);
+  frame_->executeCommand(WebString::fromUTF8("Cut"), GetFocusedElement());
+}
+
+void RenderFrameImpl::OnCopy() {
+  base::AutoReset<bool> handling_select_range(&handling_select_range_, true);
+  WebNode current_node = context_menu_node_.isNull() ?
+      GetFocusedElement() : context_menu_node_;
+  frame_->executeCommand(WebString::fromUTF8("Copy"), current_node);
+}
+
+void RenderFrameImpl::OnPaste() {
+  base::AutoReset<bool> handling_select_range(&handling_select_range_, true);
+  frame_->executeCommand(WebString::fromUTF8("Paste"), GetFocusedElement());
+}
+
+void RenderFrameImpl::OnPasteAndMatchStyle() {
+  base::AutoReset<bool> handling_select_range(&handling_select_range_, true);
+  frame_->executeCommand(
+      WebString::fromUTF8("PasteAndMatchStyle"), GetFocusedElement());
+}
 
-WebKit::WebPlugin* RenderFrameImpl::createPlugin(
-    WebKit::WebFrame* frame,
-    const WebKit::WebPluginParams& params) {
-  WebKit::WebPlugin* plugin = NULL;
+#if defined(OS_MACOSX)
+void RenderFrameImpl::OnCopyToFindPboard() {
+  // Since the find pasteboard supports only plain text, this can be simpler
+  // than the |OnCopy()| case.
+  if (frame_->hasSelection()) {
+    base::string16 selection = frame_->selectionAsText();
+    RenderThread::Get()->Send(
+        new ClipboardHostMsg_FindPboardWriteStringAsync(selection));
+  }
+}
+#endif
+
+void RenderFrameImpl::OnDelete() {
+  frame_->executeCommand(WebString::fromUTF8("Delete"), GetFocusedElement());
+}
+
+void RenderFrameImpl::OnSelectAll() {
+  base::AutoReset<bool> handling_select_range(&handling_select_range_, true);
+  frame_->executeCommand(WebString::fromUTF8("SelectAll"), GetFocusedElement());
+}
+
+void RenderFrameImpl::OnSelectRange(const gfx::Point& start,
+                                    const gfx::Point& end) {
+  // This IPC is dispatched by RenderWidgetHost, so use its routing id.
+  Send(new ViewHostMsg_SelectRange_ACK(GetRenderWidget()->routing_id()));
+
+  base::AutoReset<bool> handling_select_range(&handling_select_range_, true);
+  frame_->selectRange(start, end);
+}
+
+void RenderFrameImpl::OnUnselect() {
+  base::AutoReset<bool> handling_select_range(&handling_select_range_, true);
+  frame_->executeCommand(WebString::fromUTF8("Unselect"), GetFocusedElement());
+}
+
+void RenderFrameImpl::OnReplace(const base::string16& text) {
+  if (!frame_->hasSelection())
+    frame_->selectWordAroundCaret();
+
+  frame_->replaceSelection(text);
+}
+
+void RenderFrameImpl::OnReplaceMisspelling(const base::string16& text) {
+  if (!frame_->hasSelection())
+    return;
+
+  frame_->replaceMisspelledRange(text);
+}
+
+void RenderFrameImpl::OnCSSInsertRequest(const std::string& css) {
+  frame_->document().insertStyleSheet(WebString::fromUTF8(css));
+}
+
+void RenderFrameImpl::OnJavaScriptExecuteRequest(
+    const base::string16& jscript,
+    int id,
+    bool notify_result) {
+  TRACE_EVENT_INSTANT0("test_tracing", "OnJavaScriptExecuteRequest",
+                       TRACE_EVENT_SCOPE_THREAD);
+
+  v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
+  v8::Handle<v8::Value> result =
+      frame_->executeScriptAndReturnValue(WebScriptSource(jscript));
+  if (notify_result) {
+    base::ListValue list;
+    if (!result.IsEmpty()) {
+      v8::Local<v8::Context> context = frame_->mainWorldScriptContext();
+      v8::Context::Scope context_scope(context);
+      V8ValueConverterImpl converter;
+      converter.SetDateAllowed(true);
+      converter.SetRegExpAllowed(true);
+      base::Value* result_value = converter.FromV8Value(result, context);
+      list.Set(0, result_value ? result_value : base::Value::CreateNullValue());
+    } else {
+      list.Set(0, base::Value::CreateNullValue());
+    }
+    Send(new FrameHostMsg_JavaScriptExecuteResponse(routing_id_, id, list));
+  }
+}
+
+void RenderFrameImpl::OnSetEditableSelectionOffsets(int start, int end) {
+  base::AutoReset<bool> handling_select_range(&handling_select_range_, true);
+  if (!GetRenderWidget()->ShouldHandleImeEvent())
+    return;
+  ImeEventGuard guard(GetRenderWidget());
+  frame_->setEditableSelectionOffsets(start, end);
+}
+
+void RenderFrameImpl::OnSetCompositionFromExistingText(
+    int start, int end,
+    const std::vector<blink::WebCompositionUnderline>& underlines) {
+  if (!GetRenderWidget()->ShouldHandleImeEvent())
+    return;
+  ImeEventGuard guard(GetRenderWidget());
+  frame_->setCompositionFromExistingText(start, end, underlines);
+}
+
+void RenderFrameImpl::OnExtendSelectionAndDelete(int before, int after) {
+  if (!GetRenderWidget()->ShouldHandleImeEvent())
+    return;
+  ImeEventGuard guard(GetRenderWidget());
+  frame_->extendSelectionAndDelete(before, after);
+}
+
+void RenderFrameImpl::OnSetAccessibilityMode(AccessibilityMode new_mode) {
+  if (accessibility_mode_ == new_mode)
+    return;
+  accessibility_mode_ = new_mode;
+  if (renderer_accessibility_) {
+    delete renderer_accessibility_;
+    renderer_accessibility_ = NULL;
+  }
+  if (accessibility_mode_ == AccessibilityModeOff)
+    return;
+
+  if (accessibility_mode_ & AccessibilityModeFlagFullTree)
+    renderer_accessibility_ = new RendererAccessibilityComplete(this);
+#if !defined(OS_ANDROID)
+  else
+    renderer_accessibility_ = new RendererAccessibilityFocusOnly(this);
+#endif
+}
+
+void RenderFrameImpl::OnReload(bool ignore_cache) {
+  frame_->reload(ignore_cache);
+}
+
+void RenderFrameImpl::OnTextSurroundingSelectionRequest(size_t max_length) {
+  blink::WebSurroundingText surroundingText;
+  surroundingText.initialize(frame_->selectionRange(), max_length);
+
+  if (surroundingText.isNull()) {
+    // |surroundingText| might not be correctly initialized, for example if
+    // |frame_->selectionRange().isNull()|, in other words, if there was no
+    // selection.
+    Send(new FrameHostMsg_TextSurroundingSelectionResponse(
+        routing_id_, base::string16(), 0, 0));
+    return;
+  }
+
+  Send(new FrameHostMsg_TextSurroundingSelectionResponse(
+      routing_id_,
+      surroundingText.textContent(),
+      surroundingText.startOffsetInTextContent(),
+      surroundingText.endOffsetInTextContent()));
+}
+
+void RenderFrameImpl::OnAddStyleSheetByURL(const std::string& url) {
+  frame_->addStyleSheetByURL(WebString::fromUTF8(url));
+}
+
+void RenderFrameImpl::OnSetupTransitionView(const std::string& markup) {
+  frame_->document().setIsTransitionDocument();
+  frame_->navigateToSandboxedMarkup(WebData(markup.data(), markup.length()));
+}
+
+void RenderFrameImpl::OnBeginExitTransition(const std::string& css_selector) {
+  frame_->document().setIsTransitionDocument();
+  frame_->document().beginExitTransition(WebString::fromUTF8(css_selector));
+}
+
+bool RenderFrameImpl::ShouldUpdateSelectionTextFromContextMenuParams(
+    const base::string16& selection_text,
+    size_t selection_text_offset,
+    const gfx::Range& selection_range,
+    const ContextMenuParams& params) {
+  base::string16 trimmed_selection_text;
+  if (!selection_text.empty() && !selection_range.is_empty()) {
+    const int start = selection_range.GetMin() - selection_text_offset;
+    const size_t length = selection_range.length();
+    if (start >= 0 && start + length <= selection_text.length()) {
+      base::TrimWhitespace(selection_text.substr(start, length), base::TRIM_ALL,
+                           &trimmed_selection_text);
+    }
+  }
+  base::string16 trimmed_params_text;
+  base::TrimWhitespace(params.selection_text, base::TRIM_ALL,
+                       &trimmed_params_text);
+  return trimmed_params_text != trimmed_selection_text;
+}
+
+bool RenderFrameImpl::RunJavaScriptMessage(JavaScriptMessageType type,
+                                           const base::string16& message,
+                                           const base::string16& default_value,
+                                           const GURL& frame_url,
+                                           base::string16* result) {
+  // Don't allow further dialogs if we are waiting to swap out, since the
+  // PageGroupLoadDeferrer in our stack prevents it.
+  if (render_view()->suppress_dialogs_until_swap_out_)
+    return false;
+
+  bool success = false;
+  base::string16 result_temp;
+  if (!result)
+    result = &result_temp;
+
+  render_view()->SendAndRunNestedMessageLoop(
+      new FrameHostMsg_RunJavaScriptMessage(
+        routing_id_, message, default_value, frame_url, type, &success,
+        result));
+  return success;
+}
+
+void RenderFrameImpl::LoadNavigationErrorPage(
+    const WebURLRequest& failed_request,
+    const WebURLError& error,
+    bool replace) {
+  std::string error_html;
+  GetContentClient()->renderer()->GetNavigationErrorStrings(
+      render_view(), frame_, failed_request, error, &error_html, NULL);
+
+  frame_->loadHTMLString(error_html,
+                         GURL(kUnreachableWebDataURL),
+                         error.unreachableURL,
+                         replace);
+}
+
+void RenderFrameImpl::DidCommitCompositorFrame() {
+  FOR_EACH_OBSERVER(
+      RenderFrameObserver, observers_, DidCommitCompositorFrame());
+}
+
+RenderView* RenderFrameImpl::GetRenderView() {
+  return render_view_.get();
+}
+
+int RenderFrameImpl::GetRoutingID() {
+  return routing_id_;
+}
+
+blink::WebFrame* RenderFrameImpl::GetWebFrame() {
+  DCHECK(frame_);
+  return frame_;
+}
+
+WebPreferences& RenderFrameImpl::GetWebkitPreferences() {
+  return render_view_->GetWebkitPreferences();
+}
+
+int RenderFrameImpl::ShowContextMenu(ContextMenuClient* client,
+                                     const ContextMenuParams& params) {
+  DCHECK(client);  // A null client means "internal" when we issue callbacks.
+  ContextMenuParams our_params(params);
+  our_params.custom_context.request_id = pending_context_menus_.Add(client);
+  Send(new FrameHostMsg_ContextMenu(routing_id_, our_params));
+  return our_params.custom_context.request_id;
+}
+
+void RenderFrameImpl::CancelContextMenu(int request_id) {
+  DCHECK(pending_context_menus_.Lookup(request_id));
+  pending_context_menus_.Remove(request_id);
+}
+
+blink::WebNode RenderFrameImpl::GetContextMenuNode() const {
+  return context_menu_node_;
+}
+
+blink::WebPlugin* RenderFrameImpl::CreatePlugin(
+    blink::WebFrame* frame,
+    const WebPluginInfo& info,
+    const blink::WebPluginParams& params) {
+  DCHECK_EQ(frame_, frame);
+#if defined(ENABLE_PLUGINS)
+  bool pepper_plugin_was_registered = false;
+  scoped_refptr<PluginModule> pepper_module(PluginModule::Create(
+      this, info, &pepper_plugin_was_registered));
+  if (pepper_plugin_was_registered) {
+    if (pepper_module.get()) {
+      return new PepperWebPluginImpl(pepper_module.get(), params, this);
+    }
+  }
+#if defined(OS_CHROMEOS)
+  LOG(WARNING) << "Pepper module/plugin creation failed.";
+  return NULL;
+#else
+  // TODO(jam): change to take RenderFrame.
+  return new WebPluginImpl(frame, params, info.path, render_view_, this);
+#endif
+#else
+  return NULL;
+#endif
+}
+
+void RenderFrameImpl::LoadURLExternally(blink::WebLocalFrame* frame,
+                                        const blink::WebURLRequest& request,
+                                        blink::WebNavigationPolicy policy) {
+  DCHECK(!frame_ || frame_ == frame);
+  loadURLExternally(frame, request, policy, WebString());
+}
+
+void RenderFrameImpl::ExecuteJavaScript(const base::string16& javascript) {
+  OnJavaScriptExecuteRequest(javascript, 0, false);
+}
+
+ServiceRegistry* RenderFrameImpl::GetServiceRegistry() {
+  return &service_registry_;
+}
+
+bool RenderFrameImpl::IsFTPDirectoryListing() {
+  WebURLResponseExtraDataImpl* extra_data =
+      GetExtraDataFromResponse(frame_->dataSource()->response());
+  return extra_data ? extra_data->is_ftp_directory_listing() : false;
+}
+
+// blink::WebFrameClient implementation ----------------------------------------
+
+blink::WebPlugin* RenderFrameImpl::createPlugin(
+    blink::WebLocalFrame* frame,
+    const blink::WebPluginParams& params) {
+  DCHECK_EQ(frame_, frame);
+  blink::WebPlugin* plugin = NULL;
   if (GetContentClient()->renderer()->OverrideCreatePlugin(
-          render_view_, frame, params, &plugin)) {
+          this, frame, params, &plugin)) {
     return plugin;
   }
 
-#if defined(ENABLE_PLUGINS)
-  if (UTF16ToASCII(params.mimeType) == kBrowserPluginMimeType) {
+  if (base::UTF16ToUTF8(params.mimeType) == kBrowserPluginMimeType) {
     return render_view_->GetBrowserPluginManager()->CreateBrowserPlugin(
-        render_view_, frame, params);
+        render_view_.get(), frame, false);
   }
 
+#if defined(ENABLE_PLUGINS)
   WebPluginInfo info;
   std::string mime_type;
-  bool found = render_view_->GetPluginInfo(
-      params.url, frame->top()->document().url(), params.mimeType.utf8(),
-      &info, &mime_type);
+  bool found = false;
+  Send(new FrameHostMsg_GetPluginInfo(
+      routing_id_, params.url, frame->top()->document().url(),
+      params.mimeType.utf8(), &found, &info, &mime_type));
   if (!found)
     return NULL;
 
+  if (info.type == content::WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN) {
+    return render_view_->GetBrowserPluginManager()->CreateBrowserPlugin(
+        render_view_.get(), frame, true);
+  }
+
+
   WebPluginParams params_to_use = params;
   params_to_use.mimeType = WebString::fromUTF8(mime_type);
-  return render_view_->CreatePlugin(frame, info, params_to_use);
+  return CreatePlugin(frame, info, params_to_use);
 #else
   return NULL;
 #endif  // defined(ENABLE_PLUGINS)
 }
 
-WebKit::WebMediaPlayer* RenderFrameImpl::createMediaPlayer(
-    WebKit::WebFrame* frame,
-    const WebKit::WebURL& url,
-    WebKit::WebMediaPlayerClient* client) {
-  // TODO(nasko): Moving the implementation here involves moving a few media
-  // related client objects here or referencing them in the RenderView. Needs
-  // more work to understand where the proper place for those objects is.
-  return render_view_->createMediaPlayer(frame, url, client);
+blink::WebMediaPlayer* RenderFrameImpl::createMediaPlayer(
+    blink::WebLocalFrame* frame,
+    const blink::WebURL& url,
+    blink::WebMediaPlayerClient* client) {
+#if defined(VIDEO_HOLE)
+  if (!contains_media_player_) {
+    render_view_->RegisterVideoHoleFrame(this);
+    contains_media_player_ = true;
+  }
+#endif  // defined(VIDEO_HOLE)
+
+  blink::WebMediaStream web_stream(
+      blink::WebMediaStreamRegistry::lookupMediaStreamDescriptor(url));
+  if (!web_stream.isNull())
+    return CreateWebMediaPlayerForMediaStream(url, client);
+
+#if defined(OS_ANDROID)
+  return CreateAndroidWebMediaPlayer(url, client);
+#else
+  WebMediaPlayerParams params(
+      base::Bind(&ContentRendererClient::DeferMediaLoad,
+                 base::Unretained(GetContentClient()->renderer()),
+                 static_cast<RenderFrame*>(this)),
+      RenderThreadImpl::current()->GetAudioRendererMixerManager()->CreateInput(
+          render_view_->routing_id_, routing_id_));
+  return new WebMediaPlayerImpl(frame, client, weak_factory_.GetWeakPtr(),
+                                params);
+#endif  // defined(OS_ANDROID)
+}
+
+blink::WebContentDecryptionModule*
+RenderFrameImpl::createContentDecryptionModule(
+    blink::WebLocalFrame* frame,
+    const blink::WebSecurityOrigin& security_origin,
+    const blink::WebString& key_system) {
+  DCHECK(!frame_ || frame_ == frame);
+  return WebContentDecryptionModuleImpl::Create(
+#if defined(ENABLE_PEPPER_CDMS)
+      frame,
+#elif defined(ENABLE_BROWSER_CDMS)
+      GetCdmManager(),
+#endif
+      security_origin,
+      key_system);
 }
 
-WebKit::WebApplicationCacheHost* RenderFrameImpl::createApplicationCacheHost(
-    WebKit::WebFrame* frame,
-    WebKit::WebApplicationCacheHostClient* client) {
+blink::WebApplicationCacheHost* RenderFrameImpl::createApplicationCacheHost(
+    blink::WebLocalFrame* frame,
+    blink::WebApplicationCacheHostClient* client) {
   if (!frame || !frame->view())
     return NULL;
+  DCHECK(!frame_ || frame_ == frame);
   return new RendererWebApplicationCacheHostImpl(
       RenderViewImpl::FromWebView(frame->view()), client,
       RenderThreadImpl::current()->appcache_dispatcher()->backend_proxy());
 }
 
-WebKit::WebWorkerPermissionClientProxy*
-RenderFrameImpl::createWorkerPermissionClientProxy(WebFrame* frame) {
+blink::WebWorkerPermissionClientProxy*
+RenderFrameImpl::createWorkerPermissionClientProxy(
+    blink::WebLocalFrame* frame) {
   if (!frame || !frame->view())
     return NULL;
+  DCHECK(!frame_ || frame_ == frame);
   return GetContentClient()->renderer()->CreateWorkerPermissionClientProxy(
-      RenderViewImpl::FromWebView(frame->view()), frame);
+      this, frame);
 }
 
-WebKit::WebCookieJar* RenderFrameImpl::cookieJar(WebKit::WebFrame* frame) {
-  return render_view_->cookieJar(frame);
+blink::WebCookieJar* RenderFrameImpl::cookieJar(blink::WebLocalFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
+  return &cookie_jar_;
 }
 
-WebKit::WebServiceWorkerProvider* RenderFrameImpl::createServiceWorkerProvider(
-    WebKit::WebFrame* frame,
-    WebKit::WebServiceWorkerProviderClient* client) {
+blink::WebServiceWorkerProvider* RenderFrameImpl::createServiceWorkerProvider(
+    blink::WebLocalFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
+  // At this point we should have non-null data source.
+  DCHECK(frame->dataSource());
+  if (!ChildThread::current())
+    return NULL;  // May be null in some tests.
+  ServiceWorkerNetworkProvider* provider =
+      ServiceWorkerNetworkProvider::FromDocumentState(
+          DocumentState::FromDataSource(frame->dataSource()));
   return new WebServiceWorkerProviderImpl(
       ChildThread::current()->thread_safe_sender(),
-      ChildThread::current()->service_worker_message_filter(),
-      GURL(frame->document().securityOrigin().toString()),
-      make_scoped_ptr(client));
-}
-
-void RenderFrameImpl::didAccessInitialDocument(WebKit::WebFrame* frame) {
-  render_view_->didAccessInitialDocument(frame);
-}
-
-WebKit::WebFrame* RenderFrameImpl::createChildFrame(
-    WebKit::WebFrame* parent,
-    const WebKit::WebString& name) {
-  RenderFrameImpl* child_render_frame = this;
-  long long child_frame_identifier = WebFrame::generateEmbedderIdentifier();
-  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess)) {
-    // Synchronously notify the browser of a child frame creation to get the
-    // routing_id for the RenderFrame.
-    int routing_id;
-    Send(new FrameHostMsg_CreateChildFrame(GetRoutingID(),
-                                           parent->identifier(),
-                                           child_frame_identifier,
-                                           UTF16ToUTF8(name),
-                                           &routing_id));
-    child_render_frame = RenderFrameImpl::Create(render_view_, routing_id);
+      provider ? provider->context() : NULL);
+}
+
+void RenderFrameImpl::didAccessInitialDocument(blink::WebLocalFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
+  // Notify the browser process that it is no longer safe to show the pending
+  // URL of the main frame, since a URL spoof is now possible.
+  if (!frame->parent() && render_view_->page_id_ == -1)
+    Send(new FrameHostMsg_DidAccessInitialDocument(routing_id_));
+}
+
+blink::WebFrame* RenderFrameImpl::createChildFrame(
+    blink::WebLocalFrame* parent,
+    const blink::WebString& name) {
+  // Synchronously notify the browser of a child frame creation to get the
+  // routing_id for the RenderFrame.
+  int child_routing_id = MSG_ROUTING_NONE;
+  Send(new FrameHostMsg_CreateChildFrame(routing_id_,
+                                         base::UTF16ToUTF8(name),
+                                         &child_routing_id));
+  // Allocation of routing id failed, so we can't create a child frame. This can
+  // happen if this RenderFrameImpl's IPCs are being filtered when in swapped
+  // out state.
+  if (child_routing_id == MSG_ROUTING_NONE) {
+#if !defined(OS_LINUX)
+    // DumpWithoutCrashing() crashes on Linux in renderer processes when
+    // breakpad and sandboxing are enabled: crbug.com/349600
+    base::debug::Alias(parent);
+    base::debug::Alias(&routing_id_);
+    bool render_view_is_swapped_out = GetRenderWidget()->is_swapped_out();
+    base::debug::Alias(&render_view_is_swapped_out);
+    bool render_view_is_closing = GetRenderWidget()->closing();
+    base::debug::Alias(&render_view_is_closing);
+    base::debug::Alias(&is_swapped_out_);
+    base::debug::DumpWithoutCrashing();
+#endif
+    return NULL;
   }
 
-  WebKit::WebFrame* web_frame = WebFrame::create(child_render_frame,
-                                                 child_frame_identifier);
+  // Create the RenderFrame and WebLocalFrame, linking the two.
+  RenderFrameImpl* child_render_frame = RenderFrameImpl::Create(
+      render_view_.get(), child_routing_id);
+  blink::WebLocalFrame* web_frame = WebLocalFrame::create(child_render_frame);
+  child_render_frame->SetWebFrame(web_frame);
 
-  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess)) {
-    g_child_frame_map.Get().insert(
-        std::make_pair(web_frame, child_render_frame));
-  }
+  // Add the frame to the frame tree and initialize it.
+  parent->appendChild(web_frame);
+  child_render_frame->Initialize();
 
   return web_frame;
 }
 
-void RenderFrameImpl::didDisownOpener(WebKit::WebFrame* frame) {
-  render_view_->didDisownOpener(frame);
+void RenderFrameImpl::didDisownOpener(blink::WebLocalFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
+  // We only need to notify the browser if the active, top-level frame clears
+  // its opener.  We can ignore cases where a swapped out frame clears its
+  // opener after hearing about it from the browser, and the browser does not
+  // (yet) care about subframe openers.
+  if (render_view_->is_swapped_out_ || frame->parent())
+    return;
+
+  // Notify WebContents and all its swapped out RenderViews.
+  Send(new FrameHostMsg_DidDisownOpener(routing_id_));
 }
 
-void RenderFrameImpl::frameDetached(WebKit::WebFrame* frame) {
-  // Currently multiple WebCore::Frames can send frameDetached to a single
-  // RenderFrameImpl. This is legacy behavior from when RenderViewImpl served
-  // as a shared WebFrameClient for multiple Webcore::Frame objects. It also
-  // prevents this class from entering the |is_detaching_| state because
-  // even though one WebCore::Frame may have detached itself, others will
-  // still need to use this object.
-  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess)) {
-    // TODO(ajwong): Add CHECK(!is_detaching_) once we guarantee each
-    // RenderFrameImpl is only used by one WebCore::Frame.
-    is_detaching_ = true;
-  }
+void RenderFrameImpl::frameDetached(blink::WebFrame* frame) {
+  // NOTE: This function is called on the frame that is being detached and not
+  // the parent frame.  This is different from createChildFrame() which is
+  // called on the parent frame.
+  CHECK(!is_detaching_);
+  DCHECK(!frame_ || frame_ == frame);
 
-  int64 parent_frame_id = -1;
-  if (frame->parent())
-    parent_frame_id = frame->parent()->identifier();
+  bool is_subframe = !!frame->parent();
 
-  render_view_->Send(new FrameHostMsg_Detach(GetRoutingID(), parent_frame_id,
-                                             frame->identifier()));
+  Send(new FrameHostMsg_Detach(routing_id_));
 
-  // Call back to RenderViewImpl for observers to be notified.
-  // TODO(nasko): Remove once we have RenderFrameObserver.
-  render_view_->frameDetached(frame);
+  // The |is_detaching_| flag disables Send(). FrameHostMsg_Detach must be
+  // sent before setting |is_detaching_| to true. In contrast, Observers
+  // should only be notified afterwards so they cannot call back into here and
+  // have IPCs fired off.
+  is_detaching_ = true;
 
-  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess)) {
-    FrameMap::iterator it = g_child_frame_map.Get().find(frame);
-    DCHECK(it != g_child_frame_map.Get().end());
-    DCHECK_EQ(it->second, this);
-    delete it->second;
-    g_child_frame_map.Get().erase(it);
-  }
+  FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(),
+                    FrameDetached(frame));
+
+  // We need to clean up subframes by removing them from the map and deleting
+  // the RenderFrameImpl.  In contrast, the main frame is owned by its
+  // containing RenderViewHost (so that they have the same lifetime), so only
+  // removal from the map is needed and no deletion.
+  FrameMap::iterator it = g_frame_map.Get().find(frame);
+  CHECK(it != g_frame_map.Get().end());
+  CHECK_EQ(it->second, this);
+  g_frame_map.Get().erase(it);
+
+  if (is_subframe)
+    frame->parent()->removeChild(frame);
 
+  // |frame| is invalid after here.
   frame->close();
+
+  if (is_subframe) {
+    delete this;
+    // Object is invalid after this point.
+  }
 }
 
-void RenderFrameImpl::willClose(WebKit::WebFrame* frame) {
-  // Call back to RenderViewImpl for observers to be notified.
-  // TODO(nasko): Remove once we have RenderFrameObserver.
-  render_view_->willClose(frame);
+void RenderFrameImpl::frameFocused() {
+  Send(new FrameHostMsg_FrameFocused(routing_id_));
 }
 
-void RenderFrameImpl::didChangeName(WebKit::WebFrame* frame,
-                                    const WebKit::WebString& name) {
+void RenderFrameImpl::willClose(blink::WebFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
+
+  FOR_EACH_OBSERVER(RenderFrameObserver, observers_, FrameWillClose());
+  FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(),
+                    FrameWillClose(frame));
+}
+
+void RenderFrameImpl::didChangeName(blink::WebLocalFrame* frame,
+                                    const blink::WebString& name) {
+  DCHECK(!frame_ || frame_ == frame);
   if (!render_view_->renderer_preferences_.report_frame_name_changes)
     return;
 
-  render_view_->Send(
-      new ViewHostMsg_UpdateFrameName(render_view_->GetRoutingID(),
-                                      frame->identifier(),
-                                      !frame->parent(),
-                                      UTF16ToUTF8(name)));
+  FOR_EACH_OBSERVER(RenderFrameObserver, observers_, DidChangeName(name));
 }
 
 void RenderFrameImpl::didMatchCSS(
-    WebKit::WebFrame* frame,
-    const WebKit::WebVector<WebKit::WebString>& newly_matching_selectors,
-    const WebKit::WebVector<WebKit::WebString>& stopped_matching_selectors) {
-  render_view_->didMatchCSS(
-      frame, newly_matching_selectors, stopped_matching_selectors);
-}
+    blink::WebLocalFrame* frame,
+    const blink::WebVector<blink::WebString>& newly_matching_selectors,
+    const blink::WebVector<blink::WebString>& stopped_matching_selectors) {
+  DCHECK(!frame_ || frame_ == frame);
+
+  FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(),
+                    DidMatchCSS(frame,
+                                newly_matching_selectors,
+                                stopped_matching_selectors));
+}
+
+bool RenderFrameImpl::shouldReportDetailedMessageForSource(
+    const blink::WebString& source) {
+  return GetContentClient()->renderer()->ShouldReportDetailedMessageForSource(
+      source);
+}
+
+void RenderFrameImpl::didAddMessageToConsole(
+    const blink::WebConsoleMessage& message,
+    const blink::WebString& source_name,
+    unsigned source_line,
+    const blink::WebString& stack_trace) {
+  logging::LogSeverity log_severity = logging::LOG_VERBOSE;
+  switch (message.level) {
+    case blink::WebConsoleMessage::LevelDebug:
+      log_severity = logging::LOG_VERBOSE;
+      break;
+    case blink::WebConsoleMessage::LevelLog:
+    case blink::WebConsoleMessage::LevelInfo:
+      log_severity = logging::LOG_INFO;
+      break;
+    case blink::WebConsoleMessage::LevelWarning:
+      log_severity = logging::LOG_WARNING;
+      break;
+    case blink::WebConsoleMessage::LevelError:
+      log_severity = logging::LOG_ERROR;
+      break;
+    default:
+      NOTREACHED();
+  }
 
-void RenderFrameImpl::loadURLExternally(WebKit::WebFrame* frame,
-                                        const WebKit::WebURLRequest& request,
-                                        WebKit::WebNavigationPolicy policy) {
-  loadURLExternally(frame, request, policy, WebString());
+  if (shouldReportDetailedMessageForSource(source_name)) {
+    FOR_EACH_OBSERVER(
+        RenderFrameObserver, observers_,
+        DetailedConsoleMessageAdded(message.text,
+                                    source_name,
+                                    stack_trace,
+                                    source_line,
+                                    static_cast<int32>(log_severity)));
+  }
+
+  Send(new FrameHostMsg_AddMessageToConsole(routing_id_,
+                                            static_cast<int32>(log_severity),
+                                            message.text,
+                                            static_cast<int32>(source_line),
+                                            source_name));
 }
 
 void RenderFrameImpl::loadURLExternally(
-    WebKit::WebFrame* frame,
-    const WebKit::WebURLRequest& request,
-    WebKit::WebNavigationPolicy policy,
-    const WebKit::WebString& suggested_name) {
+    blink::WebLocalFrame* frame,
+    const blink::WebURLRequest& request,
+    blink::WebNavigationPolicy policy,
+    const blink::WebString& suggested_name) {
+  DCHECK(!frame_ || frame_ == frame);
   Referrer referrer(RenderViewImpl::GetReferrerFromRequest(frame, request));
-  if (policy == WebKit::WebNavigationPolicyDownload) {
+  if (policy == blink::WebNavigationPolicyDownload) {
+    render_view_->Send(new ViewHostMsg_DownloadUrl(render_view_->GetRoutingID(),
+                                                   request.url(), referrer,
+                                                   suggested_name, false));
+  } else if (policy == blink::WebNavigationPolicyDownloadTo) {
     render_view_->Send(new ViewHostMsg_DownloadUrl(render_view_->GetRoutingID(),
                                                    request.url(), referrer,
-                                                   suggested_name));
+                                                   suggested_name, true));
   } else {
-    render_view_->OpenURL(frame, request.url(), referrer, policy);
+    OpenURL(frame, request.url(), referrer, policy);
   }
 }
 
-WebKit::WebNavigationPolicy RenderFrameImpl::decidePolicyForNavigation(
-    WebKit::WebFrame* frame,
-    WebKit::WebDataSource::ExtraData* extra_data,
-    const WebKit::WebURLRequest& request,
-    WebKit::WebNavigationType type,
-    WebKit::WebNavigationPolicy default_policy,
-    bool is_redirect) {
-  return render_view_->decidePolicyForNavigation(
-      frame, extra_data, request, type, default_policy, is_redirect);
+blink::WebNavigationPolicy RenderFrameImpl::decidePolicyForNavigation(
+    const NavigationPolicyInfo& info) {
+  DCHECK(!frame_ || frame_ == info.frame);
+  return DecidePolicyForNavigation(this, info);
 }
 
-WebKit::WebNavigationPolicy RenderFrameImpl::decidePolicyForNavigation(
-    WebKit::WebFrame* frame,
-    const WebKit::WebURLRequest& request,
-    WebKit::WebNavigationType type,
-    WebKit::WebNavigationPolicy default_policy,
-    bool is_redirect) {
-  return render_view_->decidePolicyForNavigation(
-      frame, request, type, default_policy, is_redirect);
+blink::WebHistoryItem RenderFrameImpl::historyItemForNewChildFrame(
+    blink::WebFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
+  return render_view_->history_controller()->GetItemForNewChildFrame(this);
 }
 
-void RenderFrameImpl::willSendSubmitEvent(WebKit::WebFrame* frame,
-                                          const WebKit::WebFormElement& form) {
-  // Call back to RenderViewImpl for observers to be notified.
-  // TODO(nasko): Remove once we have RenderFrameObserver.
-  render_view_->willSendSubmitEvent(frame, form);
+void RenderFrameImpl::willSendSubmitEvent(blink::WebLocalFrame* frame,
+                                          const blink::WebFormElement& form) {
+  DCHECK(!frame_ || frame_ == frame);
+
+  FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(),
+                    WillSendSubmitEvent(frame, form));
 }
 
-void RenderFrameImpl::willSubmitForm(WebKit::WebFrame* frame,
-                                     const WebKit::WebFormElement& form) {
+void RenderFrameImpl::willSubmitForm(blink::WebLocalFrame* frame,
+                                     const blink::WebFormElement& form) {
+  DCHECK(!frame_ || frame_ == frame);
   DocumentState* document_state =
       DocumentState::FromDataSource(frame->provisionalDataSource());
   NavigationState* navigation_state = document_state->navigation_state();
@@ -380,13 +1818,14 @@ void RenderFrameImpl::willSubmitForm(WebKit::WebFrame* frame,
   internal_data->set_searchable_form_encoding(
       web_searchable_form_data.encoding().utf8());
 
-  // Call back to RenderViewImpl for observers to be notified.
-  // TODO(nasko): Remove once we have RenderFrameObserver.
-  render_view_->willSubmitForm(frame, form);
+  FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(),
+                    WillSubmitForm(frame, form));
 }
 
-void RenderFrameImpl::didCreateDataSource(WebKit::WebFrame* frame,
-                                          WebKit::WebDataSource* datasource) {
+void RenderFrameImpl::didCreateDataSource(blink::WebLocalFrame* frame,
+                                          blink::WebDataSource* datasource) {
+  DCHECK(!frame_ || frame_ == frame);
+
   // TODO(nasko): Move implementation here. Needed state:
   // * pending_navigation_params_
   // * webview
@@ -394,9 +1833,18 @@ void RenderFrameImpl::didCreateDataSource(WebKit::WebFrame* frame,
   // * PopulateDocumentStateFromPending
   // * CreateNavigationStateFromPending
   render_view_->didCreateDataSource(frame, datasource);
+
+  // Create the serviceworker's per-document network observing object.
+  scoped_ptr<ServiceWorkerNetworkProvider>
+      network_provider(new ServiceWorkerNetworkProvider());
+  ServiceWorkerNetworkProvider::AttachToDocumentState(
+      DocumentState::FromDataSource(datasource),
+      network_provider.Pass());
 }
 
-void RenderFrameImpl::didStartProvisionalLoad(WebKit::WebFrame* frame) {
+void RenderFrameImpl::didStartProvisionalLoad(blink::WebLocalFrame* frame,
+                                              bool is_transition_navigation) {
+  DCHECK(!frame_ || frame_ == frame);
   WebDataSource* ds = frame->provisionalDataSource();
 
   // In fast/loader/stop-provisional-loads.html, we abort the load before this
@@ -408,6 +1856,7 @@ void RenderFrameImpl::didStartProvisionalLoad(WebKit::WebFrame* frame) {
 
   // We should only navigate to swappedout:// when is_swapped_out_ is true.
   CHECK((ds->request().url() != GURL(kSwappedOutURL)) ||
+        is_swapped_out_ ||
         render_view_->is_swapped_out()) <<
         "Heard swappedout:// when not swapped out.";
 
@@ -434,160 +1883,615 @@ void RenderFrameImpl::didStartProvisionalLoad(WebKit::WebFrame* frame) {
         PAGE_TRANSITION_AUTO_SUBFRAME);
   }
 
-  FOR_EACH_OBSERVER(
-      RenderViewObserver, render_view_->observers(),
-      DidStartProvisionalLoad(frame));
+  FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(),
+                    DidStartProvisionalLoad(frame));
+  FOR_EACH_OBSERVER(RenderFrameObserver, observers_, DidStartProvisionalLoad());
 
   Send(new FrameHostMsg_DidStartProvisionalLoadForFrame(
-       routing_id_, frame->identifier(),
-       frame->parent() ? frame->parent()->identifier() : -1,
-       is_top_most, ds->request().url()));
+       routing_id_, ds->request().url(), is_transition_navigation));
 }
 
 void RenderFrameImpl::didReceiveServerRedirectForProvisionalLoad(
-    WebKit::WebFrame* frame) {
-  // TODO(nasko): Move implementation here. Needed state:
-  // * page_id_
-  render_view_->didReceiveServerRedirectForProvisionalLoad(frame);
+    blink::WebLocalFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
+  render_view_->history_controller()->RemoveChildrenForRedirect(this);
+  if (frame->parent())
+    return;
+  // Received a redirect on the main frame.
+  WebDataSource* data_source = frame->provisionalDataSource();
+  if (!data_source) {
+    // Should only be invoked when we have a data source.
+    NOTREACHED();
+    return;
+  }
+  std::vector<GURL> redirects;
+  GetRedirectChain(data_source, &redirects);
+  if (redirects.size() >= 2) {
+    Send(new FrameHostMsg_DidRedirectProvisionalLoad(
+        routing_id_,
+        render_view_->page_id_,
+        redirects[redirects.size() - 2],
+        redirects.back()));
+  }
 }
 
-void RenderFrameImpl::didFailProvisionalLoad(
-    WebKit::WebFrame* frame,
-    const WebKit::WebURLError& error) {
-  // TODO(nasko): Move implementation here. Needed state:
-  // * page_id_
-  // * pending_navigation_params_
-  // Needed methods
-  // * MaybeLoadAlternateErrorPage
-  // * LoadNavigationErrorPage
-  render_view_->didFailProvisionalLoad(frame, error);
+void RenderFrameImpl::didFailProvisionalLoad(blink::WebLocalFrame* frame,
+                                             const blink::WebURLError& error) {
+  DCHECK(!frame_ || frame_ == frame);
+  WebDataSource* ds = frame->provisionalDataSource();
+  DCHECK(ds);
+
+  const WebURLRequest& failed_request = ds->request();
+
+  // Notify the browser that we failed a provisional load with an error.
+  //
+  // Note: It is important this notification occur before DidStopLoading so the
+  //       SSL manager can react to the provisional load failure before being
+  //       notified the load stopped.
+  //
+  FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(),
+                    DidFailProvisionalLoad(frame, error));
+  FOR_EACH_OBSERVER(RenderFrameObserver, observers_,
+                    DidFailProvisionalLoad(error));
+
+  bool show_repost_interstitial =
+      (error.reason == net::ERR_CACHE_MISS &&
+       EqualsASCII(failed_request.httpMethod(), "POST"));
+
+  FrameHostMsg_DidFailProvisionalLoadWithError_Params params;
+  params.error_code = error.reason;
+  GetContentClient()->renderer()->GetNavigationErrorStrings(
+      render_view_.get(),
+      frame,
+      failed_request,
+      error,
+      NULL,
+      &params.error_description);
+  params.url = error.unreachableURL;
+  params.showing_repost_interstitial = show_repost_interstitial;
+  Send(new FrameHostMsg_DidFailProvisionalLoadWithError(
+      routing_id_, params));
+
+  // Don't display an error page if this is simply a cancelled load.  Aside
+  // from being dumb, WebCore doesn't expect it and it will cause a crash.
+  if (error.reason == net::ERR_ABORTED)
+    return;
+
+  // Don't display "client blocked" error page if browser has asked us not to.
+  if (error.reason == net::ERR_BLOCKED_BY_CLIENT &&
+      render_view_->renderer_preferences_.disable_client_blocked_error_page) {
+    return;
+  }
+
+  // Allow the embedder to suppress an error page.
+  if (GetContentClient()->renderer()->ShouldSuppressErrorPage(this,
+          error.unreachableURL)) {
+    return;
+  }
+
+  if (RenderThreadImpl::current() &&
+      RenderThreadImpl::current()->layout_test_mode()) {
+    return;
+  }
+
+  // Make sure we never show errors in view source mode.
+  frame->enableViewSourceMode(false);
+
+  DocumentState* document_state = DocumentState::FromDataSource(ds);
+  NavigationState* navigation_state = document_state->navigation_state();
+
+  // If this is a failed back/forward/reload navigation, then we need to do a
+  // 'replace' load.  This is necessary to avoid messing up session history.
+  // Otherwise, we do a normal load, which simulates a 'go' navigation as far
+  // as session history is concerned.
+  //
+  // AUTO_SUBFRAME loads should always be treated as loads that do not advance
+  // the page id.
+  //
+  // TODO(davidben): This should also take the failed navigation's replacement
+  // state into account, if a location.replace() failed.
+  bool replace =
+      navigation_state->pending_page_id() != -1 ||
+      PageTransitionCoreTypeIs(navigation_state->transition_type(),
+                               PAGE_TRANSITION_AUTO_SUBFRAME);
+
+  // If we failed on a browser initiated request, then make sure that our error
+  // page load is regarded as the same browser initiated request.
+  if (!navigation_state->is_content_initiated()) {
+    render_view_->pending_navigation_params_.reset(
+        new FrameMsg_Navigate_Params);
+    render_view_->pending_navigation_params_->page_id =
+        navigation_state->pending_page_id();
+    render_view_->pending_navigation_params_->pending_history_list_offset =
+        navigation_state->pending_history_list_offset();
+    render_view_->pending_navigation_params_->should_clear_history_list =
+        navigation_state->history_list_was_cleared();
+    render_view_->pending_navigation_params_->transition =
+        navigation_state->transition_type();
+    render_view_->pending_navigation_params_->request_time =
+        document_state->request_time();
+    render_view_->pending_navigation_params_->should_replace_current_entry =
+        replace;
+  }
+
+  // Load an error page.
+  LoadNavigationErrorPage(failed_request, error, replace);
 }
 
-void RenderFrameImpl::didCommitProvisionalLoad(WebKit::WebFrame* frame,
-                                               bool is_new_navigation) {
-  // TODO(nasko): Move implementation here. Needed state:
-  // * page_id_
-  // * next_page_id_
-  // * history_list_offset_
-  // * history_list_length_
-  // * history_page_ids_
-  // Needed methods
-  // * webview
-  // * UpdateSessionHistory
-  // * GetLoadingUrl
-  render_view_->didCommitProvisionalLoad(frame, is_new_navigation);
+void RenderFrameImpl::didCommitProvisionalLoad(
+    blink::WebLocalFrame* frame,
+    const blink::WebHistoryItem& item,
+    blink::WebHistoryCommitType commit_type) {
+  DCHECK(!frame_ || frame_ == frame);
+  DocumentState* document_state =
+      DocumentState::FromDataSource(frame->dataSource());
+  NavigationState* navigation_state = document_state->navigation_state();
+
+  // When we perform a new navigation, we need to update the last committed
+  // session history entry with state for the page we are leaving. Do this
+  // before updating the HistoryController state.
+  render_view_->UpdateSessionHistory(frame);
+
+  render_view_->history_controller()->UpdateForCommit(this, item, commit_type,
+      navigation_state->was_within_same_page());
+
+  InternalDocumentStateData* internal_data =
+      InternalDocumentStateData::FromDocumentState(document_state);
+
+  if (document_state->commit_load_time().is_null())
+    document_state->set_commit_load_time(Time::Now());
+
+  if (internal_data->must_reset_scroll_and_scale_state()) {
+    render_view_->webview()->resetScrollAndScaleState();
+    internal_data->set_must_reset_scroll_and_scale_state(false);
+  }
+  internal_data->set_use_error_page(false);
+
+  bool is_new_navigation = commit_type == blink::WebStandardCommit;
+  if (is_new_navigation) {
+    // We bump our Page ID to correspond with the new session history entry.
+    render_view_->page_id_ = render_view_->next_page_id_++;
+
+    // Don't update history_page_ids_ (etc) for kSwappedOutURL, since
+    // we don't want to forget the entry that was there, and since we will
+    // never come back to kSwappedOutURL.  Note that we have to call
+    // UpdateSessionHistory and update page_id_ even in this case, so that
+    // the current entry gets a state update and so that we don't send a
+    // state update to the wrong entry when we swap back in.
+    if (GetLoadingUrl() != GURL(kSwappedOutURL)) {
+      // Advance our offset in session history, applying the length limit.
+      // There is now no forward history.
+      render_view_->history_list_offset_++;
+      if (render_view_->history_list_offset_ >= kMaxSessionHistoryEntries)
+        render_view_->history_list_offset_ = kMaxSessionHistoryEntries - 1;
+      render_view_->history_list_length_ =
+          render_view_->history_list_offset_ + 1;
+      render_view_->history_page_ids_.resize(
+          render_view_->history_list_length_, -1);
+      render_view_->history_page_ids_[render_view_->history_list_offset_] =
+          render_view_->page_id_;
+    }
+  } else {
+    // Inspect the navigation_state on this frame to see if the navigation
+    // corresponds to a session history navigation...  Note: |frame| may or
+    // may not be the toplevel frame, but for the case of capturing session
+    // history, the first committed frame suffices.  We keep track of whether
+    // we've seen this commit before so that only capture session history once
+    // per navigation.
+    //
+    // Note that we need to check if the page ID changed. In the case of a
+    // reload, the page ID doesn't change, and UpdateSessionHistory gets the
+    // previous URL and the current page ID, which would be wrong.
+    if (navigation_state->pending_page_id() != -1 &&
+        navigation_state->pending_page_id() != render_view_->page_id_ &&
+        !navigation_state->request_committed()) {
+      // This is a successful session history navigation!
+      render_view_->page_id_ = navigation_state->pending_page_id();
+
+      render_view_->history_list_offset_ =
+          navigation_state->pending_history_list_offset();
+
+      // If the history list is valid, our list of page IDs should be correct.
+      DCHECK(render_view_->history_list_length_ <= 0 ||
+             render_view_->history_list_offset_ < 0 ||
+             render_view_->history_list_offset_ >=
+                 render_view_->history_list_length_ ||
+             render_view_->history_page_ids_[render_view_->history_list_offset_]
+                  == render_view_->page_id_);
+    }
+  }
+
+  FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers_,
+                    DidCommitProvisionalLoad(frame, is_new_navigation));
+  FOR_EACH_OBSERVER(RenderFrameObserver, observers_,
+                    DidCommitProvisionalLoad(is_new_navigation));
+
+  if (!frame->parent()) {  // Only for top frames.
+    RenderThreadImpl* render_thread_impl = RenderThreadImpl::current();
+    if (render_thread_impl) {  // Can be NULL in tests.
+      render_thread_impl->histogram_customizer()->
+          RenderViewNavigatedToHost(GURL(GetLoadingUrl()).host(),
+                                    RenderViewImpl::GetRenderViewCount());
+    }
+  }
+
+  // Remember that we've already processed this request, so we don't update
+  // the session history again.  We do this regardless of whether this is
+  // a session history navigation, because if we attempted a session history
+  // navigation without valid HistoryItem state, WebCore will think it is a
+  // new navigation.
+  navigation_state->set_request_committed(true);
+
+  SendDidCommitProvisionalLoad(frame);
+
+  // Check whether we have new encoding name.
+  UpdateEncoding(frame, frame->view()->pageEncoding().utf8());
 }
 
-void RenderFrameImpl::didClearWindowObject(WebKit::WebFrame* frame) {
+void RenderFrameImpl::didClearWindowObject(blink::WebLocalFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
   // TODO(nasko): Move implementation here. Needed state:
   // * enabled_bindings_
   // * dom_automation_controller_
   // * stats_collection_controller_
+
   render_view_->didClearWindowObject(frame);
+
+  if (render_view_->GetEnabledBindings() & BINDINGS_POLICY_DOM_AUTOMATION)
+    DomAutomationController::Install(this, frame);
+
+  FOR_EACH_OBSERVER(RenderFrameObserver, observers_, DidClearWindowObject());
 }
 
-void RenderFrameImpl::didCreateDocumentElement(WebKit::WebFrame* frame) {
+void RenderFrameImpl::didCreateDocumentElement(blink::WebLocalFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
+
   // Notify the browser about non-blank documents loading in the top frame.
   GURL url = frame->document().url();
-  if (url.is_valid() && url.spec() != kAboutBlankURL) {
+  if (url.is_valid() && url.spec() != url::kAboutBlankURL) {
     // TODO(nasko): Check if webview()->mainFrame() is the same as the
     // frame->tree()->top().
-    if (frame == render_view_->webview()->mainFrame()) {
+    blink::WebFrame* main_frame = render_view_->webview()->mainFrame();
+    if (frame == main_frame) {
+      // For now, don't remember plugin zoom values.  We don't want to mix them
+      // with normal web content (i.e. a fixed layout plugin would usually want
+      // them different).
       render_view_->Send(new ViewHostMsg_DocumentAvailableInMainFrame(
-          render_view_->GetRoutingID()));
+          render_view_->GetRoutingID(),
+          main_frame->document().isPluginDocument()));
     }
   }
 
-  // Call back to RenderViewImpl for observers to be notified.
-  // TODO(nasko): Remove once we have RenderFrameObserver.
-  render_view_->didCreateDocumentElement(frame);
+  FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(),
+                    DidCreateDocumentElement(frame));
 }
 
-void RenderFrameImpl::didReceiveTitle(WebKit::WebFrame* frame,
-                                      const WebKit::WebString& title,
-                                      WebKit::WebTextDirection direction) {
-  // TODO(nasko): Investigate wheather implementation should move here.
-  render_view_->didReceiveTitle(frame, title, direction);
+void RenderFrameImpl::didReceiveTitle(blink::WebLocalFrame* frame,
+                                      const blink::WebString& title,
+                                      blink::WebTextDirection direction) {
+  DCHECK(!frame_ || frame_ == frame);
+  // Ignore all but top level navigations.
+  if (!frame->parent()) {
+    base::string16 title16 = title;
+    base::debug::TraceLog::GetInstance()->UpdateProcessLabel(
+        routing_id_, base::UTF16ToUTF8(title16));
+
+    base::string16 shortened_title = title16.substr(0, kMaxTitleChars);
+    Send(new FrameHostMsg_UpdateTitle(routing_id_,
+                                      render_view_->page_id_,
+                                      shortened_title, direction));
+  }
+
+  // Also check whether we have new encoding name.
+  UpdateEncoding(frame, frame->view()->pageEncoding().utf8());
 }
 
-void RenderFrameImpl::didChangeIcon(WebKit::WebFrame* frame,
-                                    WebKit::WebIconURL::Type icon_type) {
+void RenderFrameImpl::didChangeIcon(blink::WebLocalFrame* frame,
+                                    blink::WebIconURL::Type icon_type) {
+  DCHECK(!frame_ || frame_ == frame);
   // TODO(nasko): Investigate wheather implementation should move here.
   render_view_->didChangeIcon(frame, icon_type);
 }
 
-void RenderFrameImpl::didFinishDocumentLoad(WebKit::WebFrame* frame) {
-  // TODO(nasko): Move implementation here. No state needed, just observers
-  // notification in before updating encoding.
-  render_view_->didFinishDocumentLoad(frame);
+void RenderFrameImpl::didFinishDocumentLoad(blink::WebLocalFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
+  WebDataSource* ds = frame->dataSource();
+  DocumentState* document_state = DocumentState::FromDataSource(ds);
+  document_state->set_finish_document_load_time(Time::Now());
+
+  Send(new FrameHostMsg_DidFinishDocumentLoad(routing_id_));
+
+  FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(),
+                    DidFinishDocumentLoad(frame));
+  FOR_EACH_OBSERVER(RenderFrameObserver, observers_, DidFinishDocumentLoad());
+
+  // Check whether we have new encoding name.
+  UpdateEncoding(frame, frame->view()->pageEncoding().utf8());
 }
 
-void RenderFrameImpl::didHandleOnloadEvents(WebKit::WebFrame* frame) {
-  // TODO(nasko): Move implementation here. Needed state:
-  // * page_id_
-  render_view_->didHandleOnloadEvents(frame);
+void RenderFrameImpl::didHandleOnloadEvents(blink::WebLocalFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
+  if (!frame->parent())
+    Send(new FrameHostMsg_DocumentOnLoadCompleted(routing_id_));
 }
 
-void RenderFrameImpl::didFailLoad(WebKit::WebFrame* frame,
-                                  const WebKit::WebURLError& error) {
+void RenderFrameImpl::didFailLoad(blink::WebLocalFrame* frame,
+                                  const blink::WebURLError& error) {
+  DCHECK(!frame_ || frame_ == frame);
   // TODO(nasko): Move implementation here. No state needed.
-  render_view_->didFailLoad(frame, error);
-}
+  WebDataSource* ds = frame->dataSource();
+  DCHECK(ds);
+
+  FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(),
+                    DidFailLoad(frame, error));
+
+  const WebURLRequest& failed_request = ds->request();
+  base::string16 error_description;
+  GetContentClient()->renderer()->GetNavigationErrorStrings(
+      render_view_.get(),
+      frame,
+      failed_request,
+      error,
+      NULL,
+      &error_description);
+  Send(new FrameHostMsg_DidFailLoadWithError(routing_id_,
+                                             failed_request.url(),
+                                             error.reason,
+                                             error_description));
+}
+
+void RenderFrameImpl::didFinishLoad(blink::WebLocalFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
+  WebDataSource* ds = frame->dataSource();
+  DocumentState* document_state = DocumentState::FromDataSource(ds);
+  if (document_state->finish_load_time().is_null()) {
+    if (!frame->parent()) {
+      TRACE_EVENT_INSTANT0("WebCore", "LoadFinished",
+                           TRACE_EVENT_SCOPE_PROCESS);
+    }
+    document_state->set_finish_load_time(Time::Now());
+  }
+
+  FOR_EACH_OBSERVER(RenderViewObserver, render_view_->observers(),
+                    DidFinishLoad(frame));
+  FOR_EACH_OBSERVER(RenderFrameObserver, observers_, DidFinishLoad());
 
-void RenderFrameImpl::didFinishLoad(WebKit::WebFrame* frame) {
-  // TODO(nasko): Move implementation here. No state needed, just observers
-  // notification before sending message to the browser process.
-  render_view_->didFinishLoad(frame);
+  // Don't send this message while the frame is swapped out.
+  if (is_swapped_out())
+    return;
+
+  Send(new FrameHostMsg_DidFinishLoad(routing_id_,
+                                      ds->request().url()));
 }
 
-void RenderFrameImpl::didNavigateWithinPage(WebKit::WebFrame* frame,
-                                            bool is_new_navigation) {
-  // TODO(nasko): Move implementation here. No state needed, just observers
-  // notification before sending message to the browser process.
-  render_view_->didNavigateWithinPage(frame, is_new_navigation);
+void RenderFrameImpl::didNavigateWithinPage(blink::WebLocalFrame* frame,
+    const blink::WebHistoryItem& item,
+    blink::WebHistoryCommitType commit_type) {
+  DCHECK(!frame_ || frame_ == frame);
+  // If this was a reference fragment navigation that we initiated, then we
+  // could end up having a non-null pending navigation params.  We just need to
+  // update the ExtraData on the datasource so that others who read the
+  // ExtraData will get the new NavigationState.  Similarly, if we did not
+  // initiate this navigation, then we need to take care to reset any pre-
+  // existing navigation state to a content-initiated navigation state.
+  // DidCreateDataSource conveniently takes care of this for us.
+  didCreateDataSource(frame, frame->dataSource());
+
+  DocumentState* document_state =
+      DocumentState::FromDataSource(frame->dataSource());
+  NavigationState* new_state = document_state->navigation_state();
+  new_state->set_was_within_same_page(true);
+
+  didCommitProvisionalLoad(frame, item, commit_type);
 }
 
-void RenderFrameImpl::didUpdateCurrentHistoryItem(WebKit::WebFrame* frame) {
+void RenderFrameImpl::didUpdateCurrentHistoryItem(blink::WebLocalFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
   // TODO(nasko): Move implementation here. Needed methods:
   // * StartNavStateSyncTimerIfNecessary
   render_view_->didUpdateCurrentHistoryItem(frame);
 }
 
-void RenderFrameImpl::willRequestAfterPreconnect(
-    WebKit::WebFrame* frame,
-    WebKit::WebURLRequest& request) {
-  WebKit::WebReferrerPolicy referrer_policy = WebKit::WebReferrerPolicyDefault;
-  WebString custom_user_agent;
+void RenderFrameImpl::addNavigationTransitionData(
+    const blink::WebString& allowed_destination_host_pattern,
+    const blink::WebString& selector,
+    const blink::WebString& markup) {
+  Send(new FrameHostMsg_AddNavigationTransitionData(
+      routing_id_, allowed_destination_host_pattern.utf8(), selector.utf8(),
+      markup.utf8()));
+}
 
-  if (request.extraData()) {
-    // This will only be called before willSendRequest, so only ExtraData
-    // members we have to copy here is on WebURLRequestExtraDataImpl.
-    webkit_glue::WebURLRequestExtraDataImpl* old_extra_data =
-        static_cast<webkit_glue::WebURLRequestExtraDataImpl*>(
-            request.extraData());
+void RenderFrameImpl::didChangeThemeColor() {
+  if (frame_->parent())
+    return;
 
-    referrer_policy = old_extra_data->referrer_policy();
-    custom_user_agent = old_extra_data->custom_user_agent();
+  Send(new FrameHostMsg_DidChangeThemeColor(
+      routing_id_, frame_->document().themeColor()));
+}
+
+void RenderFrameImpl::requestNotificationPermission(
+    const blink::WebSecurityOrigin& origin,
+    blink::WebNotificationPermissionCallback* callback) {
+  if (!notification_permission_dispatcher_) {
+    notification_permission_dispatcher_ =
+        new NotificationPermissionDispatcher(this);
+  }
+
+  notification_permission_dispatcher_->RequestPermission(origin, callback);
+}
+
+blink::WebNotificationPresenter* RenderFrameImpl::notificationPresenter() {
+  return notification_provider_;
+}
+
+void RenderFrameImpl::didChangeSelection(bool is_empty_selection) {
+  if (!GetRenderWidget()->handling_input_event() && !handling_select_range_)
+    return;
+
+  if (is_empty_selection)
+    selection_text_.clear();
+
+  // UpdateTextInputState should be called before SyncSelectionIfRequired.
+  // UpdateTextInputState may send TextInputStateChanged to notify the focus
+  // was changed, and SyncSelectionIfRequired may send SelectionChanged
+  // to notify the selection was changed.  Focus change should be notified
+  // before selection change.
+  GetRenderWidget()->UpdateTextInputState(
+      RenderWidget::NO_SHOW_IME, RenderWidget::FROM_NON_IME);
+  SyncSelectionIfRequired();
+}
+
+blink::WebColorChooser* RenderFrameImpl::createColorChooser(
+    blink::WebColorChooserClient* client,
+    const blink::WebColor& initial_color,
+    const blink::WebVector<blink::WebColorSuggestion>& suggestions) {
+  RendererWebColorChooserImpl* color_chooser =
+      new RendererWebColorChooserImpl(this, client);
+  std::vector<content::ColorSuggestion> color_suggestions;
+  for (size_t i = 0; i < suggestions.size(); i++) {
+    color_suggestions.push_back(content::ColorSuggestion(suggestions[i]));
+  }
+  color_chooser->Open(static_cast<SkColor>(initial_color), color_suggestions);
+  return color_chooser;
+}
+
+void RenderFrameImpl::runModalAlertDialog(const blink::WebString& message) {
+  RunJavaScriptMessage(JAVASCRIPT_MESSAGE_TYPE_ALERT,
+                       message,
+                       base::string16(),
+                       frame_->document().url(),
+                       NULL);
+}
+
+bool RenderFrameImpl::runModalConfirmDialog(const blink::WebString& message) {
+  return RunJavaScriptMessage(JAVASCRIPT_MESSAGE_TYPE_CONFIRM,
+                              message,
+                              base::string16(),
+                              frame_->document().url(),
+                              NULL);
+}
+
+bool RenderFrameImpl::runModalPromptDialog(
+    const blink::WebString& message,
+    const blink::WebString& default_value,
+    blink::WebString* actual_value) {
+  base::string16 result;
+  bool ok = RunJavaScriptMessage(JAVASCRIPT_MESSAGE_TYPE_PROMPT,
+                                 message,
+                                 default_value,
+                                 frame_->document().url(),
+                                 &result);
+  if (ok)
+    actual_value->assign(result);
+  return ok;
+}
+
+bool RenderFrameImpl::runModalBeforeUnloadDialog(
+    bool is_reload,
+    const blink::WebString& message) {
+  // If we are swapping out, we have already run the beforeunload handler.
+  // TODO(creis): Fix OnSwapOut to clear the frame without running beforeunload
+  // at all, to avoid running it twice.
+  if (render_view()->is_swapped_out_)
+    return true;
+
+  // Don't allow further dialogs if we are waiting to swap out, since the
+  // PageGroupLoadDeferrer in our stack prevents it.
+  if (render_view()->suppress_dialogs_until_swap_out_)
+    return false;
+
+  bool success = false;
+  // This is an ignored return value, but is included so we can accept the same
+  // response as RunJavaScriptMessage.
+  base::string16 ignored_result;
+  render_view()->SendAndRunNestedMessageLoop(
+      new FrameHostMsg_RunBeforeUnloadConfirm(
+          routing_id_, frame_->document().url(), message, is_reload,
+          &success, &ignored_result));
+  return success;
+}
+
+void RenderFrameImpl::showContextMenu(const blink::WebContextMenuData& data) {
+  ContextMenuParams params = ContextMenuParamsBuilder::Build(data);
+  params.source_type = GetRenderWidget()->context_menu_source_type();
+  GetRenderWidget()->OnShowHostContextMenu(&params);
+  if (GetRenderWidget()->has_host_context_menu_location()) {
+    params.x = GetRenderWidget()->host_context_menu_location().x();
+    params.y = GetRenderWidget()->host_context_menu_location().y();
   }
 
-  bool was_after_preconnect_request = true;
-  // The args after |was_after_preconnect_request| are not used, and set to
-  // correct values at |willSendRequest|.
-  request.setExtraData(new webkit_glue::WebURLRequestExtraDataImpl(
-      referrer_policy, custom_user_agent, was_after_preconnect_request));
+  // Plugins, e.g. PDF, don't currently update the render view when their
+  // selected text changes, but the context menu params do contain the updated
+  // selection. If that's the case, update the render view's state just prior
+  // to showing the context menu.
+  // TODO(asvitkine): http://crbug.com/152432
+  if (ShouldUpdateSelectionTextFromContextMenuParams(
+          selection_text_, selection_text_offset_, selection_range_, params)) {
+    selection_text_ = params.selection_text;
+    // TODO(asvitkine): Text offset and range is not available in this case.
+    selection_text_offset_ = 0;
+    selection_range_ = gfx::Range(0, selection_text_.length());
+    // This IPC is dispatched by RenderWidetHost, so use its routing ID.
+    Send(new ViewHostMsg_SelectionChanged(
+        GetRenderWidget()->routing_id(), selection_text_,
+        selection_text_offset_, selection_range_));
+  }
+
+  // Serializing a GURL longer than kMaxURLChars will fail, so don't do
+  // it.  We replace it with an empty GURL so the appropriate items are disabled
+  // in the context menu.
+  // TODO(jcivelli): http://crbug.com/45160 This prevents us from saving large
+  //                 data encoded images.  We should have a way to save them.
+  if (params.src_url.spec().size() > GetMaxURLChars())
+    params.src_url = GURL();
+  context_menu_node_ = data.node;
+
+#if defined(OS_ANDROID)
+  gfx::Rect start_rect;
+  gfx::Rect end_rect;
+  GetRenderWidget()->GetSelectionBounds(&start_rect, &end_rect);
+  params.selection_start = gfx::Point(start_rect.x(), start_rect.bottom());
+  params.selection_end = gfx::Point(end_rect.right(), end_rect.bottom());
+#endif
+
+  Send(new FrameHostMsg_ContextMenu(routing_id_, params));
+}
+
+void RenderFrameImpl::clearContextMenu() {
+  context_menu_node_.reset();
 }
 
 void RenderFrameImpl::willSendRequest(
-    WebKit::WebFrame* frame,
+    blink::WebLocalFrame* frame,
     unsigned identifier,
-    WebKit::WebURLRequest& request,
-    const WebKit::WebURLResponse& redirect_response) {
+    blink::WebURLRequest& request,
+    const blink::WebURLResponse& redirect_response) {
+  DCHECK(!frame_ || frame_ == frame);
   // The request my be empty during tests.
   if (request.url().isEmpty())
     return;
 
+  // Set the first party for cookies url if it has not been set yet (new
+  // requests). For redirects, it is updated by WebURLLoaderImpl.
+  if (request.firstPartyForCookies().isEmpty()) {
+    if (request.frameType() == blink::WebURLRequest::FrameTypeTopLevel) {
+      request.setFirstPartyForCookies(request.url());
+    } else {
+      // TODO(nasko): When the top-level frame is remote, there is no document.
+      // This is broken and should be fixed to propagate the first party.
+      WebFrame* top = frame->top();
+      if (top->isWebLocalFrame()) {
+        request.setFirstPartyForCookies(
+            frame->top()->document().firstPartyForCookies());
+      }
+    }
+  }
+
   WebFrame* top_frame = frame->top();
-  if (!top_frame)
+  // TODO(nasko): Hack around asking about top-frame data source. This means
+  // for out-of-process iframes we are treating the current frame as the
+  // top-level frame, which is wrong.
+  if (!top_frame || top_frame->isWebRemoteFrame())
     top_frame = frame;
   WebDataSource* provisional_data_source = top_frame->provisionalDataSource();
   WebDataSource* top_data_source = top_frame->dataSource();
@@ -616,26 +2520,15 @@ void RenderFrameImpl::willSendRequest(
   if (internal_data->is_cache_policy_override_set())
     request.setCachePolicy(internal_data->cache_policy_override());
 
-  WebKit::WebReferrerPolicy referrer_policy;
-  if (internal_data->is_referrer_policy_set()) {
-    referrer_policy = internal_data->referrer_policy();
-    internal_data->clear_referrer_policy();
-  } else {
-    referrer_policy = frame->document().referrerPolicy();
-  }
-
   // The request's extra data may indicate that we should set a custom user
   // agent. This needs to be done here, after WebKit is through with setting the
   // user agent on its own.
   WebString custom_user_agent;
-  bool was_after_preconnect_request = false;
   if (request.extraData()) {
-    webkit_glue::WebURLRequestExtraDataImpl* old_extra_data =
-        static_cast<webkit_glue::WebURLRequestExtraDataImpl*>(
+    RequestExtraData* old_extra_data =
+        static_cast<RequestExtraData*>(
             request.extraData());
     custom_user_agent = old_extra_data->custom_user_agent();
-    was_after_preconnect_request =
-        old_extra_data->was_after_preconnect_request();
 
     if (!custom_user_agent.isNull()) {
       if (custom_user_agent.isEmpty())
@@ -645,30 +2538,89 @@ void RenderFrameImpl::willSendRequest(
     }
   }
 
-  request.setExtraData(
-      new RequestExtraData(referrer_policy,
-                           custom_user_agent,
-                           was_after_preconnect_request,
-                           (frame == top_frame),
-                           frame->identifier(),
-                           GURL(frame->document().securityOrigin().toString()),
-                           frame->parent() == top_frame,
-                           frame->parent() ? frame->parent()->identifier() : -1,
-                           navigation_state->allow_download(),
-                           transition_type,
-                           navigation_state->transferred_request_child_id(),
-                           navigation_state->transferred_request_request_id()));
+  // Add the default accept header for frame request if it has not been set
+  // already.
+  if ((request.frameType() == blink::WebURLRequest::FrameTypeTopLevel ||
+       request.frameType() == blink::WebURLRequest::FrameTypeNested) &&
+      request.httpHeaderField(WebString::fromUTF8(kAcceptHeader)).isEmpty()) {
+    request.setHTTPHeaderField(WebString::fromUTF8(kAcceptHeader),
+                               WebString::fromUTF8(kDefaultAcceptHeader));
+  }
+
+  // Add an empty HTTP origin header for non GET methods if none is currently
+  // present.
+  request.addHTTPOriginIfNeeded(WebString());
+
+  // Attach |should_replace_current_entry| state to requests so that, should
+  // this navigation later require a request transfer, all state is preserved
+  // when it is re-created in the new process.
+  bool should_replace_current_entry = false;
+  if (navigation_state->is_content_initiated()) {
+    should_replace_current_entry = data_source->replacesCurrentHistoryItem();
+  } else {
+    // If the navigation is browser-initiated, the NavigationState contains the
+    // correct value instead of the WebDataSource.
+    //
+    // TODO(davidben): Avoid this awkward duplication of state. See comment on
+    // NavigationState::should_replace_current_entry().
+    should_replace_current_entry =
+        navigation_state->should_replace_current_entry();
+  }
+
+  int provider_id = kInvalidServiceWorkerProviderId;
+  if (request.frameType() == blink::WebURLRequest::FrameTypeTopLevel ||
+      request.frameType() == blink::WebURLRequest::FrameTypeNested) {
+    // |provisionalDataSource| may be null in some content::ResourceFetcher
+    // use cases, we don't hook those requests.
+    if (frame->provisionalDataSource()) {
+      ServiceWorkerNetworkProvider* provider =
+          ServiceWorkerNetworkProvider::FromDocumentState(
+              DocumentState::FromDataSource(frame->provisionalDataSource()));
+      provider_id = provider->provider_id();
+    }
+  } else if (frame->dataSource()) {
+    ServiceWorkerNetworkProvider* provider =
+        ServiceWorkerNetworkProvider::FromDocumentState(
+            DocumentState::FromDataSource(frame->dataSource()));
+    provider_id = provider->provider_id();
+  }
+
+  WebFrame* parent = frame->parent();
+  int parent_routing_id = MSG_ROUTING_NONE;
+  if (!parent) {
+    parent_routing_id = -1;
+  } else if (parent->isWebLocalFrame()) {
+    parent_routing_id = FromWebFrame(parent)->GetRoutingID();
+  } else {
+    parent_routing_id = RenderFrameProxy::FromWebFrame(parent)->routing_id();
+  }
+
+  RequestExtraData* extra_data = new RequestExtraData();
+  extra_data->set_visibility_state(render_view_->visibilityState());
+  extra_data->set_custom_user_agent(custom_user_agent);
+  extra_data->set_render_frame_id(routing_id_);
+  extra_data->set_is_main_frame(frame == top_frame);
+  extra_data->set_frame_origin(
+      GURL(frame->document().securityOrigin().toString()));
+  extra_data->set_parent_is_main_frame(frame->parent() == top_frame);
+  extra_data->set_parent_render_frame_id(parent_routing_id);
+  extra_data->set_allow_download(navigation_state->allow_download());
+  extra_data->set_transition_type(transition_type);
+  extra_data->set_should_replace_current_entry(should_replace_current_entry);
+  extra_data->set_transferred_request_child_id(
+      navigation_state->transferred_request_child_id());
+  extra_data->set_transferred_request_request_id(
+      navigation_state->transferred_request_request_id());
+  extra_data->set_service_worker_provider_id(provider_id);
+  request.setExtraData(extra_data);
 
   DocumentState* top_document_state =
       DocumentState::FromDataSource(top_data_source);
   if (top_document_state) {
     // TODO(gavinp): separate out prefetching and prerender field trials
     // if the rel=prerender rel type is sticking around.
-    if (request.targetType() == WebURLRequest::TargetIsPrefetch)
+    if (request.requestContext() == WebURLRequest::RequestContextPrefetch)
       top_document_state->set_was_prefetcher(true);
-
-    if (was_after_preconnect_request)
-      top_document_state->set_was_after_preconnect_request(true);
   }
 
   // This is an instance where we embed a copy of the routing id
@@ -683,19 +2635,28 @@ void RenderFrameImpl::willSendRequest(
         navigation_state->extra_headers().begin(),
         navigation_state->extra_headers().end(), "\n");
         i.GetNext(); ) {
-      request.setHTTPHeaderField(WebString::fromUTF8(i.name()),
-                                 WebString::fromUTF8(i.values()));
+      if (LowerCaseEqualsASCII(i.name(), "referer")) {
+        WebString referrer = WebSecurityPolicy::generateReferrerHeader(
+            blink::WebReferrerPolicyDefault,
+            request.url(),
+            WebString::fromUTF8(i.values()));
+        request.setHTTPReferrer(referrer, blink::WebReferrerPolicyDefault);
+      } else {
+        request.setHTTPHeaderField(WebString::fromUTF8(i.name()),
+                                   WebString::fromUTF8(i.values()));
+      }
     }
   }
 
   if (!render_view_->renderer_preferences_.enable_referrers)
-    request.clearHTTPHeaderField("Referer");
+    request.setHTTPReferrer(WebString(), blink::WebReferrerPolicyDefault);
 }
 
 void RenderFrameImpl::didReceiveResponse(
-    WebKit::WebFrame* frame,
+    blink::WebLocalFrame* frame,
     unsigned identifier,
-    const WebKit::WebURLResponse& response) {
+    const blink::WebURLResponse& response) {
+  DCHECK(!frame_ || frame_ == frame);
   // Only do this for responses that correspond to a provisional data source
   // of the top-most frame.  If we have a provisional data source, then we
   // can't have any sub-resources yet, so we know that this response must
@@ -713,8 +2674,7 @@ void RenderFrameImpl::didReceiveResponse(
   int http_status_code = response.httpStatusCode();
 
   // Record page load flags.
-  WebURLResponseExtraDataImpl* extra_data =
-      RenderViewImpl::GetExtraDataFromResponse(response);
+  WebURLResponseExtraDataImpl* extra_data = GetExtraDataFromResponse(response);
   if (extra_data) {
     document_state->set_was_fetched_via_spdy(
         extra_data->was_fetched_via_spdy());
@@ -738,19 +2698,36 @@ void RenderFrameImpl::didReceiveResponse(
   internal_data->set_use_error_page(true);
 }
 
-void RenderFrameImpl::didFinishResourceLoad(WebKit::WebFrame* frame,
+void RenderFrameImpl::didFinishResourceLoad(blink::WebLocalFrame* frame,
                                             unsigned identifier) {
-  // TODO(nasko): Move implementation here. Needed state:
-  // * devtools_agent_
-  // Needed methods:
-  // * LoadNavigationErrorPage
-  render_view_->didFinishResourceLoad(frame, identifier);
+  DCHECK(!frame_ || frame_ == frame);
+  InternalDocumentStateData* internal_data =
+      InternalDocumentStateData::FromDataSource(frame->dataSource());
+  if (!internal_data->use_error_page())
+    return;
+
+  // Do not show error page when DevTools is attached.
+  if (render_view_->devtools_agent_->IsAttached())
+    return;
+
+  // Display error page, if appropriate.
+  std::string error_domain = "http";
+  int http_status_code = internal_data->http_status_code();
+  if (GetContentClient()->renderer()->HasErrorPage(
+          http_status_code, &error_domain)) {
+    WebURLError error;
+    error.unreachableURL = frame->document().url();
+    error.domain = WebString::fromUTF8(error_domain);
+    error.reason = http_status_code;
+    LoadNavigationErrorPage(frame->dataSource()->request(), error, true);
+  }
 }
 
 void RenderFrameImpl::didLoadResourceFromMemoryCache(
-    WebKit::WebFrame* frame,
-    const WebKit::WebURLRequest& request,
-    const WebKit::WebURLResponse& response) {
+    blink::WebLocalFrame* frame,
+    const blink::WebURLRequest& request,
+    const blink::WebURLResponse& response) {
+  DCHECK(!frame_ || frame_ == frame);
   // The recipients of this message have no use for data: URLs: they don't
   // affect the page's insecure content list and are not in the disk cache. To
   // prevent large (1M+) data: URLs from crashing in the IPC system, we simply
@@ -767,25 +2744,28 @@ void RenderFrameImpl::didLoadResourceFromMemoryCache(
       response.securityInfo(),
       request.httpMethod().utf8(),
       response.mimeType().utf8(),
-      ResourceType::FromTargetType(request.targetType())));
+      WebURLRequestToResourceType(request)));
 }
 
-void RenderFrameImpl::didDisplayInsecureContent(WebKit::WebFrame* frame) {
+void RenderFrameImpl::didDisplayInsecureContent(blink::WebLocalFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
   render_view_->Send(new ViewHostMsg_DidDisplayInsecureContent(
       render_view_->GetRoutingID()));
 }
 
 void RenderFrameImpl::didRunInsecureContent(
-    WebKit::WebFrame* frame,
-    const WebKit::WebSecurityOrigin& origin,
-    const WebKit::WebURL& target) {
+    blink::WebLocalFrame* frame,
+    const blink::WebSecurityOrigin& origin,
+    const blink::WebURL& target) {
+  DCHECK(!frame_ || frame_ == frame);
   render_view_->Send(new ViewHostMsg_DidRunInsecureContent(
       render_view_->GetRoutingID(),
       origin.toString().utf8(),
       target));
 }
 
-void RenderFrameImpl::didAbortLoading(WebKit::WebFrame* frame) {
+void RenderFrameImpl::didAbortLoading(blink::WebLocalFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
 #if defined(ENABLE_PLUGINS)
   if (frame != render_view_->webview()->mainFrame())
     return;
@@ -794,46 +2774,54 @@ void RenderFrameImpl::didAbortLoading(WebKit::WebFrame* frame) {
 #endif
 }
 
-void RenderFrameImpl::didExhaustMemoryAvailableForScript(
-    WebKit::WebFrame* frame) {
-  render_view_->Send(new ViewHostMsg_JSOutOfMemory(
-      render_view_->GetRoutingID()));
-}
-
-void RenderFrameImpl::didCreateScriptContext(WebKit::WebFrame* frame,
+void RenderFrameImpl::didCreateScriptContext(blink::WebLocalFrame* frame,
                                              v8::Handle<v8::Context> context,
                                              int extension_group,
                                              int world_id) {
+  DCHECK(!frame_ || frame_ == frame);
   GetContentClient()->renderer()->DidCreateScriptContext(
       frame, context, extension_group, world_id);
 }
 
-void RenderFrameImpl::willReleaseScriptContext(WebKit::WebFrame* frame,
+void RenderFrameImpl::willReleaseScriptContext(blink::WebLocalFrame* frame,
                                                v8::Handle<v8::Context> context,
                                                int world_id) {
-  GetContentClient()->renderer()->WillReleaseScriptContext(
-      frame, context, world_id);
-}
+  DCHECK(!frame_ || frame_ == frame);
 
-void RenderFrameImpl::didFirstVisuallyNonEmptyLayout(WebKit::WebFrame* frame) {
-  render_view_->didFirstVisuallyNonEmptyLayout(frame);
+  FOR_EACH_OBSERVER(RenderFrameObserver,
+                    observers_,
+                    WillReleaseScriptContext(context, world_id));
 }
 
-void RenderFrameImpl::didChangeContentsSize(WebKit::WebFrame* frame,
-                                            const WebKit::WebSize& size) {
-  // TODO(nasko): Move implementation here. Needed state:
-  // * cached_has_main_frame_horizontal_scrollbar_
-  // * cached_has_main_frame_vertical_scrollbar_
-  render_view_->didChangeContentsSize(frame, size);
+void RenderFrameImpl::didFirstVisuallyNonEmptyLayout(
+    blink::WebLocalFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
+  if (frame->parent())
+    return;
+
+  InternalDocumentStateData* data =
+      InternalDocumentStateData::FromDataSource(frame->dataSource());
+  data->set_did_first_visually_non_empty_layout(true);
+
+#if defined(OS_ANDROID)
+  GetRenderWidget()->DidChangeBodyBackgroundColor(
+      render_view_->webwidget_->backgroundColor());
+#endif
+
+  GetRenderWidget()->QueueMessage(
+      new FrameHostMsg_DidFirstVisuallyNonEmptyPaint(routing_id_),
+      MESSAGE_DELIVERY_POLICY_WITH_VISUAL_STATE);
 }
 
-void RenderFrameImpl::didChangeScrollOffset(WebKit::WebFrame* frame) {
+void RenderFrameImpl::didChangeScrollOffset(blink::WebLocalFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
   // TODO(nasko): Move implementation here. Needed methods:
   // * StartNavStateSyncTimerIfNecessary
   render_view_->didChangeScrollOffset(frame);
 }
 
-void RenderFrameImpl::willInsertBody(WebKit::WebFrame* frame) {
+void RenderFrameImpl::willInsertBody(blink::WebLocalFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
   if (!frame->parent()) {
     render_view_->Send(new ViewHostMsg_WillInsertBody(
         render_view_->GetRoutingID()));
@@ -855,22 +2843,22 @@ void RenderFrameImpl::reportFindInPageMatchCount(int request_id,
 void RenderFrameImpl::reportFindInPageSelection(
     int request_id,
     int active_match_ordinal,
-    const WebKit::WebRect& selection_rect) {
+    const blink::WebRect& selection_rect) {
   render_view_->Send(new ViewHostMsg_Find_Reply(
       render_view_->GetRoutingID(), request_id, -1, selection_rect,
       active_match_ordinal, false));
 }
 
 void RenderFrameImpl::requestStorageQuota(
-    WebKit::WebFrame* frame,
-    WebKit::WebStorageQuotaType type,
+    blink::WebLocalFrame* frame,
+    blink::WebStorageQuotaType type,
     unsigned long long requested_size,
-    WebKit::WebStorageQuotaCallbacks* callbacks) {
-  DCHECK(frame);
+    blink::WebStorageQuotaCallbacks callbacks) {
+  DCHECK(!frame_ || frame_ == frame);
   WebSecurityOrigin origin = frame->document().securityOrigin();
   if (origin.isUnique()) {
     // Unique origins cannot store persistent state.
-    callbacks->didFail(WebKit::WebStorageQuotaErrorAbort);
+    callbacks.didFail(blink::WebStorageQuotaErrorAbort);
     return;
   }
   ChildThread::current()->quota_dispatcher()->RequestStorageQuota(
@@ -880,35 +2868,102 @@ void RenderFrameImpl::requestStorageQuota(
 }
 
 void RenderFrameImpl::willOpenSocketStream(
-    WebKit::WebSocketStreamHandle* handle) {
-  SocketStreamHandleData::AddToHandle(handle, render_view_->GetRoutingID());
+    blink::WebSocketStreamHandle* handle) {
+  WebSocketStreamHandleImpl* impl =
+      static_cast<WebSocketStreamHandleImpl*>(handle);
+  impl->SetUserData(handle, new SocketStreamHandleData(routing_id_));
+}
+
+void RenderFrameImpl::willOpenWebSocket(blink::WebSocketHandle* handle) {
+  WebSocketBridge* impl = static_cast<WebSocketBridge*>(handle);
+  impl->set_render_frame_id(routing_id_);
+}
+
+blink::WebGeolocationClient* RenderFrameImpl::geolocationClient() {
+  if (!geolocation_dispatcher_)
+    geolocation_dispatcher_ = new GeolocationDispatcher(this);
+  return geolocation_dispatcher_;
+}
+
+blink::WebPushClient* RenderFrameImpl::pushClient() {
+  if (!push_messaging_dispatcher_)
+    push_messaging_dispatcher_ = new PushMessagingDispatcher(this);
+  return push_messaging_dispatcher_;
 }
 
 void RenderFrameImpl::willStartUsingPeerConnectionHandler(
-    WebKit::WebFrame* frame,
-    WebKit::WebRTCPeerConnectionHandler* handler) {
+    blink::WebLocalFrame* frame,
+    blink::WebRTCPeerConnectionHandler* handler) {
+  DCHECK(!frame_ || frame_ == frame);
 #if defined(ENABLE_WEBRTC)
   static_cast<RTCPeerConnectionHandler*>(handler)->associateWithFrame(frame);
 #endif
 }
 
+blink::WebUserMediaClient* RenderFrameImpl::userMediaClient() {
+  if (!web_user_media_client_)
+    InitializeUserMediaClient();
+  return web_user_media_client_;
+}
+
+blink::WebMIDIClient* RenderFrameImpl::webMIDIClient() {
+  if (!midi_dispatcher_)
+    midi_dispatcher_ = new MidiDispatcher(this);
+  return midi_dispatcher_;
+}
+
 bool RenderFrameImpl::willCheckAndDispatchMessageEvent(
-    WebKit::WebFrame* sourceFrame,
-    WebKit::WebFrame* targetFrame,
-    WebKit::WebSecurityOrigin targetOrigin,
-    WebKit::WebDOMMessageEvent event) {
-  // TODO(nasko): Move implementation here. Needed state:
-  // * is_swapped_out_
-  return render_view_->willCheckAndDispatchMessageEvent(
-      sourceFrame, targetFrame, targetOrigin, event);
+    blink::WebLocalFrame* source_frame,
+    blink::WebFrame* target_frame,
+    blink::WebSecurityOrigin target_origin,
+    blink::WebDOMMessageEvent event) {
+  DCHECK(!frame_ || frame_ == target_frame);
+
+  if (!render_view_->is_swapped_out_)
+    return false;
+
+  ViewMsg_PostMessage_Params params;
+  params.is_data_raw_string = false;
+  params.data = event.data().toString();
+  params.source_origin = event.origin();
+  if (!target_origin.isNull())
+    params.target_origin = target_origin.toString();
+
+  blink::WebMessagePortChannelArray channels = event.releaseChannels();
+  if (!channels.isEmpty()) {
+    std::vector<int> message_port_ids(channels.size());
+     // Extract the port IDs from the channel array.
+     for (size_t i = 0; i < channels.size(); ++i) {
+       WebMessagePortChannelImpl* webchannel =
+           static_cast<WebMessagePortChannelImpl*>(channels[i]);
+       message_port_ids[i] = webchannel->message_port_id();
+       webchannel->QueueMessages();
+       DCHECK_NE(message_port_ids[i], MSG_ROUTING_NONE);
+     }
+     params.message_port_ids = message_port_ids;
+  }
+
+  // Include the routing ID for the source frame (if one exists), which the
+  // browser process will translate into the routing ID for the equivalent
+  // frame in the target process.
+  params.source_routing_id = MSG_ROUTING_NONE;
+  if (source_frame) {
+    RenderViewImpl* source_view =
+        RenderViewImpl::FromWebView(source_frame->view());
+    if (source_view)
+      params.source_routing_id = source_view->routing_id();
+  }
+
+  Send(new ViewHostMsg_RouteMessageEvent(render_view_->routing_id_, params));
+  return true;
 }
 
-WebKit::WebString RenderFrameImpl::userAgentOverride(
-    WebKit::WebFrame* frame,
-    const WebKit::WebURL& url) {
+blink::WebString RenderFrameImpl::userAgentOverride(blink::WebLocalFrame* frame,
+                                                    const blink::WebURL& url) {
+  DCHECK(!frame_ || frame_ == frame);
   if (!render_view_->webview() || !render_view_->webview()->mainFrame() ||
       render_view_->renderer_preferences_.user_agent_override.empty()) {
-    return WebKit::WebString();
+    return blink::WebString();
   }
 
   // If we're in the middle of committing a load, the data source we need
@@ -925,16 +2980,19 @@ WebKit::WebString RenderFrameImpl::userAgentOverride(
   if (internal_data && internal_data->is_overriding_user_agent())
     return WebString::fromUTF8(
         render_view_->renderer_preferences_.user_agent_override);
-  return WebKit::WebString();
+  return blink::WebString();
 }
 
-WebKit::WebString RenderFrameImpl::doNotTrackValue(WebKit::WebFrame* frame) {
+blink::WebString RenderFrameImpl::doNotTrackValue(blink::WebLocalFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
   if (render_view_->renderer_preferences_.enable_do_not_track)
     return WebString::fromUTF8("1");
   return WebString();
 }
 
-bool RenderFrameImpl::allowWebGL(WebKit::WebFrame* frame, bool default_value) {
+bool RenderFrameImpl::allowWebGL(blink::WebLocalFrame* frame,
+                                 bool default_value) {
+  DCHECK(!frame_ || frame_ == frame);
   if (!default_value)
     return false;
 
@@ -947,12 +3005,724 @@ bool RenderFrameImpl::allowWebGL(WebKit::WebFrame* frame, bool default_value) {
   return !blocked;
 }
 
-void RenderFrameImpl::didLoseWebGLContext(WebKit::WebFrame* frame,
+void RenderFrameImpl::didLoseWebGLContext(blink::WebLocalFrame* frame,
                                           int arb_robustness_status_code) {
+  DCHECK(!frame_ || frame_ == frame);
   render_view_->Send(new ViewHostMsg_DidLose3DContext(
       GURL(frame->top()->document().securityOrigin().toString()),
       THREE_D_API_TYPE_WEBGL,
       arb_robustness_status_code));
 }
 
+void RenderFrameImpl::forwardInputEvent(const blink::WebInputEvent* event) {
+  Send(new FrameHostMsg_ForwardInputEvent(routing_id_, event));
+}
+
+void RenderFrameImpl::initializeChildFrame(const blink::WebRect& frame_rect,
+                                           float scale_factor) {
+  Send(new FrameHostMsg_InitializeChildFrame(
+      routing_id_, frame_rect, scale_factor));
+}
+
+blink::WebScreenOrientationClient*
+    RenderFrameImpl::webScreenOrientationClient() {
+  if (!screen_orientation_dispatcher_)
+    screen_orientation_dispatcher_ = new ScreenOrientationDispatcher(this);
+  return screen_orientation_dispatcher_;
+}
+
+void RenderFrameImpl::DidPlay(blink::WebMediaPlayer* player) {
+  Send(new FrameHostMsg_MediaPlayingNotification(
+      routing_id_, reinterpret_cast<int64>(player), player->hasVideo(),
+      player->hasAudio()));
+}
+
+void RenderFrameImpl::DidPause(blink::WebMediaPlayer* player) {
+  Send(new FrameHostMsg_MediaPausedNotification(
+      routing_id_, reinterpret_cast<int64>(player)));
+}
+
+void RenderFrameImpl::PlayerGone(blink::WebMediaPlayer* player) {
+  DidPause(player);
+}
+
+void RenderFrameImpl::AddObserver(RenderFrameObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void RenderFrameImpl::RemoveObserver(RenderFrameObserver* observer) {
+  observer->RenderFrameGone();
+  observers_.RemoveObserver(observer);
+}
+
+void RenderFrameImpl::OnStop() {
+  FOR_EACH_OBSERVER(RenderFrameObserver, observers_, OnStop());
+}
+
+void RenderFrameImpl::WasHidden() {
+  FOR_EACH_OBSERVER(RenderFrameObserver, observers_, WasHidden());
+}
+
+void RenderFrameImpl::WasShown() {
+  FOR_EACH_OBSERVER(RenderFrameObserver, observers_, WasShown());
+}
+
+bool RenderFrameImpl::IsHidden() {
+  return GetRenderWidget()->is_hidden();
+}
+
+// Tell the embedding application that the URL of the active page has changed.
+void RenderFrameImpl::SendDidCommitProvisionalLoad(blink::WebFrame* frame) {
+  DCHECK(!frame_ || frame_ == frame);
+  WebDataSource* ds = frame->dataSource();
+  DCHECK(ds);
+
+  const WebURLRequest& request = ds->request();
+  const WebURLResponse& response = ds->response();
+
+  DocumentState* document_state = DocumentState::FromDataSource(ds);
+  NavigationState* navigation_state = document_state->navigation_state();
+  InternalDocumentStateData* internal_data =
+      InternalDocumentStateData::FromDocumentState(document_state);
+
+  FrameHostMsg_DidCommitProvisionalLoad_Params params;
+  params.http_status_code = response.httpStatusCode();
+  params.is_post = false;
+  params.post_id = -1;
+  params.page_id = render_view_->page_id_;
+  // We need to track the RenderViewHost routing_id because of downstream
+  // dependencies (crbug.com/392171 DownloadRequestHandle, SaveFileManager,
+  // ResourceDispatcherHostImpl, MediaStreamUIProxy,
+  // SpeechRecognitionDispatcherHost and possibly others). They look up the view
+  // based on the ID stored in the resource requests. Once those dependencies
+  // are unwound or moved to RenderFrameHost (crbug.com/304341) we can move the
+  // client to be based on the routing_id of the RenderFrameHost.
+  params.render_view_routing_id = render_view_->routing_id();
+  params.socket_address.set_host(response.remoteIPAddress().utf8());
+  params.socket_address.set_port(response.remotePort());
+  WebURLResponseExtraDataImpl* extra_data = GetExtraDataFromResponse(response);
+  if (extra_data)
+    params.was_fetched_via_proxy = extra_data->was_fetched_via_proxy();
+  params.was_within_same_page = navigation_state->was_within_same_page();
+  params.security_info = response.securityInfo();
+
+  // Set the URL to be displayed in the browser UI to the user.
+  params.url = GetLoadingUrl();
+  DCHECK(!is_swapped_out_ || params.url == GURL(kSwappedOutURL));
+
+  if (frame->document().baseURL() != params.url)
+    params.base_url = frame->document().baseURL();
+
+  GetRedirectChain(ds, &params.redirects);
+  params.should_update_history = !ds->hasUnreachableURL() &&
+      !response.isMultipartPayload() && (response.httpStatusCode() != 404);
+
+  params.searchable_form_url = internal_data->searchable_form_url();
+  params.searchable_form_encoding = internal_data->searchable_form_encoding();
+
+  params.gesture = render_view_->navigation_gesture_;
+  render_view_->navigation_gesture_ = NavigationGestureUnknown;
+
+  // Make navigation state a part of the DidCommitProvisionalLoad message so
+  // that commited entry has it at all times.
+  HistoryEntry* entry = render_view_->history_controller()->GetCurrentEntry();
+  if (entry)
+    params.page_state = HistoryEntryToPageState(entry);
+  else
+    params.page_state = PageState::CreateFromURL(request.url());
+
+  if (!frame->parent()) {
+    // Top-level navigation.
+
+    // Reset the zoom limits in case a plugin had changed them previously. This
+    // will also call us back which will cause us to send a message to
+    // update WebContentsImpl.
+    render_view_->webview()->zoomLimitsChanged(
+        ZoomFactorToZoomLevel(kMinimumZoomFactor),
+        ZoomFactorToZoomLevel(kMaximumZoomFactor));
+
+    // Set zoom level, but don't do it for full-page plugin since they don't use
+    // the same zoom settings.
+    HostZoomLevels::iterator host_zoom =
+        render_view_->host_zoom_levels_.find(GURL(request.url()));
+    if (render_view_->webview()->mainFrame()->document().isPluginDocument()) {
+      // Reset the zoom levels for plugins.
+      render_view_->webview()->setZoomLevel(0);
+    } else {
+      if (host_zoom != render_view_->host_zoom_levels_.end())
+        render_view_->webview()->setZoomLevel(host_zoom->second);
+    }
+
+    if (host_zoom != render_view_->host_zoom_levels_.end()) {
+      // This zoom level was merely recorded transiently for this load.  We can
+      // erase it now.  If at some point we reload this page, the browser will
+      // send us a new, up-to-date zoom level.
+      render_view_->host_zoom_levels_.erase(host_zoom);
+    }
+
+    // Update contents MIME type for main frame.
+    params.contents_mime_type = ds->response().mimeType().utf8();
+
+    params.transition = navigation_state->transition_type();
+    if (!PageTransitionIsMainFrame(params.transition)) {
+      // If the main frame does a load, it should not be reported as a subframe
+      // navigation.  This can occur in the following case:
+      // 1. You're on a site with frames.
+      // 2. You do a subframe navigation.  This is stored with transition type
+      //    MANUAL_SUBFRAME.
+      // 3. You navigate to some non-frame site, say, google.com.
+      // 4. You navigate back to the page from step 2.  Since it was initially
+      //    MANUAL_SUBFRAME, it will be that same transition type here.
+      // We don't want that, because any navigation that changes the toplevel
+      // frame should be tracked as a toplevel navigation (this allows us to
+      // update the URL bar, etc).
+      params.transition = PAGE_TRANSITION_LINK;
+    }
+
+    // If the page contained a client redirect (meta refresh, document.loc...),
+    // set the referrer and transition appropriately.
+    if (ds->isClientRedirect()) {
+      params.referrer =
+          Referrer(params.redirects[0], ds->request().referrerPolicy());
+      params.transition = static_cast<PageTransition>(
+          params.transition | PAGE_TRANSITION_CLIENT_REDIRECT);
+    } else {
+      params.referrer = RenderViewImpl::GetReferrerFromRequest(
+          frame, ds->request());
+    }
+
+    base::string16 method = request.httpMethod();
+    if (EqualsASCII(method, "POST")) {
+      params.is_post = true;
+      params.post_id = ExtractPostId(entry->root());
+    }
+
+    // Send the user agent override back.
+    params.is_overriding_user_agent = internal_data->is_overriding_user_agent();
+
+    // Track the URL of the original request.  We use the first entry of the
+    // redirect chain if it exists because the chain may have started in another
+    // process.
+    params.original_request_url = GetOriginalRequestURL(ds);
+
+    params.history_list_was_cleared =
+        navigation_state->history_list_was_cleared();
+
+    // Save some histogram data so we can compute the average memory used per
+    // page load of the glyphs.
+    UMA_HISTOGRAM_COUNTS_10000("Memory.GlyphPagesPerLoad",
+                               blink::WebGlyphCache::pageCount());
+
+    // This message needs to be sent before any of allowScripts(),
+    // allowImages(), allowPlugins() is called for the new page, so that when
+    // these functions send a ViewHostMsg_ContentBlocked message, it arrives
+    // after the FrameHostMsg_DidCommitProvisionalLoad message.
+    Send(new FrameHostMsg_DidCommitProvisionalLoad(routing_id_, params));
+  } else {
+    // Subframe navigation: the type depends on whether this navigation
+    // generated a new session history entry. When they do generate a session
+    // history entry, it means the user initiated the navigation and we should
+    // mark it as such. This test checks if this is the first time
+    // SendDidCommitProvisionalLoad has been called since WillNavigateToURL was
+    // called to initiate the load.
+    if (render_view_->page_id_ > render_view_->last_page_id_sent_to_browser_)
+      params.transition = PAGE_TRANSITION_MANUAL_SUBFRAME;
+    else
+      params.transition = PAGE_TRANSITION_AUTO_SUBFRAME;
+
+    DCHECK(!navigation_state->history_list_was_cleared());
+    params.history_list_was_cleared = false;
+
+    // Don't send this message while the subframe is swapped out.
+    if (!is_swapped_out())
+      Send(new FrameHostMsg_DidCommitProvisionalLoad(routing_id_, params));
+  }
+
+  render_view_->last_page_id_sent_to_browser_ =
+      std::max(render_view_->last_page_id_sent_to_browser_,
+               render_view_->page_id_);
+
+  // If we end up reusing this WebRequest (for example, due to a #ref click),
+  // we don't want the transition type to persist.  Just clear it.
+  navigation_state->set_transition_type(PAGE_TRANSITION_LINK);
+}
+
+WebElement RenderFrameImpl::GetFocusedElement() {
+  WebDocument doc = frame_->document();
+  if (!doc.isNull())
+    return doc.focusedElement();
+
+  return WebElement();
+}
+
+void RenderFrameImpl::didStartLoading(bool to_different_document) {
+  render_view_->FrameDidStartLoading(frame_);
+  Send(new FrameHostMsg_DidStartLoading(routing_id_, to_different_document));
+}
+
+void RenderFrameImpl::didStopLoading() {
+  render_view_->FrameDidStopLoading(frame_);
+  Send(new FrameHostMsg_DidStopLoading(routing_id_));
+}
+
+void RenderFrameImpl::didChangeLoadProgress(double load_progress) {
+  Send(new FrameHostMsg_DidChangeLoadProgress(routing_id_, load_progress));
+}
+
+void RenderFrameImpl::HandleWebAccessibilityEvent(
+    const blink::WebAXObject& obj, blink::WebAXEvent event) {
+  if (renderer_accessibility_)
+    renderer_accessibility_->HandleWebAccessibilityEvent(obj, event);
+}
+
+void RenderFrameImpl::FocusedNodeChanged(const WebNode& node) {
+  if (renderer_accessibility_)
+    renderer_accessibility_->FocusedNodeChanged(node);
+}
+
+WebNavigationPolicy RenderFrameImpl::DecidePolicyForNavigation(
+    RenderFrame* render_frame,
+    const NavigationPolicyInfo& info) {
+#ifdef OS_ANDROID
+  // The handlenavigation API is deprecated and will be removed once
+  // crbug.com/325351 is resolved.
+  if (info.urlRequest.url() != GURL(kSwappedOutURL) &&
+      GetContentClient()->renderer()->HandleNavigation(
+          render_frame,
+          static_cast<DocumentState*>(info.extraData),
+          render_view_->opener_id_,
+          info.frame,
+          info.urlRequest,
+          info.navigationType,
+          info.defaultPolicy,
+          info.isRedirect)) {
+    return blink::WebNavigationPolicyIgnore;
+  }
+#endif
+
+  Referrer referrer(RenderViewImpl::GetReferrerFromRequest(info.frame,
+                                                           info.urlRequest));
+  const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+
+  bool is_subframe = !!info.frame->parent();
+
+  if (command_line.HasSwitch(switches::kSitePerProcess) && is_subframe) {
+    // There's no reason to ignore navigations on subframes, since the swap out
+    // logic no longer applies.
+  } else {
+    if (is_swapped_out_ || render_view_->is_swapped_out()) {
+      if (info.urlRequest.url() != GURL(kSwappedOutURL)) {
+        // Targeted links may try to navigate a swapped out frame.  Allow the
+        // browser process to navigate the tab instead.  Note that it is also
+        // possible for non-targeted navigations (from this view) to arrive
+        // here just after we are swapped out.  It's ok to send them to the
+        // browser, as long as they're for the top level frame.
+        // TODO(creis): Ensure this supports targeted form submissions when
+        // fixing http://crbug.com/101395.
+        if (info.frame->parent() == NULL) {
+          OpenURL(info.frame, info.urlRequest.url(), referrer,
+                  info.defaultPolicy);
+          return blink::WebNavigationPolicyIgnore;  // Suppress the load here.
+        }
+
+        // We should otherwise ignore in-process iframe navigations, if they
+        // arrive just after we are swapped out.
+        return blink::WebNavigationPolicyIgnore;
+      }
+
+      // Allow kSwappedOutURL to complete.
+      return info.defaultPolicy;
+    }
+  }
+
+  // Webkit is asking whether to navigate to a new URL.
+  // This is fine normally, except if we're showing UI from one security
+  // context and they're trying to navigate to a different context.
+  const GURL& url = info.urlRequest.url();
+
+  // A content initiated navigation may have originated from a link-click,
+  // script, drag-n-drop operation, etc.
+  bool is_content_initiated = static_cast<DocumentState*>(info.extraData)->
+          navigation_state()->is_content_initiated();
+
+  // Experimental:
+  // If --enable-strict-site-isolation or --site-per-process is enabled, send
+  // all top-level navigations to the browser to let it swap processes when
+  // crossing site boundaries.  This is currently expected to break some script
+  // calls and navigations, such as form submissions.
+  bool force_swap_due_to_flag =
+      command_line.HasSwitch(switches::kEnableStrictSiteIsolation) ||
+      command_line.HasSwitch(switches::kSitePerProcess);
+  if (force_swap_due_to_flag &&
+      !info.frame->parent() && (is_content_initiated || info.isRedirect)) {
+    WebString origin_str = info.frame->document().securityOrigin().toString();
+    GURL frame_url(origin_str.utf8().data());
+    // TODO(cevans): revisit whether this site check is still necessary once
+    // crbug.com/101395 is fixed.
+    bool same_domain_or_host =
+        net::registry_controlled_domains::SameDomainOrHost(
+            frame_url,
+            url,
+            net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
+    if (!same_domain_or_host || frame_url.scheme() != url.scheme()) {
+      OpenURL(info.frame, url, referrer, info.defaultPolicy);
+      return blink::WebNavigationPolicyIgnore;
+    }
+  }
+
+  // If the browser is interested, then give it a chance to look at the request.
+  if (is_content_initiated) {
+    bool is_form_post =
+        ((info.navigationType == blink::WebNavigationTypeFormSubmitted) ||
+            (info.navigationType == blink::WebNavigationTypeFormResubmitted)) &&
+        EqualsASCII(info.urlRequest.httpMethod(), "POST");
+    bool browser_handles_request =
+        render_view_->renderer_preferences_
+            .browser_handles_non_local_top_level_requests
+        && IsNonLocalTopLevelNavigation(url, info.frame, info.navigationType,
+                                        is_form_post);
+    if (!browser_handles_request) {
+      browser_handles_request = IsTopLevelNavigation(info.frame) &&
+          render_view_->renderer_preferences_
+              .browser_handles_all_top_level_requests;
+    }
+
+    if (browser_handles_request) {
+      // Reset these counters as the RenderView could be reused for the next
+      // navigation.
+      render_view_->page_id_ = -1;
+      render_view_->last_page_id_sent_to_browser_ = -1;
+      OpenURL(info.frame, url, referrer, info.defaultPolicy);
+      return blink::WebNavigationPolicyIgnore;  // Suppress the load here.
+    }
+  }
+
+  // Use the frame's original request's URL rather than the document's URL for
+  // subsequent checks.  For a popup, the document's URL may become the opener
+  // window's URL if the opener has called document.write().
+  // See http://crbug.com/93517.
+  GURL old_url(info.frame->dataSource()->request().url());
+
+  // Detect when we're crossing a permission-based boundary (e.g. into or out of
+  // an extension or app origin, leaving a WebUI page, etc). We only care about
+  // top-level navigations (not iframes). But we sometimes navigate to
+  // about:blank to clear a tab, and we want to still allow that.
+  //
+  // Note: this is known to break POST submissions when crossing process
+  // boundaries until http://crbug.com/101395 is fixed.  This is better for
+  // security than loading a WebUI, extension or app page in the wrong process.
+  // POST requests don't work because this mechanism does not preserve form
+  // POST data. We will need to send the request's httpBody data up to the
+  // browser process, and issue a special POST navigation in WebKit (via
+  // FrameLoader::loadFrameRequest). See ResourceDispatcher and WebURLLoaderImpl
+  // for examples of how to send the httpBody data.
+  if (!info.frame->parent() && is_content_initiated &&
+      !url.SchemeIs(url::kAboutScheme)) {
+    bool send_referrer = false;
+
+    // All navigations to or from WebUI URLs or within WebUI-enabled
+    // RenderProcesses must be handled by the browser process so that the
+    // correct bindings and data sources can be registered.
+    // Similarly, navigations to view-source URLs or within ViewSource mode
+    // must be handled by the browser process (except for reloads - those are
+    // safe to leave within the renderer).
+    // Lastly, access to file:// URLs from non-file:// URL pages must be
+    // handled by the browser so that ordinary renderer processes don't get
+    // blessed with file permissions.
+    int cumulative_bindings = RenderProcess::current()->GetEnabledBindings();
+    bool is_initial_navigation = render_view_->page_id_ == -1;
+    bool should_fork = HasWebUIScheme(url) || HasWebUIScheme(old_url) ||
+        (cumulative_bindings & BINDINGS_POLICY_WEB_UI) ||
+        url.SchemeIs(kViewSourceScheme) ||
+        (info.frame->isViewSourceModeEnabled() &&
+            info.navigationType != blink::WebNavigationTypeReload);
+
+    if (!should_fork && url.SchemeIs(url::kFileScheme)) {
+      // Fork non-file to file opens.  Check the opener URL if this is the
+      // initial navigation in a newly opened window.
+      GURL source_url(old_url);
+      if (is_initial_navigation && source_url.is_empty() &&
+          info.frame->opener())
+        source_url = info.frame->opener()->top()->document().url();
+      DCHECK(!source_url.is_empty());
+      should_fork = !source_url.SchemeIs(url::kFileScheme);
+    }
+
+    if (!should_fork) {
+      // Give the embedder a chance.
+      should_fork = GetContentClient()->renderer()->ShouldFork(
+          info.frame, url, info.urlRequest.httpMethod().utf8(),
+          is_initial_navigation, info.isRedirect, &send_referrer);
+    }
+
+    if (should_fork) {
+      OpenURL(info.frame, url, send_referrer ? referrer : Referrer(),
+              info.defaultPolicy);
+      return blink::WebNavigationPolicyIgnore;  // Suppress the load here.
+    }
+  }
+
+  // Detect when a page is "forking" a new tab that can be safely rendered in
+  // its own process.  This is done by sites like Gmail that try to open links
+  // in new windows without script connections back to the original page.  We
+  // treat such cases as browser navigations (in which we will create a new
+  // renderer for a cross-site navigation), rather than WebKit navigations.
+  //
+  // We use the following heuristic to decide whether to fork a new page in its
+  // own process:
+  // The parent page must open a new tab to about:blank, set the new tab's
+  // window.opener to null, and then redirect the tab to a cross-site URL using
+  // JavaScript.
+  //
+  // TODO(creis): Deprecate this logic once we can rely on rel=noreferrer
+  // (see below).
+  bool is_fork =
+      // Must start from a tab showing about:blank, which is later redirected.
+      old_url == GURL(url::kAboutBlankURL) &&
+      // Must be the first real navigation of the tab.
+      render_view_->historyBackListCount() < 1 &&
+      render_view_->historyForwardListCount() < 1 &&
+      // The parent page must have set the child's window.opener to null before
+      // redirecting to the desired URL.
+      info.frame->opener() == NULL &&
+      // Must be a top-level frame.
+      info.frame->parent() == NULL &&
+      // Must not have issued the request from this page.
+      is_content_initiated &&
+      // Must be targeted at the current tab.
+      info.defaultPolicy == blink::WebNavigationPolicyCurrentTab &&
+      // Must be a JavaScript navigation, which appears as "other".
+      info.navigationType == blink::WebNavigationTypeOther;
+
+  if (is_fork) {
+    // Open the URL via the browser, not via WebKit.
+    OpenURL(info.frame, url, Referrer(), info.defaultPolicy);
+    return blink::WebNavigationPolicyIgnore;
+  }
+
+  return info.defaultPolicy;
+}
+
+void RenderFrameImpl::OpenURL(WebFrame* frame,
+                              const GURL& url,
+                              const Referrer& referrer,
+                              WebNavigationPolicy policy) {
+  DCHECK_EQ(frame_, frame);
+
+  FrameHostMsg_OpenURL_Params params;
+  params.url = url;
+  params.referrer = referrer;
+  params.disposition = RenderViewImpl::NavigationPolicyToDisposition(policy);
+  WebDataSource* ds = frame->provisionalDataSource();
+  if (ds) {
+    DocumentState* document_state = DocumentState::FromDataSource(ds);
+    NavigationState* navigation_state = document_state->navigation_state();
+    if (navigation_state->is_content_initiated()) {
+      params.should_replace_current_entry = ds->replacesCurrentHistoryItem();
+    } else {
+      // This is necessary to preserve the should_replace_current_entry value on
+      // cross-process redirects, in the event it was set by a previous process.
+      //
+      // TODO(davidben): Avoid this awkward duplication of state. See comment on
+      // NavigationState::should_replace_current_entry().
+      params.should_replace_current_entry =
+          navigation_state->should_replace_current_entry();
+    }
+  } else {
+    params.should_replace_current_entry = false;
+  }
+  params.user_gesture = WebUserGestureIndicator::isProcessingUserGesture();
+  if (GetContentClient()->renderer()->AllowPopup())
+    params.user_gesture = true;
+
+  if (policy == blink::WebNavigationPolicyNewBackgroundTab ||
+      policy == blink::WebNavigationPolicyNewForegroundTab ||
+      policy == blink::WebNavigationPolicyNewWindow ||
+      policy == blink::WebNavigationPolicyNewPopup) {
+    WebUserGestureIndicator::consumeUserGesture();
+  }
+
+  Send(new FrameHostMsg_OpenURL(routing_id_, params));
+}
+
+void RenderFrameImpl::UpdateEncoding(WebFrame* frame,
+                                     const std::string& encoding_name) {
+  // Only update main frame's encoding_name.
+  if (!frame->parent())
+    Send(new FrameHostMsg_UpdateEncoding(routing_id_, encoding_name));
+}
+
+void RenderFrameImpl::SyncSelectionIfRequired() {
+  base::string16 text;
+  size_t offset;
+  gfx::Range range;
+#if defined(ENABLE_PLUGINS)
+  if (render_view_->focused_pepper_plugin_) {
+    render_view_->focused_pepper_plugin_->GetSurroundingText(&text, &range);
+    offset = 0;  // Pepper API does not support offset reporting.
+    // TODO(kinaba): cut as needed.
+  } else
+#endif
+  {
+    size_t location, length;
+    if (!GetRenderWidget()->webwidget()->caretOrSelectionRange(
+            &location, &length)) {
+      return;
+    }
+
+    range = gfx::Range(location, location + length);
+
+    if (GetRenderWidget()->webwidget()->textInputInfo().type !=
+            blink::WebTextInputTypeNone) {
+      // If current focused element is editable, we will send 100 more chars
+      // before and after selection. It is for input method surrounding text
+      // feature.
+      if (location > kExtraCharsBeforeAndAfterSelection)
+        offset = location - kExtraCharsBeforeAndAfterSelection;
+      else
+        offset = 0;
+      length = location + length - offset + kExtraCharsBeforeAndAfterSelection;
+      WebRange webrange = WebRange::fromDocumentRange(frame_, offset, length);
+      if (!webrange.isNull())
+        text = WebRange::fromDocumentRange(
+            frame_, offset, length).toPlainText();
+    } else {
+      offset = location;
+      text = frame_->selectionAsText();
+      // http://crbug.com/101435
+      // In some case, frame->selectionAsText() returned text's length is not
+      // equal to the length returned from webwidget()->caretOrSelectionRange().
+      // So we have to set the range according to text.length().
+      range.set_end(range.start() + text.length());
+    }
+  }
+
+  // Sometimes we get repeated didChangeSelection calls from webkit when
+  // the selection hasn't actually changed. We don't want to report these
+  // because it will cause us to continually claim the X clipboard.
+  if (selection_text_offset_ != offset ||
+      selection_range_ != range ||
+      selection_text_ != text) {
+    selection_text_ = text;
+    selection_text_offset_ = offset;
+    selection_range_ = range;
+    // This IPC is dispatched by RenderWidetHost, so use its routing ID.
+    Send(new ViewHostMsg_SelectionChanged(
+        GetRenderWidget()->routing_id(), text, offset, range));
+  }
+  GetRenderWidget()->UpdateSelectionBounds();
+}
+
+void RenderFrameImpl::InitializeUserMediaClient() {
+  if (!RenderThreadImpl::current())  // Will be NULL during unit tests.
+    return;
+
+#if defined(OS_ANDROID)
+  if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableWebRTC))
+    return;
+#endif
+
+#if defined(ENABLE_WEBRTC)
+  DCHECK(!web_user_media_client_);
+  web_user_media_client_ = new MediaStreamImpl(
+      this,
+      RenderThreadImpl::current()->GetPeerConnectionDependencyFactory(),
+      make_scoped_ptr(new MediaStreamDispatcher(this)).Pass());
+#endif
+}
+
+WebMediaPlayer* RenderFrameImpl::CreateWebMediaPlayerForMediaStream(
+    const blink::WebURL& url,
+    WebMediaPlayerClient* client) {
+#if defined(ENABLE_WEBRTC)
+#if defined(OS_ANDROID) && defined(ARCH_CPU_ARMEL)
+  bool found_neon =
+      (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0;
+  UMA_HISTOGRAM_BOOLEAN("Platform.WebRtcNEONFound", found_neon);
+#endif  // defined(OS_ANDROID) && defined(ARCH_CPU_ARMEL)
+  return new WebMediaPlayerMS(frame_, client, weak_factory_.GetWeakPtr(),
+                              new RenderMediaLog(),
+                              CreateRendererFactory());
+#else
+  return NULL;
+#endif  // defined(ENABLE_WEBRTC)
+}
+
+scoped_ptr<MediaStreamRendererFactory>
+RenderFrameImpl::CreateRendererFactory() {
+#if defined(ENABLE_WEBRTC)
+  return scoped_ptr<MediaStreamRendererFactory>(
+      new MediaStreamRendererFactory());
+#else
+  return scoped_ptr<MediaStreamRendererFactory>(
+      static_cast<MediaStreamRendererFactory*>(NULL));
+#endif
+}
+
+GURL RenderFrameImpl::GetLoadingUrl() const {
+  WebDataSource* ds = frame_->dataSource();
+  if (ds->hasUnreachableURL())
+    return ds->unreachableURL();
+
+  const WebURLRequest& request = ds->request();
+  return request.url();
+}
+
+#if defined(OS_ANDROID)
+
+WebMediaPlayer* RenderFrameImpl::CreateAndroidWebMediaPlayer(
+      const blink::WebURL& url,
+      WebMediaPlayerClient* client) {
+  GpuChannelHost* gpu_channel_host =
+      RenderThreadImpl::current()->EstablishGpuChannelSync(
+          CAUSE_FOR_GPU_LAUNCH_VIDEODECODEACCELERATOR_INITIALIZE);
+  if (!gpu_channel_host) {
+    LOG(ERROR) << "Failed to establish GPU channel for media player";
+    return NULL;
+  }
+
+  scoped_refptr<StreamTextureFactory> stream_texture_factory;
+  if (SynchronousCompositorFactory* factory =
+          SynchronousCompositorFactory::GetInstance()) {
+    stream_texture_factory = factory->CreateStreamTextureFactory(routing_id_);
+  } else {
+    scoped_refptr<webkit::gpu::ContextProviderWebContext> context_provider =
+        RenderThreadImpl::current()->SharedMainThreadContextProvider();
+
+    if (!context_provider.get()) {
+      LOG(ERROR) << "Failed to get context3d for media player";
+      return NULL;
+    }
+
+    stream_texture_factory = StreamTextureFactoryImpl::Create(
+        context_provider, gpu_channel_host, routing_id_);
+  }
+
+  return new WebMediaPlayerAndroid(
+      frame_,
+      client,
+      weak_factory_.GetWeakPtr(),
+      GetMediaPlayerManager(),
+      GetCdmManager(),
+      stream_texture_factory,
+      RenderThreadImpl::current()->GetMediaThreadMessageLoopProxy(),
+      new RenderMediaLog());
+}
+
+RendererMediaPlayerManager* RenderFrameImpl::GetMediaPlayerManager() {
+  if (!media_player_manager_)
+    media_player_manager_ = new RendererMediaPlayerManager(this);
+  return media_player_manager_;
+}
+
+#endif  // defined(OS_ANDROID)
+
+#if defined(ENABLE_BROWSER_CDMS)
+RendererCdmManager* RenderFrameImpl::GetCdmManager() {
+  if (!cdm_manager_)
+    cdm_manager_ = new RendererCdmManager(this);
+  return cdm_manager_;
+}
+#endif  // defined(ENABLE_BROWSER_CDMS)
+
 }  // namespace content