summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authorjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-19 07:11:52 +0000
committerjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-19 07:11:52 +0000
commit1d8a3d1fe0e07d5aaf1f0a5527097b9e313d23b6 (patch)
treeb298f570c373f95b55cdfb8334ab7b510b5942c4 /content
parent3e45d8f9c4c6eabec2458cc5e0b481e73f2705ab (diff)
downloadchromium_src-1d8a3d1fe0e07d5aaf1f0a5527097b9e313d23b6.zip
chromium_src-1d8a3d1fe0e07d5aaf1f0a5527097b9e313d23b6.tar.gz
chromium_src-1d8a3d1fe0e07d5aaf1f0a5527097b9e313d23b6.tar.bz2
Move core pieces of browser\renderer_host to src\content.
TBR=avi Review URL: http://codereview.chromium.org/6532073 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@75489 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r--content/browser/renderer_host/accelerated_surface_container_mac.cc233
-rw-r--r--content/browser/renderer_host/accelerated_surface_container_mac.h152
-rw-r--r--content/browser/renderer_host/accelerated_surface_container_manager_mac.cc173
-rw-r--r--content/browser/renderer_host/accelerated_surface_container_manager_mac.h124
-rw-r--r--content/browser/renderer_host/async_resource_handler.cc265
-rw-r--r--content/browser/renderer_host/async_resource_handler.h61
-rw-r--r--content/browser/renderer_host/audio_renderer_host.cc480
-rw-r--r--content/browser/renderer_host/audio_renderer_host.h223
-rw-r--r--content/browser/renderer_host/audio_renderer_host_unittest.cc488
-rw-r--r--content/browser/renderer_host/audio_sync_reader.cc64
-rw-r--r--content/browser/renderer_host/audio_sync_reader.h56
-rw-r--r--content/browser/renderer_host/backing_store.cc17
-rw-r--r--content/browser/renderer_host/backing_store.h75
-rw-r--r--content/browser/renderer_host/backing_store_mac.h56
-rw-r--r--content/browser/renderer_host/backing_store_mac.mm242
-rw-r--r--content/browser/renderer_host/backing_store_manager.cc287
-rw-r--r--content/browser/renderer_host/backing_store_manager.h79
-rw-r--r--content/browser/renderer_host/backing_store_skia.cc105
-rw-r--r--content/browser/renderer_host/backing_store_skia.h51
-rw-r--r--content/browser/renderer_host/backing_store_win.cc175
-rw-r--r--content/browser/renderer_host/backing_store_win.h52
-rw-r--r--content/browser/renderer_host/backing_store_x.cc477
-rw-r--r--content/browser/renderer_host/backing_store_x.h101
-rw-r--r--content/browser/renderer_host/blob_message_filter.cc85
-rw-r--r--content/browser/renderer_host/blob_message_filter.h51
-rw-r--r--content/browser/renderer_host/buffered_resource_handler.cc491
-rw-r--r--content/browser/renderer_host/buffered_resource_handler.h99
-rw-r--r--content/browser/renderer_host/cross_site_resource_handler.cc219
-rw-r--r--content/browser/renderer_host/cross_site_resource_handler.h71
-rw-r--r--content/browser/renderer_host/database_message_filter.cc323
-rw-r--r--content/browser/renderer_host/database_message_filter.h103
-rw-r--r--content/browser/renderer_host/file_utilities_message_filter.cc106
-rw-r--r--content/browser/renderer_host/file_utilities_message_filter.h48
-rw-r--r--content/browser/renderer_host/global_request_id.h32
-rw-r--r--content/browser/renderer_host/gpu_message_filter.cc151
-rw-r--r--content/browser/renderer_host/gpu_message_filter.h51
-rw-r--r--content/browser/renderer_host/mock_render_process_host.cc156
-rw-r--r--content/browser/renderer_host/mock_render_process_host.h102
-rw-r--r--content/browser/renderer_host/pepper_file_message_filter.cc201
-rw-r--r--content/browser/renderer_host/pepper_file_message_filter.h72
-rw-r--r--content/browser/renderer_host/pepper_message_filter.cc280
-rw-r--r--content/browser/renderer_host/pepper_message_filter.h70
-rw-r--r--content/browser/renderer_host/redirect_to_file_resource_handler.cc221
-rw-r--r--content/browser/renderer_host/redirect_to_file_resource_handler.h89
-rw-r--r--content/browser/renderer_host/render_message_filter.cc1718
-rw-r--r--content/browser/renderer_host/render_message_filter.h498
-rw-r--r--content/browser/renderer_host/render_message_filter_gtk.cc346
-rw-r--r--content/browser/renderer_host/render_message_filter_mac.mm43
-rw-r--r--content/browser/renderer_host/render_message_filter_win.cc49
-rw-r--r--content/browser/renderer_host/render_process_host.cc204
-rw-r--r--content/browser/renderer_host/render_process_host.h327
-rw-r--r--content/browser/renderer_host/render_sandbox_host_linux.cc687
-rw-r--r--content/browser/renderer_host/render_sandbox_host_linux.h52
-rw-r--r--content/browser/renderer_host/render_view_host.cc1712
-rw-r--r--content/browser/renderer_host/render_view_host.h741
-rw-r--r--content/browser/renderer_host/render_view_host_delegate.cc74
-rw-r--r--content/browser/renderer_host/render_view_host_delegate.h584
-rw-r--r--content/browser/renderer_host/render_view_host_factory.cc37
-rw-r--r--content/browser/renderer_host/render_view_host_factory.h68
-rw-r--r--content/browser/renderer_host/render_view_host_notification_task.h337
-rw-r--r--content/browser/renderer_host/render_widget_fullscreen_host.cc10
-rw-r--r--content/browser/renderer_host/render_widget_fullscreen_host.h15
-rw-r--r--content/browser/renderer_host/render_widget_helper.cc335
-rw-r--r--content/browser/renderer_host/render_widget_helper.h216
-rw-r--r--content/browser/renderer_host/render_widget_host.cc1287
-rw-r--r--content/browser/renderer_host/render_widget_host.h677
-rw-r--r--content/browser/renderer_host/render_widget_host_unittest.cc734
-rw-r--r--content/browser/renderer_host/render_widget_host_view.cc11
-rw-r--r--content/browser/renderer_host/render_widget_host_view.h326
-rw-r--r--content/browser/renderer_host/resource_dispatcher_host.cc1952
-rw-r--r--content/browser/renderer_host/resource_dispatcher_host.h516
-rw-r--r--content/browser/renderer_host/resource_dispatcher_host_request_info.cc67
-rw-r--r--content/browser/renderer_host/resource_dispatcher_host_request_info.h241
-rw-r--r--content/browser/renderer_host/resource_dispatcher_host_uitest.cc335
-rw-r--r--content/browser/renderer_host/resource_dispatcher_host_unittest.cc1026
-rw-r--r--content/browser/renderer_host/resource_handler.h96
-rw-r--r--content/browser/renderer_host/resource_message_filter.cc52
-rw-r--r--content/browser/renderer_host/resource_message_filter.h81
-rw-r--r--content/browser/renderer_host/resource_queue.cc93
-rw-r--r--content/browser/renderer_host/resource_queue.h100
-rw-r--r--content/browser/renderer_host/resource_queue_unittest.cc289
-rw-r--r--content/browser/renderer_host/resource_request_details.cc50
-rw-r--r--content/browser/renderer_host/resource_request_details.h71
-rw-r--r--content/browser/renderer_host/socket_stream_dispatcher_host.cc164
-rw-r--r--content/browser/renderer_host/socket_stream_dispatcher_host.h65
-rw-r--r--content/browser/renderer_host/socket_stream_host.cc66
-rw-r--r--content/browser/renderer_host/socket_stream_host.h61
-rw-r--r--content/browser/renderer_host/sync_resource_handler.cc117
-rw-r--r--content/browser/renderer_host/sync_resource_handler.h60
-rw-r--r--content/browser/renderer_host/x509_user_cert_resource_handler.cc130
-rw-r--r--content/browser/renderer_host/x509_user_cert_resource_handler.h80
-rw-r--r--content/browser/site_instance.cc236
-rw-r--r--content/browser/site_instance.h197
-rw-r--r--content/content_browser.gypi87
94 files changed, 24377 insertions, 0 deletions
diff --git a/content/browser/renderer_host/accelerated_surface_container_mac.cc b/content/browser/renderer_host/accelerated_surface_container_mac.cc
new file mode 100644
index 0000000..a661c92
--- /dev/null
+++ b/content/browser/renderer_host/accelerated_surface_container_mac.cc
@@ -0,0 +1,233 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/accelerated_surface_container_mac.h"
+
+#include "app/surface/io_surface_support_mac.h"
+#include "base/logging.h"
+#include "content/browser/renderer_host/accelerated_surface_container_manager_mac.h"
+#include "webkit/plugins/npapi/webplugin.h"
+
+AcceleratedSurfaceContainerMac::AcceleratedSurfaceContainerMac(
+ AcceleratedSurfaceContainerManagerMac* manager,
+ bool opaque)
+ : manager_(manager),
+ opaque_(opaque),
+ surface_id_(0),
+ width_(0),
+ height_(0),
+ texture_(0),
+ texture_needs_upload_(true),
+ texture_pending_deletion_(0),
+ visible_(false),
+ was_painted_to_(false) {
+}
+
+AcceleratedSurfaceContainerMac::~AcceleratedSurfaceContainerMac() {
+}
+
+void AcceleratedSurfaceContainerMac::SetSizeAndIOSurface(
+ int32 width,
+ int32 height,
+ uint64 io_surface_identifier) {
+ // Ignore |io_surface_identifier|: The surface hasn't been painted to and
+ // only contains garbage data. Update the surface in |set_was_painted_to()|
+ // instead.
+ width_ = width;
+ height_ = height;
+}
+
+void AcceleratedSurfaceContainerMac::SetSizeAndTransportDIB(
+ int32 width,
+ int32 height,
+ TransportDIB::Handle transport_dib) {
+ if (TransportDIB::is_valid(transport_dib)) {
+ transport_dib_.reset(TransportDIB::Map(transport_dib));
+ EnqueueTextureForDeletion();
+ width_ = width;
+ height_ = height;
+ }
+}
+
+void AcceleratedSurfaceContainerMac::SetGeometry(
+ const webkit::npapi::WebPluginGeometry& geom) {
+ visible_ = geom.visible;
+ if (geom.rects_valid)
+ clip_rect_ = geom.clip_rect;
+}
+
+void AcceleratedSurfaceContainerMac::Draw(CGLContextObj context) {
+ IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize();
+ GLenum target = GL_TEXTURE_RECTANGLE_ARB;
+ if (texture_pending_deletion_) {
+ // Clean up an old texture object. This is essentially a pre-emptive
+ // cleanup, as the resources will be released when the OpenGL context
+ // associated with our containing NSView is destroyed. However, if we
+ // resize a plugin often, we might generate a lot of textures, so we
+ // should try to eagerly reclaim their resources. Note also that the
+ // OpenGL context must be current when performing the deletion, and it
+ // seems risky to make the OpenGL context current at an arbitrary point
+ // in time, which is why the deletion does not occur in the container's
+ // destructor.
+ glDeleteTextures(1, &texture_pending_deletion_);
+ texture_pending_deletion_ = 0;
+ }
+ if (!texture_) {
+ if ((io_surface_support && !surface_.get()) ||
+ (!io_surface_support && !transport_dib_.get()))
+ return;
+ glGenTextures(1, &texture_);
+ glBindTexture(target, texture_);
+ glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ if (io_surface_support) {
+ texture_needs_upload_ = true;
+ } else {
+ // Reserve space on the card for the actual texture upload, done with the
+ // glTexSubImage2D() call, below.
+ glTexImage2D(target,
+ 0, // mipmap level 0
+ GL_RGBA, // internal format
+ width_,
+ height_,
+ 0, // no border
+ GL_BGRA, // The GPU plugin read BGRA pixels
+ GL_UNSIGNED_INT_8_8_8_8_REV,
+ NULL); // No data, this call just reserves room.
+ }
+ }
+
+ // When using an IOSurface, the texture does not need to be repeatedly
+ // uploaded, just when we've been told we have to.
+ if (io_surface_support && texture_needs_upload_) {
+ DCHECK(surface_.get());
+ glBindTexture(target, texture_);
+ // Don't think we need to identify a plane.
+ GLuint plane = 0;
+ io_surface_support->CGLTexImageIOSurface2D(context,
+ target,
+ GL_RGBA,
+ surface_width_,
+ surface_height_,
+ GL_BGRA,
+ GL_UNSIGNED_INT_8_8_8_8_REV,
+ surface_.get(),
+ plane);
+ texture_needs_upload_ = false;
+ }
+ // If using TransportDIBs, the texture needs to be uploaded every frame.
+ if (transport_dib_.get() != NULL) {
+ void* pixel_memory = transport_dib_->memory();
+ if (pixel_memory) {
+ glBindTexture(target, texture_);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Needed for NPOT textures.
+ glTexSubImage2D(target,
+ 0, // mipmap level 0
+ 0, // x-offset
+ 0, // y-offset
+ width_,
+ height_,
+ GL_BGRA, // The GPU plugin gave us BGRA pixels
+ GL_UNSIGNED_INT_8_8_8_8_REV,
+ pixel_memory);
+ }
+ }
+
+ if (texture_) {
+ int texture_width = io_surface_support ? surface_width_ : width_;
+ int texture_height = io_surface_support ? surface_height_ : height_;
+
+ // TODO(kbr): convert this to use only OpenGL ES 2.0 functionality.
+
+ // TODO(kbr): may need to pay attention to cutout rects.
+ int clipX = clip_rect_.x();
+ int clipY = clip_rect_.y();
+ int clipWidth = clip_rect_.width();
+ int clipHeight = clip_rect_.height();
+
+ if (clipX + clipWidth > texture_width)
+ clipWidth = texture_width - clipX;
+ if (clipY + clipHeight > texture_height)
+ clipHeight = texture_height - clipY;
+
+ if (opaque_) {
+ // Pepper 3D's output is currently considered opaque even if the
+ // program draws pixels with alpha less than 1. In order to have
+ // this effect, we need to drop the alpha channel of the input,
+ // replacing it with alpha = 1.
+
+ // First fill the rectangle with alpha=1.
+ glColorMask(false, false, false, true);
+ glColor4f(0.0f, 0.0f, 0.0f, 1.0f);
+ glBegin(GL_TRIANGLE_STRIP);
+ glVertex3f(0, 0, 0);
+ glVertex3f(clipWidth, 0, 0);
+ glVertex3f(0, clipHeight, 0);
+ glVertex3f(clipWidth, clipHeight, 0);
+ glEnd();
+
+ // Now draw the color channels from the incoming texture.
+ glColorMask(true, true, true, false);
+ // This call shouldn't be necessary -- we are using the GL_REPLACE
+ // texture environment mode -- but it appears to be.
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ } else {
+ glColorMask(true, true, true, true);
+ }
+
+ // Draw the color channels from the incoming texture.
+ glBindTexture(target, texture_);
+ glEnable(target);
+ glBegin(GL_TRIANGLE_STRIP);
+
+ glTexCoord2f(clipX, texture_height - clipY);
+ glVertex3f(0, 0, 0);
+
+ glTexCoord2f(clipX + clipWidth, texture_height - clipY);
+ glVertex3f(clipWidth, 0, 0);
+
+ glTexCoord2f(clipX, texture_height - clipY - clipHeight);
+ glVertex3f(0, clipHeight, 0);
+
+ glTexCoord2f(clipX + clipWidth, texture_height - clipY - clipHeight);
+ glVertex3f(clipWidth, clipHeight, 0);
+
+ glEnd();
+ glDisable(target);
+ }
+}
+
+bool AcceleratedSurfaceContainerMac::ShouldBeVisible() const {
+ return visible_ && was_painted_to_ && !clip_rect_.IsEmpty();
+}
+
+void AcceleratedSurfaceContainerMac::set_was_painted_to(uint64 surface_id) {
+ if (surface_id && (!surface_ || surface_id != surface_id_)) {
+ // Keep the surface that was most recently painted to around.
+ if (IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize()) {
+ CFTypeRef surface = io_surface_support->IOSurfaceLookup(
+ static_cast<uint32>(surface_id));
+ // Can fail if IOSurface with that ID was already released by the
+ // gpu process or the plugin process. We will get a |set_was_painted_to()|
+ // message with a new surface soon in that case.
+ if (surface) {
+ surface_.reset(surface);
+ surface_id_ = surface_id;
+ surface_width_ = io_surface_support->IOSurfaceGetWidth(surface_);
+ surface_height_ = io_surface_support->IOSurfaceGetHeight(surface_);
+ EnqueueTextureForDeletion();
+ }
+ }
+ }
+ was_painted_to_ = true;
+}
+
+void AcceleratedSurfaceContainerMac::EnqueueTextureForDeletion() {
+ if (texture_) {
+ DCHECK(texture_pending_deletion_ == 0);
+ texture_pending_deletion_ = texture_;
+ texture_ = 0;
+ }
+}
+
diff --git a/content/browser/renderer_host/accelerated_surface_container_mac.h b/content/browser/renderer_host/accelerated_surface_container_mac.h
new file mode 100644
index 0000000..2bffb32
--- /dev/null
+++ b/content/browser/renderer_host/accelerated_surface_container_mac.h
@@ -0,0 +1,152 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_GPU_PLUGIN_CONTAINER_MAC_H_
+#define CONTENT_BROWSER_RENDERER_HOST_GPU_PLUGIN_CONTAINER_MAC_H_
+#pragma once
+
+// The "GPU plugin" is currently implemented as a special kind of
+// NPAPI plugin to provide high-performance on-screen 3D rendering for
+// Pepper 3D.
+//
+// On Windows and X11 platforms the GPU plugin relies on cross-process
+// parenting of windows, which is not supported via any public APIs in
+// the Mac OS X window system.
+//
+// To achieve full hardware acceleration we use the new IOSurface APIs
+// introduced in Mac OS X 10.6. The GPU plugin's process produces an
+// IOSurface and renders into it using OpenGL. It uses the
+// IOSurfaceGetID and IOSurfaceLookup APIs to pass a reference to this
+// surface to the browser process for on-screen rendering. The GPU
+// plugin essentially looks like a windowless plugin; the browser
+// process gets all of the mouse events, because the plugin process
+// does not have an on-screen window.
+//
+// This class encapsulates some of the management of these data
+// structures, in conjunction with the AcceleratedSurfaceContainerManagerMac.
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <OpenGL/OpenGL.h>
+
+#include "app/surface/transport_dib.h"
+#include "base/basictypes.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/scoped_ptr.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/rect.h"
+
+namespace webkit {
+namespace npapi {
+struct WebPluginGeometry;
+}
+}
+
+class AcceleratedSurfaceContainerManagerMac;
+
+class AcceleratedSurfaceContainerMac {
+ public:
+ AcceleratedSurfaceContainerMac(
+ AcceleratedSurfaceContainerManagerMac* manager,
+ bool opaque);
+ virtual ~AcceleratedSurfaceContainerMac();
+
+ // Sets the backing store and size of this accelerated surface container.
+ // There are two versions: the IOSurface version is used on systems where the
+ // IOSurface API is supported (Mac OS X 10.6 and later); the TransportDIB is
+ // used on Mac OS X 10.5 and earlier.
+ void SetSizeAndIOSurface(int32 width,
+ int32 height,
+ uint64 io_surface_identifier);
+ void SetSizeAndTransportDIB(int32 width,
+ int32 height,
+ TransportDIB::Handle transport_dib);
+
+ // Tells the accelerated surface container that its geometry has changed,
+ // for example because of a scroll event. (Note that the container
+ // currently only pays attention to the clip width and height, since the
+ // view in which it is hosted is responsible for positioning it on the
+ // page.)
+ void SetGeometry(const webkit::npapi::WebPluginGeometry& geom);
+
+ // Draws this accelerated surface's contents, texture mapped onto a quad in
+ // the given OpenGL context. TODO(kbr): figure out and define exactly how the
+ // coordinate system will work out.
+ void Draw(CGLContextObj context);
+
+ // Causes the next Draw call to trigger a texture upload. Should be called any
+ // time the drawing context has changed.
+ void ForceTextureReload() { texture_needs_upload_ = true; }
+
+ // Returns if the surface should be shown.
+ bool ShouldBeVisible() const;
+
+ // Notifies the the container that its surface was painted to.
+ void set_was_painted_to(uint64 surface_id);
+
+ // Notifies the container that its surface is invalid.
+ void set_surface_invalid() { was_painted_to_ = false; }
+ private:
+ // The manager of this accelerated surface container.
+ AcceleratedSurfaceContainerManagerMac* manager_;
+
+ // Whether this accelerated surface's content is supposed to be opaque.
+ bool opaque_;
+
+ // The IOSurfaceRef, if any, that has been handed from the GPU
+ // plugin process back to the browser process for drawing.
+ // This is held as a CFTypeRef because we can't refer to the
+ // IOSurfaceRef type when building on 10.5.
+ base::mac::ScopedCFTypeRef<CFTypeRef> surface_;
+
+ // The id of |surface_|, or 0 if |surface_| is NULL.
+ uint64 surface_id_;
+
+ // The width and height of the io surface. During resizing, this is different
+ // from |width_| and |height_|.
+ int32 surface_width_;
+ int32 surface_height_;
+
+ // The TransportDIB which is used in pre-10.6 systems where the IOSurface
+ // API is not supported. This is a weak reference to the actual TransportDIB
+ // whic is owned by the GPU process.
+ scoped_ptr<TransportDIB> transport_dib_;
+
+ // The width and height of the container.
+ int32 width_;
+ int32 height_;
+
+ // The clip rectangle, relative to the (x_, y_) origin.
+ gfx::Rect clip_rect_;
+
+ // The "live" OpenGL texture referring to this IOSurfaceRef. Note
+ // that per the CGLTexImageIOSurface2D API we do not need to
+ // explicitly update this texture's contents once created. All we
+ // need to do is ensure it is re-bound before attempting to draw
+ // with it.
+ GLuint texture_;
+
+ // True if we need to upload the texture again during the next draw.
+ bool texture_needs_upload_;
+
+ // This may refer to an old version of the texture if the container is
+ // resized, for example.
+ GLuint texture_pending_deletion_;
+
+ // Stores if the plugin has a visible state.
+ bool visible_;
+
+ // Stores if the plugin's IOSurface has been swapped before. Used to not show
+ // it before it hasn't been painted to at least once.
+ bool was_painted_to_;
+
+ // Releases the IOSurface reference, if any, retained by this object.
+ void ReleaseIOSurface();
+
+ // Enqueue our texture for later deletion.
+ void EnqueueTextureForDeletion();
+
+ DISALLOW_COPY_AND_ASSIGN(AcceleratedSurfaceContainerMac);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_GPU_PLUGIN_CONTAINER_MAC_H_
diff --git a/content/browser/renderer_host/accelerated_surface_container_manager_mac.cc b/content/browser/renderer_host/accelerated_surface_container_manager_mac.cc
new file mode 100644
index 0000000..9c14b14
--- /dev/null
+++ b/content/browser/renderer_host/accelerated_surface_container_manager_mac.cc
@@ -0,0 +1,173 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/accelerated_surface_container_manager_mac.h"
+
+#include "base/logging.h"
+#include "content/browser/renderer_host/accelerated_surface_container_mac.h"
+#include "webkit/plugins/npapi/webplugin.h"
+
+AcceleratedSurfaceContainerManagerMac::AcceleratedSurfaceContainerManagerMac()
+ : current_id_(0),
+ root_container_(NULL),
+ root_container_handle_(gfx::kNullPluginWindow),
+ gpu_rendering_active_(false) {
+}
+
+gfx::PluginWindowHandle
+AcceleratedSurfaceContainerManagerMac::AllocateFakePluginWindowHandle(
+ bool opaque, bool root) {
+ base::AutoLock lock(lock_);
+
+ AcceleratedSurfaceContainerMac* container =
+ new AcceleratedSurfaceContainerMac(this, opaque);
+ gfx::PluginWindowHandle res =
+ static_cast<gfx::PluginWindowHandle>(++current_id_);
+ plugin_window_to_container_map_.insert(std::make_pair(res, container));
+ if (root) {
+ root_container_ = container;
+ root_container_handle_ = res;
+ }
+ return res;
+}
+
+void AcceleratedSurfaceContainerManagerMac::DestroyFakePluginWindowHandle(
+ gfx::PluginWindowHandle id) {
+ base::AutoLock lock(lock_);
+
+ AcceleratedSurfaceContainerMac* container = MapIDToContainer(id);
+ if (container) {
+ if (container == root_container_) {
+ root_container_ = NULL;
+ root_container_handle_ = gfx::kNullPluginWindow;
+ }
+ delete container;
+ }
+ plugin_window_to_container_map_.erase(id);
+}
+
+bool AcceleratedSurfaceContainerManagerMac::IsRootContainer(
+ gfx::PluginWindowHandle id) const {
+ return root_container_handle_ != gfx::kNullPluginWindow &&
+ root_container_handle_ == id;
+}
+
+void AcceleratedSurfaceContainerManagerMac::
+ set_gpu_rendering_active(bool active) {
+ if (gpu_rendering_active_ && !active)
+ SetRootSurfaceInvalid();
+ gpu_rendering_active_ = active;
+}
+
+void AcceleratedSurfaceContainerManagerMac::SetSizeAndIOSurface(
+ gfx::PluginWindowHandle id,
+ int32 width,
+ int32 height,
+ uint64 io_surface_identifier) {
+ base::AutoLock lock(lock_);
+
+ AcceleratedSurfaceContainerMac* container = MapIDToContainer(id);
+ if (container) {
+ container->SetSizeAndIOSurface(width, height, io_surface_identifier);
+ }
+}
+
+void AcceleratedSurfaceContainerManagerMac::SetSizeAndTransportDIB(
+ gfx::PluginWindowHandle id,
+ int32 width,
+ int32 height,
+ TransportDIB::Handle transport_dib) {
+ base::AutoLock lock(lock_);
+
+ AcceleratedSurfaceContainerMac* container = MapIDToContainer(id);
+ if (container)
+ container->SetSizeAndTransportDIB(width, height, transport_dib);
+}
+
+void AcceleratedSurfaceContainerManagerMac::SetPluginContainerGeometry(
+ const webkit::npapi::WebPluginGeometry& move) {
+ base::AutoLock lock(lock_);
+
+ AcceleratedSurfaceContainerMac* container = MapIDToContainer(move.window);
+ if (container)
+ container->SetGeometry(move);
+}
+
+void AcceleratedSurfaceContainerManagerMac::Draw(CGLContextObj context,
+ gfx::PluginWindowHandle id) {
+ base::AutoLock lock(lock_);
+
+ glColorMask(true, true, true, true);
+ // Should match the clear color of RenderWidgetHostViewMac.
+ glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+ // TODO(thakis): Clearing the whole color buffer is wasteful, since most of
+ // it is overwritten by the surface.
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_BLEND);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+
+ AcceleratedSurfaceContainerMac* container = MapIDToContainer(id);
+ CHECK(container);
+ container->Draw(context);
+
+ // Unbind any texture from the texture target to ensure that the
+ // next time through we will have to re-bind the texture and thereby
+ // pick up modifications from the other process.
+ GLenum target = GL_TEXTURE_RECTANGLE_ARB;
+ glBindTexture(target, 0);
+
+ glFlush();
+}
+
+void AcceleratedSurfaceContainerManagerMac::ForceTextureReload() {
+ base::AutoLock lock(lock_);
+
+ for (PluginWindowToContainerMap::const_iterator i =
+ plugin_window_to_container_map_.begin();
+ i != plugin_window_to_container_map_.end(); ++i) {
+ AcceleratedSurfaceContainerMac* container = i->second;
+ container->ForceTextureReload();
+ }
+}
+
+void AcceleratedSurfaceContainerManagerMac::SetSurfaceWasPaintedTo(
+ gfx::PluginWindowHandle id, uint64 surface_id) {
+ base::AutoLock lock(lock_);
+
+ AcceleratedSurfaceContainerMac* container = MapIDToContainer(id);
+ if (container)
+ container->set_was_painted_to(surface_id);
+}
+
+void AcceleratedSurfaceContainerManagerMac::SetRootSurfaceInvalid() {
+ base::AutoLock lock(lock_);
+ if (root_container_)
+ root_container_->set_surface_invalid();
+}
+
+bool AcceleratedSurfaceContainerManagerMac::SurfaceShouldBeVisible(
+ gfx::PluginWindowHandle id) const {
+ base::AutoLock lock(lock_);
+
+ if (IsRootContainer(id) && !gpu_rendering_active_)
+ return false;
+
+ AcceleratedSurfaceContainerMac* container = MapIDToContainer(id);
+ return container && container->ShouldBeVisible();
+}
+
+AcceleratedSurfaceContainerMac*
+ AcceleratedSurfaceContainerManagerMac::MapIDToContainer(
+ gfx::PluginWindowHandle id) const {
+ PluginWindowToContainerMap::const_iterator i =
+ plugin_window_to_container_map_.find(id);
+ if (i != plugin_window_to_container_map_.end())
+ return i->second;
+
+ LOG(ERROR) << "Request for plugin container for unknown window id " << id;
+
+ return NULL;
+}
diff --git a/content/browser/renderer_host/accelerated_surface_container_manager_mac.h b/content/browser/renderer_host/accelerated_surface_container_manager_mac.h
new file mode 100644
index 0000000..316ff58
--- /dev/null
+++ b/content/browser/renderer_host/accelerated_surface_container_manager_mac.h
@@ -0,0 +1,124 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_GPU_PLUGIN_CONTAINER_MANAGER_MAC_H_
+#define CONTENT_BROWSER_RENDERER_HOST_GPU_PLUGIN_CONTAINER_MANAGER_MAC_H_
+#pragma once
+
+#include <OpenGL/OpenGL.h>
+#include <map>
+
+#include "app/surface/transport_dib.h"
+#include "base/basictypes.h"
+#include "base/synchronization/lock.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace webkit {
+namespace npapi {
+struct WebPluginGeometry;
+}
+}
+
+class AcceleratedSurfaceContainerMac;
+
+// Helper class that manages the backing store and on-screen rendering
+// of instances of the GPU plugin on the Mac.
+class AcceleratedSurfaceContainerManagerMac {
+ public:
+ AcceleratedSurfaceContainerManagerMac();
+
+ // Allocates a new "fake" PluginWindowHandle, which is used as the
+ // key for the other operations.
+ gfx::PluginWindowHandle AllocateFakePluginWindowHandle(bool opaque,
+ bool root);
+
+ // Destroys a fake PluginWindowHandle and associated storage.
+ void DestroyFakePluginWindowHandle(gfx::PluginWindowHandle id);
+
+ // Indicates whether the given PluginWindowHandle is "root", which
+ // means that we are using accelerated compositing and that this one
+ // contains the compositor's output.
+ bool IsRootContainer(gfx::PluginWindowHandle id) const;
+
+ // Returns the handle of the compositor surface, or kNullPluginWindow if no
+ // compositor surface is active.
+ gfx::PluginWindowHandle root_container_handle() const {
+ return root_container_handle_;
+ }
+
+ // Informs the manager if gpu rendering is active.
+ void set_gpu_rendering_active(bool active);
+
+ // Sets the size and backing store of the plugin instance. There are two
+ // versions: the IOSurface version is used on systems where the IOSurface
+ // API is supported (Mac OS X 10.6 and later); the TransportDIB is used on
+ // Mac OS X 10.5 and earlier.
+ void SetSizeAndIOSurface(gfx::PluginWindowHandle id,
+ int32 width,
+ int32 height,
+ uint64 io_surface_identifier);
+ void SetSizeAndTransportDIB(gfx::PluginWindowHandle id,
+ int32 width,
+ int32 height,
+ TransportDIB::Handle transport_dib);
+
+ // Takes an update from WebKit about a plugin's position and size and moves
+ // the plugin accordingly.
+ void SetPluginContainerGeometry(
+ const webkit::npapi::WebPluginGeometry& move);
+
+ // Draws the plugin container associated with the given id into the given
+ // OpenGL context, which must already be current.
+ void Draw(CGLContextObj context, gfx::PluginWindowHandle id);
+
+ // Causes the next Draw call on each container to trigger a texture upload.
+ // Should be called any time the drawing context has changed.
+ void ForceTextureReload();
+
+ // Notifies a surface that it has been painted to.
+ void SetSurfaceWasPaintedTo(gfx::PluginWindowHandle id, uint64 surface_id);
+
+ // Notifies the root container that its surface is invalid.
+ void SetRootSurfaceInvalid();
+
+ // Returns if a given surface should be shown.
+ bool SurfaceShouldBeVisible(gfx::PluginWindowHandle id) const;
+ private:
+ uint32 current_id_;
+
+ // Maps a "fake" plugin window handle to the corresponding container.
+ AcceleratedSurfaceContainerMac*
+ MapIDToContainer(gfx::PluginWindowHandle id) const;
+
+ // A map that associates plugin window handles with their containers.
+ typedef std::map<gfx::PluginWindowHandle, AcceleratedSurfaceContainerMac*>
+ PluginWindowToContainerMap;
+ PluginWindowToContainerMap plugin_window_to_container_map_;
+
+ // The "root" container, which is only used to draw the output of
+ // the accelerated compositor if it is active. Currently,
+ // accelerated plugins (Core Animation and Pepper 3D) are drawn on
+ // top of the page's contents rather than transformed and composited
+ // with the rest of the page. At some point we would like them to be
+ // treated uniformly with other page elements; when this is done,
+ // the separate treatment of the root container can go away because
+ // there will only be one container active when the accelerated
+ // compositor is active.
+ AcceleratedSurfaceContainerMac* root_container_;
+ gfx::PluginWindowHandle root_container_handle_;
+
+ // True if gpu rendering is active. The root container is created on demand
+ // and destroyed only when a renderer process exits. When the compositor was
+ // created, this is set to |false| while the compositor is not needed.
+ bool gpu_rendering_active_;
+
+ // Both |plugin_window_to_container_map_| and the
+ // AcceleratedSurfaceContainerMac in it are not threadsafe, but accessed from
+ // multiple threads. All these accesses are guarded by this lock.
+ mutable base::Lock lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(AcceleratedSurfaceContainerManagerMac);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_GPU_PLUGIN_CONTAINER_MANAGER_MAC_H_
diff --git a/content/browser/renderer_host/async_resource_handler.cc b/content/browser/renderer_host/async_resource_handler.cc
new file mode 100644
index 0000000..937f120
--- /dev/null
+++ b/content/browser/renderer_host/async_resource_handler.cc
@@ -0,0 +1,265 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/async_resource_handler.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/hash_tables.h"
+#include "base/logging.h"
+#include "base/shared_memory.h"
+#include "chrome/browser/debugger/devtools_netlog_observer.h"
+#include "chrome/browser/net/chrome_url_request_context.h"
+#include "chrome/browser/net/load_timing_observer.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/resource_response.h"
+#include "content/browser/renderer_host/global_request_id.h"
+#include "content/browser/renderer_host/resource_dispatcher_host.h"
+#include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
+#include "content/browser/renderer_host/resource_message_filter.h"
+#include "net/base/io_buffer.h"
+#include "net/base/load_flags.h"
+#include "net/base/net_log.h"
+#include "webkit/glue/resource_loader_bridge.h"
+
+using base::Time;
+using base::TimeTicks;
+
+namespace {
+
+// When reading, we don't know if we are going to get EOF (0 bytes read), so
+// we typically have a buffer that we allocated but did not use. We keep
+// this buffer around for the next read as a small optimization.
+SharedIOBuffer* g_spare_read_buffer = NULL;
+
+// The initial size of the shared memory buffer. (32 kilobytes).
+const int kInitialReadBufSize = 32768;
+
+// The maximum size of the shared memory buffer. (512 kilobytes).
+const int kMaxReadBufSize = 524288;
+
+} // namespace
+
+// Our version of IOBuffer that uses shared memory.
+class SharedIOBuffer : public net::IOBuffer {
+ public:
+ explicit SharedIOBuffer(int buffer_size)
+ : net::IOBuffer(),
+ ok_(false),
+ buffer_size_(buffer_size) {}
+
+ bool Init() {
+ if (shared_memory_.CreateAndMapAnonymous(buffer_size_)) {
+ data_ = reinterpret_cast<char*>(shared_memory_.memory());
+ DCHECK(data_);
+ ok_ = true;
+ }
+ return ok_;
+ }
+
+ base::SharedMemory* shared_memory() { return &shared_memory_; }
+ bool ok() { return ok_; }
+ int buffer_size() { return buffer_size_; }
+
+ private:
+ ~SharedIOBuffer() {
+ DCHECK(g_spare_read_buffer != this);
+ data_ = NULL;
+ }
+
+ base::SharedMemory shared_memory_;
+ bool ok_;
+ int buffer_size_;
+};
+
+AsyncResourceHandler::AsyncResourceHandler(
+ ResourceMessageFilter* filter,
+ int routing_id,
+ const GURL& url,
+ ResourceDispatcherHost* resource_dispatcher_host)
+ : filter_(filter),
+ routing_id_(routing_id),
+ rdh_(resource_dispatcher_host),
+ next_buffer_size_(kInitialReadBufSize) {
+}
+
+AsyncResourceHandler::~AsyncResourceHandler() {
+}
+
+bool AsyncResourceHandler::OnUploadProgress(int request_id,
+ uint64 position,
+ uint64 size) {
+ return filter_->Send(new ViewMsg_Resource_UploadProgress(routing_id_,
+ request_id,
+ position, size));
+}
+
+bool AsyncResourceHandler::OnRequestRedirected(int request_id,
+ const GURL& new_url,
+ ResourceResponse* response,
+ bool* defer) {
+ *defer = true;
+ net::URLRequest* request = rdh_->GetURLRequest(
+ GlobalRequestID(filter_->child_id(), request_id));
+ LoadTimingObserver::PopulateTimingInfo(request, response);
+ DevToolsNetLogObserver::PopulateResponseInfo(request, response);
+ return filter_->Send(new ViewMsg_Resource_ReceivedRedirect(
+ routing_id_, request_id, new_url, response->response_head));
+}
+
+bool AsyncResourceHandler::OnResponseStarted(int request_id,
+ ResourceResponse* response) {
+ // For changes to the main frame, inform the renderer of the new URL's
+ // per-host settings before the request actually commits. This way the
+ // renderer will be able to set these precisely at the time the
+ // request commits, avoiding the possibility of e.g. zooming the old content
+ // or of having to layout the new content twice.
+ net::URLRequest* request = rdh_->GetURLRequest(
+ GlobalRequestID(filter_->child_id(), request_id));
+
+ LoadTimingObserver::PopulateTimingInfo(request, response);
+ DevToolsNetLogObserver::PopulateResponseInfo(request, response);
+
+ ResourceDispatcherHostRequestInfo* info = rdh_->InfoForRequest(request);
+ if (info->resource_type() == ResourceType::MAIN_FRAME) {
+ GURL request_url(request->url());
+ ChromeURLRequestContext* context =
+ static_cast<ChromeURLRequestContext*>(request->context());
+ if (context) {
+ filter_->Send(new ViewMsg_SetContentSettingsForLoadingURL(
+ info->route_id(), request_url,
+ context->host_content_settings_map()->GetContentSettings(
+ request_url)));
+ filter_->Send(new ViewMsg_SetZoomLevelForLoadingURL(info->route_id(),
+ request_url, context->host_zoom_map()->GetZoomLevel(request_url)));
+ }
+ }
+
+ filter_->Send(new ViewMsg_Resource_ReceivedResponse(
+ routing_id_, request_id, response->response_head));
+
+ if (request->response_info().metadata) {
+ std::vector<char> copy(request->response_info().metadata->data(),
+ request->response_info().metadata->data() +
+ request->response_info().metadata->size());
+ filter_->Send(new ViewMsg_Resource_ReceivedCachedMetadata(
+ routing_id_, request_id, copy));
+ }
+
+ return true;
+}
+
+bool AsyncResourceHandler::OnWillStart(int request_id,
+ const GURL& url,
+ bool* defer) {
+ return true;
+}
+
+bool AsyncResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf,
+ int* buf_size, int min_size) {
+ DCHECK_EQ(-1, min_size);
+
+ if (g_spare_read_buffer) {
+ DCHECK(!read_buffer_);
+ read_buffer_.swap(&g_spare_read_buffer);
+ DCHECK(read_buffer_->data());
+
+ *buf = read_buffer_.get();
+ *buf_size = read_buffer_->buffer_size();
+ } else {
+ read_buffer_ = new SharedIOBuffer(next_buffer_size_);
+ if (!read_buffer_->Init()) {
+ DLOG(ERROR) << "Couldn't allocate shared io buffer";
+ read_buffer_ = NULL;
+ return false;
+ }
+ DCHECK(read_buffer_->data());
+ *buf = read_buffer_.get();
+ *buf_size = next_buffer_size_;
+ }
+
+ return true;
+}
+
+bool AsyncResourceHandler::OnReadCompleted(int request_id, int* bytes_read) {
+ if (!*bytes_read)
+ return true;
+ DCHECK(read_buffer_.get());
+
+ if (read_buffer_->buffer_size() == *bytes_read) {
+ // The network layer has saturated our buffer. Next time, we should give it
+ // a bigger buffer for it to fill, to minimize the number of round trips we
+ // do with the renderer process.
+ next_buffer_size_ = std::min(next_buffer_size_ * 2, kMaxReadBufSize);
+ }
+
+ if (!rdh_->WillSendData(filter_->child_id(), request_id)) {
+ // We should not send this data now, we have too many pending requests.
+ return true;
+ }
+
+ base::SharedMemoryHandle handle;
+ if (!read_buffer_->shared_memory()->GiveToProcess(
+ filter_->peer_handle(), &handle)) {
+ // We wrongfully incremented the pending data count. Fake an ACK message
+ // to fix this. We can't move this call above the WillSendData because
+ // it's killing our read_buffer_, and we don't want that when we pause
+ // the request.
+ rdh_->DataReceivedACK(filter_->child_id(), request_id);
+ // We just unmapped the memory.
+ read_buffer_ = NULL;
+ return false;
+ }
+ // We just unmapped the memory.
+ read_buffer_ = NULL;
+
+ filter_->Send(new ViewMsg_Resource_DataReceived(
+ routing_id_, request_id, handle, *bytes_read));
+
+ return true;
+}
+
+void AsyncResourceHandler::OnDataDownloaded(
+ int request_id, int bytes_downloaded) {
+ filter_->Send(new ViewMsg_Resource_DataDownloaded(
+ routing_id_, request_id, bytes_downloaded));
+}
+
+bool AsyncResourceHandler::OnResponseCompleted(
+ int request_id,
+ const net::URLRequestStatus& status,
+ const std::string& security_info) {
+ Time completion_time = Time::Now();
+ filter_->Send(new ViewMsg_Resource_RequestComplete(routing_id_,
+ request_id,
+ status,
+ security_info,
+ completion_time));
+
+ // If we still have a read buffer, then see about caching it for later...
+ // Note that we have to make sure the buffer is not still being used, so we
+ // have to perform an explicit check on the status code.
+ if (g_spare_read_buffer ||
+ net::URLRequestStatus::SUCCESS != status.status()) {
+ read_buffer_ = NULL;
+ } else if (read_buffer_.get()) {
+ DCHECK(read_buffer_->data());
+ read_buffer_.swap(&g_spare_read_buffer);
+ }
+ return true;
+}
+
+void AsyncResourceHandler::OnRequestClosed() {
+}
+
+// static
+void AsyncResourceHandler::GlobalCleanup() {
+ if (g_spare_read_buffer) {
+ // Avoid the CHECK in SharedIOBuffer::~SharedIOBuffer().
+ SharedIOBuffer* tmp = g_spare_read_buffer;
+ g_spare_read_buffer = NULL;
+ tmp->Release();
+ }
+}
diff --git a/content/browser/renderer_host/async_resource_handler.h b/content/browser/renderer_host/async_resource_handler.h
new file mode 100644
index 0000000..8e61f8a
--- /dev/null
+++ b/content/browser/renderer_host/async_resource_handler.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_ASYNC_RESOURCE_HANDLER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_ASYNC_RESOURCE_HANDLER_H_
+#pragma once
+
+#include <string>
+
+#include "chrome/browser/renderer_host/resource_handler.h"
+
+class ResourceDispatcherHost;
+class ResourceMessageFilter;
+class SharedIOBuffer;
+
+// Used to complete an asynchronous resource request in response to resource
+// load events from the resource dispatcher host.
+class AsyncResourceHandler : public ResourceHandler {
+ public:
+ AsyncResourceHandler(ResourceMessageFilter* filter,
+ int routing_id,
+ const GURL& url,
+ ResourceDispatcherHost* resource_dispatcher_host);
+
+ // ResourceHandler implementation:
+ virtual bool OnUploadProgress(int request_id, uint64 position, uint64 size);
+ virtual bool OnRequestRedirected(int request_id, const GURL& new_url,
+ ResourceResponse* response, bool* defer);
+ virtual bool OnResponseStarted(int request_id, ResourceResponse* response);
+ virtual bool OnWillStart(int request_id, const GURL& url, bool* defer);
+ virtual bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size,
+ int min_size);
+ virtual bool OnReadCompleted(int request_id, int* bytes_read);
+ virtual bool OnResponseCompleted(int request_id,
+ const net::URLRequestStatus& status,
+ const std::string& security_info);
+ virtual void OnRequestClosed();
+ virtual void OnDataDownloaded(int request_id, int bytes_downloaded);
+
+ static void GlobalCleanup();
+
+ private:
+ virtual ~AsyncResourceHandler();
+
+ scoped_refptr<SharedIOBuffer> read_buffer_;
+ ResourceMessageFilter* filter_;
+ int routing_id_;
+ ResourceDispatcherHost* rdh_;
+
+ // |next_buffer_size_| is the size of the buffer to be allocated on the next
+ // OnWillRead() call. We exponentially grow the size of the buffer allocated
+ // when our owner fills our buffers. On the first OnWillRead() call, we
+ // allocate a buffer of 32k and double it in OnReadCompleted() if the buffer
+ // was filled, up to a maximum size of 512k.
+ int next_buffer_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(AsyncResourceHandler);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_ASYNC_RESOURCE_HANDLER_H_
diff --git a/content/browser/renderer_host/audio_renderer_host.cc b/content/browser/renderer_host/audio_renderer_host.cc
new file mode 100644
index 0000000..4fac389
--- /dev/null
+++ b/content/browser/renderer_host/audio_renderer_host.cc
@@ -0,0 +1,480 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/audio_renderer_host.h"
+
+#include "base/metrics/histogram.h"
+#include "base/process.h"
+#include "base/shared_memory.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/render_messages_params.h"
+#include "content/browser/renderer_host/audio_sync_reader.h"
+#include "ipc/ipc_logging.h"
+
+// The minimum number of samples in a hardware packet.
+// This value is selected so that we can handle down to 5khz sample rate.
+static const int kMinSamplesPerHardwarePacket = 1024;
+
+// The maximum number of samples in a hardware packet.
+// This value is selected so that we can handle up to 192khz sample rate.
+static const int kMaxSamplesPerHardwarePacket = 64 * 1024;
+
+// This constant governs the hardware audio buffer size, this value should be
+// chosen carefully.
+// This value is selected so that we have 8192 samples for 48khz streams.
+static const int kMillisecondsPerHardwarePacket = 170;
+
+static uint32 SelectSamplesPerPacket(AudioParameters params) {
+ // Select the number of samples that can provide at least
+ // |kMillisecondsPerHardwarePacket| worth of audio data.
+ int samples = kMinSamplesPerHardwarePacket;
+ while (samples <= kMaxSamplesPerHardwarePacket &&
+ samples * base::Time::kMillisecondsPerSecond <
+ params.sample_rate * kMillisecondsPerHardwarePacket) {
+ samples *= 2;
+ }
+ return samples;
+}
+
+AudioRendererHost::AudioEntry::AudioEntry()
+ : render_view_id(0),
+ stream_id(0),
+ pending_buffer_request(false),
+ pending_close(false) {
+}
+
+AudioRendererHost::AudioEntry::~AudioEntry() {}
+
+///////////////////////////////////////////////////////////////////////////////
+// AudioRendererHost implementations.
+AudioRendererHost::AudioRendererHost() {
+}
+
+AudioRendererHost::~AudioRendererHost() {
+ DCHECK(audio_entries_.empty());
+}
+
+void AudioRendererHost::OnChannelClosing() {
+ BrowserMessageFilter::OnChannelClosing();
+
+ // Since the IPC channel is gone, close all requested audio streams.
+ DeleteEntries();
+}
+
+void AudioRendererHost::OnDestruct() const {
+ BrowserThread::DeleteOnIOThread::Destruct(this);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// media::AudioOutputController::EventHandler implementations.
+void AudioRendererHost::OnCreated(media::AudioOutputController* controller) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &AudioRendererHost::DoCompleteCreation,
+ make_scoped_refptr(controller)));
+}
+
+void AudioRendererHost::OnPlaying(media::AudioOutputController* controller) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &AudioRendererHost::DoSendPlayingMessage,
+ make_scoped_refptr(controller)));
+}
+
+void AudioRendererHost::OnPaused(media::AudioOutputController* controller) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &AudioRendererHost::DoSendPausedMessage,
+ make_scoped_refptr(controller)));
+}
+
+void AudioRendererHost::OnError(media::AudioOutputController* controller,
+ int error_code) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &AudioRendererHost::DoHandleError,
+ make_scoped_refptr(controller),
+ error_code));
+}
+
+void AudioRendererHost::OnMoreData(media::AudioOutputController* controller,
+ AudioBuffersState buffers_state) {
+ BrowserThread::PostTask(
+ BrowserThread::IO,
+ FROM_HERE,
+ NewRunnableMethod(this,
+ &AudioRendererHost::DoRequestMoreData,
+ make_scoped_refptr(controller),
+ buffers_state));
+}
+
+void AudioRendererHost::DoCompleteCreation(
+ media::AudioOutputController* controller) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ AudioEntry* entry = LookupByController(controller);
+ if (!entry)
+ return;
+
+ if (!peer_handle()) {
+ NOTREACHED() << "Renderer process handle is invalid.";
+ DeleteEntryOnError(entry);
+ return;
+ }
+
+ // Once the audio stream is created then complete the creation process by
+ // mapping shared memory and sharing with the renderer process.
+ base::SharedMemoryHandle foreign_memory_handle;
+ if (!entry->shared_memory.ShareToProcess(peer_handle(),
+ &foreign_memory_handle)) {
+ // If we failed to map and share the shared memory then close the audio
+ // stream and send an error message.
+ DeleteEntryOnError(entry);
+ return;
+ }
+
+ if (entry->controller->LowLatencyMode()) {
+ AudioSyncReader* reader =
+ static_cast<AudioSyncReader*>(entry->reader.get());
+
+#if defined(OS_WIN)
+ base::SyncSocket::Handle foreign_socket_handle;
+#else
+ base::FileDescriptor foreign_socket_handle;
+#endif
+
+ // If we failed to prepare the sync socket for the renderer then we fail
+ // the construction of audio stream.
+ if (!reader->PrepareForeignSocketHandle(peer_handle(),
+ &foreign_socket_handle)) {
+ DeleteEntryOnError(entry);
+ return;
+ }
+
+ Send(new ViewMsg_NotifyLowLatencyAudioStreamCreated(
+ entry->render_view_id, entry->stream_id, foreign_memory_handle,
+ foreign_socket_handle, entry->shared_memory.created_size()));
+ return;
+ }
+
+ // The normal audio stream has created, send a message to the renderer
+ // process.
+ Send(new ViewMsg_NotifyAudioStreamCreated(
+ entry->render_view_id, entry->stream_id, foreign_memory_handle,
+ entry->shared_memory.created_size()));
+}
+
+void AudioRendererHost::DoSendPlayingMessage(
+ media::AudioOutputController* controller) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ AudioEntry* entry = LookupByController(controller);
+ if (!entry)
+ return;
+
+ ViewMsg_AudioStreamState_Params params;
+ params.state = ViewMsg_AudioStreamState_Params::kPlaying;
+ Send(new ViewMsg_NotifyAudioStreamStateChanged(
+ entry->render_view_id, entry->stream_id, params));
+}
+
+void AudioRendererHost::DoSendPausedMessage(
+ media::AudioOutputController* controller) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ AudioEntry* entry = LookupByController(controller);
+ if (!entry)
+ return;
+
+ ViewMsg_AudioStreamState_Params params;
+ params.state = ViewMsg_AudioStreamState_Params::kPaused;
+ Send(new ViewMsg_NotifyAudioStreamStateChanged(
+ entry->render_view_id, entry->stream_id, params));
+}
+
+void AudioRendererHost::DoRequestMoreData(
+ media::AudioOutputController* controller,
+ AudioBuffersState buffers_state) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ // If we already have a pending request then return.
+ AudioEntry* entry = LookupByController(controller);
+ if (!entry || entry->pending_buffer_request)
+ return;
+
+ DCHECK(!entry->controller->LowLatencyMode());
+ entry->pending_buffer_request = true;
+ Send(new ViewMsg_RequestAudioPacket(
+ entry->render_view_id, entry->stream_id, buffers_state));
+}
+
+void AudioRendererHost::DoHandleError(media::AudioOutputController* controller,
+ int error_code) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ AudioEntry* entry = LookupByController(controller);
+ if (!entry)
+ return;
+
+ DeleteEntryOnError(entry);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// IPC Messages handler
+bool AudioRendererHost::OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(AudioRendererHost, message, *message_was_ok)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_CreateAudioStream, OnCreateStream)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_PlayAudioStream, OnPlayStream)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_PauseAudioStream, OnPauseStream)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_FlushAudioStream, OnFlushStream)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_CloseAudioStream, OnCloseStream)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_NotifyAudioPacketReady, OnNotifyPacketReady)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_GetAudioVolume, OnGetVolume)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_SetAudioVolume, OnSetVolume)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP_EX()
+
+ return handled;
+}
+
+void AudioRendererHost::OnCreateStream(
+ const IPC::Message& msg, int stream_id,
+ const ViewHostMsg_Audio_CreateStream_Params& params, bool low_latency) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(LookupById(msg.routing_id(), stream_id) == NULL);
+
+ AudioParameters audio_params(params.params);
+
+ // Select the hardware packet size if not specified.
+ if (!audio_params.samples_per_packet) {
+ audio_params.samples_per_packet = SelectSamplesPerPacket(audio_params);
+ }
+ uint32 packet_size = audio_params.GetPacketSize();
+
+ scoped_ptr<AudioEntry> entry(new AudioEntry());
+ // Create the shared memory and share with the renderer process.
+ if (!entry->shared_memory.CreateAndMapAnonymous(packet_size)) {
+ // If creation of shared memory failed then send an error message.
+ SendErrorMessage(msg.routing_id(), stream_id);
+ return;
+ }
+
+ if (low_latency) {
+ // If this is the low latency mode, we need to construct a SyncReader first.
+ scoped_ptr<AudioSyncReader> reader(
+ new AudioSyncReader(&entry->shared_memory));
+
+ // Then try to initialize the sync reader.
+ if (!reader->Init()) {
+ SendErrorMessage(msg.routing_id(), stream_id);
+ return;
+ }
+
+ // If we have successfully created the SyncReader then assign it to the
+ // entry and construct an AudioOutputController.
+ entry->reader.reset(reader.release());
+ entry->controller =
+ media::AudioOutputController::CreateLowLatency(this, audio_params,
+ entry->reader.get());
+ } else {
+ // The choice of buffer capacity is based on experiment.
+ entry->controller =
+ media::AudioOutputController::Create(this, audio_params,
+ 3 * packet_size);
+ }
+
+ if (!entry->controller) {
+ SendErrorMessage(msg.routing_id(), stream_id);
+ return;
+ }
+
+ // If we have created the controller successfully create a entry and add it
+ // to the map.
+ entry->render_view_id = msg.routing_id();
+ entry->stream_id = stream_id;
+
+ audio_entries_.insert(std::make_pair(
+ AudioEntryId(msg.routing_id(), stream_id),
+ entry.release()));
+}
+
+void AudioRendererHost::OnPlayStream(const IPC::Message& msg, int stream_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ AudioEntry* entry = LookupById(msg.routing_id(), stream_id);
+ if (!entry) {
+ SendErrorMessage(msg.routing_id(), stream_id);
+ return;
+ }
+
+ entry->controller->Play();
+}
+
+void AudioRendererHost::OnPauseStream(const IPC::Message& msg, int stream_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ AudioEntry* entry = LookupById(msg.routing_id(), stream_id);
+ if (!entry) {
+ SendErrorMessage(msg.routing_id(), stream_id);
+ return;
+ }
+
+ entry->controller->Pause();
+}
+
+void AudioRendererHost::OnFlushStream(const IPC::Message& msg, int stream_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ AudioEntry* entry = LookupById(msg.routing_id(), stream_id);
+ if (!entry) {
+ SendErrorMessage(msg.routing_id(), stream_id);
+ return;
+ }
+
+ entry->controller->Flush();
+}
+
+void AudioRendererHost::OnCloseStream(const IPC::Message& msg, int stream_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ AudioEntry* entry = LookupById(msg.routing_id(), stream_id);
+
+ if (entry)
+ CloseAndDeleteStream(entry);
+}
+
+void AudioRendererHost::OnSetVolume(const IPC::Message& msg, int stream_id,
+ double volume) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ AudioEntry* entry = LookupById(msg.routing_id(), stream_id);
+ if (!entry) {
+ SendErrorMessage(msg.routing_id(), stream_id);
+ return;
+ }
+
+ // Make sure the volume is valid.
+ if (volume < 0 || volume > 1.0)
+ return;
+ entry->controller->SetVolume(volume);
+}
+
+void AudioRendererHost::OnGetVolume(const IPC::Message& msg, int stream_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ NOTREACHED() << "This message shouldn't be received";
+}
+
+void AudioRendererHost::OnNotifyPacketReady(
+ const IPC::Message& msg, int stream_id, uint32 packet_size) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ AudioEntry* entry = LookupById(msg.routing_id(), stream_id);
+ if (!entry) {
+ SendErrorMessage(msg.routing_id(), stream_id);
+ return;
+ }
+
+ DCHECK(!entry->controller->LowLatencyMode());
+ CHECK(packet_size <= entry->shared_memory.created_size());
+
+ if (!entry->pending_buffer_request) {
+ NOTREACHED() << "Buffer received but no such pending request";
+ }
+ entry->pending_buffer_request = false;
+
+ // Enqueue the data to media::AudioOutputController.
+ entry->controller->EnqueueData(
+ reinterpret_cast<uint8*>(entry->shared_memory.memory()),
+ packet_size);
+}
+
+void AudioRendererHost::SendErrorMessage(int32 render_view_id,
+ int32 stream_id) {
+ ViewMsg_AudioStreamState_Params state;
+ state.state = ViewMsg_AudioStreamState_Params::kError;
+ Send(new ViewMsg_NotifyAudioStreamStateChanged(
+ render_view_id, stream_id, state));
+}
+
+void AudioRendererHost::DeleteEntries() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ for (AudioEntryMap::iterator i = audio_entries_.begin();
+ i != audio_entries_.end(); ++i) {
+ CloseAndDeleteStream(i->second);
+ }
+}
+
+void AudioRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
+ if (!entry->pending_close) {
+ entry->controller->Close(
+ NewRunnableMethod(this, &AudioRendererHost::OnStreamClosed, entry));
+ entry->pending_close = true;
+ }
+}
+
+void AudioRendererHost::OnStreamClosed(AudioEntry* entry) {
+ // Delete the entry after we've closed the stream.
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ NewRunnableMethod(this, &AudioRendererHost::DeleteEntry, entry));
+}
+
+void AudioRendererHost::DeleteEntry(AudioEntry* entry) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ // Delete the entry when this method goes out of scope.
+ scoped_ptr<AudioEntry> entry_deleter(entry);
+
+ // Erase the entry from the map.
+ audio_entries_.erase(
+ AudioEntryId(entry->render_view_id, entry->stream_id));
+}
+
+void AudioRendererHost::DeleteEntryOnError(AudioEntry* entry) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ // Sends the error message first before we close the stream because
+ // |entry| is destroyed in DeleteEntry().
+ SendErrorMessage(entry->render_view_id, entry->stream_id);
+ CloseAndDeleteStream(entry);
+}
+
+AudioRendererHost::AudioEntry* AudioRendererHost::LookupById(
+ int route_id, int stream_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ AudioEntryMap::iterator i = audio_entries_.find(
+ AudioEntryId(route_id, stream_id));
+ if (i != audio_entries_.end() && !i->second->pending_close)
+ return i->second;
+ return NULL;
+}
+
+AudioRendererHost::AudioEntry* AudioRendererHost::LookupByController(
+ media::AudioOutputController* controller) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ // Iterate the map of entries.
+ // TODO(hclam): Implement a faster look up method.
+ for (AudioEntryMap::iterator i = audio_entries_.begin();
+ i != audio_entries_.end(); ++i) {
+ if (!i->second->pending_close && controller == i->second->controller.get())
+ return i->second;
+ }
+ return NULL;
+}
diff --git a/content/browser/renderer_host/audio_renderer_host.h b/content/browser/renderer_host/audio_renderer_host.h
new file mode 100644
index 0000000..128723c
--- /dev/null
+++ b/content/browser/renderer_host/audio_renderer_host.h
@@ -0,0 +1,223 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// AudioRendererHost serves audio related requests from AudioRenderer which
+// lives inside the render process and provide access to audio hardware.
+//
+// This class is owned by BrowserRenderProcessHost, and instantiated on UI
+// thread, but all other operations and method calls happen on IO thread, so we
+// need to be extra careful about the lifetime of this object. AudioManager is a
+// singleton and created in IO thread, audio output streams are also created in
+// the IO thread, so we need to destroy them also in IO thread. After this class
+// is created, a task of OnInitialized() is posted on IO thread in which
+// singleton of AudioManager is created and.
+//
+// Here's an example of a typical IPC dialog for audio:
+//
+// Renderer AudioRendererHost
+// | |
+// | CreateStream > |
+// | < Created |
+// | |
+// | Play > |
+// | < Playing | time
+// | |
+// | < RequestAudioPacket |
+// | AudioPacketReady > |
+// | ... |
+// | < RequestAudioPacket |
+// | AudioPacketReady > |
+// | |
+// | ... |
+// | < RequestAudioPacket |
+// | AudioPacketReady > |
+// | ... |
+// | Pause > |
+// | < Paused |
+// | ... |
+// | Start > |
+// | < Started |
+// | ... |
+// | Close > |
+// v v
+
+// The above mode of operation uses relatively big buffers and has latencies
+// of 50 ms or more. There is a second mode of operation which is low latency.
+// For low latency audio, the picture above is modified by not having the
+// RequestAudioPacket and the AudioPacketReady messages, instead a SyncSocket
+// pair is used to signal buffer readiness without having to route messages
+// using the IO thread.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_AUDIO_RENDERER_HOST_H_
+#define CONTENT_BROWSER_RENDERER_HOST_AUDIO_RENDERER_HOST_H_
+#pragma once
+
+#include <map>
+
+#include "base/gtest_prod_util.h"
+#include "base/process.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "base/shared_memory.h"
+#include "chrome/browser/browser_message_filter.h"
+#include "chrome/browser/browser_thread.h"
+#include "ipc/ipc_message.h"
+#include "media/audio/audio_io.h"
+#include "media/audio/audio_output_controller.h"
+#include "media/audio/simple_sources.h"
+
+class AudioManager;
+struct ViewHostMsg_Audio_CreateStream_Params;
+
+class AudioRendererHost : public BrowserMessageFilter,
+ public media::AudioOutputController::EventHandler {
+ public:
+ typedef std::pair<int32, int> AudioEntryId;
+
+ struct AudioEntry {
+ AudioEntry();
+ ~AudioEntry();
+
+ // The AudioOutputController that manages the audio stream.
+ scoped_refptr<media::AudioOutputController> controller;
+
+ // Render view ID that requested the audio stream.
+ int32 render_view_id;
+
+ // The audio stream ID in the render view.
+ int stream_id;
+
+ // Shared memory for transmission of the audio data.
+ base::SharedMemory shared_memory;
+
+ // The synchronous reader to be used by the controller. We have the
+ // ownership of the reader.
+ scoped_ptr<media::AudioOutputController::SyncReader> reader;
+
+ bool pending_buffer_request;
+
+ // Set to true after we called Close() for the controller.
+ bool pending_close;
+ };
+
+ typedef std::map<AudioEntryId, AudioEntry*> AudioEntryMap;
+
+ // Called from UI thread from the owner of this object.
+ AudioRendererHost();
+
+
+ // BrowserMessageFilter implementation.
+ virtual void OnChannelClosing();
+ virtual void OnDestruct() const;
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok);
+
+ /////////////////////////////////////////////////////////////////////////////
+ // AudioOutputController::EventHandler implementations.
+ virtual void OnCreated(media::AudioOutputController* controller);
+ virtual void OnPlaying(media::AudioOutputController* controller);
+ virtual void OnPaused(media::AudioOutputController* controller);
+ virtual void OnError(media::AudioOutputController* controller,
+ int error_code);
+ virtual void OnMoreData(media::AudioOutputController* controller,
+ AudioBuffersState buffers_state);
+
+ private:
+ friend class AudioRendererHostTest;
+ friend class BrowserThread;
+ friend class DeleteTask<AudioRendererHost>;
+ friend class MockAudioRendererHost;
+ FRIEND_TEST_ALL_PREFIXES(AudioRendererHostTest, CreateMockStream);
+ FRIEND_TEST_ALL_PREFIXES(AudioRendererHostTest, MockStreamDataConversation);
+
+ virtual ~AudioRendererHost();
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Methods called on IO thread.
+ // Returns true if the message is an audio related message and should be
+ // handled by this class.
+ bool IsAudioRendererHostMessage(const IPC::Message& message);
+
+ // Audio related IPC message handlers.
+ // Creates an audio output stream with the specified format. If this call is
+ // successful this object would keep an internal entry of the stream for the
+ // required properties.
+ void OnCreateStream(const IPC::Message& msg, int stream_id,
+ const ViewHostMsg_Audio_CreateStream_Params& params,
+ bool low_latency);
+
+ // Play the audio stream referenced by |stream_id|.
+ void OnPlayStream(const IPC::Message& msg, int stream_id);
+
+ // Pause the audio stream referenced by |stream_id|.
+ void OnPauseStream(const IPC::Message& msg, int stream_id);
+
+ // Discard all audio data in stream referenced by |stream_id|.
+ void OnFlushStream(const IPC::Message& msg, int stream_id);
+
+ // Close the audio stream referenced by |stream_id|.
+ void OnCloseStream(const IPC::Message& msg, int stream_id);
+
+ // Set the volume of the audio stream referenced by |stream_id|.
+ void OnSetVolume(const IPC::Message& msg, int stream_id, double volume);
+
+ // Get the volume of the audio stream referenced by |stream_id|.
+ void OnGetVolume(const IPC::Message& msg, int stream_id);
+
+ // Notify packet has been prepared for the audio stream.
+ void OnNotifyPacketReady(const IPC::Message& msg, int stream_id,
+ uint32 packet_size);
+
+ // Complete the process of creating an audio stream. This will set up the
+ // shared memory or shared socket in low latency mode.
+ void DoCompleteCreation(media::AudioOutputController* controller);
+
+ // Send a state change message to the renderer.
+ void DoSendPlayingMessage(media::AudioOutputController* controller);
+ void DoSendPausedMessage(media::AudioOutputController* controller);
+
+ // Request more data from the renderer. This method is used only in normal
+ // latency mode.
+ void DoRequestMoreData(media::AudioOutputController* controller,
+ AudioBuffersState buffers_state);
+
+ // Handle error coming from audio stream.
+ void DoHandleError(media::AudioOutputController* controller, int error_code);
+
+ // Send an error message to the renderer.
+ void SendErrorMessage(int32 render_view_id, int32 stream_id);
+
+ // Delete all audio entry and all audio streams
+ void DeleteEntries();
+
+ // Closes the stream. The stream is then deleted in DeleteEntry() after it
+ // is closed.
+ void CloseAndDeleteStream(AudioEntry* entry);
+
+ // Called on the audio thread after the audio stream is closed.
+ void OnStreamClosed(AudioEntry* entry);
+
+ // Delete an audio entry and close the related audio stream.
+ void DeleteEntry(AudioEntry* entry);
+
+ // Delete audio entry and close the related audio stream due to an error,
+ // and error message is send to the renderer.
+ void DeleteEntryOnError(AudioEntry* entry);
+
+ // A helper method to look up a AudioEntry with a tuple of render view
+ // id and stream id. Returns NULL if not found.
+ AudioEntry* LookupById(int render_view_id, int stream_id);
+
+ // Search for a AudioEntry having the reference to |controller|.
+ // This method is used to look up an AudioEntry after a controller
+ // event is received.
+ AudioEntry* LookupByController(media::AudioOutputController* controller);
+
+ // A map of id to audio sources.
+ AudioEntryMap audio_entries_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioRendererHost);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_AUDIO_RENDERER_HOST_H_
diff --git a/content/browser/renderer_host/audio_renderer_host_unittest.cc b/content/browser/renderer_host/audio_renderer_host_unittest.cc
new file mode 100644
index 0000000..4f6c3cd
--- /dev/null
+++ b/content/browser/renderer_host/audio_renderer_host_unittest.cc
@@ -0,0 +1,488 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/environment.h"
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+#include "base/sync_socket.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/render_messages_params.h"
+#include "content/browser/renderer_host/audio_renderer_host.h"
+#include "ipc/ipc_message_utils.h"
+#include "media/audio/audio_manager.h"
+#include "media/audio/fake_audio_output_stream.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+using ::testing::SaveArg;
+using ::testing::SetArgumentPointee;
+
+static const int kInvalidId = -1;
+static const int kRouteId = 200;
+static const int kStreamId = 50;
+
+static bool IsRunningHeadless() {
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ if (env->HasVar("CHROME_HEADLESS"))
+ return true;
+ return false;
+}
+
+class MockAudioRendererHost : public AudioRendererHost {
+ public:
+ MockAudioRendererHost() : shared_memory_length_(0) {
+ }
+
+ virtual ~MockAudioRendererHost() {
+ }
+
+ // A list of mock methods.
+ MOCK_METHOD3(OnRequestPacket,
+ void(int routing_id, int stream_id,
+ AudioBuffersState buffers_state));
+ MOCK_METHOD3(OnStreamCreated,
+ void(int routing_id, int stream_id, int length));
+ MOCK_METHOD3(OnLowLatencyStreamCreated,
+ void(int routing_id, int stream_id, int length));
+ MOCK_METHOD2(OnStreamPlaying, void(int routing_id, int stream_id));
+ MOCK_METHOD2(OnStreamPaused, void(int routing_id, int stream_id));
+ MOCK_METHOD2(OnStreamError, void(int routing_id, int stream_id));
+ MOCK_METHOD3(OnStreamVolume,
+ void(int routing_id, int stream_id, double volume));
+
+ base::SharedMemory* shared_memory() { return shared_memory_.get(); }
+ uint32 shared_memory_length() { return shared_memory_length_; }
+
+ base::SyncSocket* sync_socket() { return sync_socket_.get(); }
+
+ private:
+ // This method is used to dispatch IPC messages to the renderer. We intercept
+ // these messages here and dispatch to our mock methods to verify the
+ // conversation between this object and the renderer.
+ virtual bool Send(IPC::Message* message) {
+ CHECK(message);
+
+ // In this method we dispatch the messages to the according handlers as if
+ // we are the renderer.
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(MockAudioRendererHost, *message)
+ IPC_MESSAGE_HANDLER(ViewMsg_RequestAudioPacket, OnRequestPacket)
+ IPC_MESSAGE_HANDLER(ViewMsg_NotifyAudioStreamCreated, OnStreamCreated)
+ IPC_MESSAGE_HANDLER(ViewMsg_NotifyLowLatencyAudioStreamCreated,
+ OnLowLatencyStreamCreated)
+ IPC_MESSAGE_HANDLER(ViewMsg_NotifyAudioStreamStateChanged,
+ OnStreamStateChanged)
+ IPC_MESSAGE_HANDLER(ViewMsg_NotifyAudioStreamVolume, OnStreamVolume)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ EXPECT_TRUE(handled);
+
+ delete message;
+ return true;
+ }
+
+ // These handler methods do minimal things and delegate to the mock methods.
+ void OnRequestPacket(const IPC::Message& msg, int stream_id,
+ AudioBuffersState buffers_state) {
+ OnRequestPacket(msg.routing_id(), stream_id, buffers_state);
+ }
+
+ void OnStreamCreated(const IPC::Message& msg, int stream_id,
+ base::SharedMemoryHandle handle, uint32 length) {
+ // Maps the shared memory.
+ shared_memory_.reset(new base::SharedMemory(handle, false));
+ ASSERT_TRUE(shared_memory_->Map(length));
+ ASSERT_TRUE(shared_memory_->memory());
+ shared_memory_length_ = length;
+
+ // And then delegate the call to the mock method.
+ OnStreamCreated(msg.routing_id(), stream_id, length);
+ }
+
+ void OnLowLatencyStreamCreated(const IPC::Message& msg, int stream_id,
+ base::SharedMemoryHandle handle,
+#if defined(OS_WIN)
+ base::SyncSocket::Handle socket_handle,
+#else
+ base::FileDescriptor socket_descriptor,
+#endif
+ uint32 length) {
+ // Maps the shared memory.
+ shared_memory_.reset(new base::SharedMemory(handle, false));
+ CHECK(shared_memory_->Map(length));
+ CHECK(shared_memory_->memory());
+ shared_memory_length_ = length;
+
+ // Create the SyncSocket using the handle.
+ base::SyncSocket::Handle sync_socket_handle;
+#if defined(OS_WIN)
+ sync_socket_handle = socket_handle;
+#else
+ sync_socket_handle = socket_descriptor.fd;
+#endif
+ sync_socket_.reset(new base::SyncSocket(sync_socket_handle));
+
+ // And then delegate the call to the mock method.
+ OnLowLatencyStreamCreated(msg.routing_id(), stream_id, length);
+ }
+
+ void OnStreamStateChanged(const IPC::Message& msg, int stream_id,
+ const ViewMsg_AudioStreamState_Params& params) {
+ if (params.state == ViewMsg_AudioStreamState_Params::kPlaying) {
+ OnStreamPlaying(msg.routing_id(), stream_id);
+ } else if (params.state == ViewMsg_AudioStreamState_Params::kPaused) {
+ OnStreamPaused(msg.routing_id(), stream_id);
+ } else if (params.state == ViewMsg_AudioStreamState_Params::kError) {
+ OnStreamError(msg.routing_id(), stream_id);
+ } else {
+ FAIL() << "Unknown stream state";
+ }
+ }
+
+ void OnStreamVolume(const IPC::Message& msg, int stream_id, double volume) {
+ OnStreamVolume(msg.routing_id(), stream_id, volume);
+ }
+
+ scoped_ptr<base::SharedMemory> shared_memory_;
+ scoped_ptr<base::SyncSocket> sync_socket_;
+ uint32 shared_memory_length_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockAudioRendererHost);
+};
+
+ACTION_P(QuitMessageLoop, message_loop) {
+ message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask());
+}
+
+class AudioRendererHostTest : public testing::Test {
+ public:
+ AudioRendererHostTest()
+ : mock_stream_(true) {
+ }
+
+ protected:
+ virtual void SetUp() {
+ // Create a message loop so AudioRendererHost can use it.
+ message_loop_.reset(new MessageLoop(MessageLoop::TYPE_IO));
+ io_thread_.reset(new BrowserThread(BrowserThread::IO, message_loop_.get()));
+ host_ = new MockAudioRendererHost();
+
+ // Simulate IPC channel connected.
+ host_->OnChannelConnected(base::GetCurrentProcId());
+ }
+
+ virtual void TearDown() {
+ // Simulate closing the IPC channel.
+ host_->OnChannelClosing();
+
+ // Release the reference to the mock object. The object will be destructed
+ // on message_loop_.
+ host_ = NULL;
+
+ // We need to continue running message_loop_ to complete all destructions.
+ SyncWithAudioThread();
+
+ io_thread_.reset();
+ }
+
+ void Create() {
+ InSequence s;
+ // 1. We will first receive a OnStreamCreated() signal.
+ EXPECT_CALL(*host_,
+ OnStreamCreated(kRouteId, kStreamId, _));
+
+ // 2. First packet request will arrive.
+ EXPECT_CALL(*host_, OnRequestPacket(kRouteId, kStreamId, _))
+ .WillOnce(QuitMessageLoop(message_loop_.get()));
+
+ IPC::Message msg;
+ msg.set_routing_id(kRouteId);
+
+ ViewHostMsg_Audio_CreateStream_Params params;
+ if (mock_stream_)
+ params.params.format = AudioParameters::AUDIO_MOCK;
+ else
+ params.params.format = AudioParameters::AUDIO_PCM_LINEAR;
+ params.params.channels = 2;
+ params.params.sample_rate = AudioParameters::kAudioCDSampleRate;
+ params.params.bits_per_sample = 16;
+ params.params.samples_per_packet = 0;
+
+ // Send a create stream message to the audio output stream and wait until
+ // we receive the created message.
+ host_->OnCreateStream(msg, kStreamId, params, false);
+ message_loop_->Run();
+ }
+
+ void CreateLowLatency() {
+ InSequence s;
+ // We will first receive a OnLowLatencyStreamCreated() signal.
+ EXPECT_CALL(*host_,
+ OnLowLatencyStreamCreated(kRouteId, kStreamId, _))
+ .WillOnce(QuitMessageLoop(message_loop_.get()));
+
+ IPC::Message msg;
+ msg.set_routing_id(kRouteId);
+
+ ViewHostMsg_Audio_CreateStream_Params params;
+ if (mock_stream_)
+ params.params.format = AudioParameters::AUDIO_MOCK;
+ else
+ params.params.format = AudioParameters::AUDIO_PCM_LINEAR;
+ params.params.channels = 2;
+ params.params.sample_rate = AudioParameters::kAudioCDSampleRate;
+ params.params.bits_per_sample = 16;
+ params.params.samples_per_packet = 0;
+
+ // Send a create stream message to the audio output stream and wait until
+ // we receive the created message.
+ host_->OnCreateStream(msg, kStreamId, params, true);
+ message_loop_->Run();
+ }
+
+ void Close() {
+ // Send a message to AudioRendererHost to tell it we want to close the
+ // stream.
+ IPC::Message msg;
+ msg.set_routing_id(kRouteId);
+ host_->OnCloseStream(msg, kStreamId);
+ message_loop_->RunAllPending();
+ }
+
+ void Play() {
+ EXPECT_CALL(*host_, OnStreamPlaying(kRouteId, kStreamId))
+ .WillOnce(QuitMessageLoop(message_loop_.get()));
+
+ IPC::Message msg;
+ msg.set_routing_id(kRouteId);
+ host_->OnPlayStream(msg, kStreamId);
+ message_loop_->Run();
+ }
+
+ void Pause() {
+ EXPECT_CALL(*host_, OnStreamPaused(kRouteId, kStreamId))
+ .WillOnce(QuitMessageLoop(message_loop_.get()));
+
+ IPC::Message msg;
+ msg.set_routing_id(kRouteId);
+ host_->OnPauseStream(msg, kStreamId);
+ message_loop_->Run();
+ }
+
+ void SetVolume(double volume) {
+ IPC::Message msg;
+ msg.set_routing_id(kRouteId);
+ host_->OnSetVolume(msg, kStreamId, volume);
+ message_loop_->RunAllPending();
+ }
+
+ void NotifyPacketReady() {
+ EXPECT_CALL(*host_, OnRequestPacket(kRouteId, kStreamId, _))
+ .WillOnce(QuitMessageLoop(message_loop_.get()));
+
+ IPC::Message msg;
+ msg.set_routing_id(kRouteId);
+ memset(host_->shared_memory()->memory(), 0, host_->shared_memory_length());
+ host_->OnNotifyPacketReady(msg, kStreamId,
+ host_->shared_memory_length());
+ message_loop_->Run();
+ }
+
+ void SimulateError() {
+ // Find the first AudioOutputController in the AudioRendererHost.
+ CHECK(host_->audio_entries_.size())
+ << "Calls Create() before calling this method";
+ media::AudioOutputController* controller =
+ host_->audio_entries_.begin()->second->controller;
+ CHECK(controller) << "AudioOutputController not found";
+
+ // Expect an error signal sent through IPC.
+ EXPECT_CALL(*host_, OnStreamError(kRouteId, kStreamId));
+
+ // Simulate an error sent from the audio device.
+ host_->OnError(controller, 0);
+ SyncWithAudioThread();
+
+ // Expect the audio stream record is removed.
+ EXPECT_EQ(0u, host_->audio_entries_.size());
+ }
+
+ // Called on the audio thread.
+ static void PostQuitMessageLoop(MessageLoop* message_loop) {
+ message_loop->PostTask(FROM_HERE, new MessageLoop::QuitTask());
+ }
+
+ // Called on the main thread.
+ static void PostQuitOnAudioThread(MessageLoop* message_loop) {
+ AudioManager::GetAudioManager()->GetMessageLoop()->PostTask(
+ FROM_HERE, NewRunnableFunction(&PostQuitMessageLoop, message_loop));
+ }
+
+ // SyncWithAudioThread() waits until all pending tasks on the audio thread
+ // are executed while also processing pending task in message_loop_ on the
+ // current thread. It is used to synchronize with the audio thread when we are
+ // closing an audio stream.
+ void SyncWithAudioThread() {
+ message_loop_->PostTask(
+ FROM_HERE, NewRunnableFunction(&PostQuitOnAudioThread,
+ message_loop_.get()));
+ message_loop_->Run();
+ }
+
+ MessageLoop* message_loop() { return message_loop_.get(); }
+ MockAudioRendererHost* host() { return host_; }
+ void EnableRealDevice() { mock_stream_ = false; }
+
+ private:
+ bool mock_stream_;
+ scoped_refptr<MockAudioRendererHost> host_;
+ scoped_ptr<MessageLoop> message_loop_;
+ scoped_ptr<BrowserThread> io_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioRendererHostTest);
+};
+
+TEST_F(AudioRendererHostTest, CreateAndClose) {
+ if (!IsRunningHeadless())
+ EnableRealDevice();
+
+ Create();
+ Close();
+}
+
+TEST_F(AudioRendererHostTest, CreatePlayAndClose) {
+ if (!IsRunningHeadless())
+ EnableRealDevice();
+
+ Create();
+ Play();
+ Close();
+}
+
+TEST_F(AudioRendererHostTest, CreatePlayPauseAndClose) {
+ if (!IsRunningHeadless())
+ EnableRealDevice();
+
+ Create();
+ Play();
+ Pause();
+ Close();
+}
+
+TEST_F(AudioRendererHostTest, SetVolume) {
+ if (!IsRunningHeadless())
+ EnableRealDevice();
+
+ Create();
+ SetVolume(0.5);
+ Play();
+ Pause();
+ Close();
+
+ // Expect the volume is set.
+ if (IsRunningHeadless()) {
+ EXPECT_EQ(0.5, FakeAudioOutputStream::GetLastFakeStream()->volume());
+ }
+}
+
+// Simulate the case where a stream is not properly closed.
+TEST_F(AudioRendererHostTest, CreateAndShutdown) {
+ if (!IsRunningHeadless())
+ EnableRealDevice();
+
+ Create();
+}
+
+// Simulate the case where a stream is not properly closed.
+TEST_F(AudioRendererHostTest, CreatePlayAndShutdown) {
+ if (!IsRunningHeadless())
+ EnableRealDevice();
+
+ Create();
+ Play();
+}
+
+// Simulate the case where a stream is not properly closed.
+TEST_F(AudioRendererHostTest, CreatePlayPauseAndShutdown) {
+ if (!IsRunningHeadless())
+ EnableRealDevice();
+
+ Create();
+ Play();
+ Pause();
+}
+
+TEST_F(AudioRendererHostTest, DataConversationMockStream) {
+ Create();
+
+ // Note that we only do notify three times because the buffer capacity is
+ // triple of one packet size.
+ NotifyPacketReady();
+ NotifyPacketReady();
+ NotifyPacketReady();
+ Close();
+}
+
+TEST_F(AudioRendererHostTest, DataConversationRealStream) {
+ if (IsRunningHeadless())
+ return;
+ EnableRealDevice();
+ Create();
+ Play();
+
+ // If this is a real audio device, the data conversation is not limited
+ // to the buffer capacity of AudioOutputController. So we do 5 exchanges
+ // before we close the device.
+ for (int i = 0; i < 5; ++i) {
+ NotifyPacketReady();
+ }
+ Close();
+}
+
+TEST_F(AudioRendererHostTest, SimulateError) {
+ if (!IsRunningHeadless())
+ EnableRealDevice();
+
+ Create();
+ Play();
+ SimulateError();
+}
+
+// Simulate the case when an error is generated on the browser process,
+// the audio device is closed but the render process try to close the
+// audio stream again.
+TEST_F(AudioRendererHostTest, SimulateErrorAndClose) {
+ if (!IsRunningHeadless())
+ EnableRealDevice();
+
+ Create();
+ Play();
+ SimulateError();
+ Close();
+}
+
+TEST_F(AudioRendererHostTest, CreateLowLatencyAndClose) {
+ if (!IsRunningHeadless())
+ EnableRealDevice();
+
+ CreateLowLatency();
+ Close();
+}
+
+// Simulate the case where a stream is not properly closed.
+TEST_F(AudioRendererHostTest, CreateLowLatencyAndShutdown) {
+ if (!IsRunningHeadless())
+ EnableRealDevice();
+
+ CreateLowLatency();
+}
+
+// TODO(hclam): Add tests for data conversation in low latency mode.
diff --git a/content/browser/renderer_host/audio_sync_reader.cc b/content/browser/renderer_host/audio_sync_reader.cc
new file mode 100644
index 0000000..261bec0
--- /dev/null
+++ b/content/browser/renderer_host/audio_sync_reader.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/audio_sync_reader.h"
+
+#include "base/process_util.h"
+#include "base/shared_memory.h"
+
+AudioSyncReader::AudioSyncReader(base::SharedMemory* shared_memory)
+ : shared_memory_(shared_memory) {
+}
+
+AudioSyncReader::~AudioSyncReader() {
+}
+
+// media::AudioOutputController::SyncReader implementations.
+void AudioSyncReader::UpdatePendingBytes(uint32 bytes) {
+ socket_->Send(&bytes, sizeof(bytes));
+}
+
+uint32 AudioSyncReader::Read(void* data, uint32 size) {
+ uint32 read_size = std::min(size, shared_memory_->created_size());
+ memcpy(data, shared_memory_->memory(), read_size);
+ memset(shared_memory_->memory(), 0, shared_memory_->created_size());
+ return read_size;
+}
+
+void AudioSyncReader::Close() {
+ socket_->Close();
+}
+
+bool AudioSyncReader::Init() {
+ base::SyncSocket* sockets[2] = {0};
+ if (base::SyncSocket::CreatePair(sockets)) {
+ socket_.reset(sockets[0]);
+ foreign_socket_.reset(sockets[1]);
+ return true;
+ }
+ return false;
+}
+
+#if defined(OS_WIN)
+bool AudioSyncReader::PrepareForeignSocketHandle(
+ base::ProcessHandle process_handle,
+ base::SyncSocket::Handle* foreign_handle) {
+ ::DuplicateHandle(GetCurrentProcess(), foreign_socket_->handle(),
+ process_handle, foreign_handle,
+ 0, FALSE, DUPLICATE_SAME_ACCESS);
+ if (*foreign_handle != 0)
+ return true;
+ return false;
+}
+#else
+bool AudioSyncReader::PrepareForeignSocketHandle(
+ base::ProcessHandle process_handle,
+ base::FileDescriptor* foreign_handle) {
+ foreign_handle->fd = foreign_socket_->handle();
+ foreign_handle->auto_close = false;
+ if (foreign_handle->fd != -1)
+ return true;
+ return false;
+}
+#endif
diff --git a/content/browser/renderer_host/audio_sync_reader.h b/content/browser/renderer_host/audio_sync_reader.h
new file mode 100644
index 0000000..054bb52
--- /dev/null
+++ b/content/browser/renderer_host/audio_sync_reader.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_AUDIO_SYNC_READER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_AUDIO_SYNC_READER_H_
+#pragma once
+
+#include "base/file_descriptor_posix.h"
+#include "base/process.h"
+#include "base/sync_socket.h"
+#include "media/audio/audio_output_controller.h"
+
+namespace base {
+
+class SharedMemory;
+
+}
+
+// A AudioOutputController::SyncReader implementation using SyncSocket. This
+// is used by AudioOutputController to provide a low latency data source for
+// transmitting audio packets between the browser process and the renderer
+// process.
+class AudioSyncReader : public media::AudioOutputController::SyncReader {
+ public:
+ explicit AudioSyncReader(base::SharedMemory* shared_memory);
+
+ virtual ~AudioSyncReader();
+
+ // media::AudioOutputController::SyncReader implementations.
+ virtual void UpdatePendingBytes(uint32 bytes);
+ virtual uint32 Read(void* data, uint32 size);
+ virtual void Close();
+
+ bool Init();
+ bool PrepareForeignSocketHandle(base::ProcessHandle process_handle,
+#if defined(OS_WIN)
+ base::SyncSocket::Handle* foreign_handle);
+#else
+ base::FileDescriptor* foreign_handle);
+#endif
+
+ private:
+ base::SharedMemory* shared_memory_;
+
+ // A pair of SyncSocket for transmitting audio data.
+ scoped_ptr<base::SyncSocket> socket_;
+
+ // SyncSocket to be used by the renderer. The reference is released after
+ // PrepareForeignSocketHandle() is called and ran successfully.
+ scoped_ptr<base::SyncSocket> foreign_socket_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioSyncReader);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_AUDIO_SYNC_READER_H_
diff --git a/content/browser/renderer_host/backing_store.cc b/content/browser/renderer_host/backing_store.cc
new file mode 100644
index 0000000..2c6edfc
--- /dev/null
+++ b/content/browser/renderer_host/backing_store.cc
@@ -0,0 +1,17 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/backing_store.h"
+
+BackingStore::BackingStore(RenderWidgetHost* widget, const gfx::Size& size)
+ : render_widget_host_(widget),
+ size_(size) {
+}
+
+BackingStore::~BackingStore() {
+}
+
+size_t BackingStore::MemorySize() {
+ return size_.GetArea() * 4;
+}
diff --git a/content/browser/renderer_host/backing_store.h b/content/browser/renderer_host/backing_store.h
new file mode 100644
index 0000000..5043b3e
--- /dev/null
+++ b/content/browser/renderer_host/backing_store.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_H_
+#define CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_H_
+#pragma once
+
+#include <vector>
+
+#include "app/surface/transport_dib.h"
+#include "base/basictypes.h"
+#include "ui/gfx/size.h"
+
+class RenderProcessHost;
+class RenderWidgetHost;
+
+namespace gfx {
+class Rect;
+}
+
+namespace skia {
+class PlatformCanvas;
+}
+
+// Represents a backing store for the pixels in a RenderWidgetHost.
+class BackingStore {
+ public:
+ virtual ~BackingStore();
+
+ RenderWidgetHost* render_widget_host() const { return render_widget_host_; }
+ const gfx::Size& size() { return size_; }
+
+ // The number of bytes that this backing store consumes. The default
+ // implementation just assumes there's 32 bits per pixel over the current
+ // size of the screen. Implementations may override this if they have more
+ // information about the color depth.
+ virtual size_t MemorySize();
+
+ // Paints the bitmap from the renderer onto the backing store. bitmap_rect
+ // gives the location of bitmap, and copy_rects specifies the subregion(s) of
+ // the backingstore to be painted from the bitmap.
+ virtual void PaintToBackingStore(
+ RenderProcessHost* process,
+ TransportDIB::Id bitmap,
+ const gfx::Rect& bitmap_rect,
+ const std::vector<gfx::Rect>& copy_rects) = 0;
+
+ // Extracts the gives subset of the backing store and copies it to the given
+ // PlatformCanvas. The PlatformCanvas should not be initialized. This function
+ // will call initialize() with the correct size. The return value indicates
+ // success.
+ virtual bool CopyFromBackingStore(const gfx::Rect& rect,
+ skia::PlatformCanvas* output) = 0;
+
+ // Scrolls the contents of clip_rect in the backing store by dx or dy (but dx
+ // and dy cannot both be non-zero).
+ virtual void ScrollBackingStore(int dx, int dy,
+ const gfx::Rect& clip_rect,
+ const gfx::Size& view_size) = 0;
+ protected:
+ // Can only be constructed via subclasses.
+ BackingStore(RenderWidgetHost* widget, const gfx::Size& size);
+
+ private:
+ // The owner of this backing store.
+ RenderWidgetHost* render_widget_host_;
+
+ // The size of the backing store.
+ gfx::Size size_;
+
+ DISALLOW_COPY_AND_ASSIGN(BackingStore);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_H_
diff --git a/content/browser/renderer_host/backing_store_mac.h b/content/browser/renderer_host/backing_store_mac.h
new file mode 100644
index 0000000..b9ac17f
--- /dev/null
+++ b/content/browser/renderer_host/backing_store_mac.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_MAC_H_
+#define CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_MAC_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "content/browser/renderer_host/backing_store.h"
+
+class BackingStoreMac : public BackingStore {
+ public:
+ BackingStoreMac(RenderWidgetHost* widget, const gfx::Size& size);
+ virtual ~BackingStoreMac();
+
+ // A CGLayer that stores the contents of the backing store, cached in GPU
+ // memory if possible.
+ CGLayerRef cg_layer() { return cg_layer_; }
+
+ // A CGBitmapContext that stores the contents of the backing store if the
+ // corresponding Cocoa view has not been inserted into an NSWindow yet.
+ CGContextRef cg_bitmap() { return cg_bitmap_; }
+
+ // BackingStore implementation.
+ virtual void PaintToBackingStore(
+ RenderProcessHost* process,
+ TransportDIB::Id bitmap,
+ const gfx::Rect& bitmap_rect,
+ const std::vector<gfx::Rect>& copy_rects);
+ virtual bool CopyFromBackingStore(const gfx::Rect& rect,
+ skia::PlatformCanvas* output);
+ virtual void ScrollBackingStore(int dx, int dy,
+ const gfx::Rect& clip_rect,
+ const gfx::Size& view_size);
+
+ private:
+ // Creates a CGLayer associated with its owner view's window's graphics
+ // context, sized properly for the backing store. Returns NULL if the owner
+ // is not in a window with a CGContext. cg_layer_ is assigned this method's
+ // result.
+ CGLayerRef CreateCGLayer();
+
+ // Creates a CGBitmapContext sized properly for the backing store. The
+ // owner view need not be in a window. cg_bitmap_ is assigned this method's
+ // result.
+ CGContextRef CreateCGBitmapContext();
+
+ base::mac::ScopedCFTypeRef<CGContextRef> cg_bitmap_;
+ base::mac::ScopedCFTypeRef<CGLayerRef> cg_layer_;
+
+ DISALLOW_COPY_AND_ASSIGN(BackingStoreMac);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_MAC_H_
diff --git a/content/browser/renderer_host/backing_store_mac.mm b/content/browser/renderer_host/backing_store_mac.mm
new file mode 100644
index 0000000..424a7b8
--- /dev/null
+++ b/content/browser/renderer_host/backing_store_mac.mm
@@ -0,0 +1,242 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <Cocoa/Cocoa.h>
+
+#include "content/browser/renderer_host/backing_store_mac.h"
+
+#include "app/surface/transport_dib.h"
+#include "base/logging.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/sys_info.h"
+#include "content/browser/renderer_host/render_process_host.h"
+#include "content/browser/renderer_host/render_widget_host.h"
+#include "content/browser/renderer_host/render_widget_host_view.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/gfx/rect.h"
+
+// Mac Backing Stores:
+//
+// Since backing stores are only ever written to or drawn into windows, we keep
+// our backing store in a CGLayer that can get cached in GPU memory. This
+// allows acclerated drawing into the layer and lets scrolling and such happen
+// all or mostly on the GPU, which is good for performance.
+
+namespace {
+
+// Returns whether this version of OS X has broken CGLayers, see
+// http://crbug.com/45553 , comments 5 and 6.
+bool NeedsLayerWorkaround() {
+ int32 os_major, os_minor, os_bugfix;
+ base::SysInfo::OperatingSystemVersionNumbers(
+ &os_major, &os_minor, &os_bugfix);
+ return os_major == 10 && os_minor == 5;
+}
+
+} // namespace
+
+BackingStoreMac::BackingStoreMac(RenderWidgetHost* widget,
+ const gfx::Size& size)
+ : BackingStore(widget, size) {
+ cg_layer_.reset(CreateCGLayer());
+ if (!cg_layer_) {
+ // The view isn't in a window yet. Use a CGBitmapContext for now.
+ cg_bitmap_.reset(CreateCGBitmapContext());
+ }
+}
+
+BackingStoreMac::~BackingStoreMac() {
+}
+
+void BackingStoreMac::PaintToBackingStore(
+ RenderProcessHost* process,
+ TransportDIB::Id bitmap,
+ const gfx::Rect& bitmap_rect,
+ const std::vector<gfx::Rect>& copy_rects) {
+ DCHECK_NE(static_cast<bool>(cg_layer()), static_cast<bool>(cg_bitmap()));
+
+ TransportDIB* dib = process->GetTransportDIB(bitmap);
+ if (!dib)
+ return;
+
+ base::mac::ScopedCFTypeRef<CGDataProviderRef> data_provider(
+ CGDataProviderCreateWithData(NULL, dib->memory(),
+ bitmap_rect.width() * bitmap_rect.height() * 4, NULL));
+
+ base::mac::ScopedCFTypeRef<CGImageRef> bitmap_image(
+ CGImageCreate(bitmap_rect.width(), bitmap_rect.height(), 8, 32,
+ 4 * bitmap_rect.width(), base::mac::GetSystemColorSpace(),
+ kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
+ data_provider, NULL, false, kCGRenderingIntentDefault));
+
+ for (size_t i = 0; i < copy_rects.size(); i++) {
+ const gfx::Rect& copy_rect = copy_rects[i];
+
+ // Only the subpixels given by copy_rect have pixels to copy.
+ base::mac::ScopedCFTypeRef<CGImageRef> image(
+ CGImageCreateWithImageInRect(bitmap_image, CGRectMake(
+ copy_rect.x() - bitmap_rect.x(),
+ copy_rect.y() - bitmap_rect.y(),
+ copy_rect.width(),
+ copy_rect.height())));
+
+ if (!cg_layer()) {
+ // The view may have moved to a window. Try to get a CGLayer.
+ cg_layer_.reset(CreateCGLayer());
+ if (cg_layer()) {
+ // now that we have a layer, copy the cached image into it
+ base::mac::ScopedCFTypeRef<CGImageRef> bitmap_image(
+ CGBitmapContextCreateImage(cg_bitmap_));
+ CGContextDrawImage(CGLayerGetContext(cg_layer()),
+ CGRectMake(0, 0, size().width(), size().height()),
+ bitmap_image);
+ // Discard the cache bitmap, since we no longer need it.
+ cg_bitmap_.reset(NULL);
+ }
+ }
+
+ DCHECK_NE(static_cast<bool>(cg_layer()), static_cast<bool>(cg_bitmap()));
+
+ if (cg_layer()) {
+ // The CGLayer's origin is in the lower left, but flipping the CTM would
+ // cause the image to get drawn upside down. So we move the rectangle
+ // to the right position before drawing the image.
+ CGContextRef layer = CGLayerGetContext(cg_layer());
+ gfx::Rect paint_rect = copy_rect;
+ paint_rect.set_y(size().height() - copy_rect.bottom());
+ CGContextDrawImage(layer, paint_rect.ToCGRect(), image);
+ } else {
+ // The layer hasn't been created yet, so draw into the cache bitmap.
+ gfx::Rect paint_rect = copy_rect;
+ paint_rect.set_y(size().height() - copy_rect.bottom());
+ CGContextDrawImage(cg_bitmap_, paint_rect.ToCGRect(), image);
+ }
+ }
+}
+
+bool BackingStoreMac::CopyFromBackingStore(const gfx::Rect& rect,
+ skia::PlatformCanvas* output) {
+ if (!output->initialize(rect.width(), rect.height(), true))
+ return false;
+
+ CGContextRef temp_context = output->beginPlatformPaint();
+ CGContextSaveGState(temp_context);
+ CGContextTranslateCTM(temp_context, 0.0, size().height());
+ CGContextScaleCTM(temp_context, 1.0, -1.0);
+ CGContextDrawLayerAtPoint(temp_context, CGPointMake(rect.x(), rect.y()),
+ cg_layer());
+ CGContextRestoreGState(temp_context);
+ output->endPlatformPaint();
+ return true;
+}
+
+// Scroll the contents of our CGLayer
+void BackingStoreMac::ScrollBackingStore(int dx, int dy,
+ const gfx::Rect& clip_rect,
+ const gfx::Size& view_size) {
+ DCHECK_NE(static_cast<bool>(cg_layer()), static_cast<bool>(cg_bitmap()));
+
+ // "Scroll" the contents of the layer by creating a new CGLayer,
+ // copying the contents of the old one into the new one offset by the scroll
+ // amount, swapping in the new CGLayer, and then painting in the new data.
+ //
+ // The Windows code always sets the whole backing store as the source of the
+ // scroll. Thus, we only have to worry about pixels which will end up inside
+ // the clipping rectangle. (Note that the clipping rectangle is not
+ // translated by the scroll.)
+
+ // We assume |clip_rect| is contained within the backing store.
+ DCHECK(clip_rect.bottom() <= size().height());
+ DCHECK(clip_rect.right() <= size().width());
+
+ if ((dx || dy) && abs(dx) < size().width() && abs(dy) < size().height()) {
+ if (cg_layer()) {
+ // See http://crbug.com/45553 , comments 5 and 6.
+ static bool needs_layer_workaround = NeedsLayerWorkaround();
+
+ base::mac::ScopedCFTypeRef<CGLayerRef> new_layer;
+ CGContextRef layer;
+
+ if (needs_layer_workaround) {
+ new_layer.reset(CreateCGLayer());
+ // If the current view is in a window, the replacement must be too.
+ DCHECK(new_layer);
+
+ layer = CGLayerGetContext(new_layer);
+ CGContextDrawLayerAtPoint(layer, CGPointMake(0, 0), cg_layer());
+ } else {
+ layer = CGLayerGetContext(cg_layer());
+ }
+
+ CGContextSaveGState(layer);
+ CGContextClipToRect(layer,
+ CGRectMake(clip_rect.x(),
+ size().height() - clip_rect.bottom(),
+ clip_rect.width(),
+ clip_rect.height()));
+ CGContextDrawLayerAtPoint(layer, CGPointMake(dx, -dy), cg_layer());
+ CGContextRestoreGState(layer);
+
+ if (needs_layer_workaround)
+ cg_layer_.swap(new_layer);
+ } else {
+ // We don't have a layer, so scroll the contents of the CGBitmapContext.
+ base::mac::ScopedCFTypeRef<CGImageRef> bitmap_image(
+ CGBitmapContextCreateImage(cg_bitmap_));
+ CGContextSaveGState(cg_bitmap_);
+ CGContextClipToRect(cg_bitmap_,
+ CGRectMake(clip_rect.x(),
+ size().height() - clip_rect.bottom(),
+ clip_rect.width(),
+ clip_rect.height()));
+ CGContextDrawImage(cg_bitmap_,
+ CGRectMake(dx, -dy, size().width(), size().height()),
+ bitmap_image);
+ CGContextRestoreGState(cg_bitmap_);
+ }
+ }
+}
+
+CGLayerRef BackingStoreMac::CreateCGLayer() {
+ // The CGLayer should be optimized for drawing into the containing window,
+ // so extract a CGContext corresponding to the window to be passed to
+ // CGLayerCreateWithContext.
+ NSWindow* window = [render_widget_host()->view()->GetNativeView() window];
+ if ([window windowNumber] <= 0) {
+ // This catches a nil |window|, as well as windows that exist but that
+ // aren't yet connected to WindowServer.
+ return NULL;
+ }
+
+ NSGraphicsContext* ns_context = [window graphicsContext];
+ DCHECK(ns_context);
+
+ CGContextRef cg_context = static_cast<CGContextRef>(
+ [ns_context graphicsPort]);
+ DCHECK(cg_context);
+
+ CGLayerRef layer = CGLayerCreateWithContext(cg_context,
+ size().ToCGSize(),
+ NULL);
+ DCHECK(layer);
+
+ return layer;
+}
+
+CGContextRef BackingStoreMac::CreateCGBitmapContext() {
+ // A CGBitmapContext serves as a stand-in for the layer before the view is
+ // in a containing window.
+ CGContextRef context = CGBitmapContextCreate(NULL,
+ size().width(), size().height(),
+ 8, size().width() * 4,
+ base::mac::GetSystemColorSpace(),
+ kCGImageAlphaPremultipliedFirst |
+ kCGBitmapByteOrder32Host);
+ DCHECK(context);
+
+ return context;
+}
diff --git a/content/browser/renderer_host/backing_store_manager.cc b/content/browser/renderer_host/backing_store_manager.cc
new file mode 100644
index 0000000..bd50021
--- /dev/null
+++ b/content/browser/renderer_host/backing_store_manager.cc
@@ -0,0 +1,287 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/backing_store_manager.h"
+
+#include "base/sys_info.h"
+#include "base/command_line.h"
+#include "chrome/common/chrome_switches.h"
+#include "content/browser/renderer_host/backing_store.h"
+#include "content/browser/renderer_host/render_widget_host.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/mru_cache.h"
+#include "chrome/common/notification_service.h"
+
+namespace {
+
+// There are two separate caches, |large_cache| and |small_cache|. large_cache
+// is meant for large items (tabs, popup windows), while small_cache is meant
+// for small items (extension toolstrips and buttons, etc.). The idea is that
+// we'll almost always try to evict from large_cache first since small_cache
+// items will tend to be visible more of the time.
+typedef OwningMRUCache<RenderWidgetHost*, BackingStore*> BackingStoreCache;
+static BackingStoreCache* large_cache = NULL;
+static BackingStoreCache* small_cache = NULL;
+
+// Threshold is based on a single large-monitor-width toolstrip.
+// (32bpp, 32 pixels high, 1920 pixels wide)
+// TODO(erikkay) 32bpp assumption isn't great.
+const size_t kSmallThreshold = 4 * 32 * 1920;
+
+// Pick a large monitor size to use as a multiplier. This is multiplied by the
+// max number of large backing stores (usually tabs) to pick a ceiling on the
+// max memory to use.
+// TODO(erikkay) Perhaps we should actually use monitor size? That way we
+// could make an assertion like "worse case, there are two tabs in the cache".
+// However, the small_cache might mess up these calculations a bit.
+// TODO(erikkay) 32bpp assumption isn't great.
+const size_t kMemoryMultiplier = 4 * 1920 * 1200; // ~9MB
+
+// The maximum number of large BackingStoreCache objects (tabs) to use.
+// Use a minimum of 2, and add one for each 256MB of physical memory you have.
+// Cap at 5, the thinking being that even if you have a gigantic amount of
+// RAM, there's a limit to how much caching helps beyond a certain number
+// of tabs. If users *really* want unlimited stores, allow it via the
+// --disable-backing-store-limit flag.
+static size_t MaxNumberOfBackingStores() {
+ static bool unlimited = false;
+ const CommandLine& command = *CommandLine::ForCurrentProcess();
+ unlimited = command.HasSwitch(switches::kDisableBackingStoreLimit);
+
+
+ if (unlimited) {
+ // 100 isn't truly unlimited, but given that backing stores count against
+ // GDI memory, it's well past any reasonable number. Many systems will
+ // begin to fail in strange ways well before they hit 100 stores.
+ return 100;
+ } else {
+ return std::min(5, 2 + (base::SysInfo::AmountOfPhysicalMemoryMB() / 256));
+ }
+}
+
+// The maximum about of memory to use for all BackingStoreCache object combined.
+static size_t MaxBackingStoreMemory() {
+ // Compute in terms of the number of large monitor's worth of backing-store.
+ return MaxNumberOfBackingStores() * kMemoryMultiplier;
+}
+
+// Expires the given |backing_store| from |cache|.
+void ExpireBackingStoreAt(BackingStoreCache* cache,
+ BackingStoreCache::iterator backing_store) {
+ NotificationService::current()->Notify(
+ NotificationType::RENDER_WIDGET_HOST_WILL_DESTROY_BACKING_STORE,
+ Source<RenderWidgetHost>(backing_store->first),
+ Details<BackingStore>(backing_store->second));
+ cache->Erase(backing_store);
+}
+
+size_t ExpireLastBackingStore(BackingStoreCache* cache) {
+ if (cache->size() < 1)
+ return 0;
+
+ // Crazy C++ alert: rbegin.base() is a forward iterator pointing to end(),
+ // so we need to do -- to move one back to the actual last item.
+ BackingStoreCache::iterator entry = --cache->rbegin().base();
+ size_t entry_size = entry->second->MemorySize();
+ ExpireBackingStoreAt(cache, entry);
+ return entry_size;
+}
+
+void CreateCacheSpace(size_t size) {
+ // Given a request for |size|, first free from the large cache (until there's
+ // only one item left) and then do the same from the small cache if we still
+ // don't have enough.
+ while (size > 0 && (large_cache->size() > 1 || small_cache->size() > 1)) {
+ BackingStoreCache* cache =
+ (large_cache->size() > 1) ? large_cache : small_cache;
+ while (size > 0 && cache->size() > 1) {
+ size_t entry_size = ExpireLastBackingStore(cache);
+ if (size > entry_size)
+ size -= entry_size;
+ else
+ size = 0;
+ }
+ }
+ DCHECK(size == 0);
+}
+
+// Creates the backing store for the host based on the dimensions passed in.
+// Removes the existing backing store if there is one.
+BackingStore* CreateBackingStore(RenderWidgetHost* host,
+ const gfx::Size& backing_store_size) {
+ // Remove any existing backing store in case we're replacing it.
+ BackingStoreManager::RemoveBackingStore(host);
+
+ if (!large_cache) {
+ large_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT);
+ small_cache = new BackingStoreCache(BackingStoreCache::NO_AUTO_EVICT);
+ }
+
+ // TODO(erikkay) 32bpp is not always accurate
+ size_t new_mem = backing_store_size.GetArea() * 4;
+ size_t current_mem = BackingStoreManager::MemorySize();
+ size_t max_mem = MaxBackingStoreMemory();
+ DCHECK(new_mem < max_mem);
+ if (current_mem + new_mem > max_mem) {
+ // Need to remove old backing stores to make room for the new one. We
+ // don't want to do this when the backing store is being replace by a new
+ // one for the same tab, but this case won't get called then: we'll have
+ // removed the old one in the RemoveBackingStore above, and the cache
+ // won't be over-sized.
+ CreateCacheSpace((current_mem + new_mem) - max_mem);
+ }
+ DCHECK((BackingStoreManager::MemorySize() + new_mem) <= max_mem);
+
+ BackingStoreCache* cache;
+ if (new_mem > kSmallThreshold) {
+ // Limit the number of large backing stores (tabs) to the memory tier number
+ // (between 2-5). While we allow a larger amount of memory for people who
+ // have large windows, this means that those who use small browser windows
+ // won't ever cache more than 5 tabs, so they pay a smaller memory cost.
+ if (large_cache->size() >= MaxNumberOfBackingStores())
+ ExpireLastBackingStore(large_cache);
+ cache = large_cache;
+ } else {
+ cache = small_cache;
+ }
+ BackingStore* backing_store = host->AllocBackingStore(backing_store_size);
+ if (backing_store)
+ cache->Put(host, backing_store);
+ return backing_store;
+}
+
+int ComputeTotalArea(const std::vector<gfx::Rect>& rects) {
+ // We assume that the given rects are non-overlapping, which is a property of
+ // the paint rects generated by the PaintAggregator.
+#ifndef NDEBUG
+ for (size_t i = 0; i < rects.size(); ++i) {
+ for (size_t j = 0; j < rects.size(); ++j) {
+ if (i != j)
+ DCHECK(!rects[i].Intersects(rects[j]));
+ }
+ }
+#endif
+ int area = 0;
+ for (size_t i = 0; i < rects.size(); ++i)
+ area += rects[i].size().GetArea();
+ return area;
+}
+
+} // namespace
+
+// BackingStoreManager ---------------------------------------------------------
+
+// static
+BackingStore* BackingStoreManager::GetBackingStore(
+ RenderWidgetHost* host,
+ const gfx::Size& desired_size) {
+ BackingStore* backing_store = Lookup(host);
+ if (backing_store) {
+ // If we already have a backing store, then make sure it is the correct
+ // size.
+ if (backing_store->size() == desired_size)
+ return backing_store;
+ backing_store = NULL;
+ }
+
+ return backing_store;
+}
+
+// static
+void BackingStoreManager::PrepareBackingStore(
+ RenderWidgetHost* host,
+ const gfx::Size& backing_store_size,
+ TransportDIB::Id bitmap,
+ const gfx::Rect& bitmap_rect,
+ const std::vector<gfx::Rect>& copy_rects,
+ bool* needs_full_paint) {
+ BackingStore* backing_store = GetBackingStore(host, backing_store_size);
+ if (!backing_store) {
+ // We need to get Webkit to generate a new paint here, as we
+ // don't have a previous snapshot.
+ if (bitmap_rect.size() != backing_store_size ||
+ bitmap_rect.x() != 0 || bitmap_rect.y() != 0 ||
+ ComputeTotalArea(copy_rects) != backing_store_size.GetArea() ||
+ !(backing_store = CreateBackingStore(host, backing_store_size))) {
+ DCHECK(needs_full_paint != NULL);
+ *needs_full_paint = true;
+ // Makes no sense to paint the transport dib if we are going
+ // to request a full paint.
+ return;
+ }
+ }
+
+ backing_store->PaintToBackingStore(host->process(), bitmap,
+ bitmap_rect, copy_rects);
+}
+
+// static
+BackingStore* BackingStoreManager::Lookup(RenderWidgetHost* host) {
+ if (large_cache) {
+ BackingStoreCache::iterator it = large_cache->Get(host);
+ if (it != large_cache->end())
+ return it->second;
+
+ // This moves host to the front of the MRU.
+ it = small_cache->Get(host);
+ if (it != small_cache->end())
+ return it->second;
+ }
+ return NULL;
+}
+
+// static
+void BackingStoreManager::RemoveBackingStore(RenderWidgetHost* host) {
+ if (!large_cache)
+ return;
+
+ BackingStoreCache* cache = large_cache;
+ BackingStoreCache::iterator it = cache->Peek(host);
+ if (it == cache->end()) {
+ cache = small_cache;
+ it = cache->Peek(host);
+ if (it == cache->end())
+ return;
+ }
+ cache->Erase(it);
+}
+
+// static
+void BackingStoreManager::RemoveAllBackingStores() {
+ if (large_cache) {
+ large_cache->Clear();
+ small_cache->Clear();
+ }
+}
+
+// static
+bool BackingStoreManager::ExpireBackingStoreForTest(RenderWidgetHost* host) {
+ BackingStoreCache* cache = large_cache;
+
+ BackingStoreCache::iterator it = cache->Peek(host);
+ if (it == cache->end()) {
+ cache = small_cache;
+ it = cache->Peek(host);
+ if (it == cache->end())
+ return false;
+ }
+ ExpireBackingStoreAt(cache, it);
+ return true;
+}
+
+// static
+size_t BackingStoreManager::MemorySize() {
+ if (!large_cache)
+ return 0;
+
+ size_t mem = 0;
+ BackingStoreCache::iterator it;
+ for (it = large_cache->begin(); it != large_cache->end(); ++it)
+ mem += it->second->MemorySize();
+
+ for (it = small_cache->begin(); it != small_cache->end(); ++it)
+ mem += it->second->MemorySize();
+
+ return mem;
+}
diff --git a/content/browser/renderer_host/backing_store_manager.h b/content/browser/renderer_host/backing_store_manager.h
new file mode 100644
index 0000000..bc09fc7
--- /dev/null
+++ b/content/browser/renderer_host/backing_store_manager.h
@@ -0,0 +1,79 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_MANAGER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_MANAGER_H_
+#pragma once
+
+#include <vector>
+
+#include "app/surface/transport_dib.h"
+#include "base/basictypes.h"
+#include "base/process.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+
+class BackingStore;
+class RenderWidgetHost;
+
+// This class manages backing stores in the browsr. Every RenderWidgetHost is
+// associated with a backing store which it requests from this class. The
+// hosts don't maintain any references to the backing stores. These backing
+// stores are maintained in a cache which can be trimmed as needed.
+class BackingStoreManager {
+ public:
+ // Returns a backing store which matches the desired dimensions.
+ //
+ // backing_store_rect
+ // The desired backing store dimensions.
+ // Returns a pointer to the backing store on success, NULL on failure.
+ static BackingStore* GetBackingStore(RenderWidgetHost* host,
+ const gfx::Size& desired_size);
+
+ // Makes a backing store which is fully ready for consumption, i.e. the
+ // bitmap from the renderer has been copied into the backing store.
+ //
+ // backing_store_size
+ // The desired backing store dimensions.
+ // bitmap_section
+ // The bitmap section from the renderer.
+ // bitmap_rect
+ // The rect to be painted into the backing store
+ // needs_full_paint
+ // Set if we need to send out a request to paint the view
+ // to the renderer.
+ static void PrepareBackingStore(
+ RenderWidgetHost* host,
+ const gfx::Size& backing_store_size,
+ TransportDIB::Id bitmap,
+ const gfx::Rect& bitmap_rect,
+ const std::vector<gfx::Rect>& copy_rects,
+ bool* needs_full_paint);
+
+ // Returns a matching backing store for the host.
+ // Returns NULL if we fail to find one.
+ static BackingStore* Lookup(RenderWidgetHost* host);
+
+ // Removes the backing store for the host.
+ static void RemoveBackingStore(RenderWidgetHost* host);
+
+ // Removes all backing stores.
+ static void RemoveAllBackingStores();
+
+ // Expires the given backing store. This emulates something getting evicted
+ // from the cache for the purpose of testing. Returns true if the host was
+ // removed, false if it wasn't found.
+ static bool ExpireBackingStoreForTest(RenderWidgetHost* host);
+
+ // Current size in bytes of the backing store cache.
+ static size_t MemorySize();
+
+ private:
+ // Not intended for instantiation.
+ BackingStoreManager() {}
+
+ DISALLOW_COPY_AND_ASSIGN(BackingStoreManager);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_MANAGER_H_
diff --git a/content/browser/renderer_host/backing_store_skia.cc b/content/browser/renderer_host/backing_store_skia.cc
new file mode 100644
index 0000000..f756504
--- /dev/null
+++ b/content/browser/renderer_host/backing_store_skia.cc
@@ -0,0 +1,105 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/backing_store_skia.h"
+
+#include "content/browser/renderer_host/render_process_host.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/canvas_skia.h"
+#include "ui/gfx/rect.h"
+
+// Assume that somewhere along the line, someone will do width * height * 4
+// with signed numbers. If the maximum value is 2**31, then 2**31 / 4 =
+// 2**29 and floor(sqrt(2**29)) = 23170.
+
+// Max height and width for layers
+static const int kMaxVideoLayerSize = 23170;
+
+BackingStoreSkia::BackingStoreSkia(RenderWidgetHost* widget,
+ const gfx::Size& size)
+ : BackingStore(widget, size) {
+ bitmap_.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height());
+ bitmap_.allocPixels();
+ canvas_.reset(new SkCanvas(bitmap_));
+}
+
+BackingStoreSkia::~BackingStoreSkia() {
+}
+
+void BackingStoreSkia::SkiaShowRect(const gfx::Point& point,
+ gfx::Canvas* canvas) {
+ canvas->AsCanvasSkia()->drawBitmap(bitmap_,
+ SkIntToScalar(point.x()), SkIntToScalar(point.y()));
+}
+
+size_t BackingStoreSkia::MemorySize() {
+ // NOTE: The computation may be different when the canvas is a subrectangle of
+ // a larger bitmap.
+ return size().GetArea() * 4;
+}
+
+void BackingStoreSkia::PaintToBackingStore(
+ RenderProcessHost* process,
+ TransportDIB::Id bitmap,
+ const gfx::Rect& bitmap_rect,
+ const std::vector<gfx::Rect>& copy_rects) {
+ if (bitmap_rect.IsEmpty())
+ return;
+
+ const int width = bitmap_rect.width();
+ const int height = bitmap_rect.height();
+
+ if (width <= 0 || width > kMaxVideoLayerSize ||
+ height <= 0 || height > kMaxVideoLayerSize)
+ return;
+
+ TransportDIB* dib = process->GetTransportDIB(bitmap);
+ if (!dib)
+ return;
+
+ scoped_ptr<skia::PlatformCanvas> p_canvas(
+ dib->GetPlatformCanvas(width, height));
+ for (size_t i = 0; i < copy_rects.size(); i++) {
+ const gfx::Rect& copy_rect = copy_rects[i];
+ int x = copy_rect.x() - bitmap_rect.x();
+ int y = copy_rect.y() - bitmap_rect.y();
+ int w = copy_rect.width();
+ int h = copy_rect.height();
+ SkIRect srcrect = SkIRect::MakeXYWH(x, y, w, h);
+ SkRect dstrect = SkRect::MakeXYWH(
+ SkIntToScalar(copy_rect.x()), SkIntToScalar(copy_rect.y()),
+ SkIntToScalar(w), SkIntToScalar(h));
+ SkBitmap b = p_canvas->getTopPlatformDevice().accessBitmap(false);
+ canvas_.get()->drawBitmapRect(b, &srcrect, dstrect);
+ }
+}
+
+void BackingStoreSkia::ScrollBackingStore(int dx, int dy,
+ const gfx::Rect& clip_rect,
+ const gfx::Size& view_size) {
+ int x = std::min(clip_rect.x(), clip_rect.x() - dx);
+ int y = std::min(clip_rect.y(), clip_rect.y() - dy);
+ int w = clip_rect.width() + abs(dx);
+ int h = clip_rect.height() + abs(dy);
+ SkIRect rect = SkIRect::MakeXYWH(x, y, w, h);
+ bitmap_.scrollRect(&rect, dx, dy);
+}
+
+bool BackingStoreSkia::CopyFromBackingStore(const gfx::Rect& rect,
+ skia::PlatformCanvas* output) {
+ const int width = std::min(size().width(), rect.width());
+ const int height = std::min(size().height(), rect.height());
+ if (!output->initialize(width, height, true))
+ return false;
+
+ SkBitmap bitmap = output->getTopPlatformDevice().accessBitmap(true);
+ SkIRect skrect = SkIRect::MakeXYWH(rect.x(), rect.y(), width, height);
+ SkBitmap b;
+ if (!canvas_->readPixels(skrect, &b))
+ return false;
+ output->writePixels(b, rect.x(), rect.y());
+ return true;
+}
diff --git a/content/browser/renderer_host/backing_store_skia.h b/content/browser/renderer_host/backing_store_skia.h
new file mode 100644
index 0000000..9f71fb0
--- /dev/null
+++ b/content/browser/renderer_host/backing_store_skia.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_SKIA_H_
+#define CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_SKIA_H_
+#pragma once
+
+#include "base/scoped_ptr.h"
+#include "content/browser/renderer_host/backing_store.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+
+class SkCanvas;
+
+namespace gfx {
+class Point;
+class Canvas;
+}
+
+// A backing store that uses skia. This is a temporary backing store used by
+// RenderWidgetHostViewViews. In time, only GPU rendering will be used for
+// RWHVV, and then this backing store will be removed.
+class BackingStoreSkia : public BackingStore {
+ public:
+ BackingStoreSkia(RenderWidgetHost* widget, const gfx::Size& size);
+ virtual ~BackingStoreSkia();
+
+ void SkiaShowRect(const gfx::Point& point, gfx::Canvas* canvas);
+
+ // BackingStore implementation.
+ virtual size_t MemorySize();
+ virtual void PaintToBackingStore(
+ RenderProcessHost* process,
+ TransportDIB::Id bitmap,
+ const gfx::Rect& bitmap_rect,
+ const std::vector<gfx::Rect>& copy_rects);
+ virtual bool CopyFromBackingStore(const gfx::Rect& rect,
+ skia::PlatformCanvas* output);
+ virtual void ScrollBackingStore(int dx, int dy,
+ const gfx::Rect& clip_rect,
+ const gfx::Size& view_size);
+
+ private:
+ SkBitmap bitmap_;
+
+ scoped_ptr<SkCanvas> canvas_;
+
+ DISALLOW_COPY_AND_ASSIGN(BackingStoreSkia);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_SKIA_H_
diff --git a/content/browser/renderer_host/backing_store_win.cc b/content/browser/renderer_host/backing_store_win.cc
new file mode 100644
index 0000000..0023b51
--- /dev/null
+++ b/content/browser/renderer_host/backing_store_win.cc
@@ -0,0 +1,175 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/backing_store_win.h"
+
+#include "app/surface/transport_dib.h"
+#include "base/command_line.h"
+#include "chrome/common/chrome_switches.h"
+#include "content/browser/renderer_host/render_process_host.h"
+#include "content/browser/renderer_host/render_widget_host.h"
+#include "skia/ext/platform_canvas.h"
+#include "ui/gfx/gdi_util.h"
+
+namespace {
+
+// Creates a dib conforming to the height/width/section parameters passed in.
+HANDLE CreateDIB(HDC dc, int width, int height, int color_depth) {
+ BITMAPV5HEADER hdr = {0};
+ ZeroMemory(&hdr, sizeof(BITMAPV5HEADER));
+
+ // These values are shared with gfx::PlatformDevice
+ hdr.bV5Size = sizeof(BITMAPINFOHEADER);
+ hdr.bV5Width = width;
+ hdr.bV5Height = -height; // minus means top-down bitmap
+ hdr.bV5Planes = 1;
+ hdr.bV5BitCount = color_depth;
+ hdr.bV5Compression = BI_RGB; // no compression
+ hdr.bV5SizeImage = 0;
+ hdr.bV5XPelsPerMeter = 1;
+ hdr.bV5YPelsPerMeter = 1;
+ hdr.bV5ClrUsed = 0;
+ hdr.bV5ClrImportant = 0;
+
+ if (BackingStoreWin::ColorManagementEnabled()) {
+ hdr.bV5CSType = LCS_sRGB;
+ hdr.bV5Intent = LCS_GM_IMAGES;
+ }
+
+ void* data = NULL;
+ HANDLE dib = CreateDIBSection(dc, reinterpret_cast<BITMAPINFO*>(&hdr),
+ 0, &data, NULL, 0);
+ DCHECK(data);
+ return dib;
+}
+
+void CallStretchDIBits(HDC hdc, int dest_x, int dest_y, int dest_w, int dest_h,
+ int src_x, int src_y, int src_w, int src_h, void* pixels,
+ const BITMAPINFO* bitmap_info) {
+ // When blitting a rectangle that touches the bottom, left corner of the
+ // bitmap, StretchDIBits looks at it top-down! For more details, see
+ // http://wiki.allegro.cc/index.php?title=StretchDIBits.
+ int rv;
+ int bitmap_h = -bitmap_info->bmiHeader.biHeight;
+ int bottom_up_src_y = bitmap_h - src_y - src_h;
+ if (bottom_up_src_y == 0 && src_x == 0 && src_h != bitmap_h) {
+ rv = StretchDIBits(hdc,
+ dest_x, dest_h + dest_y - 1, dest_w, -dest_h,
+ src_x, bitmap_h - src_y + 1, src_w, -src_h,
+ pixels, bitmap_info, DIB_RGB_COLORS, SRCCOPY);
+ } else {
+ rv = StretchDIBits(hdc,
+ dest_x, dest_y, dest_w, dest_h,
+ src_x, bottom_up_src_y, src_w, src_h,
+ pixels, bitmap_info, DIB_RGB_COLORS, SRCCOPY);
+ }
+ DCHECK(rv != GDI_ERROR);
+}
+
+} // namespace
+
+BackingStoreWin::BackingStoreWin(RenderWidgetHost* widget, const gfx::Size& size)
+ : BackingStore(widget, size),
+ backing_store_dib_(NULL),
+ original_bitmap_(NULL) {
+ HDC screen_dc = ::GetDC(NULL);
+ color_depth_ = ::GetDeviceCaps(screen_dc, BITSPIXEL);
+ // Color depths less than 16 bpp require a palette to be specified. Instead,
+ // we specify the desired color depth as 16 which lets the OS to come up
+ // with an approximation.
+ if (color_depth_ < 16)
+ color_depth_ = 16;
+ hdc_ = CreateCompatibleDC(screen_dc);
+ ReleaseDC(NULL, screen_dc);
+}
+
+BackingStoreWin::~BackingStoreWin() {
+ DCHECK(hdc_);
+ if (original_bitmap_) {
+ SelectObject(hdc_, original_bitmap_);
+ }
+ if (backing_store_dib_) {
+ DeleteObject(backing_store_dib_);
+ backing_store_dib_ = NULL;
+ }
+ DeleteDC(hdc_);
+}
+
+// static
+bool BackingStoreWin::ColorManagementEnabled() {
+ static bool enabled = false;
+ static bool checked = false;
+ if (!checked) {
+ checked = true;
+ const CommandLine& command = *CommandLine::ForCurrentProcess();
+ enabled = command.HasSwitch(switches::kEnableMonitorProfile);
+ }
+ return enabled;
+}
+
+size_t BackingStoreWin::MemorySize() {
+ return size().GetArea() * (color_depth_ / 8);
+}
+
+void BackingStoreWin::PaintToBackingStore(
+ RenderProcessHost* process,
+ TransportDIB::Id bitmap,
+ const gfx::Rect& bitmap_rect,
+ const std::vector<gfx::Rect>& copy_rects) {
+ if (!backing_store_dib_) {
+ backing_store_dib_ = CreateDIB(hdc_, size().width(),
+ size().height(), color_depth_);
+ if (!backing_store_dib_) {
+ NOTREACHED();
+ return;
+ }
+ original_bitmap_ = SelectObject(hdc_, backing_store_dib_);
+ }
+
+ TransportDIB* dib = process->GetTransportDIB(bitmap);
+ if (!dib)
+ return;
+
+ BITMAPINFOHEADER hdr;
+ gfx::CreateBitmapHeader(bitmap_rect.width(), bitmap_rect.height(), &hdr);
+ // Account for a bitmap_rect that exceeds the bounds of our view
+ gfx::Rect view_rect(size());
+
+ for (size_t i = 0; i < copy_rects.size(); i++) {
+ gfx::Rect paint_rect = view_rect.Intersect(copy_rects[i]);
+ CallStretchDIBits(hdc_,
+ paint_rect.x(),
+ paint_rect.y(),
+ paint_rect.width(),
+ paint_rect.height(),
+ paint_rect.x() - bitmap_rect.x(),
+ paint_rect.y() - bitmap_rect.y(),
+ paint_rect.width(),
+ paint_rect.height(),
+ dib->memory(),
+ reinterpret_cast<BITMAPINFO*>(&hdr));
+ }
+}
+
+bool BackingStoreWin::CopyFromBackingStore(const gfx::Rect& rect,
+ skia::PlatformCanvas* output) {
+ if (!output->initialize(rect.width(), rect.height(), true))
+ return false;
+
+ HDC temp_dc = output->beginPlatformPaint();
+ BitBlt(temp_dc, 0, 0, rect.width(), rect.height(),
+ hdc(), rect.x(), rect.y(), SRCCOPY);
+ output->endPlatformPaint();
+ return true;
+}
+
+void BackingStoreWin::ScrollBackingStore(int dx, int dy,
+ const gfx::Rect& clip_rect,
+ const gfx::Size& view_size) {
+ RECT damaged_rect, r = clip_rect.ToRECT();
+ ScrollDC(hdc_, dx, dy, NULL, &r, NULL, &damaged_rect);
+
+ // TODO(darin): this doesn't work if dx and dy are both non-zero!
+ DCHECK(dx == 0 || dy == 0);
+}
diff --git a/content/browser/renderer_host/backing_store_win.h b/content/browser/renderer_host/backing_store_win.h
new file mode 100644
index 0000000..197de6bc
--- /dev/null
+++ b/content/browser/renderer_host/backing_store_win.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_WIN_H_
+#define CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_WIN_H_
+#pragma once
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "content/browser/renderer_host/backing_store.h"
+
+class BackingStoreWin : public BackingStore {
+ public:
+ BackingStoreWin(RenderWidgetHost* widget, const gfx::Size& size);
+ virtual ~BackingStoreWin();
+
+ HDC hdc() { return hdc_; }
+
+ // Returns true if we should convert to the monitor profile when painting.
+ static bool ColorManagementEnabled();
+
+ // BackingStore implementation.
+ virtual size_t MemorySize();
+ virtual void PaintToBackingStore(RenderProcessHost* process,
+ TransportDIB::Id bitmap,
+ const gfx::Rect& bitmap_rect,
+ const std::vector<gfx::Rect>& copy_rects);
+ virtual bool CopyFromBackingStore(const gfx::Rect& rect,
+ skia::PlatformCanvas* output);
+ virtual void ScrollBackingStore(int dx, int dy,
+ const gfx::Rect& clip_rect,
+ const gfx::Size& view_size);
+
+ private:
+ // The backing store dc.
+ HDC hdc_;
+
+ // Handle to the backing store dib.
+ HANDLE backing_store_dib_;
+
+ // Handle to the original bitmap in the dc.
+ HANDLE original_bitmap_;
+
+ // Number of bits per pixel of the screen.
+ int color_depth_;
+
+ DISALLOW_COPY_AND_ASSIGN(BackingStoreWin);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_WIN_H_
diff --git a/content/browser/renderer_host/backing_store_x.cc b/content/browser/renderer_host/backing_store_x.cc
new file mode 100644
index 0000000..5e1e33a
--- /dev/null
+++ b/content/browser/renderer_host/backing_store_x.cc
@@ -0,0 +1,477 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/backing_store_x.h"
+
+#include <cairo-xlib.h>
+#include <gtk/gtk.h>
+#include <stdlib.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#if defined(OS_OPENBSD) || defined(OS_FREEBSD)
+#include <sys/endian.h>
+#endif
+
+#include <algorithm>
+#include <utility>
+#include <limits>
+
+#include "app/surface/transport_dib.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/time.h"
+#include "content/browser/renderer_host/render_process_host.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/x/x11_util.h"
+#include "ui/base/x/x11_util_internal.h"
+#include "ui/gfx/rect.h"
+
+// Assume that somewhere along the line, someone will do width * height * 4
+// with signed numbers. If the maximum value is 2**31, then 2**31 / 4 =
+// 2**29 and floor(sqrt(2**29)) = 23170.
+
+// Max height and width for layers
+static const int kMaxVideoLayerSize = 23170;
+
+
+// X Backing Stores:
+//
+// Unlike Windows, where the backing store is kept in heap memory, we keep our
+// backing store in the X server, as a pixmap. Thus expose events just require
+// instructing the X server to copy from the backing store to the window.
+//
+// The backing store is in the same format as the visual which our main window
+// is using. Bitmaps from the renderer are uploaded to the X server, either via
+// shared memory or over the wire, and XRENDER is used to convert them to the
+// correct format for the backing store.
+
+// Destroys the image and the associated shared memory structures. This is a
+// helper function for code using shared memory.
+static void DestroySharedImage(Display* display,
+ XImage* image,
+ XShmSegmentInfo* shminfo) {
+ XShmDetach(display, shminfo);
+ XDestroyImage(image);
+ shmdt(shminfo->shmaddr);
+}
+
+BackingStoreX::BackingStoreX(RenderWidgetHost* widget,
+ const gfx::Size& size,
+ void* visual,
+ int depth)
+ : BackingStore(widget, size),
+ display_(ui::GetXDisplay()),
+ shared_memory_support_(ui::QuerySharedMemorySupport(display_)),
+ use_render_(ui::QueryRenderSupport(display_)),
+ visual_(visual),
+ visual_depth_(depth),
+ root_window_(ui::GetX11RootWindow()) {
+#if defined(OS_OPENBSD) || defined(OS_FREEBSD)
+ COMPILE_ASSERT(_BYTE_ORDER == _LITTLE_ENDIAN, assumes_little_endian);
+#else
+ COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, assumes_little_endian);
+#endif
+
+ pixmap_ = XCreatePixmap(display_, root_window_,
+ size.width(), size.height(), depth);
+
+ if (use_render_) {
+ picture_ = XRenderCreatePicture(
+ display_, pixmap_,
+ ui::GetRenderVisualFormat(display_,
+ static_cast<Visual*>(visual)),
+ 0, NULL);
+ pixmap_bpp_ = 0;
+ } else {
+ picture_ = 0;
+ pixmap_bpp_ = ui::BitsPerPixelForPixmapDepth(display_, depth);
+ }
+
+ pixmap_gc_ = XCreateGC(display_, pixmap_, 0, NULL);
+}
+
+BackingStoreX::BackingStoreX(RenderWidgetHost* widget, const gfx::Size& size)
+ : BackingStore(widget, size),
+ display_(NULL),
+ shared_memory_support_(ui::SHARED_MEMORY_NONE),
+ use_render_(false),
+ pixmap_bpp_(0),
+ visual_(NULL),
+ visual_depth_(-1),
+ root_window_(0),
+ pixmap_(0),
+ picture_(0),
+ pixmap_gc_(NULL) {
+}
+
+BackingStoreX::~BackingStoreX() {
+ // In unit tests, display_ may be NULL.
+ if (!display_)
+ return;
+
+ XRenderFreePicture(display_, picture_);
+ XFreePixmap(display_, pixmap_);
+ XFreeGC(display_, static_cast<GC>(pixmap_gc_));
+}
+
+size_t BackingStoreX::MemorySize() {
+ if (!use_render_)
+ return size().GetArea() * (pixmap_bpp_ / 8);
+ else
+ return size().GetArea() * 4;
+}
+
+void BackingStoreX::PaintRectWithoutXrender(
+ TransportDIB* bitmap,
+ const gfx::Rect& bitmap_rect,
+ const std::vector<gfx::Rect>& copy_rects) {
+ const int width = bitmap_rect.width();
+ const int height = bitmap_rect.height();
+ Pixmap pixmap = XCreatePixmap(display_, root_window_, width, height,
+ visual_depth_);
+
+ // Draw ARGB transport DIB onto our pixmap.
+ ui::PutARGBImage(display_, visual_, visual_depth_, pixmap,
+ pixmap_gc_, static_cast<uint8*>(bitmap->memory()),
+ width, height);
+
+ for (size_t i = 0; i < copy_rects.size(); i++) {
+ const gfx::Rect& copy_rect = copy_rects[i];
+ XCopyArea(display_,
+ pixmap, // src
+ pixmap_, // dest
+ static_cast<GC>(pixmap_gc_), // gc
+ copy_rect.x() - bitmap_rect.x(), // src_x
+ copy_rect.y() - bitmap_rect.y(), // src_y
+ copy_rect.width(), // width
+ copy_rect.height(), // height
+ copy_rect.x(), // dest_x
+ copy_rect.y()); // dest_y
+ }
+
+ XFreePixmap(display_, pixmap);
+}
+
+void BackingStoreX::PaintToBackingStore(
+ RenderProcessHost* process,
+ TransportDIB::Id bitmap,
+ const gfx::Rect& bitmap_rect,
+ const std::vector<gfx::Rect>& copy_rects) {
+ if (!display_)
+ return;
+
+ if (bitmap_rect.IsEmpty())
+ return;
+
+ const int width = bitmap_rect.width();
+ const int height = bitmap_rect.height();
+
+ if (width <= 0 || width > kMaxVideoLayerSize ||
+ height <= 0 || height > kMaxVideoLayerSize)
+ return;
+
+ TransportDIB* dib = process->GetTransportDIB(bitmap);
+ if (!dib)
+ return;
+
+ if (!use_render_)
+ return PaintRectWithoutXrender(dib, bitmap_rect, copy_rects);
+
+ Picture picture;
+ Pixmap pixmap;
+
+ if (shared_memory_support_ == ui::SHARED_MEMORY_PIXMAP) {
+ XShmSegmentInfo shminfo = {0};
+ shminfo.shmseg = dib->MapToX(display_);
+
+ // The NULL in the following is the |data| pointer: this is an artifact of
+ // Xlib trying to be helpful, rather than just exposing the X protocol. It
+ // assumes that we have the shared memory segment mapped into our memory,
+ // which we don't, and it's trying to calculate an offset by taking the
+ // difference between the |data| pointer and the address of the mapping in
+ // |shminfo|. Since both are NULL, the offset will be calculated to be 0,
+ // which is correct for us.
+ pixmap = XShmCreatePixmap(display_, root_window_, NULL, &shminfo,
+ width, height, 32);
+ } else {
+ // We don't have shared memory pixmaps. Fall back to creating a pixmap
+ // ourselves and putting an image on it.
+ pixmap = XCreatePixmap(display_, root_window_, width, height, 32);
+ GC gc = XCreateGC(display_, pixmap, 0, NULL);
+
+ if (shared_memory_support_ == ui::SHARED_MEMORY_PUTIMAGE) {
+ const XID shmseg = dib->MapToX(display_);
+
+ XShmSegmentInfo shminfo;
+ memset(&shminfo, 0, sizeof(shminfo));
+ shminfo.shmseg = shmseg;
+ shminfo.shmaddr = static_cast<char*>(dib->memory());
+
+ XImage* image = XShmCreateImage(display_, static_cast<Visual*>(visual_),
+ 32, ZPixmap,
+ shminfo.shmaddr, &shminfo,
+ width, height);
+
+ // This code path is important for performance and we have found that
+ // different techniques work better on different platforms. See
+ // http://code.google.com/p/chromium/issues/detail?id=44124.
+ //
+ // Checking for ARM is an approximation, but it seems to be a good one so
+ // far.
+#if defined(ARCH_CPU_ARM_FAMILY)
+ for (size_t i = 0; i < copy_rects.size(); i++) {
+ const gfx::Rect& copy_rect = copy_rects[i];
+ XShmPutImage(display_, pixmap, gc, image,
+ copy_rect.x() - bitmap_rect.x(), /* source x */
+ copy_rect.y() - bitmap_rect.y(), /* source y */
+ copy_rect.x() - bitmap_rect.x(), /* dest x */
+ copy_rect.y() - bitmap_rect.y(), /* dest y */
+ copy_rect.width(), copy_rect.height(),
+ False /* send_event */);
+ }
+#else
+ XShmPutImage(display_, pixmap, gc, image,
+ 0, 0 /* source x, y */, 0, 0 /* dest x, y */,
+ width, height, False /* send_event */);
+#endif
+ XDestroyImage(image);
+ } else { // case SHARED_MEMORY_NONE
+ // No shared memory support, we have to copy the bitmap contents
+ // to the X server. Xlib wraps the underlying PutImage call
+ // behind several layers of functions which try to convert the
+ // image into the format which the X server expects. The
+ // following values hopefully disable all conversions.
+ XImage image;
+ memset(&image, 0, sizeof(image));
+
+ image.width = width;
+ image.height = height;
+ image.depth = 32;
+ image.bits_per_pixel = 32;
+ image.format = ZPixmap;
+ image.byte_order = LSBFirst;
+ image.bitmap_unit = 8;
+ image.bitmap_bit_order = LSBFirst;
+ image.bytes_per_line = width * 4;
+ image.red_mask = 0xff;
+ image.green_mask = 0xff00;
+ image.blue_mask = 0xff0000;
+ image.data = static_cast<char*>(dib->memory());
+
+ XPutImage(display_, pixmap, gc, &image,
+ 0, 0 /* source x, y */, 0, 0 /* dest x, y */,
+ width, height);
+ }
+ XFreeGC(display_, gc);
+ }
+
+ picture = ui::CreatePictureFromSkiaPixmap(display_, pixmap);
+
+ for (size_t i = 0; i < copy_rects.size(); i++) {
+ const gfx::Rect& copy_rect = copy_rects[i];
+ XRenderComposite(display_,
+ PictOpSrc, // op
+ picture, // src
+ 0, // mask
+ picture_, // dest
+ copy_rect.x() - bitmap_rect.x(), // src_x
+ copy_rect.y() - bitmap_rect.y(), // src_y
+ 0, // mask_x
+ 0, // mask_y
+ copy_rect.x(), // dest_x
+ copy_rect.y(), // dest_y
+ copy_rect.width(), // width
+ copy_rect.height()); // height
+ }
+
+ // In the case of shared memory, we wait for the composite to complete so that
+ // we are sure that the X server has finished reading from the shared memory
+ // segment.
+ if (shared_memory_support_ != ui::SHARED_MEMORY_NONE)
+ XSync(display_, False);
+
+ XRenderFreePicture(display_, picture);
+ XFreePixmap(display_, pixmap);
+}
+
+bool BackingStoreX::CopyFromBackingStore(const gfx::Rect& rect,
+ skia::PlatformCanvas* output) {
+ base::TimeTicks begin_time = base::TimeTicks::Now();
+
+ if (visual_depth_ < 24) {
+ // CopyFromBackingStore() copies pixels out of the XImage
+ // in a way that assumes that each component (red, green,
+ // blue) is a byte. This doesn't work on visuals which
+ // encode a pixel color with less than a byte per color.
+ return false;
+ }
+
+ const int width = std::min(size().width(), rect.width());
+ const int height = std::min(size().height(), rect.height());
+
+ XImage* image;
+ XShmSegmentInfo shminfo; // Used only when shared memory is enabled.
+ if (shared_memory_support_ != ui::SHARED_MEMORY_NONE) {
+ // Use shared memory for faster copies when it's available.
+ Visual* visual = static_cast<Visual*>(visual_);
+ memset(&shminfo, 0, sizeof(shminfo));
+ image = XShmCreateImage(display_, visual, 32,
+ ZPixmap, NULL, &shminfo, width, height);
+ if (!image) {
+ return false;
+ }
+ // Create the shared memory segment for the image and map it.
+ if (image->bytes_per_line == 0 || image->height == 0 ||
+ static_cast<size_t>(image->height) >
+ (std::numeric_limits<size_t>::max() / image->bytes_per_line)) {
+ XDestroyImage(image);
+ return false;
+ }
+ shminfo.shmid = shmget(IPC_PRIVATE, image->bytes_per_line * image->height,
+ IPC_CREAT|0666);
+ if (shminfo.shmid == -1) {
+ XDestroyImage(image);
+ return false;
+ }
+
+ void* mapped_memory = shmat(shminfo.shmid, NULL, SHM_RDONLY);
+ shmctl(shminfo.shmid, IPC_RMID, 0);
+ if (mapped_memory == (void*)-1) {
+ XDestroyImage(image);
+ return false;
+ }
+ shminfo.shmaddr = image->data = static_cast<char*>(mapped_memory);
+
+ if (!XShmAttach(display_, &shminfo) ||
+ !XShmGetImage(display_, pixmap_, image, rect.x(), rect.y(),
+ AllPlanes)) {
+ DestroySharedImage(display_, image, &shminfo);
+ return false;
+ }
+ } else {
+ // Non-shared memory case just copy the image from the server.
+ image = XGetImage(display_, pixmap_,
+ rect.x(), rect.y(), width, height,
+ AllPlanes, ZPixmap);
+ }
+
+ // TODO(jhawkins): Need to convert the image data if the image bits per pixel
+ // is not 32.
+ // Note that this also initializes the output bitmap as opaque.
+ if (!output->initialize(width, height, true) ||
+ image->bits_per_pixel != 32) {
+ if (shared_memory_support_ != ui::SHARED_MEMORY_NONE)
+ DestroySharedImage(display_, image, &shminfo);
+ else
+ XDestroyImage(image);
+ return false;
+ }
+
+ // The X image might have a different row stride, so iterate through
+ // it and copy each row out, only up to the pixels we're actually
+ // using. This code assumes a visual mode where a pixel is
+ // represented using a 32-bit unsigned int, with a byte per component.
+ SkBitmap bitmap = output->getTopPlatformDevice().accessBitmap(true);
+ for (int y = 0; y < height; y++) {
+ const uint32* src_row = reinterpret_cast<uint32*>(
+ &image->data[image->bytes_per_line * y]);
+ uint32* dest_row = bitmap.getAddr32(0, y);
+ for (int x = 0; x < width; ++x, ++dest_row) {
+ // Force alpha to be 0xff, because otherwise it causes rendering problems.
+ *dest_row = src_row[x] | 0xff000000;
+ }
+ }
+
+ if (shared_memory_support_ != ui::SHARED_MEMORY_NONE)
+ DestroySharedImage(display_, image, &shminfo);
+ else
+ XDestroyImage(image);
+
+ HISTOGRAM_TIMES("BackingStore.RetrievalFromX",
+ base::TimeTicks::Now() - begin_time);
+ return true;
+}
+
+void BackingStoreX::ScrollBackingStore(int dx, int dy,
+ const gfx::Rect& clip_rect,
+ const gfx::Size& view_size) {
+ if (!display_)
+ return;
+
+ // We only support scrolling in one direction at a time.
+ DCHECK(dx == 0 || dy == 0);
+
+ if (dy) {
+ // Positive values of |dy| scroll up
+ if (abs(dy) < clip_rect.height()) {
+ XCopyArea(display_, pixmap_, pixmap_, static_cast<GC>(pixmap_gc_),
+ clip_rect.x() /* source x */,
+ std::max(clip_rect.y(), clip_rect.y() - dy),
+ clip_rect.width(),
+ clip_rect.height() - abs(dy),
+ clip_rect.x() /* dest x */,
+ std::max(clip_rect.y(), clip_rect.y() + dy) /* dest y */);
+ }
+ } else if (dx) {
+ // Positive values of |dx| scroll right
+ if (abs(dx) < clip_rect.width()) {
+ XCopyArea(display_, pixmap_, pixmap_, static_cast<GC>(pixmap_gc_),
+ std::max(clip_rect.x(), clip_rect.x() - dx),
+ clip_rect.y() /* source y */,
+ clip_rect.width() - abs(dx),
+ clip_rect.height(),
+ std::max(clip_rect.x(), clip_rect.x() + dx) /* dest x */,
+ clip_rect.y() /* dest x */);
+ }
+ }
+}
+
+void BackingStoreX::XShowRect(const gfx::Point &origin,
+ const gfx::Rect& rect, XID target) {
+ XCopyArea(display_, pixmap_, target, static_cast<GC>(pixmap_gc_),
+ rect.x(), rect.y(), rect.width(), rect.height(),
+ rect.x() + origin.x(), rect.y() + origin.y());
+}
+
+void BackingStoreX::CairoShowRect(const gfx::Rect& rect,
+ GdkDrawable* drawable) {
+ cairo_surface_t* surface = cairo_xlib_surface_create(
+ display_, pixmap_, static_cast<Visual*>(visual_),
+ size().width(), size().height());
+ cairo_t* cr = gdk_cairo_create(drawable);
+ cairo_set_source_surface(cr, surface, 0, 0);
+
+ cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
+ cairo_fill(cr);
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+}
+
+#if defined(TOOLKIT_GTK)
+void BackingStoreX::PaintToRect(const gfx::Rect& rect, GdkDrawable* target) {
+ cairo_surface_t* surface = cairo_xlib_surface_create(
+ display_, pixmap_, static_cast<Visual*>(visual_),
+ size().width(), size().height());
+ cairo_t* cr = gdk_cairo_create(target);
+
+ cairo_translate(cr, rect.x(), rect.y());
+ double x_scale = static_cast<double>(rect.width()) / size().width();
+ double y_scale = static_cast<double>(rect.height()) / size().height();
+ cairo_scale(cr, x_scale, y_scale);
+
+ cairo_pattern_t* pattern = cairo_pattern_create_for_surface(surface);
+ cairo_pattern_set_filter(pattern, CAIRO_FILTER_BEST);
+ cairo_set_source(cr, pattern);
+ cairo_pattern_destroy(pattern);
+
+ cairo_identity_matrix(cr);
+
+ cairo_rectangle(cr, rect.x(), rect.y(), rect.width(), rect.height());
+ cairo_fill(cr);
+ cairo_destroy(cr);
+}
+#endif
diff --git a/content/browser/renderer_host/backing_store_x.h b/content/browser/renderer_host/backing_store_x.h
new file mode 100644
index 0000000..f911b1f
--- /dev/null
+++ b/content/browser/renderer_host/backing_store_x.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_X_H_
+#define CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_X_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "content/browser/renderer_host/backing_store.h"
+#include "ui/base/x/x11_util.h"
+
+namespace gfx {
+class Point;
+class Rect;
+} // namespace gfx
+
+typedef struct _GdkDrawable GdkDrawable;
+class SkBitmap;
+
+class BackingStoreX : public BackingStore {
+ public:
+ // Create a backing store on the X server. The visual is an Xlib Visual
+ // describing the format of the target window and the depth is the color
+ // depth of the X window which will be drawn into.
+ BackingStoreX(RenderWidgetHost* widget,
+ const gfx::Size& size,
+ void* visual,
+ int depth);
+
+ // This is for unittesting only. An object constructed using this constructor
+ // will silently ignore all paints
+ BackingStoreX(RenderWidgetHost* widget, const gfx::Size& size);
+
+ virtual ~BackingStoreX();
+
+ Display* display() const { return display_; }
+ XID root_window() const { return root_window_; }
+
+ // Copy from the server-side backing store to the target window
+ // origin: the destination rectangle origin
+ // damage: the area to copy
+ // target: the X id of the target window
+ void XShowRect(const gfx::Point &origin, const gfx::Rect& damage,
+ XID target);
+
+ // As above, but use Cairo instead of Xlib.
+ void CairoShowRect(const gfx::Rect& damage, GdkDrawable* drawable);
+
+#if defined(TOOLKIT_GTK)
+ // Paint the backing store into the target's |dest_rect|.
+ void PaintToRect(const gfx::Rect& dest_rect, GdkDrawable* target);
+#endif
+
+ // BackingStore implementation.
+ virtual size_t MemorySize();
+ virtual void PaintToBackingStore(
+ RenderProcessHost* process,
+ TransportDIB::Id bitmap,
+ const gfx::Rect& bitmap_rect,
+ const std::vector<gfx::Rect>& copy_rects);
+ virtual bool CopyFromBackingStore(const gfx::Rect& rect,
+ skia::PlatformCanvas* output);
+ virtual void ScrollBackingStore(int dx, int dy,
+ const gfx::Rect& clip_rect,
+ const gfx::Size& view_size);
+
+ private:
+ // Paints the bitmap from the renderer onto the backing store without
+ // using Xrender to composite the pixmaps.
+ void PaintRectWithoutXrender(TransportDIB* bitmap,
+ const gfx::Rect& bitmap_rect,
+ const std::vector<gfx::Rect>& copy_rects);
+
+ // This is the connection to the X server where this backing store will be
+ // displayed.
+ Display* const display_;
+ // What flavor, if any, MIT-SHM (X shared memory) support we have.
+ const ui::SharedMemorySupport shared_memory_support_;
+ // If this is true, then we can use Xrender to composite our pixmaps.
+ const bool use_render_;
+ // If |use_render_| is false, this is the number of bits-per-pixel for |depth|
+ int pixmap_bpp_;
+ // if |use_render_| is false, we need the Visual to get the RGB masks.
+ void* const visual_;
+ // This is the depth of the target window.
+ const int visual_depth_;
+ // The parent window (probably a GtkDrawingArea) for this backing store.
+ const XID root_window_;
+ // This is a handle to the server side pixmap which is our backing store.
+ XID pixmap_;
+ // This is the RENDER picture pointing at |pixmap_|.
+ XID picture_;
+ // This is a default graphic context, used in XCopyArea
+ void* pixmap_gc_;
+
+ DISALLOW_COPY_AND_ASSIGN(BackingStoreX);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_BACKING_STORE_X_H_
diff --git a/content/browser/renderer_host/blob_message_filter.cc b/content/browser/renderer_host/blob_message_filter.cc
new file mode 100644
index 0000000..433fe72
--- /dev/null
+++ b/content/browser/renderer_host/blob_message_filter.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/blob_message_filter.h"
+
+#include "chrome/browser/child_process_security_policy.h"
+#include "chrome/browser/chrome_blob_storage_context.h"
+#include "chrome/common/render_messages.h"
+#include "googleurl/src/gurl.h"
+#include "webkit/blob/blob_data.h"
+#include "webkit/blob/blob_storage_controller.h"
+
+BlobMessageFilter::BlobMessageFilter(
+ int process_id,
+ ChromeBlobStorageContext* blob_storage_context)
+ : process_id_(process_id),
+ blob_storage_context_(blob_storage_context) {
+}
+
+BlobMessageFilter::~BlobMessageFilter() {
+}
+
+void BlobMessageFilter::OnChannelClosing() {
+ BrowserMessageFilter::OnChannelClosing();
+
+ // Unregister all the blob URLs that are previously registered in this
+ // process.
+ for (base::hash_set<std::string>::const_iterator iter = blob_urls_.begin();
+ iter != blob_urls_.end(); ++iter) {
+ blob_storage_context_->controller()->UnregisterBlobUrl(GURL(*iter));
+ }
+}
+
+bool BlobMessageFilter::OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(BlobMessageFilter, message, *message_was_ok)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_RegisterBlobUrl, OnRegisterBlobUrl)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_RegisterBlobUrlFrom, OnRegisterBlobUrlFrom)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_UnregisterBlobUrl, OnUnregisterBlobUrl)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+// Check if the child process has been granted permission to register the files.
+bool BlobMessageFilter::CheckPermission(
+ webkit_blob::BlobData* blob_data) const {
+ ChildProcessSecurityPolicy* policy =
+ ChildProcessSecurityPolicy::GetInstance();
+ for (std::vector<webkit_blob::BlobData::Item>::const_iterator iter =
+ blob_data->items().begin();
+ iter != blob_data->items().end(); ++iter) {
+ if (iter->type() == webkit_blob::BlobData::TYPE_FILE) {
+ if (!policy->CanReadFile(process_id_, iter->file_path()))
+ return false;
+ }
+ }
+ return true;
+}
+
+void BlobMessageFilter::OnRegisterBlobUrl(
+ const GURL& url, const scoped_refptr<webkit_blob::BlobData>& blob_data) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ if (!CheckPermission(blob_data.get()))
+ return;
+ blob_storage_context_->controller()->RegisterBlobUrl(url, blob_data);
+ blob_urls_.insert(url.spec());
+}
+
+void BlobMessageFilter::OnRegisterBlobUrlFrom(
+ const GURL& url, const GURL& src_url) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ blob_storage_context_->controller()->RegisterBlobUrlFrom(url, src_url);
+ blob_urls_.insert(url.spec());
+}
+
+void BlobMessageFilter::OnUnregisterBlobUrl(const GURL& url) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ blob_storage_context_->controller()->UnregisterBlobUrl(url);
+ blob_urls_.erase(url.spec());
+}
diff --git a/content/browser/renderer_host/blob_message_filter.h b/content/browser/renderer_host/blob_message_filter.h
new file mode 100644
index 0000000..aa1cdc6
--- /dev/null
+++ b/content/browser/renderer_host/blob_message_filter.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_BLOB_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_BLOB_MESSAGE_FILTER_H_
+
+#include "base/hash_tables.h"
+#include "chrome/browser/browser_message_filter.h"
+
+class ChromeBlobStorageContext;
+class GURL;
+
+namespace IPC {
+class Message;
+}
+
+namespace webkit_blob {
+class BlobData;
+}
+
+class BlobMessageFilter : public BrowserMessageFilter {
+ public:
+ BlobMessageFilter(int process_id,
+ ChromeBlobStorageContext* blob_storage_context);
+ ~BlobMessageFilter();
+
+ // BrowserMessageFilter implementation.
+ virtual void OnChannelClosing();
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok);
+
+ private:
+ void OnRegisterBlobUrl(const GURL& url,
+ const scoped_refptr<webkit_blob::BlobData>& blob_data);
+ void OnRegisterBlobUrlFrom(const GURL& url, const GURL& src_url);
+ void OnUnregisterBlobUrl(const GURL& url);
+
+ bool CheckPermission(webkit_blob::BlobData* blob_data) const;
+
+ int process_id_;
+ scoped_refptr<ChromeBlobStorageContext> blob_storage_context_;
+
+ // Keep track of blob URLs registered in this process. Need to unregister
+ // all of them when the renderer process dies.
+ base::hash_set<std::string> blob_urls_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(BlobMessageFilter);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_BLOB_MESSAGE_FILTER_H_
diff --git a/content/browser/renderer_host/buffered_resource_handler.cc b/content/browser/renderer_host/buffered_resource_handler.cc
new file mode 100644
index 0000000..2e302f8
--- /dev/null
+++ b/content/browser/renderer_host/buffered_resource_handler.cc
@@ -0,0 +1,491 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/buffered_resource_handler.h"
+
+#include <vector>
+
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/string_util.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/renderer_host/download_throttling_resource_handler.h"
+#include "chrome/common/extensions/user_script.h"
+#include "chrome/common/resource_response.h"
+#include "chrome/common/url_constants.h"
+#include "content/browser/renderer_host/resource_dispatcher_host.h"
+#include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
+#include "content/browser/renderer_host/x509_user_cert_resource_handler.h"
+#include "net/base/io_buffer.h"
+#include "net/base/mime_sniffer.h"
+#include "net/base/mime_util.h"
+#include "net/base/net_errors.h"
+#include "net/http/http_response_headers.h"
+#include "webkit/plugins/npapi/plugin_list.h"
+
+namespace {
+
+void RecordSnifferMetrics(bool sniffing_blocked,
+ bool we_would_like_to_sniff,
+ const std::string& mime_type) {
+ static scoped_refptr<base::Histogram> nosniff_usage =
+ base::BooleanHistogram::FactoryGet(
+ "nosniff.usage", base::Histogram::kUmaTargetedHistogramFlag);
+ nosniff_usage->AddBoolean(sniffing_blocked);
+
+ if (sniffing_blocked) {
+ static scoped_refptr<base::Histogram> nosniff_otherwise =
+ base::BooleanHistogram::FactoryGet(
+ "nosniff.otherwise", base::Histogram::kUmaTargetedHistogramFlag);
+ nosniff_otherwise->AddBoolean(we_would_like_to_sniff);
+
+ static scoped_refptr<base::Histogram> nosniff_empty_mime_type =
+ base::BooleanHistogram::FactoryGet(
+ "nosniff.empty_mime_type",
+ base::Histogram::kUmaTargetedHistogramFlag);
+ nosniff_empty_mime_type->AddBoolean(mime_type.empty());
+ }
+}
+
+} // namespace
+
+BufferedResourceHandler::BufferedResourceHandler(ResourceHandler* handler,
+ ResourceDispatcherHost* host,
+ net::URLRequest* request)
+ : real_handler_(handler),
+ host_(host),
+ request_(request),
+ read_buffer_size_(0),
+ bytes_read_(0),
+ sniff_content_(false),
+ should_buffer_(false),
+ wait_for_plugins_(false),
+ buffering_(false),
+ finished_(false) {
+}
+
+bool BufferedResourceHandler::OnUploadProgress(int request_id,
+ uint64 position,
+ uint64 size) {
+ return real_handler_->OnUploadProgress(request_id, position, size);
+}
+
+bool BufferedResourceHandler::OnRequestRedirected(int request_id,
+ const GURL& new_url,
+ ResourceResponse* response,
+ bool* defer) {
+ return real_handler_->OnRequestRedirected(
+ request_id, new_url, response, defer);
+}
+
+bool BufferedResourceHandler::OnResponseStarted(int request_id,
+ ResourceResponse* response) {
+ response_ = response;
+ if (!DelayResponse())
+ return CompleteResponseStarted(request_id, false);
+ return true;
+}
+
+bool BufferedResourceHandler::OnResponseCompleted(
+ int request_id,
+ const net::URLRequestStatus& status,
+ const std::string& security_info) {
+ return real_handler_->OnResponseCompleted(request_id, status, security_info);
+}
+
+void BufferedResourceHandler::OnRequestClosed() {
+ request_ = NULL;
+ real_handler_->OnRequestClosed();
+}
+
+bool BufferedResourceHandler::OnWillStart(int request_id,
+ const GURL& url,
+ bool* defer) {
+ return real_handler_->OnWillStart(request_id, url, defer);
+}
+
+// We'll let the original event handler provide a buffer, and reuse it for
+// subsequent reads until we're done buffering.
+bool BufferedResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf,
+ int* buf_size, int min_size) {
+ if (buffering_) {
+ DCHECK(!my_buffer_.get());
+ my_buffer_ = new net::IOBuffer(net::kMaxBytesToSniff);
+ *buf = my_buffer_.get();
+ *buf_size = net::kMaxBytesToSniff;
+ return true;
+ }
+
+ if (finished_)
+ return false;
+
+ if (!real_handler_->OnWillRead(request_id, buf, buf_size, min_size)) {
+ return false;
+ }
+ read_buffer_ = *buf;
+ read_buffer_size_ = *buf_size;
+ DCHECK_GE(read_buffer_size_, net::kMaxBytesToSniff * 2);
+ bytes_read_ = 0;
+ return true;
+}
+
+bool BufferedResourceHandler::OnReadCompleted(int request_id, int* bytes_read) {
+ if (sniff_content_ || should_buffer_) {
+ if (KeepBuffering(*bytes_read))
+ return true;
+
+ *bytes_read = bytes_read_;
+
+ // Done buffering, send the pending ResponseStarted event.
+ if (!CompleteResponseStarted(request_id, true))
+ return false;
+ } else if (wait_for_plugins_) {
+ return true;
+ }
+
+ // Release the reference that we acquired at OnWillRead.
+ read_buffer_ = NULL;
+ return real_handler_->OnReadCompleted(request_id, bytes_read);
+}
+
+BufferedResourceHandler::~BufferedResourceHandler() {}
+
+bool BufferedResourceHandler::DelayResponse() {
+ std::string mime_type;
+ request_->GetMimeType(&mime_type);
+
+ std::string content_type_options;
+ request_->GetResponseHeaderByName("x-content-type-options",
+ &content_type_options);
+
+ const bool sniffing_blocked =
+ LowerCaseEqualsASCII(content_type_options, "nosniff");
+ const bool not_modified_status =
+ response_->response_head.headers &&
+ response_->response_head.headers->response_code() == 304;
+ const bool we_would_like_to_sniff = not_modified_status ?
+ false : net::ShouldSniffMimeType(request_->url(), mime_type);
+
+ RecordSnifferMetrics(sniffing_blocked, we_would_like_to_sniff, mime_type);
+
+ if (!sniffing_blocked && we_would_like_to_sniff) {
+ // We're going to look at the data before deciding what the content type
+ // is. That means we need to delay sending the ResponseStarted message
+ // over the IPC channel.
+ sniff_content_ = true;
+ VLOG(1) << "To buffer: " << request_->url().spec();
+ return true;
+ }
+
+ if (sniffing_blocked && mime_type.empty() && !not_modified_status) {
+ // Ugg. The server told us not to sniff the content but didn't give us a
+ // mime type. What's a browser to do? Turns out, we're supposed to treat
+ // the response as "text/plain". This is the most secure option.
+ mime_type.assign("text/plain");
+ response_->response_head.mime_type.assign(mime_type);
+ }
+
+ if (mime_type == "application/rss+xml" ||
+ mime_type == "application/atom+xml") {
+ // Sad face. The server told us that they wanted us to treat the response
+ // as RSS or Atom. Unfortunately, we don't have a built-in feed previewer
+ // like other browsers. We can't just render the content as XML because
+ // web sites let third parties inject arbitrary script into their RSS
+ // feeds. That leaves us with little choice but to practically ignore the
+ // response. In the future, when we have an RSS feed previewer, we can
+ // remove this logic.
+ mime_type.assign("text/plain");
+ response_->response_head.mime_type.assign(mime_type);
+ }
+
+ if (ShouldBuffer(request_->url(), mime_type)) {
+ // This is a temporary fix for the fact that webkit expects to have
+ // enough data to decode the doctype in order to select the rendering
+ // mode.
+ should_buffer_ = true;
+ return true;
+ }
+
+ if (!not_modified_status && ShouldWaitForPlugins()) {
+ wait_for_plugins_ = true;
+ return true;
+ }
+
+ return false;
+}
+
+bool BufferedResourceHandler::ShouldBuffer(const GURL& url,
+ const std::string& mime_type) {
+ // We are willing to buffer for HTTP and HTTPS.
+ bool sniffable_scheme = url.is_empty() ||
+ url.SchemeIs(chrome::kHttpScheme) ||
+ url.SchemeIs(chrome::kHttpsScheme);
+ if (!sniffable_scheme)
+ return false;
+
+ // Today, the only reason to buffer the request is to fix the doctype decoding
+ // performed by webkit: if there is not enough data it will go to quirks mode.
+ // We only expect the doctype check to apply to html documents.
+ return mime_type == "text/html";
+}
+
+bool BufferedResourceHandler::DidBufferEnough(int bytes_read) {
+ const int kRequiredLength = 256;
+
+ return bytes_read >= kRequiredLength;
+}
+
+bool BufferedResourceHandler::KeepBuffering(int bytes_read) {
+ DCHECK(read_buffer_);
+ if (my_buffer_) {
+ // We are using our own buffer to read, update the main buffer.
+ // TODO(darin): We should handle the case where read_buffer_size_ is small!
+ // See RedirectToFileResourceHandler::BufIsFull to see how this impairs
+ // downstream ResourceHandler implementations.
+ CHECK_LT(bytes_read + bytes_read_, read_buffer_size_);
+ memcpy(read_buffer_->data() + bytes_read_, my_buffer_->data(), bytes_read);
+ my_buffer_ = NULL;
+ }
+ bytes_read_ += bytes_read;
+ finished_ = (bytes_read == 0);
+
+ if (sniff_content_) {
+ std::string type_hint, new_type;
+ request_->GetMimeType(&type_hint);
+
+ if (!net::SniffMimeType(read_buffer_->data(), bytes_read_,
+ request_->url(), type_hint, &new_type)) {
+ // SniffMimeType() returns false if there is not enough data to determine
+ // the mime type. However, even if it returns false, it returns a new type
+ // that is probably better than the current one.
+ DCHECK_LT(bytes_read_, net::kMaxBytesToSniff);
+ if (!finished_) {
+ buffering_ = true;
+ return true;
+ }
+ }
+ sniff_content_ = false;
+ response_->response_head.mime_type.assign(new_type);
+
+ // We just sniffed the mime type, maybe there is a doctype to process.
+ if (ShouldBuffer(request_->url(), new_type)) {
+ should_buffer_ = true;
+ } else if (ShouldWaitForPlugins()) {
+ wait_for_plugins_ = true;
+ }
+ }
+
+ if (should_buffer_) {
+ if (!finished_ && !DidBufferEnough(bytes_read_)) {
+ buffering_ = true;
+ return true;
+ }
+
+ should_buffer_ = false;
+ if (ShouldWaitForPlugins())
+ wait_for_plugins_ = true;
+ }
+
+ buffering_ = false;
+
+ if (wait_for_plugins_)
+ return true;
+
+ return false;
+}
+
+bool BufferedResourceHandler::CompleteResponseStarted(int request_id,
+ bool in_complete) {
+ ResourceDispatcherHostRequestInfo* info =
+ ResourceDispatcherHost::InfoForRequest(request_);
+ std::string mime_type;
+ request_->GetMimeType(&mime_type);
+
+ // Check if this is an X.509 certificate, if yes, let it be handled
+ // by X509UserCertResourceHandler.
+ if (mime_type == "application/x-x509-user-cert") {
+ // This is entirely similar to how DownloadThrottlingResourceHandler
+ // works except we are doing it for an X.509 client certificates.
+
+ if (response_->response_head.headers && // Can be NULL if FTP.
+ response_->response_head.headers->response_code() / 100 != 2) {
+ // The response code indicates that this is an error page, but we are
+ // expecting an X.509 user certificate. We follow Firefox here and show
+ // our own error page instead of handling the error page as a
+ // certificate.
+ // TODO(abarth): We should abstract the response_code test, but this kind
+ // of check is scattered throughout our codebase.
+ request_->SimulateError(net::ERR_FILE_NOT_FOUND);
+ return false;
+ }
+
+ X509UserCertResourceHandler* x509_cert_handler =
+ new X509UserCertResourceHandler(host_, request_,
+ info->child_id(), info->route_id());
+ UseAlternateResourceHandler(request_id, x509_cert_handler);
+ }
+
+ // Check to see if we should forward the data from this request to the
+ // download thread.
+ // TODO(paulg): Only download if the context from the renderer allows it.
+ if (info->allow_download() && ShouldDownload(NULL)) {
+ if (response_->response_head.headers && // Can be NULL if FTP.
+ response_->response_head.headers->response_code() / 100 != 2) {
+ // The response code indicates that this is an error page, but we don't
+ // know how to display the content. We follow Firefox here and show our
+ // own error page instead of triggering a download.
+ // TODO(abarth): We should abstract the response_code test, but this kind
+ // of check is scattered throughout our codebase.
+ request_->SimulateError(net::ERR_FILE_NOT_FOUND);
+ return false;
+ }
+
+ info->set_is_download(true);
+
+ DownloadThrottlingResourceHandler* download_handler =
+ new DownloadThrottlingResourceHandler(host_,
+ request_,
+ request_->url(),
+ info->child_id(),
+ info->route_id(),
+ request_id,
+ in_complete);
+ UseAlternateResourceHandler(request_id, download_handler);
+ }
+ return real_handler_->OnResponseStarted(request_id, response_);
+}
+
+bool BufferedResourceHandler::ShouldWaitForPlugins() {
+ bool need_plugin_list;
+ if (!ShouldDownload(&need_plugin_list) || !need_plugin_list)
+ return false;
+
+ // We don't want to keep buffering as our buffer will fill up.
+ ResourceDispatcherHostRequestInfo* info =
+ ResourceDispatcherHost::InfoForRequest(request_);
+ host_->PauseRequest(info->child_id(), info->request_id(), true);
+
+ // Schedule plugin loading on the file thread.
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(this, &BufferedResourceHandler::LoadPlugins));
+ return true;
+}
+
+// This test mirrors the decision that WebKit makes in
+// WebFrameLoaderClient::dispatchDecidePolicyForMIMEType.
+bool BufferedResourceHandler::ShouldDownload(bool* need_plugin_list) {
+ if (need_plugin_list)
+ *need_plugin_list = false;
+ std::string type = StringToLowerASCII(response_->response_head.mime_type);
+ std::string disposition;
+ request_->GetResponseHeaderByName("content-disposition", &disposition);
+ disposition = StringToLowerASCII(disposition);
+
+ // First, examine content-disposition.
+ if (!disposition.empty()) {
+ bool should_download = true;
+
+ // Some broken sites just send ...
+ // Content-Disposition: ; filename="file"
+ // ... screen those out here.
+ if (disposition[0] == ';')
+ should_download = false;
+
+ if (disposition.compare(0, 6, "inline") == 0)
+ should_download = false;
+
+ // Some broken sites just send ...
+ // Content-Disposition: filename="file"
+ // ... without a disposition token... Screen those out.
+ if (disposition.compare(0, 8, "filename") == 0)
+ should_download = false;
+
+ // Also in use is Content-Disposition: name="file"
+ if (disposition.compare(0, 4, "name") == 0)
+ should_download = false;
+
+ // We have a content-disposition of "attachment" or unknown.
+ // RFC 2183, section 2.8 says that an unknown disposition
+ // value should be treated as "attachment".
+ if (should_download)
+ return true;
+ }
+
+ // Special-case user scripts to get downloaded instead of viewed.
+ if (UserScript::HasUserScriptFileExtension(request_->url()))
+ return true;
+
+ // MIME type checking.
+ if (net::IsSupportedMimeType(type))
+ return false;
+
+ if (need_plugin_list) {
+ if (!webkit::npapi::PluginList::Singleton()->PluginsLoaded()) {
+ *need_plugin_list = true;
+ return true;
+ }
+ } else {
+ DCHECK(webkit::npapi::PluginList::Singleton()->PluginsLoaded());
+ }
+
+ // Finally, check the plugin list.
+ webkit::npapi::WebPluginInfo info;
+ bool allow_wildcard = false;
+ return !webkit::npapi::PluginList::Singleton()->GetPluginInfo(
+ GURL(), type, allow_wildcard, &info, NULL) ||
+ !webkit::npapi::IsPluginEnabled(info);
+}
+
+void BufferedResourceHandler::UseAlternateResourceHandler(
+ int request_id,
+ ResourceHandler* handler) {
+ ResourceDispatcherHostRequestInfo* info =
+ ResourceDispatcherHost::InfoForRequest(request_);
+ if (bytes_read_) {
+ // A Read has already occured and we need to copy the data into the new
+ // ResourceHandler.
+ net::IOBuffer* buf = NULL;
+ int buf_len = 0;
+ handler->OnWillRead(request_id, &buf, &buf_len, bytes_read_);
+ CHECK((buf_len >= bytes_read_) && (bytes_read_ >= 0));
+ memcpy(buf->data(), read_buffer_->data(), bytes_read_);
+ }
+
+ // Inform the original ResourceHandler that this will be handled entirely by
+ // the new ResourceHandler.
+ real_handler_->OnResponseStarted(info->request_id(), response_);
+ net::URLRequestStatus status(net::URLRequestStatus::HANDLED_EXTERNALLY, 0);
+ real_handler_->OnResponseCompleted(info->request_id(), status, std::string());
+
+ // Remove the non-owning pointer to the CrossSiteResourceHandler, if any,
+ // from the extra request info because the CrossSiteResourceHandler (part of
+ // the original ResourceHandler chain) will be deleted by the next statement.
+ info->set_cross_site_handler(NULL);
+
+ // This is handled entirely within the new ResourceHandler, so just reset the
+ // original ResourceHandler.
+ real_handler_ = handler;
+}
+
+void BufferedResourceHandler::LoadPlugins() {
+ std::vector<webkit::npapi::WebPluginInfo> plugins;
+ webkit::npapi::PluginList::Singleton()->GetPlugins(false, &plugins);
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ NewRunnableMethod(this, &BufferedResourceHandler::OnPluginsLoaded));
+}
+
+void BufferedResourceHandler::OnPluginsLoaded() {
+ wait_for_plugins_ = false;
+ if (!request_)
+ return;
+
+ ResourceDispatcherHostRequestInfo* info =
+ ResourceDispatcherHost::InfoForRequest(request_);
+ host_->PauseRequest(info->child_id(), info->request_id(), false);
+ if (!CompleteResponseStarted(info->request_id(), false))
+ host_->CancelRequest(info->child_id(), info->request_id(), false);
+}
diff --git a/content/browser/renderer_host/buffered_resource_handler.h b/content/browser/renderer_host/buffered_resource_handler.h
new file mode 100644
index 0000000..ab5393e
--- /dev/null
+++ b/content/browser/renderer_host/buffered_resource_handler.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_BUFFERED_RESOURCE_HANDLER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_BUFFERED_RESOURCE_HANDLER_H_
+#pragma once
+
+#include <string>
+
+#include "content/browser/renderer_host/resource_handler.h"
+
+class MessageLoop;
+class ResourceDispatcherHost;
+
+namespace net {
+class URLRequest;
+} // namespace net
+
+// Used to buffer a request until enough data has been received.
+class BufferedResourceHandler : public ResourceHandler {
+ public:
+ BufferedResourceHandler(ResourceHandler* handler,
+ ResourceDispatcherHost* host,
+ net::URLRequest* request);
+
+ // ResourceHandler implementation:
+ virtual bool OnUploadProgress(int request_id, uint64 position, uint64 size);
+ virtual bool OnRequestRedirected(int request_id, const GURL& new_url,
+ ResourceResponse* response, bool* defer);
+ virtual bool OnResponseStarted(int request_id, ResourceResponse* response);
+ virtual bool OnWillStart(int request_id, const GURL& url, bool* defer);
+ virtual bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size,
+ int min_size);
+ virtual bool OnReadCompleted(int request_id, int* bytes_read);
+ virtual bool OnResponseCompleted(int request_id,
+ const net::URLRequestStatus& status,
+ const std::string& security_info);
+ virtual void OnRequestClosed();
+
+ private:
+ virtual ~BufferedResourceHandler();
+
+ // Returns true if we should delay OnResponseStarted forwarding.
+ bool DelayResponse();
+
+ // Returns true if there will be a need to parse the DocType of the document
+ // to determine the right way to handle it.
+ bool ShouldBuffer(const GURL& url, const std::string& mime_type);
+
+ // Returns true if there is enough information to process the DocType.
+ bool DidBufferEnough(int bytes_read);
+
+ // Returns true if we have to keep buffering data.
+ bool KeepBuffering(int bytes_read);
+
+ // Sends a pending OnResponseStarted notification. |in_complete| is true if
+ // this is invoked from |OnResponseCompleted|.
+ bool CompleteResponseStarted(int request_id, bool in_complete);
+
+ // Returns true if we have to wait until the plugin list is generated.
+ bool ShouldWaitForPlugins();
+
+ // A test to determining whether the request should be forwarded to the
+ // download thread. If need_plugin_list was passed in and was set to true,
+ // that means that the check couldn't be fully done because the plugins aren't
+ // loaded. The function should be called again after the plugin list is
+ // loaded.
+ bool ShouldDownload(bool* need_plugin_list);
+
+ // Informs the original ResourceHandler |real_handler_| that the response will
+ // be handled entirely by the new ResourceHandler |handler|.
+ // A reference to |handler| is acquired.
+ void UseAlternateResourceHandler(int request_id, ResourceHandler* handler);
+
+ // Called on the file thread to load the list of plugins.
+ void LoadPlugins();
+
+ // Called on the IO thread once the list of plugins has been loaded.
+ void OnPluginsLoaded();
+
+ scoped_refptr<ResourceHandler> real_handler_;
+ scoped_refptr<ResourceResponse> response_;
+ ResourceDispatcherHost* host_;
+ net::URLRequest* request_;
+ scoped_refptr<net::IOBuffer> read_buffer_;
+ scoped_refptr<net::IOBuffer> my_buffer_;
+ int read_buffer_size_;
+ int bytes_read_;
+ bool sniff_content_;
+ bool should_buffer_;
+ bool wait_for_plugins_;
+ bool buffering_;
+ bool finished_;
+
+ DISALLOW_COPY_AND_ASSIGN(BufferedResourceHandler);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_BUFFERED_RESOURCE_HANDLER_H_
diff --git a/content/browser/renderer_host/cross_site_resource_handler.cc b/content/browser/renderer_host/cross_site_resource_handler.cc
new file mode 100644
index 0000000..ceb5554
--- /dev/null
+++ b/content/browser/renderer_host/cross_site_resource_handler.cc
@@ -0,0 +1,219 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string>
+
+#include "content/browser/renderer_host/cross_site_resource_handler.h"
+
+#include "base/logging.h"
+#include "content/browser/renderer_host/global_request_id.h"
+#include "content/browser/renderer_host/render_view_host.h"
+#include "content/browser/renderer_host/render_view_host_delegate.h"
+#include "content/browser/renderer_host/render_view_host_notification_task.h"
+#include "content/browser/renderer_host/resource_dispatcher_host.h"
+#include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
+#include "net/base/io_buffer.h"
+
+CrossSiteResourceHandler::CrossSiteResourceHandler(
+ ResourceHandler* handler,
+ int render_process_host_id,
+ int render_view_id,
+ ResourceDispatcherHost* resource_dispatcher_host)
+ : next_handler_(handler),
+ render_process_host_id_(render_process_host_id),
+ render_view_id_(render_view_id),
+ has_started_response_(false),
+ in_cross_site_transition_(false),
+ request_id_(-1),
+ completed_during_transition_(false),
+ completed_status_(),
+ response_(NULL),
+ rdh_(resource_dispatcher_host) {}
+
+bool CrossSiteResourceHandler::OnUploadProgress(int request_id,
+ uint64 position,
+ uint64 size) {
+ return next_handler_->OnUploadProgress(request_id, position, size);
+}
+
+bool CrossSiteResourceHandler::OnRequestRedirected(int request_id,
+ const GURL& new_url,
+ ResourceResponse* response,
+ bool* defer) {
+ // We should not have started the transition before being redirected.
+ DCHECK(!in_cross_site_transition_);
+ return next_handler_->OnRequestRedirected(
+ request_id, new_url, response, defer);
+}
+
+bool CrossSiteResourceHandler::OnResponseStarted(int request_id,
+ ResourceResponse* response) {
+ // At this point, we know that the response is safe to send back to the
+ // renderer: it is not a download, and it has passed the SSL and safe
+ // browsing checks.
+ // We should not have already started the transition before now.
+ DCHECK(!in_cross_site_transition_);
+ has_started_response_ = true;
+
+ // Look up the request and associated info.
+ GlobalRequestID global_id(render_process_host_id_, request_id);
+ net::URLRequest* request = rdh_->GetURLRequest(global_id);
+ if (!request) {
+ DLOG(WARNING) << "Request wasn't found";
+ return false;
+ }
+ ResourceDispatcherHostRequestInfo* info =
+ ResourceDispatcherHost::InfoForRequest(request);
+
+ // If this is a download, just pass the response through without doing a
+ // cross-site check. The renderer will see it is a download and abort the
+ // request.
+ if (info->is_download()) {
+ return next_handler_->OnResponseStarted(request_id, response);
+ }
+
+ // Tell the renderer to run the onunload event handler, and wait for the
+ // reply.
+ StartCrossSiteTransition(request_id, response, global_id);
+ return true;
+}
+
+bool CrossSiteResourceHandler::OnWillStart(int request_id,
+ const GURL& url,
+ bool* defer) {
+ return next_handler_->OnWillStart(request_id, url, defer);
+}
+
+bool CrossSiteResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf,
+ int* buf_size, int min_size) {
+ return next_handler_->OnWillRead(request_id, buf, buf_size, min_size);
+}
+
+bool CrossSiteResourceHandler::OnReadCompleted(int request_id,
+ int* bytes_read) {
+ if (!in_cross_site_transition_) {
+ return next_handler_->OnReadCompleted(request_id, bytes_read);
+ }
+ return true;
+}
+
+bool CrossSiteResourceHandler::OnResponseCompleted(
+ int request_id,
+ const net::URLRequestStatus& status,
+ const std::string& security_info) {
+ if (!in_cross_site_transition_) {
+ if (has_started_response_) {
+ // We've already completed the transition, so just pass it through.
+ return next_handler_->OnResponseCompleted(request_id, status,
+ security_info);
+ } else {
+ // An error occured, we should wait now for the cross-site transition,
+ // so that the error message (e.g., 404) can be displayed to the user.
+ // Also continue with the logic below to remember that we completed
+ // during the cross-site transition.
+ GlobalRequestID global_id(render_process_host_id_, request_id);
+ StartCrossSiteTransition(request_id, NULL, global_id);
+ }
+ }
+
+ // We have to buffer the call until after the transition completes.
+ completed_during_transition_ = true;
+ completed_status_ = status;
+ completed_security_info_ = security_info;
+
+ // Return false to tell RDH not to notify the world or clean up the
+ // pending request. We will do so in ResumeResponse.
+ return false;
+}
+
+void CrossSiteResourceHandler::OnRequestClosed() {
+ next_handler_->OnRequestClosed();
+}
+
+// We can now send the response to the new renderer, which will cause
+// TabContents to swap in the new renderer and destroy the old one.
+void CrossSiteResourceHandler::ResumeResponse() {
+ DCHECK(request_id_ != -1);
+ DCHECK(in_cross_site_transition_);
+ in_cross_site_transition_ = false;
+
+ // Find the request for this response.
+ GlobalRequestID global_id(render_process_host_id_, request_id_);
+ net::URLRequest* request = rdh_->GetURLRequest(global_id);
+ if (!request) {
+ DLOG(WARNING) << "Resuming a request that wasn't found";
+ return;
+ }
+
+ if (has_started_response_) {
+ // Send OnResponseStarted to the new renderer.
+ DCHECK(response_);
+ next_handler_->OnResponseStarted(request_id_, response_);
+
+ // Unpause the request to resume reading. Any further reads will be
+ // directed toward the new renderer.
+ rdh_->PauseRequest(render_process_host_id_, request_id_, false);
+ }
+
+ // Remove ourselves from the ExtraRequestInfo.
+ ResourceDispatcherHostRequestInfo* info =
+ ResourceDispatcherHost::InfoForRequest(request);
+ info->set_cross_site_handler(NULL);
+
+ // If the response completed during the transition, notify the next
+ // event handler.
+ if (completed_during_transition_) {
+ next_handler_->OnResponseCompleted(request_id_, completed_status_,
+ completed_security_info_);
+
+ // Since we didn't notify the world or clean up the pending request in
+ // RDH::OnResponseCompleted during the transition, we should do it now.
+ rdh_->NotifyResponseCompleted(request, render_process_host_id_);
+ rdh_->RemovePendingRequest(render_process_host_id_, request_id_);
+ }
+}
+
+CrossSiteResourceHandler::~CrossSiteResourceHandler() {}
+
+// Prepare to render the cross-site response in a new RenderViewHost, by
+// telling the old RenderViewHost to run its onunload handler.
+void CrossSiteResourceHandler::StartCrossSiteTransition(
+ int request_id,
+ ResourceResponse* response,
+ const GlobalRequestID& global_id) {
+ in_cross_site_transition_ = true;
+ request_id_ = request_id;
+ response_ = response;
+
+ // Store this handler on the ExtraRequestInfo, so that RDH can call our
+ // ResumeResponse method when the close ACK is received.
+ net::URLRequest* request = rdh_->GetURLRequest(global_id);
+ if (!request) {
+ DLOG(WARNING) << "Cross site response for a request that wasn't found";
+ return;
+ }
+ ResourceDispatcherHostRequestInfo* info =
+ ResourceDispatcherHost::InfoForRequest(request);
+ info->set_cross_site_handler(this);
+
+ if (has_started_response_) {
+ // Pause the request until the old renderer is finished and the new
+ // renderer is ready.
+ rdh_->PauseRequest(render_process_host_id_, request_id, true);
+ }
+ // If our OnResponseStarted wasn't called, then we're being called by
+ // OnResponseCompleted after a failure. We don't need to pause, because
+ // there will be no reads.
+
+ // Tell the tab responsible for this request that a cross-site response is
+ // starting, so that it can tell its old renderer to run its onunload
+ // handler now. We will wait to hear the corresponding ClosePage_ACK.
+ CallRenderViewHostRendererManagementDelegate(
+ render_process_host_id_, render_view_id_,
+ &RenderViewHostDelegate::RendererManagement::OnCrossSiteResponse,
+ render_process_host_id_, request_id);
+
+ // TODO(creis): If the above call should fail, then we need to notify the IO
+ // thread to proceed anyway, using ResourceDispatcherHost::OnClosePageACK.
+}
diff --git a/content/browser/renderer_host/cross_site_resource_handler.h b/content/browser/renderer_host/cross_site_resource_handler.h
new file mode 100644
index 0000000..9fee1b9
--- /dev/null
+++ b/content/browser/renderer_host/cross_site_resource_handler.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_CROSS_SITE_RESOURCE_HANDLER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_CROSS_SITE_RESOURCE_HANDLER_H_
+#pragma once
+
+#include "content/browser/renderer_host/resource_handler.h"
+#include "net/url_request/url_request_status.h"
+
+class ResourceDispatcherHost;
+struct GlobalRequestID;
+
+// Ensures that cross-site responses are delayed until the onunload handler of
+// the previous page is allowed to run. This handler wraps an
+// AsyncEventHandler, and it sits inside SafeBrowsing and Buffered event
+// handlers. This is important, so that it can intercept OnResponseStarted
+// after we determine that a response is safe and not a download.
+class CrossSiteResourceHandler : public ResourceHandler {
+ public:
+ CrossSiteResourceHandler(ResourceHandler* handler,
+ int render_process_host_id,
+ int render_view_id,
+ ResourceDispatcherHost* resource_dispatcher_host);
+
+ // ResourceHandler implementation:
+ virtual bool OnUploadProgress(int request_id, uint64 position, uint64 size);
+ virtual bool OnRequestRedirected(int request_id, const GURL& new_url,
+ ResourceResponse* response, bool* defer);
+ virtual bool OnResponseStarted(int request_id,
+ ResourceResponse* response);
+ virtual bool OnWillStart(int request_id, const GURL& url, bool* defer);
+ virtual bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size,
+ int min_size);
+ virtual bool OnReadCompleted(int request_id, int* bytes_read);
+ virtual bool OnResponseCompleted(int request_id,
+ const net::URLRequestStatus& status,
+ const std::string& security_info);
+ virtual void OnRequestClosed();
+
+ // We can now send the response to the new renderer, which will cause
+ // TabContents to swap in the new renderer and destroy the old one.
+ void ResumeResponse();
+
+ private:
+ virtual ~CrossSiteResourceHandler();
+
+ // Prepare to render the cross-site response in a new RenderViewHost, by
+ // telling the old RenderViewHost to run its onunload handler.
+ void StartCrossSiteTransition(
+ int request_id,
+ ResourceResponse* response,
+ const GlobalRequestID& global_id);
+
+ scoped_refptr<ResourceHandler> next_handler_;
+ int render_process_host_id_;
+ int render_view_id_;
+ bool has_started_response_;
+ bool in_cross_site_transition_;
+ int request_id_;
+ bool completed_during_transition_;
+ net::URLRequestStatus completed_status_;
+ std::string completed_security_info_;
+ ResourceResponse* response_;
+ ResourceDispatcherHost* rdh_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrossSiteResourceHandler);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_CROSS_SITE_RESOURCE_HANDLER_H_
diff --git a/content/browser/renderer_host/database_message_filter.cc b/content/browser/renderer_host/database_message_filter.cc
new file mode 100644
index 0000000..1e8cdb0
--- /dev/null
+++ b/content/browser/renderer_host/database_message_filter.cc
@@ -0,0 +1,323 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/database_message_filter.h"
+
+#include <string>
+
+#include "base/string_util.h"
+#include "base/threading/thread.h"
+#include "chrome/browser/content_settings/host_content_settings_map.h"
+#include "chrome/browser/metrics/user_metrics.h"
+#include "chrome/browser/net/chrome_url_request_context.h"
+#include "chrome/common/database_messages.h"
+#include "chrome/common/result_codes.h"
+#include "googleurl/src/gurl.h"
+#include "third_party/sqlite/sqlite3.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebSecurityOrigin.h"
+#include "webkit/database/database_util.h"
+#include "webkit/database/vfs_backend.h"
+
+#if defined(OS_POSIX)
+#include "base/file_descriptor_posix.h"
+#endif
+
+using WebKit::WebSecurityOrigin;
+using webkit_database::DatabaseTracker;
+using webkit_database::DatabaseUtil;
+using webkit_database::VfsBackend;
+
+const int kNumDeleteRetries = 2;
+const int kDelayDeleteRetryMs = 100;
+
+DatabaseMessageFilter::DatabaseMessageFilter(
+ webkit_database::DatabaseTracker* db_tracker,
+ HostContentSettingsMap *host_content_settings_map)
+ : db_tracker_(db_tracker),
+ observer_added_(false),
+ host_content_settings_map_(host_content_settings_map) {
+ DCHECK(db_tracker_);
+}
+
+void DatabaseMessageFilter::OnChannelClosing() {
+ BrowserMessageFilter::OnChannelClosing();
+ if (observer_added_) {
+ observer_added_ = false;
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(this, &DatabaseMessageFilter::RemoveObserver));
+ }
+}
+
+void DatabaseMessageFilter::AddObserver() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ db_tracker_->AddObserver(this);
+}
+
+void DatabaseMessageFilter::RemoveObserver() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ // If the renderer process died without closing all databases,
+ // then we need to manually close those connections
+ db_tracker_->CloseDatabases(database_connections_);
+ database_connections_.RemoveAllConnections();
+
+ db_tracker_->RemoveObserver(this);
+}
+
+void DatabaseMessageFilter::OverrideThreadForMessage(
+ const IPC::Message& message,
+ BrowserThread::ID* thread) {
+ if (IPC_MESSAGE_CLASS(message) == DatabaseMsgStart &&
+ message.type() != DatabaseHostMsg_Allow::ID) {
+ *thread = BrowserThread::FILE;
+ }
+
+ if (message.type() == DatabaseHostMsg_OpenFile::ID && !observer_added_) {
+ observer_added_ = true;
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(this, &DatabaseMessageFilter::AddObserver));
+ }
+}
+
+bool DatabaseMessageFilter::OnMessageReceived(
+ const IPC::Message& message,
+ bool* message_was_ok) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(DatabaseMessageFilter, message, *message_was_ok)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_OpenFile,
+ OnDatabaseOpenFile)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_DeleteFile,
+ OnDatabaseDeleteFile)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetFileAttributes,
+ OnDatabaseGetFileAttributes)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_GetFileSize,
+ OnDatabaseGetFileSize)
+ IPC_MESSAGE_HANDLER(DatabaseHostMsg_Opened, OnDatabaseOpened)
+ IPC_MESSAGE_HANDLER(DatabaseHostMsg_Modified, OnDatabaseModified)
+ IPC_MESSAGE_HANDLER(DatabaseHostMsg_Closed, OnDatabaseClosed)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(DatabaseHostMsg_Allow, OnAllowDatabase)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP_EX()
+ return handled;
+}
+
+DatabaseMessageFilter::~DatabaseMessageFilter() {
+}
+
+void DatabaseMessageFilter::OnDatabaseOpenFile(const string16& vfs_file_name,
+ int desired_flags,
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ base::PlatformFile file_handle = base::kInvalidPlatformFileValue;
+ base::PlatformFile target_handle = base::kInvalidPlatformFileValue;
+ string16 origin_identifier;
+ string16 database_name;
+
+ // When in incognito mode, we want to make sure that all DB files are
+ // removed when the incognito profile goes away, so we add the
+ // SQLITE_OPEN_DELETEONCLOSE flag when opening all files, and keep
+ // open handles to them in the database tracker to make sure they're
+ // around for as long as needed.
+ if (vfs_file_name.empty()) {
+ VfsBackend::OpenTempFileInDirectory(db_tracker_->DatabaseDirectory(),
+ desired_flags, &file_handle);
+ } else if (DatabaseUtil::CrackVfsFileName(vfs_file_name, &origin_identifier,
+ &database_name, NULL) &&
+ !db_tracker_->IsDatabaseScheduledForDeletion(origin_identifier,
+ database_name)) {
+ FilePath db_file =
+ DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_, vfs_file_name);
+ if (!db_file.empty()) {
+ if (db_tracker_->IsIncognitoProfile()) {
+ db_tracker_->GetIncognitoFileHandle(vfs_file_name, &file_handle);
+ if (file_handle == base::kInvalidPlatformFileValue) {
+ VfsBackend::OpenFile(db_file,
+ desired_flags | SQLITE_OPEN_DELETEONCLOSE,
+ &file_handle);
+ if (VfsBackend::FileTypeIsMainDB(desired_flags) ||
+ VfsBackend::FileTypeIsJournal(desired_flags))
+ db_tracker_->SaveIncognitoFileHandle(vfs_file_name, file_handle);
+ }
+ } else {
+ VfsBackend::OpenFile(db_file, desired_flags, &file_handle);
+ }
+ }
+ }
+
+ // Then we duplicate the file handle to make it useable in the renderer
+ // process. The original handle is closed, unless we saved it in the
+ // database tracker.
+ bool auto_close = !db_tracker_->HasSavedIncognitoFileHandle(vfs_file_name);
+ VfsBackend::GetFileHandleForProcess(peer_handle(), file_handle,
+ &target_handle, auto_close);
+
+ DatabaseHostMsg_OpenFile::WriteReplyParams(
+ reply_msg,
+#if defined(OS_WIN)
+ target_handle
+#elif defined(OS_POSIX)
+ base::FileDescriptor(target_handle, auto_close)
+#endif
+ );
+ Send(reply_msg);
+}
+
+void DatabaseMessageFilter::OnDatabaseDeleteFile(const string16& vfs_file_name,
+ const bool& sync_dir,
+ IPC::Message* reply_msg) {
+ DatabaseDeleteFile(vfs_file_name, sync_dir, reply_msg, kNumDeleteRetries);
+}
+
+void DatabaseMessageFilter::DatabaseDeleteFile(const string16& vfs_file_name,
+ bool sync_dir,
+ IPC::Message* reply_msg,
+ int reschedule_count) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ // Return an error if the file name is invalid or if the file could not
+ // be deleted after kNumDeleteRetries attempts.
+ int error_code = SQLITE_IOERR_DELETE;
+ FilePath db_file =
+ DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_, vfs_file_name);
+ if (!db_file.empty()) {
+ // In order to delete a journal file in incognito mode, we only need to
+ // close the open handle to it that's stored in the database tracker.
+ if (db_tracker_->IsIncognitoProfile()) {
+ if (db_tracker_->CloseIncognitoFileHandle(vfs_file_name))
+ error_code = SQLITE_OK;
+ } else {
+ error_code = VfsBackend::DeleteFile(db_file, sync_dir);
+ }
+
+ if ((error_code == SQLITE_IOERR_DELETE) && reschedule_count) {
+ // If the file could not be deleted, try again.
+ BrowserThread::PostDelayedTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(this,
+ &DatabaseMessageFilter::DatabaseDeleteFile,
+ vfs_file_name,
+ sync_dir,
+ reply_msg,
+ reschedule_count - 1),
+ kDelayDeleteRetryMs);
+ return;
+ }
+ }
+
+ DatabaseHostMsg_DeleteFile::WriteReplyParams(reply_msg, error_code);
+ Send(reply_msg);
+}
+
+void DatabaseMessageFilter::OnDatabaseGetFileAttributes(
+ const string16& vfs_file_name,
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ int32 attributes = -1;
+ FilePath db_file =
+ DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_, vfs_file_name);
+ if (!db_file.empty())
+ attributes = VfsBackend::GetFileAttributes(db_file);
+
+ DatabaseHostMsg_GetFileAttributes::WriteReplyParams(
+ reply_msg, attributes);
+ Send(reply_msg);
+}
+
+void DatabaseMessageFilter::OnDatabaseGetFileSize(
+ const string16& vfs_file_name, IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ int64 size = 0;
+ FilePath db_file =
+ DatabaseUtil::GetFullFilePathForVfsFile(db_tracker_, vfs_file_name);
+ if (!db_file.empty())
+ size = VfsBackend::GetFileSize(db_file);
+
+ DatabaseHostMsg_GetFileSize::WriteReplyParams(reply_msg, size);
+ Send(reply_msg);
+}
+
+void DatabaseMessageFilter::OnDatabaseOpened(const string16& origin_identifier,
+ const string16& database_name,
+ const string16& description,
+ int64 estimated_size) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ int64 database_size = 0;
+ int64 space_available = 0;
+ database_connections_.AddConnection(origin_identifier, database_name);
+ db_tracker_->DatabaseOpened(origin_identifier, database_name, description,
+ estimated_size, &database_size, &space_available);
+ Send(new DatabaseMsg_UpdateSize(origin_identifier, database_name,
+ database_size, space_available));
+}
+
+void DatabaseMessageFilter::OnDatabaseModified(
+ const string16& origin_identifier,
+ const string16& database_name) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ if (!database_connections_.IsDatabaseOpened(
+ origin_identifier, database_name)) {
+ UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_DBMF"));
+ BadMessageReceived();
+ return;
+ }
+
+ db_tracker_->DatabaseModified(origin_identifier, database_name);
+}
+
+void DatabaseMessageFilter::OnDatabaseClosed(const string16& origin_identifier,
+ const string16& database_name) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ if (!database_connections_.IsDatabaseOpened(
+ origin_identifier, database_name)) {
+ UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_DBMF"));
+ BadMessageReceived();
+ return;
+ }
+
+ db_tracker_->DatabaseClosed(origin_identifier, database_name);
+ database_connections_.RemoveConnection(origin_identifier, database_name);
+}
+
+void DatabaseMessageFilter::OnAllowDatabase(const std::string& origin_url,
+ const string16& name,
+ const string16& display_name,
+ unsigned long estimated_size,
+ IPC::Message* reply_msg) {
+ GURL url = GURL(origin_url);
+ ContentSetting content_setting =
+ host_content_settings_map_->GetContentSetting(
+ url, CONTENT_SETTINGS_TYPE_COOKIES, "");
+ AllowDatabaseResponse(reply_msg, content_setting);
+}
+
+void DatabaseMessageFilter::AllowDatabaseResponse(
+ IPC::Message* reply_msg, ContentSetting content_setting) {
+ DCHECK((content_setting == CONTENT_SETTING_ALLOW) ||
+ (content_setting == CONTENT_SETTING_BLOCK) ||
+ (content_setting == CONTENT_SETTING_SESSION_ONLY));
+ DatabaseHostMsg_Allow::WriteReplyParams(
+ reply_msg, content_setting != CONTENT_SETTING_BLOCK);
+ Send(reply_msg);
+}
+
+void DatabaseMessageFilter::OnDatabaseSizeChanged(
+ const string16& origin_identifier,
+ const string16& database_name,
+ int64 database_size,
+ int64 space_available) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ if (database_connections_.IsOriginUsed(origin_identifier)) {
+ Send(new DatabaseMsg_UpdateSize(origin_identifier, database_name,
+ database_size, space_available));
+ }
+}
+
+void DatabaseMessageFilter::OnDatabaseScheduledForDeletion(
+ const string16& origin_identifier,
+ const string16& database_name) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ Send(new DatabaseMsg_CloseImmediately(origin_identifier, database_name));
+}
diff --git a/content/browser/renderer_host/database_message_filter.h b/content/browser/renderer_host/database_message_filter.h
new file mode 100644
index 0000000..7d432e8
--- /dev/null
+++ b/content/browser/renderer_host/database_message_filter.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_DATABASE_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_DATABASE_MESSAGE_FILTER_H_
+#pragma once
+
+#include "base/hash_tables.h"
+#include "base/string16.h"
+#include "chrome/browser/browser_message_filter.h"
+#include "chrome/common/content_settings.h"
+#include "webkit/database/database_connections.h"
+#include "webkit/database/database_tracker.h"
+
+class HostContentSettingsMap;
+
+class DatabaseMessageFilter
+ : public BrowserMessageFilter,
+ public webkit_database::DatabaseTracker::Observer {
+ public:
+ DatabaseMessageFilter(
+ webkit_database::DatabaseTracker* db_tracker,
+ HostContentSettingsMap *host_content_settings_map);
+
+ // BrowserMessageFilter implementation.
+ virtual void OnChannelClosing();
+ virtual void OverrideThreadForMessage(const IPC::Message& message,
+ BrowserThread::ID* thread);
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok);
+
+ webkit_database::DatabaseTracker* database_tracker() const {
+ return db_tracker_.get();
+ }
+
+ private:
+ virtual ~DatabaseMessageFilter();
+
+ class PromptDelegate;
+
+ void AddObserver();
+ void RemoveObserver();
+
+ // VFS message handlers (file thread)
+ void OnDatabaseOpenFile(const string16& vfs_file_name,
+ int desired_flags,
+ IPC::Message* reply_msg);
+ void OnDatabaseDeleteFile(const string16& vfs_file_name,
+ const bool& sync_dir,
+ IPC::Message* reply_msg);
+ void OnDatabaseGetFileAttributes(const string16& vfs_file_name,
+ IPC::Message* reply_msg);
+ void OnDatabaseGetFileSize(const string16& vfs_file_name,
+ IPC::Message* reply_msg);
+
+ // Database tracker message handlers (file thread)
+ void OnDatabaseOpened(const string16& origin_identifier,
+ const string16& database_name,
+ const string16& description,
+ int64 estimated_size);
+ void OnDatabaseModified(const string16& origin_identifier,
+ const string16& database_name);
+ void OnDatabaseClosed(const string16& origin_identifier,
+ const string16& database_name);
+ void OnAllowDatabase(const std::string& origin_url,
+ const string16& name,
+ const string16& display_name,
+ unsigned long estimated_size,
+ IPC::Message* reply_msg);
+
+ // DatabaseTracker::Observer callbacks (file thread)
+ virtual void OnDatabaseSizeChanged(const string16& origin_identifier,
+ const string16& database_name,
+ int64 database_size,
+ int64 space_available);
+ virtual void OnDatabaseScheduledForDeletion(const string16& origin_identifier,
+ const string16& database_name);
+
+ void DatabaseDeleteFile(const string16& vfs_file_name,
+ bool sync_dir,
+ IPC::Message* reply_msg,
+ int reschedule_count);
+
+ // CookiePromptModalDialog response handler (io thread)
+ void AllowDatabaseResponse(IPC::Message* reply_msg,
+ ContentSetting content_setting);
+
+ // The database tracker for the current profile.
+ scoped_refptr<webkit_database::DatabaseTracker> db_tracker_;
+
+ // True if and only if this instance was added as an observer
+ // to DatabaseTracker.
+ bool observer_added_;
+
+ // Keeps track of all DB connections opened by this renderer
+ webkit_database::DatabaseConnections database_connections_;
+
+ // Used to look up permissions at database creation time.
+ scoped_refptr<HostContentSettingsMap> host_content_settings_map_;
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_DATABASE_MESSAGE_FILTER_H_
diff --git a/content/browser/renderer_host/file_utilities_message_filter.cc b/content/browser/renderer_host/file_utilities_message_filter.cc
new file mode 100644
index 0000000..67377d6
--- /dev/null
+++ b/content/browser/renderer_host/file_utilities_message_filter.cc
@@ -0,0 +1,106 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/file_utilities_message_filter.h"
+
+#include "base/file_util.h"
+#include "chrome/browser/child_process_security_policy.h"
+#include "chrome/common/file_utilities_messages.h"
+
+
+FileUtilitiesMessageFilter::FileUtilitiesMessageFilter(int process_id)
+ : process_id_(process_id) {
+}
+
+FileUtilitiesMessageFilter::~FileUtilitiesMessageFilter() {
+}
+
+void FileUtilitiesMessageFilter::OverrideThreadForMessage(
+ const IPC::Message& message,
+ BrowserThread::ID* thread) {
+ if (IPC_MESSAGE_CLASS(message) == FileUtilitiesMsgStart)
+ *thread = BrowserThread::FILE;
+}
+
+bool FileUtilitiesMessageFilter::OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(FileUtilitiesMessageFilter, message, *message_was_ok)
+ IPC_MESSAGE_HANDLER(FileUtilitiesMsg_GetFileSize, OnGetFileSize)
+ IPC_MESSAGE_HANDLER(FileUtilitiesMsg_GetFileModificationTime,
+ OnGetFileModificationTime)
+ IPC_MESSAGE_HANDLER(FileUtilitiesMsg_OpenFile, OnOpenFile)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void FileUtilitiesMessageFilter::OnGetFileSize(const FilePath& path,
+ int64* result) {
+ // Get file size only when the child process has been granted permission to
+ // upload the file.
+ if (!ChildProcessSecurityPolicy::GetInstance()->CanReadFile(
+ process_id_, path)) {
+ *result = -1;
+ return;
+ }
+
+ base::PlatformFileInfo file_info;
+ file_info.size = 0;
+ file_util::GetFileInfo(path, &file_info);
+ *result = file_info.size;
+}
+
+void FileUtilitiesMessageFilter::OnGetFileModificationTime(
+ const FilePath& path, base::Time* result) {
+ // Get file modification time only when the child process has been granted
+ // permission to upload the file.
+ if (!ChildProcessSecurityPolicy::GetInstance()->CanReadFile(
+ process_id_, path)) {
+ *result = base::Time();
+ return;
+ }
+
+ base::PlatformFileInfo file_info;
+ file_info.size = 0;
+ file_util::GetFileInfo(path, &file_info);
+ *result = file_info.last_modified;
+}
+
+void FileUtilitiesMessageFilter::OnOpenFile(
+ const FilePath& path,
+ int mode,
+ IPC::PlatformFileForTransit* result) {
+ // Open the file only when the child process has been granted permission to
+ // upload the file.
+ // TODO(jianli): Do we need separate permission to control opening the file?
+ if (!ChildProcessSecurityPolicy::GetInstance()->CanReadFile(
+ process_id_, path)) {
+#if defined(OS_WIN)
+ *result = base::kInvalidPlatformFileValue;
+#elif defined(OS_POSIX)
+ *result = base::FileDescriptor(base::kInvalidPlatformFileValue, true);
+#endif
+ return;
+ }
+
+ base::PlatformFile file_handle = base::CreatePlatformFile(
+ path,
+ (mode == 0) ? (base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ)
+ : (base::PLATFORM_FILE_CREATE_ALWAYS |
+ base::PLATFORM_FILE_WRITE),
+ NULL, NULL);
+
+#if defined(OS_WIN)
+ // Duplicate the file handle so that the renderer process can access the file.
+ if (!DuplicateHandle(GetCurrentProcess(), file_handle,
+ peer_handle(), result, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ // file_handle is closed whether or not DuplicateHandle succeeds.
+ *result = INVALID_HANDLE_VALUE;
+ }
+#else
+ *result = base::FileDescriptor(file_handle, true);
+#endif
+}
diff --git a/content/browser/renderer_host/file_utilities_message_filter.h b/content/browser/renderer_host/file_utilities_message_filter.h
new file mode 100644
index 0000000..92cd2f5
--- /dev/null
+++ b/content/browser/renderer_host/file_utilities_message_filter.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_FILE_UTILITIES_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_FILE_UTILITIES_MESSAGE_FILTER_H_
+
+#include "base/basictypes.h"
+#include "base/file_path.h"
+#include "chrome/browser/browser_message_filter.h"
+#include "ipc/ipc_platform_file.h"
+
+namespace base {
+struct PlatformFileInfo;
+}
+
+namespace IPC {
+class Message;
+}
+
+class FileUtilitiesMessageFilter : public BrowserMessageFilter {
+ public:
+ explicit FileUtilitiesMessageFilter(int process_id);
+
+ // BrowserMessageFilter implementation.
+ virtual void OverrideThreadForMessage(const IPC::Message& message,
+ BrowserThread::ID* thread);
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok);
+ private:
+ ~FileUtilitiesMessageFilter();
+
+ typedef void (*FileInfoWriteFunc)(IPC::Message* reply_msg,
+ const base::PlatformFileInfo& file_info);
+
+ void OnGetFileSize(const FilePath& path, int64* result);
+ void OnGetFileModificationTime(const FilePath& path, base::Time* result);
+ void OnOpenFile(const FilePath& path,
+ int mode,
+ IPC::PlatformFileForTransit* result);
+
+ // The ID of this process.
+ int process_id_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(FileUtilitiesMessageFilter);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_FILE_UTILITIES_MESSAGE_FILTER_H_
diff --git a/content/browser/renderer_host/global_request_id.h b/content/browser/renderer_host/global_request_id.h
new file mode 100644
index 0000000..031026f
--- /dev/null
+++ b/content/browser/renderer_host/global_request_id.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_GLOBAL_REQUEST_ID_H_
+#define CONTENT_BROWSER_RENDERER_HOST_GLOBAL_REQUEST_ID_H_
+#pragma once
+
+// Uniquely identifies a net::URLRequest.
+struct GlobalRequestID {
+ GlobalRequestID() : child_id(-1), request_id(-1) {
+ }
+
+ GlobalRequestID(int child_id, int request_id)
+ : child_id(child_id),
+ request_id(request_id) {
+ }
+
+ // The unique ID of the child process (different from OS's PID).
+ int child_id;
+
+ // The request ID (unique for the child).
+ int request_id;
+
+ bool operator<(const GlobalRequestID& other) const {
+ if (child_id == other.child_id)
+ return request_id < other.request_id;
+ return child_id < other.child_id;
+ }
+};
+
+#endif // CHROME_BROWSER_RENDERER_HOST_GLOBAL_REQUEST_ID_H_
diff --git a/content/browser/renderer_host/gpu_message_filter.cc b/content/browser/renderer_host/gpu_message_filter.cc
new file mode 100644
index 0000000..157b96d
--- /dev/null
+++ b/content/browser/renderer_host/gpu_message_filter.cc
@@ -0,0 +1,151 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/gpu_message_filter.h"
+
+#include "base/callback.h"
+#include "chrome/browser/gpu_process_host_ui_shim.h"
+#include "chrome/common/gpu_create_command_buffer_config.h"
+#include "chrome/common/gpu_messages.h"
+#include "chrome/common/render_messages.h"
+
+GpuMessageFilter::GpuMessageFilter(int render_process_id)
+ : render_process_id_(render_process_id) {
+}
+
+// WeakPtrs to a GpuMessageFilter need to be Invalidated from
+// the same thread from which they were created.
+GpuMessageFilter::~GpuMessageFilter() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+}
+
+void GpuMessageFilter::OverrideThreadForMessage(
+ const IPC::Message& message,
+ BrowserThread::ID* thread) {
+ if (IPC_MESSAGE_CLASS(message) == GpuMsgStart)
+ *thread = BrowserThread::UI;
+}
+
+bool GpuMessageFilter::OnMessageReceived(
+ const IPC::Message& message,
+ bool* message_was_ok) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(GpuMessageFilter, message, *message_was_ok)
+ IPC_MESSAGE_HANDLER(GpuHostMsg_EstablishGpuChannel,
+ OnEstablishGpuChannel)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuHostMsg_SynchronizeGpu,
+ OnSynchronizeGpu)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuHostMsg_CreateViewCommandBuffer,
+ OnCreateViewCommandBuffer)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP_EX()
+ return handled;
+}
+
+void GpuMessageFilter::OnDestruct() const {
+ BrowserThread::DeleteOnUIThread::Destruct(this);
+}
+
+// Callbacks used in this file.
+namespace {
+
+class EstablishChannelCallback
+ : public CallbackRunner<Tuple2<const IPC::ChannelHandle&,
+ const GPUInfo&> > {
+ public:
+ explicit EstablishChannelCallback(GpuMessageFilter* filter):
+ filter_(filter->AsWeakPtr()) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ }
+
+ virtual void RunWithParams(const TupleType& params) {
+ DispatchToMethod(this, &EstablishChannelCallback::Send, params);
+ }
+
+ void Send(const IPC::ChannelHandle& channel,
+ const GPUInfo& gpu_info) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ ViewMsg_GpuChannelEstablished* reply =
+ new ViewMsg_GpuChannelEstablished(channel, gpu_info);
+ // If the renderer process is performing synchronous initialization,
+ // it needs to handle this message before receiving the reply for
+ // the synchronous GpuHostMsg_SynchronizeGpu message.
+ reply->set_unblock(true);
+
+ if (filter_)
+ filter_->Send(reply);
+ }
+
+ private:
+ base::WeakPtr<GpuMessageFilter> filter_;
+};
+
+class SynchronizeCallback : public CallbackRunner<Tuple0> {
+ public:
+ SynchronizeCallback(GpuMessageFilter* filter, IPC::Message* reply):
+ filter_(filter->AsWeakPtr()),
+ reply_(reply) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ }
+
+ virtual void RunWithParams(const TupleType& params) {
+ DispatchToMethod(this, &SynchronizeCallback::Send, params);
+ }
+
+ void Send() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (filter_)
+ filter_->Send(reply_);
+ }
+
+ private:
+ base::WeakPtr<GpuMessageFilter> filter_;
+ IPC::Message* reply_;
+};
+
+class CreateCommandBufferCallback : public CallbackRunner<Tuple1<int32> > {
+ public:
+ CreateCommandBufferCallback(GpuMessageFilter* filter,
+ IPC::Message* reply) :
+ filter_(filter->AsWeakPtr()),
+ reply_(reply) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ }
+
+ virtual void RunWithParams(const TupleType& params) {
+ DispatchToMethod(this, &CreateCommandBufferCallback::Send, params);
+ }
+
+ void Send(int32 route_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ GpuHostMsg_CreateViewCommandBuffer::WriteReplyParams(reply_, route_id);
+ if (filter_)
+ filter_->Send(reply_);
+ }
+
+ private:
+ base::WeakPtr<GpuMessageFilter> filter_;
+ IPC::Message* reply_;
+};
+
+} // namespace
+
+void GpuMessageFilter::OnEstablishGpuChannel() {
+ GpuProcessHostUIShim::GetInstance()->EstablishGpuChannel(
+ render_process_id_, new EstablishChannelCallback(this));
+}
+
+void GpuMessageFilter::OnSynchronizeGpu(IPC::Message* reply) {
+ GpuProcessHostUIShim::GetInstance()->
+ Synchronize(new SynchronizeCallback(this, reply));
+}
+
+void GpuMessageFilter::OnCreateViewCommandBuffer(
+ int32 render_view_id,
+ const GPUCreateCommandBufferConfig& init_params,
+ IPC::Message* reply) {
+ GpuProcessHostUIShim::GetInstance()->CreateViewCommandBuffer(
+ render_view_id, render_process_id_, init_params,
+ new CreateCommandBufferCallback(this, reply));
+}
diff --git a/content/browser/renderer_host/gpu_message_filter.h b/content/browser/renderer_host/gpu_message_filter.h
new file mode 100644
index 0000000..a81fa2a
--- /dev/null
+++ b/content/browser/renderer_host/gpu_message_filter.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_GPU_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_GPU_MESSAGE_FILTER_H_
+#pragma once
+
+#include "chrome/browser/browser_message_filter.h"
+
+struct GPUCreateCommandBufferConfig;
+class GPUInfo;
+
+namespace IPC {
+struct ChannelHandle;
+}
+
+// A message filter for messages from the renderer to the GpuProcessHost
+// in the browser. Such messages are typically destined for the GPU process,
+// but need to be mediated by the browser.
+class GpuMessageFilter : public BrowserMessageFilter,
+ public base::SupportsWeakPtr<GpuMessageFilter> {
+ public:
+ explicit GpuMessageFilter(int render_process_id);
+
+ // BrowserMessageFilter methods:
+ virtual void OverrideThreadForMessage(const IPC::Message& message,
+ BrowserThread::ID* thread);
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok);
+ virtual void OnDestruct() const;
+
+ private:
+ friend class BrowserThread;
+ friend class DeleteTask<GpuMessageFilter>;
+ virtual ~GpuMessageFilter();
+
+ // Message handlers called on the browser IO thread:
+ void OnEstablishGpuChannel();
+ void OnSynchronizeGpu(IPC::Message* reply);
+ void OnCreateViewCommandBuffer(
+ int32 render_view_id,
+ const GPUCreateCommandBufferConfig& init_params,
+ IPC::Message* reply);
+
+ int render_process_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(GpuMessageFilter);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_GPU_MESSAGE_FILTER_H_
diff --git a/content/browser/renderer_host/mock_render_process_host.cc b/content/browser/renderer_host/mock_render_process_host.cc
new file mode 100644
index 0000000..e0a2fb0
--- /dev/null
+++ b/content/browser/renderer_host/mock_render_process_host.cc
@@ -0,0 +1,156 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/mock_render_process_host.h"
+
+#include "chrome/browser/child_process_security_policy.h"
+
+MockRenderProcessHost::MockRenderProcessHost(Profile* profile)
+ : RenderProcessHost(profile),
+ transport_dib_(NULL),
+ bad_msg_count_(0),
+ factory_(NULL) {
+ // Child process security operations can't be unit tested unless we add
+ // ourselves as an existing child process.
+ ChildProcessSecurityPolicy::GetInstance()->Add(id());
+}
+
+MockRenderProcessHost::~MockRenderProcessHost() {
+ ChildProcessSecurityPolicy::GetInstance()->Remove(id());
+ delete transport_dib_;
+ if (factory_)
+ factory_->Remove(this);
+}
+
+bool MockRenderProcessHost::Init(
+ bool is_accessibility_enabled, bool is_extensions_process) {
+ return true;
+}
+
+int MockRenderProcessHost::GetNextRoutingID() {
+ static int prev_routing_id = 0;
+ return ++prev_routing_id;
+}
+
+void MockRenderProcessHost::CancelResourceRequests(int render_widget_id) {
+}
+
+void MockRenderProcessHost::CrossSiteClosePageACK(
+ const ViewMsg_ClosePage_Params& params) {
+}
+
+bool MockRenderProcessHost::WaitForUpdateMsg(int render_widget_id,
+ const base::TimeDelta& max_delay,
+ IPC::Message* msg) {
+ return false;
+}
+
+void MockRenderProcessHost::ReceivedBadMessage() {
+ ++bad_msg_count_;
+}
+
+void MockRenderProcessHost::WidgetRestored() {
+}
+
+void MockRenderProcessHost::WidgetHidden() {
+}
+
+void MockRenderProcessHost::ViewCreated() {
+}
+
+void MockRenderProcessHost::AddWord(const string16& word) {
+}
+
+void MockRenderProcessHost::SendVisitedLinkTable(
+ base::SharedMemory* table_memory) {
+}
+
+void MockRenderProcessHost::AddVisitedLinks(
+ const VisitedLinkCommon::Fingerprints& links) {
+}
+
+void MockRenderProcessHost::ResetVisitedLinks() {
+}
+
+bool MockRenderProcessHost::FastShutdownIfPossible() {
+ // We aren't actually going to do anything, but set |fast_shutdown_started_|
+ // to true so that tests know we've been called.
+ fast_shutdown_started_ = true;
+ return true;
+}
+
+bool MockRenderProcessHost::SendWithTimeout(IPC::Message* msg, int timeout_ms) {
+ // Save the message in the sink. Just ignore timeout_ms.
+ sink_.OnMessageReceived(*msg);
+ delete msg;
+ return true;
+}
+
+base::ProcessHandle MockRenderProcessHost::GetHandle() {
+ return base::kNullProcessHandle;
+}
+
+bool MockRenderProcessHost::Send(IPC::Message* msg) {
+ // Save the message in the sink.
+ sink_.OnMessageReceived(*msg);
+ delete msg;
+ return true;
+}
+
+TransportDIB* MockRenderProcessHost::GetTransportDIB(TransportDIB::Id dib_id) {
+ if (transport_dib_)
+ return transport_dib_;
+#if defined(OS_WIN)
+ HANDLE duped;
+ DuplicateHandle(GetCurrentProcess(), dib_id.handle, GetCurrentProcess(),
+ &duped, 0, TRUE, DUPLICATE_SAME_ACCESS);
+ transport_dib_ = TransportDIB::Map(duped);
+#elif defined(OS_MACOSX)
+ // On Mac, TransportDIBs are always created in the browser, so we cannot map
+ // one from a dib_id.
+ transport_dib_ = TransportDIB::Create(100 * 100 * 4, 0);
+#elif defined(OS_POSIX)
+ transport_dib_ = TransportDIB::Map(dib_id);
+#endif
+
+ return transport_dib_;
+}
+
+bool MockRenderProcessHost::OnMessageReceived(const IPC::Message& msg) {
+ return false;
+}
+
+void MockRenderProcessHost::OnChannelConnected(int32 peer_pid) {
+}
+
+MockRenderProcessHostFactory::MockRenderProcessHostFactory() {}
+
+MockRenderProcessHostFactory::~MockRenderProcessHostFactory() {
+ // Detach this object from MockRenderProcesses to prevent STLDeleteElements()
+ // from calling MockRenderProcessHostFactory::Remove().
+ for (ScopedVector<MockRenderProcessHost>::iterator it = processes_.begin();
+ it != processes_.end(); ++it) {
+ (*it)->SetFactory(NULL);
+ }
+}
+
+RenderProcessHost* MockRenderProcessHostFactory::CreateRenderProcessHost(
+ Profile* profile) const {
+ MockRenderProcessHost* host = new MockRenderProcessHost(profile);
+ if (host) {
+ processes_.push_back(host);
+ host->SetFactory(this);
+ }
+ return host;
+}
+
+void MockRenderProcessHostFactory::Remove(MockRenderProcessHost* host) const {
+ for (ScopedVector<MockRenderProcessHost>::iterator it = processes_.begin();
+ it != processes_.end(); ++it) {
+ if (*it == host) {
+ processes_.weak_erase(it);
+ break;
+ }
+ }
+}
diff --git a/content/browser/renderer_host/mock_render_process_host.h b/content/browser/renderer_host/mock_render_process_host.h
new file mode 100644
index 0000000..abe0f82
--- /dev/null
+++ b/content/browser/renderer_host/mock_render_process_host.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_MOCK_RENDER_PROCESS_HOST_H_
+#define CONTENT_BROWSER_RENDERER_HOST_MOCK_RENDER_PROCESS_HOST_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/scoped_vector.h"
+#include "content/browser/renderer_host/render_process_host.h"
+#include "ipc/ipc_test_sink.h"
+
+class MockRenderProcessHostFactory;
+class TransportDIB;
+class URLRequestContextGetter;
+
+// A mock render process host that has no corresponding renderer process. All
+// IPC messages are sent into the message sink for inspection by tests.
+class MockRenderProcessHost : public RenderProcessHost {
+ public:
+ explicit MockRenderProcessHost(Profile* profile);
+ virtual ~MockRenderProcessHost();
+
+ // Provides access to all IPC messages that would have been sent to the
+ // renderer via this RenderProcessHost.
+ IPC::TestSink& sink() { return sink_; }
+
+ // Provides tests access to the max page ID currently used for this process.
+ int max_page_id() const { return max_page_id_; }
+
+ // Provides test access to how many times a bad message has been received.
+ int bad_msg_count() const { return bad_msg_count_; }
+
+ // RenderProcessHost implementation (public portion).
+ virtual bool Init(bool is_accessibility_enabled, bool is_extensions_process);
+ virtual int GetNextRoutingID();
+ virtual void CancelResourceRequests(int render_widget_id);
+ virtual void CrossSiteClosePageACK(const ViewMsg_ClosePage_Params& params);
+ virtual bool WaitForUpdateMsg(int render_widget_id,
+ const base::TimeDelta& max_delay,
+ IPC::Message* msg);
+ virtual void ReceivedBadMessage();
+ virtual void WidgetRestored();
+ virtual void WidgetHidden();
+ virtual void ViewCreated();
+ virtual void AddWord(const string16& word);
+ virtual void SendVisitedLinkTable(base::SharedMemory* table_memory);
+ virtual void AddVisitedLinks(
+ const VisitedLinkCommon::Fingerprints& visited_links);
+ virtual void ResetVisitedLinks();
+ virtual bool FastShutdownIfPossible();
+ virtual bool SendWithTimeout(IPC::Message* msg, int timeout_ms);
+ virtual base::ProcessHandle GetHandle();
+
+ virtual TransportDIB* GetTransportDIB(TransportDIB::Id dib_id);
+
+ // IPC::Channel::Sender via RenderProcessHost.
+ virtual bool Send(IPC::Message* msg);
+
+ // IPC::Channel::Listener via RenderProcessHost.
+ virtual bool OnMessageReceived(const IPC::Message& msg);
+ virtual void OnChannelConnected(int32 peer_pid);
+
+ // Attaches the factory object so we can remove this object in its destructor
+ // and prevent MockRenderProcessHostFacotry from deleting it.
+ void SetFactory(const MockRenderProcessHostFactory* factory) {
+ factory_ = factory;
+ }
+
+ private:
+ // Stores IPC messages that would have been sent to the renderer.
+ IPC::TestSink sink_;
+ TransportDIB* transport_dib_;
+ int bad_msg_count_;
+ const MockRenderProcessHostFactory* factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockRenderProcessHost);
+};
+
+class MockRenderProcessHostFactory : public RenderProcessHostFactory {
+ public:
+ MockRenderProcessHostFactory();
+ virtual ~MockRenderProcessHostFactory();
+
+ virtual RenderProcessHost* CreateRenderProcessHost(Profile* profile) const;
+
+ // Removes the given MockRenderProcessHost from the MockRenderProcessHost list
+ // without deleting it. When a test deletes a MockRenderProcessHost, we need
+ // to remove it from |processes_| to prevent it from being deleted twice.
+ void Remove(MockRenderProcessHost* host) const;
+
+ private:
+ // A list of MockRenderProcessHosts created by this object. This list is used
+ // for deleting all MockRenderProcessHosts that have not deleted by a test in
+ // the destructor and prevent them from being leaked.
+ mutable ScopedVector<MockRenderProcessHost> processes_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockRenderProcessHostFactory);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_MOCK_RENDER_PROCESS_HOST_H_
diff --git a/content/browser/renderer_host/pepper_file_message_filter.cc b/content/browser/renderer_host/pepper_file_message_filter.cc
new file mode 100644
index 0000000..9f7f9d9
--- /dev/null
+++ b/content/browser/renderer_host/pepper_file_message_filter.cc
@@ -0,0 +1,201 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/pepper_file_message_filter.h"
+
+#include "base/callback.h"
+#include "base/file_util.h"
+#include "base/file_path.h"
+#include "base/process_util.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/renderer_host/browser_render_process_host.h"
+#include "chrome/common/child_process_host.h"
+#include "chrome/common/pepper_file_messages.h"
+#include "ipc/ipc_platform_file.h"
+
+#if defined(OS_POSIX)
+#include "base/file_descriptor_posix.h"
+#endif
+
+PepperFileMessageFilter::PepperFileMessageFilter(
+ int child_id, Profile* profile) {
+ pepper_path_ = profile->GetPath().Append(FILE_PATH_LITERAL("Pepper Data"));
+}
+
+PepperFileMessageFilter::~PepperFileMessageFilter() {
+ // This function should be called on the IO thread.
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+}
+
+void PepperFileMessageFilter::OverrideThreadForMessage(
+ const IPC::Message& message,
+ BrowserThread::ID* thread) {
+ if (IPC_MESSAGE_CLASS(message) == PepperFileMsgStart)
+ *thread = BrowserThread::FILE;
+}
+
+bool PepperFileMessageFilter::OnMessageReceived(
+ const IPC::Message& message,
+ bool* message_was_ok) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(PepperFileMessageFilter, message, *message_was_ok)
+ IPC_MESSAGE_HANDLER(PepperFileMsg_OpenFile, OnPepperOpenFile)
+ IPC_MESSAGE_HANDLER(PepperFileMsg_RenameFile, OnPepperRenameFile)
+ IPC_MESSAGE_HANDLER(PepperFileMsg_DeleteFileOrDir, OnPepperDeleteFileOrDir)
+ IPC_MESSAGE_HANDLER(PepperFileMsg_CreateDir, OnPepperCreateDir)
+ IPC_MESSAGE_HANDLER(PepperFileMsg_QueryFile, OnPepperQueryFile)
+ IPC_MESSAGE_HANDLER(PepperFileMsg_GetDirContents, OnPepperGetDirContents)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP_EX()
+ return handled;
+}
+
+void PepperFileMessageFilter::OnDestruct() const {
+ BrowserThread::DeleteOnIOThread::Destruct(this);
+}
+
+// Called on the FILE thread:
+void PepperFileMessageFilter::OnPepperOpenFile(
+ const FilePath& path,
+ int flags,
+ base::PlatformFileError* error,
+ IPC::PlatformFileForTransit* file) {
+ FilePath full_path = MakePepperPath(path);
+ if (full_path.empty()) {
+ *error = base::PLATFORM_FILE_ERROR_ACCESS_DENIED;
+ *file = IPC::InvalidPlatformFileForTransit();
+ return;
+ }
+
+ base::PlatformFile file_handle = base::CreatePlatformFile(
+ full_path, flags, NULL, error);
+
+ if (*error != base::PLATFORM_FILE_OK) {
+ *file = IPC::InvalidPlatformFileForTransit();
+ return;
+ }
+
+ // Make sure we didn't try to open a directory: directory fd shouldn't pass
+ // to untrusted processes because they open security holes.
+ base::PlatformFileInfo info;
+ if (!base::GetPlatformFileInfo(file_handle, &info) || info.is_directory) {
+ // When in doubt, throw it out.
+ *error = base::PLATFORM_FILE_ERROR_ACCESS_DENIED;
+ *file = IPC::InvalidPlatformFileForTransit();
+ return;
+ }
+
+#if defined(OS_WIN)
+ // Duplicate the file handle so that the renderer process can access the file.
+ if (!DuplicateHandle(GetCurrentProcess(), file_handle,
+ peer_handle(), file, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ // file_handle is closed whether or not DuplicateHandle succeeds.
+ *error = base::PLATFORM_FILE_ERROR_ACCESS_DENIED;
+ *file = INVALID_HANDLE_VALUE;
+ }
+#else
+ *file = base::FileDescriptor(file_handle, true);
+#endif
+}
+
+void PepperFileMessageFilter::OnPepperRenameFile(
+ const FilePath& path_from,
+ const FilePath& path_to,
+ base::PlatformFileError* error) {
+ FilePath full_path_from = MakePepperPath(path_from);
+ FilePath full_path_to = MakePepperPath(path_to);
+ if (full_path_from.empty() || full_path_to.empty()) {
+ *error = base::PLATFORM_FILE_ERROR_ACCESS_DENIED;
+ return;
+ }
+
+ bool result = file_util::Move(full_path_from, full_path_to);
+ *error = result ? base::PLATFORM_FILE_OK
+ : base::PLATFORM_FILE_ERROR_ACCESS_DENIED;
+}
+
+void PepperFileMessageFilter::OnPepperDeleteFileOrDir(
+ const FilePath& path,
+ bool recursive,
+ base::PlatformFileError* error) {
+ FilePath full_path = MakePepperPath(path);
+ if (full_path.empty()) {
+ *error = base::PLATFORM_FILE_ERROR_ACCESS_DENIED;
+ return;
+ }
+
+ bool result = file_util::Delete(full_path, recursive);
+ *error = result ? base::PLATFORM_FILE_OK
+ : base::PLATFORM_FILE_ERROR_ACCESS_DENIED;
+}
+
+void PepperFileMessageFilter::OnPepperCreateDir(
+ const FilePath& path,
+ base::PlatformFileError* error) {
+ FilePath full_path = MakePepperPath(path);
+ if (full_path.empty()) {
+ *error = base::PLATFORM_FILE_ERROR_ACCESS_DENIED;
+ return;
+ }
+
+ bool result = file_util::CreateDirectory(full_path);
+ *error = result ? base::PLATFORM_FILE_OK
+ : base::PLATFORM_FILE_ERROR_ACCESS_DENIED;
+}
+
+void PepperFileMessageFilter::OnPepperQueryFile(
+ const FilePath& path,
+ base::PlatformFileInfo* info,
+ base::PlatformFileError* error) {
+ FilePath full_path = MakePepperPath(path);
+ if (full_path.empty()) {
+ *error = base::PLATFORM_FILE_ERROR_ACCESS_DENIED;
+ return;
+ }
+
+ bool result = file_util::GetFileInfo(full_path, info);
+ *error = result ? base::PLATFORM_FILE_OK
+ : base::PLATFORM_FILE_ERROR_ACCESS_DENIED;
+}
+
+void PepperFileMessageFilter::OnPepperGetDirContents(
+ const FilePath& path,
+ webkit::ppapi::DirContents* contents,
+ base::PlatformFileError* error) {
+ FilePath full_path = MakePepperPath(path);
+ if (full_path.empty()) {
+ *error = base::PLATFORM_FILE_ERROR_ACCESS_DENIED;
+ return;
+ }
+
+ contents->clear();
+
+ file_util::FileEnumerator enumerator(
+ full_path, false,
+ static_cast<file_util::FileEnumerator::FILE_TYPE>(
+ file_util::FileEnumerator::FILES |
+ file_util::FileEnumerator::DIRECTORIES |
+ file_util::FileEnumerator::INCLUDE_DOT_DOT));
+
+ while (!enumerator.Next().empty()) {
+ file_util::FileEnumerator::FindInfo info;
+ enumerator.GetFindInfo(&info);
+ webkit::ppapi::DirEntry entry = {
+ file_util::FileEnumerator::GetFilename(info),
+ file_util::FileEnumerator::IsDirectory(info)
+ };
+ contents->push_back(entry);
+ }
+
+ *error = base::PLATFORM_FILE_OK;
+}
+
+FilePath PepperFileMessageFilter::MakePepperPath(const FilePath& base_path) {
+ if (base_path.IsAbsolute() || base_path.ReferencesParent()) {
+ return FilePath();
+ }
+ return pepper_path_.Append(base_path);
+}
diff --git a/content/browser/renderer_host/pepper_file_message_filter.h b/content/browser/renderer_host/pepper_file_message_filter.h
new file mode 100644
index 0000000..a06d9a4
--- /dev/null
+++ b/content/browser/renderer_host/pepper_file_message_filter.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_PEPPER_FILE_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_PEPPER_FILE_MESSAGE_FILTER_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/file_path.h"
+#include "base/process.h"
+#include "base/ref_counted.h"
+#include "base/task.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_message_filter.h"
+#include "ipc/ipc_platform_file.h"
+#include "webkit/plugins/ppapi/dir_contents.h"
+
+class Profile;
+
+// A message filter for Pepper-specific File I/O messages.
+class PepperFileMessageFilter : public BrowserMessageFilter {
+ public:
+ PepperFileMessageFilter(int child_id, Profile* profile);
+
+ // BrowserMessageFilter methods:
+ virtual void OverrideThreadForMessage(const IPC::Message& message,
+ BrowserThread::ID* thread);
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok);
+ virtual void OnDestruct() const;
+
+ private:
+ friend class BrowserThread;
+ friend class DeleteTask<PepperFileMessageFilter>;
+ virtual ~PepperFileMessageFilter();
+
+ // Called on the FILE thread:
+ void OnPepperOpenFile(const FilePath& path,
+ int flags,
+ base::PlatformFileError* error,
+ IPC::PlatformFileForTransit* file);
+ void OnPepperRenameFile(const FilePath& path_from,
+ const FilePath& path_to,
+ base::PlatformFileError* error);
+ void OnPepperDeleteFileOrDir(const FilePath& path,
+ bool recursive,
+ base::PlatformFileError* error);
+ void OnPepperCreateDir(const FilePath& path,
+ base::PlatformFileError* error);
+ void OnPepperQueryFile(const FilePath& path,
+ base::PlatformFileInfo* info,
+ base::PlatformFileError* error);
+ void OnPepperGetDirContents(const FilePath& path,
+ webkit::ppapi::DirContents* contents,
+ base::PlatformFileError* error);
+
+ FilePath MakePepperPath(const FilePath& base_path);
+
+ // The channel associated with the renderer connection. This pointer is not
+ // owned by this class.
+ IPC::Channel* channel_;
+
+ // The base path for the pepper data.
+ FilePath pepper_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(PepperFileMessageFilter);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_PEPPER_FILE_MESSAGE_FILTER_H_
diff --git a/content/browser/renderer_host/pepper_message_filter.cc b/content/browser/renderer_host/pepper_message_filter.cc
new file mode 100644
index 0000000..6a77171
--- /dev/null
+++ b/content/browser/renderer_host/pepper_message_filter.cc
@@ -0,0 +1,280 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/pepper_message_filter.h"
+
+#include "base/basictypes.h"
+#include "base/process_util.h"
+#include "base/threading/worker_pool.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/net/chrome_url_request_context.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/renderer_host/browser_render_process_host.h"
+#include "chrome/common/pepper_messages.h"
+#include "net/base/address_list.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/host_resolver.h"
+#include "net/url_request/url_request_context.h"
+#include "webkit/plugins/ppapi/ppb_flash_impl.h"
+
+#if defined(ENABLE_FLAPPER_HACKS)
+#include <netdb.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+// Make sure the storage in |PP_Flash_NetAddress| is big enough. (Do it here
+// since the data is opaque elsewhere.)
+COMPILE_ASSERT(sizeof(reinterpret_cast<PP_Flash_NetAddress*>(0)->data) >=
+ sizeof(sockaddr_storage), PP_Flash_NetAddress_data_too_small);
+#endif // ENABLE_FLAPPER_HACKS
+
+const PP_Flash_NetAddress kInvalidNetAddress = { 0 };
+
+PepperMessageFilter::PepperMessageFilter(Profile* profile)
+ : profile_(profile),
+ request_context_(profile_->GetRequestContext()) {
+}
+
+PepperMessageFilter::~PepperMessageFilter() {}
+
+bool PepperMessageFilter::OnMessageReceived(const IPC::Message& msg,
+ bool* message_was_ok) {
+#if defined(ENABLE_FLAPPER_HACKS)
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(PepperMessageFilter, msg, *message_was_ok)
+ IPC_MESSAGE_HANDLER(PepperMsg_ConnectTcp, OnConnectTcp)
+ IPC_MESSAGE_HANDLER(PepperMsg_ConnectTcpAddress, OnConnectTcpAddress)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP_EX()
+ return handled;
+#else
+ return false;
+#endif // ENABLE_FLAPPER_HACKS
+}
+
+#if defined(ENABLE_FLAPPER_HACKS)
+
+namespace {
+
+bool ValidateNetAddress(const PP_Flash_NetAddress& addr) {
+ if (addr.size < sizeof(sa_family_t))
+ return false;
+
+ // TODO(viettrungluu): more careful validation?
+ // Just do a size check for AF_INET.
+ if (reinterpret_cast<const sockaddr*>(addr.data)->sa_family == AF_INET &&
+ addr.size >= sizeof(sockaddr_in))
+ return true;
+
+ // Ditto for AF_INET6.
+ if (reinterpret_cast<const sockaddr*>(addr.data)->sa_family == AF_INET6 &&
+ addr.size >= sizeof(sockaddr_in6))
+ return true;
+
+ // Reject everything else.
+ return false;
+}
+
+PP_Flash_NetAddress SockaddrToNetAddress(const struct sockaddr* sa,
+ socklen_t sa_length) {
+ PP_Flash_NetAddress addr;
+ CHECK_LE(sa_length, sizeof(addr.data));
+ addr.size = sa_length;
+ memcpy(addr.data, sa, addr.size);
+ return addr;
+}
+
+int ConnectTcpSocket(const PP_Flash_NetAddress& addr,
+ PP_Flash_NetAddress* local_addr_out,
+ PP_Flash_NetAddress* remote_addr_out) {
+ *local_addr_out = kInvalidNetAddress;
+ *remote_addr_out = kInvalidNetAddress;
+
+ const struct sockaddr* sa =
+ reinterpret_cast<const struct sockaddr*>(addr.data);
+ socklen_t sa_len = addr.size;
+ int fd = socket(sa->sa_family, SOCK_STREAM, IPPROTO_TCP);
+ if (fd == -1)
+ return -1;
+ if (connect(fd, sa, sa_len) != 0) {
+ close(fd);
+ return -1;
+ }
+
+ // Get the local address.
+ socklen_t local_length = sizeof(local_addr_out->data);
+ if (getsockname(fd, reinterpret_cast<struct sockaddr*>(local_addr_out->data),
+ &local_length) == -1 ||
+ local_length > sizeof(local_addr_out->data)) {
+ close(fd);
+ return -1;
+ }
+
+ // The remote address is just the address we connected to.
+ *remote_addr_out = addr;
+
+ return fd;
+}
+
+} // namespace
+
+class PepperMessageFilter::LookupRequest {
+ public:
+ LookupRequest(PepperMessageFilter* pepper_message_filter,
+ net::HostResolver* resolver,
+ int routing_id,
+ int request_id,
+ const net::HostResolver::RequestInfo& request_info)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(
+ net_callback_(this, &LookupRequest::OnLookupFinished)),
+ pepper_message_filter_(pepper_message_filter),
+ resolver_(resolver),
+ routing_id_(routing_id),
+ request_id_(request_id),
+ request_info_(request_info) {
+ }
+
+ void Start() {
+ int result = resolver_.Resolve(request_info_, &addresses_, &net_callback_,
+ net::BoundNetLog());
+ if (result != net::ERR_IO_PENDING)
+ OnLookupFinished(result);
+ }
+
+ private:
+ void OnLookupFinished(int /*result*/) {
+ pepper_message_filter_->ConnectTcpLookupFinished(
+ routing_id_, request_id_, addresses_);
+ delete this;
+ }
+
+ // HostResolver will call us using this callback when resolution is complete.
+ net::CompletionCallbackImpl<LookupRequest> net_callback_;
+
+ PepperMessageFilter* pepper_message_filter_;
+ net::SingleRequestHostResolver resolver_;
+
+ int routing_id_;
+ int request_id_;
+ net::HostResolver::RequestInfo request_info_;
+
+ net::AddressList addresses_;
+
+ DISALLOW_COPY_AND_ASSIGN(LookupRequest);
+};
+
+void PepperMessageFilter::OnConnectTcp(int routing_id,
+ int request_id,
+ const std::string& host,
+ uint16 port) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ net::URLRequestContext* req_context =
+ request_context_->GetURLRequestContext();
+ net::HostResolver::RequestInfo request_info(net::HostPortPair(host, port));
+
+ // The lookup request will delete itself on completion.
+ LookupRequest* lookup_request =
+ new LookupRequest(this, req_context->host_resolver(),
+ routing_id, request_id, request_info);
+ lookup_request->Start();
+}
+
+void PepperMessageFilter::OnConnectTcpAddress(int routing_id,
+ int request_id,
+ const PP_Flash_NetAddress& addr) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ // Validate the address and then continue (doing |connect()|) on a worker
+ // thread.
+ if (!ValidateNetAddress(addr) ||
+ !base::WorkerPool::PostTask(FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &PepperMessageFilter::ConnectTcpAddressOnWorkerThread,
+ routing_id, request_id, addr),
+ true)) {
+ SendConnectTcpACKError(routing_id, request_id);
+ }
+}
+
+bool PepperMessageFilter::SendConnectTcpACKError(int routing_id,
+ int request_id) {
+ return Send(
+ new PepperMsg_ConnectTcpACK(routing_id, request_id,
+ IPC::InvalidPlatformFileForTransit(),
+ kInvalidNetAddress, kInvalidNetAddress));
+}
+
+void PepperMessageFilter::ConnectTcpLookupFinished(
+ int routing_id,
+ int request_id,
+ const net::AddressList& addresses) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ // If the lookup returned addresses, continue (doing |connect()|) on a worker
+ // thread.
+ if (!addresses.head() ||
+ !base::WorkerPool::PostTask(FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &PepperMessageFilter::ConnectTcpOnWorkerThread,
+ routing_id, request_id, addresses),
+ true)) {
+ SendConnectTcpACKError(routing_id, request_id);
+ }
+}
+
+void PepperMessageFilter::ConnectTcpOnWorkerThread(int routing_id,
+ int request_id,
+ net::AddressList addresses) {
+ IPC::PlatformFileForTransit socket_for_transit =
+ IPC::InvalidPlatformFileForTransit();
+ PP_Flash_NetAddress local_addr = kInvalidNetAddress;
+ PP_Flash_NetAddress remote_addr = kInvalidNetAddress;
+
+ for (const struct addrinfo* ai = addresses.head(); ai; ai = ai->ai_next) {
+ PP_Flash_NetAddress addr = SockaddrToNetAddress(ai->ai_addr,
+ ai->ai_addrlen);
+ int fd = ConnectTcpSocket(addr, &local_addr, &remote_addr);
+ if (fd != -1) {
+ socket_for_transit = base::FileDescriptor(fd, true);
+ break;
+ }
+ }
+
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ NewRunnableMethod(
+ this, &PepperMessageFilter::Send,
+ new PepperMsg_ConnectTcpACK(
+ routing_id, request_id,
+ socket_for_transit, local_addr, remote_addr)));
+}
+
+// TODO(vluu): Eliminate duplication between this and
+// |ConnectTcpOnWorkerThread()|.
+void PepperMessageFilter::ConnectTcpAddressOnWorkerThread(
+ int routing_id,
+ int request_id,
+ PP_Flash_NetAddress addr) {
+ IPC::PlatformFileForTransit socket_for_transit =
+ IPC::InvalidPlatformFileForTransit();
+ PP_Flash_NetAddress local_addr = kInvalidNetAddress;
+ PP_Flash_NetAddress remote_addr = kInvalidNetAddress;
+
+ int fd = ConnectTcpSocket(addr, &local_addr, &remote_addr);
+ if (fd != -1)
+ socket_for_transit = base::FileDescriptor(fd, true);
+
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
+ NewRunnableMethod(
+ this, &PepperMessageFilter::Send,
+ new PepperMsg_ConnectTcpACK(
+ routing_id, request_id,
+ socket_for_transit, local_addr, remote_addr)));
+}
+
+#endif // ENABLE_FLAPPER_HACKS
diff --git a/content/browser/renderer_host/pepper_message_filter.h b/content/browser/renderer_host/pepper_message_filter.h
new file mode 100644
index 0000000..31f920c
--- /dev/null
+++ b/content/browser/renderer_host/pepper_message_filter.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_PEPPER_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_PEPPER_MESSAGE_FILTER_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/process.h"
+#include "chrome/browser/browser_message_filter.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ppapi/c/private/ppb_flash.h"
+
+class Profile;
+class URLRequestContextGetter;
+
+namespace net {
+class AddressList;
+}
+
+class PepperMessageFilter : public BrowserMessageFilter {
+ public:
+ explicit PepperMessageFilter(Profile* profile);
+ virtual ~PepperMessageFilter();
+
+ private:
+ // BrowserMessageFilter methods.
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok);
+
+#if defined(ENABLE_FLAPPER_HACKS)
+ // Message handlers.
+ void OnConnectTcp(int routing_id,
+ int request_id,
+ const std::string& host,
+ uint16 port);
+ void OnConnectTcpAddress(int routing_id,
+ int request_id,
+ const PP_Flash_NetAddress& address);
+
+ // |Send()| a |PepperMsg_ConnectTcpACK|, which reports an error.
+ bool SendConnectTcpACKError(int routing_id,
+ int request_id);
+
+ // Used by |OnConnectTcp()| (below).
+ class LookupRequest;
+ friend class LookupRequest;
+
+ // Continuation of |OnConnectTcp()|.
+ void ConnectTcpLookupFinished(int routing_id,
+ int request_id,
+ const net::AddressList& addresses);
+ void ConnectTcpOnWorkerThread(int routing_id,
+ int request_id,
+ net::AddressList addresses);
+
+ // Continuation of |OnConnectTcpAddress()|.
+ void ConnectTcpAddressOnWorkerThread(int routing_id,
+ int request_id,
+ PP_Flash_NetAddress addr);
+#endif // ENABLE_FLAPPER_HACKS
+
+ Profile* profile_;
+ scoped_refptr<URLRequestContextGetter> request_context_;
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_PEPPER_MESSAGE_FILTER_H_
diff --git a/content/browser/renderer_host/redirect_to_file_resource_handler.cc b/content/browser/renderer_host/redirect_to_file_resource_handler.cc
new file mode 100644
index 0000000..bb18a4a
--- /dev/null
+++ b/content/browser/renderer_host/redirect_to_file_resource_handler.cc
@@ -0,0 +1,221 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/redirect_to_file_resource_handler.h"
+
+#include "base/file_util.h"
+#include "base/file_util_proxy.h"
+#include "base/logging.h"
+#include "base/platform_file.h"
+#include "base/task.h"
+#include "chrome/common/resource_response.h"
+#include "content/browser/renderer_host/resource_dispatcher_host.h"
+#include "net/base/file_stream.h"
+#include "net/base/io_buffer.h"
+#include "net/base/mime_sniffer.h"
+#include "net/base/net_errors.h"
+#include "webkit/blob/deletable_file_reference.h"
+
+using webkit_blob::DeletableFileReference;
+
+// TODO(darin): Use the buffer sizing algorithm from AsyncResourceHandler.
+static const int kReadBufSize = 32768;
+
+RedirectToFileResourceHandler::RedirectToFileResourceHandler(
+ ResourceHandler* next_handler,
+ int process_id,
+ ResourceDispatcherHost* host)
+ : callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
+ host_(host),
+ next_handler_(next_handler),
+ process_id_(process_id),
+ request_id_(-1),
+ buf_(new net::GrowableIOBuffer()),
+ buf_write_pending_(false),
+ write_cursor_(0),
+ write_callback_(ALLOW_THIS_IN_INITIALIZER_LIST(this),
+ &RedirectToFileResourceHandler::DidWriteToFile),
+ write_callback_pending_(false) {
+}
+
+bool RedirectToFileResourceHandler::OnUploadProgress(int request_id,
+ uint64 position,
+ uint64 size) {
+ return next_handler_->OnUploadProgress(request_id, position, size);
+}
+
+bool RedirectToFileResourceHandler::OnRequestRedirected(
+ int request_id,
+ const GURL& new_url,
+ ResourceResponse* response,
+ bool* defer) {
+ return next_handler_->OnRequestRedirected(request_id, new_url, response,
+ defer);
+}
+
+bool RedirectToFileResourceHandler::OnResponseStarted(
+ int request_id,
+ ResourceResponse* response) {
+ if (response->response_head.status.is_success()) {
+ DCHECK(deletable_file_ && !deletable_file_->path().empty());
+ response->response_head.download_file_path = deletable_file_->path();
+ }
+ return next_handler_->OnResponseStarted(request_id, response);
+}
+
+bool RedirectToFileResourceHandler::OnWillStart(int request_id,
+ const GURL& url,
+ bool* defer) {
+ request_id_ = request_id;
+ if (!deletable_file_) {
+ // Defer starting the request until we have created the temporary file.
+ // TODO(darin): This is sub-optimal. We should not delay starting the
+ // network request like this.
+ *defer = true;
+ base::FileUtilProxy::CreateTemporary(
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
+ callback_factory_.NewCallback(
+ &RedirectToFileResourceHandler::DidCreateTemporaryFile));
+ return true;
+ }
+ return next_handler_->OnWillStart(request_id, url, defer);
+}
+
+bool RedirectToFileResourceHandler::OnWillRead(int request_id,
+ net::IOBuffer** buf,
+ int* buf_size,
+ int min_size) {
+ DCHECK(min_size == -1);
+
+ if (!buf_->capacity())
+ buf_->SetCapacity(kReadBufSize);
+
+ // We should have paused this network request already if the buffer is full.
+ DCHECK(!BufIsFull());
+
+ *buf = buf_;
+ *buf_size = buf_->RemainingCapacity();
+
+ buf_write_pending_ = true;
+ return true;
+}
+
+bool RedirectToFileResourceHandler::OnReadCompleted(int request_id,
+ int* bytes_read) {
+ if (!buf_write_pending_) {
+ // Ignore spurious OnReadCompleted! PauseRequest(true) called from within
+ // OnReadCompleted tells the ResourceDispatcherHost that we did not consume
+ // the data. PauseRequest(false) then repeats the last OnReadCompleted
+ // call. We pause the request so that we can copy our buffer to disk, so
+ // we need to consume the data now. The ResourceDispatcherHost pause
+ // mechanism does not fit our use case very well.
+ // TODO(darin): Fix the ResourceDispatcherHost to avoid this hack!
+ return true;
+ }
+
+ buf_write_pending_ = false;
+
+ // We use the buffer's offset field to record the end of the buffer.
+
+ int new_offset = buf_->offset() + *bytes_read;
+ DCHECK(new_offset <= buf_->capacity());
+ buf_->set_offset(new_offset);
+
+ if (BufIsFull())
+ host_->PauseRequest(process_id_, request_id, true);
+
+ if (*bytes_read > 0)
+ next_handler_->OnDataDownloaded(request_id, *bytes_read);
+
+ return WriteMore();
+}
+
+bool RedirectToFileResourceHandler::OnResponseCompleted(
+ int request_id,
+ const net::URLRequestStatus& status,
+ const std::string& security_info) {
+ return next_handler_->OnResponseCompleted(request_id, status, security_info);
+}
+
+void RedirectToFileResourceHandler::OnRequestClosed() {
+ // We require this explicit call to Close since file_stream_ was constructed
+ // directly from a PlatformFile.
+ file_stream_->Close();
+ file_stream_.reset();
+ deletable_file_ = NULL;
+
+ next_handler_->OnRequestClosed();
+}
+
+RedirectToFileResourceHandler::~RedirectToFileResourceHandler() {
+ DCHECK(!file_stream_.get());
+}
+
+void RedirectToFileResourceHandler::DidCreateTemporaryFile(
+ base::PlatformFileError /*error_code*/,
+ base::PassPlatformFile file_handle,
+ FilePath file_path) {
+ deletable_file_ = DeletableFileReference::GetOrCreate(
+ file_path,
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
+ file_stream_.reset(new net::FileStream(file_handle.ReleaseValue(),
+ base::PLATFORM_FILE_WRITE |
+ base::PLATFORM_FILE_ASYNC));
+ host_->RegisterDownloadedTempFile(
+ process_id_, request_id_, deletable_file_.get());
+ host_->StartDeferredRequest(process_id_, request_id_);
+}
+
+void RedirectToFileResourceHandler::DidWriteToFile(int result) {
+ write_callback_pending_ = false;
+
+ bool failed = false;
+ if (result > 0) {
+ write_cursor_ += result;
+ failed = !WriteMore();
+ } else {
+ failed = true;
+ }
+
+ if (failed)
+ host_->CancelRequest(process_id_, request_id_, false);
+}
+
+bool RedirectToFileResourceHandler::WriteMore() {
+ DCHECK(file_stream_.get());
+ for (;;) {
+ if (write_cursor_ == buf_->offset()) {
+ // We've caught up to the network load, but it may be in the process of
+ // appending more data to the buffer.
+ if (!buf_write_pending_) {
+ if (BufIsFull())
+ host_->PauseRequest(process_id_, request_id_, false);
+ buf_->set_offset(0);
+ write_cursor_ = 0;
+ }
+ return true;
+ }
+ if (write_callback_pending_)
+ return true;
+ DCHECK(write_cursor_ < buf_->offset());
+ int rv = file_stream_->Write(buf_->StartOfBuffer() + write_cursor_,
+ buf_->offset() - write_cursor_,
+ &write_callback_);
+ if (rv == net::ERR_IO_PENDING) {
+ write_callback_pending_ = true;
+ return true;
+ }
+ if (rv < 0)
+ return false;
+ write_cursor_ += rv;
+ }
+}
+
+bool RedirectToFileResourceHandler::BufIsFull() const {
+ // This is a hack to workaround BufferedResourceHandler's inability to
+ // deal with a ResourceHandler that returns a buffer size of less than
+ // 2 * net::kMaxBytesToSniff from its OnWillRead method.
+ // TODO(darin): Fix this retardation!
+ return buf_->RemainingCapacity() <= (2 * net::kMaxBytesToSniff);
+}
diff --git a/content/browser/renderer_host/redirect_to_file_resource_handler.h b/content/browser/renderer_host/redirect_to_file_resource_handler.h
new file mode 100644
index 0000000..34fb082
--- /dev/null
+++ b/content/browser/renderer_host/redirect_to_file_resource_handler.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_REDIRECT_TO_FILE_RESOURCE_HANDLER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_REDIRECT_TO_FILE_RESOURCE_HANDLER_H_
+
+#include "base/file_path.h"
+#include "base/platform_file.h"
+#include "base/ref_counted.h"
+#include "base/scoped_callback_factory.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/renderer_host/resource_handler.h"
+#include "net/base/completion_callback.h"
+
+class RefCountedPlatformFile;
+class ResourceDispatcherHost;
+
+namespace net {
+class FileStream;
+class GrowableIOBuffer;
+}
+
+namespace webkit_blob {
+class DeletableFileReference;
+}
+
+// Redirects network data to a file. This is intended to be layered in front
+// of either the AsyncResourceHandler or the SyncResourceHandler.
+class RedirectToFileResourceHandler : public ResourceHandler {
+ public:
+ RedirectToFileResourceHandler(
+ ResourceHandler* next_handler,
+ int process_id,
+ ResourceDispatcherHost* resource_dispatcher_host);
+
+ // ResourceHandler implementation:
+ virtual bool OnUploadProgress(int request_id, uint64 position, uint64 size);
+ virtual bool OnRequestRedirected(int request_id, const GURL& new_url,
+ ResourceResponse* response, bool* defer);
+ virtual bool OnResponseStarted(int request_id, ResourceResponse* response);
+ virtual bool OnWillStart(int request_id, const GURL& url, bool* defer);
+ virtual bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size,
+ int min_size);
+ virtual bool OnReadCompleted(int request_id, int* bytes_read);
+ virtual bool OnResponseCompleted(int request_id,
+ const net::URLRequestStatus& status,
+ const std::string& security_info);
+ virtual void OnRequestClosed();
+
+ private:
+ virtual ~RedirectToFileResourceHandler();
+ void DidCreateTemporaryFile(base::PlatformFileError error_code,
+ base::PassPlatformFile file_handle,
+ FilePath file_path);
+ void DidWriteToFile(int result);
+ bool WriteMore();
+ bool BufIsFull() const;
+
+ base::ScopedCallbackFactory<RedirectToFileResourceHandler> callback_factory_;
+
+ ResourceDispatcherHost* host_;
+ scoped_refptr<ResourceHandler> next_handler_;
+ int process_id_;
+ int request_id_;
+
+ // We allocate a single, fixed-size IO buffer (buf_) used to read from the
+ // network (buf_write_pending_ is true while the system is copying data into
+ // buf_), and then write this buffer out to disk (write_callback_pending_ is
+ // true while writing to disk). Reading from the network is suspended while
+ // the buffer is full (BufIsFull returns true). The write_cursor_ member
+ // tracks the offset into buf_ that we are writing to disk.
+
+ scoped_refptr<net::GrowableIOBuffer> buf_;
+ bool buf_write_pending_;
+ int write_cursor_;
+
+ scoped_ptr<net::FileStream> file_stream_;
+ net::CompletionCallbackImpl<RedirectToFileResourceHandler> write_callback_;
+ bool write_callback_pending_;
+
+ // We create a DeletableFileReference for the temp file created as
+ // a result of the download.
+ scoped_refptr<webkit_blob::DeletableFileReference> deletable_file_;
+
+ DISALLOW_COPY_AND_ASSIGN(RedirectToFileResourceHandler);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_REDIRECT_TO_FILE_RESOURCE_HANDLER_H_
diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc
new file mode 100644
index 0000000..388bddb
--- /dev/null
+++ b/content/browser/renderer_host/render_message_filter.cc
@@ -0,0 +1,1718 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/render_message_filter.h"
+
+#include <map>
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/metrics/histogram.h"
+#include "base/process_util.h"
+#include "base/shared_memory.h"
+#include "base/sys_string_conversions.h"
+#include "base/threading/worker_pool.h"
+#include "base/threading/thread.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/automation/automation_resource_message_filter.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/child_process_security_policy.h"
+#include "chrome/browser/chrome_plugin_browsing_context.h"
+#include "chrome/browser/clipboard_dispatcher.h"
+#include "chrome/browser/download/download_types.h"
+#include "chrome/browser/extensions/extension_message_service.h"
+#include "chrome/browser/host_zoom_map.h"
+#include "chrome/browser/metrics/histogram_synchronizer.h"
+#include "chrome/browser/metrics/user_metrics.h"
+#include "chrome/browser/nacl_host/nacl_process_host.h"
+#include "chrome/browser/net/chrome_url_request_context.h"
+#include "chrome/browser/net/predictor_api.h"
+#include "chrome/browser/notifications/desktop_notification_service.h"
+#include "chrome/browser/notifications/notifications_prefs_cache.h"
+#include "chrome/browser/platform_util.h"
+#include "chrome/browser/plugin_process_host.h"
+#include "chrome/browser/plugin_service.h"
+#include "chrome/browser/ppapi_plugin_process_host.h"
+#include "chrome/browser/printing/print_job_manager.h"
+#include "chrome/browser/printing/printer_query.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/renderer_host/browser_render_process_host.h"
+#include "chrome/browser/spellchecker_platform_engine.h"
+#include "chrome/browser/task_manager/task_manager.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/extension_file_util.h"
+#include "chrome/common/extensions/extension_message_bundle.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/render_messages_params.h"
+#include "chrome/common/url_constants.h"
+#include "content/browser/renderer_host/render_view_host_delegate.h"
+#include "content/browser/renderer_host/render_view_host_notification_task.h"
+#include "content/browser/renderer_host/render_widget_helper.h"
+#include "ipc/ipc_channel_handle.h"
+#include "net/base/cookie_monster.h"
+#include "net/base/io_buffer.h"
+#include "net/base/keygen_handler.h"
+#include "net/base/mime_util.h"
+#include "net/base/net_errors.h"
+#include "net/disk_cache/disk_cache.h"
+#include "net/http/http_cache.h"
+#include "net/http/http_network_layer.h"
+#include "net/url_request/url_request_context.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebNotificationPresenter.h"
+#include "webkit/glue/context_menu.h"
+#include "webkit/glue/webcookie.h"
+#include "webkit/glue/webkit_glue.h"
+#include "webkit/plugins/npapi/plugin_group.h"
+#include "webkit/plugins/npapi/plugin_list.h"
+#include "webkit/plugins/npapi/webplugin.h"
+#include "webkit/plugins/npapi/webplugininfo.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/plugin_selection_policy.h"
+#endif
+#if defined(OS_MACOSX)
+#include "chrome/common/font_descriptor_mac.h"
+#include "chrome/common/font_loader_mac.h"
+#endif
+#if defined(OS_POSIX)
+#include "base/file_descriptor_posix.h"
+#endif
+#if defined(OS_WIN)
+#include "chrome/common/child_process_host.h"
+#endif
+#if defined(USE_NSS)
+#include "chrome/browser/ui/crypto_module_password_dialog.h"
+#endif
+#if defined(USE_TCMALLOC)
+#include "chrome/browser/browser_about_handler.h"
+#endif
+
+using net::CookieStore;
+using WebKit::WebCache;
+
+namespace {
+
+const int kPluginsRefreshThresholdInSeconds = 3;
+
+// Context menus are somewhat complicated. We need to intercept them here on
+// the I/O thread to add any spelling suggestions to them. After that's done,
+// we need to forward the modified message to the UI thread and the normal
+// message forwarding isn't set up for sending modified messages.
+//
+// Therefore, this class dispatches the IPC message to the RenderProcessHost
+// with the given ID (if possible) to emulate the normal dispatch.
+class ContextMenuMessageDispatcher : public Task {
+ public:
+ ContextMenuMessageDispatcher(
+ int render_process_id,
+ const ViewHostMsg_ContextMenu& context_menu_message)
+ : render_process_id_(render_process_id),
+ context_menu_message_(context_menu_message) {
+ }
+
+ void Run() {
+ RenderProcessHost* host =
+ RenderProcessHost::FromID(render_process_id_);
+ if (host)
+ host->OnMessageReceived(context_menu_message_);
+ }
+
+ private:
+ int render_process_id_;
+ const ViewHostMsg_ContextMenu context_menu_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContextMenuMessageDispatcher);
+};
+
+// Completes a clipboard write initiated by the renderer. The write must be
+// performed on the UI thread because the clipboard service from the IO thread
+// cannot create windows so it cannot be the "owner" of the clipboard's
+// contents.
+class WriteClipboardTask : public Task {
+ public:
+ explicit WriteClipboardTask(ui::Clipboard::ObjectMap* objects)
+ : objects_(objects) {}
+ ~WriteClipboardTask() {}
+
+ void Run() {
+ g_browser_process->clipboard()->WriteObjects(*objects_.get());
+ }
+
+ private:
+ scoped_ptr<ui::Clipboard::ObjectMap> objects_;
+};
+
+void RenderParamsFromPrintSettings(const printing::PrintSettings& settings,
+ ViewMsg_Print_Params* params) {
+ DCHECK(params);
+ params->page_size = settings.page_setup_device_units().physical_size();
+ params->printable_size.SetSize(
+ settings.page_setup_device_units().content_area().width(),
+ settings.page_setup_device_units().content_area().height());
+ params->margin_top = settings.page_setup_device_units().content_area().x();
+ params->margin_left = settings.page_setup_device_units().content_area().y();
+ params->dpi = settings.dpi();
+ // Currently hardcoded at 1.25. See PrintSettings' constructor.
+ params->min_shrink = settings.min_shrink;
+ // Currently hardcoded at 2.0. See PrintSettings' constructor.
+ params->max_shrink = settings.max_shrink;
+ // Currently hardcoded at 72dpi. See PrintSettings' constructor.
+ params->desired_dpi = settings.desired_dpi;
+ // Always use an invalid cookie.
+ params->document_cookie = 0;
+ params->selection_only = settings.selection_only;
+ params->supports_alpha_blend = settings.supports_alpha_blend();
+}
+
+// Common functionality for converting a sync renderer message to a callback
+// function in the browser. Derive from this, create it on the heap when
+// issuing your callback. When done, write your reply parameters into
+// reply_msg(), and then call SendReplyAndDeleteThis().
+class RenderMessageCompletionCallback {
+ public:
+ RenderMessageCompletionCallback(RenderMessageFilter* filter,
+ IPC::Message* reply_msg)
+ : filter_(filter),
+ reply_msg_(reply_msg) {
+ }
+
+ virtual ~RenderMessageCompletionCallback() {
+ }
+
+ RenderMessageFilter* filter() { return filter_.get(); }
+ IPC::Message* reply_msg() { return reply_msg_; }
+
+ void SendReplyAndDeleteThis() {
+ filter_->Send(reply_msg_);
+ delete this;
+ }
+
+ private:
+ scoped_refptr<RenderMessageFilter> filter_;
+ IPC::Message* reply_msg_;
+};
+
+class ClearCacheCompletion : public RenderMessageCompletionCallback,
+ public net::CompletionCallback {
+ public:
+ ClearCacheCompletion(RenderMessageFilter* filter,
+ IPC::Message* reply_msg)
+ : RenderMessageCompletionCallback(filter, reply_msg) {
+ }
+
+ virtual void RunWithParams(const Tuple1<int>& params) {
+ ViewHostMsg_ClearCache::WriteReplyParams(reply_msg(), params.a);
+ SendReplyAndDeleteThis();
+ }
+};
+
+class OpenChannelToNpapiPluginCallback : public RenderMessageCompletionCallback,
+ public PluginProcessHost::Client {
+ public:
+ OpenChannelToNpapiPluginCallback(RenderMessageFilter* filter,
+ IPC::Message* reply_msg)
+ : RenderMessageCompletionCallback(filter, reply_msg) {
+ }
+
+ virtual int ID() {
+ return filter()->render_process_id();
+ }
+
+ virtual bool OffTheRecord() {
+ return filter()->off_the_record();
+ }
+
+ virtual void SetPluginInfo(const webkit::npapi::WebPluginInfo& info) {
+ info_ = info;
+ }
+
+ virtual void OnChannelOpened(const IPC::ChannelHandle& handle) {
+ WriteReplyAndDeleteThis(handle);
+ }
+
+ virtual void OnError() {
+ WriteReplyAndDeleteThis(IPC::ChannelHandle());
+ }
+
+ private:
+ void WriteReplyAndDeleteThis(const IPC::ChannelHandle& handle) {
+ ViewHostMsg_OpenChannelToPlugin::WriteReplyParams(reply_msg(),
+ handle, info_);
+ SendReplyAndDeleteThis();
+ }
+
+ webkit::npapi::WebPluginInfo info_;
+};
+
+class OpenChannelToPpapiPluginCallback : public RenderMessageCompletionCallback,
+ public PpapiPluginProcessHost::Client {
+ public:
+ OpenChannelToPpapiPluginCallback(RenderMessageFilter* filter,
+ IPC::Message* reply_msg)
+ : RenderMessageCompletionCallback(filter, reply_msg) {
+ }
+
+ virtual void GetChannelInfo(base::ProcessHandle* renderer_handle,
+ int* renderer_id) {
+ *renderer_handle = filter()->peer_handle();
+ *renderer_id = filter()->render_process_id();
+ }
+
+ virtual void OnChannelOpened(base::ProcessHandle plugin_process_handle,
+ const IPC::ChannelHandle& channel_handle) {
+ ViewHostMsg_OpenChannelToPepperPlugin::WriteReplyParams(
+ reply_msg(), plugin_process_handle, channel_handle);
+ SendReplyAndDeleteThis();
+ }
+};
+
+// Class to assist with clearing out the cache when we want to preserve
+// the sslhostinfo entries. It's not very efficient, but its just for debug.
+class DoomEntriesHelper {
+ public:
+ explicit DoomEntriesHelper(disk_cache::Backend* backend)
+ : backend_(backend),
+ entry_(NULL),
+ iter_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(callback_(this,
+ &DoomEntriesHelper::CacheCallback)),
+ user_callback_(NULL) {
+ }
+
+ void ClearCache(ClearCacheCompletion* callback) {
+ user_callback_ = callback;
+ return CacheCallback(net::OK); // Start clearing the cache.
+ }
+
+ private:
+ void CacheCallback(int result) {
+ do {
+ if (result != net::OK) {
+ user_callback_->RunWithParams(Tuple1<int>(result));
+ delete this;
+ return;
+ }
+
+ if (entry_) {
+ // Doom all entries except those with snapstart information.
+ std::string key = entry_->GetKey();
+ if (key.find("sslhostinfo:") != 0) {
+ entry_->Doom();
+ backend_->EndEnumeration(&iter_);
+ iter_ = NULL; // We invalidated our iterator - start from the top!
+ }
+ entry_->Close();
+ entry_ = NULL;
+ }
+ result = backend_->OpenNextEntry(&iter_, &entry_, &callback_);
+ } while (result != net::ERR_IO_PENDING);
+ }
+
+ disk_cache::Backend* backend_;
+ disk_cache::Entry* entry_;
+ void* iter_;
+ net::CompletionCallbackImpl<DoomEntriesHelper> callback_;
+ ClearCacheCompletion* user_callback_;
+};
+
+} // namespace
+
+RenderMessageFilter::RenderMessageFilter(
+ int render_process_id,
+ PluginService* plugin_service,
+ Profile* profile,
+ RenderWidgetHelper* render_widget_helper)
+ : resource_dispatcher_host_(g_browser_process->resource_dispatcher_host()),
+ plugin_service_(plugin_service),
+ print_job_manager_(g_browser_process->print_job_manager()),
+ profile_(profile),
+ content_settings_(profile->GetHostContentSettingsMap()),
+ ALLOW_THIS_IN_INITIALIZER_LIST(resolve_proxy_msg_helper_(this, NULL)),
+ extensions_request_context_(profile->GetRequestContextForExtensions()),
+ render_widget_helper_(render_widget_helper),
+ notification_prefs_(
+ profile->GetDesktopNotificationService()->prefs_cache()),
+ host_zoom_map_(profile->GetHostZoomMap()),
+ off_the_record_(profile->IsOffTheRecord()),
+ webkit_context_(profile->GetWebKitContext()),
+ render_process_id_(render_process_id) {
+ request_context_ = profile_->GetRequestContext();
+ DCHECK(request_context_);
+
+ render_widget_helper_->Init(render_process_id_, resource_dispatcher_host_);
+#if defined(OS_CHROMEOS)
+ cloud_print_enabled_ = true;
+#else
+ cloud_print_enabled_ = CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableCloudPrint);
+#endif
+}
+
+RenderMessageFilter::~RenderMessageFilter() {
+ // This function should be called on the IO thread.
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+}
+
+// Called on the IPC thread:
+bool RenderMessageFilter::OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(RenderMessageFilter, message, *message_was_ok)
+ // On Linux we need to dispatch these messages to the UI2 thread
+ // because we cannot make X calls from the IO thread. Mac
+ // doesn't have windowed plug-ins so we handle the messages in
+ // the UI thread. On Windows, we intercept the messages and
+ // handle them directly.
+#if !defined(OS_MACOSX)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_GetScreenInfo, OnGetScreenInfo)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_GetWindowRect, OnGetWindowRect)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_GetRootWindowRect,
+ OnGetRootWindowRect)
+#endif
+
+ IPC_MESSAGE_HANDLER(ViewHostMsg_GenerateRoutingID, OnGenerateRoutingID)
+
+ IPC_MESSAGE_HANDLER(ViewHostMsg_CreateWindow, OnMsgCreateWindow)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_CreateWidget, OnMsgCreateWidget)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_CreateFullscreenWidget,
+ OnMsgCreateFullscreenWidget)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_SetCookie, OnSetCookie)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_GetCookies, OnGetCookies)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_GetRawCookies, OnGetRawCookies)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DeleteCookie, OnDeleteCookie)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_CookiesEnabled,
+ OnCookiesEnabled)
+#if defined(OS_MACOSX)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_LoadFont, OnLoadFont)
+#endif
+#if defined(OS_WIN) // This hack is Windows-specific.
+ IPC_MESSAGE_HANDLER(ViewHostMsg_PreCacheFont, OnPreCacheFont)
+#endif
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_GetPlugins, OnGetPlugins)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_GetPluginInfo, OnGetPluginInfo)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DownloadUrl, OnDownloadUrl)
+ IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_ContextMenu,
+ OnReceiveContextMenuMsg(message))
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_OpenChannelToPlugin,
+ OnOpenChannelToPlugin)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_OpenChannelToPepperPlugin,
+ OnOpenChannelToPepperPlugin)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_LaunchNaCl, OnLaunchNaCl)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_SpellChecker_PlatformCheckSpelling,
+ OnPlatformCheckSpelling)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_SpellChecker_PlatformFillSuggestionList,
+ OnPlatformFillSuggestionList)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_GetDocumentTag,
+ OnGetDocumentTag)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DocumentWithTagClosed,
+ OnDocumentWithTagClosed)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ShowSpellingPanel, OnShowSpellingPanel)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateSpellingPanelWithMisspelledWord,
+ OnUpdateSpellingPanelWithMisspelledWord)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DnsPrefetch, OnDnsPrefetch)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_RendererHistograms, OnRendererHistograms)
+ IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_UpdateRect,
+ render_widget_helper_->DidReceiveUpdateMsg(message))
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ClipboardWriteObjectsAsync,
+ OnClipboardWriteObjectsAsync)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ClipboardWriteObjectsSync,
+ OnClipboardWriteObjectsSync)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_ClipboardIsFormatAvailable,
+ OnClipboardIsFormatAvailable)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_ClipboardReadText,
+ OnClipboardReadText)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_ClipboardReadAsciiText,
+ OnClipboardReadAsciiText)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_ClipboardReadHTML,
+ OnClipboardReadHTML)
+#if defined(OS_MACOSX)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ClipboardFindPboardWriteStringAsync,
+ OnClipboardFindPboardWriteString)
+#endif
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_ClipboardReadAvailableTypes,
+ OnClipboardReadAvailableTypes)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_ClipboardReadData,
+ OnClipboardReadData)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_ClipboardReadFilenames,
+ OnClipboardReadFilenames)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_CheckNotificationPermission,
+ OnCheckNotificationPermission)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_RevealFolderInOS, OnRevealFolderInOS)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_GetCPBrowsingContext,
+ OnGetCPBrowsingContext)
+#if defined(OS_WIN)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DuplicateSection, OnDuplicateSection)
+#endif
+#if defined(OS_POSIX)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_AllocateSharedMemoryBuffer,
+ OnAllocateSharedMemoryBuffer)
+#endif
+#if defined(OS_CHROMEOS)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_AllocateTempFileForPrinting,
+ OnAllocateTempFileForPrinting)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_TempFileForPrintingWritten,
+ OnTempFileForPrintingWritten)
+#endif
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ResourceTypeStats, OnResourceTypeStats)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_V8HeapStats, OnV8HeapStats)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DidZoomURL, OnDidZoomURL)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_ResolveProxy, OnResolveProxy)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_GetDefaultPrintSettings,
+ OnGetDefaultPrintSettings)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_ScriptedPrint, OnScriptedPrint)
+#if defined(OS_MACOSX)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_AllocTransportDIB, OnAllocTransportDIB)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_FreeTransportDIB, OnFreeTransportDIB)
+#endif
+ IPC_MESSAGE_HANDLER(ViewHostMsg_OpenChannelToExtension,
+ OnOpenChannelToExtension)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_OpenChannelToTab, OnOpenChannelToTab)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_CloseCurrentConnections,
+ OnCloseCurrentConnections)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_SetCacheMode, OnSetCacheMode)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_ClearCache, OnClearCache)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DidGenerateCacheableMetadata,
+ OnCacheableMetadataAvailable)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_EnableSpdy, OnEnableSpdy)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_Keygen, OnKeygen)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_GetExtensionMessageBundle,
+ OnGetExtensionMessageBundle)
+#if defined(USE_TCMALLOC)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_RendererTcmalloc, OnRendererTcmalloc)
+#endif
+ IPC_MESSAGE_HANDLER(ViewHostMsg_AsyncOpenFile, OnAsyncOpenFile)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP_EX()
+
+ return handled;
+}
+
+void RenderMessageFilter::OnRevealFolderInOS(const FilePath& path) {
+#if defined(OS_MACOSX)
+ const BrowserThread::ID kThreadID = BrowserThread::UI;
+#else
+ const BrowserThread::ID kThreadID = BrowserThread::FILE;
+#endif
+ if (!BrowserThread::CurrentlyOn(kThreadID)) {
+ // Only honor the request if appropriate persmissions are granted.
+ if (ChildProcessSecurityPolicy::GetInstance()->CanReadFile(
+ render_process_id_, path)) {
+ BrowserThread::PostTask(
+ kThreadID, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::OnRevealFolderInOS, path));
+ }
+ return;
+ }
+
+ DCHECK(BrowserThread::CurrentlyOn(kThreadID));
+ platform_util::OpenItem(path);
+}
+
+void RenderMessageFilter::OnDestruct() const {
+ BrowserThread::DeleteOnIOThread::Destruct(this);
+}
+
+void RenderMessageFilter::OnReceiveContextMenuMsg(const IPC::Message& msg) {
+ void* iter = NULL;
+ ContextMenuParams params;
+ if (!IPC::ParamTraits<ContextMenuParams>::Read(&msg, &iter, &params))
+ return;
+
+ // Create a new ViewHostMsg_ContextMenu message.
+ const ViewHostMsg_ContextMenu context_menu_message(msg.routing_id(), params);
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ new ContextMenuMessageDispatcher(
+ render_process_id_, context_menu_message));
+}
+
+void RenderMessageFilter::OnMsgCreateWindow(
+ const ViewHostMsg_CreateWindow_Params& params,
+ int* route_id, int64* cloned_session_storage_namespace_id) {
+ // If the opener is trying to create a background window but doesn't have
+ // the appropriate permission, fail the attempt.
+ if (params.window_container_type == WINDOW_CONTAINER_TYPE_BACKGROUND) {
+ ChromeURLRequestContext* context =
+ GetRequestContextForURL(params.opener_url);
+ if (!context->extension_info_map()->CheckURLAccessToExtensionPermission(
+ params.opener_url, Extension::kBackgroundPermission)) {
+ *route_id = MSG_ROUTING_NONE;
+ return;
+ }
+ }
+
+ *cloned_session_storage_namespace_id =
+ webkit_context_->dom_storage_context()->CloneSessionStorage(
+ params.session_storage_namespace_id);
+ render_widget_helper_->CreateNewWindow(params,
+ peer_handle(),
+ route_id);
+}
+
+void RenderMessageFilter::OnMsgCreateWidget(int opener_id,
+ WebKit::WebPopupType popup_type,
+ int* route_id) {
+ render_widget_helper_->CreateNewWidget(opener_id, popup_type, route_id);
+}
+
+void RenderMessageFilter::OnMsgCreateFullscreenWidget(int opener_id,
+ int* route_id) {
+ render_widget_helper_->CreateNewFullscreenWidget(opener_id, route_id);
+}
+
+void RenderMessageFilter::OnSetCookie(const IPC::Message& message,
+ const GURL& url,
+ const GURL& first_party_for_cookies,
+ const std::string& cookie) {
+ ChromeURLRequestContext* context = GetRequestContextForURL(url);
+
+ SetCookieCompletion* callback = new SetCookieCompletion(
+ render_process_id_, message.routing_id(), url, cookie, context);
+
+ // If this render view is associated with an automation channel, aka
+ // ChromeFrame then we need to set cookies in the external host.
+ if (!AutomationResourceMessageFilter::SetCookiesForUrl(url,
+ cookie,
+ callback)) {
+ int policy = net::OK;
+ if (context->cookie_policy()) {
+ policy = context->cookie_policy()->CanSetCookie(
+ url, first_party_for_cookies, cookie, callback);
+ if (policy == net::ERR_IO_PENDING)
+ return;
+ }
+ callback->Run(policy);
+ }
+}
+
+void RenderMessageFilter::OnGetCookies(const GURL& url,
+ const GURL& first_party_for_cookies,
+ IPC::Message* reply_msg) {
+ ChromeURLRequestContext* context = GetRequestContextForURL(url);
+
+ GetCookiesCompletion* callback = new GetCookiesCompletion(
+ render_process_id_, reply_msg->routing_id(), url, reply_msg, this,
+ context, false);
+
+ // If this render view is associated with an automation channel, aka
+ // ChromeFrame then we need to retrieve cookies from the external host.
+ if (!AutomationResourceMessageFilter::GetCookiesForUrl(url, callback)) {
+ int policy = net::OK;
+ if (context->cookie_policy()) {
+ policy = context->cookie_policy()->CanGetCookies(
+ url, first_party_for_cookies, callback);
+ if (policy == net::ERR_IO_PENDING) {
+ Send(new ViewMsg_SignalCookiePromptEvent());
+ return;
+ }
+ }
+ callback->Run(policy);
+ }
+}
+
+void RenderMessageFilter::OnGetRawCookies(
+ const GURL& url,
+ const GURL& first_party_for_cookies,
+ IPC::Message* reply_msg) {
+
+ ChromeURLRequestContext* context = GetRequestContextForURL(url);
+
+ // Only return raw cookies to trusted renderers or if this request is
+ // not targeted to an an external host like ChromeFrame.
+ // TODO(ananta) We need to support retreiving raw cookies from external
+ // hosts.
+ if (!ChildProcessSecurityPolicy::GetInstance()->CanReadRawCookies(
+ render_process_id_)) {
+ ViewHostMsg_GetRawCookies::WriteReplyParams(
+ reply_msg,
+ std::vector<webkit_glue::WebCookie>());
+ Send(reply_msg);
+ return;
+ }
+
+ GetCookiesCompletion* callback = new GetCookiesCompletion(
+ render_process_id_, reply_msg->routing_id(), url, reply_msg, this,
+ context, true);
+
+ // We check policy here to avoid sending back cookies that would not normally
+ // be applied to outbound requests for the given URL. Since this cookie info
+ // is visible in the developer tools, it is helpful to make it match reality.
+ int policy = net::OK;
+ if (context->cookie_policy()) {
+ policy = context->cookie_policy()->CanGetCookies(
+ url, first_party_for_cookies, callback);
+ if (policy == net::ERR_IO_PENDING) {
+ Send(new ViewMsg_SignalCookiePromptEvent());
+ return;
+ }
+ }
+ callback->Run(policy);
+}
+
+void RenderMessageFilter::OnDeleteCookie(const GURL& url,
+ const std::string& cookie_name) {
+ net::URLRequestContext* context = GetRequestContextForURL(url);
+ context->cookie_store()->DeleteCookie(url, cookie_name);
+}
+
+void RenderMessageFilter::OnCookiesEnabled(
+ const GURL& url,
+ const GURL& first_party_for_cookies,
+ IPC::Message* reply_msg) {
+ net::URLRequestContext* context = GetRequestContextForURL(url);
+ CookiesEnabledCompletion* callback =
+ new CookiesEnabledCompletion(reply_msg, this);
+ int policy = net::OK;
+ // TODO(ananta): If this render view is associated with an automation channel,
+ // aka ChromeFrame then we need to retrieve cookie settings from the external
+ // host.
+ if (context->cookie_policy()) {
+ policy = context->cookie_policy()->CanGetCookies(
+ url, first_party_for_cookies, callback);
+ if (policy == net::ERR_IO_PENDING) {
+ Send(new ViewMsg_SignalCookiePromptEvent());
+ return; // CanGetCookies will call our callback in this case.
+ }
+ }
+ callback->Run(policy);
+}
+
+#if defined(OS_MACOSX)
+void RenderMessageFilter::OnLoadFont(const FontDescriptor& font,
+ uint32* handle_size,
+ base::SharedMemoryHandle* handle) {
+ base::SharedMemory font_data;
+ uint32 font_data_size = 0;
+ bool ok = FontLoader::LoadFontIntoBuffer(font.nsFont(), &font_data,
+ &font_data_size);
+ if (!ok || font_data_size == 0) {
+ LOG(ERROR) << "Couldn't load font data for " << font.font_name <<
+ " ok=" << ok << " font_data_size=" << font_data_size;
+ *handle_size = 0;
+ *handle = base::SharedMemory::NULLHandle();
+ return;
+ }
+
+ *handle_size = font_data_size;
+ font_data.GiveToProcess(base::GetCurrentProcessHandle(), handle);
+}
+#endif // OS_MACOSX
+
+#if defined(OS_WIN) // This hack is Windows-specific.
+void RenderMessageFilter::OnPreCacheFont(LOGFONT font) {
+ ChildProcessHost::PreCacheFont(font);
+}
+#endif // OS_WIN
+
+void RenderMessageFilter::OnGetPlugins(bool refresh,
+ IPC::Message* reply_msg) {
+ // Don't refresh if the specified threshold has not been passed. Note that
+ // this check is performed before off-loading to the file thread. The reason
+ // we do this is that some pages tend to request that the list of plugins be
+ // refreshed at an excessive rate. This instigates disk scanning, as the list
+ // is accumulated by doing multiple reads from disk. This effect is
+ // multiplied when we have several pages requesting this operation.
+ if (refresh) {
+ const base::TimeDelta threshold = base::TimeDelta::FromSeconds(
+ kPluginsRefreshThresholdInSeconds);
+ const base::TimeTicks now = base::TimeTicks::Now();
+ if (now - last_plugin_refresh_time_ < threshold)
+ refresh = false; // Ignore refresh request; threshold not exceeded yet.
+ else
+ last_plugin_refresh_time_ = now;
+ }
+
+ // Can't load plugins on IO thread, so go to the FILE thread.
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::OnGetPluginsOnFileThread, refresh,
+ reply_msg));
+}
+
+void RenderMessageFilter::OnGetPluginsOnFileThread(
+ bool refresh, IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ std::vector<webkit::npapi::WebPluginInfo> plugins;
+ webkit::npapi::PluginList::Singleton()->GetEnabledPlugins(refresh, &plugins);
+ ViewHostMsg_GetPlugins::WriteReplyParams(reply_msg, plugins);
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ NewRunnableMethod(this, &RenderMessageFilter::Send, reply_msg));
+}
+
+void RenderMessageFilter::OnGetPluginInfo(int routing_id,
+ const GURL& url,
+ const GURL& policy_url,
+ const std::string& mime_type,
+ IPC::Message* reply_msg) {
+ // The PluginService::GetFirstAllowedPluginInfo may need to load the
+ // plugins. Don't do it on the IO thread.
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::OnGetPluginInfoOnFileThread,
+ routing_id, url, policy_url, mime_type, reply_msg));
+}
+
+void RenderMessageFilter::OnGetPluginInfoOnFileThread(
+ int render_view_id,
+ const GURL& url,
+ const GURL& policy_url,
+ const std::string& mime_type,
+ IPC::Message* reply_msg) {
+ std::string actual_mime_type;
+ webkit::npapi::WebPluginInfo info;
+ bool found = plugin_service_->GetFirstAllowedPluginInfo(
+ render_process_id_, render_view_id, url, mime_type, &info,
+ &actual_mime_type);
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::OnGotPluginInfo,
+ found, info, actual_mime_type, policy_url, reply_msg));
+}
+
+void RenderMessageFilter::OnGotPluginInfo(
+ bool found,
+ const webkit::npapi::WebPluginInfo& info,
+ const std::string& actual_mime_type,
+ const GURL& policy_url,
+ IPC::Message* reply_msg) {
+ ContentSetting setting = CONTENT_SETTING_DEFAULT;
+ webkit::npapi::WebPluginInfo info_copy = info;
+ if (found) {
+ // TODO(mpcomplete): The plugin service should do this check. We should
+ // not be calling the PluginList directly.
+ if (!plugin_service_->PrivatePluginAllowedForURL(
+ info_copy.path, policy_url))
+ info_copy.enabled |= webkit::npapi::WebPluginInfo::POLICY_DISABLED;
+ std::string resource =
+ webkit::npapi::PluginList::Singleton()->GetPluginGroupIdentifier(
+ info_copy);
+ setting = content_settings_->GetContentSetting(
+ policy_url,
+ CONTENT_SETTINGS_TYPE_PLUGINS,
+ resource);
+ }
+
+ ViewHostMsg_GetPluginInfo::WriteReplyParams(
+ reply_msg, found, info_copy, setting, actual_mime_type);
+ Send(reply_msg);
+}
+
+void RenderMessageFilter::OnOpenChannelToPlugin(int routing_id,
+ const GURL& url,
+ const std::string& mime_type,
+ IPC::Message* reply_msg) {
+ plugin_service_->OpenChannelToNpapiPlugin(
+ render_process_id_, routing_id, url, mime_type,
+ new OpenChannelToNpapiPluginCallback(this, reply_msg));
+}
+
+void RenderMessageFilter::OnOpenChannelToPepperPlugin(
+ const FilePath& path,
+ IPC::Message* reply_msg) {
+ plugin_service_->OpenChannelToPpapiPlugin(
+ path, new OpenChannelToPpapiPluginCallback(this, reply_msg));
+}
+
+void RenderMessageFilter::OnLaunchNaCl(
+ const std::wstring& url, int channel_descriptor, IPC::Message* reply_msg) {
+ NaClProcessHost* host = new NaClProcessHost(resource_dispatcher_host_, url);
+ host->Launch(this, channel_descriptor, reply_msg);
+}
+
+void RenderMessageFilter::OnGenerateRoutingID(int* route_id) {
+ *route_id = render_widget_helper_->GetNextRoutingID();
+}
+
+void RenderMessageFilter::OnDownloadUrl(const IPC::Message& message,
+ const GURL& url,
+ const GURL& referrer) {
+ net::URLRequestContext* context = request_context_->GetURLRequestContext();
+
+ // Don't show "Save As" UI.
+ bool prompt_for_save_location = false;
+ resource_dispatcher_host_->BeginDownload(url,
+ referrer,
+ DownloadSaveInfo(),
+ prompt_for_save_location,
+ render_process_id_,
+ message.routing_id(),
+ context);
+}
+
+void RenderMessageFilter::OnClipboardWriteObjectsSync(
+ const ui::Clipboard::ObjectMap& objects,
+ base::SharedMemoryHandle bitmap_handle) {
+ DCHECK(base::SharedMemory::IsHandleValid(bitmap_handle))
+ << "Bad bitmap handle";
+ // We cannot write directly from the IO thread, and cannot service the IPC
+ // on the UI thread. We'll copy the relevant data and get a handle to any
+ // shared memory so it doesn't go away when we resume the renderer, and post
+ // a task to perform the write on the UI thread.
+ ui::Clipboard::ObjectMap* long_living_objects =
+ new ui::Clipboard::ObjectMap(objects);
+
+ // Splice the shared memory handle into the clipboard data.
+ ui::Clipboard::ReplaceSharedMemHandle(long_living_objects, bitmap_handle,
+ peer_handle());
+
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ new WriteClipboardTask(long_living_objects));
+}
+
+void RenderMessageFilter::OnClipboardWriteObjectsAsync(
+ const ui::Clipboard::ObjectMap& objects) {
+ // We cannot write directly from the IO thread, and cannot service the IPC
+ // on the UI thread. We'll copy the relevant data and post a task to preform
+ // the write on the UI thread.
+ ui::Clipboard::ObjectMap* long_living_objects =
+ new ui::Clipboard::ObjectMap(objects);
+
+ // This async message doesn't support shared-memory based bitmaps; they must
+ // be removed otherwise we might dereference a rubbish pointer.
+ long_living_objects->erase(ui::Clipboard::CBF_SMBITMAP);
+
+ BrowserThread::PostTask(
+ BrowserThread::UI,
+ FROM_HERE,
+ new WriteClipboardTask(long_living_objects));
+}
+
+#if !defined(USE_X11)
+// On non-X11 platforms, clipboard actions can be performed on the IO thread.
+// On X11, since the clipboard is linked with GTK, we either have to do this
+// with GTK on the UI thread, or with Xlib on the BACKGROUND_X11 thread. In an
+// ideal world, we would do the latter. However, for now we're going to
+// terminate these calls on the UI thread. This risks deadlock in the case of
+// plugins, but it's better than crashing which is what doing on the IO thread
+// gives us.
+//
+// See resource_message_filter_gtk.cc for the Linux implementation of these
+// functions.
+
+void RenderMessageFilter::OnClipboardIsFormatAvailable(
+ ui::Clipboard::FormatType format, ui::Clipboard::Buffer buffer,
+ IPC::Message* reply) {
+ const bool result = GetClipboard()->IsFormatAvailable(format, buffer);
+ ViewHostMsg_ClipboardIsFormatAvailable::WriteReplyParams(reply, result);
+ Send(reply);
+}
+
+void RenderMessageFilter::OnClipboardReadText(ui::Clipboard::Buffer buffer,
+ IPC::Message* reply) {
+ string16 result;
+ GetClipboard()->ReadText(buffer, &result);
+ ViewHostMsg_ClipboardReadText::WriteReplyParams(reply, result);
+ Send(reply);
+}
+
+void RenderMessageFilter::OnClipboardReadAsciiText(ui::Clipboard::Buffer buffer,
+ IPC::Message* reply) {
+ std::string result;
+ GetClipboard()->ReadAsciiText(buffer, &result);
+ ViewHostMsg_ClipboardReadAsciiText::WriteReplyParams(reply, result);
+ Send(reply);
+}
+
+void RenderMessageFilter::OnClipboardReadHTML(ui::Clipboard::Buffer buffer,
+ IPC::Message* reply) {
+ std::string src_url_str;
+ string16 markup;
+ GetClipboard()->ReadHTML(buffer, &markup, &src_url_str);
+ const GURL src_url = GURL(src_url_str);
+
+ ViewHostMsg_ClipboardReadHTML::WriteReplyParams(reply, markup, src_url);
+ Send(reply);
+}
+
+void RenderMessageFilter::OnClipboardReadAvailableTypes(
+ ui::Clipboard::Buffer buffer, IPC::Message* reply) {
+ std::vector<string16> types;
+ bool contains_filenames = false;
+ bool result = ClipboardDispatcher::ReadAvailableTypes(
+ buffer, &types, &contains_filenames);
+ ViewHostMsg_ClipboardReadAvailableTypes::WriteReplyParams(
+ reply, result, types, contains_filenames);
+ Send(reply);
+}
+
+void RenderMessageFilter::OnClipboardReadData(
+ ui::Clipboard::Buffer buffer, const string16& type, IPC::Message* reply) {
+ string16 data;
+ string16 metadata;
+ bool result = ClipboardDispatcher::ReadData(buffer, type, &data, &metadata);
+ ViewHostMsg_ClipboardReadData::WriteReplyParams(
+ reply, result, data, metadata);
+ Send(reply);
+}
+
+void RenderMessageFilter::OnClipboardReadFilenames(
+ ui::Clipboard::Buffer buffer, IPC::Message* reply) {
+ std::vector<string16> filenames;
+ bool result = ClipboardDispatcher::ReadFilenames(buffer, &filenames);
+ ViewHostMsg_ClipboardReadFilenames::WriteReplyParams(
+ reply, result, filenames);
+ Send(reply);
+}
+
+#endif
+
+void RenderMessageFilter::OnCheckNotificationPermission(
+ const GURL& source_url, int* result) {
+ *result = WebKit::WebNotificationPresenter::PermissionNotAllowed;
+
+ ChromeURLRequestContext* context = GetRequestContextForURL(source_url);
+ if (context->extension_info_map()->CheckURLAccessToExtensionPermission(
+ source_url, Extension::kNotificationPermission)) {
+ *result = WebKit::WebNotificationPresenter::PermissionAllowed;
+ return;
+ }
+
+ // Fall back to the regular notification preferences, which works on an
+ // origin basis.
+ *result = notification_prefs_->HasPermission(source_url.GetOrigin());
+}
+
+void RenderMessageFilter::OnGetCPBrowsingContext(uint32* context) {
+ // Always allocate a new context when a plugin requests one, since it needs to
+ // be unique for that plugin instance.
+ *context = CPBrowsingContextManager::GetInstance()->Allocate(
+ request_context_->GetURLRequestContext());
+}
+
+#if defined(OS_WIN)
+void RenderMessageFilter::OnDuplicateSection(
+ base::SharedMemoryHandle renderer_handle,
+ base::SharedMemoryHandle* browser_handle) {
+ // Duplicate the handle in this process right now so the memory is kept alive
+ // (even if it is not mapped)
+ base::SharedMemory shared_buf(renderer_handle, true, peer_handle());
+ shared_buf.GiveToProcess(base::GetCurrentProcessHandle(), browser_handle);
+}
+#endif
+
+#if defined(OS_POSIX)
+void RenderMessageFilter::OnAllocateSharedMemoryBuffer(
+ uint32 buffer_size,
+ base::SharedMemoryHandle* handle) {
+ base::SharedMemory shared_buf;
+ if (!shared_buf.CreateAndMapAnonymous(buffer_size)) {
+ *handle = base::SharedMemory::NULLHandle();
+ NOTREACHED() << "Cannot map shared memory buffer";
+ return;
+ }
+ shared_buf.GiveToProcess(base::GetCurrentProcessHandle(), handle);
+}
+#endif
+
+void RenderMessageFilter::OnResourceTypeStats(
+ const WebCache::ResourceTypeStats& stats) {
+ HISTOGRAM_COUNTS("WebCoreCache.ImagesSizeKB",
+ static_cast<int>(stats.images.size / 1024));
+ HISTOGRAM_COUNTS("WebCoreCache.CSSStylesheetsSizeKB",
+ static_cast<int>(stats.cssStyleSheets.size / 1024));
+ HISTOGRAM_COUNTS("WebCoreCache.ScriptsSizeKB",
+ static_cast<int>(stats.scripts.size / 1024));
+ HISTOGRAM_COUNTS("WebCoreCache.XSLStylesheetsSizeKB",
+ static_cast<int>(stats.xslStyleSheets.size / 1024));
+ HISTOGRAM_COUNTS("WebCoreCache.FontsSizeKB",
+ static_cast<int>(stats.fonts.size / 1024));
+ // We need to notify the TaskManager of these statistics from the UI
+ // thread.
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableFunction(
+ &RenderMessageFilter::OnResourceTypeStatsOnUIThread,
+ stats,
+ base::GetProcId(peer_handle())));
+}
+
+void RenderMessageFilter::OnResourceTypeStatsOnUIThread(
+ const WebCache::ResourceTypeStats& stats, base::ProcessId renderer_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ TaskManager::GetInstance()->model()->NotifyResourceTypeStats(
+ renderer_id, stats);
+}
+
+
+void RenderMessageFilter::OnV8HeapStats(int v8_memory_allocated,
+ int v8_memory_used) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableFunction(&RenderMessageFilter::OnV8HeapStatsOnUIThread,
+ v8_memory_allocated,
+ v8_memory_used,
+ base::GetProcId(peer_handle())));
+}
+
+// static
+void RenderMessageFilter::OnV8HeapStatsOnUIThread(
+ int v8_memory_allocated, int v8_memory_used, base::ProcessId renderer_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ TaskManager::GetInstance()->model()->NotifyV8HeapStats(
+ renderer_id,
+ static_cast<size_t>(v8_memory_allocated),
+ static_cast<size_t>(v8_memory_used));
+}
+
+void RenderMessageFilter::OnDidZoomURL(const IPC::Message& message,
+ double zoom_level,
+ bool remember,
+ const GURL& url) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(this,
+ &RenderMessageFilter::UpdateHostZoomLevelsOnUIThread,
+ zoom_level, remember, url, render_process_id_, message.routing_id()));
+}
+
+void RenderMessageFilter::UpdateHostZoomLevelsOnUIThread(
+ double zoom_level,
+ bool remember,
+ const GURL& url,
+ int render_process_id,
+ int render_view_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (remember) {
+ host_zoom_map_->SetZoomLevel(url, zoom_level);
+ // Notify renderers from this profile.
+ for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator());
+ !i.IsAtEnd(); i.Advance()) {
+ RenderProcessHost* render_process_host = i.GetCurrentValue();
+ if (render_process_host->profile() == profile_) {
+ render_process_host->Send(
+ new ViewMsg_SetZoomLevelForCurrentURL(url, zoom_level));
+ }
+ }
+ } else {
+ host_zoom_map_->SetTemporaryZoomLevel(
+ render_process_id, render_view_id, zoom_level);
+ }
+}
+
+void RenderMessageFilter::OnResolveProxy(const GURL& url,
+ IPC::Message* reply_msg) {
+ resolve_proxy_msg_helper_.Start(url, reply_msg);
+}
+
+void RenderMessageFilter::OnResolveProxyCompleted(
+ IPC::Message* reply_msg,
+ int result,
+ const std::string& proxy_list) {
+ ViewHostMsg_ResolveProxy::WriteReplyParams(reply_msg, result, proxy_list);
+ Send(reply_msg);
+}
+
+void RenderMessageFilter::OnGetDefaultPrintSettings(IPC::Message* reply_msg) {
+ scoped_refptr<printing::PrinterQuery> printer_query;
+ if (!print_job_manager_->printing_enabled()) {
+ // Reply with NULL query.
+ OnGetDefaultPrintSettingsReply(printer_query, reply_msg);
+ return;
+ }
+
+ print_job_manager_->PopPrinterQuery(0, &printer_query);
+ if (!printer_query.get()) {
+ printer_query = new printing::PrinterQuery;
+ }
+
+ CancelableTask* task = NewRunnableMethod(
+ this,
+ &RenderMessageFilter::OnGetDefaultPrintSettingsReply,
+ printer_query,
+ reply_msg);
+ // Loads default settings. This is asynchronous, only the IPC message sender
+ // will hang until the settings are retrieved.
+ printer_query->GetSettings(printing::PrinterQuery::DEFAULTS,
+ NULL,
+ 0,
+ false,
+ true,
+ task);
+}
+
+void RenderMessageFilter::OnGetDefaultPrintSettingsReply(
+ scoped_refptr<printing::PrinterQuery> printer_query,
+ IPC::Message* reply_msg) {
+ ViewMsg_Print_Params params;
+ if (!printer_query.get() ||
+ printer_query->last_status() != printing::PrintingContext::OK) {
+ memset(&params, 0, sizeof(params));
+ } else {
+ RenderParamsFromPrintSettings(printer_query->settings(), &params);
+ params.document_cookie = printer_query->cookie();
+ }
+ ViewHostMsg_GetDefaultPrintSettings::WriteReplyParams(reply_msg, params);
+ Send(reply_msg);
+ // If printing was enabled.
+ if (printer_query.get()) {
+ // If user hasn't cancelled.
+ if (printer_query->cookie() && printer_query->settings().dpi()) {
+ print_job_manager_->QueuePrinterQuery(printer_query.get());
+ } else {
+ printer_query->StopWorker();
+ }
+ }
+}
+
+void RenderMessageFilter::OnScriptedPrint(
+ const ViewHostMsg_ScriptedPrint_Params& params,
+ IPC::Message* reply_msg) {
+ gfx::NativeView host_view =
+ gfx::NativeViewFromIdInBrowser(params.host_window_id);
+
+ scoped_refptr<printing::PrinterQuery> printer_query;
+ print_job_manager_->PopPrinterQuery(params.cookie, &printer_query);
+ if (!printer_query.get()) {
+ printer_query = new printing::PrinterQuery;
+ }
+
+ CancelableTask* task = NewRunnableMethod(
+ this,
+ &RenderMessageFilter::OnScriptedPrintReply,
+ printer_query,
+ params.routing_id,
+ reply_msg);
+
+ printer_query->GetSettings(printing::PrinterQuery::ASK_USER,
+ host_view,
+ params.expected_pages_count,
+ params.has_selection,
+ params.use_overlays,
+ task);
+}
+
+void RenderMessageFilter::OnScriptedPrintReply(
+ scoped_refptr<printing::PrinterQuery> printer_query,
+ int routing_id,
+ IPC::Message* reply_msg) {
+ ViewMsg_PrintPages_Params params;
+ if (printer_query->last_status() != printing::PrintingContext::OK ||
+ !printer_query->settings().dpi()) {
+ memset(&params, 0, sizeof(params));
+ } else {
+ RenderParamsFromPrintSettings(printer_query->settings(), &params.params);
+ params.params.document_cookie = printer_query->cookie();
+ params.pages =
+ printing::PageRange::GetPages(printer_query->settings().ranges);
+ }
+ ViewHostMsg_ScriptedPrint::WriteReplyParams(reply_msg, params);
+ Send(reply_msg);
+ if (params.params.dpi && params.params.document_cookie) {
+ print_job_manager_->QueuePrinterQuery(printer_query.get());
+ } else {
+ printer_query->StopWorker();
+ }
+}
+
+// static
+ui::Clipboard* RenderMessageFilter::GetClipboard() {
+ // We have a static instance of the clipboard service for use by all message
+ // filters. This instance lives for the life of the browser processes.
+ static ui::Clipboard* clipboard = new ui::Clipboard;
+
+ return clipboard;
+}
+
+ChromeURLRequestContext* RenderMessageFilter::GetRequestContextForURL(
+ const GURL& url) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ URLRequestContextGetter* context_getter =
+ url.SchemeIs(chrome::kExtensionScheme) ?
+ extensions_request_context_ : request_context_;
+ return static_cast<ChromeURLRequestContext*>(
+ context_getter->GetURLRequestContext());
+}
+
+void RenderMessageFilter::OnPlatformCheckSpelling(const string16& word,
+ int tag,
+ bool* correct) {
+ *correct = SpellCheckerPlatform::CheckSpelling(word, tag);
+}
+
+void RenderMessageFilter::OnPlatformFillSuggestionList(
+ const string16& word,
+ std::vector<string16>* suggestions) {
+ SpellCheckerPlatform::FillSuggestionList(word, suggestions);
+}
+
+void RenderMessageFilter::OnGetDocumentTag(IPC::Message* reply_msg) {
+ int tag = SpellCheckerPlatform::GetDocumentTag();
+ ViewHostMsg_GetDocumentTag::WriteReplyParams(reply_msg, tag);
+ Send(reply_msg);
+ return;
+}
+
+void RenderMessageFilter::OnDocumentWithTagClosed(int tag) {
+ SpellCheckerPlatform::CloseDocumentWithTag(tag);
+}
+
+void RenderMessageFilter::OnShowSpellingPanel(bool show) {
+ SpellCheckerPlatform::ShowSpellingPanel(show);
+}
+
+void RenderMessageFilter::OnUpdateSpellingPanelWithMisspelledWord(
+ const string16& word) {
+ SpellCheckerPlatform::UpdateSpellingPanelWithMisspelledWord(word);
+}
+
+void RenderMessageFilter::OnDnsPrefetch(
+ const std::vector<std::string>& hostnames) {
+ chrome_browser_net::DnsPrefetchList(hostnames);
+}
+
+void RenderMessageFilter::OnRendererHistograms(
+ int sequence_number,
+ const std::vector<std::string>& histograms) {
+ HistogramSynchronizer::DeserializeHistogramList(sequence_number, histograms);
+}
+
+#if defined(OS_MACOSX)
+void RenderMessageFilter::OnAllocTransportDIB(
+ size_t size, bool cache_in_browser, TransportDIB::Handle* handle) {
+ render_widget_helper_->AllocTransportDIB(size, cache_in_browser, handle);
+}
+
+void RenderMessageFilter::OnFreeTransportDIB(
+ TransportDIB::Id dib_id) {
+ render_widget_helper_->FreeTransportDIB(dib_id);
+}
+#endif
+
+void RenderMessageFilter::OnOpenChannelToExtension(
+ int routing_id, const std::string& source_extension_id,
+ const std::string& target_extension_id,
+ const std::string& channel_name, int* port_id) {
+ int port2_id;
+ ExtensionMessageService::AllocatePortIdPair(port_id, &port2_id);
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::OpenChannelToExtensionOnUIThread,
+ render_process_id_, routing_id, port2_id, source_extension_id,
+ target_extension_id, channel_name));
+}
+
+void RenderMessageFilter::OpenChannelToExtensionOnUIThread(
+ int source_process_id, int source_routing_id,
+ int receiver_port_id,
+ const std::string& source_extension_id,
+ const std::string& target_extension_id,
+ const std::string& channel_name) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ profile_->GetExtensionMessageService()->OpenChannelToExtension(
+ source_process_id, source_routing_id, receiver_port_id,
+ source_extension_id, target_extension_id, channel_name);
+}
+
+void RenderMessageFilter::OnOpenChannelToTab(
+ int routing_id, int tab_id, const std::string& extension_id,
+ const std::string& channel_name, int* port_id) {
+ int port2_id;
+ ExtensionMessageService::AllocatePortIdPair(port_id, &port2_id);
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::OpenChannelToTabOnUIThread,
+ render_process_id_, routing_id, port2_id, tab_id, extension_id,
+ channel_name));
+}
+
+void RenderMessageFilter::OpenChannelToTabOnUIThread(
+ int source_process_id, int source_routing_id,
+ int receiver_port_id,
+ int tab_id,
+ const std::string& extension_id,
+ const std::string& channel_name) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ profile_->GetExtensionMessageService()->OpenChannelToTab(
+ source_process_id, source_routing_id, receiver_port_id,
+ tab_id, extension_id, channel_name);
+}
+
+bool RenderMessageFilter::CheckBenchmarkingEnabled() const {
+ static bool checked = false;
+ static bool result = false;
+ if (!checked) {
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ result = command_line.HasSwitch(switches::kEnableBenchmarking);
+ checked = true;
+ }
+ return result;
+}
+
+void RenderMessageFilter::OnCloseCurrentConnections() {
+ // This function is disabled unless the user has enabled
+ // benchmarking extensions.
+ if (!CheckBenchmarkingEnabled())
+ return;
+ request_context_->GetURLRequestContext()->
+ http_transaction_factory()->GetCache()->CloseCurrentConnections();
+}
+
+void RenderMessageFilter::OnSetCacheMode(bool enabled) {
+ // This function is disabled unless the user has enabled
+ // benchmarking extensions.
+ if (!CheckBenchmarkingEnabled())
+ return;
+
+ net::HttpCache::Mode mode = enabled ?
+ net::HttpCache::NORMAL : net::HttpCache::DISABLE;
+ net::HttpCache* http_cache = request_context_->GetURLRequestContext()->
+ http_transaction_factory()->GetCache();
+ http_cache->set_mode(mode);
+}
+
+void RenderMessageFilter::OnClearCache(bool preserve_ssl_host_info,
+ IPC::Message* reply_msg) {
+ // This function is disabled unless the user has enabled
+ // benchmarking extensions.
+ int rv = -1;
+ if (CheckBenchmarkingEnabled()) {
+ disk_cache::Backend* backend = request_context_->GetURLRequestContext()->
+ http_transaction_factory()->GetCache()->GetCurrentBackend();
+ if (backend) {
+ ClearCacheCompletion* callback =
+ new ClearCacheCompletion(this, reply_msg);
+ if (preserve_ssl_host_info) {
+ DoomEntriesHelper* helper = new DoomEntriesHelper(backend);
+ helper->ClearCache(callback); // Will self clean.
+ return;
+ } else {
+ rv = backend->DoomAllEntries(callback);
+ if (rv == net::ERR_IO_PENDING) {
+ // The callback will send the reply.
+ return;
+ }
+ // Completed synchronously, no need for the callback.
+ delete callback;
+ }
+ }
+ }
+ ViewHostMsg_ClearCache::WriteReplyParams(reply_msg, rv);
+ Send(reply_msg);
+}
+
+bool RenderMessageFilter::CheckPreparsedJsCachingEnabled() const {
+ static bool checked = false;
+ static bool result = false;
+ if (!checked) {
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess();
+ result = command_line.HasSwitch(switches::kEnablePreparsedJsCaching);
+ checked = true;
+ }
+ return result;
+}
+
+void RenderMessageFilter::OnCacheableMetadataAvailable(
+ const GURL& url,
+ double expected_response_time,
+ const std::vector<char>& data) {
+ if (!CheckPreparsedJsCachingEnabled())
+ return;
+
+ net::HttpCache* cache = request_context_->GetURLRequestContext()->
+ http_transaction_factory()->GetCache();
+ DCHECK(cache);
+
+ scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(data.size()));
+ memcpy(buf->data(), &data.front(), data.size());
+ cache->WriteMetadata(
+ url, base::Time::FromDoubleT(expected_response_time), buf, data.size());
+}
+
+// TODO(lzheng): This only enables spdy over ssl. Enable spdy for http
+// when needed.
+void RenderMessageFilter::OnEnableSpdy(bool enable) {
+ if (enable) {
+ net::HttpNetworkLayer::EnableSpdy("npn,force-alt-protocols");
+ } else {
+ net::HttpNetworkLayer::EnableSpdy("npn-http");
+ }
+}
+
+void RenderMessageFilter::OnKeygen(uint32 key_size_index,
+ const std::string& challenge_string,
+ const GURL& url,
+ IPC::Message* reply_msg) {
+ // Map displayed strings indicating level of keysecurity in the <keygen>
+ // menu to the key size in bits. (See SSLKeyGeneratorChromium.cpp in WebCore.)
+ int key_size_in_bits;
+ switch (key_size_index) {
+ case 0:
+ key_size_in_bits = 2048;
+ break;
+ case 1:
+ key_size_in_bits = 1024;
+ break;
+ default:
+ DCHECK(false) << "Illegal key_size_index " << key_size_index;
+ ViewHostMsg_Keygen::WriteReplyParams(reply_msg, std::string());
+ Send(reply_msg);
+ return;
+ }
+
+ VLOG(1) << "Dispatching keygen task to worker pool.";
+ // Dispatch to worker pool, so we do not block the IO thread.
+ if (!base::WorkerPool::PostTask(
+ FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::OnKeygenOnWorkerThread,
+ key_size_in_bits, challenge_string, url, reply_msg),
+ true)) {
+ NOTREACHED() << "Failed to dispatch keygen task to worker pool";
+ ViewHostMsg_Keygen::WriteReplyParams(reply_msg, std::string());
+ Send(reply_msg);
+ return;
+ }
+}
+
+void RenderMessageFilter::OnKeygenOnWorkerThread(
+ int key_size_in_bits,
+ const std::string& challenge_string,
+ const GURL& url,
+ IPC::Message* reply_msg) {
+ DCHECK(reply_msg);
+
+ // Generate a signed public key and challenge, then send it back.
+ net::KeygenHandler keygen_handler(key_size_in_bits, challenge_string, url);
+
+#if defined(USE_NSS)
+ // Attach a password delegate so we can authenticate.
+ keygen_handler.set_crypto_module_password_delegate(
+ browser::NewCryptoModuleBlockingDialogDelegate(
+ browser::kCryptoModulePasswordKeygen, url.host()));
+#endif // defined(USE_NSS)
+
+ ViewHostMsg_Keygen::WriteReplyParams(
+ reply_msg,
+ keygen_handler.GenKeyAndSignChallenge());
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ NewRunnableMethod(this, &RenderMessageFilter::Send, reply_msg));
+}
+
+#if defined(USE_TCMALLOC)
+void RenderMessageFilter::OnRendererTcmalloc(base::ProcessId pid,
+ const std::string& output) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableFunction(AboutTcmallocRendererCallback, pid, output));
+}
+#endif
+
+void RenderMessageFilter::OnGetExtensionMessageBundle(
+ const std::string& extension_id, IPC::Message* reply_msg) {
+ ChromeURLRequestContext* context = static_cast<ChromeURLRequestContext*>(
+ request_context_->GetURLRequestContext());
+
+ FilePath extension_path =
+ context->extension_info_map()->GetPathForExtension(extension_id);
+ std::string default_locale =
+ context->extension_info_map()->GetDefaultLocaleForExtension(extension_id);
+
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::OnGetExtensionMessageBundleOnFileThread,
+ extension_path, extension_id, default_locale, reply_msg));
+}
+
+void RenderMessageFilter::OnGetExtensionMessageBundleOnFileThread(
+ const FilePath& extension_path,
+ const std::string& extension_id,
+ const std::string& default_locale,
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+
+ std::map<std::string, std::string> dictionary_map;
+ if (!default_locale.empty()) {
+ // Touch disk only if extension is localized.
+ std::string error;
+ scoped_ptr<ExtensionMessageBundle> bundle(
+ extension_file_util::LoadExtensionMessageBundle(
+ extension_path, default_locale, &error));
+
+ if (bundle.get())
+ dictionary_map = *bundle->dictionary();
+ }
+
+ // Add @@extension_id reserved message here, so it's available to
+ // non-localized extensions too.
+ dictionary_map.insert(
+ std::make_pair(ExtensionMessageBundle::kExtensionIdKey, extension_id));
+
+ ViewHostMsg_GetExtensionMessageBundle::WriteReplyParams(
+ reply_msg, dictionary_map);
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ NewRunnableMethod(this, &RenderMessageFilter::Send, reply_msg));
+}
+
+void RenderMessageFilter::OnAsyncOpenFile(const IPC::Message& msg,
+ const FilePath& path,
+ int flags,
+ int message_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (!ChildProcessSecurityPolicy::GetInstance()->HasPermissionsForFile(
+ render_process_id_, path, flags)) {
+ DLOG(ERROR) << "Bad flags in ViewMsgHost_AsyncOpenFile message: " << flags;
+ UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_AOF"));
+ BadMessageReceived();
+ return;
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE, NewRunnableMethod(
+ this, &RenderMessageFilter::AsyncOpenFileOnFileThread,
+ path, flags, message_id, msg.routing_id()));
+}
+
+void RenderMessageFilter::AsyncOpenFileOnFileThread(const FilePath& path,
+ int flags,
+ int message_id,
+ int routing_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
+ base::PlatformFile file = base::CreatePlatformFile(
+ path, flags, NULL, &error_code);
+ IPC::PlatformFileForTransit file_for_transit =
+ IPC::InvalidPlatformFileForTransit();
+ if (file != base::kInvalidPlatformFileValue) {
+#if defined(OS_WIN)
+ ::DuplicateHandle(::GetCurrentProcess(), file, peer_handle(),
+ &file_for_transit, 0, false, DUPLICATE_SAME_ACCESS);
+#else
+ file_for_transit = base::FileDescriptor(file, true);
+#endif
+ }
+
+ IPC::Message* reply = new ViewMsg_AsyncOpenFile_ACK(
+ routing_id, error_code, file_for_transit, message_id);
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE, NewRunnableMethod(
+ this, &RenderMessageFilter::Send, reply));
+}
+
+SetCookieCompletion::SetCookieCompletion(int render_process_id,
+ int render_view_id,
+ const GURL& url,
+ const std::string& cookie_line,
+ ChromeURLRequestContext* context)
+ : render_process_id_(render_process_id),
+ render_view_id_(render_view_id),
+ url_(url),
+ cookie_line_(cookie_line),
+ context_(context) {
+}
+
+SetCookieCompletion::~SetCookieCompletion() {}
+
+void SetCookieCompletion::RunWithParams(const Tuple1<int>& params) {
+ int result = params.a;
+ bool blocked_by_policy = true;
+ net::CookieOptions options;
+ if (result == net::OK ||
+ result == net::OK_FOR_SESSION_ONLY) {
+ blocked_by_policy = false;
+ if (result == net::OK_FOR_SESSION_ONLY)
+ options.set_force_session();
+ context_->cookie_store()->SetCookieWithOptions(url_, cookie_line_,
+ options);
+ }
+ CallRenderViewHostContentSettingsDelegate(
+ render_process_id_, render_view_id_,
+ &RenderViewHostDelegate::ContentSettings::OnCookieChanged,
+ url_, cookie_line_, options, blocked_by_policy);
+ delete this;
+}
+
+GetCookiesCompletion::GetCookiesCompletion(int render_process_id,
+ int render_view_id,
+ const GURL& url,
+ IPC::Message* reply_msg,
+ RenderMessageFilter* filter,
+ ChromeURLRequestContext* context,
+ bool raw_cookies)
+ : url_(url),
+ reply_msg_(reply_msg),
+ filter_(filter),
+ context_(context),
+ render_process_id_(render_process_id),
+ render_view_id_(render_view_id),
+ raw_cookies_(raw_cookies) {
+ set_cookie_store(context_->cookie_store());
+}
+
+GetCookiesCompletion::~GetCookiesCompletion() {}
+
+void GetCookiesCompletion::RunWithParams(const Tuple1<int>& params) {
+ if (!raw_cookies_) {
+ int result = params.a;
+ std::string cookies;
+ if (result == net::OK)
+ cookies = cookie_store()->GetCookies(url_);
+ ViewHostMsg_GetCookies::WriteReplyParams(reply_msg_, cookies);
+ filter_->Send(reply_msg_);
+ net::CookieMonster* cookie_monster =
+ context_->cookie_store()->GetCookieMonster();
+ net::CookieList cookie_list =
+ cookie_monster->GetAllCookiesForURLWithOptions(
+ url_, net::CookieOptions());
+ CallRenderViewHostContentSettingsDelegate(
+ render_process_id_, render_view_id_,
+ &RenderViewHostDelegate::ContentSettings::OnCookiesRead,
+ url_, cookie_list, result != net::OK);
+ delete this;
+ } else {
+ // Ignore the policy result. We only waited on the policy result so that
+ // any pending 'set-cookie' requests could be flushed. The intent of
+ // querying the raw cookies is to reveal the contents of the cookie DB, so
+ // it important that we don't read the cookie db ahead of pending writes.
+ net::CookieMonster* cookie_monster =
+ context_->cookie_store()->GetCookieMonster();
+ net::CookieList cookie_list = cookie_monster->GetAllCookiesForURL(url_);
+
+ std::vector<webkit_glue::WebCookie> cookies;
+ for (size_t i = 0; i < cookie_list.size(); ++i) {
+ cookies.push_back(webkit_glue::WebCookie(cookie_list[i]));
+ }
+
+ ViewHostMsg_GetRawCookies::WriteReplyParams(reply_msg_, cookies);
+ filter_->Send(reply_msg_);
+ delete this;
+ }
+}
+
+void GetCookiesCompletion::set_cookie_store(CookieStore* cookie_store) {
+ cookie_store_ = cookie_store;
+}
+
+CookiesEnabledCompletion::CookiesEnabledCompletion(
+ IPC::Message* reply_msg,
+ RenderMessageFilter* filter)
+ : reply_msg_(reply_msg),
+ filter_(filter) {
+}
+
+CookiesEnabledCompletion::~CookiesEnabledCompletion() {}
+
+void CookiesEnabledCompletion::RunWithParams(const Tuple1<int>& params) {
+ bool result = params.a != net::ERR_ACCESS_DENIED;
+ ViewHostMsg_CookiesEnabled::WriteReplyParams(reply_msg_, result);
+ filter_->Send(reply_msg_);
+ delete this;
+}
diff --git a/content/browser/renderer_host/render_message_filter.h b/content/browser/renderer_host/render_message_filter.h
new file mode 100644
index 0000000..b821466
--- /dev/null
+++ b/content/browser/renderer_host/render_message_filter.h
@@ -0,0 +1,498 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_RENDER_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_RENDER_MESSAGE_FILTER_H_
+#pragma once
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#include <string>
+#include <vector>
+
+#include "app/surface/transport_dib.h"
+#include "base/file_path.h"
+#include "base/linked_ptr.h"
+#include "base/string16.h"
+#include "base/task.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_message_filter.h"
+#include "chrome/browser/in_process_webkit/webkit_context.h"
+#include "chrome/browser/net/resolve_proxy_msg_helper.h"
+#include "content/browser/renderer_host/resource_dispatcher_host.h"
+#include "chrome/common/content_settings.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebCache.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebPopupType.h"
+#include "ui/base/clipboard/clipboard.h"
+#include "ui/gfx/native_widget_types.h"
+
+class ChromeURLRequestContext;
+struct FontDescriptor;
+class HostContentSettingsMap;
+class HostZoomMap;
+class NotificationsPrefsCache;
+class Profile;
+class RenderWidgetHelper;
+class URLRequestContextGetter;
+struct ViewHostMsg_CreateWindow_Params;
+struct ViewHostMsg_CreateWorker_Params;
+
+namespace webkit {
+namespace npapi {
+struct WebPluginInfo;
+}
+}
+
+namespace base {
+class SharedMemory;
+}
+
+namespace net {
+class CookieStore;
+}
+
+namespace printing {
+class PrinterQuery;
+class PrintJobManager;
+}
+
+struct ViewHostMsg_ScriptedPrint_Params;
+
+// This class filters out incoming IPC messages for the renderer process on the
+// IPC thread.
+class RenderMessageFilter : public BrowserMessageFilter,
+ public ResolveProxyMsgHelper::Delegate {
+ public:
+ // Create the filter.
+ RenderMessageFilter(int render_process_id,
+ PluginService* plugin_service,
+ Profile* profile,
+ RenderWidgetHelper* render_widget_helper);
+
+ // BrowserMessageFilter methods:
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok);
+ virtual void OnDestruct() const;
+
+ int render_process_id() const { return render_process_id_; }
+ ResourceDispatcherHost* resource_dispatcher_host() {
+ return resource_dispatcher_host_;
+ }
+ bool off_the_record() { return off_the_record_; }
+
+ // Returns either the extension net::URLRequestContext or regular
+ // net::URLRequestContext depending on whether |url| is an extension URL.
+ // Only call on the IO thread.
+ ChromeURLRequestContext* GetRequestContextForURL(const GURL& url);
+
+ private:
+ friend class BrowserThread;
+ friend class DeleteTask<RenderMessageFilter>;
+
+ virtual ~RenderMessageFilter();
+
+ void OnMsgCreateWindow(const ViewHostMsg_CreateWindow_Params& params,
+ int* route_id,
+ int64* cloned_session_storage_namespace_id);
+ void OnMsgCreateWidget(int opener_id,
+ WebKit::WebPopupType popup_type,
+ int* route_id);
+ void OnMsgCreateFullscreenWidget(int opener_id, int* route_id);
+ void OnSetCookie(const IPC::Message& message,
+ const GURL& url,
+ const GURL& first_party_for_cookies,
+ const std::string& cookie);
+ void OnGetCookies(const GURL& url,
+ const GURL& first_party_for_cookies,
+ IPC::Message* reply_msg);
+ void OnGetRawCookies(const GURL& url,
+ const GURL& first_party_for_cookies,
+ IPC::Message* reply_msg);
+ void OnDeleteCookie(const GURL& url,
+ const std::string& cookieName);
+ void OnCookiesEnabled(const GURL& url,
+ const GURL& first_party_for_cookies,
+ IPC::Message* reply_msg);
+ void OnPluginFileDialog(const IPC::Message& msg,
+ bool multiple_files,
+ const std::wstring& title,
+ const std::wstring& filter,
+ uint32 user_data);
+
+#if defined(OS_MACOSX)
+ void OnLoadFont(const FontDescriptor& font,
+ uint32* handle_size,
+ base::SharedMemoryHandle* handle);
+#endif
+
+#if defined(OS_WIN) // This hack is Windows-specific.
+ // Cache fonts for the renderer. See RenderMessageFilter::OnPreCacheFont
+ // implementation for more details.
+ void OnPreCacheFont(LOGFONT font);
+#endif
+
+#if !defined(OS_MACOSX)
+ // Not handled in the IO thread on Mac.
+ void OnGetScreenInfo(gfx::NativeViewId window, IPC::Message* reply);
+#endif
+ void OnGetPlugins(bool refresh, IPC::Message* reply_msg);
+ void OnGetPluginsOnFileThread(bool refresh, IPC::Message* reply_msg);
+ void OnGetPluginInfo(int routing_id,
+ const GURL& url,
+ const GURL& policy_url,
+ const std::string& mime_type,
+ IPC::Message* reply_msg);
+ void OnGetPluginInfoOnFileThread(int render_view_id,
+ const GURL& url,
+ const GURL& policy_url,
+ const std::string& mime_type,
+ IPC::Message* reply_msg);
+ void OnGotPluginInfo(bool found,
+ const webkit::npapi::WebPluginInfo& info,
+ const std::string& actual_mime_type,
+ const GURL& policy_url,
+ IPC::Message* reply_msg);
+ void OnOpenChannelToPlugin(int routing_id,
+ const GURL& url,
+ const std::string& mime_type,
+ IPC::Message* reply_msg);
+ void OnOpenChannelToPepperPlugin(const FilePath& path,
+ IPC::Message* reply_msg);
+ void OnLaunchNaCl(const std::wstring& url,
+ int channel_descriptor,
+ IPC::Message* reply_msg);
+ void OnGenerateRoutingID(int* route_id);
+ void OnDownloadUrl(const IPC::Message& message,
+ const GURL& url,
+ const GURL& referrer);
+ void OnPlatformCheckSpelling(const string16& word, int tag, bool* correct);
+ void OnPlatformFillSuggestionList(const string16& word,
+ std::vector<string16>* suggestions);
+ void OnGetDocumentTag(IPC::Message* reply_msg);
+ void OnDocumentWithTagClosed(int tag);
+ void OnShowSpellingPanel(bool show);
+ void OnUpdateSpellingPanelWithMisspelledWord(const string16& word);
+ void OnDnsPrefetch(const std::vector<std::string>& hostnames);
+ void OnRendererHistograms(int sequence_number,
+ const std::vector<std::string>& histogram_info);
+#if defined(USE_TCMALLOC)
+ void OnRendererTcmalloc(base::ProcessId pid, const std::string& output);
+#endif
+ void OnReceiveContextMenuMsg(const IPC::Message& msg);
+ // Clipboard messages
+ void OnClipboardWriteObjectsAsync(const ui::Clipboard::ObjectMap& objects);
+ void OnClipboardWriteObjectsSync(const ui::Clipboard::ObjectMap& objects,
+ base::SharedMemoryHandle bitmap_handle);
+
+ void OnClipboardIsFormatAvailable(ui::Clipboard::FormatType format,
+ ui::Clipboard::Buffer buffer,
+ IPC::Message* reply);
+ void OnClipboardReadText(ui::Clipboard::Buffer buffer, IPC::Message* reply);
+ void OnClipboardReadAsciiText(ui::Clipboard::Buffer buffer,
+ IPC::Message* reply);
+ void OnClipboardReadHTML(ui::Clipboard::Buffer buffer, IPC::Message* reply);
+#if defined(OS_MACOSX)
+ void OnClipboardFindPboardWriteString(const string16& text);
+#endif
+ void OnClipboardReadAvailableTypes(ui::Clipboard::Buffer buffer,
+ IPC::Message* reply);
+ void OnClipboardReadData(ui::Clipboard::Buffer buffer, const string16& type,
+ IPC::Message* reply);
+ void OnClipboardReadFilenames(ui::Clipboard::Buffer buffer,
+ IPC::Message* reply);
+
+ void OnCheckNotificationPermission(const GURL& source_url,
+ int* permission_level);
+
+#if !defined(OS_MACOSX)
+ // Not handled in the IO thread on Mac.
+ void OnGetWindowRect(gfx::NativeViewId window, IPC::Message* reply);
+ void OnGetRootWindowRect(gfx::NativeViewId window, IPC::Message* reply);
+#endif
+
+ void OnRevealFolderInOS(const FilePath& path);
+ void OnGetCPBrowsingContext(uint32* context);
+
+#if defined(OS_WIN)
+ // Used to pass resulting EMF from renderer to browser in printing.
+ void OnDuplicateSection(base::SharedMemoryHandle renderer_handle,
+ base::SharedMemoryHandle* browser_handle);
+#endif
+
+#if defined(OS_CHROMEOS)
+ // Used to ask the browser allocate a temporary file for the renderer
+ // to fill in resulting PDF in renderer.
+ void OnAllocateTempFileForPrinting(IPC::Message* reply_msg);
+ void OnTempFileForPrintingWritten(int sequence_number);
+#endif
+
+#if defined(OS_POSIX)
+ // Used to ask the browser to allocate a block of shared memory for the
+ // renderer to send back data in, since shared memory can't be created
+ // in the renderer on POSIX due to the sandbox.
+ void OnAllocateSharedMemoryBuffer(uint32 buffer_size,
+ base::SharedMemoryHandle* handle);
+#endif
+
+ void OnResourceTypeStats(const WebKit::WebCache::ResourceTypeStats& stats);
+ static void OnResourceTypeStatsOnUIThread(
+ const WebKit::WebCache::ResourceTypeStats&,
+ base::ProcessId renderer_id);
+
+ void OnV8HeapStats(int v8_memory_allocated, int v8_memory_used);
+ static void OnV8HeapStatsOnUIThread(int v8_memory_allocated,
+ int v8_memory_used,
+ base::ProcessId renderer_id);
+
+ void OnDidZoomURL(const IPC::Message& message,
+ double zoom_level,
+ bool remember,
+ const GURL& url);
+ void UpdateHostZoomLevelsOnUIThread(double zoom_level,
+ bool remember,
+ const GURL& url,
+ int render_process_id,
+ int render_view_id);
+
+ void OnResolveProxy(const GURL& url, IPC::Message* reply_msg);
+
+ // ResolveProxyMsgHelper::Delegate implementation:
+ virtual void OnResolveProxyCompleted(IPC::Message* reply_msg,
+ int result,
+ const std::string& proxy_list);
+
+ // A javascript code requested to print the current page. This is done in two
+ // steps and this is the first step. Get the print setting right here
+ // synchronously. It will hang the I/O completely.
+ void OnGetDefaultPrintSettings(IPC::Message* reply_msg);
+ void OnGetDefaultPrintSettingsReply(
+ scoped_refptr<printing::PrinterQuery> printer_query,
+ IPC::Message* reply_msg);
+
+ // A javascript code requested to print the current page. The renderer host
+ // have to show to the user the print dialog and returns the selected print
+ // settings.
+ void OnScriptedPrint(const ViewHostMsg_ScriptedPrint_Params& params,
+ IPC::Message* reply_msg);
+ void OnScriptedPrintReply(
+ scoped_refptr<printing::PrinterQuery> printer_query,
+ int routing_id,
+ IPC::Message* reply_msg);
+
+ // Browser side transport DIB allocation
+ void OnAllocTransportDIB(size_t size,
+ bool cache_in_browser,
+ TransportDIB::Handle* result);
+ void OnFreeTransportDIB(TransportDIB::Id dib_id);
+
+ void OnOpenChannelToExtension(int routing_id,
+ const std::string& source_extension_id,
+ const std::string& target_extension_id,
+ const std::string& channel_name, int* port_id);
+ void OpenChannelToExtensionOnUIThread(int source_process_id,
+ int source_routing_id,
+ int receiver_port_id,
+ const std::string& source_extension_id,
+ const std::string& target_extension_id,
+ const std::string& channel_name);
+ void OnOpenChannelToTab(int routing_id, int tab_id,
+ const std::string& extension_id,
+ const std::string& channel_name, int* port_id);
+ void OpenChannelToTabOnUIThread(int source_process_id, int source_routing_id,
+ int receiver_port_id,
+ int tab_id, const std::string& extension_id,
+ const std::string& channel_name);
+
+ void OnCloseCurrentConnections();
+ void OnSetCacheMode(bool enabled);
+ void OnClearCache(bool preserve_ssl_host_info, IPC::Message* reply_msg);
+ void OnCacheableMetadataAvailable(const GURL& url,
+ double expected_response_time,
+ const std::vector<char>& data);
+ void OnEnableSpdy(bool enable);
+ void OnKeygen(uint32 key_size_index, const std::string& challenge_string,
+ const GURL& url, IPC::Message* reply_msg);
+ void OnKeygenOnWorkerThread(
+ int key_size_in_bits,
+ const std::string& challenge_string,
+ const GURL& url,
+ IPC::Message* reply_msg);
+ void OnGetExtensionMessageBundle(const std::string& extension_id,
+ IPC::Message* reply_msg);
+ void OnGetExtensionMessageBundleOnFileThread(
+ const FilePath& extension_path,
+ const std::string& extension_id,
+ const std::string& default_locale,
+ IPC::Message* reply_msg);
+ void OnAsyncOpenFile(const IPC::Message& msg,
+ const FilePath& path,
+ int flags,
+ int message_id);
+ void AsyncOpenFileOnFileThread(const FilePath& path,
+ int flags,
+ int message_id,
+ int routing_id);
+
+#if defined(USE_X11)
+ void DoOnGetScreenInfo(gfx::NativeViewId view, IPC::Message* reply_msg);
+ void DoOnGetWindowRect(gfx::NativeViewId view, IPC::Message* reply_msg);
+ void DoOnGetRootWindowRect(gfx::NativeViewId view, IPC::Message* reply_msg);
+ void DoOnClipboardIsFormatAvailable(ui::Clipboard::FormatType format,
+ ui::Clipboard::Buffer buffer,
+ IPC::Message* reply_msg);
+ void DoOnClipboardReadText(ui::Clipboard::Buffer buffer,
+ IPC::Message* reply_msg);
+ void DoOnClipboardReadAsciiText(ui::Clipboard::Buffer buffer,
+ IPC::Message* reply_msg);
+ void DoOnClipboardReadHTML(ui::Clipboard::Buffer buffer,
+ IPC::Message* reply_msg);
+ void DoOnClipboardReadAvailableTypes(ui::Clipboard::Buffer buffer,
+ IPC::Message* reply_msg);
+ void DoOnClipboardReadData(ui::Clipboard::Buffer buffer, const string16& type,
+ IPC::Message* reply_msg);
+ void DoOnClipboardReadFilenames(ui::Clipboard::Buffer buffer,
+ IPC::Message* reply_msg);
+ void DoOnAllocateTempFileForPrinting(IPC::Message* reply_msg);
+ void DoOnTempFileForPrintingWritten(int sequence_number);
+#endif
+
+ bool CheckBenchmarkingEnabled() const;
+ bool CheckPreparsedJsCachingEnabled() const;
+
+ // We have our own clipboard because we want to access the clipboard on the
+ // IO thread instead of forwarding (possibly synchronous) messages to the UI
+ // thread. This instance of the clipboard should be accessed only on the IO
+ // thread.
+ static ui::Clipboard* GetClipboard();
+
+ // Cached resource request dispatcher host and plugin service, guaranteed to
+ // be non-null if Init succeeds. We do not own the objects, they are managed
+ // by the BrowserProcess, which has a wider scope than we do.
+ ResourceDispatcherHost* resource_dispatcher_host_;
+ PluginService* plugin_service_;
+ printing::PrintJobManager* print_job_manager_;
+
+ // The Profile associated with our renderer process. This should only be
+ // accessed on the UI thread!
+ Profile* profile_;
+
+ // The host content settings map. Stored separately from the profile so we can
+ // access it on other threads.
+ HostContentSettingsMap* content_settings_;
+
+ // Helper class for handling PluginProcessHost_ResolveProxy messages (manages
+ // the requests to the proxy service).
+ ResolveProxyMsgHelper resolve_proxy_msg_helper_;
+
+ // Contextual information to be used for requests created here.
+ scoped_refptr<URLRequestContextGetter> request_context_;
+
+ // A request context that holds a cookie store for chrome-extension URLs.
+ scoped_refptr<URLRequestContextGetter> extensions_request_context_;
+
+ scoped_refptr<RenderWidgetHelper> render_widget_helper_;
+
+ // A cache of notifications preferences which is used to handle
+ // Desktop Notifications permission messages.
+ scoped_refptr<NotificationsPrefsCache> notification_prefs_;
+
+ // Handles zoom-related messages.
+ scoped_refptr<HostZoomMap> host_zoom_map_;
+
+ // Whether this process is used for off the record tabs.
+ bool off_the_record_;
+
+ bool cloud_print_enabled_;
+
+ base::TimeTicks last_plugin_refresh_time_; // Initialized to 0.
+
+ scoped_refptr<WebKitContext> webkit_context_;
+
+ int render_process_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderMessageFilter);
+};
+
+// These classes implement completion callbacks for getting and setting
+// cookies.
+class SetCookieCompletion : public net::CompletionCallback {
+ public:
+ SetCookieCompletion(int render_process_id,
+ int render_view_id,
+ const GURL& url,
+ const std::string& cookie_line,
+ ChromeURLRequestContext* context);
+ virtual ~SetCookieCompletion();
+
+ virtual void RunWithParams(const Tuple1<int>& params);
+
+ int render_process_id() const {
+ return render_process_id_;
+ }
+
+ int render_view_id() const {
+ return render_view_id_;
+ }
+
+ private:
+ int render_process_id_;
+ int render_view_id_;
+ GURL url_;
+ std::string cookie_line_;
+ scoped_refptr<ChromeURLRequestContext> context_;
+};
+
+class GetCookiesCompletion : public net::CompletionCallback {
+ public:
+ GetCookiesCompletion(int render_process_id,
+ int render_view_id,
+ const GURL& url, IPC::Message* reply_msg,
+ RenderMessageFilter* filter,
+ ChromeURLRequestContext* context,
+ bool raw_cookies);
+ virtual ~GetCookiesCompletion();
+
+ virtual void RunWithParams(const Tuple1<int>& params);
+
+ int render_process_id() const {
+ return render_process_id_;
+ }
+
+ int render_view_id() const {
+ return render_view_id_;
+ }
+
+ void set_cookie_store(net::CookieStore* cookie_store);
+
+ net::CookieStore* cookie_store() {
+ return cookie_store_.get();
+ }
+
+ private:
+ GURL url_;
+ IPC::Message* reply_msg_;
+ scoped_refptr<RenderMessageFilter> filter_;
+ scoped_refptr<ChromeURLRequestContext> context_;
+ int render_process_id_;
+ int render_view_id_;
+ bool raw_cookies_;
+ scoped_refptr<net::CookieStore> cookie_store_;
+};
+
+class CookiesEnabledCompletion : public net::CompletionCallback {
+ public:
+ CookiesEnabledCompletion(IPC::Message* reply_msg,
+ RenderMessageFilter* filter);
+ virtual ~CookiesEnabledCompletion();
+
+ virtual void RunWithParams(const Tuple1<int>& params);
+
+ private:
+ IPC::Message* reply_msg_;
+ scoped_refptr<RenderMessageFilter> filter_;
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_RENDER_MESSAGE_FILTER_H_
diff --git a/content/browser/renderer_host/render_message_filter_gtk.cc b/content/browser/renderer_host/render_message_filter_gtk.cc
new file mode 100644
index 0000000..ce260c4
--- /dev/null
+++ b/content/browser/renderer_host/render_message_filter_gtk.cc
@@ -0,0 +1,346 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/render_message_filter.h"
+
+#include <fcntl.h>
+#include <map>
+
+#include "base/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/path_service.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/render_messages.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebScreenInfo.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/x11/WebScreenInfoFactory.h"
+#include "ui/base/clipboard/clipboard.h"
+#include "ui/base/x/x11_util.h"
+#include "ui/gfx/gtk_native_view_id_manager.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/printing/print_dialog_cloud.h"
+#endif
+
+using WebKit::WebScreenInfo;
+using WebKit::WebScreenInfoFactory;
+
+namespace {
+
+typedef std::map<int, FilePath> SequenceToPathMap;
+
+struct PrintingSequencePathMap {
+ SequenceToPathMap map;
+ int sequence;
+};
+
+// No locking, only access on the FILE thread.
+static base::LazyInstance<PrintingSequencePathMap>
+ g_printing_file_descriptor_map(base::LINKER_INITIALIZED);
+
+} // namespace
+
+// We get null window_ids passed into the two functions below; please see
+// http://crbug.com/9060 for more details.
+
+void RenderMessageFilter::DoOnGetScreenInfo(gfx::NativeViewId view,
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::BACKGROUND_X11));
+ Display* display = ui::GetSecondaryDisplay();
+ int screen = ui::GetDefaultScreen(display);
+ WebScreenInfo results = WebScreenInfoFactory::screenInfo(display, screen);
+ ViewHostMsg_GetScreenInfo::WriteReplyParams(reply_msg, results);
+ Send(reply_msg);
+}
+
+void RenderMessageFilter::DoOnGetWindowRect(gfx::NativeViewId view,
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::BACKGROUND_X11));
+ // This is called to get the x, y offset (in screen coordinates) of the given
+ // view and its width and height.
+ gfx::Rect rect;
+ XID window;
+
+ base::AutoLock lock(GtkNativeViewManager::GetInstance()->unrealize_lock());
+ if (GtkNativeViewManager::GetInstance()->GetXIDForId(&window, view)) {
+ if (window) {
+ int x, y;
+ unsigned width, height;
+ if (ui::GetWindowGeometry(&x, &y, &width, &height, window))
+ rect = gfx::Rect(x, y, width, height);
+ }
+ }
+
+ ViewHostMsg_GetWindowRect::WriteReplyParams(reply_msg, rect);
+ Send(reply_msg);
+}
+
+// Return the top-level parent of the given window. Called on the
+// BACKGROUND_X11 thread.
+static XID GetTopLevelWindow(XID window) {
+ bool parent_is_root;
+ XID parent_window;
+
+ if (!ui::GetWindowParent(&parent_window, &parent_is_root, window))
+ return 0;
+ if (parent_is_root)
+ return window;
+
+ return GetTopLevelWindow(parent_window);
+}
+
+void RenderMessageFilter::DoOnGetRootWindowRect(gfx::NativeViewId view,
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::BACKGROUND_X11));
+ // This is called to get the screen coordinates and size of the browser
+ // window itself.
+ gfx::Rect rect;
+ XID window;
+
+ base::AutoLock lock(GtkNativeViewManager::GetInstance()->unrealize_lock());
+ if (GtkNativeViewManager::GetInstance()->GetXIDForId(&window, view)) {
+ if (window) {
+ const XID toplevel = GetTopLevelWindow(window);
+ if (toplevel) {
+ int x, y;
+ unsigned width, height;
+ if (ui::GetWindowGeometry(&x, &y, &width, &height, toplevel))
+ rect = gfx::Rect(x, y, width, height);
+ }
+ }
+ }
+
+ ViewHostMsg_GetRootWindowRect::WriteReplyParams(reply_msg, rect);
+ Send(reply_msg);
+}
+
+void RenderMessageFilter::DoOnClipboardIsFormatAvailable(
+ ui::Clipboard::FormatType format, ui::Clipboard::Buffer buffer,
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ const bool result = GetClipboard()->IsFormatAvailable(format, buffer);
+
+ ViewHostMsg_ClipboardIsFormatAvailable::WriteReplyParams(reply_msg, result);
+ Send(reply_msg);
+}
+
+void RenderMessageFilter::DoOnClipboardReadText(ui::Clipboard::Buffer buffer,
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ string16 result;
+ GetClipboard()->ReadText(buffer, &result);
+
+ ViewHostMsg_ClipboardReadText::WriteReplyParams(reply_msg, result);
+ Send(reply_msg);
+}
+
+void RenderMessageFilter::DoOnClipboardReadAsciiText(
+ ui::Clipboard::Buffer buffer, IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ std::string result;
+ GetClipboard()->ReadAsciiText(buffer, &result);
+
+ ViewHostMsg_ClipboardReadAsciiText::WriteReplyParams(reply_msg, result);
+ Send(reply_msg);
+}
+
+void RenderMessageFilter::DoOnClipboardReadHTML(ui::Clipboard::Buffer buffer,
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ std::string src_url_str;
+ string16 markup;
+ GetClipboard()->ReadHTML(buffer, &markup, &src_url_str);
+ const GURL src_url = GURL(src_url_str);
+
+ ViewHostMsg_ClipboardReadHTML::WriteReplyParams(reply_msg, markup, src_url);
+ Send(reply_msg);
+}
+
+void RenderMessageFilter::DoOnClipboardReadAvailableTypes(
+ ui::Clipboard::Buffer buffer, IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ Send(reply_msg);
+}
+
+void RenderMessageFilter::DoOnClipboardReadData(ui::Clipboard::Buffer buffer,
+ const string16& type,
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ Send(reply_msg);
+}
+void RenderMessageFilter::DoOnClipboardReadFilenames(
+ ui::Clipboard::Buffer buffer, IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ Send(reply_msg);
+}
+
+#if defined(OS_CHROMEOS)
+void RenderMessageFilter::DoOnAllocateTempFileForPrinting(
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ base::FileDescriptor temp_file_fd(-1, false);
+ SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map;
+ const int sequence_number = g_printing_file_descriptor_map.Get().sequence++;
+
+ FilePath path;
+ if (file_util::CreateTemporaryFile(&path)) {
+ int fd = open(path.value().c_str(), O_WRONLY);
+ if (fd >= 0) {
+ SequenceToPathMap::iterator it = map->find(sequence_number);
+ if (it != map->end()) {
+ NOTREACHED() << "Sequence number already in use. seq=" <<
+ sequence_number;
+ } else {
+ (*map)[sequence_number] = path;
+ temp_file_fd.fd = fd;
+ temp_file_fd.auto_close = true;
+ }
+ }
+ }
+
+ ViewHostMsg_AllocateTempFileForPrinting::WriteReplyParams(
+ reply_msg, temp_file_fd, sequence_number);
+ Send(reply_msg);
+}
+
+void RenderMessageFilter::DoOnTempFileForPrintingWritten(int sequence_number) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ SequenceToPathMap* map = &g_printing_file_descriptor_map.Get().map;
+ SequenceToPathMap::iterator it = map->find(sequence_number);
+ if (it == map->end()) {
+ NOTREACHED() << "Got a sequence that we didn't pass to the "
+ "renderer: " << sequence_number;
+ return;
+ }
+
+ if (cloud_print_enabled_)
+ PrintDialogCloud::CreatePrintDialogForPdf(it->second, string16(), true);
+ else
+ NOTIMPLEMENTED();
+
+ // Erase the entry in the map.
+ map->erase(it);
+}
+
+#endif // defined(OS_CHROMEOS)
+
+void RenderMessageFilter::OnGetScreenInfo(gfx::NativeViewId view,
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(
+ BrowserThread::BACKGROUND_X11, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::DoOnGetScreenInfo, view, reply_msg));
+}
+
+void RenderMessageFilter::OnGetWindowRect(gfx::NativeViewId view,
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(
+ BrowserThread::BACKGROUND_X11, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::DoOnGetWindowRect, view, reply_msg));
+}
+
+void RenderMessageFilter::OnGetRootWindowRect(gfx::NativeViewId view,
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(
+ BrowserThread::BACKGROUND_X11, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::DoOnGetRootWindowRect, view, reply_msg));
+}
+
+void RenderMessageFilter::OnClipboardIsFormatAvailable(
+ ui::Clipboard::FormatType format, ui::Clipboard::Buffer buffer,
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::DoOnClipboardIsFormatAvailable, format,
+ buffer, reply_msg));
+}
+
+void RenderMessageFilter::OnClipboardReadText(ui::Clipboard::Buffer buffer,
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::DoOnClipboardReadText, buffer,
+ reply_msg));
+}
+
+void RenderMessageFilter::OnClipboardReadAsciiText(ui::Clipboard::Buffer buffer,
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::DoOnClipboardReadAsciiText, buffer,
+ reply_msg));
+}
+
+void RenderMessageFilter::OnClipboardReadHTML(ui::Clipboard::Buffer buffer,
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::DoOnClipboardReadHTML, buffer,
+ reply_msg));
+}
+
+void RenderMessageFilter::OnClipboardReadAvailableTypes(
+ ui::Clipboard::Buffer buffer, IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::DoOnClipboardReadAvailableTypes, buffer,
+ reply_msg));
+}
+
+void RenderMessageFilter::OnClipboardReadData(ui::Clipboard::Buffer buffer,
+ const string16& type,
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::DoOnClipboardReadData, buffer, type,
+ reply_msg));
+}
+
+void RenderMessageFilter::OnClipboardReadFilenames(
+ ui::Clipboard::Buffer buffer, IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::DoOnClipboardReadFilenames, buffer,
+ reply_msg));
+}
+
+#if defined(OS_CHROMEOS)
+void RenderMessageFilter::OnAllocateTempFileForPrinting(
+ IPC::Message* reply_msg) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::DoOnAllocateTempFileForPrinting,
+ reply_msg));
+}
+
+void RenderMessageFilter::OnTempFileForPrintingWritten(int sequence_number) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderMessageFilter::DoOnTempFileForPrintingWritten,
+ sequence_number));
+}
+#endif // defined(OS_CHROMEOS)
diff --git a/content/browser/renderer_host/render_message_filter_mac.mm b/content/browser/renderer_host/render_message_filter_mac.mm
new file mode 100644
index 0000000..a7d7c9f
--- /dev/null
+++ b/content/browser/renderer_host/render_message_filter_mac.mm
@@ -0,0 +1,43 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/render_message_filter.h"
+
+#import <Cocoa/Cocoa.h>
+
+#include "base/message_loop.h"
+#include "base/sys_string_conversions.h"
+#include "chrome/browser/browser_thread.h"
+#import "chrome/browser/ui/cocoa/find_pasteboard.h"
+
+// The number of utf16 code units that will be written to the find pasteboard,
+// longer texts are silently ignored. This is to prevent that a compromised
+// renderer can write unlimited amounts of data into the find pasteboard.
+static const size_t kMaxFindPboardStringLength = 4096;
+
+class WriteFindPboardTask : public Task {
+ public:
+ explicit WriteFindPboardTask(NSString* text)
+ : text_([text retain]) {}
+
+ void Run() {
+ [[FindPasteboard sharedInstance] setFindText:text_];
+ }
+
+ private:
+ scoped_nsobject<NSString> text_;
+};
+
+// Called on the IO thread.
+void RenderMessageFilter::OnClipboardFindPboardWriteString(
+ const string16& text) {
+ if (text.length() <= kMaxFindPboardStringLength) {
+ NSString* nsText = base::SysUTF16ToNSString(text);
+ if (nsText) {
+ // FindPasteboard must be used on the UI thread.
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE, new WriteFindPboardTask(nsText));
+ }
+ }
+}
diff --git a/content/browser/renderer_host/render_message_filter_win.cc b/content/browser/renderer_host/render_message_filter_win.cc
new file mode 100644
index 0000000..4f305fde
--- /dev/null
+++ b/content/browser/renderer_host/render_message_filter_win.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/render_message_filter.h"
+
+#include "chrome/common/render_messages.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebScreenInfo.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/win/WebScreenInfoFactory.h"
+
+using WebKit::WebScreenInfo;
+using WebKit::WebScreenInfoFactory;
+
+// We get null window_ids passed into the two functions below; please see
+// http://crbug.com/9060 for more details.
+
+// TODO(shess): Provide a mapping from reply_msg->routing_id() to HWND
+// so that we can eliminate the NativeViewId parameter.
+
+void RenderMessageFilter::OnGetWindowRect(gfx::NativeViewId window_id,
+ IPC::Message* reply_msg) {
+ HWND window = gfx::NativeViewFromId(window_id);
+ RECT window_rect = {0};
+ GetWindowRect(window, &window_rect);
+ gfx::Rect rect(window_rect);
+
+ ViewHostMsg_GetWindowRect::WriteReplyParams(reply_msg, rect);
+ Send(reply_msg);
+}
+
+void RenderMessageFilter::OnGetRootWindowRect(gfx::NativeViewId window_id,
+ IPC::Message* reply_msg) {
+ HWND window = gfx::NativeViewFromId(window_id);
+ RECT window_rect = {0};
+ HWND root_window = ::GetAncestor(window, GA_ROOT);
+ GetWindowRect(root_window, &window_rect);
+ gfx::Rect rect(window_rect);
+
+ ViewHostMsg_GetRootWindowRect::WriteReplyParams(reply_msg, rect);
+ Send(reply_msg);
+}
+
+void RenderMessageFilter::OnGetScreenInfo(gfx::NativeViewId view,
+ IPC::Message* reply_msg) {
+ WebScreenInfo results =
+ WebScreenInfoFactory::screenInfo(gfx::NativeViewFromId(view));
+ ViewHostMsg_GetScreenInfo::WriteReplyParams(reply_msg, results);
+ Send(reply_msg);
+}
diff --git a/content/browser/renderer_host/render_process_host.cc b/content/browser/renderer_host/render_process_host.cc
new file mode 100644
index 0000000..aed6d08
--- /dev/null
+++ b/content/browser/renderer_host/render_process_host.cc
@@ -0,0 +1,204 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/render_process_host.h"
+
+#include "base/rand_util.h"
+#include "base/sys_info.h"
+#include "chrome/browser/child_process_security_policy.h"
+#include "chrome/common/child_process_info.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/notification_service.h"
+
+namespace {
+
+size_t max_renderer_count_override = 0;
+
+size_t GetMaxRendererProcessCount() {
+ if (max_renderer_count_override)
+ return max_renderer_count_override;
+
+ // Defines the maximum number of renderer processes according to the
+ // amount of installed memory as reported by the OS. The table
+ // values are calculated by assuming that you want the renderers to
+ // use half of the installed ram and assuming that each tab uses
+ // ~40MB, however the curve is not linear but piecewise linear with
+ // interleaved slopes of 3 and 2.
+ // If you modify this table you need to adjust browser\browser_uitest.cc
+ // to match the expected number of processes.
+
+ static const size_t kMaxRenderersByRamTier[] = {
+ 3, // less than 256MB
+ 6, // 256MB
+ 9, // 512MB
+ 12, // 768MB
+ 14, // 1024MB
+ 18, // 1280MB
+ 20, // 1536MB
+ 22, // 1792MB
+ 24, // 2048MB
+ 26, // 2304MB
+ 29, // 2560MB
+ 32, // 2816MB
+ 35, // 3072MB
+ 38, // 3328MB
+ 40 // 3584MB
+ };
+
+ static size_t max_count = 0;
+ if (!max_count) {
+ size_t memory_tier = base::SysInfo::AmountOfPhysicalMemoryMB() / 256;
+ if (memory_tier >= arraysize(kMaxRenderersByRamTier))
+ max_count = chrome::kMaxRendererProcessCount;
+ else
+ max_count = kMaxRenderersByRamTier[memory_tier];
+ }
+ return max_count;
+}
+
+// Returns true if the given host is suitable for launching a new view
+// associated with the given profile.
+static bool IsSuitableHost(RenderProcessHost* host, Profile* profile,
+ RenderProcessHost::Type type) {
+ if (host->profile() != profile)
+ return false;
+
+ RenderProcessHost::Type host_type = RenderProcessHost::TYPE_NORMAL;
+ if (ChildProcessSecurityPolicy::GetInstance()->HasWebUIBindings(host->id()))
+ host_type = RenderProcessHost::TYPE_WEBUI;
+ if (ChildProcessSecurityPolicy::GetInstance()->
+ HasExtensionBindings(host->id()))
+ host_type = RenderProcessHost::TYPE_EXTENSION;
+
+ return host_type == type;
+}
+
+// the global list of all renderer processes
+IDMap<RenderProcessHost> all_hosts;
+
+} // namespace
+
+extern bool g_log_bug53991;
+
+// static
+bool RenderProcessHost::run_renderer_in_process_ = false;
+
+// static
+void RenderProcessHost::SetMaxRendererProcessCount(size_t count) {
+ max_renderer_count_override = count;
+}
+
+RenderProcessHost::RenderProcessHost(Profile* profile)
+ : max_page_id_(-1),
+ fast_shutdown_started_(false),
+ deleting_soon_(false),
+ id_(ChildProcessInfo::GenerateChildProcessUniqueId()),
+ profile_(profile),
+ sudden_termination_allowed_(true),
+ ignore_input_events_(false) {
+ all_hosts.AddWithID(this, id());
+ all_hosts.set_check_on_null_data(true);
+ // Initialize |child_process_activity_time_| to a reasonable value.
+ mark_child_process_activity_time();
+}
+
+RenderProcessHost::~RenderProcessHost() {
+ // In unit tests, Release() might not have been called.
+ if (all_hosts.Lookup(id()))
+ all_hosts.Remove(id());
+}
+
+void RenderProcessHost::Attach(IPC::Channel::Listener* listener,
+ int routing_id) {
+ VLOG_IF(1, g_log_bug53991) << "AddListener: (" << this << "): " << routing_id;
+ listeners_.AddWithID(listener, routing_id);
+}
+
+void RenderProcessHost::Release(int listener_id) {
+ VLOG_IF(1, g_log_bug53991) << "RemListener: (" << this << "): "
+ << listener_id;
+ DCHECK(listeners_.Lookup(listener_id) != NULL);
+ listeners_.Remove(listener_id);
+
+ // Make sure that all associated resource requests are stopped.
+ CancelResourceRequests(listener_id);
+
+ // When no other owners of this object, we can delete ourselves
+ if (listeners_.IsEmpty()) {
+ NotificationService::current()->Notify(
+ NotificationType::RENDERER_PROCESS_TERMINATED,
+ Source<RenderProcessHost>(this), NotificationService::NoDetails());
+ MessageLoop::current()->DeleteSoon(FROM_HERE, this);
+ deleting_soon_ = true;
+
+ // Remove ourself from the list of renderer processes so that we can't be
+ // reused in between now and when the Delete task runs.
+ all_hosts.Remove(id());
+ }
+}
+
+void RenderProcessHost::ReportExpectingClose(int32 listener_id) {
+ listeners_expecting_close_.insert(listener_id);
+}
+
+void RenderProcessHost::UpdateMaxPageID(int32 page_id) {
+ if (page_id > max_page_id_)
+ max_page_id_ = page_id;
+}
+
+bool RenderProcessHost::FastShutdownForPageCount(size_t count) {
+ if (listeners_.size() == count)
+ return FastShutdownIfPossible();
+ return false;
+}
+
+// static
+RenderProcessHost::iterator RenderProcessHost::AllHostsIterator() {
+ return iterator(&all_hosts);
+}
+
+// static
+RenderProcessHost* RenderProcessHost::FromID(int render_process_id) {
+ return all_hosts.Lookup(render_process_id);
+}
+
+// static
+bool RenderProcessHost::ShouldTryToUseExistingProcessHost() {
+ size_t renderer_process_count = all_hosts.size();
+
+ // NOTE: Sometimes it's necessary to create more render processes than
+ // GetMaxRendererProcessCount(), for instance when we want to create
+ // a renderer process for a profile that has no existing renderers.
+ // This is OK in moderation, since the GetMaxRendererProcessCount()
+ // is conservative.
+
+ return run_renderer_in_process() ||
+ (renderer_process_count >= GetMaxRendererProcessCount());
+}
+
+// static
+RenderProcessHost* RenderProcessHost::GetExistingProcessHost(Profile* profile,
+ Type type) {
+ // First figure out which existing renderers we can use.
+ std::vector<RenderProcessHost*> suitable_renderers;
+ suitable_renderers.reserve(all_hosts.size());
+
+ iterator iter(AllHostsIterator());
+ while (!iter.IsAtEnd()) {
+ if (run_renderer_in_process() ||
+ IsSuitableHost(iter.GetCurrentValue(), profile, type))
+ suitable_renderers.push_back(iter.GetCurrentValue());
+
+ iter.Advance();
+ }
+
+ // Now pick a random suitable renderer, if we have any.
+ if (!suitable_renderers.empty()) {
+ int suitable_count = static_cast<int>(suitable_renderers.size());
+ int random_index = base::RandInt(0, suitable_count - 1);
+ return suitable_renderers[random_index];
+ }
+
+ return NULL;
+}
diff --git a/content/browser/renderer_host/render_process_host.h b/content/browser/renderer_host/render_process_host.h
new file mode 100644
index 0000000..cc469fa
--- /dev/null
+++ b/content/browser/renderer_host/render_process_host.h
@@ -0,0 +1,327 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_RENDER_PROCESS_HOST_H_
+#define CONTENT_BROWSER_RENDERER_HOST_RENDER_PROCESS_HOST_H_
+#pragma once
+
+#include <set>
+
+#include "app/surface/transport_dib.h"
+#include "base/id_map.h"
+#include "base/process.h"
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+#include "chrome/common/visitedlink_common.h"
+#include "ipc/ipc_sync_channel.h"
+
+class Profile;
+class URLRequestContextGetter;
+struct ViewMsg_ClosePage_Params;
+
+namespace base {
+class SharedMemory;
+}
+
+// Virtual interface that represents the browser side of the browser <->
+// renderer communication channel. There will generally be one
+// RenderProcessHost per renderer process.
+//
+// The concrete implementation of this class for normal use is the
+// BrowserRenderProcessHost. It may also be implemented by a testing interface
+// for mocking purposes.
+class RenderProcessHost : public IPC::Channel::Sender,
+ public IPC::Channel::Listener {
+ public:
+ typedef IDMap<RenderProcessHost>::iterator iterator;
+
+ // We classify renderers according to their highest privilege, and try
+ // to group pages into renderers with similar privileges.
+ // Note: it may be possible for a renderer to have multiple privileges,
+ // in which case we call it an "extension" renderer.
+ enum Type {
+ TYPE_NORMAL, // Normal renderer, no extra privileges.
+ TYPE_WEBUI, // Renderer with WebUI privileges, like New Tab.
+ TYPE_EXTENSION, // Renderer with extension privileges.
+ };
+
+ // Details for RENDERER_PROCESS_CLOSED notifications.
+ struct RendererClosedDetails {
+ RendererClosedDetails(base::TerminationStatus status,
+ int exit_code,
+ bool was_extension_renderer) {
+ this->status = status;
+ this->exit_code = exit_code;
+ this->was_extension_renderer = was_extension_renderer;
+ }
+ base::TerminationStatus status;
+ int exit_code;
+ bool was_extension_renderer;
+ };
+
+ explicit RenderProcessHost(Profile* profile);
+ virtual ~RenderProcessHost();
+
+ // Returns the user profile associated with this renderer process.
+ Profile* profile() const { return profile_; }
+
+ // Returns the unique ID for this child process. This can be used later in
+ // a call to FromID() to get back to this object (this is used to avoid
+ // sending non-threadsafe pointers to other threads).
+ //
+ // This ID will be unique for all child processes, including workers, plugins,
+ // etc. It is generated by ChildProcessInfo.
+ int id() const { return id_; }
+
+ // Returns true iff channel_ has been set to non-NULL. Use this for checking
+ // if there is connection or not.
+ bool HasConnection() { return channel_.get() != NULL; }
+
+ bool sudden_termination_allowed() const {
+ return sudden_termination_allowed_;
+ }
+ void set_sudden_termination_allowed(bool enabled) {
+ sudden_termination_allowed_ = enabled;
+ }
+
+ // Used for refcounting, each holder of this object must Attach and Release
+ // just like it would for a COM object. This object should be allocated on
+ // the heap; when no listeners own it any more, it will delete itself.
+ void Attach(IPC::Channel::Listener* listener, int routing_id);
+
+ // See Attach()
+ void Release(int listener_id);
+
+ // Listeners should call this when they've sent a "Close" message and
+ // they're waiting for a "Close_ACK", so that if the renderer process
+ // goes away we'll know that it was intentional rather than a crash.
+ void ReportExpectingClose(int32 listener_id);
+
+ // Allows iteration over this RenderProcessHost's RenderViewHost listeners.
+ // Use from UI thread only.
+ typedef IDMap<IPC::Channel::Listener>::const_iterator listeners_iterator;
+
+ listeners_iterator ListenersIterator() {
+ return listeners_iterator(&listeners_);
+ }
+
+ IPC::Channel::Listener* GetListenerByID(int routing_id) {
+ return listeners_.Lookup(routing_id);
+ }
+
+ // Called to inform the render process host of a new "max page id" for a
+ // render view host. The render process host computes the largest page id
+ // across all render view hosts and uses the value when it needs to
+ // initialize a new renderer in place of the current one.
+ void UpdateMaxPageID(int32 page_id);
+
+ void set_ignore_input_events(bool ignore_input_events) {
+ ignore_input_events_ = ignore_input_events;
+ }
+ bool ignore_input_events() {
+ return ignore_input_events_;
+ }
+
+ // Returns how long the child has been idle. The definition of idle
+ // depends on when a derived class calls mark_child_process_activity_time().
+ // This is a rough indicator and its resolution should not be better than
+ // 10 milliseconds.
+ base::TimeDelta get_child_process_idle_time() const {
+ return base::TimeTicks::Now() - child_process_activity_time_;
+ }
+
+ // Call this function when it is evident that the child process is actively
+ // performing some operation, for example if we just received an IPC message.
+ void mark_child_process_activity_time() {
+ child_process_activity_time_ = base::TimeTicks::Now();
+ }
+
+ // Try to shutdown the associated render process as fast as possible, but
+ // only if |count| matches the number of render widgets that this process
+ // controls.
+ bool FastShutdownForPageCount(size_t count);
+
+ bool fast_shutdown_started() {
+ return fast_shutdown_started_;
+ }
+
+ // Virtual interface ---------------------------------------------------------
+
+ // Initialize the new renderer process, returning true on success. This must
+ // be called once before the object can be used, but can be called after
+ // that with no effect. Therefore, if the caller isn't sure about whether
+ // the process has been created, it should just call Init().
+ virtual bool Init(
+ bool is_accessibility_enabled, bool is_extensions_process) = 0;
+
+ // Gets the next available routing id.
+ virtual int GetNextRoutingID() = 0;
+
+ // Called on the UI thread to cancel any outstanding resource requests for
+ // the specified render widget.
+ virtual void CancelResourceRequests(int render_widget_id) = 0;
+
+ // Called on the UI thread to simulate a ClosePage_ACK message to the
+ // ResourceDispatcherHost. Necessary for a cross-site request, in the case
+ // that the original RenderViewHost is not live and thus cannot run an
+ // onunload handler.
+ virtual void CrossSiteClosePageACK(
+ const ViewMsg_ClosePage_Params& params) = 0;
+
+ // Called on the UI thread to wait for the next UpdateRect message for the
+ // specified render widget. Returns true if successful, and the msg out-
+ // param will contain a copy of the received UpdateRect message.
+ virtual bool WaitForUpdateMsg(int render_widget_id,
+ const base::TimeDelta& max_delay,
+ IPC::Message* msg) = 0;
+
+ // Called when a received message cannot be decoded.
+ virtual void ReceivedBadMessage() = 0;
+
+ // Track the count of visible widgets. Called by listeners to register and
+ // unregister visibility.
+ virtual void WidgetRestored() = 0;
+ virtual void WidgetHidden() = 0;
+
+ // Called when RenderView is created by a listener.
+ virtual void ViewCreated() = 0;
+
+ // Informs the renderer about a new visited link table.
+ virtual void SendVisitedLinkTable(base::SharedMemory* table_memory) = 0;
+
+ // Notify the renderer that a link was visited.
+ virtual void AddVisitedLinks(
+ const VisitedLinkCommon::Fingerprints& links) = 0;
+
+ // Clear internal visited links buffer and ask the renderer to update link
+ // coloring state for all of its links.
+ virtual void ResetVisitedLinks() = 0;
+
+ // Try to shutdown the associated renderer process as fast as possible.
+ // If this renderer has any RenderViews with unload handlers, then this
+ // function does nothing. The current implementation uses TerminateProcess.
+ // Returns True if it was able to do fast shutdown.
+ virtual bool FastShutdownIfPossible() = 0;
+
+ // Synchronously sends the message, waiting for the specified timeout. The
+ // implementor takes ownership of the given Message regardless of whether or
+ // not this method succeeds. Returns true on success.
+ virtual bool SendWithTimeout(IPC::Message* msg, int timeout_ms) = 0;
+
+ // Returns the process object associated with the child process. In certain
+ // tests or single-process mode, this will actually represent the current
+ // process.
+ //
+ // NOTE: this is not necessarily valid immediately after calling Init, as
+ // Init starts the process asynchronously. It's guaranteed to be valid after
+ // the first IPC arrives.
+ virtual base::ProcessHandle GetHandle() = 0;
+
+ // Transport DIB functions ---------------------------------------------------
+
+ // Return the TransportDIB for the given id. On Linux, this can involve
+ // mapping shared memory. On Mac, the shared memory is created in the browser
+ // process and the cached metadata is returned. On Windows, this involves
+ // duplicating the handle from the remote process. The RenderProcessHost
+ // still owns the returned DIB.
+ virtual TransportDIB* GetTransportDIB(TransportDIB::Id dib_id) = 0;
+
+ // Static management functions -----------------------------------------------
+
+ // Flag to run the renderer in process. This is primarily
+ // for debugging purposes. When running "in process", the
+ // browser maintains a single RenderProcessHost which communicates
+ // to a RenderProcess which is instantiated in the same process
+ // with the Browser. All IPC between the Browser and the
+ // Renderer is the same, it's just not crossing a process boundary.
+ static bool run_renderer_in_process() {
+ return run_renderer_in_process_;
+ }
+ static void set_run_renderer_in_process(bool value) {
+ run_renderer_in_process_ = value;
+ }
+
+ // Allows iteration over all the RenderProcessHosts in the browser. Note
+ // that each host may not be active, and therefore may have NULL channels.
+ static iterator AllHostsIterator();
+
+ // Returns the RenderProcessHost given its ID. Returns NULL if the ID does
+ // not correspond to a live RenderProcessHost.
+ static RenderProcessHost* FromID(int render_process_id);
+
+ // Returns true if the caller should attempt to use an existing
+ // RenderProcessHost rather than creating a new one.
+ static bool ShouldTryToUseExistingProcessHost();
+
+ // Get an existing RenderProcessHost associated with the given profile, if
+ // possible. The renderer process is chosen randomly from suitable renderers
+ // that share the same profile and type.
+ // Returns NULL if no suitable renderer process is available, in which case
+ // the caller is free to create a new renderer.
+ static RenderProcessHost* GetExistingProcessHost(Profile* profile, Type type);
+
+ // Overrides the default heuristic for limiting the max renderer process
+ // count. This is useful for unit testing process limit behaviors.
+ // A value of zero means to use the default heuristic.
+ static void SetMaxRendererProcessCount(size_t count);
+
+ protected:
+ // A proxy for our IPC::Channel that lives on the IO thread (see
+ // browser_process.h)
+ scoped_ptr<IPC::SyncChannel> channel_;
+
+ // The registered listeners. When this list is empty or all NULL, we should
+ // delete ourselves
+ IDMap<IPC::Channel::Listener> listeners_;
+
+ // The maximum page ID we've ever seen from the renderer process.
+ int32 max_page_id_;
+
+ // True if fast shutdown has been performed on this RPH.
+ bool fast_shutdown_started_;
+
+ // True if we've posted a DeleteTask and will be deleted soon.
+ bool deleting_soon_;
+
+ private:
+ // The globally-unique identifier for this RPH.
+ int id_;
+
+ Profile* profile_;
+
+ // set of listeners that expect the renderer process to close
+ std::set<int> listeners_expecting_close_;
+
+ // True if the process can be shut down suddenly. If this is true, then we're
+ // sure that all the RenderViews in the process can be shutdown suddenly. If
+ // it's false, then specific RenderViews might still be allowed to be shutdown
+ // suddenly by checking their SuddenTerminationAllowed() flag. This can occur
+ // if one tab has an unload event listener but another tab in the same process
+ // doesn't.
+ bool sudden_termination_allowed_;
+
+ // Set to true if we shouldn't send input events. We actually do the
+ // filtering for this at the render widget level.
+ bool ignore_input_events_;
+
+ // See getter above.
+ static bool run_renderer_in_process_;
+
+ // Records the last time we regarded the child process active.
+ base::TimeTicks child_process_activity_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderProcessHost);
+};
+
+// Factory object for RenderProcessHosts. Using this factory allows tests to
+// swap out a different one to use a TestRenderProcessHost.
+class RenderProcessHostFactory {
+ public:
+ virtual ~RenderProcessHostFactory() {}
+ virtual RenderProcessHost* CreateRenderProcessHost(
+ Profile* profile) const = 0;
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_RENDER_PROCESS_HOST_H_
diff --git a/content/browser/renderer_host/render_sandbox_host_linux.cc b/content/browser/renderer_host/render_sandbox_host_linux.cc
new file mode 100644
index 0000000..8d05790
--- /dev/null
+++ b/content/browser/renderer_host/render_sandbox_host_linux.cc
@@ -0,0 +1,687 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/render_sandbox_host_linux.h"
+
+#include <fcntl.h>
+#include <fontconfig/fontconfig.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <time.h>
+
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/eintr_wrapper.h"
+#include "base/linux_util.h"
+#include "base/pickle.h"
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+#include "base/shared_memory.h"
+#include "base/singleton.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "chrome/common/font_config_ipc_linux.h"
+#include "chrome/common/sandbox_methods_linux.h"
+#include "chrome/common/unix_domain_socket_posix.h"
+#include "skia/ext/SkFontHost_fontconfig_direct.h"
+#include "third_party/npapi/bindings/npapi_extensions.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/gtk/WebFontInfo.h"
+
+using WebKit::WebCString;
+using WebKit::WebFontInfo;
+using WebKit::WebUChar;
+
+// http://code.google.com/p/chromium/wiki/LinuxSandboxIPC
+
+// BEWARE: code in this file run across *processes* (not just threads).
+
+// This code runs in a child process
+class SandboxIPCProcess {
+ public:
+ // lifeline_fd: this is the read end of a pipe which the browser process
+ // holds the other end of. If the browser process dies, its descriptors are
+ // closed and we will noticed an EOF on the pipe. That's our signal to exit.
+ // browser_socket: the browser's end of the sandbox IPC socketpair. From the
+ // point of view of the renderer, it's talking to the browser but this
+ // object actually services the requests.
+ // sandbox_cmd: the path of the sandbox executable
+ SandboxIPCProcess(int lifeline_fd, int browser_socket,
+ std::string sandbox_cmd)
+ : lifeline_fd_(lifeline_fd),
+ browser_socket_(browser_socket),
+ font_config_(new FontConfigDirect()) {
+ base::InjectiveMultimap multimap;
+ multimap.push_back(base::InjectionArc(0, lifeline_fd, false));
+ multimap.push_back(base::InjectionArc(0, browser_socket, false));
+
+ base::CloseSuperfluousFds(multimap);
+
+ if (!sandbox_cmd.empty()) {
+ sandbox_cmd_.push_back(sandbox_cmd);
+ sandbox_cmd_.push_back(base::kFindInodeSwitch);
+ }
+ }
+
+ void Run() {
+ struct pollfd pfds[2];
+ pfds[0].fd = lifeline_fd_;
+ pfds[0].events = POLLIN;
+ pfds[1].fd = browser_socket_;
+ pfds[1].events = POLLIN;
+
+ int failed_polls = 0;
+ for (;;) {
+ const int r = HANDLE_EINTR(poll(pfds, 2, -1));
+ if (r < 1) {
+ LOG(WARNING) << "poll errno:" << errno;
+ if (failed_polls++ == 3) {
+ LOG(FATAL) << "poll failing. Sandbox host aborting.";
+ return;
+ }
+ continue;
+ }
+
+ failed_polls = 0;
+
+ if (pfds[0].revents) {
+ // our parent died so we should too.
+ _exit(0);
+ }
+
+ if (pfds[1].revents) {
+ HandleRequestFromRenderer(browser_socket_);
+ }
+ }
+ }
+
+ private:
+ // ---------------------------------------------------------------------------
+ // Requests from the renderer...
+
+ void HandleRequestFromRenderer(int fd) {
+ std::vector<int> fds;
+
+ // A FontConfigIPC::METHOD_MATCH message could be kMaxFontFamilyLength
+ // bytes long (this is the largest message type).
+ // 128 bytes padding are necessary so recvmsg() does not return MSG_TRUNC
+ // error for a maximum length message.
+ char buf[FontConfigInterface::kMaxFontFamilyLength + 128];
+
+ const ssize_t len = UnixDomainSocket::RecvMsg(fd, buf, sizeof(buf), &fds);
+ if (len == -1) {
+ // TODO: should send an error reply, or the sender might block forever.
+ NOTREACHED()
+ << "Sandbox host message is larger than kMaxFontFamilyLength";
+ return;
+ }
+ if (fds.size() == 0)
+ return;
+
+ Pickle pickle(buf, len);
+ void* iter = NULL;
+
+ int kind;
+ if (!pickle.ReadInt(&iter, &kind))
+ goto error;
+
+ if (kind == FontConfigIPC::METHOD_MATCH) {
+ HandleFontMatchRequest(fd, pickle, iter, fds);
+ } else if (kind == FontConfigIPC::METHOD_OPEN) {
+ HandleFontOpenRequest(fd, pickle, iter, fds);
+ } else if (kind == LinuxSandbox::METHOD_GET_FONT_FAMILY_FOR_CHARS) {
+ HandleGetFontFamilyForChars(fd, pickle, iter, fds);
+ } else if (kind == LinuxSandbox::METHOD_LOCALTIME) {
+ HandleLocaltime(fd, pickle, iter, fds);
+ } else if (kind == LinuxSandbox::METHOD_GET_CHILD_WITH_INODE) {
+ HandleGetChildWithInode(fd, pickle, iter, fds);
+ } else if (kind == LinuxSandbox::METHOD_GET_STYLE_FOR_STRIKE) {
+ HandleGetStyleForStrike(fd, pickle, iter, fds);
+ } else if (kind == LinuxSandbox::METHOD_MAKE_SHARED_MEMORY_SEGMENT) {
+ HandleMakeSharedMemorySegment(fd, pickle, iter, fds);
+ } else if (kind == LinuxSandbox::METHOD_MATCH_WITH_FALLBACK) {
+ HandleMatchWithFallback(fd, pickle, iter, fds);
+ }
+
+ error:
+ for (std::vector<int>::const_iterator
+ i = fds.begin(); i != fds.end(); ++i) {
+ close(*i);
+ }
+ }
+
+ void HandleFontMatchRequest(int fd, const Pickle& pickle, void* iter,
+ std::vector<int>& fds) {
+ bool filefaceid_valid;
+ uint32_t filefaceid;
+
+ if (!pickle.ReadBool(&iter, &filefaceid_valid))
+ return;
+ if (filefaceid_valid) {
+ if (!pickle.ReadUInt32(&iter, &filefaceid))
+ return;
+ }
+ bool is_bold, is_italic;
+ if (!pickle.ReadBool(&iter, &is_bold) ||
+ !pickle.ReadBool(&iter, &is_italic)) {
+ return;
+ }
+
+ uint32_t characters_bytes;
+ if (!pickle.ReadUInt32(&iter, &characters_bytes))
+ return;
+ const char* characters = NULL;
+ if (characters_bytes > 0) {
+ const uint32_t kMaxCharactersBytes = 1 << 10;
+ if (characters_bytes % 2 != 0 || // We expect UTF-16.
+ characters_bytes > kMaxCharactersBytes ||
+ !pickle.ReadBytes(&iter, &characters, characters_bytes))
+ return;
+ }
+
+ std::string family;
+ if (!pickle.ReadString(&iter, &family))
+ return;
+
+ std::string result_family;
+ unsigned result_filefaceid;
+ const bool r = font_config_->Match(
+ &result_family, &result_filefaceid, filefaceid_valid, filefaceid,
+ family, characters, characters_bytes, &is_bold, &is_italic);
+
+ Pickle reply;
+ if (!r) {
+ reply.WriteBool(false);
+ } else {
+ reply.WriteBool(true);
+ reply.WriteUInt32(result_filefaceid);
+ reply.WriteString(result_family);
+ reply.WriteBool(is_bold);
+ reply.WriteBool(is_italic);
+ }
+ SendRendererReply(fds, reply, -1);
+ }
+
+ void HandleFontOpenRequest(int fd, const Pickle& pickle, void* iter,
+ std::vector<int>& fds) {
+ uint32_t filefaceid;
+ if (!pickle.ReadUInt32(&iter, &filefaceid))
+ return;
+ const int result_fd = font_config_->Open(filefaceid);
+
+ Pickle reply;
+ if (result_fd == -1) {
+ reply.WriteBool(false);
+ } else {
+ reply.WriteBool(true);
+ }
+
+ SendRendererReply(fds, reply, result_fd);
+
+ if (result_fd >= 0)
+ close(result_fd);
+ }
+
+ void HandleGetFontFamilyForChars(int fd, const Pickle& pickle, void* iter,
+ std::vector<int>& fds) {
+ // The other side of this call is
+ // chrome/renderer/renderer_sandbox_support_linux.cc
+
+ int num_chars;
+ if (!pickle.ReadInt(&iter, &num_chars))
+ return;
+
+ // We don't want a corrupt renderer asking too much of us, it might
+ // overflow later in the code.
+ static const int kMaxChars = 4096;
+ if (num_chars < 1 || num_chars > kMaxChars) {
+ LOG(WARNING) << "HandleGetFontFamilyForChars: too many chars: "
+ << num_chars;
+ return;
+ }
+
+ scoped_array<WebUChar> chars(new WebUChar[num_chars]);
+
+ for (int i = 0; i < num_chars; ++i) {
+ uint32_t c;
+ if (!pickle.ReadUInt32(&iter, &c)) {
+ return;
+ }
+
+ chars[i] = c;
+ }
+
+ WebCString family = WebFontInfo::familyForChars(chars.get(), num_chars);
+
+ Pickle reply;
+ if (family.data()) {
+ reply.WriteString(family.data());
+ } else {
+ reply.WriteString("");
+ }
+ SendRendererReply(fds, reply, -1);
+ }
+
+ void HandleGetStyleForStrike(int fd, const Pickle& pickle, void* iter,
+ std::vector<int>& fds) {
+ std::string family;
+ int sizeAndStyle;
+
+ if (!pickle.ReadString(&iter, &family) ||
+ !pickle.ReadInt(&iter, &sizeAndStyle)) {
+ return;
+ }
+
+ WebKit::WebFontRenderStyle style;
+ WebFontInfo::renderStyleForStrike(family.c_str(), sizeAndStyle, &style);
+
+ Pickle reply;
+ reply.WriteInt(style.useBitmaps);
+ reply.WriteInt(style.useAutoHint);
+ reply.WriteInt(style.useHinting);
+ reply.WriteInt(style.hintStyle);
+ reply.WriteInt(style.useAntiAlias);
+ reply.WriteInt(style.useSubpixel);
+
+ SendRendererReply(fds, reply, -1);
+ }
+
+ void HandleLocaltime(int fd, const Pickle& pickle, void* iter,
+ std::vector<int>& fds) {
+ // The other side of this call is in zygote_main_linux.cc
+
+ std::string time_string;
+ if (!pickle.ReadString(&iter, &time_string) ||
+ time_string.size() != sizeof(time_t)) {
+ return;
+ }
+
+ time_t time;
+ memcpy(&time, time_string.data(), sizeof(time));
+ // We use localtime here because we need the tm_zone field to be filled
+ // out. Since we are a single-threaded process, this is safe.
+ const struct tm* expanded_time = localtime(&time);
+
+ std::string result_string;
+ const char* time_zone_string = "";
+ if (expanded_time != NULL) {
+ result_string = std::string(reinterpret_cast<const char*>(expanded_time),
+ sizeof(struct tm));
+ time_zone_string = expanded_time->tm_zone;
+ }
+
+ Pickle reply;
+ reply.WriteString(result_string);
+ reply.WriteString(time_zone_string);
+ SendRendererReply(fds, reply, -1);
+ }
+
+ void HandleGetChildWithInode(int fd, const Pickle& pickle, void* iter,
+ std::vector<int>& fds) {
+ // The other side of this call is in zygote_main_linux.cc
+ if (sandbox_cmd_.empty()) {
+ LOG(ERROR) << "Not in the sandbox, this should not be called";
+ return;
+ }
+
+ uint64_t inode;
+ if (!pickle.ReadUInt64(&iter, &inode))
+ return;
+
+ base::ProcessId pid = 0;
+ std::string inode_output;
+
+ std::vector<std::string> sandbox_cmd = sandbox_cmd_;
+ sandbox_cmd.push_back(base::Int64ToString(inode));
+ CommandLine get_inode_cmd(sandbox_cmd);
+ if (base::GetAppOutput(get_inode_cmd, &inode_output))
+ base::StringToInt(inode_output, &pid);
+
+ if (!pid) {
+ // Even though the pid is invalid, we still need to reply to the zygote
+ // and not just return here.
+ LOG(ERROR) << "Could not get pid";
+ }
+
+ Pickle reply;
+ reply.WriteInt(pid);
+ SendRendererReply(fds, reply, -1);
+ }
+
+ void HandleMakeSharedMemorySegment(int fd, const Pickle& pickle, void* iter,
+ std::vector<int>& fds) {
+ uint32_t shm_size;
+ if (!pickle.ReadUInt32(&iter, &shm_size))
+ return;
+ int shm_fd = -1;
+ base::SharedMemory shm;
+ if (shm.CreateAnonymous(shm_size))
+ shm_fd = shm.handle().fd;
+ Pickle reply;
+ SendRendererReply(fds, reply, shm_fd);
+ }
+
+ void HandleMatchWithFallback(int fd, const Pickle& pickle, void* iter,
+ std::vector<int>& fds) {
+ // Unlike the other calls, for which we are an indirection in front of
+ // WebKit or Skia, this call is always made via this sandbox helper
+ // process. Therefore the fontconfig code goes in here directly.
+
+ std::string face;
+ bool is_bold, is_italic;
+ uint32 charset;
+
+ if (!pickle.ReadString(&iter, &face) ||
+ face.empty() ||
+ !pickle.ReadBool(&iter, &is_bold) ||
+ !pickle.ReadBool(&iter, &is_italic) ||
+ !pickle.ReadUInt32(&iter, &charset)) {
+ return;
+ }
+
+ FcLangSet* langset = FcLangSetCreate();
+ MSCharSetToFontconfig(langset, charset);
+
+ FcPattern* pattern = FcPatternCreate();
+ // TODO(agl): FC_FAMILy needs to change
+ FcPatternAddString(pattern, FC_FAMILY, (FcChar8*) face.c_str());
+ if (is_bold)
+ FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD);
+ if (is_italic)
+ FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC);
+ FcPatternAddLangSet(pattern, FC_LANG, langset);
+ FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
+ FcConfigSubstitute(NULL, pattern, FcMatchPattern);
+ FcDefaultSubstitute(pattern);
+
+ FcResult result;
+ FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
+ int font_fd = -1;
+ int good_enough_index = -1;
+ bool good_enough_index_set = false;
+
+ if (font_set) {
+ for (int i = 0; i < font_set->nfont; ++i) {
+ FcPattern* current = font_set->fonts[i];
+
+ // Older versions of fontconfig have a bug where they cannot select
+ // only scalable fonts so we have to manually filter the results.
+ FcBool is_scalable;
+ if (FcPatternGetBool(current, FC_SCALABLE, 0,
+ &is_scalable) != FcResultMatch ||
+ !is_scalable) {
+ continue;
+ }
+
+ FcChar8* c_filename;
+ if (FcPatternGetString(current, FC_FILE, 0, &c_filename) !=
+ FcResultMatch) {
+ continue;
+ }
+
+ // We only want to return sfnt (TrueType) based fonts. We don't have a
+ // very good way of detecting this so we'll filter based on the
+ // filename.
+ bool is_sfnt = false;
+ static const char kSFNTExtensions[][5] = {
+ ".ttf", ".otc", ".TTF", ".ttc", ""
+ };
+ const size_t filename_len = strlen(reinterpret_cast<char*>(c_filename));
+ for (unsigned j = 0; ; j++) {
+ if (kSFNTExtensions[j][0] == 0) {
+ // None of the extensions matched.
+ break;
+ }
+ const size_t ext_len = strlen(kSFNTExtensions[j]);
+ if (filename_len > ext_len &&
+ memcmp(c_filename + filename_len - ext_len,
+ kSFNTExtensions[j], ext_len) == 0) {
+ is_sfnt = true;
+ break;
+ }
+ }
+
+ if (!is_sfnt)
+ continue;
+
+ // This font is good enough to pass muster, but we might be able to do
+ // better with subsequent ones.
+ if (!good_enough_index_set) {
+ good_enough_index = i;
+ good_enough_index_set = true;
+ }
+
+ FcValue matrix;
+ bool have_matrix = FcPatternGet(current, FC_MATRIX, 0, &matrix) == 0;
+
+ if (is_italic && have_matrix) {
+ // we asked for an italic font, but fontconfig is giving us a
+ // non-italic font with a transformation matrix.
+ continue;
+ }
+
+ FcValue embolden;
+ const bool have_embolden =
+ FcPatternGet(current, FC_EMBOLDEN, 0, &embolden) == 0;
+
+ if (is_bold && have_embolden) {
+ // we asked for a bold font, but fontconfig gave us a non-bold font
+ // and asked us to apply fake bolding.
+ continue;
+ }
+
+ font_fd = open(reinterpret_cast<char*>(c_filename), O_RDONLY);
+ if (font_fd >= 0)
+ break;
+ }
+ }
+
+ if (font_fd == -1 && good_enough_index_set) {
+ // We didn't find a font that we liked, so we fallback to something
+ // acceptable.
+ FcPattern* current = font_set->fonts[good_enough_index];
+ FcChar8* c_filename;
+ FcPatternGetString(current, FC_FILE, 0, &c_filename);
+ font_fd = open(reinterpret_cast<char*>(c_filename), O_RDONLY);
+ }
+
+ if (font_set)
+ FcFontSetDestroy(font_set);
+ FcPatternDestroy(pattern);
+
+ Pickle reply;
+ SendRendererReply(fds, reply, font_fd);
+
+ if (font_fd >= 0) {
+ if (HANDLE_EINTR(close(font_fd)) < 0)
+ PLOG(ERROR) << "close";
+ }
+ }
+
+ // MSCharSetToFontconfig translates a Microsoft charset identifier to a
+ // fontconfig language set by appending to |langset|.
+ static void MSCharSetToFontconfig(FcLangSet* langset, unsigned fdwCharSet) {
+ // We have need to translate raw fdwCharSet values into terms that
+ // fontconfig can understand. (See the description of fdwCharSet in the MSDN
+ // documentation for CreateFont:
+ // http://msdn.microsoft.com/en-us/library/dd183499(VS.85).aspx )
+ //
+ // Although the argument is /called/ 'charset', the actual values conflate
+ // character sets (which are sets of Unicode code points) and character
+ // encodings (which are algorithms for turning a series of bits into a
+ // series of code points.) Sometimes the values will name a language,
+ // sometimes they'll name an encoding. In the latter case I'm assuming that
+ // they mean the set of code points in the domain of that encoding.
+ //
+ // fontconfig deals with ISO 639-1 language codes:
+ // http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
+ //
+ // So, for each of the documented fdwCharSet values I've had to take a
+ // guess at the set of ISO 639-1 languages intended.
+
+ switch (fdwCharSet) {
+ case NPCharsetAnsi:
+ // These values I don't really know what to do with, so I'm going to map
+ // them to English also.
+ case NPCharsetDefault:
+ case NPCharsetMac:
+ case NPCharsetOEM:
+ case NPCharsetSymbol:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("en"));
+ break;
+ case NPCharsetBaltic:
+ // The three baltic languages.
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("et"));
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("lv"));
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("lt"));
+ break;
+ // TODO(jungshik): Would we be better off mapping Big5 to zh-tw
+ // and GB2312 to zh-cn? Fontconfig has 4 separate orthography
+ // files (zh-{cn,tw,hk,mo}.
+ case NPCharsetChineseBIG5:
+ case NPCharsetGB2312:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("zh"));
+ break;
+ case NPCharsetEastEurope:
+ // A scattering of eastern European languages.
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("pl"));
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("cs"));
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("sk"));
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("hu"));
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("hr"));
+ break;
+ case NPCharsetGreek:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("el"));
+ break;
+ case NPCharsetHangul:
+ case NPCharsetJohab:
+ // Korean
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ko"));
+ break;
+ case NPCharsetRussian:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ru"));
+ break;
+ case NPCharsetShiftJIS:
+ // Japanese
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ja"));
+ break;
+ case NPCharsetTurkish:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("tr"));
+ break;
+ case NPCharsetVietnamese:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("vi"));
+ break;
+ case NPCharsetArabic:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("ar"));
+ break;
+ case NPCharsetHebrew:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("he"));
+ break;
+ case NPCharsetThai:
+ FcLangSetAdd(langset, reinterpret_cast<const FcChar8*>("th"));
+ break;
+ // default:
+ // Don't add any languages in that case that we don't recognise the
+ // constant.
+ }
+ }
+
+ void SendRendererReply(const std::vector<int>& fds, const Pickle& reply,
+ int reply_fd) {
+ struct msghdr msg;
+ memset(&msg, 0, sizeof(msg));
+ struct iovec iov = {const_cast<void*>(reply.data()), reply.size()};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ char control_buffer[CMSG_SPACE(sizeof(int))];
+
+ if (reply_fd != -1) {
+ struct stat st;
+ if (fstat(reply_fd, &st) == 0 && S_ISDIR(st.st_mode)) {
+ LOG(FATAL) << "Tried to send a directory descriptor over sandbox IPC";
+ // We must never send directory descriptors to a sandboxed process
+ // because they can use openat with ".." elements in the path in order
+ // to escape the sandbox and reach the real filesystem.
+ }
+
+ struct cmsghdr *cmsg;
+ msg.msg_control = control_buffer;
+ msg.msg_controllen = sizeof(control_buffer);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cmsg), &reply_fd, sizeof(reply_fd));
+ msg.msg_controllen = cmsg->cmsg_len;
+ }
+
+ if (HANDLE_EINTR(sendmsg(fds[0], &msg, MSG_DONTWAIT)) < 0)
+ PLOG(ERROR) << "sendmsg";
+ }
+
+ // ---------------------------------------------------------------------------
+
+ const int lifeline_fd_;
+ const int browser_socket_;
+ FontConfigDirect* const font_config_;
+ std::vector<std::string> sandbox_cmd_;
+};
+
+// -----------------------------------------------------------------------------
+
+// Runs on the main thread at startup.
+RenderSandboxHostLinux::RenderSandboxHostLinux()
+ : initialized_(false),
+ renderer_socket_(0),
+ childs_lifeline_fd_(0),
+ pid_(0) {
+}
+
+// static
+RenderSandboxHostLinux* RenderSandboxHostLinux::GetInstance() {
+ return Singleton<RenderSandboxHostLinux>::get();
+}
+
+void RenderSandboxHostLinux::Init(const std::string& sandbox_path) {
+ DCHECK(!initialized_);
+ initialized_ = true;
+
+ int fds[2];
+ // We use SOCK_SEQPACKET rather than SOCK_DGRAM to prevent the renderer from
+ // sending datagrams to other sockets on the system. The sandbox may prevent
+ // the renderer from calling socket() to create new sockets, but it'll still
+ // inherit some sockets. With PF_UNIX+SOCK_DGRAM, it can call sendmsg to send
+ // a datagram to any (abstract) socket on the same system. With
+ // SOCK_SEQPACKET, this is prevented.
+ CHECK(socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) == 0);
+
+ renderer_socket_ = fds[0];
+ const int browser_socket = fds[1];
+
+ int pipefds[2];
+ CHECK(0 == pipe(pipefds));
+ const int child_lifeline_fd = pipefds[0];
+ childs_lifeline_fd_ = pipefds[1];
+
+ pid_ = fork();
+ if (pid_ == 0) {
+ SandboxIPCProcess handler(child_lifeline_fd, browser_socket, sandbox_path);
+ handler.Run();
+ _exit(0);
+ }
+}
+
+RenderSandboxHostLinux::~RenderSandboxHostLinux() {
+ if (initialized_) {
+ if (HANDLE_EINTR(close(renderer_socket_)) < 0)
+ PLOG(ERROR) << "close";
+ if (HANDLE_EINTR(close(childs_lifeline_fd_)) < 0)
+ PLOG(ERROR) << "close";
+ }
+}
diff --git a/content/browser/renderer_host/render_sandbox_host_linux.h b/content/browser/renderer_host/render_sandbox_host_linux.h
new file mode 100644
index 0000000..8a4d0aa11
--- /dev/null
+++ b/content/browser/renderer_host/render_sandbox_host_linux.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// http://code.google.com/p/chromium/wiki/LinuxSandboxIPC
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_RENDER_SANDBOX_HOST_LINUX_H_
+#define CONTENT_BROWSER_RENDERER_HOST_RENDER_SANDBOX_HOST_LINUX_H_
+#pragma once
+
+#include <string>
+
+#include "base/logging.h"
+
+template <typename T> struct DefaultSingletonTraits;
+
+// This is a singleton object which handles sandbox requests from the
+// renderers.
+class RenderSandboxHostLinux {
+ public:
+ // Returns the singleton instance.
+ static RenderSandboxHostLinux* GetInstance();
+
+ // Get the file descriptor which renderers should be given in order to signal
+ // crashes to the browser.
+ int GetRendererSocket() const {
+ DCHECK(initialized_);
+ return renderer_socket_;
+ }
+ pid_t pid() const {
+ DCHECK(initialized_);
+ return pid_;
+ }
+ void Init(const std::string& sandbox_path);
+
+ private:
+ friend struct DefaultSingletonTraits<RenderSandboxHostLinux>;
+ // This object must be constructed on the main thread.
+ RenderSandboxHostLinux();
+ ~RenderSandboxHostLinux();
+
+ // Whether Init() has been called yet.
+ bool initialized_;
+
+ int renderer_socket_;
+ int childs_lifeline_fd_;
+ pid_t pid_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderSandboxHostLinux);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_RENDER_SANDBOX_HOST_LINUX_H_
diff --git a/content/browser/renderer_host/render_view_host.cc b/content/browser/renderer_host/render_view_host.cc
new file mode 100644
index 0000000..58d3768
--- /dev/null
+++ b/content/browser/renderer_host/render_view_host.cc
@@ -0,0 +1,1712 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/render_view_host.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/i18n/rtl.h"
+#include "base/json/json_reader.h"
+#include "base/string_util.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.h"
+#include "base/values.h"
+#include "chrome/browser/child_process_security_policy.h"
+#include "chrome/browser/cross_site_request_manager.h"
+#include "chrome/browser/debugger/devtools_manager.h"
+#include "chrome/browser/dom_operation_notification_details.h"
+#include "chrome/browser/extensions/extension_message_service.h"
+#include "chrome/browser/in_process_webkit/session_storage_namespace.h"
+#include "chrome/browser/metrics/user_metrics.h"
+#include "chrome/browser/net/predictor_api.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/renderer_host/site_instance.h"
+#include "chrome/common/bindings_policy.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/native_web_keyboard_event.h"
+#include "chrome/common/net/url_request_context_getter.h"
+#include "chrome/common/notification_details.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/notification_type.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/render_messages_params.h"
+#include "chrome/common/result_codes.h"
+#include "chrome/common/thumbnail_score.h"
+#include "chrome/common/translate_errors.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/common/web_apps.h"
+#include "content/browser/renderer_host/render_process_host.h"
+#include "content/browser/renderer_host/render_view_host_delegate.h"
+#include "content/browser/renderer_host/render_widget_host.h"
+#include "content/browser/renderer_host/render_widget_host_view.h"
+#include "net/base/net_util.h"
+#include "printing/native_metafile.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebFindOptions.h"
+#include "ui/gfx/native_widget_types.h"
+#include "webkit/glue/context_menu.h"
+#include "webkit/glue/webaccessibility.h"
+#include "webkit/glue/webdropdata.h"
+
+using base::TimeDelta;
+using WebKit::WebConsoleMessage;
+using WebKit::WebDragOperation;
+using WebKit::WebDragOperationNone;
+using WebKit::WebDragOperationsMask;
+using WebKit::WebFindOptions;
+using WebKit::WebInputEvent;
+using WebKit::WebMediaPlayerAction;
+using WebKit::WebTextDirection;
+
+namespace {
+
+// Delay to wait on closing the tab for a beforeunload/unload handler to fire.
+const int kUnloadTimeoutMS = 1000;
+
+} // namespace
+
+///////////////////////////////////////////////////////////////////////////////
+// RenderViewHost, public:
+
+// static
+RenderViewHost* RenderViewHost::FromID(int render_process_id,
+ int render_view_id) {
+ RenderProcessHost* process = RenderProcessHost::FromID(render_process_id);
+ if (!process)
+ return NULL;
+ RenderWidgetHost* widget = static_cast<RenderWidgetHost*>(
+ process->GetListenerByID(render_view_id));
+ if (!widget || !widget->IsRenderView())
+ return NULL;
+ return static_cast<RenderViewHost*>(widget);
+}
+
+RenderViewHost::RenderViewHost(SiteInstance* instance,
+ RenderViewHostDelegate* delegate,
+ int routing_id,
+ SessionStorageNamespace* session_storage)
+ : RenderWidgetHost(instance->GetProcess(), routing_id),
+ instance_(instance),
+ delegate_(delegate),
+ waiting_for_drag_context_response_(false),
+ enabled_bindings_(0),
+ pending_request_id_(0),
+ navigations_suspended_(false),
+ suspended_nav_message_(NULL),
+ run_modal_reply_msg_(NULL),
+ is_waiting_for_beforeunload_ack_(false),
+ is_waiting_for_unload_ack_(false),
+ unload_ack_is_for_cross_site_transition_(false),
+ are_javascript_messages_suppressed_(false),
+ sudden_termination_allowed_(false),
+ session_storage_namespace_(session_storage),
+ is_extension_process_(false),
+ save_accessibility_tree_for_testing_(false),
+ render_view_termination_status_(base::TERMINATION_STATUS_STILL_RUNNING) {
+ if (!session_storage_namespace_) {
+ session_storage_namespace_ =
+ new SessionStorageNamespace(process()->profile());
+ }
+
+ DCHECK(instance_);
+ DCHECK(delegate_);
+}
+
+RenderViewHost::~RenderViewHost() {
+ delegate()->RenderViewDeleted(this);
+
+ // Be sure to clean up any leftover state from cross-site requests.
+ CrossSiteRequestManager::GetInstance()->SetHasPendingCrossSiteRequest(
+ process()->id(), routing_id(), false);
+}
+
+bool RenderViewHost::CreateRenderView(const string16& frame_name) {
+ DCHECK(!IsRenderViewLive()) << "Creating view twice";
+
+ // The process may (if we're sharing a process with another host that already
+ // initialized it) or may not (we have our own process or the old process
+ // crashed) have been initialized. Calling Init multiple times will be
+ // ignored, so this is safe.
+ if (!process()->Init(renderer_accessible(), is_extension_process_))
+ return false;
+ DCHECK(process()->HasConnection());
+ DCHECK(process()->profile());
+
+ if (BindingsPolicy::is_web_ui_enabled(enabled_bindings_)) {
+ ChildProcessSecurityPolicy::GetInstance()->GrantWebUIBindings(
+ process()->id());
+ }
+
+ if (BindingsPolicy::is_extension_enabled(enabled_bindings_)) {
+ ChildProcessSecurityPolicy::GetInstance()->GrantExtensionBindings(
+ process()->id());
+
+ // Extensions may have permission to access chrome:// URLs.
+ ChildProcessSecurityPolicy::GetInstance()->GrantScheme(
+ process()->id(), chrome::kChromeUIScheme);
+ }
+
+ renderer_initialized_ = true;
+
+ ViewMsg_New_Params params;
+ params.parent_window = GetNativeViewId();
+ params.renderer_preferences =
+ delegate_->GetRendererPrefs(process()->profile());
+ params.web_preferences = delegate_->GetWebkitPrefs();
+ params.view_id = routing_id();
+ params.session_storage_namespace_id = session_storage_namespace_->id();
+ params.frame_name = frame_name;
+ Send(new ViewMsg_New(params));
+
+ // Set the alternate error page, which is profile specific, in the renderer.
+ GURL url = delegate_->GetAlternateErrorPageURL();
+ SetAlternateErrorPageURL(url);
+
+ // If it's enabled, tell the renderer to set up the Javascript bindings for
+ // sending messages back to the browser.
+ Send(new ViewMsg_AllowBindings(routing_id(), enabled_bindings_));
+ UpdateBrowserWindowId(delegate_->GetBrowserWindowID());
+ Send(new ViewMsg_NotifyRenderViewType(routing_id(),
+ delegate_->GetRenderViewType()));
+ // Let our delegate know that we created a RenderView.
+ delegate_->RenderViewCreated(this);
+ process()->ViewCreated();
+
+ return true;
+}
+
+bool RenderViewHost::IsRenderViewLive() const {
+ return process()->HasConnection() && renderer_initialized_;
+}
+
+void RenderViewHost::SyncRendererPrefs() {
+ Send(new ViewMsg_SetRendererPrefs(routing_id(),
+ delegate_->GetRendererPrefs(
+ process()->profile())));
+}
+
+void RenderViewHost::Navigate(const ViewMsg_Navigate_Params& params) {
+ ChildProcessSecurityPolicy::GetInstance()->GrantRequestURL(
+ process()->id(), params.url);
+
+ ViewMsg_Navigate* nav_message = new ViewMsg_Navigate(routing_id(), params);
+
+ // Only send the message if we aren't suspended at the start of a cross-site
+ // request.
+ if (navigations_suspended_) {
+ // Shouldn't be possible to have a second navigation while suspended, since
+ // navigations will only be suspended during a cross-site request. If a
+ // second navigation occurs, TabContents will cancel this pending RVH
+ // create a new pending RVH.
+ DCHECK(!suspended_nav_message_.get());
+ suspended_nav_message_.reset(nav_message);
+ } else {
+ // Unset this, otherwise if true and the hang monitor fires we'll
+ // incorrectly close the tab.
+ is_waiting_for_unload_ack_ = false;
+
+ Send(nav_message);
+
+ // Force the throbber to start. We do this because WebKit's "started
+ // loading" message will be received asynchronously from the UI of the
+ // browser. But we want to keep the throbber in sync with what's happening
+ // in the UI. For example, we want to start throbbing immediately when the
+ // user naivgates even if the renderer is delayed. There is also an issue
+ // with the throbber starting because the WebUI (which controls whether the
+ // favicon is displayed) happens synchronously. If the start loading
+ // messages was asynchronous, then the default favicon would flash in.
+ //
+ // WebKit doesn't send throb notifications for JavaScript URLs, so we
+ // don't want to either.
+ if (!params.url.SchemeIs(chrome::kJavaScriptScheme))
+ delegate_->DidStartLoading();
+ }
+ const GURL& url = params.url;
+ if (!delegate_->IsExternalTabContainer() &&
+ (url.SchemeIs("http") || url.SchemeIs("https")))
+ chrome_browser_net::PreconnectUrlAndSubresources(url);
+}
+
+void RenderViewHost::NavigateToURL(const GURL& url) {
+ ViewMsg_Navigate_Params params;
+ params.page_id = -1;
+ params.url = url;
+ params.transition = PageTransition::LINK;
+ params.navigation_type = ViewMsg_Navigate_Params::NORMAL;
+ Navigate(params);
+}
+
+void RenderViewHost::SetNavigationsSuspended(bool suspend) {
+ // This should only be called to toggle the state.
+ DCHECK(navigations_suspended_ != suspend);
+
+ navigations_suspended_ = suspend;
+ if (!suspend && suspended_nav_message_.get()) {
+ // There's a navigation message waiting to be sent. Now that we're not
+ // suspended anymore, resume navigation by sending it.
+ Send(suspended_nav_message_.release());
+ }
+}
+
+void RenderViewHost::FirePageBeforeUnload(bool for_cross_site_transition) {
+ if (!IsRenderViewLive()) {
+ // This RenderViewHost doesn't have a live renderer, so just skip running
+ // the onbeforeunload handler.
+ is_waiting_for_beforeunload_ack_ = true; // Checked by OnMsgShouldCloseACK.
+ unload_ack_is_for_cross_site_transition_ = for_cross_site_transition;
+ OnMsgShouldCloseACK(true);
+ return;
+ }
+
+ // This may be called more than once (if the user clicks the tab close button
+ // several times, or if she clicks the tab close button then the browser close
+ // button), and we only send the message once.
+ if (is_waiting_for_beforeunload_ack_) {
+ // Some of our close messages could be for the tab, others for cross-site
+ // transitions. We always want to think it's for closing the tab if any
+ // of the messages were, since otherwise it might be impossible to close
+ // (if there was a cross-site "close" request pending when the user clicked
+ // the close button). We want to keep the "for cross site" flag only if
+ // both the old and the new ones are also for cross site.
+ unload_ack_is_for_cross_site_transition_ =
+ unload_ack_is_for_cross_site_transition_ && for_cross_site_transition;
+ } else {
+ // Start the hang monitor in case the renderer hangs in the beforeunload
+ // handler.
+ is_waiting_for_beforeunload_ack_ = true;
+ unload_ack_is_for_cross_site_transition_ = for_cross_site_transition;
+ StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
+ Send(new ViewMsg_ShouldClose(routing_id()));
+ }
+}
+
+void RenderViewHost::ClosePage(bool for_cross_site_transition,
+ int new_render_process_host_id,
+ int new_request_id) {
+ // In most cases, this will not be set to false afterward. Either the tab
+ // will be closed, or a pending RenderViewHost will replace this one.
+ is_waiting_for_unload_ack_ = true;
+ // Start the hang monitor in case the renderer hangs in the unload handler.
+ StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
+
+ ViewMsg_ClosePage_Params params;
+ params.closing_process_id = process()->id();
+ params.closing_route_id = routing_id();
+ params.for_cross_site_transition = for_cross_site_transition;
+ params.new_render_process_host_id = new_render_process_host_id;
+ params.new_request_id = new_request_id;
+ if (IsRenderViewLive()) {
+ NotificationService::current()->Notify(
+ NotificationType::RENDER_VIEW_HOST_WILL_CLOSE_RENDER_VIEW,
+ Source<RenderViewHost>(this),
+ NotificationService::NoDetails());
+
+ Send(new ViewMsg_ClosePage(routing_id(), params));
+ } else {
+ // This RenderViewHost doesn't have a live renderer, so just skip closing
+ // the page. We must notify the ResourceDispatcherHost on the IO thread,
+ // which we will do through the RenderProcessHost's widget helper.
+ process()->CrossSiteClosePageACK(params);
+ }
+}
+
+void RenderViewHost::ClosePageIgnoringUnloadEvents() {
+ StopHangMonitorTimeout();
+ is_waiting_for_beforeunload_ack_ = false;
+ is_waiting_for_unload_ack_ = false;
+
+ sudden_termination_allowed_ = true;
+ delegate_->Close(this);
+}
+
+void RenderViewHost::SetHasPendingCrossSiteRequest(bool has_pending_request,
+ int request_id) {
+ CrossSiteRequestManager::GetInstance()->SetHasPendingCrossSiteRequest(
+ process()->id(), routing_id(), has_pending_request);
+ pending_request_id_ = request_id;
+}
+
+int RenderViewHost::GetPendingRequestId() {
+ return pending_request_id_;
+}
+
+RenderViewHost::CommandState RenderViewHost::GetStateForCommand(
+ RenderViewCommand command) const {
+ if (command != RENDER_VIEW_COMMAND_TOGGLE_SPELL_CHECK)
+ LOG(DFATAL) << "Unknown command " << command;
+
+ std::map<RenderViewCommand, CommandState>::const_iterator it =
+ command_states_.find(command);
+ if (it == command_states_.end()) {
+ CommandState state;
+ state.is_enabled = false;
+ state.checked_state = RENDER_VIEW_COMMAND_CHECKED_STATE_UNCHECKED;
+ return state;
+ }
+ return it->second;
+}
+
+void RenderViewHost::Stop() {
+ Send(new ViewMsg_Stop(routing_id()));
+}
+
+void RenderViewHost::ReloadFrame() {
+ Send(new ViewMsg_ReloadFrame(routing_id()));
+}
+
+bool RenderViewHost::PrintPages() {
+ return Send(new ViewMsg_PrintPages(routing_id()));
+}
+
+bool RenderViewHost::PrintPreview() {
+ return Send(new ViewMsg_PrintPreview(routing_id()));
+}
+
+void RenderViewHost::PrintingDone(int document_cookie, bool success) {
+ Send(new ViewMsg_PrintingDone(routing_id(), document_cookie, success));
+}
+
+void RenderViewHost::StartFinding(int request_id,
+ const string16& search_text,
+ bool forward,
+ bool match_case,
+ bool find_next) {
+ if (search_text.empty())
+ return;
+
+ WebFindOptions options;
+ options.forward = forward;
+ options.matchCase = match_case;
+ options.findNext = find_next;
+ Send(new ViewMsg_Find(routing_id(), request_id, search_text, options));
+
+ // This call is asynchronous and returns immediately.
+ // The result of the search is sent as a notification message by the renderer.
+}
+
+void RenderViewHost::StopFinding(
+ FindBarController::SelectionAction selection_action) {
+ ViewMsg_StopFinding_Params params;
+
+ switch (selection_action) {
+ case FindBarController::kClearSelection:
+ params.action = ViewMsg_StopFinding_Params::kClearSelection;
+ break;
+ case FindBarController::kKeepSelection:
+ params.action = ViewMsg_StopFinding_Params::kKeepSelection;
+ break;
+ case FindBarController::kActivateSelection:
+ params.action = ViewMsg_StopFinding_Params::kActivateSelection;
+ break;
+ default:
+ NOTREACHED();
+ params.action = ViewMsg_StopFinding_Params::kKeepSelection;
+ }
+ Send(new ViewMsg_StopFinding(routing_id(), params));
+}
+
+void RenderViewHost::Zoom(PageZoom::Function function) {
+ Send(new ViewMsg_Zoom(routing_id(), function));
+}
+
+void RenderViewHost::SetZoomLevel(double zoom_level) {
+ Send(new ViewMsg_SetZoomLevel(routing_id(), zoom_level));
+}
+
+void RenderViewHost::SetPageEncoding(const std::string& encoding_name) {
+ Send(new ViewMsg_SetPageEncoding(routing_id(), encoding_name));
+}
+
+void RenderViewHost::ResetPageEncodingToDefault() {
+ Send(new ViewMsg_ResetPageEncodingToDefault(routing_id()));
+}
+
+void RenderViewHost::SetAlternateErrorPageURL(const GURL& url) {
+ Send(new ViewMsg_SetAltErrorPageURL(routing_id(), url));
+}
+
+void RenderViewHost::DragTargetDragEnter(
+ const WebDropData& drop_data,
+ const gfx::Point& client_pt,
+ const gfx::Point& screen_pt,
+ WebDragOperationsMask operations_allowed) {
+ // Grant the renderer the ability to load the drop_data.
+ ChildProcessSecurityPolicy* policy =
+ ChildProcessSecurityPolicy::GetInstance();
+ policy->GrantRequestURL(process()->id(), drop_data.url);
+ for (std::vector<string16>::const_iterator iter(drop_data.filenames.begin());
+ iter != drop_data.filenames.end(); ++iter) {
+ FilePath path = FilePath::FromWStringHack(UTF16ToWideHack(*iter));
+ policy->GrantRequestURL(process()->id(),
+ net::FilePathToFileURL(path));
+ policy->GrantReadFile(process()->id(), path);
+ }
+ Send(new ViewMsg_DragTargetDragEnter(routing_id(), drop_data, client_pt,
+ screen_pt, operations_allowed));
+}
+
+void RenderViewHost::DragTargetDragOver(
+ const gfx::Point& client_pt, const gfx::Point& screen_pt,
+ WebDragOperationsMask operations_allowed) {
+ Send(new ViewMsg_DragTargetDragOver(routing_id(), client_pt, screen_pt,
+ operations_allowed));
+}
+
+void RenderViewHost::DragTargetDragLeave() {
+ Send(new ViewMsg_DragTargetDragLeave(routing_id()));
+}
+
+void RenderViewHost::DragTargetDrop(
+ const gfx::Point& client_pt, const gfx::Point& screen_pt) {
+ Send(new ViewMsg_DragTargetDrop(routing_id(), client_pt, screen_pt));
+}
+
+void RenderViewHost::ReservePageIDRange(int size) {
+ Send(new ViewMsg_ReservePageIDRange(routing_id(), size));
+}
+
+void RenderViewHost::ExecuteJavascriptInWebFrame(
+ const string16& frame_xpath,
+ const string16& jscript) {
+ Send(new ViewMsg_ScriptEvalRequest(routing_id(), frame_xpath, jscript,
+ 0, false));
+}
+
+int RenderViewHost::ExecuteJavascriptInWebFrameNotifyResult(
+ const string16& frame_xpath,
+ const string16& jscript) {
+ static int next_id = 1;
+ Send(new ViewMsg_ScriptEvalRequest(routing_id(), frame_xpath, jscript,
+ next_id, true));
+ return next_id++;
+}
+
+void RenderViewHost::InsertCSSInWebFrame(
+ const std::wstring& frame_xpath,
+ const std::string& css,
+ const std::string& id) {
+ Send(new ViewMsg_CSSInsertRequest(routing_id(), frame_xpath, css, id));
+}
+
+void RenderViewHost::AddMessageToConsole(
+ const string16& frame_xpath,
+ const string16& message,
+ const WebConsoleMessage::Level& level) {
+ Send(new ViewMsg_AddMessageToConsole(
+ routing_id(), frame_xpath, message, level));
+}
+
+void RenderViewHost::Undo() {
+ Send(new ViewMsg_Undo(routing_id()));
+}
+
+void RenderViewHost::Redo() {
+ Send(new ViewMsg_Redo(routing_id()));
+}
+
+void RenderViewHost::Cut() {
+ Send(new ViewMsg_Cut(routing_id()));
+}
+
+void RenderViewHost::Copy() {
+ Send(new ViewMsg_Copy(routing_id()));
+}
+
+void RenderViewHost::CopyToFindPboard() {
+#if defined(OS_MACOSX)
+ // Windows/Linux don't have the concept of a find pasteboard.
+ Send(new ViewMsg_CopyToFindPboard(routing_id()));
+#endif
+}
+
+void RenderViewHost::Paste() {
+ Send(new ViewMsg_Paste(routing_id()));
+}
+
+void RenderViewHost::ToggleSpellCheck() {
+ Send(new ViewMsg_ToggleSpellCheck(routing_id()));
+}
+
+void RenderViewHost::Delete() {
+ Send(new ViewMsg_Delete(routing_id()));
+}
+
+void RenderViewHost::SelectAll() {
+ Send(new ViewMsg_SelectAll(routing_id()));
+}
+
+void RenderViewHost::ToggleSpellPanel(bool is_currently_visible) {
+ Send(new ViewMsg_ToggleSpellPanel(routing_id(), is_currently_visible));
+}
+
+int RenderViewHost::DownloadFavIcon(const GURL& url, int image_size) {
+ if (!url.is_valid()) {
+ NOTREACHED();
+ return 0;
+ }
+ static int next_id = 1;
+ int id = next_id++;
+ Send(new ViewMsg_DownloadFavIcon(routing_id(), id, url, image_size));
+ return id;
+}
+
+void RenderViewHost::GetApplicationInfo(int32 page_id) {
+ Send(new ViewMsg_GetApplicationInfo(routing_id(), page_id));
+}
+
+void RenderViewHost::CaptureThumbnail() {
+ Send(new ViewMsg_CaptureThumbnail(routing_id()));
+}
+
+void RenderViewHost::CaptureSnapshot() {
+ Send(new ViewMsg_CaptureSnapshot(routing_id()));
+}
+
+void RenderViewHost::JavaScriptMessageBoxClosed(IPC::Message* reply_msg,
+ bool success,
+ const std::wstring& prompt) {
+ process()->set_ignore_input_events(false);
+ bool is_waiting =
+ is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_;
+ if (is_waiting)
+ StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
+
+ ViewHostMsg_RunJavaScriptMessage::WriteReplyParams(reply_msg,
+ success, prompt);
+ Send(reply_msg);
+
+ // If we are waiting for an unload or beforeunload ack and the user has
+ // suppressed messages, kill the tab immediately; a page that's spamming
+ // alerts in onbeforeunload is presumably malicious, so there's no point in
+ // continuing to run its script and dragging out the process.
+ // This must be done after sending the reply since RenderView can't close
+ // correctly while waiting for a response.
+ if (is_waiting && are_javascript_messages_suppressed_)
+ delegate_->RendererUnresponsive(this, is_waiting);
+}
+
+void RenderViewHost::ModalHTMLDialogClosed(IPC::Message* reply_msg,
+ const std::string& json_retval) {
+ if (is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_)
+ StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
+
+ ViewHostMsg_ShowModalHTMLDialog::WriteReplyParams(reply_msg, json_retval);
+ Send(reply_msg);
+}
+
+void RenderViewHost::CopyImageAt(int x, int y) {
+ Send(new ViewMsg_CopyImageAt(routing_id(), x, y));
+}
+
+void RenderViewHost::DragSourceEndedAt(
+ int client_x, int client_y, int screen_x, int screen_y,
+ WebDragOperation operation) {
+ Send(new ViewMsg_DragSourceEndedOrMoved(
+ routing_id(),
+ gfx::Point(client_x, client_y),
+ gfx::Point(screen_x, screen_y),
+ true, operation));
+}
+
+void RenderViewHost::DragSourceMovedTo(
+ int client_x, int client_y, int screen_x, int screen_y) {
+ Send(new ViewMsg_DragSourceEndedOrMoved(
+ routing_id(),
+ gfx::Point(client_x, client_y),
+ gfx::Point(screen_x, screen_y),
+ false, WebDragOperationNone));
+}
+
+void RenderViewHost::DragSourceSystemDragEnded() {
+ Send(new ViewMsg_DragSourceSystemDragEnded(routing_id()));
+}
+
+void RenderViewHost::AllowBindings(int bindings_flags) {
+ DCHECK(!renderer_initialized_);
+ enabled_bindings_ |= bindings_flags;
+}
+
+void RenderViewHost::SetWebUIProperty(const std::string& name,
+ const std::string& value) {
+ DCHECK(BindingsPolicy::is_web_ui_enabled(enabled_bindings_));
+ Send(new ViewMsg_SetWebUIProperty(routing_id(), name, value));
+}
+
+void RenderViewHost::GotFocus() {
+ RenderWidgetHost::GotFocus(); // Notifies the renderer it got focus.
+
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (view)
+ view->GotFocus();
+}
+
+void RenderViewHost::LostCapture() {
+ RenderWidgetHost::LostCapture();
+
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (view)
+ view->LostCapture();
+}
+
+void RenderViewHost::SetInitialFocus(bool reverse) {
+ Send(new ViewMsg_SetInitialFocus(routing_id(), reverse));
+}
+
+void RenderViewHost::ClearFocusedNode() {
+ Send(new ViewMsg_ClearFocusedNode(routing_id()));
+}
+
+void RenderViewHost::ScrollFocusedEditableNodeIntoView() {
+ Send(new ViewMsg_ScrollFocusedEditableNodeIntoView(routing_id()));
+}
+
+void RenderViewHost::UpdateWebPreferences(const WebPreferences& prefs) {
+ Send(new ViewMsg_UpdateWebPreferences(routing_id(), prefs));
+}
+
+void RenderViewHost::InstallMissingPlugin() {
+ Send(new ViewMsg_InstallMissingPlugin(routing_id()));
+}
+
+void RenderViewHost::LoadBlockedPlugins() {
+ Send(new ViewMsg_LoadBlockedPlugins(routing_id()));
+}
+
+void RenderViewHost::FilesSelectedInChooser(
+ const std::vector<FilePath>& files) {
+ // Grant the security access requested to the given files.
+ for (std::vector<FilePath>::const_iterator file = files.begin();
+ file != files.end(); ++file) {
+ ChildProcessSecurityPolicy::GetInstance()->GrantReadFile(
+ process()->id(), *file);
+ }
+ Send(new ViewMsg_RunFileChooserResponse(routing_id(), files));
+}
+
+void RenderViewHost::LoadStateChanged(const GURL& url,
+ net::LoadState load_state,
+ uint64 upload_position,
+ uint64 upload_size) {
+ delegate_->LoadStateChanged(url, load_state, upload_position, upload_size);
+}
+
+bool RenderViewHost::SuddenTerminationAllowed() const {
+ return sudden_termination_allowed_ || process()->sudden_termination_allowed();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// RenderViewHost, IPC message handlers:
+
+bool RenderViewHost::OnMessageReceived(const IPC::Message& msg) {
+#if defined(OS_WIN)
+ // On Windows there's a potential deadlock with sync messsages going in
+ // a circle from browser -> plugin -> renderer -> browser.
+ // On Linux we can avoid this by avoiding sync messages from browser->plugin.
+ // On Mac we avoid this by not supporting windowed plugins.
+ if (msg.is_sync() && !msg.is_caller_pumping_messages()) {
+ // NOTE: IF YOU HIT THIS ASSERT, THE SOLUTION IS ALMOST NEVER TO RUN A
+ // NESTED MESSAGE LOOP IN THE RENDERER!!!
+ // That introduces reentrancy which causes hard to track bugs. You should
+ // find a way to either turn this into an asynchronous message, or one
+ // that can be answered on the IO thread.
+ NOTREACHED() << "Can't send sync messages to UI thread without pumping "
+ "messages in the renderer or else deadlocks can occur if the page "
+ "has windowed plugins! (message type " << msg.type() << ")";
+ IPC::Message* reply = IPC::SyncMessage::GenerateReply(&msg);
+ reply->set_reply_error();
+ Send(reply);
+ return true;
+ }
+#endif
+
+ if (delegate_->OnMessageReceived(msg))
+ return true;
+
+ bool handled = true;
+ bool msg_is_ok = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(RenderViewHost, msg, msg_is_ok)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ShowView, OnMsgShowView)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ShowWidget, OnMsgShowWidget)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ShowFullscreenWidget,
+ OnMsgShowFullscreenWidget)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_RunModal, OnMsgRunModal)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_RenderViewReady, OnMsgRenderViewReady)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_RenderViewGone, OnMsgRenderViewGone)
+ IPC_MESSAGE_HANDLER_GENERIC(ViewHostMsg_FrameNavigate, OnMsgNavigate(msg))
+ IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateState, OnMsgUpdateState)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateTitle, OnMsgUpdateTitle)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateEncoding, OnMsgUpdateEncoding)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateTargetURL, OnMsgUpdateTargetURL)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_Thumbnail, OnMsgThumbnail)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_Snapshot, OnMsgScreenshot)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateInspectorSetting,
+ OnUpdateInspectorSetting)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_Close, OnMsgClose)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_RequestMove, OnMsgRequestMove)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DidStartLoading, OnMsgDidStartLoading)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DidStopLoading, OnMsgDidStopLoading)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DidChangeLoadProgress,
+ OnMsgDidChangeLoadProgress)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DocumentAvailableInMainFrame,
+ OnMsgDocumentAvailableInMainFrame)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DocumentOnLoadCompletedInMainFrame,
+ OnMsgDocumentOnLoadCompletedInMainFrame)
+ IPC_MESSAGE_HANDLER(ViewMsg_ExecuteCodeFinished,
+ OnExecuteCodeFinished)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ContextMenu, OnMsgContextMenu)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_OpenURL, OnMsgOpenURL)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DidContentsPreferredSizeChange,
+ OnMsgDidContentsPreferredSizeChange)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DomOperationResponse,
+ OnMsgDomOperationResponse)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_WebUISend, OnMsgWebUISend)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ForwardMessageToExternalHost,
+ OnMsgForwardMessageToExternalHost)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_SetTooltipText, OnMsgSetTooltipText)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_RunJavaScriptMessage,
+ OnMsgRunJavaScriptMessage)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_RunBeforeUnloadConfirm,
+ OnMsgRunBeforeUnloadConfirm)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_ShowModalHTMLDialog,
+ OnMsgShowModalHTMLDialog)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_StartDragging, OnMsgStartDragging)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateDragCursor, OnUpdateDragCursor)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_TakeFocus, OnTakeFocus)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_AddMessageToConsole, OnAddMessageToConsole)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ForwardToDevToolsAgent,
+ OnForwardToDevToolsAgent)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ForwardToDevToolsClient,
+ OnForwardToDevToolsClient)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ActivateDevToolsWindow,
+ OnActivateDevToolsWindow)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_CloseDevToolsWindow,
+ OnCloseDevToolsWindow)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_RequestDockDevToolsWindow,
+ OnRequestDockDevToolsWindow)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_RequestUndockDevToolsWindow,
+ OnRequestUndockDevToolsWindow)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DevToolsRuntimePropertyChanged,
+ OnDevToolsRuntimePropertyChanged)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ShouldClose_ACK, OnMsgShouldCloseACK)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ExtensionRequest, OnExtensionRequest)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_SelectionChanged, OnMsgSelectionChanged)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ExtensionPostMessage,
+ OnExtensionPostMessage)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_AccessibilityNotifications,
+ OnAccessibilityNotifications)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_OnCSSInserted, OnCSSInserted)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ContentBlocked, OnContentBlocked)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_AppCacheAccessed, OnAppCacheAccessed)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_WebDatabaseAccessed, OnWebDatabaseAccessed)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_FocusedNodeChanged, OnMsgFocusedNodeChanged)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateZoomLimits, OnUpdateZoomLimits)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ScriptEvalResponse, OnScriptEvalResponse)
+#if defined(OS_MACOSX)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ShowPopup, OnMsgShowPopup)
+#endif
+ IPC_MESSAGE_HANDLER(ViewHostMsg_CommandStateChanged,
+ OnCommandStateChanged)
+ // Have the super handle all other messages.
+ IPC_MESSAGE_UNHANDLED(handled = RenderWidgetHost::OnMessageReceived(msg))
+ IPC_END_MESSAGE_MAP_EX()
+
+ if (!msg_is_ok) {
+ // The message had a handler, but its de-serialization failed.
+ // Kill the renderer.
+ UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_RVH"));
+ process()->ReceivedBadMessage();
+ }
+
+ return handled;
+}
+
+void RenderViewHost::Shutdown() {
+ // If we are being run modally (see RunModal), then we need to cleanup.
+ if (run_modal_reply_msg_) {
+ Send(run_modal_reply_msg_);
+ run_modal_reply_msg_ = NULL;
+ }
+
+ DevToolsManager* devtools_manager = DevToolsManager::GetInstance();
+ if (devtools_manager) // NULL in tests
+ devtools_manager->UnregisterDevToolsClientHostFor(this);
+
+ RenderWidgetHost::Shutdown();
+}
+
+bool RenderViewHost::IsRenderView() const {
+ return true;
+}
+
+void RenderViewHost::CreateNewWindow(
+ int route_id,
+ const ViewHostMsg_CreateWindow_Params& params) {
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (!view)
+ return;
+
+ view->CreateNewWindow(route_id, params);
+}
+
+void RenderViewHost::CreateNewWidget(int route_id,
+ WebKit::WebPopupType popup_type) {
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (view)
+ view->CreateNewWidget(route_id, popup_type);
+}
+
+void RenderViewHost::CreateNewFullscreenWidget(int route_id) {
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (view)
+ view->CreateNewFullscreenWidget(route_id);
+}
+
+void RenderViewHost::OnMsgShowView(int route_id,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture) {
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (view) {
+ view->ShowCreatedWindow(route_id, disposition, initial_pos, user_gesture);
+ Send(new ViewMsg_Move_ACK(route_id));
+ }
+}
+
+void RenderViewHost::OnMsgShowWidget(int route_id,
+ const gfx::Rect& initial_pos) {
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (view) {
+ view->ShowCreatedWidget(route_id, initial_pos);
+ Send(new ViewMsg_Move_ACK(route_id));
+ }
+}
+
+void RenderViewHost::OnMsgShowFullscreenWidget(int route_id) {
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (view) {
+ view->ShowCreatedFullscreenWidget(route_id);
+ Send(new ViewMsg_Move_ACK(route_id));
+ }
+}
+
+void RenderViewHost::OnMsgRunModal(IPC::Message* reply_msg) {
+ DCHECK(!run_modal_reply_msg_);
+ run_modal_reply_msg_ = reply_msg;
+
+ // TODO(darin): Bug 1107929: Need to inform our delegate to show this view in
+ // an app-modal fashion.
+}
+
+void RenderViewHost::OnMsgRenderViewReady() {
+ render_view_termination_status_ = base::TERMINATION_STATUS_STILL_RUNNING;
+ WasResized();
+ delegate_->RenderViewReady(this);
+}
+
+void RenderViewHost::OnMsgRenderViewGone(int status, int exit_code) {
+ // Keep the termination status so we can get at it later when we
+ // need to know why it died.
+ render_view_termination_status_ =
+ static_cast<base::TerminationStatus>(status);
+
+ // Our base class RenderWidgetHost needs to reset some stuff.
+ RendererExited(render_view_termination_status_, exit_code);
+
+ delegate_->RenderViewGone(this,
+ static_cast<base::TerminationStatus>(status),
+ exit_code);
+}
+
+// Called when the renderer navigates. For every frame loaded, we'll get this
+// notification containing parameters identifying the navigation.
+//
+// Subframes are identified by the page transition type. For subframes loaded
+// as part of a wider page load, the page_id will be the same as for the top
+// level frame. If the user explicitly requests a subframe navigation, we will
+// get a new page_id because we need to create a new navigation entry for that
+// action.
+void RenderViewHost::OnMsgNavigate(const IPC::Message& msg) {
+ // Read the parameters out of the IPC message directly to avoid making another
+ // copy when we filter the URLs.
+ void* iter = NULL;
+ ViewHostMsg_FrameNavigate_Params validated_params;
+ if (!IPC::ParamTraits<ViewHostMsg_FrameNavigate_Params>::
+ Read(&msg, &iter, &validated_params))
+ return;
+
+ // If we're waiting for a beforeunload ack from this renderer and we receive
+ // a Navigate message from the main frame, then the renderer was navigating
+ // before it received the request. If it is during a cross-site navigation,
+ // then we should forget about the beforeunload, because the navigation will
+ // now be canceled. (If it is instead during an attempt to close the page,
+ // we should be sure to keep waiting for the ack, which the new page will
+ // send.)
+ //
+ // If we did not clear this state, an unresponsiveness timer might think we
+ // are waiting for an ack but are not in a cross-site navigation, and would
+ // close the tab. TODO(creis): That timer code should be refactored to only
+ // close the tab if we explicitly know the user tried to close the tab, and
+ // not just check for the absence of a cross-site navigation. Once that's
+ // fixed, this check can go away.
+ if (is_waiting_for_beforeunload_ack_ &&
+ unload_ack_is_for_cross_site_transition_ &&
+ PageTransition::IsMainFrame(validated_params.transition)) {
+ is_waiting_for_beforeunload_ack_ = false;
+ StopHangMonitorTimeout();
+ }
+
+ // If we're waiting for an unload ack from this renderer and we receive a
+ // Navigate message, then the renderer was navigating before it received the
+ // unload request. It will either respond to the unload request soon or our
+ // timer will expire. Either way, we should ignore this message, because we
+ // have already committed to closing this renderer.
+ if (is_waiting_for_unload_ack_)
+ return;
+
+ const int renderer_id = process()->id();
+ ChildProcessSecurityPolicy* policy =
+ ChildProcessSecurityPolicy::GetInstance();
+ // Without this check, an evil renderer can trick the browser into creating
+ // a navigation entry for a banned URL. If the user clicks the back button
+ // followed by the forward button (or clicks reload, or round-trips through
+ // session restore, etc), we'll think that the browser commanded the
+ // renderer to load the URL and grant the renderer the privileges to request
+ // the URL. To prevent this attack, we block the renderer from inserting
+ // banned URLs into the navigation controller in the first place.
+ FilterURL(policy, renderer_id, &validated_params.url);
+ FilterURL(policy, renderer_id, &validated_params.referrer);
+ for (std::vector<GURL>::iterator it(validated_params.redirects.begin());
+ it != validated_params.redirects.end(); ++it) {
+ FilterURL(policy, renderer_id, &(*it));
+ }
+ FilterURL(policy, renderer_id, &validated_params.searchable_form_url);
+ FilterURL(policy, renderer_id, &validated_params.password_form.origin);
+ FilterURL(policy, renderer_id, &validated_params.password_form.action);
+
+ delegate_->DidNavigate(this, validated_params);
+}
+
+void RenderViewHost::OnMsgUpdateState(int32 page_id,
+ const std::string& state) {
+ delegate_->UpdateState(this, page_id, state);
+}
+
+void RenderViewHost::OnMsgUpdateTitle(int32 page_id,
+ const std::wstring& title) {
+ if (title.length() > chrome::kMaxTitleChars) {
+ NOTREACHED() << "Renderer sent too many characters in title.";
+ return;
+ }
+ delegate_->UpdateTitle(this, page_id, title);
+}
+
+void RenderViewHost::OnMsgUpdateEncoding(const std::string& encoding_name) {
+ delegate_->UpdateEncoding(this, encoding_name);
+}
+
+void RenderViewHost::OnMsgUpdateTargetURL(int32 page_id,
+ const GURL& url) {
+ delegate_->UpdateTargetURL(page_id, url);
+
+ // Send a notification back to the renderer that we are ready to
+ // receive more target urls.
+ Send(new ViewMsg_UpdateTargetURL_ACK(routing_id()));
+}
+
+void RenderViewHost::OnMsgThumbnail(const GURL& url,
+ const ThumbnailScore& score,
+ const SkBitmap& bitmap) {
+ delegate_->UpdateThumbnail(url, bitmap, score);
+}
+
+void RenderViewHost::OnMsgScreenshot(const SkBitmap& bitmap) {
+ NotificationService::current()->Notify(
+ NotificationType::TAB_SNAPSHOT_TAKEN,
+ Source<RenderViewHost>(this),
+ Details<const SkBitmap>(&bitmap));
+}
+
+void RenderViewHost::OnUpdateInspectorSetting(
+ const std::string& key, const std::string& value) {
+ delegate_->UpdateInspectorSetting(key, value);
+}
+
+void RenderViewHost::OnMsgClose() {
+ // If the renderer is telling us to close, it has already run the unload
+ // events, and we can take the fast path.
+ ClosePageIgnoringUnloadEvents();
+}
+
+void RenderViewHost::OnMsgRequestMove(const gfx::Rect& pos) {
+ delegate_->RequestMove(pos);
+ Send(new ViewMsg_Move_ACK(routing_id()));
+}
+
+void RenderViewHost::OnMsgDidStartLoading() {
+ delegate_->DidStartLoading();
+}
+
+void RenderViewHost::OnMsgDidStopLoading() {
+ delegate_->DidStopLoading();
+}
+
+void RenderViewHost::OnMsgDidChangeLoadProgress(double load_progress) {
+ delegate_->DidChangeLoadProgress(load_progress);
+}
+
+void RenderViewHost::OnMsgDocumentAvailableInMainFrame() {
+ delegate_->DocumentAvailableInMainFrame(this);
+}
+
+void RenderViewHost::OnMsgDocumentOnLoadCompletedInMainFrame(int32 page_id) {
+ delegate_->DocumentOnLoadCompletedInMainFrame(this, page_id);
+}
+
+void RenderViewHost::OnExecuteCodeFinished(int request_id, bool success) {
+ std::pair<int, bool> result_details(request_id, success);
+ NotificationService::current()->Notify(
+ NotificationType::TAB_CODE_EXECUTED,
+ NotificationService::AllSources(),
+ Details<std::pair<int, bool> >(&result_details));
+}
+
+void RenderViewHost::OnMsgContextMenu(const ContextMenuParams& params) {
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (!view)
+ return;
+
+ // Validate the URLs in |params|. If the renderer can't request the URLs
+ // directly, don't show them in the context menu.
+ ContextMenuParams validated_params(params);
+ int renderer_id = process()->id();
+ ChildProcessSecurityPolicy* policy =
+ ChildProcessSecurityPolicy::GetInstance();
+
+ // We don't validate |unfiltered_link_url| so that this field can be used
+ // when users want to copy the original link URL.
+ FilterURL(policy, renderer_id, &validated_params.link_url);
+ FilterURL(policy, renderer_id, &validated_params.src_url);
+ FilterURL(policy, renderer_id, &validated_params.page_url);
+ FilterURL(policy, renderer_id, &validated_params.frame_url);
+
+ view->ShowContextMenu(validated_params);
+}
+
+void RenderViewHost::OnMsgOpenURL(const GURL& url,
+ const GURL& referrer,
+ WindowOpenDisposition disposition) {
+ GURL validated_url(url);
+ FilterURL(ChildProcessSecurityPolicy::GetInstance(),
+ process()->id(), &validated_url);
+
+ delegate_->RequestOpenURL(validated_url, referrer, disposition);
+}
+
+void RenderViewHost::OnMsgDidContentsPreferredSizeChange(
+ const gfx::Size& new_size) {
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (!view)
+ return;
+ view->UpdatePreferredSize(new_size);
+}
+
+void RenderViewHost::OnMsgDomOperationResponse(
+ const std::string& json_string, int automation_id) {
+ delegate_->DomOperationResponse(json_string, automation_id);
+
+ // We also fire a notification for more loosely-coupled use cases.
+ DomOperationNotificationDetails details(json_string, automation_id);
+ NotificationService::current()->Notify(
+ NotificationType::DOM_OPERATION_RESPONSE, Source<RenderViewHost>(this),
+ Details<DomOperationNotificationDetails>(&details));
+}
+
+void RenderViewHost::OnMsgWebUISend(
+ const GURL& source_url, const std::string& message,
+ const std::string& content) {
+ if (!ChildProcessSecurityPolicy::GetInstance()->
+ HasWebUIBindings(process()->id())) {
+ NOTREACHED() << "Blocked unauthorized use of WebUIBindings.";
+ return;
+ }
+
+ scoped_ptr<Value> value;
+ if (!content.empty()) {
+ value.reset(base::JSONReader::Read(content, false));
+ if (!value.get() || !value->IsType(Value::TYPE_LIST)) {
+ // The page sent us something that we didn't understand.
+ // This probably indicates a programming error.
+ NOTREACHED() << "Invalid JSON argument in OnMsgWebUISend.";
+ return;
+ }
+ }
+
+ ViewHostMsg_DomMessage_Params params;
+ params.name = message;
+ if (value.get())
+ params.arguments.Swap(static_cast<ListValue*>(value.get()));
+ params.source_url = source_url;
+ // WebUI doesn't use these values yet.
+ // TODO(aa): When WebUI is ported to ExtensionFunctionDispatcher, send real
+ // values here.
+ params.request_id = -1;
+ params.has_callback = false;
+ params.user_gesture = false;
+ delegate_->ProcessWebUIMessage(params);
+}
+
+void RenderViewHost::OnMsgForwardMessageToExternalHost(
+ const std::string& message, const std::string& origin,
+ const std::string& target) {
+ delegate_->ProcessExternalHostMessage(message, origin, target);
+}
+
+void RenderViewHost::DisassociateFromPopupCount() {
+ Send(new ViewMsg_DisassociateFromPopupCount(routing_id()));
+}
+
+void RenderViewHost::AllowScriptToClose(bool script_can_close) {
+ Send(new ViewMsg_AllowScriptToClose(routing_id(), script_can_close));
+}
+
+void RenderViewHost::OnMsgSetTooltipText(
+ const std::wstring& tooltip_text,
+ WebTextDirection text_direction_hint) {
+ // First, add directionality marks around tooltip text if necessary.
+ // A naive solution would be to simply always wrap the text. However, on
+ // windows, Unicode directional embedding characters can't be displayed on
+ // systems that lack RTL fonts and are instead displayed as empty squares.
+ //
+ // To get around this we only wrap the string when we deem it necessary i.e.
+ // when the locale direction is different than the tooltip direction hint.
+ //
+ // Currently, we use element's directionality as the tooltip direction hint.
+ // An alternate solution would be to set the overall directionality based on
+ // trying to detect the directionality from the tooltip text rather than the
+ // element direction. One could argue that would be a preferable solution
+ // but we use the current approach to match Fx & IE's behavior.
+ std::wstring wrapped_tooltip_text = tooltip_text;
+ if (!tooltip_text.empty()) {
+ if (text_direction_hint == WebKit::WebTextDirectionLeftToRight) {
+ // Force the tooltip to have LTR directionality.
+ wrapped_tooltip_text = UTF16ToWide(
+ base::i18n::GetDisplayStringInLTRDirectionality(
+ WideToUTF16(wrapped_tooltip_text)));
+ } else if (text_direction_hint == WebKit::WebTextDirectionRightToLeft &&
+ !base::i18n::IsRTL()) {
+ // Force the tooltip to have RTL directionality.
+ base::i18n::WrapStringWithRTLFormatting(&wrapped_tooltip_text);
+ }
+ }
+ if (view())
+ view()->SetTooltipText(wrapped_tooltip_text);
+}
+
+void RenderViewHost::OnMsgSelectionChanged(const std::string& text) {
+ if (view())
+ view()->SelectionChanged(text);
+}
+
+void RenderViewHost::OnMsgRunJavaScriptMessage(
+ const std::wstring& message,
+ const std::wstring& default_prompt,
+ const GURL& frame_url,
+ const int flags,
+ IPC::Message* reply_msg) {
+ // While a JS message dialog is showing, tabs in the same process shouldn't
+ // process input events.
+ process()->set_ignore_input_events(true);
+ StopHangMonitorTimeout();
+ delegate_->RunJavaScriptMessage(message, default_prompt, frame_url, flags,
+ reply_msg,
+ &are_javascript_messages_suppressed_);
+}
+
+void RenderViewHost::OnMsgRunBeforeUnloadConfirm(const GURL& frame_url,
+ const std::wstring& message,
+ IPC::Message* reply_msg) {
+ // While a JS before unload dialog is showing, tabs in the same process
+ // shouldn't process input events.
+ process()->set_ignore_input_events(true);
+ StopHangMonitorTimeout();
+ delegate_->RunBeforeUnloadConfirm(message, reply_msg);
+}
+
+void RenderViewHost::OnMsgShowModalHTMLDialog(
+ const GURL& url, int width, int height, const std::string& json_arguments,
+ IPC::Message* reply_msg) {
+ StopHangMonitorTimeout();
+ delegate_->ShowModalHTMLDialog(url, width, height, json_arguments, reply_msg);
+}
+
+void RenderViewHost::MediaPlayerActionAt(const gfx::Point& location,
+ const WebMediaPlayerAction& action) {
+ // TODO(ajwong): Which thread should run this? Does it matter?
+ Send(new ViewMsg_MediaPlayerActionAt(routing_id(), location, action));
+}
+
+void RenderViewHost::ContextMenuClosed(
+ const webkit_glue::CustomContextMenuContext& custom_context) {
+ Send(new ViewMsg_ContextMenuClosed(routing_id(), custom_context));
+}
+
+void RenderViewHost::PrintNodeUnderContextMenu() {
+ Send(new ViewMsg_PrintNodeUnderContextMenu(routing_id()));
+}
+
+void RenderViewHost::PrintForPrintPreview() {
+ Send(new ViewMsg_PrintForPrintPreview(routing_id()));
+}
+
+void RenderViewHost::OnMsgStartDragging(
+ const WebDropData& drop_data,
+ WebDragOperationsMask drag_operations_mask,
+ const SkBitmap& image,
+ const gfx::Point& image_offset) {
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (view)
+ view->StartDragging(drop_data, drag_operations_mask, image, image_offset);
+}
+
+void RenderViewHost::OnUpdateDragCursor(WebDragOperation current_op) {
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (view)
+ view->UpdateDragCursor(current_op);
+}
+
+void RenderViewHost::OnTakeFocus(bool reverse) {
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (view)
+ view->TakeFocus(reverse);
+}
+
+void RenderViewHost::OnAddMessageToConsole(const std::wstring& message,
+ int32 line_no,
+ const std::wstring& source_id) {
+ std::wstring msg = StringPrintf(L"\"%ls,\" source: %ls (%d)", message.c_str(),
+ source_id.c_str(), line_no);
+ logging::LogMessage("CONSOLE", 0).stream() << msg;
+}
+
+void RenderViewHost::OnForwardToDevToolsAgent(const IPC::Message& message) {
+ DevToolsManager::GetInstance()->ForwardToDevToolsAgent(this, message);
+}
+
+void RenderViewHost::OnForwardToDevToolsClient(const IPC::Message& message) {
+ DevToolsManager::GetInstance()->ForwardToDevToolsClient(this, message);
+}
+
+void RenderViewHost::OnActivateDevToolsWindow() {
+ DevToolsManager::GetInstance()->ActivateWindow(this);
+}
+
+void RenderViewHost::OnCloseDevToolsWindow() {
+ DevToolsManager::GetInstance()->CloseWindow(this);
+}
+
+void RenderViewHost::OnRequestDockDevToolsWindow() {
+ DevToolsManager::GetInstance()->RequestDockWindow(this);
+}
+
+void RenderViewHost::OnRequestUndockDevToolsWindow() {
+ DevToolsManager::GetInstance()->RequestUndockWindow(this);
+}
+
+void RenderViewHost::OnDevToolsRuntimePropertyChanged(
+ const std::string& name,
+ const std::string& value) {
+ DevToolsManager::GetInstance()->
+ RuntimePropertyChanged(this, name, value);
+}
+
+bool RenderViewHost::PreHandleKeyboardEvent(
+ const NativeWebKeyboardEvent& event, bool* is_keyboard_shortcut) {
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ return view && view->PreHandleKeyboardEvent(event, is_keyboard_shortcut);
+}
+
+void RenderViewHost::UnhandledKeyboardEvent(
+ const NativeWebKeyboardEvent& event) {
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (view)
+ view->HandleKeyboardEvent(event);
+}
+
+void RenderViewHost::OnUserGesture() {
+ delegate_->OnUserGesture();
+}
+
+void RenderViewHost::GetMalwareDOMDetails() {
+ Send(new ViewMsg_GetMalwareDOMDetails(routing_id()));
+}
+
+void RenderViewHost::GetAllSavableResourceLinksForCurrentPage(
+ const GURL& page_url) {
+ Send(new ViewMsg_GetAllSavableResourceLinksForCurrentPage(routing_id(),
+ page_url));
+}
+
+void RenderViewHost::GetSerializedHtmlDataForCurrentPageWithLocalLinks(
+ const std::vector<GURL>& links,
+ const std::vector<FilePath>& local_paths,
+ const FilePath& local_directory_name) {
+ Send(new ViewMsg_GetSerializedHtmlDataForCurrentPageWithLocalLinks(
+ routing_id(), links, local_paths, local_directory_name));
+}
+
+void RenderViewHost::OnMsgShouldCloseACK(bool proceed) {
+ StopHangMonitorTimeout();
+ // If this renderer navigated while the beforeunload request was in flight, we
+ // may have cleared this state in OnMsgNavigate, in which case we can ignore
+ // this message.
+ if (!is_waiting_for_beforeunload_ack_)
+ return;
+
+ is_waiting_for_beforeunload_ack_ = false;
+
+ RenderViewHostDelegate::RendererManagement* management_delegate =
+ delegate_->GetRendererManagementDelegate();
+ if (management_delegate) {
+ management_delegate->ShouldClosePage(
+ unload_ack_is_for_cross_site_transition_, proceed);
+ }
+}
+
+void RenderViewHost::WindowMoveOrResizeStarted() {
+ Send(new ViewMsg_MoveOrResizeStarted(routing_id()));
+}
+
+void RenderViewHost::NotifyRendererUnresponsive() {
+ delegate_->RendererUnresponsive(
+ this, is_waiting_for_beforeunload_ack_ || is_waiting_for_unload_ack_);
+}
+
+void RenderViewHost::NotifyRendererResponsive() {
+ delegate_->RendererResponsive(this);
+}
+
+void RenderViewHost::OnMsgFocusedNodeChanged(bool is_editable_node) {
+ delegate_->FocusedNodeChanged(is_editable_node);
+}
+
+void RenderViewHost::OnMsgFocus() {
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (view)
+ view->Activate();
+}
+
+void RenderViewHost::OnMsgBlur() {
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (view)
+ view->Deactivate();
+}
+
+void RenderViewHost::ForwardMouseEvent(
+ const WebKit::WebMouseEvent& mouse_event) {
+
+ // We make a copy of the mouse event because
+ // RenderWidgetHost::ForwardMouseEvent will delete |mouse_event|.
+ WebKit::WebMouseEvent event_copy(mouse_event);
+ RenderWidgetHost::ForwardMouseEvent(event_copy);
+
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (view) {
+ switch (event_copy.type) {
+ case WebInputEvent::MouseMove:
+ view->HandleMouseMove();
+ break;
+ case WebInputEvent::MouseLeave:
+ view->HandleMouseLeave();
+ break;
+ case WebInputEvent::MouseDown:
+ view->HandleMouseDown();
+ break;
+ case WebInputEvent::MouseWheel:
+ if (ignore_input_events() && delegate_)
+ delegate_->OnIgnoredUIEvent();
+ break;
+ case WebInputEvent::MouseUp:
+ view->HandleMouseUp();
+ default:
+ // For now, we don't care about the rest.
+ break;
+ }
+ }
+}
+
+void RenderViewHost::OnMouseActivate() {
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (view)
+ view->HandleMouseActivate();
+}
+
+void RenderViewHost::ForwardKeyboardEvent(
+ const NativeWebKeyboardEvent& key_event) {
+ if (ignore_input_events()) {
+ if (key_event.type == WebInputEvent::RawKeyDown && delegate_)
+ delegate_->OnIgnoredUIEvent();
+ return;
+ }
+ RenderWidgetHost::ForwardKeyboardEvent(key_event);
+}
+
+void RenderViewHost::ForwardEditCommand(const std::string& name,
+ const std::string& value) {
+ IPC::Message* message = new ViewMsg_ExecuteEditCommand(routing_id(),
+ name,
+ value);
+ Send(message);
+}
+
+void RenderViewHost::ForwardEditCommandsForNextKeyEvent(
+ const EditCommands& edit_commands) {
+ IPC::Message* message = new ViewMsg_SetEditCommandsForNextKeyEvent(
+ routing_id(), edit_commands);
+ Send(message);
+}
+
+void RenderViewHost::ForwardMessageFromExternalHost(const std::string& message,
+ const std::string& origin,
+ const std::string& target) {
+ Send(new ViewMsg_HandleMessageFromExternalHost(routing_id(), message, origin,
+ target));
+}
+
+void RenderViewHost::OnExtensionRequest(
+ const ViewHostMsg_DomMessage_Params& params) {
+ if (!ChildProcessSecurityPolicy::GetInstance()->
+ HasExtensionBindings(process()->id())) {
+ // This can happen if someone uses window.open() to open an extension URL
+ // from a non-extension context.
+ BlockExtensionRequest(params.request_id);
+ return;
+ }
+
+ delegate_->ProcessWebUIMessage(params);
+}
+
+void RenderViewHost::SendExtensionResponse(int request_id, bool success,
+ const std::string& response,
+ const std::string& error) {
+ Send(new ViewMsg_ExtensionResponse(routing_id(), request_id, success,
+ response, error));
+}
+
+void RenderViewHost::BlockExtensionRequest(int request_id) {
+ SendExtensionResponse(request_id, false, "",
+ "Access to extension API denied.");
+}
+
+void RenderViewHost::UpdateBrowserWindowId(int window_id) {
+ Send(new ViewMsg_UpdateBrowserWindowId(routing_id(), window_id));
+}
+
+void RenderViewHost::PerformCustomContextMenuAction(
+ const webkit_glue::CustomContextMenuContext& custom_context,
+ unsigned action) {
+ Send(new ViewMsg_CustomContextMenuAction(routing_id(),
+ custom_context,
+ action));
+}
+
+void RenderViewHost::SendContentSettings(const GURL& url,
+ const ContentSettings& settings) {
+ Send(new ViewMsg_SetContentSettingsForCurrentURL(url, settings));
+}
+
+void RenderViewHost::EnablePreferredSizeChangedMode(int flags) {
+ Send(new ViewMsg_EnablePreferredSizeChangedMode(routing_id(), flags));
+}
+
+#if defined(OS_MACOSX)
+void RenderViewHost::DidSelectPopupMenuItem(int selected_index) {
+ Send(new ViewMsg_SelectPopupMenuItem(routing_id(), selected_index));
+}
+
+void RenderViewHost::DidCancelPopupMenu() {
+ Send(new ViewMsg_SelectPopupMenuItem(routing_id(), -1));
+}
+#endif
+
+void RenderViewHost::SearchBoxChange(const string16& value,
+ bool verbatim,
+ int selection_start,
+ int selection_end) {
+ Send(new ViewMsg_SearchBoxChange(
+ routing_id(), value, verbatim, selection_start, selection_end));
+}
+
+void RenderViewHost::SearchBoxSubmit(const string16& value,
+ bool verbatim) {
+ Send(new ViewMsg_SearchBoxSubmit(routing_id(), value, verbatim));
+}
+
+void RenderViewHost::SearchBoxCancel() {
+ Send(new ViewMsg_SearchBoxCancel(routing_id()));
+}
+
+void RenderViewHost::SearchBoxResize(const gfx::Rect& search_box_bounds) {
+ Send(new ViewMsg_SearchBoxResize(routing_id(), search_box_bounds));
+}
+
+void RenderViewHost::DetermineIfPageSupportsInstant(const string16& value,
+ bool verbatim,
+ int selection_start,
+ int selection_end) {
+ Send(new ViewMsg_DetermineIfPageSupportsInstant(
+ routing_id(), value, verbatim, selection_start, selection_end));
+}
+
+void RenderViewHost::FilterURL(ChildProcessSecurityPolicy* policy,
+ int renderer_id,
+ GURL* url) {
+ if (!url->is_valid())
+ return; // We don't need to block invalid URLs.
+
+ if (url->SchemeIs(chrome::kAboutScheme)) {
+ // The renderer treats all URLs in the about: scheme as being about:blank.
+ // Canonicalize about: URLs to about:blank.
+ *url = GURL(chrome::kAboutBlankURL);
+ }
+
+ if (!policy->CanRequestURL(renderer_id, *url)) {
+ // If this renderer is not permitted to request this URL, we invalidate the
+ // URL. This prevents us from storing the blocked URL and becoming confused
+ // later.
+ VLOG(1) << "Blocked URL " << url->spec();
+ *url = GURL();
+ }
+}
+
+void RenderViewHost::JavaScriptStressTestControl(int cmd, int param) {
+ Send(new ViewMsg_JavaScriptStressTestControl(routing_id(), cmd, param));
+}
+
+void RenderViewHost::OnExtensionPostMessage(
+ int port_id, const std::string& message) {
+ if (process()->profile()->GetExtensionMessageService()) {
+ process()->profile()->GetExtensionMessageService()->
+ PostMessageFromRenderer(port_id, message);
+ }
+}
+
+void RenderViewHost::OnAccessibilityNotifications(
+ const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params) {
+ if (view())
+ view()->OnAccessibilityNotifications(params);
+
+ if (params.size() > 0) {
+ for (unsigned i = 0; i < params.size(); i++) {
+ const ViewHostMsg_AccessibilityNotification_Params& param = params[i];
+
+ if (param.notification_type ==
+ ViewHostMsg_AccessibilityNotification_Params::
+ NOTIFICATION_TYPE_LOAD_COMPLETE) {
+ // TODO(ctguil): Remove when mac processes OnAccessibilityNotifications.
+ if (view())
+ view()->UpdateAccessibilityTree(param.acc_obj);
+
+ if (save_accessibility_tree_for_testing_)
+ accessibility_tree_ = param.acc_obj;
+ }
+ }
+
+ NotificationService::current()->Notify(
+ NotificationType::RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED,
+ Source<RenderViewHost>(this),
+ NotificationService::NoDetails());
+ }
+
+ AccessibilityNotificationsAck();
+}
+
+void RenderViewHost::OnCSSInserted() {
+ delegate_->DidInsertCSS();
+}
+
+void RenderViewHost::OnContentBlocked(ContentSettingsType type,
+ const std::string& resource_identifier) {
+ RenderViewHostDelegate::ContentSettings* content_settings_delegate =
+ delegate_->GetContentSettingsDelegate();
+ if (content_settings_delegate)
+ content_settings_delegate->OnContentBlocked(type, resource_identifier);
+}
+
+void RenderViewHost::OnAppCacheAccessed(const GURL& manifest_url,
+ bool blocked_by_policy) {
+ RenderViewHostDelegate::ContentSettings* content_settings_delegate =
+ delegate_->GetContentSettingsDelegate();
+ if (content_settings_delegate)
+ content_settings_delegate->OnAppCacheAccessed(manifest_url,
+ blocked_by_policy);
+}
+
+void RenderViewHost::OnWebDatabaseAccessed(const GURL& url,
+ const string16& name,
+ const string16& display_name,
+ unsigned long estimated_size,
+ bool blocked_by_policy) {
+ RenderViewHostDelegate::ContentSettings* content_settings_delegate =
+ delegate_->GetContentSettingsDelegate();
+ if (content_settings_delegate)
+ content_settings_delegate->OnWebDatabaseAccessed(
+ url, name, display_name, estimated_size, blocked_by_policy);
+}
+
+void RenderViewHost::OnUpdateZoomLimits(int minimum_percent,
+ int maximum_percent,
+ bool remember) {
+ delegate_->UpdateZoomLimits(minimum_percent, maximum_percent, remember);
+}
+
+void RenderViewHost::OnScriptEvalResponse(int id, const ListValue& result) {
+ Value* result_value;
+ result.Get(0, &result_value);
+ std::pair<int, Value*> details(id, result_value);
+ NotificationService::current()->Notify(
+ NotificationType::EXECUTE_JAVASCRIPT_RESULT,
+ Source<RenderViewHost>(this),
+ Details<std::pair<int, Value*> >(&details));
+}
+
+#if defined(OS_MACOSX)
+void RenderViewHost::OnMsgShowPopup(
+ const ViewHostMsg_ShowPopup_Params& params) {
+ RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
+ if (view) {
+ view->ShowPopupMenu(params.bounds,
+ params.item_height,
+ params.item_font_size,
+ params.selected_item,
+ params.popup_items,
+ params.right_aligned);
+ }
+}
+#endif
+
+void RenderViewHost::OnCommandStateChanged(int command,
+ bool is_enabled,
+ int checked_state) {
+ if (command != RENDER_VIEW_COMMAND_TOGGLE_SPELL_CHECK) {
+ LOG(DFATAL) << "Unknown command " << command;
+ return;
+ }
+
+ if (checked_state != RENDER_VIEW_COMMAND_CHECKED_STATE_UNCHECKED &&
+ checked_state != RENDER_VIEW_COMMAND_CHECKED_STATE_CHECKED &&
+ checked_state != RENDER_VIEW_COMMAND_CHECKED_STATE_MIXED) {
+ LOG(DFATAL) << "Invalid checked state " << checked_state;
+ return;
+ }
+
+ CommandState state;
+ state.is_enabled = is_enabled;
+ state.checked_state =
+ static_cast<RenderViewCommandCheckedState>(checked_state);
+ command_states_[static_cast<RenderViewCommand>(command)] = state;
+}
diff --git a/content/browser/renderer_host/render_view_host.h b/content/browser/renderer_host/render_view_host.h
new file mode 100644
index 0000000..cc3bfac
--- /dev/null
+++ b/content/browser/renderer_host/render_view_host.h
@@ -0,0 +1,741 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_RENDER_VIEW_HOST_H_
+#define CONTENT_BROWSER_RENDERER_HOST_RENDER_VIEW_HOST_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/ui/find_bar/find_bar_controller.h"
+#include "chrome/common/content_settings_types.h"
+#include "chrome/common/page_zoom.h"
+#include "chrome/common/render_view_commands.h"
+#include "chrome/common/translate_errors.h"
+#include "chrome/common/view_types.h"
+#include "chrome/common/window_container_type.h"
+#include "content/browser/renderer_host/render_widget_host.h"
+#include "net/base/load_states.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebConsoleMessage.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDragOperation.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebPopupType.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebTextDirection.h"
+#include "webkit/glue/webaccessibility.h"
+#include "webkit/glue/window_open_disposition.h"
+
+class ChildProcessSecurityPolicy;
+class FilePath;
+class GURL;
+class ListValue;
+class RenderViewHostDelegate;
+class SessionStorageNamespace;
+class SiteInstance;
+class SkBitmap;
+class ViewMsg_Navigate;
+struct ContentSettings;
+struct ContextMenuParams;
+struct MediaPlayerAction;
+struct ThumbnailScore;
+struct ViewHostMsg_AccessibilityNotification_Params;
+struct ViewHostMsg_CreateWindow_Params;
+struct ViewHostMsg_DomMessage_Params;
+struct ViewHostMsg_ShowPopup_Params;
+struct ViewMsg_Navigate_Params;
+struct WebApplicationInfo;
+struct WebDropData;
+struct WebPreferences;
+struct UserMetricsAction;
+
+namespace gfx {
+class Point;
+} // namespace gfx
+
+namespace webkit_glue {
+struct CustomContextMenuContext;
+struct WebAccessibility;
+} // namespace webkit_glue
+
+namespace WebKit {
+struct WebMediaPlayerAction;
+} // namespace WebKit
+
+class URLRequestContextGetter;
+
+//
+// RenderViewHost
+//
+// A RenderViewHost is responsible for creating and talking to a RenderView
+// object in a child process. It exposes a high level API to users, for things
+// like loading pages, adjusting the display and other browser functionality,
+// which it translates into IPC messages sent over the IPC channel with the
+// RenderView. It responds to all IPC messages sent by that RenderView and
+// cracks them, calling a delegate object back with higher level types where
+// possible.
+//
+// The intent of this class is to provide a view-agnostic communication
+// conduit with a renderer. This is so we can build HTML views not only as
+// TabContents (see TabContents for an example) but also as views, etc.
+//
+// The exact API of this object needs to be more thoroughly designed. Right
+// now it mimics what TabContents exposed, which is a fairly large API and may
+// contain things that are not relevant to a common subset of views. See also
+// the comment in render_view_host_delegate.h about the size and scope of the
+// delegate API.
+//
+// Right now, the concept of page navigation (both top level and frame) exists
+// in the TabContents still, so if you instantiate one of these elsewhere, you
+// will not be able to traverse pages back and forward. We need to determine
+// if we want to bring that and other functionality down into this object so
+// it can be shared by others.
+//
+class RenderViewHost : public RenderWidgetHost {
+ public:
+ // Returns the RenderViewHost given its ID and the ID of its render process.
+ // Returns NULL if the IDs do not correspond to a live RenderViewHost.
+ static RenderViewHost* FromID(int render_process_id, int render_view_id);
+
+ // routing_id could be a valid route id, or it could be MSG_ROUTING_NONE, in
+ // which case RenderWidgetHost will create a new one.
+ //
+ // The session storage namespace parameter allows multiple render views and
+ // tab contentses to share the same session storage (part of the WebStorage
+ // spec) space. This is useful when restoring tabs, but most callers should
+ // pass in NULL which will cause a new SessionStorageNamespace to be created.
+ RenderViewHost(SiteInstance* instance,
+ RenderViewHostDelegate* delegate,
+ int routing_id,
+ SessionStorageNamespace* session_storage_namespace);
+ virtual ~RenderViewHost();
+
+ SiteInstance* site_instance() const { return instance_; }
+ RenderViewHostDelegate* delegate() const { return delegate_; }
+ void set_delegate(RenderViewHostDelegate* d) { delegate_ = d; }
+
+ // Set up the RenderView child process. Virtual because it is overridden by
+ // TestRenderViewHost. If the |frame_name| parameter is non-empty, it is used
+ // as the name of the new top-level frame.
+ virtual bool CreateRenderView(const string16& frame_name);
+
+ // Returns true if the RenderView is active and has not crashed. Virtual
+ // because it is overridden by TestRenderViewHost.
+ virtual bool IsRenderViewLive() const;
+
+ base::TerminationStatus render_view_termination_status() const {
+ return render_view_termination_status_;
+ }
+
+ // Send the renderer process the current preferences supplied by the
+ // RenderViewHostDelegate.
+ void SyncRendererPrefs();
+
+ // Sends the given navigation message. Use this rather than sending it
+ // yourself since this does the internal bookkeeping described below. This
+ // function takes ownership of the provided message pointer.
+ //
+ // If a cross-site request is in progress, we may be suspended while waiting
+ // for the onbeforeunload handler, so this function might buffer the message
+ // rather than sending it.
+ void Navigate(const ViewMsg_Navigate_Params& message);
+
+ // Load the specified URL, this is a shortcut for Navigate().
+ void NavigateToURL(const GURL& url);
+
+ // Returns whether navigation messages are currently suspended for this
+ // RenderViewHost. Only true during a cross-site navigation, while waiting
+ // for the onbeforeunload handler.
+ bool are_navigations_suspended() const { return navigations_suspended_; }
+
+ // Suspends (or unsuspends) any navigation messages from being sent from this
+ // RenderViewHost. This is called when a pending RenderViewHost is created
+ // for a cross-site navigation, because we must suspend any navigations until
+ // we hear back from the old renderer's onbeforeunload handler. Note that it
+ // is important that only one navigation event happen after calling this
+ // method with |suspend| equal to true. If |suspend| is false and there is
+ // a suspended_nav_message_, this will send the message. This function
+ // should only be called to toggle the state; callers should check
+ // are_navigations_suspended() first.
+ void SetNavigationsSuspended(bool suspend);
+
+ // Causes the renderer to invoke the onbeforeunload event handler. The
+ // result will be returned via ViewMsg_ShouldClose. See also ClosePage which
+ // will fire the PageUnload event.
+ //
+ // Set bool for_cross_site_transition when this close is just for the current
+ // RenderView in the case of a cross-site transition. False means we're
+ // closing the entire tab.
+ void FirePageBeforeUnload(bool for_cross_site_transition);
+
+ // Causes the renderer to close the current page, including running its
+ // onunload event handler. A ClosePage_ACK message will be sent to the
+ // ResourceDispatcherHost when it is finished.
+ //
+ // Please see ViewMsg_ClosePage in resource_messages_internal.h for a
+ // description of the parameters.
+ void ClosePage(bool for_cross_site_transition,
+ int new_render_process_host_id,
+ int new_request_id);
+
+ // Close the page ignoring whether it has unload events registers.
+ // This is called after the beforeunload and unload events have fired
+ // and the user has agreed to continue with closing the page.
+ void ClosePageIgnoringUnloadEvents();
+
+ // Sets whether this RenderViewHost has an outstanding cross-site request,
+ // for which another renderer will need to run an onunload event handler.
+ // This is called before the first navigation event for this RenderViewHost,
+ // and again after the corresponding OnCrossSiteResponse.
+ void SetHasPendingCrossSiteRequest(bool has_pending_request, int request_id);
+
+ // Returns the request_id for the pending cross-site request.
+ // This is just needed in case the unload of the current page
+ // hangs, in which case we need to swap to the pending RenderViewHost.
+ int GetPendingRequestId();
+
+ struct CommandState {
+ bool is_enabled;
+ RenderViewCommandCheckedState checked_state;
+ };
+ CommandState GetStateForCommand(RenderViewCommand command) const;
+
+ // Stops the current load.
+ void Stop();
+
+ // Reloads the current frame.
+ void ReloadFrame();
+
+ // Asks the renderer to "render" printed pages and initiate printing on our
+ // behalf.
+ bool PrintPages();
+
+ // Asks the renderer to render pages for print preview.
+ bool PrintPreview();
+
+ // Notify renderer of success/failure of print job.
+ void PrintingDone(int document_cookie, bool success);
+
+ // Start looking for a string within the content of the page, with the
+ // specified options.
+ void StartFinding(int request_id,
+ const string16& search_string,
+ bool forward,
+ bool match_case,
+ bool find_next);
+
+ // Cancel a pending find operation.
+ void StopFinding(FindBarController::SelectionAction selection_action);
+
+ // Increment, decrement, or reset the zoom level of a page.
+ void Zoom(PageZoom::Function function);
+
+ // Change the zoom level of a page to a specific value.
+ void SetZoomLevel(double zoom_level);
+
+ // Change the encoding of the page.
+ void SetPageEncoding(const std::string& encoding);
+
+ // Reset any override encoding on the page and change back to default.
+ void ResetPageEncodingToDefault();
+
+ // Change the alternate error page URL. An empty GURL disables the use of
+ // alternate error pages.
+ void SetAlternateErrorPageURL(const GURL& url);
+
+ // D&d drop target messages that get sent to WebKit.
+ void DragTargetDragEnter(const WebDropData& drop_data,
+ const gfx::Point& client_pt,
+ const gfx::Point& screen_pt,
+ WebKit::WebDragOperationsMask operations_allowed);
+ void DragTargetDragOver(const gfx::Point& client_pt,
+ const gfx::Point& screen_pt,
+ WebKit::WebDragOperationsMask operations_allowed);
+ void DragTargetDragLeave();
+ void DragTargetDrop(const gfx::Point& client_pt,
+ const gfx::Point& screen_pt);
+
+ // Tell the RenderView to reserve a range of page ids of the given size.
+ void ReservePageIDRange(int size);
+
+ // Runs some javascript within the context of a frame in the page.
+ void ExecuteJavascriptInWebFrame(const string16& frame_xpath,
+ const string16& jscript);
+
+ // Runs some javascript within the context of a frame in the page. The result
+ // is sent back via the notification EXECUTE_JAVASCRIPT_RESULT.
+ int ExecuteJavascriptInWebFrameNotifyResult(const string16& frame_xpath,
+ const string16& jscript);
+
+ // Insert some css into a frame in the page. |id| is optional, and specifies
+ // the element id given when inserting/replacing the style element.
+ void InsertCSSInWebFrame(const std::wstring& frame_xpath,
+ const std::string& css,
+ const std::string& id);
+
+ // Logs a message to the console of a frame in the page.
+ void AddMessageToConsole(const string16& frame_xpath,
+ const string16& message,
+ const WebKit::WebConsoleMessage::Level&);
+
+ // Edit operations.
+ void Undo();
+ void Redo();
+ void Cut();
+ void Copy();
+ void CopyToFindPboard();
+ void Paste();
+ void ToggleSpellCheck();
+ void Delete();
+ void SelectAll();
+ void ToggleSpellPanel(bool is_currently_visible);
+
+ // Downloads an image notifying the FavIcon delegate appropriately. The
+ // returned integer uniquely identifies the download for the lifetime of the
+ // browser.
+ int DownloadFavIcon(const GURL& url, int image_size);
+
+ // Requests application info for the specified page. This is an asynchronous
+ // request. The delegate is notified by way of OnDidGetApplicationInfo when
+ // the data is available.
+ void GetApplicationInfo(int32 page_id);
+
+ // Captures a thumbnail representation of the page.
+ void CaptureThumbnail();
+
+ // Captures a snapshot of the page.
+ void CaptureSnapshot();
+
+ // Notifies the RenderView that the JavaScript message that was shown was
+ // closed by the user.
+ void JavaScriptMessageBoxClosed(IPC::Message* reply_msg,
+ bool success,
+ const std::wstring& prompt);
+
+ // Notifies the RenderView that the modal html dialog has been closed.
+ void ModalHTMLDialogClosed(IPC::Message* reply_msg,
+ const std::string& json_retval);
+
+ // Send an action to the media player element located at |location|.
+ void MediaPlayerActionAt(const gfx::Point& location,
+ const WebKit::WebMediaPlayerAction& action);
+
+ // Notifies the renderer that the context menu has closed.
+ void ContextMenuClosed(
+ const webkit_glue::CustomContextMenuContext& custom_context);
+
+ // Prints the node that's under the context menu.
+ void PrintNodeUnderContextMenu();
+
+ // Triggers printing of the preview PDF.
+ void PrintForPrintPreview();
+
+ // Copies the image at the specified point.
+ void CopyImageAt(int x, int y);
+
+ // Notifies the renderer that a a drag operation that it started has ended,
+ // either in a drop or by being cancelled.
+ void DragSourceEndedAt(
+ int client_x, int client_y, int screen_x, int screen_y,
+ WebKit::WebDragOperation operation);
+
+ // Notifies the renderer that a drag and drop operation is in progress, with
+ // droppable items positioned over the renderer's view.
+ void DragSourceMovedTo(
+ int client_x, int client_y, int screen_x, int screen_y);
+
+ // Notifies the renderer that we're done with the drag and drop operation.
+ // This allows the renderer to reset some state.
+ void DragSourceSystemDragEnded();
+
+ // Tell the render view to enable a set of javascript bindings. The argument
+ // should be a combination of values from BindingsPolicy.
+ void AllowBindings(int binding_flags);
+
+ // Returns a bitwise OR of bindings types that have been enabled for this
+ // RenderView. See BindingsPolicy for details.
+ int enabled_bindings() const { return enabled_bindings_; }
+
+ // See variable comment.
+ bool is_extension_process() const { return is_extension_process_; }
+ void set_is_extension_process(bool is_extension_process) {
+ is_extension_process_ = is_extension_process;
+ }
+
+ // Sets a property with the given name and value on the Web UI binding object.
+ // Must call AllowWebUIBindings() on this renderer first.
+ void SetWebUIProperty(const std::string& name, const std::string& value);
+
+ // Tells the renderer view to focus the first (last if reverse is true) node.
+ void SetInitialFocus(bool reverse);
+
+ // Clears the node that is currently focused (if any).
+ void ClearFocusedNode();
+
+ // Tells the renderer view to scroll to the focused node.
+ void ScrollFocusedEditableNodeIntoView();
+
+ // Update render view specific (WebKit) preferences.
+ void UpdateWebPreferences(const WebPreferences& prefs);
+
+ // Request the Renderer to ask the default plugin to start installation of
+ // missing plugin. Called by PluginInstallerInfoBarDelegate.
+ void InstallMissingPlugin();
+
+ // Load all blocked plugins in the RenderView.
+ void LoadBlockedPlugins();
+
+ // Get all script and frame urls from all frames in the current document.
+ // Called when a malware interstitial page is shown.
+ void GetMalwareDOMDetails();
+
+ // Get all savable resource links from current webpage, include main
+ // frame and sub-frame.
+ void GetAllSavableResourceLinksForCurrentPage(const GURL& page_url);
+
+ // Get html data by serializing all frames of current page with lists
+ // which contain all resource links that have local copy.
+ // The parameter links contain original URLs of all saved links.
+ // The parameter local_paths contain corresponding local file paths of
+ // all saved links, which matched with vector:links one by one.
+ // The parameter local_directory_name is relative path of directory which
+ // contain all saved auxiliary files included all sub frames and resouces.
+ void GetSerializedHtmlDataForCurrentPageWithLocalLinks(
+ const std::vector<GURL>& links,
+ const std::vector<FilePath>& local_paths,
+ const FilePath& local_directory_name);
+
+ // Notifies the Listener that one or more files have been chosen by the user
+ // from an Open File dialog for the form.
+ void FilesSelectedInChooser(const std::vector<FilePath>& files);
+
+ // Notifies the RenderViewHost that its load state changed.
+ void LoadStateChanged(const GURL& url, net::LoadState load_state,
+ uint64 upload_position, uint64 upload_size);
+
+ bool SuddenTerminationAllowed() const;
+ void set_sudden_termination_allowed(bool enabled) {
+ sudden_termination_allowed_ = enabled;
+ }
+
+ // Forward a message from external host to chrome renderer.
+ void ForwardMessageFromExternalHost(const std::string& message,
+ const std::string& origin,
+ const std::string& target);
+
+ // Message the renderer that we should be counted as a new document and not
+ // as a popup.
+ void DisassociateFromPopupCount();
+
+ // Tells the renderer whether it should allow window.close. This is initially
+ // set to false when creating a renderer-initiated window via window.open.
+ void AllowScriptToClose(bool visible);
+
+ // Notifies the Renderer that a move or resize of its containing window has
+ // started (this is used to hide the autocomplete popups if any).
+ void WindowMoveOrResizeStarted();
+
+ // RenderWidgetHost public overrides.
+ virtual void Shutdown();
+ virtual bool IsRenderView() const;
+ virtual bool OnMessageReceived(const IPC::Message& msg);
+ virtual void GotFocus();
+ virtual void LostCapture();
+ virtual void ForwardMouseEvent(const WebKit::WebMouseEvent& mouse_event);
+ virtual void OnMouseActivate();
+ virtual void ForwardKeyboardEvent(const NativeWebKeyboardEvent& key_event);
+ virtual void ForwardEditCommand(const std::string& name,
+ const std::string& value);
+ virtual void ForwardEditCommandsForNextKeyEvent(
+ const EditCommands& edit_commands);
+
+ // Creates a new RenderView with the given route id.
+ void CreateNewWindow(int route_id,
+ const ViewHostMsg_CreateWindow_Params& params);
+
+ // Creates a new RenderWidget with the given route id. |popup_type| indicates
+ // if this widget is a popup and what kind of popup it is (select, autofill).
+ void CreateNewWidget(int route_id, WebKit::WebPopupType popup_type);
+
+ // Creates a full screen RenderWidget.
+ void CreateNewFullscreenWidget(int route_id);
+
+ // Sends the response to an extension api call.
+ void SendExtensionResponse(int request_id, bool success,
+ const std::string& response,
+ const std::string& error);
+
+ // Sends a response to an extension api call that it was blocked for lack of
+ // permission.
+ void BlockExtensionRequest(int request_id);
+
+ // Tells the renderer which browser window it is being attached to.
+ void UpdateBrowserWindowId(int window_id);
+
+ // Tells the render view that a custom context action has been selected.
+ void PerformCustomContextMenuAction(
+ const webkit_glue::CustomContextMenuContext& custom_context,
+ unsigned action);
+
+ // Informs renderer of updated content settings.
+ void SendContentSettings(const GURL& url,
+ const ContentSettings& settings);
+
+ // Tells the renderer to notify us when the page contents preferred size
+ // changed. |flags| is a combination of
+ // |ViewHostMsg_EnablePreferredSizeChangedMode_Flags| values, which is defined
+ // in render_messages.h.
+ void EnablePreferredSizeChangedMode(int flags);
+
+#if defined(OS_MACOSX)
+ // Select popup menu related methods (for external popup menus).
+ void DidSelectPopupMenuItem(int selected_index);
+ void DidCancelPopupMenu();
+#endif
+
+ // SearchBox notifications.
+ void SearchBoxChange(const string16& value,
+ bool verbatim,
+ int selection_start,
+ int selection_end);
+ void SearchBoxSubmit(const string16& value,
+ bool verbatim);
+ void SearchBoxCancel();
+ void SearchBoxResize(const gfx::Rect& search_box_bounds);
+ void DetermineIfPageSupportsInstant(const string16& value,
+ bool verbatim,
+ int selection_start,
+ int selection_end);
+
+ // Send a notification to the V8 JavaScript engine to change its parameters
+ // while performing stress testing. |cmd| is one of the values defined by
+ // |ViewHostMsg_JavaScriptStressTestControl_Commands|, which is defined
+ // in render_messages.h.
+ void JavaScriptStressTestControl(int cmd, int param);
+
+#if defined(UNIT_TEST)
+ // These functions shouldn't be necessary outside of testing.
+
+ void set_save_accessibility_tree_for_testing(bool save) {
+ save_accessibility_tree_for_testing_ = save;
+ }
+
+ const webkit_glue::WebAccessibility& accessibility_tree() {
+ return accessibility_tree_;
+ }
+
+ bool is_waiting_for_unload_ack() { return is_waiting_for_unload_ack_; }
+#endif
+
+ // Checks that the given renderer can request |url|, if not it sets it to an
+ // empty url.
+ static void FilterURL(ChildProcessSecurityPolicy* policy,
+ int renderer_id,
+ GURL* url);
+
+ protected:
+ // RenderWidgetHost protected overrides.
+ virtual bool PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event,
+ bool* is_keyboard_shortcut);
+ virtual void UnhandledKeyboardEvent(const NativeWebKeyboardEvent& event);
+ virtual void OnUserGesture();
+ virtual void NotifyRendererUnresponsive();
+ virtual void NotifyRendererResponsive();
+ virtual void OnMsgFocusedNodeChanged(bool is_editable_node);
+ virtual void OnMsgFocus();
+ virtual void OnMsgBlur();
+
+ // IPC message handlers.
+ void OnMsgShowView(int route_id,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture);
+ void OnMsgShowWidget(int route_id, const gfx::Rect& initial_pos);
+ void OnMsgShowFullscreenWidget(int route_id);
+ void OnMsgRunModal(IPC::Message* reply_msg);
+ void OnMsgRenderViewReady();
+ void OnMsgRenderViewGone(int status, int error_code);
+ void OnMsgNavigate(const IPC::Message& msg);
+ void OnMsgUpdateState(int32 page_id,
+ const std::string& state);
+ void OnMsgUpdateTitle(int32 page_id, const std::wstring& title);
+ void OnMsgUpdateEncoding(const std::string& encoding);
+ void OnMsgUpdateTargetURL(int32 page_id, const GURL& url);
+ void OnMsgThumbnail(const GURL& url,
+ const ThumbnailScore& score,
+ const SkBitmap& bitmap);
+ void OnMsgScreenshot(const SkBitmap& bitmap);
+ void OnMsgClose();
+ void OnMsgRequestMove(const gfx::Rect& pos);
+ void OnMsgDidStartLoading();
+ void OnMsgDidStopLoading();
+ void OnMsgDidChangeLoadProgress(double load_progress);
+ void OnMsgDocumentAvailableInMainFrame();
+ void OnMsgDocumentOnLoadCompletedInMainFrame(int32 page_id);
+ void OnExecuteCodeFinished(int request_id, bool success);
+ void OnMsgUpdateFavIconURL(int32 page_id, const GURL& icon_url);
+ void OnMsgDidDownloadFavIcon(int id,
+ const GURL& image_url,
+ bool errored,
+ const SkBitmap& image_data);
+ void OnMsgContextMenu(const ContextMenuParams& params);
+ void OnMsgOpenURL(const GURL& url, const GURL& referrer,
+ WindowOpenDisposition disposition);
+ void OnMsgDidContentsPreferredSizeChange(const gfx::Size& new_size);
+ void OnMsgDomOperationResponse(const std::string& json_string,
+ int automation_id);
+ void OnMsgWebUISend(const GURL& source_url,
+ const std::string& message,
+ const std::string& content);
+ void OnMsgForwardMessageToExternalHost(const std::string& message,
+ const std::string& origin,
+ const std::string& target);
+ void OnMsgSetTooltipText(const std::wstring& tooltip_text,
+ WebKit::WebTextDirection text_direction_hint);
+ void OnMsgSelectionChanged(const std::string& text);
+ void OnMsgPasteFromSelectionClipboard();
+ void OnMsgRunJavaScriptMessage(const std::wstring& message,
+ const std::wstring& default_prompt,
+ const GURL& frame_url,
+ const int flags,
+ IPC::Message* reply_msg);
+ void OnMsgRunBeforeUnloadConfirm(const GURL& frame_url,
+ const std::wstring& message,
+ IPC::Message* reply_msg);
+ void OnMsgShowModalHTMLDialog(const GURL& url, int width, int height,
+ const std::string& json_arguments,
+ IPC::Message* reply_msg);
+ void OnMsgStartDragging(const WebDropData& drop_data,
+ WebKit::WebDragOperationsMask operations_allowed,
+ const SkBitmap& image,
+ const gfx::Point& image_offset);
+ void OnUpdateDragCursor(WebKit::WebDragOperation drag_operation);
+ void OnTakeFocus(bool reverse);
+ void OnAddMessageToConsole(const std::wstring& message,
+ int32 line_no,
+ const std::wstring& source_id);
+ void OnUpdateInspectorSetting(const std::string& key,
+ const std::string& value);
+ void OnForwardToDevToolsAgent(const IPC::Message& message);
+ void OnForwardToDevToolsClient(const IPC::Message& message);
+ void OnActivateDevToolsWindow();
+ void OnCloseDevToolsWindow();
+ void OnRequestDockDevToolsWindow();
+ void OnRequestUndockDevToolsWindow();
+ void OnDevToolsRuntimePropertyChanged(const std::string& name,
+ const std::string& value);
+ void OnMsgShouldCloseACK(bool proceed);
+
+ void OnExtensionRequest(const ViewHostMsg_DomMessage_Params& params);
+ void OnExtensionPostMessage(int port_id, const std::string& message);
+ void OnAccessibilityNotifications(
+ const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params);
+ void OnCSSInserted();
+ void OnContentBlocked(ContentSettingsType type,
+ const std::string& resource_identifier);
+ void OnAppCacheAccessed(const GURL& manifest_url, bool blocked_by_policy);
+ void OnWebDatabaseAccessed(const GURL& url,
+ const string16& name,
+ const string16& display_name,
+ unsigned long estimated_size,
+ bool blocked_by_policy);
+ void OnUpdateZoomLimits(int minimum_percent,
+ int maximum_percent,
+ bool remember);
+ void OnScriptEvalResponse(int id, const ListValue& result);
+ void OnCommandStateChanged(int command,
+ bool is_enabled,
+ int checked_state);
+
+#if defined(OS_MACOSX)
+ void OnMsgShowPopup(const ViewHostMsg_ShowPopup_Params& params);
+#endif
+
+ private:
+ friend class TestRenderViewHost;
+
+ // The SiteInstance associated with this RenderViewHost. All pages drawn
+ // in this RenderViewHost are part of this SiteInstance. Should not change
+ // over time.
+ scoped_refptr<SiteInstance> instance_;
+
+ // Our delegate, which wants to know about changes in the RenderView.
+ RenderViewHostDelegate* delegate_;
+
+ // true if we are currently waiting for a response for drag context
+ // information.
+ bool waiting_for_drag_context_response_;
+
+ // A bitwise OR of bindings types that have been enabled for this RenderView.
+ // See BindingsPolicy for details.
+ int enabled_bindings_;
+
+ // The request_id for the pending cross-site request. Set to -1 if
+ // there is a pending request, but we have not yet started the unload
+ // for the current page. Set to the request_id value of the pending
+ // request once we have gotten the some data for the pending page
+ // and thus started the unload process.
+ int pending_request_id_;
+
+ // Whether we should buffer outgoing Navigate messages rather than sending
+ // them. This will be true when a RenderViewHost is created for a cross-site
+ // request, until we hear back from the onbeforeunload handler of the old
+ // RenderViewHost.
+ bool navigations_suspended_;
+
+ // We only buffer a suspended navigation message while we a pending RVH for a
+ // TabContents. There will only ever be one suspended navigation, because
+ // TabContents will destroy the pending RVH and create a new one if a second
+ // navigation occurs.
+ scoped_ptr<ViewMsg_Navigate> suspended_nav_message_;
+
+ // If we were asked to RunModal, then this will hold the reply_msg that we
+ // must return to the renderer to unblock it.
+ IPC::Message* run_modal_reply_msg_;
+
+ // Set to true when there is a pending ViewMsg_ShouldClose message. This
+ // ensures we don't spam the renderer with multiple beforeunload requests.
+ // When either this value or is_waiting_for_unload_ack_ is true, the value of
+ // unload_ack_is_for_cross_site_transition_ indicates whether this is for a
+ // cross-site transition or a tab close attempt.
+ bool is_waiting_for_beforeunload_ack_;
+
+ // Set to true when there is a pending ViewMsg_Close message. Also see
+ // is_waiting_for_beforeunload_ack_, unload_ack_is_for_cross_site_transition_.
+ bool is_waiting_for_unload_ack_;
+
+ // Valid only when is_waiting_for_beforeunload_ack_ or
+ // is_waiting_for_unload_ack_ is true. This tells us if the unload request
+ // is for closing the entire tab ( = false), or only this RenderViewHost in
+ // the case of a cross-site transition ( = true).
+ bool unload_ack_is_for_cross_site_transition_;
+
+ bool are_javascript_messages_suppressed_;
+
+ // True if the render view can be shut down suddenly.
+ bool sudden_termination_allowed_;
+
+ // The session storage namespace to be used by the associated render view.
+ scoped_refptr<SessionStorageNamespace> session_storage_namespace_;
+
+ // Whether this render view will get extension api bindings. This controls
+ // what process type we use.
+ bool is_extension_process_;
+
+ // Whether the accessibility tree should be saved, for unit testing.
+ bool save_accessibility_tree_for_testing_;
+
+ // The most recently received accessibility tree - for unit testing only.
+ webkit_glue::WebAccessibility accessibility_tree_;
+
+ // The termination status of the last render view that terminated.
+ base::TerminationStatus render_view_termination_status_;
+
+ // The enabled/disabled states of various commands.
+ std::map<RenderViewCommand, CommandState> command_states_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderViewHost);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_RENDER_VIEW_HOST_H_
diff --git a/content/browser/renderer_host/render_view_host_delegate.cc b/content/browser/renderer_host/render_view_host_delegate.cc
new file mode 100644
index 0000000..f169205
--- /dev/null
+++ b/content/browser/renderer_host/render_view_host_delegate.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/render_view_host_delegate.h"
+
+#include "base/singleton.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/render_messages_params.h"
+#include "chrome/common/renderer_preferences.h"
+#include "googleurl/src/gurl.h"
+#include "ui/gfx/rect.h"
+#include "webkit/glue/webpreferences.h"
+
+#if defined(TOOLKIT_USES_GTK)
+#include "chrome/browser/ui/gtk/gtk_util.h"
+#endif
+
+RenderViewHostDelegate::View* RenderViewHostDelegate::GetViewDelegate() {
+ return NULL;
+}
+
+RenderViewHostDelegate::RendererManagement*
+RenderViewHostDelegate::GetRendererManagementDelegate() {
+ return NULL;
+}
+
+RenderViewHostDelegate::ContentSettings*
+RenderViewHostDelegate::GetContentSettingsDelegate() {
+ return NULL;
+}
+
+RenderViewHostDelegate::BookmarkDrag*
+RenderViewHostDelegate::GetBookmarkDragDelegate() {
+ return NULL;
+}
+
+RenderViewHostDelegate::SSL*
+RenderViewHostDelegate::GetSSLDelegate() {
+ return NULL;
+}
+
+AutomationResourceRoutingDelegate*
+RenderViewHostDelegate::GetAutomationResourceRoutingDelegate() {
+ return NULL;
+}
+
+bool RenderViewHostDelegate::OnMessageReceived(const IPC::Message& message) {
+ return false;
+}
+
+const GURL& RenderViewHostDelegate::GetURL() const {
+ return GURL::EmptyGURL();
+}
+
+TabContents* RenderViewHostDelegate::GetAsTabContents() {
+ return NULL;
+}
+
+BackgroundContents* RenderViewHostDelegate::GetAsBackgroundContents() {
+ return NULL;
+}
+
+GURL RenderViewHostDelegate::GetAlternateErrorPageURL() const {
+ return GURL();
+}
+
+WebPreferences RenderViewHostDelegate::GetWebkitPrefs() {
+ return WebPreferences();
+}
+
+bool RenderViewHostDelegate::IsExternalTabContainer() const {
+ return false;
+}
diff --git a/content/browser/renderer_host/render_view_host_delegate.h b/content/browser/renderer_host/render_view_host_delegate.h
new file mode 100644
index 0000000..e4c5f46
--- /dev/null
+++ b/content/browser/renderer_host/render_view_host_delegate.h
@@ -0,0 +1,584 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_RENDER_VIEW_HOST_DELEGATE_H_
+#define CONTENT_BROWSER_RENDERER_HOST_RENDER_VIEW_HOST_DELEGATE_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/process_util.h"
+#include "base/ref_counted.h"
+#include "base/string16.h"
+#include "chrome/common/content_settings_types.h"
+#include "chrome/common/dom_storage_common.h"
+#include "chrome/common/translate_errors.h"
+#include "chrome/common/view_types.h"
+#include "chrome/common/window_container_type.h"
+#include "ipc/ipc_channel.h"
+#include "net/base/load_states.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebDragOperation.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebPopupType.h"
+#include "webkit/glue/window_open_disposition.h"
+
+
+class AutomationResourceRoutingDelegate;
+class BackgroundContents;
+struct BookmarkNodeData;
+class BookmarkNode;
+struct ContextMenuParams;
+class FilePath;
+class GURL;
+class ListValue;
+struct NativeWebKeyboardEvent;
+class NavigationEntry;
+class Profile;
+struct RendererPreferences;
+class RenderProcessHost;
+class RenderViewHost;
+class ResourceRedirectDetails;
+class ResourceRequestDetails;
+class SkBitmap;
+class SSLClientAuthHandler;
+class SSLAddCertHandler;
+class TabContents;
+struct ThumbnailScore;
+struct ViewHostMsg_CreateWindow_Params;
+struct ViewHostMsg_DomMessage_Params;
+struct ViewHostMsg_FrameNavigate_Params;
+struct WebApplicationInfo;
+struct WebDropData;
+struct WebMenuItem;
+class WebKeyboardEvent;
+struct WebPreferences;
+
+namespace base {
+class WaitableEvent;
+}
+
+namespace gfx {
+class Point;
+class Rect;
+class Size;
+}
+
+namespace IPC {
+class Message;
+}
+
+namespace net {
+class CookieList;
+class CookieOptions;
+}
+
+namespace webkit_glue {
+struct FormData;
+class FormField;
+struct PasswordForm;
+}
+
+//
+// RenderViewHostDelegate
+//
+// An interface implemented by an object interested in knowing about the state
+// of the RenderViewHost.
+//
+// This interface currently encompasses every type of message that was
+// previously being sent by TabContents itself. Some of these notifications
+// may not be relevant to all users of RenderViewHost and we should consider
+// exposing a more generic Send function on RenderViewHost and a response
+// listener here to serve that need.
+//
+class RenderViewHostDelegate : public IPC::Channel::Listener {
+ public:
+ // View ----------------------------------------------------------------------
+ // Functions that can be routed directly to a view-specific class.
+
+ class View {
+ public:
+ // The page is trying to open a new page (e.g. a popup window). The window
+ // should be created associated with the given route, but it should not be
+ // shown yet. That should happen in response to ShowCreatedWindow.
+ // |params.window_container_type| describes the type of RenderViewHost
+ // container that is requested -- in particular, the window.open call may
+ // have specified 'background' and 'persistent' in the feature string.
+ //
+ // The passed |params.frame_name| parameter is the name parameter that was
+ // passed to window.open(), and will be empty if none was passed.
+ //
+ // Note: this is not called "CreateWindow" because that will clash with
+ // the Windows function which is actually a #define.
+ //
+ // NOTE: this takes ownership of @modal_dialog_event
+ virtual void CreateNewWindow(
+ int route_id,
+ const ViewHostMsg_CreateWindow_Params& params) = 0;
+
+ // The page is trying to open a new widget (e.g. a select popup). The
+ // widget should be created associated with the given route, but it should
+ // not be shown yet. That should happen in response to ShowCreatedWidget.
+ // |popup_type| indicates if the widget is a popup and what kind of popup it
+ // is (select, autofill...).
+ virtual void CreateNewWidget(int route_id,
+ WebKit::WebPopupType popup_type) = 0;
+
+ // Creates a full screen RenderWidget. Similar to above.
+ virtual void CreateNewFullscreenWidget(int route_id) = 0;
+
+ // Show a previously created page with the specified disposition and bounds.
+ // The window is identified by the route_id passed to CreateNewWindow.
+ //
+ // Note: this is not called "ShowWindow" because that will clash with
+ // the Windows function which is actually a #define.
+ virtual void ShowCreatedWindow(int route_id,
+ WindowOpenDisposition disposition,
+ const gfx::Rect& initial_pos,
+ bool user_gesture) = 0;
+
+ // Show the newly created widget with the specified bounds.
+ // The widget is identified by the route_id passed to CreateNewWidget.
+ virtual void ShowCreatedWidget(int route_id,
+ const gfx::Rect& initial_pos) = 0;
+
+ // Show the newly created full screen widget. Similar to above.
+ virtual void ShowCreatedFullscreenWidget(int route_id) = 0;
+
+ // A context menu should be shown, to be built using the context information
+ // provided in the supplied params.
+ virtual void ShowContextMenu(const ContextMenuParams& params) = 0;
+
+ // Shows a popup menu with the specified items.
+ // This method should call RenderViewHost::DidSelectPopupMenuItemAt() or
+ // RenderViewHost::DidCancelPopupMenu() ased on the user action.
+ virtual void ShowPopupMenu(const gfx::Rect& bounds,
+ int item_height,
+ double item_font_size,
+ int selected_item,
+ const std::vector<WebMenuItem>& items,
+ bool right_aligned) = 0;
+
+ // The user started dragging content of the specified type within the
+ // RenderView. Contextual information about the dragged content is supplied
+ // by WebDropData.
+ virtual void StartDragging(const WebDropData& drop_data,
+ WebKit::WebDragOperationsMask allowed_ops,
+ const SkBitmap& image,
+ const gfx::Point& image_offset) = 0;
+
+ // The page wants to update the mouse cursor during a drag & drop operation.
+ // |operation| describes the current operation (none, move, copy, link.)
+ virtual void UpdateDragCursor(WebKit::WebDragOperation operation) = 0;
+
+ // Notification that view for this delegate got the focus.
+ virtual void GotFocus() = 0;
+
+ // Callback to inform the browser that the page is returning the focus to
+ // the browser's chrome. If reverse is true, it means the focus was
+ // retrieved by doing a Shift-Tab.
+ virtual void TakeFocus(bool reverse) = 0;
+
+ // Notification that the view has lost capture.
+ virtual void LostCapture() = 0;
+
+ // The page wants the hosting window to activate/deactivate itself (it
+ // called the JavaScript window.focus()/blur() method).
+ virtual void Activate() = 0;
+ virtual void Deactivate() = 0;
+
+ // Callback to give the browser a chance to handle the specified keyboard
+ // event before sending it to the renderer.
+ // Returns true if the |event| was handled. Otherwise, if the |event| would
+ // be handled in HandleKeyboardEvent() method as a normal keyboard shortcut,
+ // |*is_keyboard_shortcut| should be set to true.
+ virtual bool PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event,
+ bool* is_keyboard_shortcut) = 0;
+
+ // Callback to inform the browser that the renderer did not process the
+ // specified events. This gives an opportunity to the browser to process the
+ // event (used for keyboard shortcuts).
+ virtual void HandleKeyboardEvent(const NativeWebKeyboardEvent& event) = 0;
+
+ // Notifications about mouse events in this view. This is useful for
+ // implementing global 'on hover' features external to the view.
+ virtual void HandleMouseMove() = 0;
+ virtual void HandleMouseDown() = 0;
+ virtual void HandleMouseLeave() = 0;
+ virtual void HandleMouseUp() = 0;
+ virtual void HandleMouseActivate() = 0;
+
+ // The contents' preferred size changed.
+ virtual void UpdatePreferredSize(const gfx::Size& pref_size) = 0;
+
+ protected:
+ virtual ~View() {}
+ };
+
+ // RendererManagerment -------------------------------------------------------
+ // Functions for managing switching of Renderers. For TabContents, this is
+ // implemented by the RenderViewHostManager
+
+ class RendererManagement {
+ public:
+ // Notification whether we should close the page, after an explicit call to
+ // AttemptToClosePage. This is called before a cross-site request or before
+ // a tab/window is closed (as indicated by the first parameter) to allow the
+ // appropriate renderer to approve or deny the request. |proceed| indicates
+ // whether the user chose to proceed.
+ virtual void ShouldClosePage(bool for_cross_site_transition,
+ bool proceed) = 0;
+
+ // Called by ResourceDispatcherHost when a response for a pending cross-site
+ // request is received. The ResourceDispatcherHost will pause the response
+ // until the onunload handler of the previous renderer is run.
+ virtual void OnCrossSiteResponse(int new_render_process_host_id,
+ int new_request_id) = 0;
+
+ // Called the ResourceDispatcherHost's associate CrossSiteRequestHandler
+ // when a cross-site navigation has been canceled.
+ virtual void OnCrossSiteNavigationCanceled() = 0;
+
+ protected:
+ virtual ~RendererManagement() {}
+ };
+
+ // ContentSettings------------------------------------------------------------
+ // Interface for content settings related events.
+
+ class ContentSettings {
+ public:
+ // Called when content in the current page was blocked due to the user's
+ // content settings.
+ virtual void OnContentBlocked(ContentSettingsType type,
+ const std::string& resource_identifier) = 0;
+
+ // Called when cookies for the given URL were read either from within the
+ // current page or while loading it. |blocked_by_policy| should be true, if
+ // reading cookies was blocked due to the user's content settings. In that
+ // case, this function should invoke OnContentBlocked.
+ virtual void OnCookiesRead(
+ const GURL& url,
+ const net::CookieList& cookie_list,
+ bool blocked_by_policy) = 0;
+
+ // Called when a specific cookie in the current page was changed.
+ // |blocked_by_policy| should be true, if the cookie was blocked due to the
+ // user's content settings. In that case, this function should invoke
+ // OnContentBlocked.
+ virtual void OnCookieChanged(const GURL& url,
+ const std::string& cookie_line,
+ const net::CookieOptions& options,
+ bool blocked_by_policy) = 0;
+
+ // Called when a specific indexed db factory in the current page was
+ // accessed. If access was blocked due to the user's content settings,
+ // |blocked_by_policy| should be true, and this function should invoke
+ // OnContentBlocked.
+ virtual void OnIndexedDBAccessed(const GURL& url,
+ const string16& description,
+ bool blocked_by_policy) = 0;
+
+ // Called when a specific local storage area in the current page was
+ // accessed. If access was blocked due to the user's content settings,
+ // |blocked_by_policy| should be true, and this function should invoke
+ // OnContentBlocked.
+ virtual void OnLocalStorageAccessed(const GURL& url,
+ DOMStorageType storage_type,
+ bool blocked_by_policy) = 0;
+
+ // Called when a specific Web database in the current page was accessed. If
+ // access was blocked due to the user's content settings,
+ // |blocked_by_policy| should eb true, and this function should invoke
+ // OnContentBlocked.
+ virtual void OnWebDatabaseAccessed(const GURL& url,
+ const string16& name,
+ const string16& display_name,
+ unsigned long estimated_size,
+ bool blocked_by_policy) = 0;
+
+ // Called when a specific appcache in the current page was accessed. If
+ // access was blocked due to the user's content settings,
+ // |blocked_by_policy| should eb true, and this function should invoke
+ // OnContentBlocked.
+ virtual void OnAppCacheAccessed(const GURL& manifest_url,
+ bool blocked_by_policy) = 0;
+
+ // Called when geolocation permission was set in a frame on the current
+ // page.
+ virtual void OnGeolocationPermissionSet(const GURL& requesting_frame,
+ bool allowed) = 0;
+
+ protected:
+ virtual ~ContentSettings() {}
+ };
+
+ // BookmarkDrag --------------------------------------------------------------
+ // Interface for forwarding bookmark drag and drop to extenstions.
+
+ class BookmarkDrag {
+ public:
+ virtual void OnDragEnter(const BookmarkNodeData& data) = 0;
+ virtual void OnDragOver(const BookmarkNodeData& data) = 0;
+ virtual void OnDragLeave(const BookmarkNodeData& data) = 0;
+ virtual void OnDrop(const BookmarkNodeData& data) = 0;
+
+ protected:
+ virtual ~BookmarkDrag() {}
+ };
+
+ // SSL -----------------------------------------------------------------------
+ // Interface for UI and other RenderViewHost-specific interactions with SSL.
+
+ class SSL {
+ public:
+ // Displays a dialog to select client certificates from |request_info|,
+ // returning them to |handler|.
+ virtual void ShowClientCertificateRequestDialog(
+ scoped_refptr<SSLClientAuthHandler> handler) = 0;
+
+ // Called when |handler| encounters an error in verifying a
+ // received client certificate. Note that, because CAs often will
+ // not send us intermediate certificates, the verification we can
+ // do is minimal: we verify the certificate is parseable, that we
+ // have the corresponding private key, and that the certificate
+ // has not expired.
+ virtual void OnVerifyClientCertificateError(
+ scoped_refptr<SSLAddCertHandler> handler, int error_code) = 0;
+
+ // Called when |handler| requests the user's confirmation in adding a
+ // client certificate.
+ virtual void AskToAddClientCertificate(
+ scoped_refptr<SSLAddCertHandler> handler) = 0;
+
+ // Called when |handler| successfully adds a client certificate.
+ virtual void OnAddClientCertificateSuccess(
+ scoped_refptr<SSLAddCertHandler> handler) = 0;
+
+ // Called when |handler| encounters an error adding a client certificate.
+ virtual void OnAddClientCertificateError(
+ scoped_refptr<SSLAddCertHandler> handler, int error_code) = 0;
+
+ // Called when |handler| has completed, so the delegate may release any
+ // state accumulated.
+ virtual void OnAddClientCertificateFinished(
+ scoped_refptr<SSLAddCertHandler> handler) = 0;
+
+ protected:
+ virtual ~SSL() {}
+ };
+
+ // ---------------------------------------------------------------------------
+
+ // Returns the current delegate associated with a feature. May return NULL if
+ // there is no corresponding delegate.
+ virtual View* GetViewDelegate();
+ virtual RendererManagement* GetRendererManagementDelegate();
+ virtual ContentSettings* GetContentSettingsDelegate();
+
+ virtual BookmarkDrag* GetBookmarkDragDelegate();
+ virtual SSL* GetSSLDelegate();
+
+ // Return the delegate for registering RenderViewHosts for automation resource
+ // routing.
+ virtual AutomationResourceRoutingDelegate*
+ GetAutomationResourceRoutingDelegate();
+
+ // IPC::Channel::Listener implementation.
+ // This is used to give the delegate a chance to filter IPC messages.
+ virtual bool OnMessageReceived(const IPC::Message& message);
+
+ // Gets the URL that is currently being displayed, if there is one.
+ virtual const GURL& GetURL() const;
+
+ // Return this object cast to a TabContents, if it is one. If the object is
+ // not a TabContents, returns NULL. DEPRECATED: Be sure to include brettw and
+ // jam as reviewers before you use this method.
+ virtual TabContents* GetAsTabContents();
+
+ // Return this object cast to a BackgroundContents, if it is one. If the
+ // object is not a BackgroundContents, returns NULL.
+ virtual BackgroundContents* GetAsBackgroundContents();
+
+ // Return id number of browser window which this object is attached to. If no
+ // browser window is attached to, just return -1.
+ virtual int GetBrowserWindowID() const = 0;
+
+ // Return type of RenderView which is attached with this object.
+ virtual ViewType::Type GetRenderViewType() const = 0;
+
+ // The RenderView is being constructed (message sent to the renderer process
+ // to construct a RenderView). Now is a good time to send other setup events
+ // to the RenderView. This precedes any other commands to the RenderView.
+ virtual void RenderViewCreated(RenderViewHost* render_view_host) {}
+
+ // The RenderView has been constructed.
+ virtual void RenderViewReady(RenderViewHost* render_view_host) {}
+
+ // The RenderView died somehow (crashed or was killed by the user).
+ virtual void RenderViewGone(RenderViewHost* render_view_host,
+ base::TerminationStatus status,
+ int error_code) {}
+
+ // The RenderView is going to be deleted. This is called when each
+ // RenderView is going to be destroyed
+ virtual void RenderViewDeleted(RenderViewHost* render_view_host) {}
+
+ // The RenderView was navigated to a different page.
+ virtual void DidNavigate(RenderViewHost* render_view_host,
+ const ViewHostMsg_FrameNavigate_Params& params) {}
+
+ // The state for the page changed and should be updated.
+ virtual void UpdateState(RenderViewHost* render_view_host,
+ int32 page_id,
+ const std::string& state) {}
+
+ // The page's title was changed and should be updated.
+ virtual void UpdateTitle(RenderViewHost* render_view_host,
+ int32 page_id,
+ const std::wstring& title) {}
+
+ // The page's encoding was changed and should be updated.
+ virtual void UpdateEncoding(RenderViewHost* render_view_host,
+ const std::string& encoding) {}
+
+ // The destination URL has changed should be updated
+ virtual void UpdateTargetURL(int32 page_id, const GURL& url) {}
+
+ // The thumbnail representation of the page changed and should be updated.
+ virtual void UpdateThumbnail(const GURL& url,
+ const SkBitmap& bitmap,
+ const ThumbnailScore& score) {}
+
+ // Inspector setting was changed and should be persisted.
+ virtual void UpdateInspectorSetting(const std::string& key,
+ const std::string& value) = 0;
+
+ virtual void ClearInspectorSettings() = 0;
+
+ // The page is trying to close the RenderView's representation in the client.
+ virtual void Close(RenderViewHost* render_view_host) {}
+
+ // The page is trying to move the RenderView's representation in the client.
+ virtual void RequestMove(const gfx::Rect& new_bounds) {}
+
+ // The RenderView began loading a new page. This corresponds to WebKit's
+ // notion of the throbber starting.
+ virtual void DidStartLoading() {}
+
+ // The RenderView stopped loading a page. This corresponds to WebKit's
+ // notion of the throbber stopping.
+ virtual void DidStopLoading() {}
+
+ // The RenderView made progress loading a page's top frame.
+ // |progress| is a value between 0 (nothing loaded) to 1.0 (top frame
+ // entirely loaded).
+ virtual void DidChangeLoadProgress(double progress) {}
+
+ // The RenderView's main frame document element is ready. This happens when
+ // the document has finished parsing.
+ virtual void DocumentAvailableInMainFrame(RenderViewHost* render_view_host) {}
+
+ // The onload handler in the RenderView's main frame has completed.
+ virtual void DocumentOnLoadCompletedInMainFrame(
+ RenderViewHost* render_view_host,
+ int32 page_id) {}
+
+ // The page wants to open a URL with the specified disposition.
+ virtual void RequestOpenURL(const GURL& url,
+ const GURL& referrer,
+ WindowOpenDisposition disposition) {}
+
+ // A DOM automation operation completed. The result of the operation is
+ // expressed in a json string.
+ virtual void DomOperationResponse(const std::string& json_string,
+ int automation_id) {}
+
+ // A message was sent from HTML-based UI.
+ // By default we ignore such messages.
+ virtual void ProcessWebUIMessage(
+ const ViewHostMsg_DomMessage_Params& params) {}
+
+ // A message for external host. By default we ignore such messages.
+ // |receiver| can be a receiving script and |message| is any
+ // arbitrary string that makes sense to the receiver.
+ virtual void ProcessExternalHostMessage(const std::string& message,
+ const std::string& origin,
+ const std::string& target) {}
+
+ // A javascript message, confirmation or prompt should be shown.
+ virtual void RunJavaScriptMessage(const std::wstring& message,
+ const std::wstring& default_prompt,
+ const GURL& frame_url,
+ const int flags,
+ IPC::Message* reply_msg,
+ bool* did_suppress_message) {}
+
+ virtual void RunBeforeUnloadConfirm(const std::wstring& message,
+ IPC::Message* reply_msg) {}
+
+ virtual void ShowModalHTMLDialog(const GURL& url, int width, int height,
+ const std::string& json_arguments,
+ IPC::Message* reply_msg) {}
+
+ // |url| is assigned to a server that can provide alternate error pages. If
+ // the returned URL is empty, the default error page built into WebKit will
+ // be used.
+ virtual GURL GetAlternateErrorPageURL() const;
+
+ // Return a dummy RendererPreferences object that will be used by the renderer
+ // associated with the owning RenderViewHost.
+ virtual RendererPreferences GetRendererPrefs(Profile* profile) const = 0;
+
+ // Returns a WebPreferences object that will be used by the renderer
+ // associated with the owning render view host.
+ virtual WebPreferences GetWebkitPrefs();
+
+ // Notification the user has made a gesture while focus was on the
+ // page. This is used to avoid uninitiated user downloads (aka carpet
+ // bombing), see DownloadRequestLimiter for details.
+ virtual void OnUserGesture() {}
+
+ // Notification from the renderer host that blocked UI event occurred.
+ // This happens when there are tab-modal dialogs. In this case, the
+ // notification is needed to let us draw attention to the dialog (i.e.
+ // refocus on the modal dialog, flash title etc).
+ virtual void OnIgnoredUIEvent() {}
+
+ // Notification that the renderer has become unresponsive. The
+ // delegate can use this notification to show a warning to the user.
+ virtual void RendererUnresponsive(RenderViewHost* render_view_host,
+ bool is_during_unload) {}
+
+ // Notification that a previously unresponsive renderer has become
+ // responsive again. The delegate can use this notification to end the
+ // warning shown to the user.
+ virtual void RendererResponsive(RenderViewHost* render_view_host) {}
+
+ // Notification that the RenderViewHost's load state changed.
+ virtual void LoadStateChanged(const GURL& url, net::LoadState load_state,
+ uint64 upload_position, uint64 upload_size) {}
+
+ // Returns true if this view is used to host an external tab container.
+ virtual bool IsExternalTabContainer() const;
+
+ // The RenderView has inserted one css file into page.
+ virtual void DidInsertCSS() {}
+
+ // A different node in the page got focused.
+ virtual void FocusedNodeChanged(bool is_editable_node) {}
+
+ // Updates the minimum and maximum zoom percentages.
+ virtual void UpdateZoomLimits(int minimum_percent,
+ int maximum_percent,
+ bool remember) {}
+
+ // Notification that a worker process has crashed.
+ void WorkerCrashed() {}
+
+ protected:
+ virtual ~RenderViewHostDelegate() {}
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_RENDER_VIEW_HOST_DELEGATE_H_
diff --git a/content/browser/renderer_host/render_view_host_factory.cc b/content/browser/renderer_host/render_view_host_factory.cc
new file mode 100644
index 0000000..6fc01cf
--- /dev/null
+++ b/content/browser/renderer_host/render_view_host_factory.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/render_view_host_factory.h"
+
+#include "base/logging.h"
+#include "content/browser/renderer_host/render_view_host.h"
+
+// static
+RenderViewHostFactory* RenderViewHostFactory::factory_ = NULL;
+
+// static
+RenderViewHost* RenderViewHostFactory::Create(
+ SiteInstance* instance,
+ RenderViewHostDelegate* delegate,
+ int routing_id,
+ SessionStorageNamespace* session_storage_namespace) {
+ if (factory_) {
+ return factory_->CreateRenderViewHost(instance, delegate, routing_id,
+ session_storage_namespace);
+ }
+ return new RenderViewHost(instance, delegate, routing_id,
+ session_storage_namespace);
+}
+
+// static
+void RenderViewHostFactory::RegisterFactory(RenderViewHostFactory* factory) {
+ DCHECK(!factory_) << "Can't register two factories at once.";
+ factory_ = factory;
+}
+
+// static
+void RenderViewHostFactory::UnregisterFactory() {
+ DCHECK(factory_) << "No factory to unregister.";
+ factory_ = NULL;
+}
diff --git a/content/browser/renderer_host/render_view_host_factory.h b/content/browser/renderer_host/render_view_host_factory.h
new file mode 100644
index 0000000..5d0f58d
--- /dev/null
+++ b/content/browser/renderer_host/render_view_host_factory.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_RENDER_VIEW_HOST_FACTORY_H_
+#define CONTENT_BROWSER_RENDERER_HOST_RENDER_VIEW_HOST_FACTORY_H_
+#pragma once
+
+#include "base/basictypes.h"
+
+class RenderViewHost;
+class RenderViewHostDelegate;
+class SessionStorageNamespace;
+class SiteInstance;
+
+namespace base {
+class WaitableEvent;
+} // namespace base
+
+// A factory for creating RenderViewHosts. There is a global factory function
+// that can be installed for the purposes of testing to provide a specialized
+// RenderViewHost class.
+class RenderViewHostFactory {
+ public:
+ // Creates a RenderViewHost using the currently registered factory, or the
+ // default one if no factory is registered. Ownership of the returned
+ // pointer will be passed to the caller.
+ static RenderViewHost* Create(SiteInstance* instance,
+ RenderViewHostDelegate* delegate,
+ int routing_id,
+ SessionStorageNamespace* session_storage);
+
+ // Returns true if there is currently a globally-registered factory.
+ static bool has_factory() {
+ return !!factory_;
+ }
+
+ protected:
+ RenderViewHostFactory() {}
+ virtual ~RenderViewHostFactory() {}
+
+ // You can derive from this class and specify an implementation for this
+ // function to create a different kind of RenderViewHost for testing.
+ virtual RenderViewHost* CreateRenderViewHost(
+ SiteInstance* instance,
+ RenderViewHostDelegate* delegate,
+ int routing_id,
+ SessionStorageNamespace* session_storage_namespace) = 0;
+
+ // Registers your factory to be called when new RenderViewHosts are created.
+ // We have only one global factory, so there must be no factory registered
+ // before the call. This class does NOT take ownership of the pointer.
+ static void RegisterFactory(RenderViewHostFactory* factory);
+
+ // Unregister the previously registered factory. With no factory registered,
+ // the default RenderViewHosts will be created.
+ static void UnregisterFactory();
+
+ private:
+ // The current globally registered factory. This is NULL when we should
+ // create the default RenderViewHosts.
+ static RenderViewHostFactory* factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderViewHostFactory);
+};
+
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_RENDER_VIEW_HOST_FACTORY_H_
diff --git a/content/browser/renderer_host/render_view_host_notification_task.h b/content/browser/renderer_host/render_view_host_notification_task.h
new file mode 100644
index 0000000..f9051c4
--- /dev/null
+++ b/content/browser/renderer_host/render_view_host_notification_task.h
@@ -0,0 +1,337 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file defines utility functions for sending notifications (calling
+// methods that return void and do not have out params) to the RenderViewHost
+// or one of its delegate interfaces. The notifications are dispatched
+// asynchronously, and only if the specified RenderViewHost still exists.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_RENDER_VIEW_HOST_NOTIFICATION_TASK_H_
+#define CONTENT_BROWSER_RENDERER_HOST_RENDER_VIEW_HOST_NOTIFICATION_TASK_H_
+#pragma once
+
+#include "base/callback.h"
+#include "base/task.h"
+#include "chrome/browser/browser_thread.h"
+#include "content/browser/renderer_host/render_view_host.h"
+#include "content/browser/renderer_host/render_view_host_delegate.h"
+
+// ----------------------------------------------------------------------------
+
+namespace internal {
+
+// The simplest Mapper, used when proxying calls to a RenderViewHost.
+class RenderViewHostIdentityMapper {
+ public:
+ typedef RenderViewHost MappedType;
+ static MappedType* Map(RenderViewHost* rvh) { return rvh; }
+};
+
+template <typename Method, typename Params,
+ typename Mapper = RenderViewHostIdentityMapper>
+class RenderViewHostNotificationTask : public Task {
+ public:
+ RenderViewHostNotificationTask(int render_process_id,
+ int render_view_id,
+ Method method,
+ const Params& params)
+ : render_process_id_(render_process_id),
+ render_view_id_(render_view_id),
+ unbound_method_(method, params) {
+ }
+
+ virtual void Run() {
+ RenderViewHost* rvh = RenderViewHost::FromID(render_process_id_,
+ render_view_id_);
+ typename Mapper::MappedType* obj = Mapper::Map(rvh);
+ if (obj)
+ unbound_method_.Run(obj);
+ }
+
+ private:
+ int render_process_id_;
+ int render_view_id_;
+ UnboundMethod<typename Mapper::MappedType, Method, Params> unbound_method_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderViewHostNotificationTask);
+};
+
+// For proxying calls to RenderViewHost
+
+template <typename Method, typename Params>
+inline void CallRenderViewHostHelper(int render_process_id, int render_view_id,
+ Method method, const Params& params) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ new RenderViewHostNotificationTask<Method, Params>(render_process_id,
+ render_view_id,
+ method,
+ params));
+}
+
+// For proxying calls to RenderViewHostDelegate::ContentSettings
+
+class RenderViewHostToContentSettingsDelegate {
+ public:
+ typedef RenderViewHostDelegate::ContentSettings MappedType;
+ static MappedType* Map(RenderViewHost* rvh) {
+ return rvh ? rvh->delegate()->GetContentSettingsDelegate() : NULL;
+ }
+};
+
+template <typename Method, typename Params>
+inline void CallRenderViewHostContentSettingsDelegateHelper(
+ int render_process_id,
+ int render_view_id,
+ Method method,
+ const Params& params) {
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ new RenderViewHostNotificationTask<
+ Method, Params, RenderViewHostToContentSettingsDelegate>(
+ render_process_id,
+ render_view_id,
+ method,
+ params));
+}
+
+// For proxying calls to RenderViewHostDelegate::RendererManagement
+
+class RenderViewHostToRendererManagementDelegate {
+ public:
+ typedef RenderViewHostDelegate::RendererManagement MappedType;
+ static MappedType* Map(RenderViewHost* rvh) {
+ return rvh ? rvh->delegate()->GetRendererManagementDelegate() : NULL;
+ }
+};
+
+template <typename Method, typename Params>
+inline void CallRenderViewHostRendererManagementDelegateHelper(
+ int render_process_id,
+ int render_view_id,
+ Method method,
+ const Params& params) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ new RenderViewHostNotificationTask<
+ Method, Params, RenderViewHostToRendererManagementDelegate>(
+ render_process_id,
+ render_view_id,
+ method,
+ params));
+}
+
+// For proxying calls to RenderViewHostDelegate::SSL
+
+class RenderViewHostToSSLDelegate {
+ public:
+ typedef RenderViewHostDelegate::SSL MappedType;
+ static MappedType* Map(RenderViewHost* rvh) {
+ return rvh ? rvh->delegate()->GetSSLDelegate() : NULL;
+ }
+};
+
+template <typename Method, typename Params>
+inline void CallRenderViewHostSSLDelegateHelper(
+ int render_process_id,
+ int render_view_id,
+ Method method,
+ const Params& params) {
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ new RenderViewHostNotificationTask<
+ Method, Params, RenderViewHostToSSLDelegate>(
+ render_process_id,
+ render_view_id,
+ method,
+ params));
+}
+
+} // namespace internal
+
+// ----------------------------------------------------------------------------
+// Proxy calls to the specified RenderViewHost.
+
+template <typename Method>
+inline void CallRenderViewHost(int render_process_id,
+ int render_view_id,
+ Method method) {
+ internal::CallRenderViewHostHelper(render_process_id,
+ render_view_id,
+ method,
+ MakeTuple());
+}
+
+template <typename Method, typename A>
+inline void CallRenderViewHost(int render_process_id,
+ int render_view_id,
+ Method method,
+ const A& a) {
+ internal::CallRenderViewHostHelper(render_process_id,
+ render_view_id,
+ method,
+ MakeTuple(a));
+}
+
+template <typename Method, typename A, typename B>
+inline void CallRenderViewHost(int render_process_id,
+ int render_view_id,
+ Method method,
+ const A& a,
+ const B& b) {
+ internal::CallRenderViewHostHelper(render_process_id,
+ render_view_id,
+ method,
+ MakeTuple(a, b));
+}
+
+// ----------------------------------------------------------------------------
+// Proxy calls to the specified RenderViewHost's ContentSettings delegate.
+
+template <typename Method>
+inline void CallRenderViewHostContentSettingsDelegate(int render_process_id,
+ int render_view_id,
+ Method method) {
+ internal::CallRenderViewHostContentSettingsDelegateHelper(render_process_id,
+ render_view_id,
+ method,
+ MakeTuple());
+}
+
+template <typename Method, typename A>
+inline void CallRenderViewHostContentSettingsDelegate(int render_process_id,
+ int render_view_id,
+ Method method,
+ const A& a) {
+ internal::CallRenderViewHostContentSettingsDelegateHelper(render_process_id,
+ render_view_id,
+ method,
+ MakeTuple(a));
+ }
+
+template <typename Method, typename A, typename B>
+inline void CallRenderViewHostContentSettingsDelegate(int render_process_id,
+ int render_view_id,
+ Method method,
+ const A& a,
+ const B& b) {
+ internal::CallRenderViewHostContentSettingsDelegateHelper(render_process_id,
+ render_view_id,
+ method,
+ MakeTuple(a, b));
+}
+
+template <typename Method, typename A, typename B, typename C>
+inline void CallRenderViewHostContentSettingsDelegate(int render_process_id,
+ int render_view_id,
+ Method method,
+ const A& a,
+ const B& b,
+ const C& c) {
+ internal::CallRenderViewHostContentSettingsDelegateHelper(render_process_id,
+ render_view_id,
+ method,
+ MakeTuple(a, b, c));
+}
+
+template <typename Method, typename A, typename B, typename C, typename D>
+inline void CallRenderViewHostContentSettingsDelegate(int render_process_id,
+ int render_view_id,
+ Method method,
+ const A& a,
+ const B& b,
+ const C& c,
+ const D& d) {
+ internal::CallRenderViewHostContentSettingsDelegateHelper(
+ render_process_id,
+ render_view_id,
+ method,
+ MakeTuple(a, b, c, d));
+}
+
+template <typename Method,
+ typename A, typename B, typename C, typename D, typename E>
+inline void CallRenderViewHostContentSettingsDelegate(int render_process_id,
+ int render_view_id,
+ Method method,
+ const A& a,
+ const B& b,
+ const C& c,
+ const D& d,
+ const E& e) {
+ internal::CallRenderViewHostContentSettingsDelegateHelper(
+ render_process_id, render_view_id, method, MakeTuple(a, b, c, d, e));
+}
+
+// ----------------------------------------------------------------------------
+// Proxy calls to the specified RenderViewHost's RendererManagement delegate.
+
+template <typename Method>
+inline void CallRenderViewHostRendererManagementDelegate(int render_process_id,
+ int render_view_id,
+ Method method) {
+ internal::CallRenderViewHostRendererManagementDelegateHelper(
+ render_process_id,
+ render_view_id,
+ method,
+ MakeTuple());
+}
+
+template <typename Method, typename A>
+inline void CallRenderViewHostRendererManagementDelegate(int render_process_id,
+ int render_view_id,
+ Method method,
+ const A& a) {
+ internal::CallRenderViewHostRendererManagementDelegateHelper(
+ render_process_id,
+ render_view_id,
+ method,
+ MakeTuple(a));
+}
+
+template <typename Method, typename A, typename B>
+inline void CallRenderViewHostRendererManagementDelegate(int render_process_id,
+ int render_view_id,
+ Method method,
+ const A& a,
+ const B& b) {
+ internal::CallRenderViewHostRendererManagementDelegateHelper(
+ render_process_id,
+ render_view_id,
+ method,
+ MakeTuple(a, b));
+}
+
+// ----------------------------------------------------------------------------
+// Proxy calls to the specified RenderViewHost's SSL delegate.
+
+template <typename Method, typename A>
+inline void CallRenderViewHostSSLDelegate(int render_process_id,
+ int render_view_id,
+ Method method,
+ const A& a) {
+ internal::CallRenderViewHostSSLDelegateHelper(
+ render_process_id,
+ render_view_id,
+ method,
+ MakeTuple(a));
+}
+
+template <typename Method, typename A, typename B>
+inline void CallRenderViewHostSSLDelegate(int render_process_id,
+ int render_view_id,
+ Method method,
+ const A& a,
+ const B& b) {
+ internal::CallRenderViewHostSSLDelegateHelper(
+ render_process_id,
+ render_view_id,
+ method,
+ MakeTuple(a, b));
+}
+
+// ----------------------------------------------------------------------------
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_RENDER_VIEW_HOST_NOTIFICATION_TASK_H_
diff --git a/content/browser/renderer_host/render_widget_fullscreen_host.cc b/content/browser/renderer_host/render_widget_fullscreen_host.cc
new file mode 100644
index 0000000..4fbd140
--- /dev/null
+++ b/content/browser/renderer_host/render_widget_fullscreen_host.cc
@@ -0,0 +1,10 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/render_widget_fullscreen_host.h"
+
+RenderWidgetFullscreenHost::RenderWidgetFullscreenHost(
+ RenderProcessHost* process, int routing_id)
+ : RenderWidgetHost(process, routing_id) {
+}
diff --git a/content/browser/renderer_host/render_widget_fullscreen_host.h b/content/browser/renderer_host/render_widget_fullscreen_host.h
new file mode 100644
index 0000000..fce2200
--- /dev/null
+++ b/content/browser/renderer_host/render_widget_fullscreen_host.h
@@ -0,0 +1,15 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_FULLSCREEN_HOST_H_
+#define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_FULLSCREEN_HOST_H_
+
+#include "content/browser/renderer_host/render_widget_host.h"
+
+class RenderWidgetFullscreenHost : public RenderWidgetHost {
+ public:
+ RenderWidgetFullscreenHost(RenderProcessHost* process, int routing_id);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_FULLSCREEN_HOST_H_
diff --git a/content/browser/renderer_host/render_widget_helper.cc b/content/browser/renderer_host/render_widget_helper.cc
new file mode 100644
index 0000000..c3bb7de
--- /dev/null
+++ b/content/browser/renderer_host/render_widget_helper.cc
@@ -0,0 +1,335 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/render_widget_helper.h"
+
+#include "base/eintr_wrapper.h"
+#include "base/threading/thread.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/common/render_messages_params.h"
+#include "content/browser/renderer_host/render_process_host.h"
+#include "content/browser/renderer_host/render_view_host.h"
+#include "content/browser/renderer_host/resource_dispatcher_host.h"
+
+// A Task used with InvokeLater that we hold a pointer to in pending_paints_.
+// Instances are deleted by MessageLoop after it calls their Run method.
+class RenderWidgetHelper::UpdateMsgProxy : public Task {
+ public:
+ UpdateMsgProxy(RenderWidgetHelper* h, const IPC::Message& m)
+ : helper(h),
+ message(m),
+ cancelled(false) {
+ }
+
+ ~UpdateMsgProxy() {
+ // If the paint message was never dispatched, then we need to let the
+ // helper know that we are going away.
+ if (!cancelled && helper)
+ helper->OnDiscardUpdateMsg(this);
+ }
+
+ virtual void Run() {
+ if (!cancelled) {
+ helper->OnDispatchUpdateMsg(this);
+ helper = NULL;
+ }
+ }
+
+ scoped_refptr<RenderWidgetHelper> helper;
+ IPC::Message message;
+ bool cancelled; // If true, then the message will not be dispatched.
+
+ DISALLOW_COPY_AND_ASSIGN(UpdateMsgProxy);
+};
+
+RenderWidgetHelper::RenderWidgetHelper()
+ : render_process_id_(-1),
+#if defined(OS_WIN)
+ event_(CreateEvent(NULL, FALSE /* auto-reset */, FALSE, NULL)),
+#elif defined(OS_POSIX)
+ event_(false /* auto-reset */, false),
+#endif
+ resource_dispatcher_host_(NULL) {
+}
+
+RenderWidgetHelper::~RenderWidgetHelper() {
+ // The elements of pending_paints_ each hold an owning reference back to this
+ // object, so we should not be destroyed unless pending_paints_ is empty!
+ DCHECK(pending_paints_.empty());
+
+#if defined(OS_MACOSX)
+ ClearAllocatedDIBs();
+#endif
+}
+
+void RenderWidgetHelper::Init(
+ int render_process_id,
+ ResourceDispatcherHost* resource_dispatcher_host) {
+ render_process_id_ = render_process_id;
+ resource_dispatcher_host_ = resource_dispatcher_host;
+}
+
+int RenderWidgetHelper::GetNextRoutingID() {
+ return next_routing_id_.GetNext() + 1;
+}
+
+void RenderWidgetHelper::CancelResourceRequests(int render_widget_id) {
+ if (render_process_id_ == -1)
+ return;
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ NewRunnableMethod(this,
+ &RenderWidgetHelper::OnCancelResourceRequests,
+ render_widget_id));
+}
+
+void RenderWidgetHelper::CrossSiteClosePageACK(
+ const ViewMsg_ClosePage_Params& params) {
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ NewRunnableMethod(this,
+ &RenderWidgetHelper::OnCrossSiteClosePageACK,
+ params));
+}
+
+bool RenderWidgetHelper::WaitForUpdateMsg(int render_widget_id,
+ const base::TimeDelta& max_delay,
+ IPC::Message* msg) {
+ base::TimeTicks time_start = base::TimeTicks::Now();
+
+ for (;;) {
+ UpdateMsgProxy* proxy = NULL;
+ {
+ base::AutoLock lock(pending_paints_lock_);
+
+ UpdateMsgProxyMap::iterator it = pending_paints_.find(render_widget_id);
+ if (it != pending_paints_.end()) {
+ proxy = it->second;
+
+ // Flag the proxy as cancelled so that when it is run as a task it will
+ // do nothing.
+ proxy->cancelled = true;
+
+ pending_paints_.erase(it);
+ }
+ }
+
+ if (proxy) {
+ *msg = proxy->message;
+ DCHECK(msg->routing_id() == render_widget_id);
+ return true;
+ }
+
+ // Calculate the maximum amount of time that we are willing to sleep.
+ base::TimeDelta max_sleep_time =
+ max_delay - (base::TimeTicks::Now() - time_start);
+ if (max_sleep_time <= base::TimeDelta::FromMilliseconds(0))
+ break;
+
+ event_.TimedWait(max_sleep_time);
+ }
+
+ return false;
+}
+
+void RenderWidgetHelper::DidReceiveUpdateMsg(const IPC::Message& msg) {
+ int render_widget_id = msg.routing_id();
+
+ UpdateMsgProxy* proxy = NULL;
+ {
+ base::AutoLock lock(pending_paints_lock_);
+
+ // Visual Studio 2010 has problems converting NULL to the null pointer for
+ // std::pair. See http://connect.microsoft.com/VisualStudio/feedback/details/520043/error-converting-from-null-to-a-pointer-type-in-std-pair
+ // It will work if we pass nullptr.
+#if defined(_MSC_VER) && _MSC_VER >= 1600
+ RenderWidgetHelper::UpdateMsgProxy* null_proxy = nullptr;
+#else
+ RenderWidgetHelper::UpdateMsgProxy* null_proxy = NULL;
+#endif
+ UpdateMsgProxyMap::value_type new_value(render_widget_id, null_proxy);
+
+ // We expect only a single PaintRect message at a time. Optimize for the
+ // case that we don't already have an entry by using the 'insert' method.
+ std::pair<UpdateMsgProxyMap::iterator, bool> result =
+ pending_paints_.insert(new_value);
+ if (!result.second) {
+ NOTREACHED() << "Unexpected PaintRect message!";
+ return;
+ }
+
+ result.first->second = (proxy = new UpdateMsgProxy(this, msg));
+ }
+
+ // Notify anyone waiting on the UI thread that there is a new entry in the
+ // proxy map. If they don't find the entry they are looking for, then they
+ // will just continue waiting.
+ event_.Signal();
+
+ // The proxy will be deleted when it is run as a task.
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, proxy);
+}
+
+void RenderWidgetHelper::OnDiscardUpdateMsg(UpdateMsgProxy* proxy) {
+ const IPC::Message& msg = proxy->message;
+
+ // Remove the proxy from the map now that we are going to handle it normally.
+ {
+ base::AutoLock lock(pending_paints_lock_);
+
+ UpdateMsgProxyMap::iterator it = pending_paints_.find(msg.routing_id());
+ DCHECK(it != pending_paints_.end());
+ DCHECK(it->second == proxy);
+
+ pending_paints_.erase(it);
+ }
+}
+
+void RenderWidgetHelper::OnDispatchUpdateMsg(UpdateMsgProxy* proxy) {
+ OnDiscardUpdateMsg(proxy);
+
+ // It is reasonable for the host to no longer exist.
+ RenderProcessHost* host = RenderProcessHost::FromID(render_process_id_);
+ if (host)
+ host->OnMessageReceived(proxy->message);
+}
+
+void RenderWidgetHelper::OnCancelResourceRequests(
+ int render_widget_id) {
+ resource_dispatcher_host_->CancelRequestsForRoute(
+ render_process_id_, render_widget_id);
+}
+
+void RenderWidgetHelper::OnCrossSiteClosePageACK(
+ const ViewMsg_ClosePage_Params& params) {
+ resource_dispatcher_host_->OnClosePageACK(params);
+}
+
+void RenderWidgetHelper::CreateNewWindow(
+ const ViewHostMsg_CreateWindow_Params& params,
+ base::ProcessHandle render_process,
+ int* route_id) {
+ *route_id = GetNextRoutingID();
+ // Block resource requests until the view is created, since the HWND might be
+ // needed if a response ends up creating a plugin.
+ resource_dispatcher_host_->BlockRequestsForRoute(
+ render_process_id_, *route_id);
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderWidgetHelper::OnCreateWindowOnUI, params, *route_id));
+}
+
+void RenderWidgetHelper::OnCreateWindowOnUI(
+ const ViewHostMsg_CreateWindow_Params& params,
+ int route_id) {
+ RenderViewHost* host =
+ RenderViewHost::FromID(render_process_id_, params.opener_id);
+ if (host)
+ host->CreateNewWindow(route_id, params);
+
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ NewRunnableMethod(this, &RenderWidgetHelper::OnCreateWindowOnIO,
+ route_id));
+}
+
+void RenderWidgetHelper::OnCreateWindowOnIO(int route_id) {
+ resource_dispatcher_host_->ResumeBlockedRequestsForRoute(
+ render_process_id_, route_id);
+}
+
+void RenderWidgetHelper::CreateNewWidget(int opener_id,
+ WebKit::WebPopupType popup_type,
+ int* route_id) {
+ *route_id = GetNextRoutingID();
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderWidgetHelper::OnCreateWidgetOnUI, opener_id, *route_id,
+ popup_type));
+}
+
+void RenderWidgetHelper::CreateNewFullscreenWidget(int opener_id,
+ int* route_id) {
+ *route_id = GetNextRoutingID();
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this, &RenderWidgetHelper::OnCreateFullscreenWidgetOnUI,
+ opener_id, *route_id));
+}
+
+void RenderWidgetHelper::OnCreateWidgetOnUI(
+ int opener_id, int route_id, WebKit::WebPopupType popup_type) {
+ RenderViewHost* host = RenderViewHost::FromID(render_process_id_, opener_id);
+ if (host)
+ host->CreateNewWidget(route_id, popup_type);
+}
+
+void RenderWidgetHelper::OnCreateFullscreenWidgetOnUI(int opener_id,
+ int route_id) {
+ RenderViewHost* host = RenderViewHost::FromID(render_process_id_, opener_id);
+ if (host)
+ host->CreateNewFullscreenWidget(route_id);
+}
+
+#if defined(OS_MACOSX)
+TransportDIB* RenderWidgetHelper::MapTransportDIB(TransportDIB::Id dib_id) {
+ base::AutoLock locked(allocated_dibs_lock_);
+
+ const std::map<TransportDIB::Id, int>::iterator
+ i = allocated_dibs_.find(dib_id);
+ if (i == allocated_dibs_.end())
+ return NULL;
+
+ base::FileDescriptor fd(dup(i->second), true);
+ return TransportDIB::Map(fd);
+}
+
+void RenderWidgetHelper::AllocTransportDIB(
+ size_t size, bool cache_in_browser, TransportDIB::Handle* result) {
+ scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory());
+ if (!shared_memory->CreateAnonymous(size)) {
+ result->fd = -1;
+ result->auto_close = false;
+ return;
+ }
+
+ shared_memory->GiveToProcess(0 /* pid, not needed */, result);
+
+ if (cache_in_browser) {
+ // Keep a copy of the file descriptor around
+ base::AutoLock locked(allocated_dibs_lock_);
+ allocated_dibs_[shared_memory->id()] = dup(result->fd);
+ }
+}
+
+void RenderWidgetHelper::FreeTransportDIB(TransportDIB::Id dib_id) {
+ base::AutoLock locked(allocated_dibs_lock_);
+
+ const std::map<TransportDIB::Id, int>::iterator
+ i = allocated_dibs_.find(dib_id);
+
+ if (i != allocated_dibs_.end()) {
+ if (HANDLE_EINTR(close(i->second)) < 0)
+ PLOG(ERROR) << "close";
+ allocated_dibs_.erase(i);
+ } else {
+ DLOG(WARNING) << "Renderer asked us to free unknown transport DIB";
+ }
+}
+
+void RenderWidgetHelper::ClearAllocatedDIBs() {
+ for (std::map<TransportDIB::Id, int>::iterator
+ i = allocated_dibs_.begin(); i != allocated_dibs_.end(); ++i) {
+ if (HANDLE_EINTR(close(i->second)) < 0)
+ PLOG(ERROR) << "close: " << i->first;
+ }
+
+ allocated_dibs_.clear();
+}
+#endif
diff --git a/content/browser/renderer_host/render_widget_helper.h b/content/browser/renderer_host/render_widget_helper.h
new file mode 100644
index 0000000..0dd25d7
--- /dev/null
+++ b/content/browser/renderer_host/render_widget_helper.h
@@ -0,0 +1,216 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HELPER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HELPER_H_
+#pragma once
+
+#include <map>
+
+#include "app/surface/transport_dib.h"
+#include "base/atomic_sequence_num.h"
+#include "base/hash_tables.h"
+#include "base/process.h"
+#include "base/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "chrome/common/window_container_type.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebPopupType.h"
+
+namespace IPC {
+class Message;
+}
+
+namespace base {
+class TimeDelta;
+}
+
+class ResourceDispatcherHost;
+struct ViewHostMsg_CreateWindow_Params;
+struct ViewMsg_ClosePage_Params;
+
+
+// Instantiated per RenderProcessHost to provide various optimizations on
+// behalf of a RenderWidgetHost. This class bridges between the IO thread
+// where the RenderProcessHost's MessageFilter lives and the UI thread where
+// the RenderWidgetHost lives.
+//
+//
+// OPTIMIZED RESIZE
+//
+// RenderWidgetHelper is used to implement optimized resize. When the
+// RenderWidgetHost is resized, it sends a Resize message to its RenderWidget
+// counterpart in the renderer process. The RenderWidget generates a
+// UpdateRect message in response to the Resize message, and it sets the
+// IS_RESIZE_ACK flag in the UpdateRect message to true.
+//
+// Back in the browser process, when the RenderProcessHost's MessageFilter
+// sees a UpdateRect message, it directs it to the RenderWidgetHelper by
+// calling the DidReceiveUpdateMsg method. That method stores the data for
+// the UpdateRect message in a map, where it can be directly accessed by the
+// RenderWidgetHost on the UI thread during a call to RenderWidgetHost's
+// GetBackingStore method.
+//
+// When the RenderWidgetHost's GetBackingStore method is called, it first
+// checks to see if it is waiting for a resize ack. If it is, then it calls
+// the RenderWidgetHelper's WaitForUpdateMsg to check if there is already a
+// resulting UpdateRect message (or to wait a short amount of time for one to
+// arrive). The main goal of this mechanism is to short-cut the usual way in
+// which IPC messages are proxied over to the UI thread via InvokeLater.
+// This approach is necessary since window resize is followed up immediately
+// by a request to repaint the window.
+//
+//
+// OPTIMIZED TAB SWITCHING
+//
+// When a RenderWidgetHost is in a background tab, it is flagged as hidden.
+// This causes the corresponding RenderWidget to stop sending UpdateRect
+// messages. The RenderWidgetHost also discards its backingstore when it is
+// hidden, which helps free up memory. As a result, when a RenderWidgetHost
+// is restored, it can be momentarily without a backingstore. (Restoring a
+// RenderWidgetHost results in a WasRestored message being sent to the
+// RenderWidget, which triggers a full UpdateRect message.) This can lead to
+// an observed rendering glitch as the TabContents will just have to fill
+// white overtop the RenderWidgetHost until the RenderWidgetHost receives a
+// UpdateRect message to refresh its backingstore.
+//
+// To avoid this 'white flash', the RenderWidgetHost again makes use of the
+// RenderWidgetHelper's WaitForUpdateMsg method. When the RenderWidgetHost's
+// GetBackingStore method is called, it will call WaitForUpdateMsg if it has
+// no backingstore.
+//
+// TRANSPORT DIB CREATION
+//
+// On some platforms (currently the Mac) the renderer cannot create transport
+// DIBs because of sandbox limitations. Thus, it has to make synchronous IPCs
+// to the browser for them. Since these requests are synchronous, they cannot
+// terminate on the UI thread. Thus, in this case, this object performs the
+// allocation and maintains the set of allocated transport DIBs which the
+// renderers can refer to.
+//
+class RenderWidgetHelper
+ : public base::RefCountedThreadSafe<RenderWidgetHelper> {
+ public:
+ RenderWidgetHelper();
+
+ void Init(int render_process_id,
+ ResourceDispatcherHost* resource_dispatcher_host);
+
+ // Gets the next available routing id. This is thread safe.
+ int GetNextRoutingID();
+
+
+ // UI THREAD ONLY -----------------------------------------------------------
+
+ // These three functions provide the backend implementation of the
+ // corresponding functions in RenderProcessHost. See those declarations
+ // for documentation.
+ void CancelResourceRequests(int render_widget_id);
+ void CrossSiteClosePageACK(const ViewMsg_ClosePage_Params& params);
+ bool WaitForUpdateMsg(int render_widget_id,
+ const base::TimeDelta& max_delay,
+ IPC::Message* msg);
+
+#if defined(OS_MACOSX)
+ // Given the id of a transport DIB, return a mapping to it or NULL on error.
+ TransportDIB* MapTransportDIB(TransportDIB::Id dib_id);
+#endif
+
+
+ // IO THREAD ONLY -----------------------------------------------------------
+
+ // Called on the IO thread when a UpdateRect message is received.
+ void DidReceiveUpdateMsg(const IPC::Message& msg);
+
+ void CreateNewWindow(const ViewHostMsg_CreateWindow_Params& params,
+ base::ProcessHandle render_process,
+ int* route_id);
+ void CreateNewWidget(int opener_id,
+ WebKit::WebPopupType popup_type,
+ int* route_id);
+ void CreateNewFullscreenWidget(int opener_id, int* route_id);
+
+#if defined(OS_MACOSX)
+ // Called on the IO thread to handle the allocation of a TransportDIB. If
+ // |cache_in_browser| is |true|, then a copy of the shmem is kept by the
+ // browser, and it is the caller's repsonsibility to call
+ // FreeTransportDIB(). In all cases, the caller is responsible for deleting
+ // the resulting TransportDIB.
+ void AllocTransportDIB(size_t size,
+ bool cache_in_browser,
+ TransportDIB::Handle* result);
+
+ // Called on the IO thread to handle the freeing of a transport DIB
+ void FreeTransportDIB(TransportDIB::Id dib_id);
+#endif
+
+ private:
+ // A class used to proxy a paint message. PaintMsgProxy objects are created
+ // on the IO thread and destroyed on the UI thread.
+ class UpdateMsgProxy;
+ friend class UpdateMsgProxy;
+ friend class base::RefCountedThreadSafe<RenderWidgetHelper>;
+
+ // Map from render_widget_id to live PaintMsgProxy instance.
+ typedef base::hash_map<int, UpdateMsgProxy*> UpdateMsgProxyMap;
+
+ ~RenderWidgetHelper();
+
+ // Called on the UI thread to discard a paint message.
+ void OnDiscardUpdateMsg(UpdateMsgProxy* proxy);
+
+ // Called on the UI thread to dispatch a paint message if necessary.
+ void OnDispatchUpdateMsg(UpdateMsgProxy* proxy);
+
+ // Called on the UI thread to finish creating a window.
+ void OnCreateWindowOnUI(const ViewHostMsg_CreateWindow_Params& params,
+ int route_id);
+
+ // Called on the IO thread after a window was created on the UI thread.
+ void OnCreateWindowOnIO(int route_id);
+
+ // Called on the UI thread to finish creating a widget.
+ void OnCreateWidgetOnUI(int opener_id,
+ int route_id,
+ WebKit::WebPopupType popup_type);
+
+ // Called on the UI thread to create a fullscreen widget.
+ void OnCreateFullscreenWidgetOnUI(int opener_id, int route_id);
+
+ // Called on the IO thread to cancel resource requests for the render widget.
+ void OnCancelResourceRequests(int render_widget_id);
+
+ // Called on the IO thread to resume a cross-site response.
+ void OnCrossSiteClosePageACK(const ViewMsg_ClosePage_Params& params);
+
+#if defined(OS_MACOSX)
+ // Called on destruction to release all allocated transport DIBs
+ void ClearAllocatedDIBs();
+
+ // On OSX we keep file descriptors to all the allocated DIBs around until
+ // the renderer frees them.
+ base::Lock allocated_dibs_lock_;
+ std::map<TransportDIB::Id, int> allocated_dibs_;
+#endif
+
+ // A map of live paint messages. Must hold pending_paints_lock_ to access.
+ // The UpdateMsgProxy objects are not owned by this map. (See UpdateMsgProxy
+ // for details about how the lifetime of instances are managed.)
+ UpdateMsgProxyMap pending_paints_;
+ base::Lock pending_paints_lock_;
+
+ int render_process_id_;
+
+ // Event used to implement WaitForUpdateMsg.
+ base::WaitableEvent event_;
+
+ // The next routing id to use.
+ base::AtomicSequenceNumber next_routing_id_;
+
+ ResourceDispatcherHost* resource_dispatcher_host_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderWidgetHelper);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HELPER_H_
diff --git a/content/browser/renderer_host/render_widget_host.cc b/content/browser/renderer_host/render_widget_host.cc
new file mode 100644
index 0000000..5fbd2df
--- /dev/null
+++ b/content/browser/renderer_host/render_widget_host.cc
@@ -0,0 +1,1287 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/render_widget_host.h"
+
+#include "base/auto_reset.h"
+#include "base/command_line.h"
+#include "base/message_loop.h"
+#include "base/metrics/histogram.h"
+#include "chrome/browser/accessibility/browser_accessibility_state.h"
+#include "chrome/browser/metrics/user_metrics.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/result_codes.h"
+#include "chrome/common/native_web_keyboard_event.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/render_messages_params.h"
+#include "content/browser/renderer_host/backing_store.h"
+#include "content/browser/renderer_host/backing_store_manager.h"
+#include "content/browser/renderer_host/render_process_host.h"
+#include "content/browser/renderer_host/render_widget_helper.h"
+#include "content/browser/renderer_host/render_widget_host_view.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebCompositionUnderline.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+#include "webkit/glue/webcursor.h"
+#include "webkit/plugins/npapi/webplugin.h"
+
+#if defined(TOOLKIT_VIEWS)
+#include "views/view.h"
+#endif
+
+#if defined (OS_MACOSX)
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebScreenInfo.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/mac/WebScreenInfoFactory.h"
+#endif
+
+using base::Time;
+using base::TimeDelta;
+using base::TimeTicks;
+
+using WebKit::WebInputEvent;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebMouseEvent;
+using WebKit::WebMouseWheelEvent;
+using WebKit::WebTextDirection;
+
+#if defined (OS_MACOSX)
+using WebKit::WebScreenInfo;
+using WebKit::WebScreenInfoFactory;
+#endif
+
+// How long to (synchronously) wait for the renderer to respond with a
+// PaintRect message, when our backing-store is invalid, before giving up and
+// returning a null or incorrectly sized backing-store from GetBackingStore.
+// This timeout impacts the "choppiness" of our window resize perf.
+static const int kPaintMsgTimeoutMS = 40;
+
+// How long to wait before we consider a renderer hung.
+static const int kHungRendererDelayMs = 20000;
+
+// The maximum time between wheel messages while coalescing. This trades off
+// smoothness of scrolling with a risk of falling behind the events, resulting
+// in trailing scrolls after the user ends their input.
+static const int kMaxTimeBetweenWheelMessagesMs = 250;
+
+///////////////////////////////////////////////////////////////////////////////
+// RenderWidgetHost
+
+RenderWidgetHost::RenderWidgetHost(RenderProcessHost* process,
+ int routing_id)
+ : renderer_initialized_(false),
+ renderer_accessible_(false),
+ view_(NULL),
+ process_(process),
+ routing_id_(routing_id),
+ is_loading_(false),
+ is_hidden_(false),
+ is_accelerated_compositing_active_(false),
+ repaint_ack_pending_(false),
+ resize_ack_pending_(false),
+ mouse_move_pending_(false),
+ mouse_wheel_pending_(false),
+ needs_repainting_on_restore_(false),
+ is_unresponsive_(false),
+ in_get_backing_store_(false),
+ view_being_painted_(false),
+ ignore_input_events_(false),
+ text_direction_updated_(false),
+ text_direction_(WebKit::WebTextDirectionLeftToRight),
+ text_direction_canceled_(false),
+ suppress_next_char_events_(false) {
+ if (routing_id_ == MSG_ROUTING_NONE)
+ routing_id_ = process_->GetNextRoutingID();
+
+ process_->Attach(this, routing_id_);
+ // Because the widget initializes as is_hidden_ == false,
+ // tell the process host that we're alive.
+ process_->WidgetRestored();
+
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kForceRendererAccessibility) ||
+ BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser()) {
+ EnableRendererAccessibility();
+ }
+}
+
+RenderWidgetHost::~RenderWidgetHost() {
+ // Clear our current or cached backing store if either remains.
+ BackingStoreManager::RemoveBackingStore(this);
+
+ process_->Release(routing_id_);
+}
+
+gfx::NativeViewId RenderWidgetHost::GetNativeViewId() {
+ if (view_)
+ return gfx::IdFromNativeView(view_->GetNativeView());
+ return 0;
+}
+
+bool RenderWidgetHost::PreHandleKeyboardEvent(
+ const NativeWebKeyboardEvent& event,
+ bool* is_keyboard_shortcut) {
+ return false;
+}
+
+void RenderWidgetHost::Init() {
+ DCHECK(process_->HasConnection());
+
+ renderer_initialized_ = true;
+
+ // Send the ack along with the information on placement.
+ Send(new ViewMsg_CreatingNew_ACK(routing_id_, GetNativeViewId()));
+ WasResized();
+}
+
+void RenderWidgetHost::Shutdown() {
+ if (process_->HasConnection()) {
+ // Tell the renderer object to close.
+ process_->ReportExpectingClose(routing_id_);
+ bool rv = Send(new ViewMsg_Close(routing_id_));
+ DCHECK(rv);
+ }
+
+ Destroy();
+}
+
+bool RenderWidgetHost::IsRenderView() const {
+ return false;
+}
+
+bool RenderWidgetHost::OnMessageReceived(const IPC::Message &msg) {
+ bool handled = true;
+ bool msg_is_ok = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(RenderWidgetHost, msg, msg_is_ok)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_RenderViewReady, OnMsgRenderViewReady)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_RenderViewGone, OnMsgRenderViewGone)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_Close, OnMsgClose)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_RequestMove, OnMsgRequestMove)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_PaintAtSize_ACK, OnMsgPaintAtSizeAck)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_UpdateRect, OnMsgUpdateRect)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_HandleInputEvent_ACK, OnMsgInputEventAck)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_Focus, OnMsgFocus)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_Blur, OnMsgBlur)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_SetCursor, OnMsgSetCursor)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ImeUpdateTextInputState,
+ OnMsgImeUpdateTextInputState)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ImeCancelComposition,
+ OnMsgImeCancelComposition)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DidActivateAcceleratedCompositing,
+ OnMsgDidActivateAcceleratedCompositing)
+#if defined(OS_MACOSX)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_GetScreenInfo, OnMsgGetScreenInfo)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_GetWindowRect, OnMsgGetWindowRect)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_GetRootWindowRect, OnMsgGetRootWindowRect)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_PluginFocusChanged,
+ OnMsgPluginFocusChanged)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_StartPluginIme,
+ OnMsgStartPluginIme)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_AllocateFakePluginWindowHandle,
+ OnAllocateFakePluginWindowHandle)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DestroyFakePluginWindowHandle,
+ OnDestroyFakePluginWindowHandle)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_AcceleratedSurfaceSetIOSurface,
+ OnAcceleratedSurfaceSetIOSurface)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_AcceleratedSurfaceSetTransportDIB,
+ OnAcceleratedSurfaceSetTransportDIB)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_AcceleratedSurfaceBuffersSwapped,
+ OnAcceleratedSurfaceBuffersSwapped)
+#elif defined(OS_POSIX)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_CreatePluginContainer,
+ OnMsgCreatePluginContainer)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DestroyPluginContainer,
+ OnMsgDestroyPluginContainer)
+#endif
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP_EX()
+
+ if (!msg_is_ok) {
+ // The message de-serialization failed. Kill the renderer process.
+ UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_RWH"));
+ process()->ReceivedBadMessage();
+ }
+ return handled;
+}
+
+bool RenderWidgetHost::Send(IPC::Message* msg) {
+ return process_->Send(msg);
+}
+
+void RenderWidgetHost::WasHidden() {
+ is_hidden_ = true;
+
+ // Don't bother reporting hung state when we aren't the active tab.
+ StopHangMonitorTimeout();
+
+ // If we have a renderer, then inform it that we are being hidden so it can
+ // reduce its resource utilization.
+ Send(new ViewMsg_WasHidden(routing_id_));
+
+ // TODO(darin): what about constrained windows? it doesn't look like they
+ // see a message when their parent is hidden. maybe there is something more
+ // generic we can do at the TabContents API level instead of relying on
+ // Windows messages.
+
+ // Tell the RenderProcessHost we were hidden.
+ process_->WidgetHidden();
+
+ bool is_visible = false;
+ NotificationService::current()->Notify(
+ NotificationType::RENDER_WIDGET_VISIBILITY_CHANGED,
+ Source<RenderWidgetHost>(this),
+ Details<bool>(&is_visible));
+}
+
+void RenderWidgetHost::WasRestored() {
+ // When we create the widget, it is created as *not* hidden.
+ if (!is_hidden_)
+ return;
+ is_hidden_ = false;
+
+ BackingStore* backing_store = BackingStoreManager::Lookup(this);
+ // If we already have a backing store for this widget, then we don't need to
+ // repaint on restore _unless_ we know that our backing store is invalid.
+ // When accelerated compositing is on, we must always repaint, even when
+ // the backing store exists.
+ bool needs_repainting;
+ if (needs_repainting_on_restore_ || !backing_store ||
+ is_accelerated_compositing_active()) {
+ needs_repainting = true;
+ needs_repainting_on_restore_ = false;
+ } else {
+ needs_repainting = false;
+ }
+ Send(new ViewMsg_WasRestored(routing_id_, needs_repainting));
+
+ process_->WidgetRestored();
+
+ bool is_visible = true;
+ NotificationService::current()->Notify(
+ NotificationType::RENDER_WIDGET_VISIBILITY_CHANGED,
+ Source<RenderWidgetHost>(this),
+ Details<bool>(&is_visible));
+
+ // It's possible for our size to be out of sync with the renderer. The
+ // following is one case that leads to this:
+ // 1. WasResized -> Send ViewMsg_Resize to render
+ // 2. WasResized -> do nothing as resize_ack_pending_ is true
+ // 3. WasHidden
+ // 4. OnMsgUpdateRect from (1) processed. Does NOT invoke WasResized as view
+ // is hidden. Now renderer/browser out of sync with what they think size
+ // is.
+ // By invoking WasResized the renderer is updated as necessary. WasResized
+ // does nothing if the sizes are already in sync.
+ //
+ // TODO: ideally ViewMsg_WasRestored would take a size. This way, the renderer
+ // could handle both the restore and resize at once. This isn't that big a
+ // deal as RenderWidget::WasRestored delays updating, so that the resize from
+ // WasResized is usually processed before the renderer is painted.
+ WasResized();
+}
+
+void RenderWidgetHost::WasResized() {
+ if (resize_ack_pending_ || !process_->HasConnection() || !view_ ||
+ !renderer_initialized_) {
+ return;
+ }
+
+#if !defined(OS_MACOSX)
+ gfx::Size new_size = view_->GetViewBounds().size();
+#else
+ // When UI scaling is enabled on OS X, allocate a smaller bitmap and
+ // pixel-scale it up.
+ // TODO(thakis): Use pixel size on mac and set UI scale in renderer.
+ // http://crbug.com/31960
+ gfx::Size new_size(view_->GetViewCocoaBounds().size());
+#endif
+ gfx::Rect reserved_rect = view_->reserved_contents_rect();
+
+ // Avoid asking the RenderWidget to resize to its current size, since it
+ // won't send us a PaintRect message in that case, unless reserved area is
+ // changed, but even in this case PaintRect message won't be sent.
+ if (new_size == current_size_ && reserved_rect == current_reserved_rect_)
+ return;
+
+ if (in_flight_size_ != gfx::Size() && new_size == in_flight_size_ &&
+ in_flight_reserved_rect_ == reserved_rect) {
+ return;
+ }
+
+ // We don't expect to receive an ACK when the requested size is empty or
+ // only reserved area is changed.
+ resize_ack_pending_ = !new_size.IsEmpty() && new_size != current_size_;
+
+ if (!Send(new ViewMsg_Resize(routing_id_, new_size, reserved_rect))) {
+ resize_ack_pending_ = false;
+ } else {
+ if (resize_ack_pending_) {
+ in_flight_size_ = new_size;
+ in_flight_reserved_rect_ = reserved_rect;
+ } else {
+ // Message was sent successfully, but we do not expect to receive an ACK,
+ // so update current values right away.
+ current_size_ = new_size;
+ // TODO(alekseys): send a message from renderer to ack a reserved rect
+ // changes only.
+ current_reserved_rect_ = reserved_rect;
+ }
+ }
+}
+
+void RenderWidgetHost::GotFocus() {
+ Focus();
+}
+
+void RenderWidgetHost::Focus() {
+ Send(new ViewMsg_SetFocus(routing_id_, true));
+}
+
+void RenderWidgetHost::Blur() {
+ Send(new ViewMsg_SetFocus(routing_id_, false));
+}
+
+void RenderWidgetHost::LostCapture() {
+ Send(new ViewMsg_MouseCaptureLost(routing_id_));
+}
+
+void RenderWidgetHost::ViewDestroyed() {
+ // TODO(evanm): tracking this may no longer be necessary;
+ // eliminate this function if so.
+ view_ = NULL;
+}
+
+void RenderWidgetHost::SetIsLoading(bool is_loading) {
+ is_loading_ = is_loading;
+ if (!view_)
+ return;
+ view_->SetIsLoading(is_loading);
+}
+
+void RenderWidgetHost::PaintAtSize(TransportDIB::Handle dib_handle,
+ int tag,
+ const gfx::Size& page_size,
+ const gfx::Size& desired_size) {
+ // Ask the renderer to create a bitmap regardless of whether it's
+ // hidden, being resized, redrawn, etc. It resizes the web widget
+ // to the page_size and then scales it to the desired_size.
+ Send(new ViewMsg_PaintAtSize(routing_id_, dib_handle, tag,
+ page_size, desired_size));
+}
+
+BackingStore* RenderWidgetHost::GetBackingStore(bool force_create) {
+ // We should not be asked to paint while we are hidden. If we are hidden,
+ // then it means that our consumer failed to call WasRestored. If we're not
+ // force creating the backing store, it's OK since we can feel free to give
+ // out our cached one if we have it.
+ DCHECK(!is_hidden_ || !force_create) <<
+ "GetBackingStore called while hidden!";
+
+ // We should never be called recursively; this can theoretically lead to
+ // infinite recursion and almost certainly leads to lower performance.
+ DCHECK(!in_get_backing_store_) << "GetBackingStore called recursively!";
+ AutoReset<bool> auto_reset_in_get_backing_store(&in_get_backing_store_, true);
+
+ // We might have a cached backing store that we can reuse!
+ BackingStore* backing_store =
+ BackingStoreManager::GetBackingStore(this, current_size_);
+ if (!force_create)
+ return backing_store;
+
+ // If we fail to find a backing store in the cache, send out a request
+ // to the renderer to paint the view if required.
+ if (!backing_store && !repaint_ack_pending_ && !resize_ack_pending_ &&
+ !view_being_painted_) {
+ repaint_start_time_ = TimeTicks::Now();
+ repaint_ack_pending_ = true;
+ Send(new ViewMsg_Repaint(routing_id_, current_size_));
+ }
+
+ // When we have asked the RenderWidget to resize, and we are still waiting on
+ // a response, block for a little while to see if we can't get a response
+ // before returning the old (incorrectly sized) backing store.
+ if (resize_ack_pending_ || !backing_store) {
+ IPC::Message msg;
+ TimeDelta max_delay = TimeDelta::FromMilliseconds(kPaintMsgTimeoutMS);
+ if (process_->WaitForUpdateMsg(routing_id_, max_delay, &msg)) {
+ OnMessageReceived(msg);
+ backing_store = BackingStoreManager::GetBackingStore(this, current_size_);
+ }
+ }
+
+ return backing_store;
+}
+
+BackingStore* RenderWidgetHost::AllocBackingStore(const gfx::Size& size) {
+ if (!view_)
+ return NULL;
+ return view_->AllocBackingStore(size);
+}
+
+void RenderWidgetHost::DonePaintingToBackingStore() {
+ Send(new ViewMsg_UpdateRect_ACK(routing_id()));
+}
+
+void RenderWidgetHost::ScheduleComposite() {
+ if (is_hidden_ || !is_accelerated_compositing_active_) {
+ return;
+ }
+
+ // Send out a request to the renderer to paint the view if required.
+ if (!repaint_ack_pending_ && !resize_ack_pending_ && !view_being_painted_) {
+ repaint_start_time_ = TimeTicks::Now();
+ repaint_ack_pending_ = true;
+ Send(new ViewMsg_Repaint(routing_id_, current_size_));
+ }
+
+ // When we have asked the RenderWidget to resize, and we are still waiting on
+ // a response, block for a little while to see if we can't get a response.
+ // We always block on response because we do not have a backing store.
+ IPC::Message msg;
+ TimeDelta max_delay = TimeDelta::FromMilliseconds(kPaintMsgTimeoutMS);
+ if (process_->WaitForUpdateMsg(routing_id_, max_delay, &msg))
+ OnMessageReceived(msg);
+}
+
+void RenderWidgetHost::StartHangMonitorTimeout(TimeDelta delay) {
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableHangMonitor)) {
+ return;
+ }
+
+ // If we already have a timer that will expire at or before the given delay,
+ // then we have nothing more to do now. If we have set our end time to null
+ // by calling StopHangMonitorTimeout, though, we will need to restart the
+ // timer.
+ if (hung_renderer_timer_.IsRunning() &&
+ hung_renderer_timer_.GetCurrentDelay() <= delay &&
+ !time_when_considered_hung_.is_null()) {
+ return;
+ }
+
+ // Either the timer is not yet running, or we need to adjust the timer to
+ // fire sooner.
+ time_when_considered_hung_ = Time::Now() + delay;
+ hung_renderer_timer_.Stop();
+ hung_renderer_timer_.Start(delay, this,
+ &RenderWidgetHost::CheckRendererIsUnresponsive);
+}
+
+void RenderWidgetHost::RestartHangMonitorTimeout() {
+ // Setting to null will cause StartHangMonitorTimeout to restart the timer.
+ time_when_considered_hung_ = Time();
+ StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kHungRendererDelayMs));
+}
+
+void RenderWidgetHost::StopHangMonitorTimeout() {
+ time_when_considered_hung_ = Time();
+ RendererIsResponsive();
+
+ // We do not bother to stop the hung_renderer_timer_ here in case it will be
+ // started again shortly, which happens to be the common use case.
+}
+
+void RenderWidgetHost::SystemThemeChanged() {
+ Send(new ViewMsg_ThemeChanged(routing_id_));
+}
+
+void RenderWidgetHost::ForwardMouseEvent(const WebMouseEvent& mouse_event) {
+ if (ignore_input_events_ || process_->ignore_input_events())
+ return;
+
+ // Avoid spamming the renderer with mouse move events. It is important
+ // to note that WM_MOUSEMOVE events are anyways synthetic, but since our
+ // thread is able to rapidly consume WM_MOUSEMOVE events, we may get way
+ // more WM_MOUSEMOVE events than we wish to send to the renderer.
+ if (mouse_event.type == WebInputEvent::MouseMove) {
+ if (mouse_move_pending_) {
+ next_mouse_move_.reset(new WebMouseEvent(mouse_event));
+ return;
+ }
+ mouse_move_pending_ = true;
+ } else if (mouse_event.type == WebInputEvent::MouseDown) {
+ OnUserGesture();
+ }
+
+ ForwardInputEvent(mouse_event, sizeof(WebMouseEvent), false);
+}
+
+void RenderWidgetHost::OnMouseActivate() {
+}
+
+void RenderWidgetHost::ForwardWheelEvent(
+ const WebMouseWheelEvent& wheel_event) {
+ if (ignore_input_events_ || process_->ignore_input_events())
+ return;
+
+ // If there's already a mouse wheel event waiting to be sent to the renderer,
+ // add the new deltas to that event. Not doing so (e.g., by dropping the old
+ // event, as for mouse moves) results in very slow scrolling on the Mac (on
+ // which many, very small wheel events are sent).
+ if (mouse_wheel_pending_) {
+ if (coalesced_mouse_wheel_events_.empty() ||
+ coalesced_mouse_wheel_events_.back().modifiers
+ != wheel_event.modifiers ||
+ coalesced_mouse_wheel_events_.back().scrollByPage
+ != wheel_event.scrollByPage) {
+ coalesced_mouse_wheel_events_.push_back(wheel_event);
+ } else {
+ WebMouseWheelEvent* last_wheel_event =
+ &coalesced_mouse_wheel_events_.back();
+ last_wheel_event->deltaX += wheel_event.deltaX;
+ last_wheel_event->deltaY += wheel_event.deltaY;
+ DCHECK_GE(wheel_event.timeStampSeconds,
+ last_wheel_event->timeStampSeconds);
+ last_wheel_event->timeStampSeconds = wheel_event.timeStampSeconds;
+ }
+ return;
+ }
+ mouse_wheel_pending_ = true;
+
+ HISTOGRAM_COUNTS_100("MPArch.RWH_WheelQueueSize",
+ coalesced_mouse_wheel_events_.size());
+
+ ForwardInputEvent(wheel_event, sizeof(WebMouseWheelEvent), false);
+}
+
+void RenderWidgetHost::ForwardKeyboardEvent(
+ const NativeWebKeyboardEvent& key_event) {
+ if (ignore_input_events_ || process_->ignore_input_events())
+ return;
+
+ if (key_event.type == WebKeyboardEvent::Char &&
+ (key_event.windowsKeyCode == ui::VKEY_RETURN ||
+ key_event.windowsKeyCode == ui::VKEY_SPACE)) {
+ OnUserGesture();
+ }
+
+ // Double check the type to make sure caller hasn't sent us nonsense that
+ // will mess up our key queue.
+ if (WebInputEvent::isKeyboardEventType(key_event.type)) {
+ if (suppress_next_char_events_) {
+ // If preceding RawKeyDown event was handled by the browser, then we need
+ // suppress all Char events generated by it. Please note that, one
+ // RawKeyDown event may generate multiple Char events, so we can't reset
+ // |suppress_next_char_events_| until we get a KeyUp or a RawKeyDown.
+ if (key_event.type == WebKeyboardEvent::Char)
+ return;
+ // We get a KeyUp or a RawKeyDown event.
+ suppress_next_char_events_ = false;
+ }
+
+ bool is_keyboard_shortcut = false;
+ // Only pre-handle the key event if it's not handled by the input method.
+ if (!key_event.skip_in_browser) {
+ // We need to set |suppress_next_char_events_| to true if
+ // PreHandleKeyboardEvent() returns true, but |this| may already be
+ // destroyed at that time. So set |suppress_next_char_events_| true here,
+ // then revert it afterwards when necessary.
+ if (key_event.type == WebKeyboardEvent::RawKeyDown)
+ suppress_next_char_events_ = true;
+
+ // Tab switching/closing accelerators aren't sent to the renderer to avoid
+ // a hung/malicious renderer from interfering.
+ if (PreHandleKeyboardEvent(key_event, &is_keyboard_shortcut))
+ return;
+
+ if (key_event.type == WebKeyboardEvent::RawKeyDown)
+ suppress_next_char_events_ = false;
+ }
+
+ // Don't add this key to the queue if we have no way to send the message...
+ if (!process_->HasConnection())
+ return;
+
+ // Put all WebKeyboardEvent objects in a queue since we can't trust the
+ // renderer and we need to give something to the UnhandledInputEvent
+ // handler.
+ key_queue_.push_back(key_event);
+ HISTOGRAM_COUNTS_100("Renderer.KeyboardQueueSize", key_queue_.size());
+
+ // Only forward the non-native portions of our event.
+ ForwardInputEvent(key_event, sizeof(WebKeyboardEvent),
+ is_keyboard_shortcut);
+ }
+}
+
+void RenderWidgetHost::ForwardInputEvent(const WebInputEvent& input_event,
+ int event_size,
+ bool is_keyboard_shortcut) {
+ if (!process_->HasConnection())
+ return;
+
+ DCHECK(!process_->ignore_input_events());
+
+ IPC::Message* message = new ViewMsg_HandleInputEvent(routing_id_);
+ message->WriteData(
+ reinterpret_cast<const char*>(&input_event), event_size);
+ // |is_keyboard_shortcut| only makes sense for RawKeyDown events.
+ if (input_event.type == WebInputEvent::RawKeyDown)
+ message->WriteBool(is_keyboard_shortcut);
+ input_event_start_time_ = TimeTicks::Now();
+ Send(message);
+
+ // Any non-wheel input event cancels pending wheel events.
+ if (input_event.type != WebInputEvent::MouseWheel)
+ coalesced_mouse_wheel_events_.clear();
+
+ // Any input event cancels a pending mouse move event. Note that
+ // |next_mouse_move_| possibly owns |input_event|, so don't use |input_event|
+ // after this line.
+ next_mouse_move_.reset();
+
+ StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kHungRendererDelayMs));
+}
+
+void RenderWidgetHost::ForwardEditCommand(const std::string& name,
+ const std::string& value) {
+ // We don't need an implementation of this function here since the
+ // only place we use this is for the case of dropdown menus and other
+ // edge cases for which edit commands don't make sense.
+}
+
+void RenderWidgetHost::ForwardEditCommandsForNextKeyEvent(
+ const EditCommands& edit_commands) {
+ // We don't need an implementation of this function here since this message is
+ // only handled by RenderView.
+}
+
+#if defined(TOUCH_UI)
+void RenderWidgetHost::ForwardTouchEvent(
+ const WebKit::WebTouchEvent& touch_event) {
+ ForwardInputEvent(touch_event, sizeof(WebKit::WebTouchEvent), false);
+}
+#endif
+
+void RenderWidgetHost::RendererExited(base::TerminationStatus status,
+ int exit_code) {
+ // Clearing this flag causes us to re-create the renderer when recovering
+ // from a crashed renderer.
+ renderer_initialized_ = false;
+
+ // Must reset these to ensure that mouse move/wheel events work with a new
+ // renderer.
+ mouse_move_pending_ = false;
+ next_mouse_move_.reset();
+ mouse_wheel_pending_ = false;
+ coalesced_mouse_wheel_events_.clear();
+
+ // Must reset these to ensure that keyboard events work with a new renderer.
+ key_queue_.clear();
+ suppress_next_char_events_ = false;
+
+ // Reset some fields in preparation for recovering from a crash.
+ resize_ack_pending_ = false;
+ repaint_ack_pending_ = false;
+
+ in_flight_size_.SetSize(0, 0);
+ in_flight_reserved_rect_.SetRect(0, 0, 0, 0);
+ current_size_.SetSize(0, 0);
+ current_reserved_rect_.SetRect(0, 0, 0, 0);
+ is_hidden_ = false;
+ is_accelerated_compositing_active_ = false;
+
+ if (view_) {
+ view_->RenderViewGone(status, exit_code);
+ view_ = NULL; // The View should be deleted by RenderViewGone.
+ }
+
+ BackingStoreManager::RemoveBackingStore(this);
+}
+
+void RenderWidgetHost::UpdateTextDirection(WebTextDirection direction) {
+ text_direction_updated_ = true;
+ text_direction_ = direction;
+}
+
+void RenderWidgetHost::CancelUpdateTextDirection() {
+ if (text_direction_updated_)
+ text_direction_canceled_ = true;
+}
+
+void RenderWidgetHost::NotifyTextDirection() {
+ if (text_direction_updated_) {
+ if (!text_direction_canceled_)
+ Send(new ViewMsg_SetTextDirection(routing_id(), text_direction_));
+ text_direction_updated_ = false;
+ text_direction_canceled_ = false;
+ }
+}
+
+void RenderWidgetHost::SetInputMethodActive(bool activate) {
+ Send(new ViewMsg_SetInputMethodActive(routing_id(), activate));
+}
+
+void RenderWidgetHost::ImeSetComposition(
+ const string16& text,
+ const std::vector<WebKit::WebCompositionUnderline>& underlines,
+ int selection_start,
+ int selection_end) {
+ Send(new ViewMsg_ImeSetComposition(
+ routing_id(), text, underlines, selection_start, selection_end));
+}
+
+void RenderWidgetHost::ImeConfirmComposition(const string16& text) {
+ Send(new ViewMsg_ImeConfirmComposition(routing_id(), text));
+}
+
+void RenderWidgetHost::ImeConfirmComposition() {
+ Send(new ViewMsg_ImeConfirmComposition(routing_id(), string16()));
+}
+
+void RenderWidgetHost::ImeCancelComposition() {
+ Send(new ViewMsg_ImeSetComposition(routing_id(), string16(),
+ std::vector<WebKit::WebCompositionUnderline>(), 0, 0));
+}
+
+void RenderWidgetHost::SetActive(bool active) {
+ Send(new ViewMsg_SetActive(routing_id(), active));
+}
+
+void RenderWidgetHost::Destroy() {
+ NotificationService::current()->Notify(
+ NotificationType::RENDER_WIDGET_HOST_DESTROYED,
+ Source<RenderWidgetHost>(this),
+ NotificationService::NoDetails());
+
+ // Tell the view to die.
+ // Note that in the process of the view shutting down, it can call a ton
+ // of other messages on us. So if you do any other deinitialization here,
+ // do it after this call to view_->Destroy().
+ if (view_)
+ view_->Destroy();
+
+ delete this;
+}
+
+void RenderWidgetHost::CheckRendererIsUnresponsive() {
+ // If we received a call to StopHangMonitorTimeout.
+ if (time_when_considered_hung_.is_null())
+ return;
+
+ // If we have not waited long enough, then wait some more.
+ Time now = Time::Now();
+ if (now < time_when_considered_hung_) {
+ StartHangMonitorTimeout(time_when_considered_hung_ - now);
+ return;
+ }
+
+ // OK, looks like we have a hung renderer!
+ NotificationService::current()->Notify(
+ NotificationType::RENDERER_PROCESS_HANG,
+ Source<RenderWidgetHost>(this),
+ NotificationService::NoDetails());
+ is_unresponsive_ = true;
+ NotifyRendererUnresponsive();
+}
+
+void RenderWidgetHost::RendererIsResponsive() {
+ if (is_unresponsive_) {
+ is_unresponsive_ = false;
+ NotifyRendererResponsive();
+ }
+}
+
+void RenderWidgetHost::OnMsgRenderViewReady() {
+ WasResized();
+}
+
+void RenderWidgetHost::OnMsgRenderViewGone(int status, int exit_code) {
+ // TODO(evanm): This synchronously ends up calling "delete this".
+ // Is that really what we want in response to this message? I'm matching
+ // previous behavior of the code here.
+ Destroy();
+}
+
+void RenderWidgetHost::OnMsgClose() {
+ Shutdown();
+}
+
+void RenderWidgetHost::OnMsgRequestMove(const gfx::Rect& pos) {
+ // Note that we ignore the position.
+ if (view_) {
+ view_->SetSize(pos.size());
+ Send(new ViewMsg_Move_ACK(routing_id_));
+ }
+}
+
+void RenderWidgetHost::OnMsgPaintAtSizeAck(int tag, const gfx::Size& size) {
+ PaintAtSizeAckDetails details = {tag, size};
+ gfx::Size size_details = size;
+ NotificationService::current()->Notify(
+ NotificationType::RENDER_WIDGET_HOST_DID_RECEIVE_PAINT_AT_SIZE_ACK,
+ Source<RenderWidgetHost>(this),
+ Details<PaintAtSizeAckDetails>(&details));
+}
+
+void RenderWidgetHost::OnMsgUpdateRect(
+ const ViewHostMsg_UpdateRect_Params& params) {
+ TimeTicks paint_start = TimeTicks::Now();
+
+ NotificationService::current()->Notify(
+ NotificationType::RENDER_WIDGET_HOST_WILL_PAINT,
+ Source<RenderWidgetHost>(this),
+ NotificationService::NoDetails());
+
+ // Update our knowledge of the RenderWidget's size.
+ current_size_ = params.view_size;
+ // Update our knowledge of the RenderWidget's scroll offset.
+ last_scroll_offset_ = params.scroll_offset;
+
+ bool is_resize_ack =
+ ViewHostMsg_UpdateRect_Flags::is_resize_ack(params.flags);
+
+ // resize_ack_pending_ needs to be cleared before we call DidPaintRect, since
+ // that will end up reaching GetBackingStore.
+ if (is_resize_ack) {
+ DCHECK(resize_ack_pending_);
+ resize_ack_pending_ = false;
+ in_flight_size_.SetSize(0, 0);
+ in_flight_reserved_rect_.SetRect(0, 0, 0, 0);
+ // Update our knowledge of the RenderWidget's resizer rect.
+ // ViewMsg_Resize is acknowledged only when view size is actually changed,
+ // otherwise current_reserved_rect_ is updated immediately after sending
+ // ViewMsg_Resize to the RenderWidget and can be clobbered by
+ // OnMsgUpdateRect called for a paint that was initiated before the resize
+ // message was sent.
+ current_reserved_rect_ = params.resizer_rect;
+ }
+
+ bool is_repaint_ack =
+ ViewHostMsg_UpdateRect_Flags::is_repaint_ack(params.flags);
+ if (is_repaint_ack) {
+ repaint_ack_pending_ = false;
+ TimeDelta delta = TimeTicks::Now() - repaint_start_time_;
+ UMA_HISTOGRAM_TIMES("MPArch.RWH_RepaintDelta", delta);
+ }
+
+ DCHECK(!params.bitmap_rect.IsEmpty());
+ DCHECK(!params.view_size.IsEmpty());
+
+ if (!is_accelerated_compositing_active_) {
+ const size_t size = params.bitmap_rect.height() *
+ params.bitmap_rect.width() * 4;
+ TransportDIB* dib = process_->GetTransportDIB(params.bitmap);
+
+ // If gpu process does painting, scroll_rect and copy_rects are always empty
+ // and backing store is never used.
+ if (dib) {
+ if (dib->size() < size) {
+ DLOG(WARNING) << "Transport DIB too small for given rectangle";
+ UserMetrics::RecordAction(UserMetricsAction(
+ "BadMessageTerminate_RWH1"));
+ process()->ReceivedBadMessage();
+ } else {
+ // Scroll the backing store.
+ if (!params.scroll_rect.IsEmpty()) {
+ ScrollBackingStoreRect(params.dx, params.dy,
+ params.scroll_rect,
+ params.view_size);
+ }
+
+ // Paint the backing store. This will update it with the
+ // renderer-supplied bits. The view will read out of the backing store
+ // later to actually draw to the screen.
+ PaintBackingStoreRect(params.bitmap, params.bitmap_rect,
+ params.copy_rects, params.view_size);
+ }
+ }
+ }
+
+ // ACK early so we can prefetch the next PaintRect if there is a next one.
+ // This must be done AFTER we're done painting with the bitmap supplied by the
+ // renderer. This ACK is a signal to the renderer that the backing store can
+ // be re-used, so the bitmap may be invalid after this call.
+ Send(new ViewMsg_UpdateRect_ACK(routing_id_));
+
+ // We don't need to update the view if the view is hidden. We must do this
+ // early return after the ACK is sent, however, or the renderer will not send
+ // us more data.
+ if (is_hidden_)
+ return;
+
+ // Now paint the view. Watch out: it might be destroyed already.
+ if (view_) {
+ view_->MovePluginWindows(params.plugin_window_moves);
+ // The view_ pointer could be destroyed in the context of MovePluginWindows
+ // which attempts to move the plugin windows and in the process could
+ // dispatch other window messages which could cause the view to be
+ // destroyed.
+ if (view_ && !is_accelerated_compositing_active_) {
+ view_being_painted_ = true;
+ view_->DidUpdateBackingStore(params.scroll_rect, params.dx, params.dy,
+ params.copy_rects);
+ view_being_painted_ = false;
+ }
+ }
+
+ NotificationService::current()->Notify(
+ NotificationType::RENDER_WIDGET_HOST_DID_PAINT,
+ Source<RenderWidgetHost>(this),
+ NotificationService::NoDetails());
+
+ // If we got a resize ack, then perhaps we have another resize to send?
+ if (is_resize_ack && view_) {
+ // WasResized checks the current size and sends the resize update only
+ // when something was actually changed.
+ WasResized();
+ }
+
+ NotificationService::current()->Notify(
+ NotificationType::RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
+ Source<RenderWidgetHost>(this),
+ NotificationService::NoDetails());
+
+ // Log the time delta for processing a paint message.
+ TimeDelta delta = TimeTicks::Now() - paint_start;
+ UMA_HISTOGRAM_TIMES("MPArch.RWH_OnMsgUpdateRect", delta);
+}
+
+void RenderWidgetHost::OnMsgInputEventAck(const IPC::Message& message) {
+ // Log the time delta for processing an input event.
+ TimeDelta delta = TimeTicks::Now() - input_event_start_time_;
+ UMA_HISTOGRAM_TIMES("MPArch.RWH_InputEventDelta", delta);
+
+ // Cancel pending hung renderer checks since the renderer is responsive.
+ StopHangMonitorTimeout();
+
+ void* iter = NULL;
+ int type = 0;
+ if (!message.ReadInt(&iter, &type) || (type < WebInputEvent::Undefined)) {
+ UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_RWH2"));
+ process()->ReceivedBadMessage();
+ } else if (type == WebInputEvent::MouseMove) {
+ mouse_move_pending_ = false;
+
+ // now, we can send the next mouse move event
+ if (next_mouse_move_.get()) {
+ DCHECK(next_mouse_move_->type == WebInputEvent::MouseMove);
+ ForwardMouseEvent(*next_mouse_move_);
+ }
+ } else if (type == WebInputEvent::MouseWheel) {
+ ProcessWheelAck();
+ } else if (WebInputEvent::isKeyboardEventType(type)) {
+ bool processed = false;
+ if (!message.ReadBool(&iter, &processed)) {
+ UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_RWH3"));
+ process()->ReceivedBadMessage();
+ }
+
+ ProcessKeyboardEventAck(type, processed);
+ }
+ // This is used only for testing.
+ NotificationService::current()->Notify(
+ NotificationType::RENDER_WIDGET_HOST_DID_RECEIVE_INPUT_EVENT_ACK,
+ Source<RenderWidgetHost>(this),
+ Details<int>(&type));
+}
+
+void RenderWidgetHost::ProcessWheelAck() {
+ mouse_wheel_pending_ = false;
+
+ // Now send the next (coalesced) mouse wheel event.
+ if (!coalesced_mouse_wheel_events_.empty()) {
+ WebMouseWheelEvent next_wheel_event =
+ coalesced_mouse_wheel_events_.front();
+ coalesced_mouse_wheel_events_.pop_front();
+ ForwardWheelEvent(next_wheel_event);
+ }
+}
+
+void RenderWidgetHost::OnMsgFocus() {
+ // Only RenderViewHost can deal with that message.
+ UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_RWH4"));
+ process()->ReceivedBadMessage();
+}
+
+void RenderWidgetHost::OnMsgBlur() {
+ // Only RenderViewHost can deal with that message.
+ UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_RWH5"));
+ process()->ReceivedBadMessage();
+}
+
+void RenderWidgetHost::OnMsgSetCursor(const WebCursor& cursor) {
+ if (!view_) {
+ return;
+ }
+ view_->UpdateCursor(cursor);
+}
+
+void RenderWidgetHost::OnMsgImeUpdateTextInputState(
+ WebKit::WebTextInputType type,
+ const gfx::Rect& caret_rect) {
+ if (view_)
+ view_->ImeUpdateTextInputState(type, caret_rect);
+}
+
+void RenderWidgetHost::OnMsgImeCancelComposition() {
+ if (view_)
+ view_->ImeCancelComposition();
+}
+
+void RenderWidgetHost::OnMsgDidActivateAcceleratedCompositing(bool activated) {
+#if defined(OS_MACOSX)
+ bool old_state = is_accelerated_compositing_active_;
+#endif
+ is_accelerated_compositing_active_ = activated;
+#if defined(OS_MACOSX)
+ if (old_state != is_accelerated_compositing_active_ && view_)
+ view_->GpuRenderingStateDidChange();
+#elif defined(OS_WIN)
+ if (view_)
+ view_->ShowCompositorHostWindow(is_accelerated_compositing_active_);
+#elif defined(TOOLKIT_USES_GTK)
+ if (view_)
+ view_->AcceleratedCompositingActivated(activated);
+#endif
+}
+
+#if defined(OS_MACOSX)
+
+void RenderWidgetHost::OnMsgGetScreenInfo(gfx::NativeViewId view,
+ WebScreenInfo* results) {
+ gfx::NativeView native_view = view_ ? view_->GetNativeView() : NULL;
+ *results = WebScreenInfoFactory::screenInfo(native_view);
+}
+
+void RenderWidgetHost::OnMsgGetWindowRect(gfx::NativeViewId window_id,
+ gfx::Rect* results) {
+ if (view_) {
+ *results = view_->GetViewBounds();
+ }
+}
+
+void RenderWidgetHost::OnMsgGetRootWindowRect(gfx::NativeViewId window_id,
+ gfx::Rect* results) {
+ if (view_) {
+ *results = view_->GetRootWindowRect();
+ }
+}
+
+void RenderWidgetHost::OnMsgPluginFocusChanged(bool focused, int plugin_id) {
+ if (view_)
+ view_->PluginFocusChanged(focused, plugin_id);
+}
+
+void RenderWidgetHost::OnMsgStartPluginIme() {
+ if (view_)
+ view_->StartPluginIme();
+}
+
+void RenderWidgetHost::OnAllocateFakePluginWindowHandle(
+ bool opaque,
+ bool root,
+ gfx::PluginWindowHandle* id) {
+ // TODO(kbr): similar potential issue here as in OnMsgCreatePluginContainer.
+ // Possibly less of an issue because this is only used for the GPU plugin.
+ if (view_) {
+ *id = view_->AllocateFakePluginWindowHandle(opaque, root);
+ } else {
+ NOTIMPLEMENTED();
+ }
+}
+
+void RenderWidgetHost::OnDestroyFakePluginWindowHandle(
+ gfx::PluginWindowHandle id) {
+ if (view_) {
+ view_->DestroyFakePluginWindowHandle(id);
+ } else {
+ NOTIMPLEMENTED();
+ }
+}
+
+void RenderWidgetHost::OnAcceleratedSurfaceSetIOSurface(
+ gfx::PluginWindowHandle window,
+ int32 width,
+ int32 height,
+ uint64 mach_port) {
+ if (view_) {
+ view_->AcceleratedSurfaceSetIOSurface(window, width, height, mach_port);
+ }
+}
+
+void RenderWidgetHost::OnAcceleratedSurfaceSetTransportDIB(
+ gfx::PluginWindowHandle window,
+ int32 width,
+ int32 height,
+ TransportDIB::Handle transport_dib) {
+ if (view_) {
+ view_->AcceleratedSurfaceSetTransportDIB(window, width, height,
+ transport_dib);
+ }
+}
+
+void RenderWidgetHost::OnAcceleratedSurfaceBuffersSwapped(
+ gfx::PluginWindowHandle window, uint64 surface_id) {
+ if (view_) {
+ // This code path could be updated to implement flow control for
+ // updating of accelerated plugins as well. However, if we add support
+ // for composited plugins then this is not necessary.
+ view_->AcceleratedSurfaceBuffersSwapped(window, surface_id,
+ 0, 0, 0);
+ }
+}
+#elif defined(OS_POSIX)
+
+void RenderWidgetHost::OnMsgCreatePluginContainer(gfx::PluginWindowHandle id) {
+ // TODO(piman): view_ can only be NULL with delayed view creation in
+ // extensions (see ExtensionHost::CreateRenderViewSoon). Figure out how to
+ // support plugins in that case.
+ if (view_) {
+ view_->CreatePluginContainer(id);
+ } else {
+ deferred_plugin_handles_.push_back(id);
+ }
+}
+
+void RenderWidgetHost::OnMsgDestroyPluginContainer(gfx::PluginWindowHandle id) {
+ if (view_) {
+ view_->DestroyPluginContainer(id);
+ } else {
+ for (int i = 0;
+ i < static_cast<int>(deferred_plugin_handles_.size());
+ i++) {
+ if (deferred_plugin_handles_[i] == id) {
+ deferred_plugin_handles_.erase(deferred_plugin_handles_.begin() + i);
+ i--;
+ }
+ }
+ }
+}
+#endif
+
+void RenderWidgetHost::PaintBackingStoreRect(
+ TransportDIB::Id bitmap,
+ const gfx::Rect& bitmap_rect,
+ const std::vector<gfx::Rect>& copy_rects,
+ const gfx::Size& view_size) {
+ // The view may be destroyed already.
+ if (!view_)
+ return;
+
+ if (is_hidden_) {
+ // Don't bother updating the backing store when we're hidden. Just mark it
+ // as being totally invalid. This will cause a complete repaint when the
+ // view is restored.
+ needs_repainting_on_restore_ = true;
+ return;
+ }
+
+ bool needs_full_paint = false;
+ BackingStoreManager::PrepareBackingStore(this, view_size, bitmap, bitmap_rect,
+ copy_rects, &needs_full_paint);
+ if (needs_full_paint) {
+ repaint_start_time_ = TimeTicks::Now();
+ repaint_ack_pending_ = true;
+ Send(new ViewMsg_Repaint(routing_id_, view_size));
+ }
+}
+
+void RenderWidgetHost::ScrollBackingStoreRect(int dx, int dy,
+ const gfx::Rect& clip_rect,
+ const gfx::Size& view_size) {
+ if (is_hidden_) {
+ // Don't bother updating the backing store when we're hidden. Just mark it
+ // as being totally invalid. This will cause a complete repaint when the
+ // view is restored.
+ needs_repainting_on_restore_ = true;
+ return;
+ }
+
+ // TODO(darin): do we need to do something else if our backing store is not
+ // the same size as the advertised view? maybe we just assume there is a
+ // full paint on its way?
+ BackingStore* backing_store = BackingStoreManager::Lookup(this);
+ if (!backing_store || (backing_store->size() != view_size))
+ return;
+ backing_store->ScrollBackingStore(dx, dy, clip_rect, view_size);
+}
+
+void RenderWidgetHost::ToggleSpellPanel(bool is_currently_visible) {
+ Send(new ViewMsg_ToggleSpellPanel(routing_id(), is_currently_visible));
+}
+
+void RenderWidgetHost::Replace(const string16& word) {
+ Send(new ViewMsg_Replace(routing_id_, word));
+}
+
+void RenderWidgetHost::AdvanceToNextMisspelling() {
+ Send(new ViewMsg_AdvanceToNextMisspelling(routing_id_));
+}
+
+void RenderWidgetHost::EnableRendererAccessibility() {
+ if (renderer_accessible_)
+ return;
+
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableRendererAccessibility)) {
+ return;
+ }
+
+ renderer_accessible_ = true;
+
+ if (process_->HasConnection()) {
+ // Renderer accessibility wasn't enabled on process launch. Enable it now.
+ Send(new ViewMsg_EnableAccessibility(routing_id()));
+ }
+}
+
+void RenderWidgetHost::SetAccessibilityFocus(int acc_obj_id) {
+ Send(new ViewMsg_SetAccessibilityFocus(routing_id(), acc_obj_id));
+}
+
+void RenderWidgetHost::AccessibilityDoDefaultAction(int acc_obj_id) {
+ Send(new ViewMsg_AccessibilityDoDefaultAction(routing_id(), acc_obj_id));
+}
+
+void RenderWidgetHost::AccessibilityNotificationsAck() {
+ Send(new ViewMsg_AccessibilityNotifications_ACK(routing_id()));
+}
+
+void RenderWidgetHost::ProcessKeyboardEventAck(int type, bool processed) {
+ if (key_queue_.size() == 0) {
+ LOG(ERROR) << "Got a KeyEvent back from the renderer but we "
+ << "don't seem to have sent it to the renderer!";
+ } else if (key_queue_.front().type != type) {
+ LOG(ERROR) << "We seem to have a different key type sent from "
+ << "the renderer. (" << key_queue_.front().type << " vs. "
+ << type << "). Ignoring event.";
+
+ // Something must be wrong. Clear the |key_queue_| and
+ // |suppress_next_char_events_| so that we can resume from the error.
+ key_queue_.clear();
+ suppress_next_char_events_ = false;
+ } else {
+ NativeWebKeyboardEvent front_item = key_queue_.front();
+ key_queue_.pop_front();
+
+#if defined(OS_MACOSX)
+ if (!is_hidden_ && view_->PostProcessEventForPluginIme(front_item))
+ return;
+#endif
+
+ // We only send unprocessed key event upwards if we are not hidden,
+ // because the user has moved away from us and no longer expect any effect
+ // of this key event.
+ if (!processed && !is_hidden_ && !front_item.skip_in_browser) {
+ UnhandledKeyboardEvent(front_item);
+
+ // WARNING: This RenderWidgetHost can be deallocated at this point
+ // (i.e. in the case of Ctrl+W, where the call to
+ // UnhandledKeyboardEvent destroys this RenderWidgetHost).
+ }
+ }
+}
+
+void RenderWidgetHost::ActivateDeferredPluginHandles() {
+ if (view_ == NULL)
+ return;
+
+ for (int i = 0; i < static_cast<int>(deferred_plugin_handles_.size()); i++) {
+#if defined(TOOLKIT_USES_GTK)
+ view_->CreatePluginContainer(deferred_plugin_handles_[i]);
+#endif
+ }
+
+ deferred_plugin_handles_.clear();
+}
diff --git a/content/browser/renderer_host/render_widget_host.h b/content/browser/renderer_host/render_widget_host.h
new file mode 100644
index 0000000..d3598dd
--- /dev/null
+++ b/content/browser/renderer_host/render_widget_host.h
@@ -0,0 +1,677 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_H_
+#define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_H_
+#pragma once
+
+#include <deque>
+#include <string>
+#include <vector>
+
+#include "app/surface/transport_dib.h"
+#include "base/gtest_prod_util.h"
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+#include "base/string16.h"
+#include "base/timer.h"
+#include "chrome/common/edit_command.h"
+#include "chrome/common/native_web_keyboard_event.h"
+#include "chrome/common/property_bag.h"
+#include "ipc/ipc_channel.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebTextDirection.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebTextInputType.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace WebKit {
+class WebInputEvent;
+class WebMouseEvent;
+struct WebCompositionUnderline;
+struct WebScreenInfo;
+}
+
+class BackingStore;
+class PaintObserver;
+class RenderProcessHost;
+class RenderWidgetHostView;
+class TransportDIB;
+class WebCursor;
+struct ViewHostMsg_UpdateRect_Params;
+
+// This class manages the browser side of a browser<->renderer HWND connection.
+// The HWND lives in the browser process, and windows events are sent over
+// IPC to the corresponding object in the renderer. The renderer paints into
+// shared memory, which we transfer to a backing store and blit to the screen
+// when Windows sends us a WM_PAINT message.
+//
+// How Shutdown Works
+//
+// There are two situations in which this object, a RenderWidgetHost, can be
+// instantiated:
+//
+// 1. By a TabContents as the communication conduit for a rendered web page.
+// The TabContents instantiates a derived class: RenderViewHost.
+// 2. By a TabContents as the communication conduit for a select widget. The
+// TabContents instantiates the RenderWidgetHost directly.
+//
+// For every TabContents there are several objects in play that need to be
+// properly destroyed or cleaned up when certain events occur.
+//
+// - TabContents - the TabContents itself, and its associated HWND.
+// - RenderViewHost - representing the communication conduit with the child
+// process.
+// - RenderWidgetHostView - the view of the web page content, message handler,
+// and plugin root.
+//
+// Normally, the TabContents contains a child RenderWidgetHostView that renders
+// the contents of the loaded page. It has a WS_CLIPCHILDREN style so that it
+// does no painting of its own.
+//
+// The lifetime of the RenderWidgetHostView is tied to the render process. If
+// the render process dies, the RenderWidgetHostView goes away and all
+// references to it must become NULL. If the TabContents finds itself without a
+// RenderWidgetHostView, it paints Sad Tab instead.
+//
+// RenderViewHost (a RenderWidgetHost subclass) is the conduit used to
+// communicate with the RenderView and is owned by the TabContents. If the
+// render process crashes, the RenderViewHost remains and restarts the render
+// process if needed to continue navigation.
+//
+// The TabContents is itself owned by the NavigationController in which it
+// resides.
+//
+// Some examples of how shutdown works:
+//
+// When a tab is closed (either by the user, the web page calling window.close,
+// etc) the TabStrip destroys the associated NavigationController, which calls
+// Destroy on each TabContents it owns.
+//
+// For a TabContents, its Destroy method tells the RenderViewHost to
+// shut down the render process and die.
+//
+// When the render process is destroyed it destroys the View: the
+// RenderWidgetHostView, which destroys its HWND and deletes that object.
+//
+// For select popups, the situation is a little different. The RenderWidgetHost
+// associated with the select popup owns the view and itself (is responsible
+// for destroying itself when the view is closed). The TabContents's only
+// responsibility is to select popups is to create them when it is told to. When
+// the View is destroyed via an IPC message (for when WebCore destroys the
+// popup, e.g. if the user selects one of the options), or because
+// WM_CANCELMODE is received by the view, the View schedules the destruction of
+// the render process. However in this case since there's no TabContents
+// container, when the render process is destroyed, the RenderWidgetHost just
+// deletes itself, which is safe because no one else should have any references
+// to it (the TabContents does not).
+//
+// It should be noted that the RenderViewHost, not the RenderWidgetHost,
+// handles IPC messages relating to the render process going away, since the
+// way a RenderViewHost (TabContents) handles the process dying is different to
+// the way a select popup does. As such the RenderWidgetHostView handles these
+// messages for select popups. This placement is more out of convenience than
+// anything else. When the view is live, these messages are forwarded to it by
+// the RenderWidgetHost's IPC message map.
+//
+class RenderWidgetHost : public IPC::Channel::Listener,
+ public IPC::Channel::Sender {
+ public:
+ // Used as the details object for a
+ // RENDER_WIDGET_HOST_DID_RECEIVE_PAINT_AT_SIZE_ACK notification.
+ struct PaintAtSizeAckDetails {
+ // The tag that was passed to the PaintAtSize() call that triggered this
+ // ack.
+ int tag;
+ gfx::Size size;
+ };
+
+ // routing_id can be MSG_ROUTING_NONE, in which case the next available
+ // routing id is taken from the RenderProcessHost.
+ RenderWidgetHost(RenderProcessHost* process, int routing_id);
+ virtual ~RenderWidgetHost();
+
+ // Gets/Sets the View of this RenderWidgetHost. Can be NULL, e.g. if the
+ // RenderWidget is being destroyed or the render process crashed. You should
+ // never cache this pointer since it can become NULL if the renderer crashes,
+ // instead you should always ask for it using the accessor.
+ void set_view(RenderWidgetHostView* view) { view_ = view; }
+ RenderWidgetHostView* view() const { return view_; }
+
+ RenderProcessHost* process() const { return process_; }
+ int routing_id() const { return routing_id_; }
+ bool renderer_accessible() { return renderer_accessible_; }
+
+ // Returns the property bag for this widget, where callers can add extra data
+ // they may wish to associate with it. Returns a pointer rather than a
+ // reference since the PropertyAccessors expect this.
+ const PropertyBag* property_bag() const { return &property_bag_; }
+ PropertyBag* property_bag() { return &property_bag_; }
+
+ // Called when a renderer object already been created for this host, and we
+ // just need to be attached to it. Used for window.open, <select> dropdown
+ // menus, and other times when the renderer initiates creating an object.
+ void Init();
+
+ // Tells the renderer to die and then calls Destroy().
+ virtual void Shutdown();
+
+ // Manual RTTI FTW. We are not hosting a web page.
+ virtual bool IsRenderView() const;
+
+ // IPC::Channel::Listener
+ virtual bool OnMessageReceived(const IPC::Message& msg);
+
+ // Sends a message to the corresponding object in the renderer.
+ virtual bool Send(IPC::Message* msg);
+
+ // Called to notify the RenderWidget that it has been hidden or restored from
+ // having been hidden.
+ void WasHidden();
+ void WasRestored();
+
+ // Called to notify the RenderWidget that it has been resized.
+ void WasResized();
+
+ // Called to notify the RenderWidget that its associated native window got
+ // focused.
+ virtual void GotFocus();
+
+ // Tells the renderer it got/lost focus.
+ void Focus();
+ void Blur();
+ virtual void LostCapture();
+
+ // Tells us whether the page is rendered directly via the GPU process.
+ bool is_accelerated_compositing_active() {
+ return is_accelerated_compositing_active_;
+ }
+
+ // Notifies the RenderWidgetHost that the View was destroyed.
+ void ViewDestroyed();
+
+ // Indicates if the page has finished loading.
+ void SetIsLoading(bool is_loading);
+
+ // This tells the renderer to paint into a bitmap and return it,
+ // regardless of whether the tab is hidden or not. It resizes the
+ // web widget to match the |page_size| and then returns the bitmap
+ // scaled so it matches the |desired_size|, so that the scaling
+ // happens on the rendering thread. When the bitmap is ready, the
+ // renderer sends a PaintAtSizeACK to this host, and a
+ // RENDER_WIDGET_HOST_DID_RECEIVE_PAINT_AT_SIZE_ACK notification is issued.
+ // Note that this bypasses most of the update logic that is normally invoked,
+ // and doesn't put the results into the backing store.
+ void PaintAtSize(TransportDIB::Handle dib_handle,
+ int tag,
+ const gfx::Size& page_size,
+ const gfx::Size& desired_size);
+
+ // Get access to the widget's backing store. If a resize is in progress,
+ // then the current size of the backing store may be less than the size of
+ // the widget's view. If you pass |force_create| as true, then the backing
+ // store will be created if it doesn't exist. Otherwise, NULL will be returned
+ // if the backing store doesn't already exist. It will also return NULL if the
+ // backing store could not be created.
+ BackingStore* GetBackingStore(bool force_create);
+
+ // Allocate a new backing store of the given size. Returns NULL on failure
+ // (for example, if we don't currently have a RenderWidgetHostView.)
+ BackingStore* AllocBackingStore(const gfx::Size& size);
+
+ // When a backing store does asynchronous painting, it will call this function
+ // when it is done with the DIB. We will then forward a message to the
+ // renderer to send another paint.
+ void DonePaintingToBackingStore();
+
+ // GPU accelerated version of GetBackingStore function. This will
+ // trigger a re-composite to the view. If a resize is pending, it will
+ // block briefly waiting for an ack from the renderer.
+ void ScheduleComposite();
+
+ // Starts a hang monitor timeout. If there's already a hang monitor timeout
+ // the new one will only fire if it has a shorter delay than the time
+ // left on the existing timeouts.
+ void StartHangMonitorTimeout(base::TimeDelta delay);
+
+ // Restart the active hang monitor timeout. Clears all existing timeouts and
+ // starts with a new one. This can be because the renderer has become
+ // active, the tab is being hidden, or the user has chosen to wait some more
+ // to give the tab a chance to become active and we don't want to display a
+ // warning too soon.
+ void RestartHangMonitorTimeout();
+
+ // Stops all existing hang monitor timeouts and assumes the renderer is
+ // responsive.
+ void StopHangMonitorTimeout();
+
+ // Called when the system theme changes. At this time all existing native
+ // theme handles are invalid and the renderer must obtain new ones and
+ // repaint.
+ void SystemThemeChanged();
+
+ // Forwards the given message to the renderer. These are called by the view
+ // when it has received a message.
+ virtual void ForwardMouseEvent(const WebKit::WebMouseEvent& mouse_event);
+ // Called when a mouse click activates the renderer.
+ virtual void OnMouseActivate();
+ void ForwardWheelEvent(const WebKit::WebMouseWheelEvent& wheel_event);
+ virtual void ForwardKeyboardEvent(const NativeWebKeyboardEvent& key_event);
+ virtual void ForwardEditCommand(const std::string& name,
+ const std::string& value);
+ virtual void ForwardEditCommandsForNextKeyEvent(
+ const EditCommands& edit_commands);
+#if defined(TOUCH_UI)
+ virtual void ForwardTouchEvent(const WebKit::WebTouchEvent& touch_event);
+#endif
+
+
+ // Update the text direction of the focused input element and notify it to a
+ // renderer process.
+ // These functions have two usage scenarios: changing the text direction
+ // from a menu (as Safari does), and; changing the text direction when a user
+ // presses a set of keys (as IE and Firefox do).
+ // 1. Change the text direction from a menu.
+ // In this scenario, we receive a menu event only once and we should update
+ // the text direction immediately when a user chooses a menu item. So, we
+ // should call both functions at once as listed in the following snippet.
+ // void RenderViewHost::SetTextDirection(WebTextDirection direction) {
+ // UpdateTextDirection(direction);
+ // NotifyTextDirection();
+ // }
+ // 2. Change the text direction when pressing a set of keys.
+ // Because of auto-repeat, we may receive the same key-press event many
+ // times while we presses the keys and it is nonsense to send the same IPC
+ // message every time when we receive a key-press event.
+ // To suppress the number of IPC messages, we just update the text direction
+ // when receiving a key-press event and send an IPC message when we release
+ // the keys as listed in the following snippet.
+ // if (key_event.type == WebKeyboardEvent::KEY_DOWN) {
+ // if (key_event.windows_key_code == 'A' &&
+ // key_event.modifiers == WebKeyboardEvent::CTRL_KEY) {
+ // UpdateTextDirection(dir);
+ // } else {
+ // CancelUpdateTextDirection();
+ // }
+ // } else if (key_event.type == WebKeyboardEvent::KEY_UP) {
+ // NotifyTextDirection();
+ // }
+ // Once we cancel updating the text direction, we have to ignore all
+ // succeeding UpdateTextDirection() requests until calling
+ // NotifyTextDirection(). (We may receive keydown events even after we
+ // canceled updating the text direction because of auto-repeat.)
+ // Note: we cannot undo this change for compatibility with Firefox and IE.
+ void UpdateTextDirection(WebKit::WebTextDirection direction);
+ void CancelUpdateTextDirection();
+ void NotifyTextDirection();
+
+ // Notifies the renderer whether or not the input method attached to this
+ // process is activated.
+ // When the input method is activated, a renderer process sends IPC messages
+ // to notify the status of its composition node. (This message is mainly used
+ // for notifying the position of the input cursor so that the browser can
+ // display input method windows under the cursor.)
+ void SetInputMethodActive(bool activate);
+
+ // Update the composition node of the renderer (or WebKit).
+ // WebKit has a special node (a composition node) for input method to change
+ // its text without affecting any other DOM nodes. When the input method
+ // (attached to the browser) updates its text, the browser sends IPC messages
+ // to update the composition node of the renderer.
+ // (Read the comments of each function for its detail.)
+
+ // Sets the text of the composition node.
+ // This function can also update the cursor position and mark the specified
+ // range in the composition node.
+ // A browser should call this function:
+ // * when it receives a WM_IME_COMPOSITION message with a GCS_COMPSTR flag
+ // (on Windows);
+ // * when it receives a "preedit_changed" signal of GtkIMContext (on Linux);
+ // * when markedText of NSTextInput is called (on Mac).
+ void ImeSetComposition(
+ const string16& text,
+ const std::vector<WebKit::WebCompositionUnderline>& underlines,
+ int selection_start,
+ int selection_end);
+
+ // Finishes an ongoing composition with the specified text.
+ // A browser should call this function:
+ // * when it receives a WM_IME_COMPOSITION message with a GCS_RESULTSTR flag
+ // (on Windows);
+ // * when it receives a "commit" signal of GtkIMContext (on Linux);
+ // * when insertText of NSTextInput is called (on Mac).
+ void ImeConfirmComposition(const string16& text);
+
+ // Finishes an ongoing composition with the composition text set by last
+ // SetComposition() call.
+ void ImeConfirmComposition();
+
+ // Cancels an ongoing composition.
+ void ImeCancelComposition();
+
+ // Makes an IPC call to toggle the spelling panel.
+ void ToggleSpellPanel(bool is_currently_visible);
+
+ // Makes an IPC call to tell webkit to replace the currently selected word
+ // or a word around the cursor.
+ void Replace(const string16& word);
+
+ // Makes an IPC call to tell webkit to advance to the next misspelling.
+ void AdvanceToNextMisspelling();
+
+ // Enable renderer accessibility. This should only be called when a
+ // screenreader is detected.
+ void EnableRendererAccessibility();
+
+ // Relays a request from assistive technology to set focus to the
+ // node with this accessibility object id.
+ void SetAccessibilityFocus(int acc_obj_id);
+
+ // Relays a request from assistive technology to perform the default action
+ // on a node with this accessibility object id.
+ void AccessibilityDoDefaultAction(int acc_obj_id);
+
+ // Acknowledges a ViewHostMsg_AccessibilityNotifications message.
+ void AccessibilityNotificationsAck();
+
+ // Sets the active state (i.e., control tints).
+ virtual void SetActive(bool active);
+
+ void set_ignore_input_events(bool ignore_input_events) {
+ ignore_input_events_ = ignore_input_events;
+ }
+ bool ignore_input_events() const {
+ return ignore_input_events_;
+ }
+
+ // Activate deferred plugin handles.
+ void ActivateDeferredPluginHandles();
+
+ const gfx::Point& last_scroll_offset() const { return last_scroll_offset_; }
+
+ protected:
+ // Internal implementation of the public Forward*Event() methods.
+ void ForwardInputEvent(const WebKit::WebInputEvent& input_event,
+ int event_size, bool is_keyboard_shortcut);
+
+ // Called when we receive a notification indicating that the renderer
+ // process has gone. This will reset our state so that our state will be
+ // consistent if a new renderer is created.
+ void RendererExited(base::TerminationStatus status, int exit_code);
+
+ // Retrieves an id the renderer can use to refer to its view.
+ // This is used for various IPC messages, including plugins.
+ gfx::NativeViewId GetNativeViewId();
+
+ // Called to handled a keyboard event before sending it to the renderer.
+ // This is overridden by RenderView to send upwards to its delegate.
+ // Returns true if the event was handled, and then the keyboard event will
+ // not be sent to the renderer anymore. Otherwise, if the |event| would
+ // be handled in HandleKeyboardEvent() method as a normal keyboard shortcut,
+ // |*is_keyboard_shortcut| should be set to true.
+ virtual bool PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event,
+ bool* is_keyboard_shortcut);
+
+ // Called when a keyboard event was not processed by the renderer. This is
+ // overridden by RenderView to send upwards to its delegate.
+ virtual void UnhandledKeyboardEvent(const NativeWebKeyboardEvent& event) {}
+
+ // Notification that the user has made some kind of input that could
+ // perform an action. The render view host overrides this to forward the
+ // information to its delegate (see corresponding function in
+ // RenderViewHostDelegate). The gestures that count are 1) any mouse down
+ // event and 2) enter or space key presses.
+ virtual void OnUserGesture() {}
+
+ // Callbacks for notification when the renderer becomes unresponsive to user
+ // input events, and subsequently responsive again. RenderViewHost overrides
+ // these to tell its delegate to show the user a warning.
+ virtual void NotifyRendererUnresponsive() {}
+ virtual void NotifyRendererResponsive() {}
+
+ protected:
+ // true if a renderer has once been valid. We use this flag to display a sad
+ // tab only when we lose our renderer and not if a paint occurs during
+ // initialization.
+ bool renderer_initialized_;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostTest, Resize);
+ FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostTest, ResizeThenCrash);
+ FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostTest, HiddenPaint);
+ FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostTest, PaintAtSize);
+
+ // Tell this object to destroy itself.
+ void Destroy();
+
+ // Checks whether the renderer is hung and calls NotifyRendererUnresponsive
+ // if it is.
+ void CheckRendererIsUnresponsive();
+
+ // Called if we know the renderer is responsive. When we currently think the
+ // renderer is unresponsive, this will clear that state and call
+ // NotifyRendererResponsive.
+ void RendererIsResponsive();
+
+ // IPC message handlers
+ void OnMsgRenderViewReady();
+ void OnMsgRenderViewGone(int status, int error_code);
+ void OnMsgClose();
+ void OnMsgRequestMove(const gfx::Rect& pos);
+ void OnMsgPaintAtSizeAck(int tag, const gfx::Size& size);
+ void OnMsgUpdateRect(const ViewHostMsg_UpdateRect_Params& params);
+ void OnMsgInputEventAck(const IPC::Message& message);
+ virtual void OnMsgFocus();
+ virtual void OnMsgBlur();
+
+ void OnMsgSetCursor(const WebCursor& cursor);
+ void OnMsgImeUpdateTextInputState(WebKit::WebTextInputType type,
+ const gfx::Rect& caret_rect);
+ void OnMsgImeCancelComposition();
+
+ void OnMsgDidActivateAcceleratedCompositing(bool activated);
+
+#if defined(OS_MACOSX)
+ void OnMsgGetScreenInfo(gfx::NativeViewId view,
+ WebKit::WebScreenInfo* results);
+ void OnMsgGetWindowRect(gfx::NativeViewId window_id, gfx::Rect* results);
+ void OnMsgGetRootWindowRect(gfx::NativeViewId window_id, gfx::Rect* results);
+ void OnMsgPluginFocusChanged(bool focused, int plugin_id);
+ void OnMsgStartPluginIme();
+ void OnAllocateFakePluginWindowHandle(bool opaque,
+ bool root,
+ gfx::PluginWindowHandle* id);
+ void OnDestroyFakePluginWindowHandle(gfx::PluginWindowHandle id);
+ void OnAcceleratedSurfaceSetIOSurface(gfx::PluginWindowHandle window,
+ int32 width,
+ int32 height,
+ uint64 mach_port);
+ void OnAcceleratedSurfaceSetTransportDIB(gfx::PluginWindowHandle window,
+ int32 width,
+ int32 height,
+ TransportDIB::Handle transport_dib);
+ void OnAcceleratedSurfaceBuffersSwapped(gfx::PluginWindowHandle window,
+ uint64 surface_id);
+#elif defined(OS_POSIX)
+ void OnMsgCreatePluginContainer(gfx::PluginWindowHandle id);
+ void OnMsgDestroyPluginContainer(gfx::PluginWindowHandle id);
+#endif
+
+ // Paints the given bitmap to the current backing store at the given location.
+ void PaintBackingStoreRect(TransportDIB::Id bitmap,
+ const gfx::Rect& bitmap_rect,
+ const std::vector<gfx::Rect>& copy_rects,
+ const gfx::Size& view_size);
+
+ // Scrolls the given |clip_rect| in the backing by the given dx/dy amount. The
+ // |dib| and its corresponding location |bitmap_rect| in the backing store
+ // is the newly painted pixels by the renderer.
+ void ScrollBackingStoreRect(int dx, int dy, const gfx::Rect& clip_rect,
+ const gfx::Size& view_size);
+
+ // Called by OnMsgInputEventAck() to process a keyboard event ack message.
+ void ProcessKeyboardEventAck(int type, bool processed);
+
+ // Called by OnMsgInputEventAck() to process a wheel event ack message.
+ // This could result in a task being posted to allow additional wheel
+ // input messages to be coalesced.
+ void ProcessWheelAck();
+
+ // True if renderer accessibility is enabled. This should only be set when a
+ // screenreader is detected as it can potentially slow down Chrome.
+ bool renderer_accessible_;
+
+ // The View associated with the RenderViewHost. The lifetime of this object
+ // is associated with the lifetime of the Render process. If the Renderer
+ // crashes, its View is destroyed and this pointer becomes NULL, even though
+ // render_view_host_ lives on to load another URL (creating a new View while
+ // doing so).
+ RenderWidgetHostView* view_;
+
+ // Created during construction but initialized during Init*(). Therefore, it
+ // is guaranteed never to be NULL, but its channel may be NULL if the
+ // renderer crashed, so you must always check that.
+ RenderProcessHost* process_;
+
+ // Stores random bits of data for others to associate with this object.
+ PropertyBag property_bag_;
+
+ // The ID of the corresponding object in the Renderer Instance.
+ int routing_id_;
+
+ // Indicates whether a page is loading or not.
+ bool is_loading_;
+
+ // Indicates whether a page is hidden or not.
+ bool is_hidden_;
+
+ // True when a page is rendered directly via the GPU process.
+ bool is_accelerated_compositing_active_;
+
+ // Set if we are waiting for a repaint ack for the view.
+ bool repaint_ack_pending_;
+
+ // True when waiting for RESIZE_ACK.
+ bool resize_ack_pending_;
+
+ // The current size of the RenderWidget.
+ gfx::Size current_size_;
+
+ // The current reserved area of the RenderWidget where contents should not be
+ // rendered to draw the resize corner, sidebar mini tabs etc.
+ gfx::Rect current_reserved_rect_;
+
+ // The size we last sent as requested size to the renderer. |current_size_|
+ // is only updated once the resize message has been ack'd. This on the other
+ // hand is updated when the resize message is sent. This is very similar to
+ // |resize_ack_pending_|, but the latter is not set if the new size has width
+ // or height zero, which is why we need this too.
+ gfx::Size in_flight_size_;
+
+ // The reserved area we last sent to the renderer. |current_reserved_rect_|
+ // is only updated once the resize message has been ack'd. This on the other
+ // hand is updated when the resize message is sent.
+ gfx::Rect in_flight_reserved_rect_;
+
+ // True if a mouse move event was sent to the render view and we are waiting
+ // for a corresponding ViewHostMsg_HandleInputEvent_ACK message.
+ bool mouse_move_pending_;
+
+ // The next mouse move event to send (only non-null while mouse_move_pending_
+ // is true).
+ scoped_ptr<WebKit::WebMouseEvent> next_mouse_move_;
+
+ // (Similar to |mouse_move_pending_|.) True if a mouse wheel event was sent
+ // and we are waiting for a corresponding ack.
+ bool mouse_wheel_pending_;
+
+ typedef std::deque<WebKit::WebMouseWheelEvent> WheelEventQueue;
+
+ // (Similar to |next_mouse_move_|.) The next mouse wheel events to send.
+ // Unlike mouse moves, mouse wheel events received while one is pending are
+ // coalesced (by accumulating deltas) if they match the previous event in
+ // modifiers. On the Mac, in particular, mouse wheel events are received at a
+ // high rate; not waiting for the ack results in jankiness, and using the same
+ // mechanism as for mouse moves (just dropping old events when multiple ones
+ // would be queued) results in very slow scrolling.
+ WheelEventQueue coalesced_mouse_wheel_events_;
+
+ // The time when an input event was sent to the RenderWidget.
+ base::TimeTicks input_event_start_time_;
+
+ // If true, then we should repaint when restoring even if we have a
+ // backingstore. This flag is set to true if we receive a paint message
+ // while is_hidden_ to true. Even though we tell the render widget to hide
+ // itself, a paint message could already be in flight at that point.
+ bool needs_repainting_on_restore_;
+
+ // This is true if the renderer is currently unresponsive.
+ bool is_unresponsive_;
+
+ // The following value indicates a time in the future when we would consider
+ // the renderer hung if it does not generate an appropriate response message.
+ base::Time time_when_considered_hung_;
+
+ // This timer runs to check if time_when_considered_hung_ has past.
+ base::OneShotTimer<RenderWidgetHost> hung_renderer_timer_;
+
+ // Flag to detect recursive calls to GetBackingStore().
+ bool in_get_backing_store_;
+
+ // Set when we call DidPaintRect/DidScrollRect on the view.
+ bool view_being_painted_;
+
+ // Used for UMA histogram logging to measure the time for a repaint view
+ // operation to finish.
+ base::TimeTicks repaint_start_time_;
+
+ // Queue of keyboard events that we need to track.
+ typedef std::deque<NativeWebKeyboardEvent> KeyQueue;
+
+ // A queue of keyboard events. We can't trust data from the renderer so we
+ // stuff key events into a queue and pop them out on ACK, feeding our copy
+ // back to whatever unhandled handler instead of the returned version.
+ KeyQueue key_queue_;
+
+ // Set to true if we shouldn't send input events from the render widget.
+ bool ignore_input_events_;
+
+ // Set when we update the text direction of the selected input element.
+ bool text_direction_updated_;
+ WebKit::WebTextDirection text_direction_;
+
+ // Set when we cancel updating the text direction.
+ // This flag also ignores succeeding update requests until we call
+ // NotifyTextDirection().
+ bool text_direction_canceled_;
+
+ // Indicates if the next sequence of Char events should be suppressed or not.
+ // System may translate a RawKeyDown event into zero or more Char events,
+ // usually we send them to the renderer directly in sequence. However, If a
+ // RawKeyDown event was not handled by the renderer but was handled by
+ // our UnhandledKeyboardEvent() method, e.g. as an accelerator key, then we
+ // shall not send the following sequence of Char events, which was generated
+ // by this RawKeyDown event, to the renderer. Otherwise the renderer may
+ // handle the Char events and cause unexpected behavior.
+ // For example, pressing alt-2 may let the browser switch to the second tab,
+ // but the Char event generated by alt-2 may also activate a HTML element
+ // if its accesskey happens to be "2", then the user may get confused when
+ // switching back to the original tab, because the content may already be
+ // changed.
+ bool suppress_next_char_events_;
+
+ std::vector<gfx::PluginWindowHandle> deferred_plugin_handles_;
+
+ // The last scroll offset of the render widget.
+ gfx::Point last_scroll_offset_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderWidgetHost);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_H_
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc
new file mode 100644
index 0000000..546854e
--- /dev/null
+++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -0,0 +1,734 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "base/shared_memory.h"
+#include "base/timer.h"
+#include "build/build_config.h"
+#include "chrome/browser/renderer_host/backing_store.h"
+#include "chrome/browser/renderer_host/test/test_render_view_host.h"
+#include "chrome/common/notification_details.h"
+#include "chrome/common/notification_source.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/render_messages_params.h"
+#include "chrome/test/testing_profile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/keycodes/keyboard_codes.h"
+#include "ui/gfx/canvas_skia.h"
+
+using base::TimeDelta;
+
+using WebKit::WebInputEvent;
+using WebKit::WebMouseWheelEvent;
+
+namespace gfx {
+class Size;
+}
+
+// RenderWidgetHostProcess -----------------------------------------------------
+
+class RenderWidgetHostProcess : public MockRenderProcessHost {
+ public:
+ explicit RenderWidgetHostProcess(Profile* profile)
+ : MockRenderProcessHost(profile),
+ current_update_buf_(NULL),
+ update_msg_should_reply_(false),
+ update_msg_reply_flags_(0) {
+ // DANGER! This is a hack. The RenderWidgetHost checks the channel to see
+ // if the process is still alive, but it doesn't actually dereference it.
+ // An IPC::SyncChannel is nontrivial, so we just fake it here. If you end up
+ // crashing by dereferencing 1, then you'll have to make a real channel.
+ channel_.reset(reinterpret_cast<IPC::SyncChannel*>(0x1));
+ }
+ ~RenderWidgetHostProcess() {
+ // We don't want to actually delete the channel, since it's not a real
+ // pointer.
+ ignore_result(channel_.release());
+ delete current_update_buf_;
+ }
+
+ void set_update_msg_should_reply(bool reply) {
+ update_msg_should_reply_ = reply;
+ }
+ void set_update_msg_reply_flags(int flags) {
+ update_msg_reply_flags_ = flags;
+ }
+
+ // Fills the given update parameters with resonable default values.
+ void InitUpdateRectParams(ViewHostMsg_UpdateRect_Params* params);
+
+ protected:
+ virtual bool WaitForUpdateMsg(int render_widget_id,
+ const base::TimeDelta& max_delay,
+ IPC::Message* msg);
+
+ TransportDIB* current_update_buf_;
+
+ // Set to true when WaitForUpdateMsg should return a successful update message
+ // reply. False implies timeout.
+ bool update_msg_should_reply_;
+
+ // Indicates the flags that should be sent with a the repaint request. This
+ // only has an effect when update_msg_should_reply_ is true.
+ int update_msg_reply_flags_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostProcess);
+};
+
+void RenderWidgetHostProcess::InitUpdateRectParams(
+ ViewHostMsg_UpdateRect_Params* params) {
+ // Create the shared backing store.
+ const int w = 100, h = 100;
+ const size_t pixel_size = w * h * 4;
+
+ if (!current_update_buf_)
+ current_update_buf_ = TransportDIB::Create(pixel_size, 0);
+ params->bitmap = current_update_buf_->id();
+ params->bitmap_rect = gfx::Rect(0, 0, w, h);
+ params->dx = 0;
+ params->dy = 0;
+ params->copy_rects.push_back(params->bitmap_rect);
+ params->view_size = gfx::Size(w, h);
+ params->flags = update_msg_reply_flags_;
+}
+
+bool RenderWidgetHostProcess::WaitForUpdateMsg(int render_widget_id,
+ const base::TimeDelta& max_delay,
+ IPC::Message* msg) {
+ if (!update_msg_should_reply_)
+ return false;
+
+ // Construct a fake update reply.
+ ViewHostMsg_UpdateRect_Params params;
+ InitUpdateRectParams(&params);
+
+ ViewHostMsg_UpdateRect message(render_widget_id, params);
+ *msg = message;
+ return true;
+}
+
+// TestView --------------------------------------------------------------------
+
+// This test view allows us to specify the size.
+class TestView : public TestRenderWidgetHostView {
+ public:
+ explicit TestView(RenderWidgetHost* rwh) : TestRenderWidgetHostView(rwh) {}
+
+ // Sets the bounds returned by GetViewBounds.
+ void set_bounds(const gfx::Rect& bounds) {
+ bounds_ = bounds;
+ }
+
+ // RenderWidgetHostView override.
+ virtual gfx::Rect GetViewBounds() const {
+ return bounds_;
+ }
+
+#if defined(OS_MACOSX)
+ virtual gfx::Rect GetViewCocoaBounds() const {
+ return bounds_;
+ }
+#endif
+
+ protected:
+ gfx::Rect bounds_;
+ DISALLOW_COPY_AND_ASSIGN(TestView);
+};
+
+// MockRenderWidgetHost ----------------------------------------------------
+
+class MockRenderWidgetHost : public RenderWidgetHost {
+ public:
+ MockRenderWidgetHost(RenderProcessHost* process, int routing_id)
+ : RenderWidgetHost(process, routing_id),
+ prehandle_keyboard_event_(false),
+ prehandle_keyboard_event_called_(false),
+ prehandle_keyboard_event_type_(WebInputEvent::Undefined),
+ unhandled_keyboard_event_called_(false),
+ unhandled_keyboard_event_type_(WebInputEvent::Undefined),
+ unresponsive_timer_fired_(false) {
+ }
+
+ // Tests that make sure we ignore keyboard event acknowledgments to events we
+ // didn't send work by making sure we didn't call UnhandledKeyboardEvent().
+ bool unhandled_keyboard_event_called() const {
+ return unhandled_keyboard_event_called_;
+ }
+
+ WebInputEvent::Type unhandled_keyboard_event_type() const {
+ return unhandled_keyboard_event_type_;
+ }
+
+ bool prehandle_keyboard_event_called() const {
+ return prehandle_keyboard_event_called_;
+ }
+
+ WebInputEvent::Type prehandle_keyboard_event_type() const {
+ return prehandle_keyboard_event_type_;
+ }
+
+ void set_prehandle_keyboard_event(bool handle) {
+ prehandle_keyboard_event_ = handle;
+ }
+
+ bool unresponsive_timer_fired() const {
+ return unresponsive_timer_fired_;
+ }
+
+ protected:
+ virtual bool PreHandleKeyboardEvent(const NativeWebKeyboardEvent& event,
+ bool* is_keyboard_shortcut) {
+ prehandle_keyboard_event_type_ = event.type;
+ prehandle_keyboard_event_called_ = true;
+ return prehandle_keyboard_event_;
+ }
+
+ virtual void UnhandledKeyboardEvent(const NativeWebKeyboardEvent& event) {
+ unhandled_keyboard_event_type_ = event.type;
+ unhandled_keyboard_event_called_ = true;
+ }
+
+ virtual void NotifyRendererUnresponsive() {
+ unresponsive_timer_fired_ = true;
+ }
+
+ private:
+ bool prehandle_keyboard_event_;
+ bool prehandle_keyboard_event_called_;
+ WebInputEvent::Type prehandle_keyboard_event_type_;
+
+ bool unhandled_keyboard_event_called_;
+ WebInputEvent::Type unhandled_keyboard_event_type_;
+
+ bool unresponsive_timer_fired_;
+};
+
+// MockPaintingObserver --------------------------------------------------------
+
+class MockPaintingObserver : public NotificationObserver {
+ public:
+ void WidgetDidReceivePaintAtSizeAck(RenderWidgetHost* host,
+ int tag,
+ const gfx::Size& size) {
+ host_ = reinterpret_cast<MockRenderWidgetHost*>(host);
+ tag_ = tag;
+ size_ = size;
+ }
+
+ void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ if (type ==
+ NotificationType::RENDER_WIDGET_HOST_DID_RECEIVE_PAINT_AT_SIZE_ACK) {
+ RenderWidgetHost::PaintAtSizeAckDetails* size_ack_details =
+ Details<RenderWidgetHost::PaintAtSizeAckDetails>(details).ptr();
+ WidgetDidReceivePaintAtSizeAck(
+ Source<RenderWidgetHost>(source).ptr(),
+ size_ack_details->tag,
+ size_ack_details->size);
+ }
+ }
+
+ MockRenderWidgetHost* host() const { return host_; }
+ int tag() const { return tag_; }
+ gfx::Size size() const { return size_; }
+
+ private:
+ MockRenderWidgetHost* host_;
+ int tag_;
+ gfx::Size size_;
+};
+
+
+// RenderWidgetHostTest --------------------------------------------------------
+
+class RenderWidgetHostTest : public testing::Test {
+ public:
+ RenderWidgetHostTest() : process_(NULL) {
+ }
+ ~RenderWidgetHostTest() {
+ }
+
+ protected:
+ // testing::Test
+ void SetUp() {
+ profile_.reset(new TestingProfile());
+ process_ = new RenderWidgetHostProcess(profile_.get());
+ host_.reset(new MockRenderWidgetHost(process_, 1));
+ view_.reset(new TestView(host_.get()));
+ host_->set_view(view_.get());
+ host_->Init();
+ }
+ void TearDown() {
+ view_.reset();
+ host_.reset();
+ process_ = NULL;
+ profile_.reset();
+
+ // Process all pending tasks to avoid leaks.
+ MessageLoop::current()->RunAllPending();
+ }
+
+ void SendInputEventACK(WebInputEvent::Type type, bool processed) {
+ scoped_ptr<IPC::Message> response(
+ new ViewHostMsg_HandleInputEvent_ACK(0));
+ response->WriteInt(type);
+ response->WriteBool(processed);
+ host_->OnMessageReceived(*response);
+ }
+
+ void SimulateKeyboardEvent(WebInputEvent::Type type) {
+ NativeWebKeyboardEvent key_event;
+ key_event.type = type;
+ key_event.windowsKeyCode = ui::VKEY_L; // non-null made up value.
+ host_->ForwardKeyboardEvent(key_event);
+ }
+
+ void SimulateWheelEvent(float dX, float dY, int modifiers) {
+ WebMouseWheelEvent wheel_event;
+ wheel_event.type = WebInputEvent::MouseWheel;
+ wheel_event.deltaX = dX;
+ wheel_event.deltaY = dY;
+ wheel_event.modifiers = modifiers;
+ host_->ForwardWheelEvent(wheel_event);
+ }
+
+ MessageLoopForUI message_loop_;
+
+ scoped_ptr<TestingProfile> profile_;
+ RenderWidgetHostProcess* process_; // Deleted automatically by the widget.
+ scoped_ptr<MockRenderWidgetHost> host_;
+ scoped_ptr<TestView> view_;
+
+ DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostTest);
+};
+
+// -----------------------------------------------------------------------------
+
+TEST_F(RenderWidgetHostTest, Resize) {
+ // The initial bounds is the empty rect, so setting it to the same thing
+ // should do nothing.
+ view_->set_bounds(gfx::Rect());
+ host_->WasResized();
+ EXPECT_FALSE(host_->resize_ack_pending_);
+ EXPECT_EQ(gfx::Size(), host_->in_flight_size_);
+ EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID));
+
+ // Setting the bounds to a "real" rect should send out the notification.
+ gfx::Rect original_size(0, 0, 100, 100);
+ process_->sink().ClearMessages();
+ view_->set_bounds(original_size);
+ host_->WasResized();
+ EXPECT_TRUE(host_->resize_ack_pending_);
+ EXPECT_EQ(original_size.size(), host_->in_flight_size_);
+ EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID));
+
+ // Send out a update that's not a resize ack. This should not clean the
+ // resize ack pending flag.
+ ViewHostMsg_UpdateRect_Params params;
+ process_->InitUpdateRectParams(&params);
+ host_->OnMsgUpdateRect(params);
+ EXPECT_TRUE(host_->resize_ack_pending_);
+ EXPECT_EQ(original_size.size(), host_->in_flight_size_);
+
+ // Sending out a new notification should NOT send out a new IPC message since
+ // a resize ACK is pending.
+ gfx::Rect second_size(0, 0, 90, 90);
+ process_->sink().ClearMessages();
+ view_->set_bounds(second_size);
+ host_->WasResized();
+ EXPECT_TRUE(host_->resize_ack_pending_);
+ EXPECT_EQ(original_size.size(), host_->in_flight_size_);
+ EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID));
+
+ // Send a update that's a resize ack, but for the original_size we sent. Since
+ // this isn't the second_size, the message handler should immediately send
+ // a new resize message for the new size to the renderer.
+ process_->sink().ClearMessages();
+ params.flags = ViewHostMsg_UpdateRect_Flags::IS_RESIZE_ACK;
+ params.view_size = original_size.size();
+ host_->OnMsgUpdateRect(params);
+ EXPECT_TRUE(host_->resize_ack_pending_);
+ EXPECT_EQ(second_size.size(), host_->in_flight_size_);
+ ASSERT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID));
+
+ // Send the resize ack for the latest size.
+ process_->sink().ClearMessages();
+ params.view_size = second_size.size();
+ host_->OnMsgUpdateRect(params);
+ EXPECT_FALSE(host_->resize_ack_pending_);
+ EXPECT_EQ(gfx::Size(), host_->in_flight_size_);
+ ASSERT_FALSE(process_->sink().GetFirstMessageMatching(ViewMsg_Resize::ID));
+
+ // Now clearing the bounds should send out a notification but we shouldn't
+ // expect a resize ack (since the renderer won't ack empty sizes). The message
+ // should contain the new size (0x0) and not the previous one that we skipped
+ process_->sink().ClearMessages();
+ view_->set_bounds(gfx::Rect());
+ host_->WasResized();
+ EXPECT_FALSE(host_->resize_ack_pending_);
+ EXPECT_EQ(gfx::Size(), host_->in_flight_size_);
+ EXPECT_EQ(gfx::Size(), host_->current_size_);
+ EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID));
+
+ // Send a rect that has no area but has either width or height set.
+ // since we do not expect ACK, current_size_ should be updated right away.
+ process_->sink().ClearMessages();
+ view_->set_bounds(gfx::Rect(0, 0, 0, 30));
+ host_->WasResized();
+ EXPECT_FALSE(host_->resize_ack_pending_);
+ EXPECT_EQ(gfx::Size(), host_->in_flight_size_);
+ EXPECT_EQ(gfx::Size(0, 30), host_->current_size_);
+ EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID));
+
+ // Set the same size again. It should not be sent again.
+ process_->sink().ClearMessages();
+ host_->WasResized();
+ EXPECT_FALSE(host_->resize_ack_pending_);
+ EXPECT_EQ(gfx::Size(), host_->in_flight_size_);
+ EXPECT_EQ(gfx::Size(0, 30), host_->current_size_);
+ EXPECT_FALSE(process_->sink().GetFirstMessageMatching(ViewMsg_Resize::ID));
+
+ // A different size should be sent again, however.
+ view_->set_bounds(gfx::Rect(0, 0, 0, 31));
+ host_->WasResized();
+ EXPECT_FALSE(host_->resize_ack_pending_);
+ EXPECT_EQ(gfx::Size(), host_->in_flight_size_);
+ EXPECT_EQ(gfx::Size(0, 31), host_->current_size_);
+ EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID));
+}
+
+// Test for crbug.com/25097. If a renderer crashes between a resize and the
+// corresponding update message, we must be sure to clear the resize ack logic.
+TEST_F(RenderWidgetHostTest, ResizeThenCrash) {
+ // Setting the bounds to a "real" rect should send out the notification.
+ gfx::Rect original_size(0, 0, 100, 100);
+ view_->set_bounds(original_size);
+ host_->WasResized();
+ EXPECT_TRUE(host_->resize_ack_pending_);
+ EXPECT_EQ(original_size.size(), host_->in_flight_size_);
+ EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Resize::ID));
+
+ // Simulate a renderer crash before the update message. Ensure all the
+ // resize ack logic is cleared. Must clear the view first so it doesn't get
+ // deleted.
+ host_->set_view(NULL);
+ host_->RendererExited(base::TERMINATION_STATUS_PROCESS_CRASHED, -1);
+ EXPECT_FALSE(host_->resize_ack_pending_);
+ EXPECT_EQ(gfx::Size(), host_->in_flight_size_);
+
+ // Reset the view so we can exit the test cleanly.
+ host_->set_view(view_.get());
+}
+
+// Tests setting custom background
+TEST_F(RenderWidgetHostTest, Background) {
+#if defined(OS_WIN) || defined(OS_LINUX)
+ scoped_ptr<RenderWidgetHostView> view(
+ RenderWidgetHostView::CreateViewForWidget(host_.get()));
+ host_->set_view(view.get());
+
+ // Create a checkerboard background to test with.
+ gfx::CanvasSkia canvas(4, 4, true);
+ canvas.FillRectInt(SK_ColorBLACK, 0, 0, 2, 2);
+ canvas.FillRectInt(SK_ColorWHITE, 2, 0, 2, 2);
+ canvas.FillRectInt(SK_ColorWHITE, 0, 2, 2, 2);
+ canvas.FillRectInt(SK_ColorBLACK, 2, 2, 2, 2);
+ const SkBitmap& background = canvas.getDevice()->accessBitmap(false);
+
+ // Set the background and make sure we get back a copy.
+ view->SetBackground(background);
+ EXPECT_EQ(4, view->background().width());
+ EXPECT_EQ(4, view->background().height());
+ EXPECT_EQ(background.getSize(), view->background().getSize());
+ EXPECT_TRUE(0 == memcmp(background.getPixels(),
+ view->background().getPixels(),
+ background.getSize()));
+
+#if defined(OS_WIN)
+ // A message should have been dispatched telling the renderer about the new
+ // background.
+ const IPC::Message* set_background =
+ process_->sink().GetUniqueMessageMatching(ViewMsg_SetBackground::ID);
+ ASSERT_TRUE(set_background);
+ Tuple1<SkBitmap> sent_background;
+ ViewMsg_SetBackground::Read(set_background, &sent_background);
+ EXPECT_EQ(background.getSize(), sent_background.a.getSize());
+ EXPECT_TRUE(0 == memcmp(background.getPixels(),
+ sent_background.a.getPixels(),
+ background.getSize()));
+#else
+ // TODO(port): When custom backgrounds are implemented for other ports, this
+ // test should work (assuming the background must still be copied into the
+ // renderer -- if not, then maybe the test doesn't apply?).
+#endif
+
+#else
+ // TODO(port): Mac does not have gfx::Canvas. Maybe we can just change this
+ // test to use SkCanvas directly?
+#endif
+
+ // TODO(aa): It would be nice to factor out the painting logic so that we
+ // could test that, but it appears that would mean painting everything twice
+ // since windows HDC structures are opaque.
+}
+
+// Tests getting the backing store with the renderer not setting repaint ack
+// flags.
+TEST_F(RenderWidgetHostTest, GetBackingStore_NoRepaintAck) {
+ // We don't currently have a backing store, and if the renderer doesn't send
+ // one in time, we should get nothing.
+ process_->set_update_msg_should_reply(false);
+ BackingStore* backing = host_->GetBackingStore(true);
+ EXPECT_FALSE(backing);
+ // The widget host should have sent a request for a repaint, and there should
+ // be no paint ACK.
+ EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Repaint::ID));
+ EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(
+ ViewMsg_UpdateRect_ACK::ID));
+
+ // Allowing the renderer to reply in time should give is a backing store.
+ process_->sink().ClearMessages();
+ process_->set_update_msg_should_reply(true);
+ process_->set_update_msg_reply_flags(0);
+ backing = host_->GetBackingStore(true);
+ EXPECT_TRUE(backing);
+ // The widget host should NOT have sent a request for a repaint, since there
+ // was an ACK already pending.
+ EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(ViewMsg_Repaint::ID));
+ EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
+ ViewMsg_UpdateRect_ACK::ID));
+}
+
+// Tests getting the backing store with the renderer sending a repaint ack.
+TEST_F(RenderWidgetHostTest, GetBackingStore_RepaintAck) {
+ // Doing a request request with the update message allowed should work and
+ // the repaint ack should work.
+ process_->set_update_msg_should_reply(true);
+ process_->set_update_msg_reply_flags(
+ ViewHostMsg_UpdateRect_Flags::IS_REPAINT_ACK);
+ BackingStore* backing = host_->GetBackingStore(true);
+ EXPECT_TRUE(backing);
+ // We still should not have sent out a repaint request since the last flags
+ // didn't have the repaint ack set, and the pending flag will still be set.
+ EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_Repaint::ID));
+ EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
+ ViewMsg_UpdateRect_ACK::ID));
+
+ // Asking again for the backing store should just re-use the existing one
+ // and not send any messagse.
+ process_->sink().ClearMessages();
+ backing = host_->GetBackingStore(true);
+ EXPECT_TRUE(backing);
+ EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(ViewMsg_Repaint::ID));
+ EXPECT_FALSE(process_->sink().GetUniqueMessageMatching(
+ ViewMsg_UpdateRect_ACK::ID));
+}
+
+// Test that we don't paint when we're hidden, but we still send the ACK. Most
+// of the rest of the painting is tested in the GetBackingStore* ones.
+TEST_F(RenderWidgetHostTest, HiddenPaint) {
+ // Hide the widget, it should have sent out a message to the renderer.
+ EXPECT_FALSE(host_->is_hidden_);
+ host_->WasHidden();
+ EXPECT_TRUE(host_->is_hidden_);
+ EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(ViewMsg_WasHidden::ID));
+
+ // Send it an update as from the renderer.
+ process_->sink().ClearMessages();
+ ViewHostMsg_UpdateRect_Params params;
+ process_->InitUpdateRectParams(&params);
+ host_->OnMsgUpdateRect(params);
+
+ // It should have sent out the ACK.
+ EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
+ ViewMsg_UpdateRect_ACK::ID));
+
+ // Now unhide.
+ process_->sink().ClearMessages();
+ host_->WasRestored();
+ EXPECT_FALSE(host_->is_hidden_);
+
+ // It should have sent out a restored message with a request to paint.
+ const IPC::Message* restored = process_->sink().GetUniqueMessageMatching(
+ ViewMsg_WasRestored::ID);
+ ASSERT_TRUE(restored);
+ Tuple1<bool> needs_repaint;
+ ViewMsg_WasRestored::Read(restored, &needs_repaint);
+ EXPECT_TRUE(needs_repaint.a);
+}
+
+TEST_F(RenderWidgetHostTest, PaintAtSize) {
+ const int kPaintAtSizeTag = 42;
+ host_->PaintAtSize(TransportDIB::GetFakeHandleForTest(), kPaintAtSizeTag,
+ gfx::Size(40, 60), gfx::Size(20, 30));
+ EXPECT_TRUE(
+ process_->sink().GetUniqueMessageMatching(ViewMsg_PaintAtSize::ID));
+
+ NotificationRegistrar registrar;
+ MockPaintingObserver observer;
+ registrar.Add(
+ &observer,
+ NotificationType::RENDER_WIDGET_HOST_DID_RECEIVE_PAINT_AT_SIZE_ACK,
+ Source<RenderWidgetHost>(host_.get()));
+
+ host_->OnMsgPaintAtSizeAck(kPaintAtSizeTag, gfx::Size(20, 30));
+ EXPECT_EQ(host_.get(), observer.host());
+ EXPECT_EQ(kPaintAtSizeTag, observer.tag());
+ EXPECT_EQ(20, observer.size().width());
+ EXPECT_EQ(30, observer.size().height());
+}
+
+TEST_F(RenderWidgetHostTest, HandleKeyEventsWeSent) {
+ // Simulate a keyboard event.
+ SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
+
+ // Make sure we sent the input event to the renderer.
+ EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
+ ViewMsg_HandleInputEvent::ID));
+ process_->sink().ClearMessages();
+
+ // Send the simulated response from the renderer back.
+ SendInputEventACK(WebInputEvent::RawKeyDown, false);
+
+ EXPECT_TRUE(host_->unhandled_keyboard_event_called());
+ EXPECT_EQ(WebInputEvent::RawKeyDown, host_->unhandled_keyboard_event_type());
+}
+
+TEST_F(RenderWidgetHostTest, IgnoreKeyEventsWeDidntSend) {
+ // Send a simulated, unrequested key response. We should ignore this.
+ SendInputEventACK(WebInputEvent::RawKeyDown, false);
+
+ EXPECT_FALSE(host_->unhandled_keyboard_event_called());
+}
+
+TEST_F(RenderWidgetHostTest, IgnoreKeyEventsHandledByRenderer) {
+ // Simulate a keyboard event.
+ SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
+
+ // Make sure we sent the input event to the renderer.
+ EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
+ ViewMsg_HandleInputEvent::ID));
+ process_->sink().ClearMessages();
+
+ // Send the simulated response from the renderer back.
+ SendInputEventACK(WebInputEvent::RawKeyDown, true);
+ EXPECT_FALSE(host_->unhandled_keyboard_event_called());
+}
+
+TEST_F(RenderWidgetHostTest, PreHandleRawKeyDownEvent) {
+ // Simluate the situation that the browser handled the key down event during
+ // pre-handle phrase.
+ host_->set_prehandle_keyboard_event(true);
+ process_->sink().ClearMessages();
+
+ // Simulate a keyboard event.
+ SimulateKeyboardEvent(WebInputEvent::RawKeyDown);
+
+ EXPECT_TRUE(host_->prehandle_keyboard_event_called());
+ EXPECT_EQ(WebInputEvent::RawKeyDown, host_->prehandle_keyboard_event_type());
+
+ // Make sure the RawKeyDown event is not sent to the renderer.
+ EXPECT_EQ(0U, process_->sink().message_count());
+
+ // The browser won't pre-handle a Char event.
+ host_->set_prehandle_keyboard_event(false);
+
+ // Forward the Char event.
+ SimulateKeyboardEvent(WebInputEvent::Char);
+
+ // Make sure the Char event is suppressed.
+ EXPECT_EQ(0U, process_->sink().message_count());
+
+ // Forward the KeyUp event.
+ SimulateKeyboardEvent(WebInputEvent::KeyUp);
+
+ // Make sure only KeyUp was sent to the renderer.
+ EXPECT_EQ(1U, process_->sink().message_count());
+ EXPECT_EQ(ViewMsg_HandleInputEvent::ID,
+ process_->sink().GetMessageAt(0)->type());
+ process_->sink().ClearMessages();
+
+ // Send the simulated response from the renderer back.
+ SendInputEventACK(WebInputEvent::KeyUp, false);
+
+ EXPECT_TRUE(host_->unhandled_keyboard_event_called());
+ EXPECT_EQ(WebInputEvent::KeyUp, host_->unhandled_keyboard_event_type());
+}
+
+TEST_F(RenderWidgetHostTest, CoalescesWheelEvents) {
+ process_->sink().ClearMessages();
+
+ // Simulate wheel events.
+ SimulateWheelEvent(0, -5, 0); // sent directly
+ SimulateWheelEvent(0, -10, 0); // enqueued
+ SimulateWheelEvent(8, -6, 0); // coalesced into previous event
+ SimulateWheelEvent(9, -7, 1); // enqueued, different modifiers
+
+ // Check that only the first event was sent.
+ EXPECT_EQ(1U, process_->sink().message_count());
+ EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
+ ViewMsg_HandleInputEvent::ID));
+ process_->sink().ClearMessages();
+
+ // Check that the ACK sends the second message.
+ SendInputEventACK(WebInputEvent::MouseWheel, true);
+ // The coalesced events can queue up a delayed ack
+ // so that additional input events can be processed before
+ // we turn off coalescing.
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(1U, process_->sink().message_count());
+ EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
+ ViewMsg_HandleInputEvent::ID));
+ process_->sink().ClearMessages();
+
+ // One more time.
+ SendInputEventACK(WebInputEvent::MouseWheel, true);
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(1U, process_->sink().message_count());
+ EXPECT_TRUE(process_->sink().GetUniqueMessageMatching(
+ ViewMsg_HandleInputEvent::ID));
+ process_->sink().ClearMessages();
+
+ // After the final ack, the queue should be empty.
+ SendInputEventACK(WebInputEvent::MouseWheel, true);
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(0U, process_->sink().message_count());
+}
+
+// Test that the hang monitor timer expires properly if a new timer is started
+// while one is in progress (see crbug.com/11007).
+TEST_F(RenderWidgetHostTest, DontPostponeHangMonitorTimeout) {
+ // Start with a short timeout.
+ host_->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(10));
+
+ // Immediately try to add a long 30 second timeout.
+ EXPECT_FALSE(host_->unresponsive_timer_fired());
+ host_->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(30000));
+
+ // Wait long enough for first timeout and see if it fired.
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ new MessageLoop::QuitTask(), 10);
+ MessageLoop::current()->Run();
+ EXPECT_TRUE(host_->unresponsive_timer_fired());
+}
+
+// Test that the hang monitor timer expires properly if it is started, stopped,
+// and then started again.
+TEST_F(RenderWidgetHostTest, StopAndStartHangMonitorTimeout) {
+ // Start with a short timeout, then stop it.
+ host_->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(10));
+ host_->StopHangMonitorTimeout();
+
+ // Start it again to ensure it still works.
+ EXPECT_FALSE(host_->unresponsive_timer_fired());
+ host_->StartHangMonitorTimeout(TimeDelta::FromMilliseconds(10));
+
+ // Wait long enough for first timeout and see if it fired.
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ new MessageLoop::QuitTask(), 10);
+ MessageLoop::current()->Run();
+ EXPECT_TRUE(host_->unresponsive_timer_fired());
+}
diff --git a/content/browser/renderer_host/render_widget_host_view.cc b/content/browser/renderer_host/render_widget_host_view.cc
new file mode 100644
index 0000000..a3b4d8c
--- /dev/null
+++ b/content/browser/renderer_host/render_widget_host_view.cc
@@ -0,0 +1,11 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/render_widget_host_view.h"
+
+RenderWidgetHostView::~RenderWidgetHostView() {}
+
+void RenderWidgetHostView::SetBackground(const SkBitmap& background) {
+ background_ = background;
+}
diff --git a/content/browser/renderer_host/render_widget_host_view.h b/content/browser/renderer_host/render_widget_host_view.h
new file mode 100644
index 0000000..a3f7031
--- /dev/null
+++ b/content/browser/renderer_host/render_widget_host_view.h
@@ -0,0 +1,326 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_H_
+#define CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_H_
+#pragma once
+
+#if defined(OS_MACOSX)
+#include <OpenGL/OpenGL.h>
+#endif
+
+#include <string>
+#include <vector>
+
+#include "app/surface/transport_dib.h"
+#include "base/process_util.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebPopupType.h"
+#include "third_party/WebKit/Source/WebKit/chromium/public/WebTextInputType.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/gfx/rect.h"
+
+namespace gfx {
+class Rect;
+class Size;
+}
+namespace IPC {
+class Message;
+}
+
+class BackingStore;
+class RenderProcessHost;
+class RenderWidgetHost;
+class WebCursor;
+struct NativeWebKeyboardEvent;
+struct ViewHostMsg_AccessibilityNotification_Params;
+
+namespace webkit_glue {
+struct WebAccessibility;
+}
+
+namespace webkit {
+namespace npapi {
+struct WebPluginGeometry;
+}
+}
+
+// RenderWidgetHostView is an interface implemented by an object that acts as
+// the "View" portion of a RenderWidgetHost. The RenderWidgetHost and its
+// associated RenderProcessHost own the "Model" in this case which is the
+// child renderer process. The View is responsible for receiving events from
+// the surrounding environment and passing them to the RenderWidgetHost, and
+// for actually displaying the content of the RenderWidgetHost when it
+// changes.
+class RenderWidgetHostView {
+ public:
+ virtual ~RenderWidgetHostView();
+
+ // Platform-specific creator. Use this to construct new RenderWidgetHostViews
+ // rather than using RenderWidgetHostViewWin & friends.
+ //
+ // This function must NOT size it, because the RenderView in the renderer
+ // wounldn't have been created yet. The widget would set its "waiting for
+ // resize ack" flag, and the ack would never come becasue no RenderView
+ // received it.
+ //
+ // The RenderWidgetHost must already be created (because we can't know if it's
+ // going to be a regular RenderWidgetHost or a RenderViewHost (a subclass).
+ static RenderWidgetHostView* CreateViewForWidget(RenderWidgetHost* widget);
+
+ // Retrieves the RenderWidgetHostView corresponding to the specified
+ // |native_view|, or NULL if there is no such instance.
+ static RenderWidgetHostView* GetRenderWidgetHostViewFromNativeView(
+ gfx::NativeView native_view);
+
+ // Perform all the initialization steps necessary for this object to represent
+ // a popup (such as a <select> dropdown), then shows the popup at |pos|.
+ virtual void InitAsPopup(RenderWidgetHostView* parent_host_view,
+ const gfx::Rect& pos) = 0;
+
+ // Perform all the initialization steps necessary for this object to represent
+ // a full screen window.
+ virtual void InitAsFullscreen() = 0;
+
+ // Returns the associated RenderWidgetHost.
+ virtual RenderWidgetHost* GetRenderWidgetHost() const = 0;
+
+ // Notifies the View that it has become visible.
+ virtual void DidBecomeSelected() = 0;
+
+ // Notifies the View that it has been hidden.
+ virtual void WasHidden() = 0;
+
+ // Tells the View to size itself to the specified size.
+ virtual void SetSize(const gfx::Size& size) = 0;
+
+ // Retrieves the native view used to contain plugins and identify the
+ // renderer in IPC messages.
+ virtual gfx::NativeView GetNativeView() = 0;
+
+ // Moves all plugin windows as described in the given list.
+ virtual void MovePluginWindows(
+ const std::vector<webkit::npapi::WebPluginGeometry>& moves) = 0;
+
+ // Actually set/take focus to/from the associated View component.
+ virtual void Focus() = 0;
+ virtual void Blur() = 0;
+
+ // Returns true if the View currently has the focus.
+ virtual bool HasFocus() = 0;
+
+ // Shows/hides the view. These must always be called together in pairs.
+ // It is not legal to call Hide() multiple times in a row.
+ virtual void Show() = 0;
+ virtual void Hide() = 0;
+
+ // Whether the view is showing.
+ virtual bool IsShowing() = 0;
+
+ // Retrieve the bounds of the View, in screen coordinates.
+ virtual gfx::Rect GetViewBounds() const = 0;
+
+ // Sets the cursor to the one associated with the specified cursor_type
+ virtual void UpdateCursor(const WebCursor& cursor) = 0;
+
+ // Indicates whether the page has finished loading.
+ virtual void SetIsLoading(bool is_loading) = 0;
+
+ // Updates the state of the input method attached to the view.
+ virtual void ImeUpdateTextInputState(WebKit::WebTextInputType type,
+ const gfx::Rect& caret_rect) = 0;
+
+ // Cancel the ongoing composition of the input method attached to the view.
+ virtual void ImeCancelComposition() = 0;
+
+ // Informs the view that a portion of the widget's backing store was scrolled
+ // and/or painted. The view should ensure this gets copied to the screen.
+ //
+ // If the scroll_rect is non-empty, then a portion of the widget's backing
+ // store was scrolled by dx pixels horizontally and dy pixels vertically.
+ // The exposed rect from the scroll operation is included in copy_rects.
+ //
+ // There are subtle performance implications here. The RenderWidget gets sent
+ // a paint ack after this returns, so if the view only ever invalidates in
+ // response to this, then on Windows, where WM_PAINT has lower priority than
+ // events which can cause renderer resizes/paint rect updates, e.g.
+ // drag-resizing can starve painting; this function thus provides the view its
+ // main chance to ensure it stays painted and not just invalidated. On the
+ // other hand, if this always blindly paints, then if we're already in the
+ // midst of a paint on the callstack, we can double-paint unnecessarily.
+ // (Worse, we might recursively call RenderWidgetHost::GetBackingStore().)
+ // Thus implementers should generally paint as much of |rect| as possible
+ // synchronously with as little overpainting as possible.
+ virtual void DidUpdateBackingStore(
+ const gfx::Rect& scroll_rect, int scroll_dx, int scroll_dy,
+ const std::vector<gfx::Rect>& copy_rects) = 0;
+
+ // Notifies the View that the renderer has ceased to exist.
+ virtual void RenderViewGone(base::TerminationStatus status,
+ int error_code) = 0;
+
+ // Notifies the View that the renderer will be delete soon.
+ virtual void WillDestroyRenderWidget(RenderWidgetHost* rwh) = 0;
+
+ // Tells the View to destroy itself.
+ virtual void Destroy() = 0;
+
+ // Tells the View that the tooltip text for the current mouse position over
+ // the page has changed.
+ virtual void SetTooltipText(const std::wstring& tooltip_text) = 0;
+
+ // Notifies the View that the renderer text selection has changed.
+ virtual void SelectionChanged(const std::string& text) {}
+
+ // Tells the View whether the context menu is showing. This is used on Linux
+ // to suppress updates to webkit focus for the duration of the show.
+ virtual void ShowingContextMenu(bool showing) {}
+
+ // Allocate a backing store for this view
+ virtual BackingStore* AllocBackingStore(const gfx::Size& size) = 0;
+
+#if defined(OS_MACOSX)
+ // Tells the view whether or not to accept first responder status. If |flag|
+ // is true, the view does not accept first responder status and instead
+ // manually becomes first responder when it receives a mouse down event. If
+ // |flag| is false, the view participates in the key-view chain as normal.
+ virtual void SetTakesFocusOnlyOnMouseDown(bool flag) = 0;
+
+ // Retrieve the bounds of the view, in cocoa view coordinates.
+ // If the UI scale factor is 2, |GetViewBounds()| will return a size of e.g.
+ // (400, 300) in pixels, while this method will return (200, 150).
+ // Even though this returns an gfx::Rect, the result is NOT IN PIXELS.
+ virtual gfx::Rect GetViewCocoaBounds() const = 0;
+
+ // Get the view's window's position on the screen.
+ virtual gfx::Rect GetRootWindowRect() = 0;
+
+ // Set the view's active state (i.e., tint state of controls).
+ virtual void SetActive(bool active) = 0;
+
+ // Notifies the view that its enclosing window has changed visibility
+ // (minimized/unminimized, app hidden/unhidden, etc).
+ // TODO(stuartmorgan): This is a temporary plugin-specific workaround for
+ // <http://crbug.com/34266>. Once that is fixed, this (and the corresponding
+ // message and renderer-side handling) can be removed in favor of using
+ // WasHidden/DidBecomeSelected.
+ virtual void SetWindowVisibility(bool visible) = 0;
+
+ // Informs the view that its containing window's frame changed.
+ virtual void WindowFrameChanged() = 0;
+
+ // Informs the view that a plugin gained or lost focus.
+ virtual void PluginFocusChanged(bool focused, int plugin_id) = 0;
+
+ // Start plugin IME.
+ virtual void StartPluginIme() = 0;
+
+ // Does any event handling necessary for plugin IME; should be called after
+ // the plugin has already had a chance to process the event. If plugin IME is
+ // not enabled, this is a no-op, so it is always safe to call.
+ // Returns true if the event was handled by IME.
+ virtual bool PostProcessEventForPluginIme(
+ const NativeWebKeyboardEvent& event) = 0;
+
+ // Methods associated with GPU-accelerated plug-in instances.
+ virtual gfx::PluginWindowHandle AllocateFakePluginWindowHandle(
+ bool opaque, bool root) = 0;
+ virtual void DestroyFakePluginWindowHandle(
+ gfx::PluginWindowHandle window) = 0;
+ virtual void AcceleratedSurfaceSetIOSurface(
+ gfx::PluginWindowHandle window,
+ int32 width,
+ int32 height,
+ uint64 io_surface_identifier) = 0;
+ virtual void AcceleratedSurfaceSetTransportDIB(
+ gfx::PluginWindowHandle window,
+ int32 width,
+ int32 height,
+ TransportDIB::Handle transport_dib) = 0;
+ // |window| and |surface_id| indicate which accelerated surface's
+ // buffers swapped. |renderer_id|, |route_id| and
+ // |swap_buffers_count| are used to formulate a reply to the GPU
+ // process to prevent it from getting too far ahead. They may all be
+ // zero, in which case no flow control is enforced; this case is
+ // currently used for accelerated plugins.
+ virtual void AcceleratedSurfaceBuffersSwapped(
+ gfx::PluginWindowHandle window,
+ uint64 surface_id,
+ int renderer_id,
+ int32 route_id,
+ uint64 swap_buffers_count) = 0;
+ virtual void GpuRenderingStateDidChange() = 0;
+#endif
+
+#if defined(TOOLKIT_USES_GTK)
+ virtual void CreatePluginContainer(gfx::PluginWindowHandle id) = 0;
+ virtual void DestroyPluginContainer(gfx::PluginWindowHandle id) = 0;
+ virtual void AcceleratedCompositingActivated(bool activated) = 0;
+#endif
+
+#if defined(OS_WIN)
+ virtual void WillWmDestroy() = 0;
+ virtual void ShowCompositorHostWindow(bool show) = 0;
+#endif
+
+ virtual gfx::PluginWindowHandle AcquireCompositingSurface() = 0;
+ virtual void ReleaseCompositingSurface(gfx::PluginWindowHandle surface) = 0;
+
+ // Toggles visual muting of the render view area. This is on when a
+ // constrained window is showing, for example. |color| is the shade of
+ // the overlay that covers the render view. If |animate| is true, the overlay
+ // gradually fades in; otherwise it takes effect immediately. To remove the
+ // fade effect, pass a NULL value for |color|. In this case, |animate| is
+ // ignored.
+ virtual void SetVisuallyDeemphasized(const SkColor* color, bool animate) = 0;
+
+ void set_popup_type(WebKit::WebPopupType popup_type) {
+ popup_type_ = popup_type;
+ }
+ WebKit::WebPopupType popup_type() const { return popup_type_; }
+
+ // Subclasses should override this method to do what is appropriate to set
+ // the custom background for their platform.
+ virtual void SetBackground(const SkBitmap& background);
+ const SkBitmap& background() const { return background_; }
+
+ // Returns true if the native view, |native_view|, is contained within in the
+ // widget associated with this RenderWidgetHostView.
+ virtual bool ContainsNativeView(gfx::NativeView native_view) const = 0;
+
+ virtual void UpdateAccessibilityTree(
+ const webkit_glue::WebAccessibility& tree) { }
+ virtual void OnAccessibilityNotifications(
+ const std::vector<ViewHostMsg_AccessibilityNotification_Params>& params) {
+ }
+
+ gfx::Rect reserved_contents_rect() const {
+ return reserved_rect_;
+ }
+ void set_reserved_contents_rect(const gfx::Rect& reserved_rect) {
+ reserved_rect_ = reserved_rect;
+ }
+
+ protected:
+ // Interface class only, do not construct.
+ RenderWidgetHostView() : popup_type_(WebKit::WebPopupTypeNone) {}
+
+ // Whether this view is a popup and what kind of popup it is (select,
+ // autofill...).
+ WebKit::WebPopupType popup_type_;
+
+ // A custom background to paint behind the web content. This will be tiled
+ // horizontally. Can be null, in which case we fall back to painting white.
+ SkBitmap background_;
+
+ // The current reserved area in view coordinates where contents should not be
+ // rendered to draw the resize corner, sidebar mini tabs etc.
+ gfx::Rect reserved_rect_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostView);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_RENDER_WIDGET_HOST_VIEW_H_
diff --git a/content/browser/renderer_host/resource_dispatcher_host.cc b/content/browser/renderer_host/resource_dispatcher_host.cc
new file mode 100644
index 0000000..94f5c5e
--- /dev/null
+++ b/content/browser/renderer_host/resource_dispatcher_host.cc
@@ -0,0 +1,1952 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// See http://dev.chromium.org/developers/design-documents/multi-process-resource-loading
+
+#include "content/browser/renderer_host/resource_dispatcher_host.h"
+
+#include <set>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/command_line.h"
+#include "base/message_loop.h"
+#include "base/metrics/histogram.h"
+#include "base/scoped_ptr.h"
+#include "base/shared_memory.h"
+#include "base/stl_util-inl.h"
+#include "base/time.h"
+#include "chrome/browser/cert_store.h"
+#include "chrome/browser/child_process_security_policy.h"
+#include "chrome/browser/chrome_blob_storage_context.h"
+#include "chrome/browser/cross_site_request_manager.h"
+#include "chrome/browser/download/download_file_manager.h"
+#include "chrome/browser/download/download_manager.h"
+#include "chrome/browser/download/download_request_limiter.h"
+#include "chrome/browser/download/download_util.h"
+#include "chrome/browser/download/save_file_manager.h"
+#include "chrome/browser/extensions/user_script_listener.h"
+#include "chrome/browser/external_protocol_handler.h"
+#include "chrome/browser/in_process_webkit/webkit_thread.h"
+#include "chrome/browser/net/chrome_url_request_context.h"
+#include "chrome/browser/net/url_request_tracking.h"
+#include "chrome/browser/plugin_service.h"
+#include "chrome/browser/prerender/prerender_manager.h"
+#include "chrome/browser/prerender/prerender_resource_handler.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/renderer_host/download_resource_handler.h"
+#include "chrome/browser/renderer_host/safe_browsing_resource_handler.h"
+#include "chrome/browser/renderer_host/save_file_resource_handler.h"
+#include "chrome/browser/safe_browsing/safe_browsing_service.h"
+#include "chrome/browser/ssl/ssl_client_auth_handler.h"
+#include "chrome/browser/ssl/ssl_manager.h"
+#include "chrome/browser/ui/login/login_prompt.h"
+#include "chrome/browser/worker_host/worker_service.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/render_messages_params.h"
+#include "chrome/common/url_constants.h"
+#include "content/browser/renderer_host/async_resource_handler.h"
+#include "content/browser/renderer_host/buffered_resource_handler.h"
+#include "content/browser/renderer_host/cross_site_resource_handler.h"
+#include "content/browser/renderer_host/global_request_id.h"
+#include "content/browser/renderer_host/redirect_to_file_resource_handler.h"
+#include "content/browser/renderer_host/render_view_host.h"
+#include "content/browser/renderer_host/render_view_host_delegate.h"
+#include "content/browser/renderer_host/render_view_host_notification_task.h"
+#include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
+#include "content/browser/renderer_host/resource_message_filter.h"
+#include "content/browser/renderer_host/resource_queue.h"
+#include "content/browser/renderer_host/resource_request_details.h"
+#include "content/browser/renderer_host/sync_resource_handler.h"
+#include "net/base/auth.h"
+#include "net/base/cert_status_flags.h"
+#include "net/base/cookie_monster.h"
+#include "net/base/load_flags.h"
+#include "net/base/mime_util.h"
+#include "net/base/net_errors.h"
+#include "net/base/request_priority.h"
+#include "net/base/ssl_cert_request_info.h"
+#include "net/base/upload_data.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
+#include "webkit/appcache/appcache_interceptor.h"
+#include "webkit/appcache/appcache_interfaces.h"
+#include "webkit/blob/blob_storage_controller.h"
+#include "webkit/blob/deletable_file_reference.h"
+
+// TODO(oshima): Enable this for other platforms.
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/renderer_host/offline_resource_handler.h"
+#endif
+
+using base::Time;
+using base::TimeDelta;
+using base::TimeTicks;
+using webkit_blob::DeletableFileReference;
+
+// ----------------------------------------------------------------------------
+
+// A ShutdownTask proxies a shutdown task from the UI thread to the IO thread.
+// It should be constructed on the UI thread and run in the IO thread.
+class ResourceDispatcherHost::ShutdownTask : public Task {
+ public:
+ explicit ShutdownTask(ResourceDispatcherHost* resource_dispatcher_host)
+ : rdh_(resource_dispatcher_host) {
+ }
+
+ void Run() {
+ rdh_->OnShutdown();
+ }
+
+ private:
+ ResourceDispatcherHost* rdh_;
+};
+
+namespace {
+
+// The interval for calls to ResourceDispatcherHost::UpdateLoadStates
+const int kUpdateLoadStatesIntervalMsec = 100;
+
+// Maximum number of pending data messages sent to the renderer at any
+// given time for a given request.
+const int kMaxPendingDataMessages = 20;
+
+// Maximum byte "cost" of all the outstanding requests for a renderer.
+// See delcaration of |max_outstanding_requests_cost_per_process_| for details.
+// This bound is 25MB, which allows for around 6000 outstanding requests.
+const int kMaxOutstandingRequestsCostPerProcess = 26214400;
+
+// Consults the RendererSecurity policy to determine whether the
+// ResourceDispatcherHost should service this request. A request might be
+// disallowed if the renderer is not authorized to retrieve the request URL or
+// if the renderer is attempting to upload an unauthorized file.
+bool ShouldServiceRequest(ChildProcessInfo::ProcessType process_type,
+ int child_id,
+ const ViewHostMsg_Resource_Request& request_data) {
+ if (process_type == ChildProcessInfo::PLUGIN_PROCESS)
+ return true;
+
+ if (request_data.resource_type == ResourceType::PREFETCH) {
+ prerender::PrerenderManager::RecordPrefetchTagObserved();
+ if (!ResourceDispatcherHost::is_prefetch_enabled())
+ return false;
+ }
+
+ ChildProcessSecurityPolicy* policy =
+ ChildProcessSecurityPolicy::GetInstance();
+
+ // Check if the renderer is permitted to request the requested URL.
+ if (!policy->CanRequestURL(child_id, request_data.url)) {
+ VLOG(1) << "Denied unauthorized request for "
+ << request_data.url.possibly_invalid_spec();
+ return false;
+ }
+
+ // Check if the renderer is permitted to upload the requested files.
+ if (request_data.upload_data) {
+ const std::vector<net::UploadData::Element>* uploads =
+ request_data.upload_data->elements();
+ std::vector<net::UploadData::Element>::const_iterator iter;
+ for (iter = uploads->begin(); iter != uploads->end(); ++iter) {
+ if (iter->type() == net::UploadData::TYPE_FILE &&
+ !policy->CanReadFile(child_id, iter->file_path())) {
+ NOTREACHED() << "Denied unauthorized upload of "
+ << iter->file_path().value();
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+void PopulateResourceResponse(net::URLRequest* request,
+ bool replace_extension_localization_templates,
+ ResourceResponse* response) {
+ response->response_head.status = request->status();
+ response->response_head.request_time = request->request_time();
+ response->response_head.response_time = request->response_time();
+ response->response_head.headers = request->response_headers();
+ request->GetCharset(&response->response_head.charset);
+ response->response_head.replace_extension_localization_templates =
+ replace_extension_localization_templates;
+ response->response_head.content_length = request->GetExpectedContentSize();
+ request->GetMimeType(&response->response_head.mime_type);
+ response->response_head.was_fetched_via_spdy =
+ request->was_fetched_via_spdy();
+ response->response_head.was_npn_negotiated = request->was_npn_negotiated();
+ response->response_head.was_alternate_protocol_available =
+ request->was_alternate_protocol_available();
+ response->response_head.was_fetched_via_proxy =
+ request->was_fetched_via_proxy();
+ appcache::AppCacheInterceptor::GetExtraResponseInfo(
+ request,
+ &response->response_head.appcache_id,
+ &response->response_head.appcache_manifest_url);
+}
+
+// Returns a list of all the possible error codes from the network module.
+// Note that the error codes are all positive (since histograms expect positive
+// sample values).
+std::vector<int> GetAllNetErrorCodes() {
+ std::vector<int> all_error_codes;
+#define NET_ERROR(label, value) all_error_codes.push_back(-(value));
+#include "net/base/net_error_list.h"
+#undef NET_ERROR
+ return all_error_codes;
+}
+
+#if defined(OS_WIN)
+#pragma warning(disable: 4748)
+#pragma optimize("", off)
+#endif
+
+#if defined(OS_WIN)
+#pragma optimize("", on)
+#pragma warning(default: 4748)
+#endif
+
+} // namespace
+
+ResourceDispatcherHost::ResourceDispatcherHost()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(
+ download_file_manager_(new DownloadFileManager(this))),
+ download_request_limiter_(new DownloadRequestLimiter()),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ save_file_manager_(new SaveFileManager(this))),
+ user_script_listener_(new UserScriptListener(&resource_queue_)),
+ safe_browsing_(SafeBrowsingService::CreateSafeBrowsingService()),
+ webkit_thread_(new WebKitThread),
+ request_id_(-1),
+ ALLOW_THIS_IN_INITIALIZER_LIST(method_runner_(this)),
+ is_shutdown_(false),
+ max_outstanding_requests_cost_per_process_(
+ kMaxOutstandingRequestsCostPerProcess),
+ filter_(NULL) {
+ ResourceQueue::DelegateSet resource_queue_delegates;
+ resource_queue_delegates.insert(user_script_listener_.get());
+ resource_queue_.Initialize(resource_queue_delegates);
+}
+
+ResourceDispatcherHost::~ResourceDispatcherHost() {
+ AsyncResourceHandler::GlobalCleanup();
+ STLDeleteValues(&pending_requests_);
+
+ user_script_listener_->ShutdownMainThread();
+}
+
+void ResourceDispatcherHost::Initialize() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ webkit_thread_->Initialize();
+ safe_browsing_->Initialize();
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ NewRunnableFunction(&appcache::AppCacheInterceptor::EnsureRegistered));
+}
+
+void ResourceDispatcherHost::Shutdown() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, new ShutdownTask(this));
+}
+
+void ResourceDispatcherHost::SetRequestInfo(
+ net::URLRequest* request,
+ ResourceDispatcherHostRequestInfo* info) {
+ request->SetUserData(NULL, info);
+}
+
+void ResourceDispatcherHost::OnShutdown() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ is_shutdown_ = true;
+ resource_queue_.Shutdown();
+ STLDeleteValues(&pending_requests_);
+ // Make sure we shutdown the timer now, otherwise by the time our destructor
+ // runs if the timer is still running the Task is deleted twice (once by
+ // the MessageLoop and the second time by RepeatingTimer).
+ update_load_states_timer_.Stop();
+
+ // Clear blocked requests if any left.
+ // Note that we have to do this in 2 passes as we cannot call
+ // CancelBlockedRequestsForRoute while iterating over
+ // blocked_requests_map_, as it modifies it.
+ std::set<ProcessRouteIDs> ids;
+ for (BlockedRequestMap::const_iterator iter = blocked_requests_map_.begin();
+ iter != blocked_requests_map_.end(); ++iter) {
+ std::pair<std::set<ProcessRouteIDs>::iterator, bool> result =
+ ids.insert(iter->first);
+ // We should not have duplicates.
+ DCHECK(result.second);
+ }
+ for (std::set<ProcessRouteIDs>::const_iterator iter = ids.begin();
+ iter != ids.end(); ++iter) {
+ CancelBlockedRequestsForRoute(iter->first, iter->second);
+ }
+}
+
+bool ResourceDispatcherHost::HandleExternalProtocol(int request_id,
+ int child_id,
+ int route_id,
+ const GURL& url,
+ ResourceType::Type type,
+ ResourceHandler* handler) {
+ if (!ResourceType::IsFrame(type) || net::URLRequest::IsHandledURL(url))
+ return false;
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableFunction(
+ &ExternalProtocolHandler::LaunchUrl, url, child_id, route_id));
+
+ handler->OnResponseCompleted(
+ request_id,
+ net::URLRequestStatus(net::URLRequestStatus::FAILED, net::ERR_ABORTED),
+ std::string()); // No security info necessary.
+ return true;
+}
+
+bool ResourceDispatcherHost::OnMessageReceived(const IPC::Message& message,
+ ResourceMessageFilter* filter,
+ bool* message_was_ok) {
+ filter_ = filter;
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(ResourceDispatcherHost, message, *message_was_ok)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_RequestResource, OnRequestResource)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_SyncLoad, OnSyncLoad)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ReleaseDownloadedFile,
+ OnReleaseDownloadedFile)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DataReceived_ACK, OnDataReceivedACK)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_DataDownloaded_ACK, OnDataDownloadedACK)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_UploadProgress_ACK, OnUploadProgressACK)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_CancelRequest, OnCancelRequest)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_FollowRedirect, OnFollowRedirect)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_ClosePage_ACK, OnClosePageACK)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP_EX()
+
+ filter_ = NULL;
+ return handled;
+}
+
+void ResourceDispatcherHost::OnRequestResource(
+ const IPC::Message& message,
+ int request_id,
+ const ViewHostMsg_Resource_Request& request_data) {
+ BeginRequest(request_id, request_data, NULL, message.routing_id());
+}
+
+// Begins a resource request with the given params on behalf of the specified
+// child process. Responses will be dispatched through the given receiver. The
+// process ID is used to lookup TabContents from routing_id's in the case of a
+// request from a renderer. request_context is the cookie/cache context to be
+// used for this request.
+//
+// If sync_result is non-null, then a SyncLoad reply will be generated, else
+// a normal asynchronous set of response messages will be generated.
+void ResourceDispatcherHost::OnSyncLoad(
+ int request_id,
+ const ViewHostMsg_Resource_Request& request_data,
+ IPC::Message* sync_result) {
+ BeginRequest(request_id, request_data, sync_result,
+ sync_result->routing_id());
+}
+
+void ResourceDispatcherHost::BeginRequest(
+ int request_id,
+ const ViewHostMsg_Resource_Request& request_data,
+ IPC::Message* sync_result, // only valid for sync
+ int route_id) {
+ ChildProcessInfo::ProcessType process_type = filter_->process_type();
+ int child_id = filter_->child_id();
+
+ ChromeURLRequestContext* context = filter_->GetURLRequestContext(
+ request_data);
+
+ // Might need to resolve the blob references in the upload data.
+ if (request_data.upload_data && context) {
+ context->blob_storage_context()->controller()->
+ ResolveBlobReferencesInUploadData(request_data.upload_data.get());
+ }
+
+ if (is_shutdown_ ||
+ !ShouldServiceRequest(process_type, child_id, request_data)) {
+ net::URLRequestStatus status(net::URLRequestStatus::FAILED,
+ net::ERR_ABORTED);
+ if (sync_result) {
+ SyncLoadResult result;
+ result.status = status;
+ ViewHostMsg_SyncLoad::WriteReplyParams(sync_result, result);
+ filter_->Send(sync_result);
+ } else {
+ // Tell the renderer that this request was disallowed.
+ filter_->Send(new ViewMsg_Resource_RequestComplete(
+ route_id,
+ request_id,
+ status,
+ std::string(), // No security info needed, connection was not
+ base::Time())); // established.
+ }
+ return;
+ }
+
+ // Ensure the Chrome plugins are loaded, as they may intercept network
+ // requests. Does nothing if they are already loaded.
+ // TODO(mpcomplete): This takes 200 ms! Investigate parallelizing this by
+ // starting the load earlier in a BG thread.
+ PluginService::GetInstance()->LoadChromePlugins(this);
+
+ // Construct the event handler.
+ scoped_refptr<ResourceHandler> handler;
+ if (sync_result) {
+ handler = new SyncResourceHandler(
+ filter_, request_data.url, sync_result, this);
+ } else {
+ handler = new AsyncResourceHandler(
+ filter_, route_id, request_data.url, this);
+ }
+
+ // The RedirectToFileResourceHandler depends on being next in the chain.
+ if (request_data.download_to_file)
+ handler = new RedirectToFileResourceHandler(handler, child_id, this);
+
+ if (HandleExternalProtocol(request_id, child_id, route_id,
+ request_data.url, request_data.resource_type,
+ handler)) {
+ return;
+ }
+
+ // Construct the request.
+ net::URLRequest* request = new net::URLRequest(request_data.url, this);
+ request->set_method(request_data.method);
+ request->set_first_party_for_cookies(request_data.first_party_for_cookies);
+ request->set_referrer(CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kNoReferrers) ? std::string() : request_data.referrer.spec());
+ net::HttpRequestHeaders headers;
+ headers.AddHeadersFromString(request_data.headers);
+ request->SetExtraRequestHeaders(headers);
+
+ int load_flags = request_data.load_flags;
+ // Although EV status is irrelevant to sub-frames and sub-resources, we have
+ // to perform EV certificate verification on all resources because an HTTP
+ // keep-alive connection created to load a sub-frame or a sub-resource could
+ // be reused to load a main frame.
+ load_flags |= net::LOAD_VERIFY_EV_CERT;
+ if (request_data.resource_type == ResourceType::MAIN_FRAME) {
+ load_flags |= net::LOAD_MAIN_FRAME;
+ } else if (request_data.resource_type == ResourceType::SUB_FRAME) {
+ load_flags |= net::LOAD_SUB_FRAME;
+ } else if (request_data.resource_type == ResourceType::PREFETCH) {
+ load_flags |= net::LOAD_PREFETCH;
+ }
+ // Raw headers are sensitive, as they inclide Cookie/Set-Cookie, so only
+ // allow requesting them if requestor has ReadRawCookies permission.
+ if ((load_flags & net::LOAD_REPORT_RAW_HEADERS)
+ && !ChildProcessSecurityPolicy::GetInstance()->
+ CanReadRawCookies(child_id)) {
+ VLOG(1) << "Denied unathorized request for raw headers";
+ load_flags &= ~net::LOAD_REPORT_RAW_HEADERS;
+ }
+
+ request->set_load_flags(load_flags);
+ request->set_context(context);
+ request->set_priority(DetermineRequestPriority(request_data.resource_type));
+
+ // Set upload data.
+ uint64 upload_size = 0;
+ if (request_data.upload_data) {
+ request->set_upload(request_data.upload_data);
+ upload_size = request_data.upload_data->GetContentLength();
+ }
+
+ // Install a PrerenderResourceHandler if the requested URL could
+ // be prerendered. This should be in front of the [a]syncResourceHandler,
+ // but after the BufferedResourceHandler since it depends on the MIME
+ // sniffing capabilities in the BufferedResourceHandler.
+ prerender::PrerenderResourceHandler* pre_handler =
+ prerender::PrerenderResourceHandler::MaybeCreate(*request,
+ context,
+ handler);
+ if (pre_handler)
+ handler = pre_handler;
+
+ // Install a CrossSiteResourceHandler if this request is coming from a
+ // RenderViewHost with a pending cross-site request. We only check this for
+ // MAIN_FRAME requests. Unblock requests only come from a blocked page, do
+ // not count as cross-site, otherwise it gets blocked indefinitely.
+ if (request_data.resource_type == ResourceType::MAIN_FRAME &&
+ process_type == ChildProcessInfo::RENDER_PROCESS &&
+ CrossSiteRequestManager::GetInstance()->
+ HasPendingCrossSiteRequest(child_id, route_id)) {
+ // Wrap the event handler to be sure the current page's onunload handler
+ // has a chance to run before we render the new page.
+ handler = new CrossSiteResourceHandler(handler,
+ child_id,
+ route_id,
+ this);
+ }
+
+ // Insert a buffered event handler before the actual one.
+ handler = new BufferedResourceHandler(handler, this, request);
+
+ // Insert safe browsing at the front of the chain, so it gets to decide
+ // on policies first.
+ if (safe_browsing_->enabled()) {
+ handler = CreateSafeBrowsingResourceHandler(handler, child_id, route_id,
+ request_data.resource_type);
+ }
+
+#if defined(OS_CHROMEOS)
+ // We check offline first, then check safe browsing so that we still can block
+ // unsafe site after we remove offline page.
+ handler =
+ new OfflineResourceHandler(handler, child_id, route_id, this, request);
+#endif
+
+ // Make extra info and read footer (contains request ID).
+ ResourceDispatcherHostRequestInfo* extra_info =
+ new ResourceDispatcherHostRequestInfo(
+ handler,
+ process_type,
+ child_id,
+ route_id,
+ request_id,
+ request_data.resource_type,
+ upload_size,
+ false, // is download
+ ResourceType::IsFrame(request_data.resource_type), // allow_download
+ request_data.has_user_gesture,
+ request_data.host_renderer_id,
+ request_data.host_render_view_id);
+ ApplyExtensionLocalizationFilter(request_data.url, request_data.resource_type,
+ extra_info);
+ SetRequestInfo(request, extra_info); // Request takes ownership.
+ chrome_browser_net::SetOriginPIDForRequest(
+ request_data.origin_pid, request);
+
+ if (request->url().SchemeIs(chrome::kBlobScheme) && context) {
+ // Hang on to a reference to ensure the blob is not released prior
+ // to the job being started.
+ webkit_blob::BlobStorageController* controller =
+ context->blob_storage_context()->controller();
+ extra_info->set_requested_blob_data(
+ controller->GetBlobDataFromUrl(request->url()));
+ }
+
+ // Have the appcache associate its extra info with the request.
+ appcache::AppCacheInterceptor::SetExtraRequestInfo(
+ request, context ? context->appcache_service() : NULL, child_id,
+ request_data.appcache_host_id, request_data.resource_type);
+
+ BeginRequestInternal(request);
+}
+
+void ResourceDispatcherHost::OnReleaseDownloadedFile(int request_id) {
+ DCHECK(pending_requests_.end() ==
+ pending_requests_.find(
+ GlobalRequestID(filter_->child_id(), request_id)));
+ UnregisterDownloadedTempFile(filter_->child_id(), request_id);
+}
+
+void ResourceDispatcherHost::OnDataReceivedACK(int request_id) {
+ DataReceivedACK(filter_->child_id(), request_id);
+}
+
+void ResourceDispatcherHost::DataReceivedACK(int child_id,
+ int request_id) {
+ PendingRequestList::iterator i = pending_requests_.find(
+ GlobalRequestID(child_id, request_id));
+ if (i == pending_requests_.end())
+ return;
+
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(i->second);
+
+ // Decrement the number of pending data messages.
+ info->DecrementPendingDataCount();
+
+ // If the pending data count was higher than the max, resume the request.
+ if (info->pending_data_count() == kMaxPendingDataMessages) {
+ // Decrement the pending data count one more time because we also
+ // incremented it before pausing the request.
+ info->DecrementPendingDataCount();
+
+ // Resume the request.
+ PauseRequest(child_id, request_id, false);
+ }
+}
+
+void ResourceDispatcherHost::OnDataDownloadedACK(int request_id) {
+ // TODO(michaeln): maybe throttle DataDownloaded messages
+}
+
+void ResourceDispatcherHost::RegisterDownloadedTempFile(
+ int child_id, int request_id, DeletableFileReference* reference) {
+ registered_temp_files_[child_id][request_id] = reference;
+ ChildProcessSecurityPolicy::GetInstance()->GrantReadFile(
+ child_id, reference->path());
+}
+
+void ResourceDispatcherHost::UnregisterDownloadedTempFile(
+ int child_id, int request_id) {
+ DeletableFilesMap& map = registered_temp_files_[child_id];
+ DeletableFilesMap::iterator found = map.find(request_id);
+ if (found == map.end())
+ return;
+
+ ChildProcessSecurityPolicy::GetInstance()->RevokeAllPermissionsForFile(
+ child_id, found->second->path());
+ map.erase(found);
+}
+
+bool ResourceDispatcherHost::Send(IPC::Message* message) {
+ delete message;
+ return false;
+}
+
+void ResourceDispatcherHost::OnUploadProgressACK(int request_id) {
+ int child_id = filter_->child_id();
+ PendingRequestList::iterator i = pending_requests_.find(
+ GlobalRequestID(child_id, request_id));
+ if (i == pending_requests_.end())
+ return;
+
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(i->second);
+ info->set_waiting_for_upload_progress_ack(false);
+}
+
+void ResourceDispatcherHost::OnCancelRequest(int request_id) {
+ CancelRequest(filter_->child_id(), request_id, true);
+}
+
+void ResourceDispatcherHost::OnFollowRedirect(
+ int request_id,
+ bool has_new_first_party_for_cookies,
+ const GURL& new_first_party_for_cookies) {
+ FollowDeferredRedirect(filter_->child_id(), request_id,
+ has_new_first_party_for_cookies,
+ new_first_party_for_cookies);
+}
+
+ResourceHandler* ResourceDispatcherHost::CreateSafeBrowsingResourceHandler(
+ ResourceHandler* handler, int child_id, int route_id,
+ ResourceType::Type resource_type) {
+ return new SafeBrowsingResourceHandler(
+ handler, child_id, route_id, resource_type, safe_browsing_, this);
+}
+
+ResourceDispatcherHostRequestInfo*
+ResourceDispatcherHost::CreateRequestInfoForBrowserRequest(
+ ResourceHandler* handler, int child_id, int route_id, bool download) {
+ return new ResourceDispatcherHostRequestInfo(handler,
+ ChildProcessInfo::RENDER_PROCESS,
+ child_id,
+ route_id,
+ request_id_,
+ ResourceType::SUB_RESOURCE,
+ 0, // upload_size
+ download, // is_download
+ download, // allow_download
+ false, // has_user_gesture
+ -1, // host renderer id
+ -1); // host render view id
+}
+
+void ResourceDispatcherHost::OnClosePageACK(
+ const ViewMsg_ClosePage_Params& params) {
+ if (params.for_cross_site_transition) {
+ // Closes for cross-site transitions are handled such that the cross-site
+ // transition continues.
+ GlobalRequestID global_id(params.new_render_process_host_id,
+ params.new_request_id);
+ PendingRequestList::iterator i = pending_requests_.find(global_id);
+ if (i != pending_requests_.end()) {
+ // The response we were meant to resume could have already been canceled.
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(i->second);
+ if (info->cross_site_handler())
+ info->cross_site_handler()->ResumeResponse();
+ }
+ } else {
+ // This is a tab close, so just forward the message to close it.
+ DCHECK(params.new_render_process_host_id == -1);
+ DCHECK(params.new_request_id == -1);
+ CallRenderViewHost(params.closing_process_id,
+ params.closing_route_id,
+ &RenderViewHost::ClosePageIgnoringUnloadEvents);
+ }
+}
+
+// We are explicitly forcing the download of 'url'.
+void ResourceDispatcherHost::BeginDownload(
+ const GURL& url,
+ const GURL& referrer,
+ const DownloadSaveInfo& save_info,
+ bool prompt_for_save_location,
+ int child_id,
+ int route_id,
+ net::URLRequestContext* request_context) {
+ if (is_shutdown_)
+ return;
+
+ // Check if the renderer is permitted to request the requested URL.
+ if (!ChildProcessSecurityPolicy::GetInstance()->
+ CanRequestURL(child_id, url)) {
+ VLOG(1) << "Denied unauthorized download request for "
+ << url.possibly_invalid_spec();
+ return;
+ }
+
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableFunction(&download_util::NotifyDownloadInitiated,
+ child_id, route_id));
+
+ // Ensure the Chrome plugins are loaded, as they may intercept network
+ // requests. Does nothing if they are already loaded.
+ PluginService::GetInstance()->LoadChromePlugins(this);
+ net::URLRequest* request = new net::URLRequest(url, this);
+
+ request_id_--;
+
+ scoped_refptr<ResourceHandler> handler(
+ new DownloadResourceHandler(this,
+ child_id,
+ route_id,
+ request_id_,
+ url,
+ download_file_manager_.get(),
+ request,
+ prompt_for_save_location,
+ save_info));
+
+ if (safe_browsing_->enabled()) {
+ handler = CreateSafeBrowsingResourceHandler(handler, child_id, route_id,
+ ResourceType::MAIN_FRAME);
+ }
+
+ if (!net::URLRequest::IsHandledURL(url)) {
+ VLOG(1) << "Download request for unsupported protocol: "
+ << url.possibly_invalid_spec();
+ return;
+ }
+
+ request->set_method("GET");
+ request->set_referrer(CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kNoReferrers) ? std::string() : referrer.spec());
+ request->set_context(request_context);
+ request->set_load_flags(request->load_flags() |
+ net::LOAD_IS_DOWNLOAD);
+
+ ResourceDispatcherHostRequestInfo* extra_info =
+ CreateRequestInfoForBrowserRequest(handler, child_id, route_id, true);
+ SetRequestInfo(request, extra_info); // Request takes ownership.
+
+ BeginRequestInternal(request);
+}
+
+// This function is only used for saving feature.
+void ResourceDispatcherHost::BeginSaveFile(
+ const GURL& url,
+ const GURL& referrer,
+ int child_id,
+ int route_id,
+ net::URLRequestContext* request_context) {
+ if (is_shutdown_)
+ return;
+
+ // Ensure the Chrome plugins are loaded, as they may intercept network
+ // requests. Does nothing if they are already loaded.
+ PluginService::GetInstance()->LoadChromePlugins(this);
+
+ scoped_refptr<ResourceHandler> handler(
+ new SaveFileResourceHandler(child_id,
+ route_id,
+ url,
+ save_file_manager_.get()));
+ request_id_--;
+
+ bool known_proto = net::URLRequest::IsHandledURL(url);
+ if (!known_proto) {
+ // Since any URLs which have non-standard scheme have been filtered
+ // by save manager(see GURL::SchemeIsStandard). This situation
+ // should not happen.
+ NOTREACHED();
+ return;
+ }
+
+ net::URLRequest* request = new net::URLRequest(url, this);
+ request->set_method("GET");
+ request->set_referrer(CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kNoReferrers) ? std::string() : referrer.spec());
+ // So far, for saving page, we need fetch content from cache, in the
+ // future, maybe we can use a configuration to configure this behavior.
+ request->set_load_flags(net::LOAD_PREFERRING_CACHE);
+ request->set_context(request_context);
+
+ // Since we're just saving some resources we need, disallow downloading.
+ ResourceDispatcherHostRequestInfo* extra_info =
+ CreateRequestInfoForBrowserRequest(handler, child_id, route_id, false);
+ SetRequestInfo(request, extra_info); // Request takes ownership.
+
+ BeginRequestInternal(request);
+}
+
+void ResourceDispatcherHost::FollowDeferredRedirect(
+ int child_id,
+ int request_id,
+ bool has_new_first_party_for_cookies,
+ const GURL& new_first_party_for_cookies) {
+ PendingRequestList::iterator i = pending_requests_.find(
+ GlobalRequestID(child_id, request_id));
+ if (i == pending_requests_.end() || !i->second->status().is_success()) {
+ DLOG(WARNING) << "FollowDeferredRedirect for invalid request";
+ return;
+ }
+
+ if (has_new_first_party_for_cookies)
+ i->second->set_first_party_for_cookies(new_first_party_for_cookies);
+ i->second->FollowDeferredRedirect();
+}
+
+void ResourceDispatcherHost::StartDeferredRequest(int process_unique_id,
+ int request_id) {
+ GlobalRequestID global_id(process_unique_id, request_id);
+ PendingRequestList::iterator i = pending_requests_.find(global_id);
+ if (i == pending_requests_.end()) {
+ // The request may have been destroyed
+ LOG(WARNING) << "Trying to resume a non-existent request ("
+ << process_unique_id << ", " << request_id << ")";
+ return;
+ }
+
+ // TODO(eroman): are there other considerations for paused or blocked
+ // requests?
+
+ net::URLRequest* request = i->second;
+ InsertIntoResourceQueue(request, *InfoForRequest(request));
+}
+
+bool ResourceDispatcherHost::WillSendData(int child_id,
+ int request_id) {
+ PendingRequestList::iterator i = pending_requests_.find(
+ GlobalRequestID(child_id, request_id));
+ if (i == pending_requests_.end()) {
+ NOTREACHED() << "WillSendData for invalid request";
+ return false;
+ }
+
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(i->second);
+
+ info->IncrementPendingDataCount();
+ if (info->pending_data_count() > kMaxPendingDataMessages) {
+ // We reached the max number of data messages that can be sent to
+ // the renderer for a given request. Pause the request and wait for
+ // the renderer to start processing them before resuming it.
+ PauseRequest(child_id, request_id, true);
+ return false;
+ }
+
+ return true;
+}
+
+void ResourceDispatcherHost::PauseRequest(int child_id,
+ int request_id,
+ bool pause) {
+ GlobalRequestID global_id(child_id, request_id);
+ PendingRequestList::iterator i = pending_requests_.find(global_id);
+ if (i == pending_requests_.end()) {
+ DLOG(WARNING) << "Pausing a request that wasn't found";
+ return;
+ }
+
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(i->second);
+ int pause_count = info->pause_count() + (pause ? 1 : -1);
+ if (pause_count < 0) {
+ NOTREACHED(); // Unbalanced call to pause.
+ return;
+ }
+ info->set_pause_count(pause_count);
+
+ VLOG(1) << "To pause (" << pause << "): " << i->second->url().spec();
+
+ // If we're resuming, kick the request to start reading again. Run the read
+ // asynchronously to avoid recursion problems.
+ if (info->pause_count() == 0) {
+ MessageLoop::current()->PostTask(FROM_HERE,
+ method_runner_.NewRunnableMethod(
+ &ResourceDispatcherHost::ResumeRequest, global_id));
+ }
+}
+
+int ResourceDispatcherHost::GetOutstandingRequestsMemoryCost(
+ int child_id) const {
+ OutstandingRequestsMemoryCostMap::const_iterator entry =
+ outstanding_requests_memory_cost_map_.find(child_id);
+ return (entry == outstanding_requests_memory_cost_map_.end()) ?
+ 0 : entry->second;
+}
+
+// The object died, so cancel and detach all requests associated with it except
+// for downloads, which belong to the browser process even if initiated via a
+// renderer.
+void ResourceDispatcherHost::CancelRequestsForProcess(int child_id) {
+ CancelRequestsForRoute(child_id, -1 /* cancel all */);
+ registered_temp_files_.erase(child_id);
+}
+
+void ResourceDispatcherHost::CancelRequestsForRoute(int child_id,
+ int route_id) {
+ // Since pending_requests_ is a map, we first build up a list of all of the
+ // matching requests to be cancelled, and then we cancel them. Since there
+ // may be more than one request to cancel, we cannot simply hold onto the map
+ // iterators found in the first loop.
+
+ // Find the global ID of all matching elements.
+ std::vector<GlobalRequestID> matching_requests;
+ for (PendingRequestList::const_iterator i = pending_requests_.begin();
+ i != pending_requests_.end(); ++i) {
+ if (i->first.child_id == child_id) {
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(i->second);
+ if (!info->is_download() &&
+ (route_id == -1 || route_id == info->route_id())) {
+ matching_requests.push_back(
+ GlobalRequestID(child_id, i->first.request_id));
+ }
+ }
+ }
+
+ // Remove matches.
+ for (size_t i = 0; i < matching_requests.size(); ++i) {
+ PendingRequestList::iterator iter =
+ pending_requests_.find(matching_requests[i]);
+ // Although every matching request was in pending_requests_ when we built
+ // matching_requests, it is normal for a matching request to be not found
+ // in pending_requests_ after we have removed some matching requests from
+ // pending_requests_. For example, deleting a net::URLRequest that has
+ // exclusive (write) access to an HTTP cache entry may unblock another
+ // net::URLRequest that needs exclusive access to the same cache entry, and
+ // that net::URLRequest may complete and remove itself from
+ // pending_requests_. So we need to check that iter is not equal to
+ // pending_requests_.end().
+ if (iter != pending_requests_.end())
+ RemovePendingRequest(iter);
+ }
+
+ // Now deal with blocked requests if any.
+ if (route_id != -1) {
+ if (blocked_requests_map_.find(std::pair<int, int>(child_id, route_id)) !=
+ blocked_requests_map_.end()) {
+ CancelBlockedRequestsForRoute(child_id, route_id);
+ }
+ } else {
+ // We have to do all render views for the process |child_id|.
+ // Note that we have to do this in 2 passes as we cannot call
+ // CancelBlockedRequestsForRoute while iterating over
+ // blocked_requests_map_, as it modifies it.
+ std::set<int> route_ids;
+ for (BlockedRequestMap::const_iterator iter = blocked_requests_map_.begin();
+ iter != blocked_requests_map_.end(); ++iter) {
+ if (iter->first.first == child_id)
+ route_ids.insert(iter->first.second);
+ }
+ for (std::set<int>::const_iterator iter = route_ids.begin();
+ iter != route_ids.end(); ++iter) {
+ CancelBlockedRequestsForRoute(child_id, *iter);
+ }
+ }
+}
+
+// Cancels the request and removes it from the list.
+void ResourceDispatcherHost::RemovePendingRequest(int child_id,
+ int request_id) {
+ PendingRequestList::iterator i = pending_requests_.find(
+ GlobalRequestID(child_id, request_id));
+ if (i == pending_requests_.end()) {
+ NOTREACHED() << "Trying to remove a request that's not here";
+ return;
+ }
+ RemovePendingRequest(i);
+}
+
+void ResourceDispatcherHost::RemovePendingRequest(
+ const PendingRequestList::iterator& iter) {
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(iter->second);
+
+ // Remove the memory credit that we added when pushing the request onto
+ // the pending list.
+ IncrementOutstandingRequestsMemoryCost(-1 * info->memory_cost(),
+ info->child_id());
+
+ // Notify interested parties that the request object is going away.
+ if (info->login_handler())
+ info->login_handler()->OnRequestCancelled();
+ if (info->ssl_client_auth_handler())
+ info->ssl_client_auth_handler()->OnRequestCancelled();
+ resource_queue_.RemoveRequest(iter->first);
+
+ delete iter->second;
+ pending_requests_.erase(iter);
+
+ // If we have no more pending requests, then stop the load state monitor
+ if (pending_requests_.empty())
+ update_load_states_timer_.Stop();
+}
+
+// net::URLRequest::Delegate ---------------------------------------------------
+
+void ResourceDispatcherHost::OnReceivedRedirect(net::URLRequest* request,
+ const GURL& new_url,
+ bool* defer_redirect) {
+ VLOG(1) << "OnReceivedRedirect: " << request->url().spec();
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
+
+ DCHECK(request->status().is_success());
+
+ if (info->process_type() != ChildProcessInfo::PLUGIN_PROCESS &&
+ !ChildProcessSecurityPolicy::GetInstance()->
+ CanRequestURL(info->child_id(), new_url)) {
+ VLOG(1) << "Denied unauthorized request for "
+ << new_url.possibly_invalid_spec();
+
+ // Tell the renderer that this request was disallowed.
+ CancelRequestInternal(request, false);
+ return;
+ }
+
+ NotifyReceivedRedirect(request, info->child_id(), new_url);
+
+ if (HandleExternalProtocol(info->request_id(), info->child_id(),
+ info->route_id(), new_url,
+ info->resource_type(), info->resource_handler())) {
+ // The request is complete so we can remove it.
+ RemovePendingRequest(info->child_id(), info->request_id());
+ return;
+ }
+
+ scoped_refptr<ResourceResponse> response(new ResourceResponse);
+ PopulateResourceResponse(request,
+ info->replace_extension_localization_templates(), response);
+ if (!info->resource_handler()->OnRequestRedirected(info->request_id(),
+ new_url,
+ response, defer_redirect))
+ CancelRequestInternal(request, false);
+}
+
+void ResourceDispatcherHost::OnAuthRequired(
+ net::URLRequest* request,
+ net::AuthChallengeInfo* auth_info) {
+ if (request->load_flags() & net::LOAD_PREFETCH) {
+ request->CancelAuth();
+ return;
+ }
+ // Create a login dialog on the UI thread to get authentication data,
+ // or pull from cache and continue on the IO thread.
+ // TODO(mpcomplete): We should block the parent tab while waiting for
+ // authentication.
+ // That would also solve the problem of the net::URLRequest being cancelled
+ // before we receive authentication.
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
+ DCHECK(!info->login_handler()) <<
+ "OnAuthRequired called with login_handler pending";
+ info->set_login_handler(CreateLoginPrompt(auth_info, request));
+}
+
+void ResourceDispatcherHost::OnCertificateRequested(
+ net::URLRequest* request,
+ net::SSLCertRequestInfo* cert_request_info) {
+ DCHECK(request);
+
+ if (cert_request_info->client_certs.empty()) {
+ // No need to query the user if there are no certs to choose from.
+ request->ContinueWithCertificate(NULL);
+ return;
+ }
+
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
+ DCHECK(!info->ssl_client_auth_handler()) <<
+ "OnCertificateRequested called with ssl_client_auth_handler pending";
+ info->set_ssl_client_auth_handler(
+ new SSLClientAuthHandler(request, cert_request_info));
+ info->ssl_client_auth_handler()->SelectCertificate();
+}
+
+void ResourceDispatcherHost::OnSSLCertificateError(
+ net::URLRequest* request,
+ int cert_error,
+ net::X509Certificate* cert) {
+ DCHECK(request);
+ SSLManager::OnSSLCertificateError(this, request, cert_error, cert);
+}
+
+void ResourceDispatcherHost::OnGetCookies(
+ net::URLRequest* request,
+ bool blocked_by_policy) {
+ VLOG(1) << "OnGetCookies: " << request->url().spec();
+
+ int render_process_id, render_view_id;
+ if (!RenderViewForRequest(request, &render_process_id, &render_view_id))
+ return;
+
+ net::URLRequestContext* context = request->context();
+
+ net::CookieMonster* cookie_monster =
+ context->cookie_store()->GetCookieMonster();
+ net::CookieList cookie_list =
+ cookie_monster->GetAllCookiesForURL(request->url());
+ CallRenderViewHostContentSettingsDelegate(
+ render_process_id, render_view_id,
+ &RenderViewHostDelegate::ContentSettings::OnCookiesRead,
+ request->url(), cookie_list, blocked_by_policy);
+}
+
+void ResourceDispatcherHost::OnSetCookie(net::URLRequest* request,
+ const std::string& cookie_line,
+ const net::CookieOptions& options,
+ bool blocked_by_policy) {
+ VLOG(1) << "OnSetCookie: " << request->url().spec();
+
+ int render_process_id, render_view_id;
+ if (!RenderViewForRequest(request, &render_process_id, &render_view_id))
+ return;
+
+ CallRenderViewHostContentSettingsDelegate(
+ render_process_id, render_view_id,
+ &RenderViewHostDelegate::ContentSettings::OnCookieChanged,
+ request->url(), cookie_line, options, blocked_by_policy);
+}
+
+void ResourceDispatcherHost::OnResponseStarted(net::URLRequest* request) {
+ VLOG(1) << "OnResponseStarted: " << request->url().spec();
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
+ if (PauseRequestIfNeeded(info)) {
+ VLOG(1) << "OnResponseStarted pausing: " << request->url().spec();
+ return;
+ }
+
+ if (request->status().is_success()) {
+ // We want to send a final upload progress message prior to sending
+ // the response complete message even if we're waiting for an ack to
+ // to a previous upload progress message.
+ info->set_waiting_for_upload_progress_ack(false);
+ MaybeUpdateUploadProgress(info, request);
+
+ if (!CompleteResponseStarted(request)) {
+ CancelRequestInternal(request, false);
+ } else {
+ // Check if the handler paused the request in their OnResponseStarted.
+ if (PauseRequestIfNeeded(info)) {
+ VLOG(1) << "OnResponseStarted pausing2: " << request->url().spec();
+ return;
+ }
+
+ StartReading(request);
+ }
+ } else {
+ OnResponseCompleted(request);
+ }
+}
+
+bool ResourceDispatcherHost::CompleteResponseStarted(net::URLRequest* request) {
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
+
+ scoped_refptr<ResourceResponse> response(new ResourceResponse);
+ PopulateResourceResponse(request,
+ info->replace_extension_localization_templates(), response);
+
+ if (request->ssl_info().cert) {
+ int cert_id =
+ CertStore::GetInstance()->StoreCert(request->ssl_info().cert,
+ info->child_id());
+ response->response_head.security_info =
+ SSLManager::SerializeSecurityInfo(
+ cert_id, request->ssl_info().cert_status,
+ request->ssl_info().security_bits,
+ request->ssl_info().connection_status);
+ } else {
+ // We should not have any SSL state.
+ DCHECK(!request->ssl_info().cert_status &&
+ request->ssl_info().security_bits == -1 &&
+ !request->ssl_info().connection_status);
+ }
+
+ NotifyResponseStarted(request, info->child_id());
+ info->set_called_on_response_started(true);
+ return info->resource_handler()->OnResponseStarted(info->request_id(),
+ response.get());
+}
+
+void ResourceDispatcherHost::CancelRequest(int child_id,
+ int request_id,
+ bool from_renderer) {
+ PendingRequestList::iterator i = pending_requests_.find(
+ GlobalRequestID(child_id, request_id));
+ if (i == pending_requests_.end()) {
+ // We probably want to remove this warning eventually, but I wanted to be
+ // able to notice when this happens during initial development since it
+ // should be rare and may indicate a bug.
+ DLOG(WARNING) << "Canceling a request that wasn't found";
+ return;
+ }
+ CancelRequestInternal(i->second, from_renderer);
+}
+
+void ResourceDispatcherHost::CancelRequestInternal(net::URLRequest* request,
+ bool from_renderer) {
+ VLOG(1) << "CancelRequest: " << request->url().spec();
+
+ // WebKit will send us a cancel for downloads since it no longer handles them.
+ // In this case, ignore the cancel since we handle downloads in the browser.
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
+ if (!from_renderer || !info->is_download()) {
+ if (info->login_handler()) {
+ info->login_handler()->OnRequestCancelled();
+ info->set_login_handler(NULL);
+ }
+ if (info->ssl_client_auth_handler()) {
+ info->ssl_client_auth_handler()->OnRequestCancelled();
+ info->set_ssl_client_auth_handler(NULL);
+ }
+ request->Cancel();
+ // Our callers assume |request| is valid after we return.
+ DCHECK(IsValidRequest(request));
+ }
+
+ // Do not remove from the pending requests, as the request will still
+ // call AllDataReceived, and may even have more data before it does
+ // that.
+}
+
+int ResourceDispatcherHost::IncrementOutstandingRequestsMemoryCost(
+ int cost,
+ int child_id) {
+ // Retrieve the previous value (defaulting to 0 if not found).
+ OutstandingRequestsMemoryCostMap::iterator prev_entry =
+ outstanding_requests_memory_cost_map_.find(child_id);
+ int new_cost = 0;
+ if (prev_entry != outstanding_requests_memory_cost_map_.end())
+ new_cost = prev_entry->second;
+
+ // Insert/update the total; delete entries when their value reaches 0.
+ new_cost += cost;
+ CHECK(new_cost >= 0);
+ if (new_cost == 0)
+ outstanding_requests_memory_cost_map_.erase(prev_entry);
+ else
+ outstanding_requests_memory_cost_map_[child_id] = new_cost;
+
+ return new_cost;
+}
+
+// static
+int ResourceDispatcherHost::CalculateApproximateMemoryCost(
+ net::URLRequest* request) {
+ // The following fields should be a minor size contribution (experimentally
+ // on the order of 100). However since they are variable length, it could
+ // in theory be a sizeable contribution.
+ int strings_cost = request->extra_request_headers().ToString().size() +
+ request->original_url().spec().size() +
+ request->referrer().size() +
+ request->method().size();
+
+ int upload_cost = 0;
+
+ // TODO(eroman): don't enable the upload throttling until we have data
+ // showing what a reasonable limit is (limiting to 25MB of uploads may
+ // be too restrictive).
+#if 0
+ // Sum all the (non-file) upload data attached to the request, if any.
+ if (request->has_upload()) {
+ const std::vector<net::UploadData::Element>& uploads =
+ request->get_upload()->elements();
+ std::vector<net::UploadData::Element>::const_iterator iter;
+ for (iter = uploads.begin(); iter != uploads.end(); ++iter) {
+ if (iter->type() == net::UploadData::TYPE_BYTES) {
+ int64 element_size = iter->GetContentLength();
+ // This cast should not result in truncation.
+ upload_cost += static_cast<int>(element_size);
+ }
+ }
+ }
+#endif
+
+ // Note that this expression will typically be dominated by:
+ // |kAvgBytesPerOutstandingRequest|.
+ return kAvgBytesPerOutstandingRequest + strings_cost + upload_cost;
+}
+
+void ResourceDispatcherHost::BeginRequestInternal(net::URLRequest* request) {
+ DCHECK(!request->is_pending());
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
+
+ // Add the memory estimate that starting this request will consume.
+ info->set_memory_cost(CalculateApproximateMemoryCost(request));
+ int memory_cost = IncrementOutstandingRequestsMemoryCost(info->memory_cost(),
+ info->child_id());
+
+ // If enqueing/starting this request will exceed our per-process memory
+ // bound, abort it right away.
+ if (memory_cost > max_outstanding_requests_cost_per_process_) {
+ // We call "SimulateError()" as a way of setting the net::URLRequest's
+ // status -- it has no effect beyond this, since the request hasn't started.
+ request->SimulateError(net::ERR_INSUFFICIENT_RESOURCES);
+
+ // TODO(eroman): this is kinda funky -- we insert the unstarted request into
+ // |pending_requests_| simply to please OnResponseCompleted().
+ GlobalRequestID global_id(info->child_id(), info->request_id());
+ pending_requests_[global_id] = request;
+ OnResponseCompleted(request);
+ return;
+ }
+
+ std::pair<int, int> pair_id(info->child_id(), info->route_id());
+ BlockedRequestMap::const_iterator iter = blocked_requests_map_.find(pair_id);
+ if (iter != blocked_requests_map_.end()) {
+ // The request should be blocked.
+ iter->second->push_back(request);
+ return;
+ }
+
+ GlobalRequestID global_id(info->child_id(), info->request_id());
+ pending_requests_[global_id] = request;
+
+ // Give the resource handlers an opportunity to delay the net::URLRequest from
+ // being started.
+ //
+ // There are three cases:
+ //
+ // (1) if OnWillStart() returns false, the request is cancelled (regardless
+ // of whether |defer_start| was set).
+ // (2) If |defer_start| was set to true, then the request is not added
+ // into the resource queue, and will only be started in response to
+ // calling StartDeferredRequest().
+ // (3) If |defer_start| is not set, then the request is inserted into
+ // the resource_queue_ (which may pause it further, or start it).
+ bool defer_start = false;
+ if (!info->resource_handler()->OnWillStart(
+ info->request_id(), request->url(),
+ &defer_start)) {
+ CancelRequestInternal(request, false);
+ return;
+ }
+
+ if (!defer_start) {
+ InsertIntoResourceQueue(request, *info);
+ }
+}
+
+void ResourceDispatcherHost::InsertIntoResourceQueue(
+ net::URLRequest* request,
+ const ResourceDispatcherHostRequestInfo& request_info) {
+ resource_queue_.AddRequest(request, request_info);
+
+ // Make sure we have the load state monitor running
+ if (!update_load_states_timer_.IsRunning()) {
+ update_load_states_timer_.Start(
+ TimeDelta::FromMilliseconds(kUpdateLoadStatesIntervalMsec),
+ this, &ResourceDispatcherHost::UpdateLoadStates);
+ }
+}
+
+bool ResourceDispatcherHost::PauseRequestIfNeeded(
+ ResourceDispatcherHostRequestInfo* info) {
+ if (info->pause_count() > 0)
+ info->set_is_paused(true);
+ return info->is_paused();
+}
+
+void ResourceDispatcherHost::ResumeRequest(const GlobalRequestID& request_id) {
+ PendingRequestList::iterator i = pending_requests_.find(request_id);
+ if (i == pending_requests_.end()) // The request may have been destroyed
+ return;
+
+ net::URLRequest* request = i->second;
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
+ if (!info->is_paused())
+ return;
+
+ VLOG(1) << "Resuming: " << i->second->url().spec();
+
+ info->set_is_paused(false);
+
+ if (info->called_on_response_started()) {
+ if (info->has_started_reading()) {
+ OnReadCompleted(i->second, info->paused_read_bytes());
+ } else {
+ StartReading(request);
+ }
+ } else {
+ OnResponseStarted(i->second);
+ }
+}
+
+void ResourceDispatcherHost::StartReading(net::URLRequest* request) {
+ // Start reading.
+ int bytes_read = 0;
+ if (Read(request, &bytes_read)) {
+ OnReadCompleted(request, bytes_read);
+ } else if (!request->status().is_io_pending()) {
+ DCHECK(!InfoForRequest(request)->is_paused());
+ // If the error is not an IO pending, then we're done reading.
+ OnResponseCompleted(request);
+ }
+}
+
+bool ResourceDispatcherHost::Read(net::URLRequest* request, int* bytes_read) {
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
+ DCHECK(!info->is_paused());
+
+ net::IOBuffer* buf;
+ int buf_size;
+ if (!info->resource_handler()->OnWillRead(info->request_id(),
+ &buf, &buf_size, -1)) {
+ return false;
+ }
+
+ DCHECK(buf);
+ DCHECK(buf_size > 0);
+
+ info->set_has_started_reading(true);
+ return request->Read(buf, buf_size, bytes_read);
+}
+
+void ResourceDispatcherHost::OnReadCompleted(net::URLRequest* request,
+ int bytes_read) {
+ DCHECK(request);
+ VLOG(1) << "OnReadCompleted: " << request->url().spec();
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
+
+ // OnReadCompleted can be called without Read (e.g., for chrome:// URLs).
+ // Make sure we know that a read has begun.
+ info->set_has_started_reading(true);
+
+ if (PauseRequestIfNeeded(info)) {
+ info->set_paused_read_bytes(bytes_read);
+ VLOG(1) << "OnReadCompleted pausing: " << request->url().spec();
+ return;
+ }
+
+ if (request->status().is_success() && CompleteRead(request, &bytes_read)) {
+ // The request can be paused if we realize that the renderer is not
+ // servicing messages fast enough.
+ if (info->pause_count() == 0 &&
+ Read(request, &bytes_read) &&
+ request->status().is_success()) {
+ if (bytes_read == 0) {
+ CompleteRead(request, &bytes_read);
+ } else {
+ // Force the next CompleteRead / Read pair to run as a separate task.
+ // This avoids a fast, large network request from monopolizing the IO
+ // thread and starving other IO operations from running.
+ info->set_paused_read_bytes(bytes_read);
+ info->set_is_paused(true);
+ GlobalRequestID id(info->child_id(), info->request_id());
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ method_runner_.NewRunnableMethod(
+ &ResourceDispatcherHost::ResumeRequest, id));
+ return;
+ }
+ }
+ }
+
+ if (PauseRequestIfNeeded(info)) {
+ info->set_paused_read_bytes(bytes_read);
+ VLOG(1) << "OnReadCompleted (CompleteRead) pausing: "
+ << request->url().spec();
+ return;
+ }
+
+ // If the status is not IO pending then we've either finished (success) or we
+ // had an error. Either way, we're done!
+ if (!request->status().is_io_pending())
+ OnResponseCompleted(request);
+}
+
+bool ResourceDispatcherHost::CompleteRead(net::URLRequest* request,
+ int* bytes_read) {
+ if (!request || !request->status().is_success()) {
+ NOTREACHED();
+ return false;
+ }
+
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
+ if (!info->resource_handler()->OnReadCompleted(info->request_id(),
+ bytes_read)) {
+ CancelRequestInternal(request, false);
+ return false;
+ }
+
+ return *bytes_read != 0;
+}
+
+void ResourceDispatcherHost::OnResponseCompleted(net::URLRequest* request) {
+ VLOG(1) << "OnResponseCompleted: " << request->url().spec();
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
+
+ // If the load for a main frame has failed, track it in a histogram,
+ // since it will probably cause the user to see an error page.
+ if (!request->status().is_success() &&
+ info->resource_type() == ResourceType::MAIN_FRAME &&
+ request->status().os_error() != net::ERR_ABORTED) {
+ UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.ErrorCodesForMainFrame",
+ -request->status().os_error(),
+ GetAllNetErrorCodes());
+ }
+
+ std::string security_info;
+ const net::SSLInfo& ssl_info = request->ssl_info();
+ if (ssl_info.cert != NULL) {
+ int cert_id = CertStore::GetInstance()->StoreCert(ssl_info.cert,
+ info->child_id());
+ security_info = SSLManager::SerializeSecurityInfo(
+ cert_id, ssl_info.cert_status, ssl_info.security_bits,
+ ssl_info.connection_status);
+ }
+
+ if (info->resource_handler()->OnResponseCompleted(info->request_id(),
+ request->status(),
+ security_info)) {
+ NotifyResponseCompleted(request, info->child_id());
+
+ // The request is complete so we can remove it.
+ RemovePendingRequest(info->child_id(), info->request_id());
+ }
+ // If the handler's OnResponseCompleted returns false, we are deferring the
+ // call until later. We will notify the world and clean up when we resume.
+}
+
+// static
+ResourceDispatcherHostRequestInfo* ResourceDispatcherHost::InfoForRequest(
+ net::URLRequest* request) {
+ // Avoid writing this function twice by casting the cosnt version.
+ const net::URLRequest* const_request = request;
+ return const_cast<ResourceDispatcherHostRequestInfo*>(
+ InfoForRequest(const_request));
+}
+
+// static
+const ResourceDispatcherHostRequestInfo* ResourceDispatcherHost::InfoForRequest(
+ const net::URLRequest* request) {
+ const ResourceDispatcherHostRequestInfo* info =
+ static_cast<const ResourceDispatcherHostRequestInfo*>(
+ request->GetUserData(NULL));
+ DLOG_IF(WARNING, !info) << "Request doesn't seem to have our data";
+ return info;
+}
+
+// static
+bool ResourceDispatcherHost::RenderViewForRequest(
+ const net::URLRequest* request,
+ int* render_process_host_id,
+ int* render_view_host_id) {
+ const ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
+ if (!info) {
+ *render_process_host_id = -1;
+ *render_view_host_id = -1;
+ return false;
+ }
+
+ // If the request is from the worker process, find a tab that owns the worker.
+ if (info->process_type() == ChildProcessInfo::WORKER_PROCESS) {
+ // Need to display some related UI for this network request - pick an
+ // arbitrary parent to do so.
+ if (!WorkerService::GetInstance()->GetRendererForWorker(
+ info->child_id(), render_process_host_id, render_view_host_id)) {
+ *render_process_host_id = -1;
+ *render_view_host_id = -1;
+ return false;
+ }
+ } else {
+ *render_process_host_id = info->child_id();
+ *render_view_host_id = info->route_id();
+ }
+ return true;
+}
+
+void ResourceDispatcherHost::AddObserver(Observer* obs) {
+ observer_list_.AddObserver(obs);
+}
+
+void ResourceDispatcherHost::RemoveObserver(Observer* obs) {
+ observer_list_.RemoveObserver(obs);
+}
+
+net::URLRequest* ResourceDispatcherHost::GetURLRequest(
+ const GlobalRequestID& request_id) const {
+ // This should be running in the IO loop.
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ PendingRequestList::const_iterator i = pending_requests_.find(request_id);
+ if (i == pending_requests_.end())
+ return NULL;
+
+ return i->second;
+}
+
+static int GetCertID(net::URLRequest* request, int child_id) {
+ if (request->ssl_info().cert) {
+ return CertStore::GetInstance()->StoreCert(request->ssl_info().cert,
+ child_id);
+ }
+ return 0;
+}
+
+void ResourceDispatcherHost::NotifyResponseStarted(net::URLRequest* request,
+ int child_id) {
+ // Notify the observers on the IO thread.
+ FOR_EACH_OBSERVER(Observer, observer_list_, OnRequestStarted(this, request));
+
+ int render_process_id, render_view_id;
+ if (!RenderViewForRequest(request, &render_process_id, &render_view_id))
+ return;
+
+ // Notify the observers on the UI thread.
+ ResourceRequestDetails* detail = new ResourceRequestDetails(
+ request, GetCertID(request, child_id));
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableFunction(
+ &ResourceDispatcherHost::NotifyOnUI<ResourceRequestDetails>,
+ NotificationType::RESOURCE_RESPONSE_STARTED,
+ render_process_id, render_view_id, detail));
+}
+
+void ResourceDispatcherHost::NotifyResponseCompleted(net::URLRequest* request,
+ int child_id) {
+ // Notify the observers on the IO thread.
+ FOR_EACH_OBSERVER(Observer, observer_list_,
+ OnResponseCompleted(this, request));
+}
+
+void ResourceDispatcherHost::NotifyReceivedRedirect(net::URLRequest* request,
+ int child_id,
+ const GURL& new_url) {
+ // Notify the observers on the IO thread.
+ FOR_EACH_OBSERVER(Observer, observer_list_,
+ OnReceivedRedirect(this, request, new_url));
+
+ int render_process_id, render_view_id;
+ if (!RenderViewForRequest(request, &render_process_id, &render_view_id))
+ return;
+
+ // Notify the observers on the UI thread.
+ ResourceRedirectDetails* detail = new ResourceRedirectDetails(
+ request, GetCertID(request, child_id), new_url);
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableFunction(
+ &ResourceDispatcherHost::NotifyOnUI<ResourceRedirectDetails>,
+ NotificationType::RESOURCE_RECEIVED_REDIRECT,
+ render_process_id, render_view_id, detail));
+}
+
+template <class T>
+void ResourceDispatcherHost::NotifyOnUI(NotificationType type,
+ int render_process_id,
+ int render_view_id,
+ T* detail) {
+ RenderViewHost* rvh =
+ RenderViewHost::FromID(render_process_id, render_view_id);
+ if (rvh) {
+ RenderViewHostDelegate* rvhd = rvh->delegate();
+ NotificationService::current()->Notify(
+ type, Source<RenderViewHostDelegate>(rvhd), Details<T>(detail));
+ }
+ delete detail;
+}
+
+namespace {
+
+// This function attempts to return the "more interesting" load state of |a|
+// and |b|. We don't have temporal information about these load states
+// (meaning we don't know when we transitioned into these states), so we just
+// rank them according to how "interesting" the states are.
+//
+// We take advantage of the fact that the load states are an enumeration listed
+// in the order in which they occur during the lifetime of a request, so we can
+// regard states with larger numeric values as being further along toward
+// completion. We regard those states as more interesting to report since they
+// represent progress.
+//
+// For example, by this measure "tranferring data" is a more interesting state
+// than "resolving host" because when we are transferring data we are actually
+// doing something that corresponds to changes that the user might observe,
+// whereas waiting for a host name to resolve implies being stuck.
+//
+net::LoadState MoreInterestingLoadState(net::LoadState a, net::LoadState b) {
+ return (a < b) ? b : a;
+}
+
+// Carries information about a load state change.
+struct LoadInfo {
+ GURL url;
+ net::LoadState load_state;
+ uint64 upload_position;
+ uint64 upload_size;
+};
+
+// Map from ProcessID+ViewID pair to LoadState
+typedef std::map<std::pair<int, int>, LoadInfo> LoadInfoMap;
+
+// Used to marshall calls to LoadStateChanged from the IO to UI threads. We do
+// them all as a single task to avoid spamming the UI thread.
+class LoadInfoUpdateTask : public Task {
+ public:
+ virtual void Run() {
+ LoadInfoMap::const_iterator i;
+ for (i = info_map.begin(); i != info_map.end(); ++i) {
+ RenderViewHost* view =
+ RenderViewHost::FromID(i->first.first, i->first.second);
+ if (view) // The view could be gone at this point.
+ view->LoadStateChanged(i->second.url, i->second.load_state,
+ i->second.upload_position,
+ i->second.upload_size);
+ }
+ }
+ LoadInfoMap info_map;
+};
+
+} // namespace
+
+void ResourceDispatcherHost::UpdateLoadStates() {
+ // Populate this map with load state changes, and then send them on to the UI
+ // thread where they can be passed along to the respective RVHs.
+ LoadInfoMap info_map;
+
+ PendingRequestList::const_iterator i;
+
+ // Determine the largest upload size of all requests
+ // in each View (good chance it's zero).
+ std::map<std::pair<int, int>, uint64> largest_upload_size;
+ for (i = pending_requests_.begin(); i != pending_requests_.end(); ++i) {
+ net::URLRequest* request = i->second;
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
+ uint64 upload_size = info->upload_size();
+ if (request->GetLoadState() != net::LOAD_STATE_SENDING_REQUEST)
+ upload_size = 0;
+ std::pair<int, int> key(info->child_id(), info->route_id());
+ if (upload_size && largest_upload_size[key] < upload_size)
+ largest_upload_size[key] = upload_size;
+ }
+
+ for (i = pending_requests_.begin(); i != pending_requests_.end(); ++i) {
+ net::URLRequest* request = i->second;
+ net::LoadState load_state = request->GetLoadState();
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
+
+ // We also poll for upload progress on this timer and send upload
+ // progress ipc messages to the plugin process.
+ bool update_upload_progress = MaybeUpdateUploadProgress(info, request);
+
+ if (info->last_load_state() != load_state || update_upload_progress) {
+ std::pair<int, int> key(info->child_id(), info->route_id());
+
+ // If a request is uploading data, ignore all other requests so that the
+ // upload progress takes priority for being shown in the status bar.
+ if (largest_upload_size.find(key) != largest_upload_size.end() &&
+ info->upload_size() < largest_upload_size[key])
+ continue;
+
+ info->set_last_load_state(load_state);
+
+ net::LoadState to_insert;
+ LoadInfoMap::iterator existing = info_map.find(key);
+ if (existing == info_map.end()) {
+ to_insert = load_state;
+ } else {
+ to_insert =
+ MoreInterestingLoadState(existing->second.load_state, load_state);
+ if (to_insert == existing->second.load_state)
+ continue;
+ }
+ LoadInfo& load_info = info_map[key];
+ load_info.url = request->url();
+ load_info.load_state = to_insert;
+ load_info.upload_size = info->upload_size();
+ load_info.upload_position = request->GetUploadProgress();
+ }
+ }
+
+ if (info_map.empty())
+ return;
+
+ LoadInfoUpdateTask* task = new LoadInfoUpdateTask;
+ task->info_map.swap(info_map);
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, task);
+}
+
+// Calls the ResourceHandler to send upload progress messages to the renderer.
+// Returns true iff an upload progress message should be sent to the UI thread.
+bool ResourceDispatcherHost::MaybeUpdateUploadProgress(
+ ResourceDispatcherHostRequestInfo *info,
+ net::URLRequest *request) {
+
+ if (!info->upload_size() || info->waiting_for_upload_progress_ack())
+ return false;
+
+ uint64 size = info->upload_size();
+ uint64 position = request->GetUploadProgress();
+ if (position == info->last_upload_position())
+ return false; // no progress made since last time
+
+ const uint64 kHalfPercentIncrements = 200;
+ const TimeDelta kOneSecond = TimeDelta::FromMilliseconds(1000);
+
+ uint64 amt_since_last = position - info->last_upload_position();
+ TimeDelta time_since_last = TimeTicks::Now() - info->last_upload_ticks();
+
+ bool is_finished = (size == position);
+ bool enough_new_progress = (amt_since_last > (size / kHalfPercentIncrements));
+ bool too_much_time_passed = time_since_last > kOneSecond;
+
+ if (is_finished || enough_new_progress || too_much_time_passed) {
+ if (request->load_flags() & net::LOAD_ENABLE_UPLOAD_PROGRESS) {
+ info->resource_handler()->OnUploadProgress(info->request_id(),
+ position, size);
+ info->set_waiting_for_upload_progress_ack(true);
+ }
+ info->set_last_upload_ticks(TimeTicks::Now());
+ info->set_last_upload_position(position);
+ return true;
+ }
+ return false;
+}
+
+void ResourceDispatcherHost::BlockRequestsForRoute(int child_id, int route_id) {
+ std::pair<int, int> key(child_id, route_id);
+ DCHECK(blocked_requests_map_.find(key) == blocked_requests_map_.end()) <<
+ "BlockRequestsForRoute called multiple time for the same RVH";
+ blocked_requests_map_[key] = new BlockedRequestsList();
+}
+
+void ResourceDispatcherHost::ResumeBlockedRequestsForRoute(int child_id,
+ int route_id) {
+ ProcessBlockedRequestsForRoute(child_id, route_id, false);
+}
+
+void ResourceDispatcherHost::CancelBlockedRequestsForRoute(int child_id,
+ int route_id) {
+ ProcessBlockedRequestsForRoute(child_id, route_id, true);
+}
+
+void ResourceDispatcherHost::ProcessBlockedRequestsForRoute(
+ int child_id,
+ int route_id,
+ bool cancel_requests) {
+ BlockedRequestMap::iterator iter = blocked_requests_map_.find(
+ std::pair<int, int>(child_id, route_id));
+ if (iter == blocked_requests_map_.end()) {
+ // It's possible to reach here if the renderer crashed while an interstitial
+ // page was showing.
+ return;
+ }
+
+ BlockedRequestsList* requests = iter->second;
+
+ // Removing the vector from the map unblocks any subsequent requests.
+ blocked_requests_map_.erase(iter);
+
+ for (BlockedRequestsList::iterator req_iter = requests->begin();
+ req_iter != requests->end(); ++req_iter) {
+ // Remove the memory credit that we added when pushing the request onto
+ // the blocked list.
+ net::URLRequest* request = *req_iter;
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
+ IncrementOutstandingRequestsMemoryCost(-1 * info->memory_cost(),
+ info->child_id());
+ if (cancel_requests)
+ delete request;
+ else
+ BeginRequestInternal(request);
+ }
+
+ delete requests;
+}
+
+bool ResourceDispatcherHost::IsValidRequest(net::URLRequest* request) {
+ if (!request)
+ return false;
+ ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
+ return pending_requests_.find(
+ GlobalRequestID(info->child_id(), info->request_id())) !=
+ pending_requests_.end();
+}
+
+// static
+void ResourceDispatcherHost::ApplyExtensionLocalizationFilter(
+ const GURL& url,
+ const ResourceType::Type& resource_type,
+ ResourceDispatcherHostRequestInfo* request_info) {
+ // Apply filter to chrome extension CSS files.
+ if (url.SchemeIs(chrome::kExtensionScheme) &&
+ resource_type == ResourceType::STYLESHEET)
+ request_info->set_replace_extension_localization_templates();
+}
+
+// static
+net::RequestPriority ResourceDispatcherHost::DetermineRequestPriority(
+ ResourceType::Type type) {
+ // Determine request priority based on how critical this resource typically
+ // is to user-perceived page load performance. Important considerations are:
+ // * Can this resource block the download of other resources.
+ // * Can this resource block the rendering of the page.
+ // * How useful is the page to the user if this resource is not loaded yet.
+ switch (type) {
+ // Main frames are the highest priority because they can block nearly every
+ // type of other resource and there is no useful display without them.
+ // Sub frames are a close second, however it is a common pattern to wrap
+ // ads in an iframe or even in multiple nested iframes. It is worth
+ // investigating if there is a better priority for them.
+ case ResourceType::MAIN_FRAME:
+ case ResourceType::SUB_FRAME:
+ return net::HIGHEST;
+
+ // Stylesheets and scripts can block rendering and loading of other
+ // resources. Fonts can block text from rendering.
+ case ResourceType::STYLESHEET:
+ case ResourceType::SCRIPT:
+ case ResourceType::FONT_RESOURCE:
+ return net::MEDIUM;
+
+ // Sub resources, objects and media are lower priority than potentially
+ // blocking stylesheets, scripts and fonts, but are higher priority than
+ // images because if they exist they are probably more central to the page
+ // focus than images on the page.
+ case ResourceType::SUB_RESOURCE:
+ case ResourceType::OBJECT:
+ case ResourceType::MEDIA:
+ case ResourceType::WORKER:
+ case ResourceType::SHARED_WORKER:
+ return net::LOW;
+
+ // Images are the "lowest" priority because they typically do not block
+ // downloads or rendering and most pages have some useful content without
+ // them.
+ case ResourceType::IMAGE:
+ return net::LOWEST;
+
+ // Prefetches are at a lower priority than even LOWEST, since they
+ // are not even required for rendering of the current page.
+ case ResourceType::PREFETCH:
+ return net::IDLE;
+
+ default:
+ // When new resource types are added, their priority must be considered.
+ NOTREACHED();
+ return net::LOW;
+ }
+}
+
+// static
+bool ResourceDispatcherHost::is_prefetch_enabled() {
+ return is_prefetch_enabled_;
+}
+
+// static
+void ResourceDispatcherHost::set_is_prefetch_enabled(bool value) {
+ is_prefetch_enabled_ = value;
+}
+
+// static
+bool ResourceDispatcherHost::is_prefetch_enabled_ = false;
diff --git a/content/browser/renderer_host/resource_dispatcher_host.h b/content/browser/renderer_host/resource_dispatcher_host.h
new file mode 100644
index 0000000..fb78dcc
--- /dev/null
+++ b/content/browser/renderer_host/resource_dispatcher_host.h
@@ -0,0 +1,516 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is the browser side of the resource dispatcher, it receives requests
+// from the child process (i.e. [Renderer, Plugin, Worker]ProcessHost), and
+// dispatches them to URLRequests. It then forwards the messages from the
+// URLRequests back to the correct process for handling.
+//
+// See http://dev.chromium.org/developers/design-documents/multi-process-resource-loading
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_RESOURCE_DISPATCHER_HOST_H_
+#define CONTENT_BROWSER_RENDERER_HOST_RESOURCE_DISPATCHER_HOST_H_
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/observer_list.h"
+#include "base/scoped_ptr.h"
+#include "base/timer.h"
+#include "chrome/common/child_process_info.h"
+#include "chrome/common/notification_type.h"
+#include "content/browser/renderer_host/resource_queue.h"
+#include "ipc/ipc_message.h"
+#include "net/url_request/url_request.h"
+#include "webkit/glue/resource_type.h"
+
+class CrossSiteResourceHandler;
+class DownloadFileManager;
+class DownloadRequestLimiter;
+class LoginHandler;
+class NotificationDetails;
+class PluginService;
+class ResourceDispatcherHostRequestInfo;
+class ResourceHandler;
+class ResourceMessageFilter;
+class SafeBrowsingService;
+class SaveFileManager;
+class SSLClientAuthHandler;
+class UserScriptListener;
+class WebKitThread;
+struct DownloadSaveInfo;
+struct GlobalRequestID;
+struct ViewHostMsg_Resource_Request;
+struct ViewMsg_ClosePage_Params;
+
+namespace net {
+class URLRequestContext;
+} // namespace net
+
+namespace webkit_blob {
+class DeletableFileReference;
+}
+
+class ResourceDispatcherHost : public net::URLRequest::Delegate {
+ public:
+ class Observer {
+ public:
+ virtual ~Observer() {}
+ virtual void OnRequestStarted(ResourceDispatcherHost* resource_dispatcher,
+ net::URLRequest* request) = 0;
+ virtual void OnResponseCompleted(
+ ResourceDispatcherHost* resource_dispatcher,
+ net::URLRequest* request) = 0;
+ virtual void OnReceivedRedirect(ResourceDispatcherHost* resource_dispatcher,
+ net::URLRequest* request,
+ const GURL& new_url) = 0;
+ };
+
+ ResourceDispatcherHost();
+ ~ResourceDispatcherHost();
+
+ void Initialize();
+
+ // Puts the resource dispatcher host in an inactive state (unable to begin
+ // new requests). Cancels all pending requests.
+ void Shutdown();
+
+ // Returns true if the message was a resource message that was processed.
+ // If it was, message_was_ok will be false iff the message was corrupt.
+ bool OnMessageReceived(const IPC::Message& message,
+ ResourceMessageFilter* filter,
+ bool* message_was_ok);
+
+ // Initiates a download from the browser process (as opposed to a resource
+ // request from the renderer or another child process).
+ void BeginDownload(const GURL& url,
+ const GURL& referrer,
+ const DownloadSaveInfo& save_info,
+ bool prompt_for_save_location,
+ int process_unique_id,
+ int route_id,
+ net::URLRequestContext* request_context);
+
+ // Initiates a save file from the browser process (as opposed to a resource
+ // request from the renderer or another child process).
+ void BeginSaveFile(const GURL& url,
+ const GURL& referrer,
+ int process_unique_id,
+ int route_id,
+ net::URLRequestContext* request_context);
+
+ // Cancels the given request if it still exists. We ignore cancels from the
+ // renderer in the event of a download.
+ void CancelRequest(int process_unique_id,
+ int request_id,
+ bool from_renderer);
+
+ // Follows a deferred redirect for the given request.
+ // new_first_party_for_cookies, if non-empty, is the new cookie policy URL
+ // for the redirected URL. If the cookie policy URL needs changing, pass
+ // true as has_new_first_party_for_cookies and the new cookie policy URL as
+ // new_first_party_for_cookies. Otherwise, pass false as
+ // has_new_first_party_for_cookies, and new_first_party_for_cookies will not
+ // be used.
+ void FollowDeferredRedirect(int process_unique_id,
+ int request_id,
+ bool has_new_first_party_for_cookies,
+ const GURL& new_first_party_for_cookies);
+
+ // Starts a request that was deferred during ResourceHandler::OnWillStart().
+ void StartDeferredRequest(int process_unique_id, int request_id);
+
+ // Returns true if it's ok to send the data. If there are already too many
+ // data messages pending, it pauses the request and returns false. In this
+ // case the caller should not send the data.
+ bool WillSendData(int process_unique_id, int request_id);
+
+ // Pauses or resumes network activity for a particular request.
+ void PauseRequest(int process_unique_id, int request_id, bool pause);
+
+ // Returns the number of pending requests. This is designed for the unittests
+ int pending_requests() const {
+ return static_cast<int>(pending_requests_.size());
+ }
+
+ // Intended for unit-tests only. Returns the memory cost of all the
+ // outstanding requests (pending and blocked) for |process_unique_id|.
+ int GetOutstandingRequestsMemoryCost(int process_unique_id) const;
+
+ // Intended for unit-tests only. Overrides the outstanding requests bound.
+ void set_max_outstanding_requests_cost_per_process(int limit) {
+ max_outstanding_requests_cost_per_process_ = limit;
+ }
+
+ // The average private bytes increase of the browser for each new pending
+ // request. Experimentally obtained.
+ static const int kAvgBytesPerOutstandingRequest = 4400;
+
+ DownloadFileManager* download_file_manager() const {
+ return download_file_manager_;
+ }
+
+ DownloadRequestLimiter* download_request_limiter() const {
+ return download_request_limiter_.get();
+ }
+
+ SaveFileManager* save_file_manager() const {
+ return save_file_manager_;
+ }
+
+ SafeBrowsingService* safe_browsing_service() const {
+ return safe_browsing_;
+ }
+
+ WebKitThread* webkit_thread() const {
+ return webkit_thread_.get();
+ }
+
+ // Called when the onunload handler for a cross-site request has finished.
+ void OnClosePageACK(const ViewMsg_ClosePage_Params& params);
+
+ // Force cancels any pending requests for the given process.
+ void CancelRequestsForProcess(int process_unique_id);
+
+ // Force cancels any pending requests for the given route id. This method
+ // acts like CancelRequestsForProcess when route_id is -1.
+ void CancelRequestsForRoute(int process_unique_id, int route_id);
+
+ // net::URLRequest::Delegate
+ virtual void OnReceivedRedirect(net::URLRequest* request,
+ const GURL& new_url,
+ bool* defer_redirect);
+ virtual void OnAuthRequired(net::URLRequest* request,
+ net::AuthChallengeInfo* auth_info);
+ virtual void OnCertificateRequested(
+ net::URLRequest* request,
+ net::SSLCertRequestInfo* cert_request_info);
+ virtual void OnSSLCertificateError(net::URLRequest* request,
+ int cert_error,
+ net::X509Certificate* cert);
+ virtual void OnGetCookies(net::URLRequest* request,
+ bool blocked_by_policy);
+ virtual void OnSetCookie(net::URLRequest* request,
+ const std::string& cookie_line,
+ const net::CookieOptions& options,
+ bool blocked_by_policy);
+ virtual void OnResponseStarted(net::URLRequest* request);
+ virtual void OnReadCompleted(net::URLRequest* request, int bytes_read);
+ void OnResponseCompleted(net::URLRequest* request);
+
+ // Helper functions to get our extra data out of a request. The given request
+ // must have been one we created so that it has the proper extra data pointer.
+ static ResourceDispatcherHostRequestInfo* InfoForRequest(
+ net::URLRequest* request);
+ static const ResourceDispatcherHostRequestInfo* InfoForRequest(
+ const net::URLRequest* request);
+
+ // Extracts the render view/process host's identifiers from the given request
+ // and places them in the given out params (both required). If there are no
+ // such IDs associated with the request (such as non-page-related requests),
+ // this function will return false and both out params will be -1.
+ static bool RenderViewForRequest(const net::URLRequest* request,
+ int* render_process_host_id,
+ int* render_view_host_id);
+
+ // Adds an observer. The observer will be called on the IO thread. To
+ // observe resource events on the UI thread, subscribe to the
+ // NOTIFY_RESOURCE_* notifications of the notification service.
+ void AddObserver(Observer* obs);
+
+ // Removes an observer.
+ void RemoveObserver(Observer* obs);
+
+ // Retrieves a net::URLRequest. Must be called from the IO thread.
+ net::URLRequest* GetURLRequest(const GlobalRequestID& request_id) const;
+
+ // Notifies our observers that a request has been cancelled.
+ void NotifyResponseCompleted(net::URLRequest* request, int process_unique_id);
+
+ void RemovePendingRequest(int process_unique_id, int request_id);
+
+ // Causes all new requests for the route identified by
+ // |process_unique_id| and |route_id| to be blocked (not being
+ // started) until ResumeBlockedRequestsForRoute or
+ // CancelBlockedRequestsForRoute is called.
+ void BlockRequestsForRoute(int process_unique_id, int route_id);
+
+ // Resumes any blocked request for the specified route id.
+ void ResumeBlockedRequestsForRoute(int process_unique_id, int route_id);
+
+ // Cancels any blocked request for the specified route id.
+ void CancelBlockedRequestsForRoute(int process_unique_id, int route_id);
+
+ // Decrements the pending_data_count for the request and resumes
+ // the request if it was paused due to too many pending data
+ // messages sent.
+ void DataReceivedACK(int process_unique_id, int request_id);
+
+ // Maintains a collection of temp files created in support of
+ // the download_to_file capability. Used to grant access to the
+ // child process and to defer deletion of the file until it's
+ // no longer needed.
+ void RegisterDownloadedTempFile(
+ int child_id, int request_id,
+ webkit_blob::DeletableFileReference* reference);
+ void UnregisterDownloadedTempFile(int child_id, int request_id);
+
+ // Needed for the sync IPC message dispatcher macros.
+ bool Send(IPC::Message* message);
+
+ // Controls if we launch or squash prefetch requests as they arrive
+ // from renderers.
+ static bool is_prefetch_enabled();
+ static void set_is_prefetch_enabled(bool value);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(ResourceDispatcherHostTest,
+ TestBlockedRequestsProcessDies);
+ FRIEND_TEST_ALL_PREFIXES(ResourceDispatcherHostTest,
+ IncrementOutstandingRequestsMemoryCost);
+ FRIEND_TEST_ALL_PREFIXES(ResourceDispatcherHostTest,
+ CalculateApproximateMemoryCost);
+ FRIEND_TEST_ALL_PREFIXES(ApplyExtensionLocalizationFilterTest, WrongScheme);
+ FRIEND_TEST_ALL_PREFIXES(ApplyExtensionLocalizationFilterTest, GoodScheme);
+ FRIEND_TEST_ALL_PREFIXES(ApplyExtensionLocalizationFilterTest,
+ GoodSchemeWrongResourceType);
+
+ class ShutdownTask;
+
+ friend class ShutdownTask;
+
+ // Associates the given info with the given request. The info will then be
+ // owned by the request.
+ void SetRequestInfo(net::URLRequest* request,
+ ResourceDispatcherHostRequestInfo* info);
+
+ // A shutdown helper that runs on the IO thread.
+ void OnShutdown();
+
+ // Returns true if the request is paused.
+ bool PauseRequestIfNeeded(ResourceDispatcherHostRequestInfo* info);
+
+ // Resumes the given request by calling OnResponseStarted or OnReadCompleted.
+ void ResumeRequest(const GlobalRequestID& request_id);
+
+ // Internal function to start reading for the first time.
+ void StartReading(net::URLRequest* request);
+
+ // Reads data from the response using our internal buffer as async IO.
+ // Returns true if data is available immediately, false otherwise. If the
+ // return value is false, we will receive a OnReadComplete() callback later.
+ bool Read(net::URLRequest* request, int* bytes_read);
+
+ // Internal function to finish an async IO which has completed. Returns
+ // true if there is more data to read (e.g. we haven't read EOF yet and
+ // no errors have occurred).
+ bool CompleteRead(net::URLRequest*, int* bytes_read);
+
+ // Internal function to finish handling the ResponseStarted message. Returns
+ // true on success.
+ bool CompleteResponseStarted(net::URLRequest* request);
+
+ // Helper function for regular and download requests.
+ void BeginRequestInternal(net::URLRequest* request);
+
+ // Helper function that cancels |request|.
+ void CancelRequestInternal(net::URLRequest* request, bool from_renderer);
+
+ // Helper function that inserts |request| into the resource queue.
+ void InsertIntoResourceQueue(
+ net::URLRequest* request,
+ const ResourceDispatcherHostRequestInfo& request_info);
+
+ // Updates the "cost" of outstanding requests for |process_unique_id|.
+ // The "cost" approximates how many bytes are consumed by all the in-memory
+ // data structures supporting this request (net::URLRequest object,
+ // HttpNetworkTransaction, etc...).
+ // The value of |cost| is added to the running total, and the resulting
+ // sum is returned.
+ int IncrementOutstandingRequestsMemoryCost(int cost,
+ int process_unique_id);
+
+ // Estimate how much heap space |request| will consume to run.
+ static int CalculateApproximateMemoryCost(net::URLRequest* request);
+
+ // The list of all requests that we have pending. This list is not really
+ // optimized, and assumes that we have relatively few requests pending at once
+ // since some operations require brute-force searching of the list.
+ //
+ // It may be enhanced in the future to provide some kind of prioritization
+ // mechanism. We should also consider a hashtable or binary tree if it turns
+ // out we have a lot of things here.
+ typedef std::map<GlobalRequestID, net::URLRequest*> PendingRequestList;
+
+ // Deletes the pending request identified by the iterator passed in.
+ // This function will invalidate the iterator passed in. Callers should
+ // not rely on this iterator being valid on return.
+ void RemovePendingRequest(const PendingRequestList::iterator& iter);
+
+ // Notify our observers that we started receiving a response for a request.
+ void NotifyResponseStarted(net::URLRequest* request, int process_unique_id);
+
+ // Notify our observers that a request has been redirected.
+ void NotifyReceivedRedirect(net::URLRequest* request,
+ int process_unique_id,
+ const GURL& new_url);
+
+ // Tries to handle the url with an external protocol. If the request is
+ // handled, the function returns true. False otherwise.
+ bool HandleExternalProtocol(int request_id,
+ int process_unique_id,
+ int route_id,
+ const GURL& url,
+ ResourceType::Type resource_type,
+ ResourceHandler* handler);
+
+ // Checks all pending requests and updates the load states and upload
+ // progress if necessary.
+ void UpdateLoadStates();
+
+ // Checks the upload state and sends an update if one is necessary.
+ bool MaybeUpdateUploadProgress(ResourceDispatcherHostRequestInfo *info,
+ net::URLRequest *request);
+
+ // Resumes or cancels (if |cancel_requests| is true) any blocked requests.
+ void ProcessBlockedRequestsForRoute(int process_unique_id,
+ int route_id,
+ bool cancel_requests);
+
+ void OnRequestResource(const IPC::Message& msg,
+ int request_id,
+ const ViewHostMsg_Resource_Request& request_data);
+ void OnSyncLoad(int request_id,
+ const ViewHostMsg_Resource_Request& request_data,
+ IPC::Message* sync_result);
+ void BeginRequest(int request_id,
+ const ViewHostMsg_Resource_Request& request_data,
+ IPC::Message* sync_result, // only valid for sync
+ int route_id); // only valid for async
+ void OnDataReceivedACK(int request_id);
+ void OnDataDownloadedACK(int request_id);
+ void OnUploadProgressACK(int request_id);
+ void OnCancelRequest(int request_id);
+ void OnFollowRedirect(int request_id,
+ bool has_new_first_party_for_cookies,
+ const GURL& new_first_party_for_cookies);
+ void OnReleaseDownloadedFile(int request_id);
+
+ ResourceHandler* CreateSafeBrowsingResourceHandler(
+ ResourceHandler* handler, int child_id, int route_id,
+ ResourceType::Type resource_type);
+
+ // Creates ResourceDispatcherHostRequestInfo for a browser-initiated request
+ // (a download or a page save). |download| should be true if the request
+ // is a file download.
+ ResourceDispatcherHostRequestInfo* CreateRequestInfoForBrowserRequest(
+ ResourceHandler* handler, int child_id, int route_id, bool download);
+
+ // Returns true if |request| is in |pending_requests_|.
+ bool IsValidRequest(net::URLRequest* request);
+
+ // Sets replace_extension_localization_templates on all text/css requests that
+ // have "chrome-extension://" scheme.
+ static void ApplyExtensionLocalizationFilter(
+ const GURL& url,
+ const ResourceType::Type& resource_type,
+ ResourceDispatcherHostRequestInfo* request_info);
+
+ // Determine request priority based on how critical this resource typically
+ // is to user-perceived page load performance.
+ static net::RequestPriority DetermineRequestPriority(ResourceType::Type type);
+
+ // Sends the given notification on the UI thread. The RenderViewHost's
+ // controller is used as the source.
+ template <class T>
+ static void NotifyOnUI(NotificationType type,
+ int render_process_id,
+ int render_view_id,
+ T* detail);
+
+ PendingRequestList pending_requests_;
+
+ // Collection of temp files downloaded for child processes via
+ // the download_to_file mechanism. We avoid deleting them until
+ // the client no longer needs them.
+ typedef std::map<int, scoped_refptr<webkit_blob::DeletableFileReference> >
+ DeletableFilesMap; // key is request id
+ typedef std::map<int, DeletableFilesMap>
+ RegisteredTempFiles; // key is child process id
+ RegisteredTempFiles registered_temp_files_;
+
+ // A timer that periodically calls UpdateLoadStates while pending_requests_
+ // is not empty.
+ base::RepeatingTimer<ResourceDispatcherHost> update_load_states_timer_;
+
+ // Handles the resource requests from the moment we want to start them.
+ ResourceQueue resource_queue_;
+
+ // We own the download file writing thread and manager
+ scoped_refptr<DownloadFileManager> download_file_manager_;
+
+ // Determines whether a download is allowed.
+ scoped_refptr<DownloadRequestLimiter> download_request_limiter_;
+
+ // We own the save file manager.
+ scoped_refptr<SaveFileManager> save_file_manager_;
+
+ scoped_refptr<UserScriptListener> user_script_listener_;
+
+ scoped_refptr<SafeBrowsingService> safe_browsing_;
+
+ // We own the WebKit thread and see to its destruction.
+ scoped_ptr<WebKitThread> webkit_thread_;
+
+ // Request ID for browser initiated requests. request_ids generated by
+ // child processes are counted up from 0, while browser created requests
+ // start at -2 and go down from there. (We need to start at -2 because -1 is
+ // used as a special value all over the resource_dispatcher_host for
+ // uninitialized variables.) This way, we no longer have the unlikely (but
+ // observed in the real world!) event where we have two requests with the same
+ // request_id_.
+ int request_id_;
+
+ // List of objects observing resource dispatching.
+ ObserverList<Observer> observer_list_;
+
+ // For running tasks.
+ ScopedRunnableMethodFactory<ResourceDispatcherHost> method_runner_;
+
+ // True if the resource dispatcher host has been shut down.
+ bool is_shutdown_;
+
+ typedef std::vector<net::URLRequest*> BlockedRequestsList;
+ typedef std::pair<int, int> ProcessRouteIDs;
+ typedef std::map<ProcessRouteIDs, BlockedRequestsList*> BlockedRequestMap;
+ BlockedRequestMap blocked_requests_map_;
+
+ // Maps the process_unique_ids to the approximate number of bytes
+ // being used to service its resource requests. No entry implies 0 cost.
+ typedef std::map<int, int> OutstandingRequestsMemoryCostMap;
+ OutstandingRequestsMemoryCostMap outstanding_requests_memory_cost_map_;
+
+ // |max_outstanding_requests_cost_per_process_| is the upper bound on how
+ // many outstanding requests can be issued per child process host.
+ // The constraint is expressed in terms of bytes (where the cost of
+ // individual requests is given by CalculateApproximateMemoryCost).
+ // The total number of outstanding requests is roughly:
+ // (max_outstanding_requests_cost_per_process_ /
+ // kAvgBytesPerOutstandingRequest)
+ int max_outstanding_requests_cost_per_process_;
+
+ // Used during IPC message dispatching so that the handlers can get a pointer
+ // to the source of the message.
+ ResourceMessageFilter* filter_;
+
+ static bool is_prefetch_enabled_;
+
+ DISALLOW_COPY_AND_ASSIGN(ResourceDispatcherHost);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_RESOURCE_DISPATCHER_HOST_H_
diff --git a/content/browser/renderer_host/resource_dispatcher_host_request_info.cc b/content/browser/renderer_host/resource_dispatcher_host_request_info.cc
new file mode 100644
index 0000000..695889e
--- /dev/null
+++ b/content/browser/renderer_host/resource_dispatcher_host_request_info.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
+
+#include "chrome/browser/ssl/ssl_client_auth_handler.h"
+#include "chrome/browser/ui/login/login_prompt.h"
+#include "content/browser/renderer_host/resource_handler.h"
+#include "webkit/blob/blob_data.h"
+
+ResourceDispatcherHostRequestInfo::ResourceDispatcherHostRequestInfo(
+ ResourceHandler* handler,
+ ChildProcessInfo::ProcessType process_type,
+ int child_id,
+ int route_id,
+ int request_id,
+ ResourceType::Type resource_type,
+ uint64 upload_size,
+ bool is_download,
+ bool allow_download,
+ bool has_user_gesture,
+ int host_renderer_id,
+ int host_render_view_id)
+ : resource_handler_(handler),
+ cross_site_handler_(NULL),
+ process_type_(process_type),
+ child_id_(child_id),
+ route_id_(route_id),
+ request_id_(request_id),
+ pending_data_count_(0),
+ is_download_(is_download),
+ allow_download_(allow_download),
+ has_user_gesture_(has_user_gesture),
+ pause_count_(0),
+ resource_type_(resource_type),
+ replace_extension_localization_templates_(false),
+ last_load_state_(net::LOAD_STATE_IDLE),
+ upload_size_(upload_size),
+ last_upload_position_(0),
+ waiting_for_upload_progress_ack_(false),
+ memory_cost_(0),
+ is_paused_(false),
+ called_on_response_started_(false),
+ has_started_reading_(false),
+ paused_read_bytes_(0),
+ host_renderer_id_(host_renderer_id),
+ host_render_view_id_(host_render_view_id) {
+}
+
+ResourceDispatcherHostRequestInfo::~ResourceDispatcherHostRequestInfo() {
+ resource_handler_->OnRequestClosed();
+}
+
+void ResourceDispatcherHostRequestInfo::set_login_handler(LoginHandler* lh) {
+ login_handler_ = lh;
+}
+
+void ResourceDispatcherHostRequestInfo::set_ssl_client_auth_handler(
+ SSLClientAuthHandler* s) {
+ ssl_client_auth_handler_ = s;
+}
+
+void ResourceDispatcherHostRequestInfo::set_requested_blob_data(
+ webkit_blob::BlobData* data) {
+ requested_blob_data_ = data;
+}
diff --git a/content/browser/renderer_host/resource_dispatcher_host_request_info.h b/content/browser/renderer_host/resource_dispatcher_host_request_info.h
new file mode 100644
index 0000000..c05c4f9
--- /dev/null
+++ b/content/browser/renderer_host/resource_dispatcher_host_request_info.h
@@ -0,0 +1,241 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_RESOURCE_DISPATCHER_HOST_REQUEST_INFO_H_
+#define CONTENT_BROWSER_RENDERER_HOST_RESOURCE_DISPATCHER_HOST_REQUEST_INFO_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/time.h"
+#include "chrome/common/child_process_info.h"
+#include "net/base/load_states.h"
+#include "net/url_request/url_request.h"
+#include "webkit/glue/resource_type.h"
+
+class CrossSiteResourceHandler;
+class LoginHandler;
+class ResourceDispatcherHost;
+class ResourceHandler;
+class SSLClientAuthHandler;
+
+namespace webkit_blob {
+class BlobData;
+}
+
+// Holds the data ResourceDispatcherHost associates with each request.
+// Retrieve this data by calling ResourceDispatcherHost::InfoForRequest.
+class ResourceDispatcherHostRequestInfo : public net::URLRequest::UserData {
+ public:
+ // This will take a reference to the handler.
+ ResourceDispatcherHostRequestInfo(
+ ResourceHandler* handler,
+ ChildProcessInfo::ProcessType process_type,
+ int child_id,
+ int route_id,
+ int request_id,
+ ResourceType::Type resource_type,
+ uint64 upload_size,
+ bool is_download,
+ bool allow_download,
+ bool has_user_gesture,
+ int host_renderer_id,
+ int host_render_view_id);
+ virtual ~ResourceDispatcherHostRequestInfo();
+
+ // Top-level ResourceHandler servicing this request.
+ ResourceHandler* resource_handler() { return resource_handler_.get(); }
+
+ // CrossSiteResourceHandler for this request, if it is a cross-site request.
+ // (NULL otherwise.) This handler is part of the chain of ResourceHandlers
+ // pointed to by resource_handler, and is not owned by this class.
+ CrossSiteResourceHandler* cross_site_handler() {
+ return cross_site_handler_;
+ }
+ void set_cross_site_handler(CrossSiteResourceHandler* h) {
+ cross_site_handler_ = h;
+ }
+
+ // Pointer to the login handler, or NULL if there is none for this request.
+ LoginHandler* login_handler() const { return login_handler_.get(); }
+ void set_login_handler(LoginHandler* lh);
+
+ // Pointer to the SSL auth, or NULL if there is none for this request.
+ SSLClientAuthHandler* ssl_client_auth_handler() const {
+ return ssl_client_auth_handler_.get();
+ }
+ void set_ssl_client_auth_handler(SSLClientAuthHandler* s);
+
+ // Identifies the type of process (renderer, plugin, etc.) making the request.
+ ChildProcessInfo::ProcessType process_type() const {
+ return process_type_;
+ }
+
+ // The child process unique ID of the requestor. This duplicates the value
+ // stored on the request by SetChildProcessUniqueIDForRequest in
+ // url_request_tracking.
+ int child_id() const { return child_id_; }
+
+ // The IPC route identifier for this request (this identifies the RenderView
+ // or like-thing in the renderer that the request gets routed to).
+ int route_id() const { return route_id_; }
+
+ // Unique identifier for this resource request.
+ int request_id() const { return request_id_; }
+
+ // Number of messages we've sent to the renderer that we haven't gotten an
+ // ACK for. This allows us to avoid having too many messages in flight.
+ int pending_data_count() const { return pending_data_count_; }
+ void IncrementPendingDataCount() { pending_data_count_++; }
+ void DecrementPendingDataCount() { pending_data_count_--; }
+
+ // Downloads are allowed only as a top level request.
+ bool allow_download() const { return allow_download_; }
+
+ bool has_user_gesture() const { return has_user_gesture_; }
+
+ // Whether this is a download.
+ bool is_download() const { return is_download_; }
+ void set_is_download(bool download) { is_download_ = download; }
+
+ // The number of clients that have called pause on this request.
+ int pause_count() const { return pause_count_; }
+ void set_pause_count(int count) { pause_count_ = count; }
+
+ // Identifies the type of resource, such as subframe, media, etc.
+ ResourceType::Type resource_type() const { return resource_type_; }
+
+ // Whether we should apply a filter to this resource that replaces
+ // localization templates with the appropriate localized strings. This is set
+ // for CSS resources used by extensions.
+ bool replace_extension_localization_templates() const {
+ return replace_extension_localization_templates_;
+ }
+ void set_replace_extension_localization_templates() {
+ replace_extension_localization_templates_ = true;
+ }
+
+ // Returns the last updated state of the load. This is updated periodically
+ // by the ResourceDispatcherHost and tracked here so we don't send out
+ // unnecessary state change notifications.
+ net::LoadState last_load_state() const { return last_load_state_; }
+ void set_last_load_state(net::LoadState s) { last_load_state_ = s; }
+
+ // When there is upload data, this is the byte count of that data. When there
+ // is no upload, this will be 0.
+ uint64 upload_size() const { return upload_size_; }
+ void set_upload_size(uint64 upload_size) { upload_size_ = upload_size; }
+
+ // When we're uploading data, this is the the byte offset into the uploaded
+ // data that we've uploaded that we've send an upload progress update about.
+ // The ResourceDispatcherHost will periodically update this value to track
+ // upload progress and make sure it doesn't sent out duplicate updates.
+ uint64 last_upload_position() const { return last_upload_position_; }
+ void set_last_upload_position(uint64 p) { last_upload_position_ = p; }
+
+ // Indicates when the ResourceDispatcherHost last update the upload
+ // position. This is used to make sure we don't send too many updates.
+ base::TimeTicks last_upload_ticks() const { return last_upload_ticks_; }
+ void set_last_upload_ticks(base::TimeTicks t) { last_upload_ticks_ = t; }
+
+ // Set when the ResourceDispatcherHost has sent out an upload progress, and
+ // cleared whtn the ACK is received. This is used to throttle updates so
+ // multiple updates aren't in flight at once.
+ bool waiting_for_upload_progress_ack() const {
+ return waiting_for_upload_progress_ack_;
+ }
+ void set_waiting_for_upload_progress_ack(bool waiting) {
+ waiting_for_upload_progress_ack_ = waiting;
+ }
+
+ // The approximate in-memory size (bytes) that we credited this request
+ // as consuming in |outstanding_requests_memory_cost_map_|.
+ int memory_cost() const { return memory_cost_; }
+ void set_memory_cost(int cost) { memory_cost_ = cost; }
+
+ int host_renderer_id() const { return host_renderer_id_; }
+ int host_render_view_id() const { return host_render_view_id_; }
+
+ // We hold a reference to the requested blob data to ensure it doesn't
+ // get finally released prior to the net::URLRequestJob being started.
+ webkit_blob::BlobData* requested_blob_data() const {
+ return requested_blob_data_.get();
+ }
+ void set_requested_blob_data(webkit_blob::BlobData* data);
+
+ private:
+ friend class ResourceDispatcherHost;
+
+ // Request is temporarily not handling network data. Should be used only
+ // by the ResourceDispatcherHost, not the event handlers (accessors are
+ // provided for consistency with the rest of the interface).
+ bool is_paused() const { return is_paused_; }
+ void set_is_paused(bool paused) { is_paused_ = paused; }
+
+ // Whether we called OnResponseStarted for this request or not. Should be used
+ // only by the ResourceDispatcherHost, not the event handlers (accessors are
+ // provided for consistency with the rest of the interface).
+ bool called_on_response_started() const {
+ return called_on_response_started_;
+ }
+ void set_called_on_response_started(bool called) {
+ called_on_response_started_ = called;
+ }
+
+ // Whether this request has started reading any bytes from the response
+ // yet. Will be true after the first (unpaused) call to Read. Should be used
+ // only by the ResourceDispatcherHost, not the event handlers (accessors are
+ // provided for consistency with the rest of the interface).
+ bool has_started_reading() const { return has_started_reading_; }
+ void set_has_started_reading(bool reading) { has_started_reading_ = reading; }
+
+ // How many bytes have been read while this request has been paused. Should be
+ // used only by the ResourceDispatcherHost, not the event handlers (accessors
+ // are provided for consistency with the rest of the interface).
+ int paused_read_bytes() const { return paused_read_bytes_; }
+ void set_paused_read_bytes(int bytes) { paused_read_bytes_ = bytes; }
+
+ scoped_refptr<ResourceHandler> resource_handler_;
+ CrossSiteResourceHandler* cross_site_handler_; // Non-owning, may be NULL.
+ scoped_refptr<LoginHandler> login_handler_;
+ scoped_refptr<SSLClientAuthHandler> ssl_client_auth_handler_;
+ ChildProcessInfo::ProcessType process_type_;
+ int child_id_;
+ int route_id_;
+ int request_id_;
+ int pending_data_count_;
+ bool is_download_;
+ bool allow_download_;
+ bool has_user_gesture_;
+ int pause_count_;
+ ResourceType::Type resource_type_;
+ bool replace_extension_localization_templates_;
+ net::LoadState last_load_state_;
+ uint64 upload_size_;
+ uint64 last_upload_position_;
+ base::TimeTicks last_upload_ticks_;
+ bool waiting_for_upload_progress_ack_;
+ int memory_cost_;
+ scoped_refptr<webkit_blob::BlobData> requested_blob_data_;
+
+ // "Private" data accessible only to ResourceDispatcherHost (use the
+ // accessors above for consistency).
+ bool is_paused_;
+ bool called_on_response_started_;
+ bool has_started_reading_;
+ int paused_read_bytes_;
+
+ // The following two members are specified if the request is initiated by
+ // a plugin like Gears.
+
+ // Contains the id of the host renderer.
+ int host_renderer_id_;
+ // Contains the id of the host render view.
+ int host_render_view_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(ResourceDispatcherHostRequestInfo);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_RESOURCE_DISPATCHER_HOST_REQUEST_INFO_H_
diff --git a/content/browser/renderer_host/resource_dispatcher_host_uitest.cc b/content/browser/renderer_host/resource_dispatcher_host_uitest.cc
new file mode 100644
index 0000000..5271595
--- /dev/null
+++ b/content/browser/renderer_host/resource_dispatcher_host_uitest.cc
@@ -0,0 +1,335 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <sstream>
+#include <string>
+
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "base/test/test_timeouts.h"
+#include "chrome/browser/net/url_request_failed_dns_job.h"
+#include "chrome/browser/net/url_request_mock_http_job.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/test/automation/browser_proxy.h"
+#include "chrome/test/automation/tab_proxy.h"
+#include "chrome/test/ui/ui_test.h"
+#include "net/base/net_util.h"
+#include "net/test/test_server.h"
+
+namespace {
+
+class ResourceDispatcherTest : public UITest {
+ public:
+ void CheckTitleTest(const std::wstring& file,
+ const std::wstring& expected_title,
+ int expected_navigations) {
+ NavigateToURLBlockUntilNavigationsComplete(
+ URLRequestMockHTTPJob::GetMockUrl(FilePath::FromWStringHack(file)),
+ expected_navigations);
+ EXPECT_EQ(expected_title, GetActiveTabTitle());
+ }
+
+ protected:
+ ResourceDispatcherTest() : UITest() {
+ dom_automation_enabled_ = true;
+ }
+};
+
+TEST_F(ResourceDispatcherTest, SniffHTMLWithNoContentType) {
+ CheckTitleTest(L"content-sniffer-test0.html",
+ L"Content Sniffer Test 0", 1);
+}
+
+TEST_F(ResourceDispatcherTest, RespectNoSniffDirective) {
+ CheckTitleTest(L"nosniff-test.html", L"", 1);
+}
+
+TEST_F(ResourceDispatcherTest, DoNotSniffHTMLFromTextPlain) {
+ CheckTitleTest(L"content-sniffer-test1.html", L"", 1);
+}
+
+TEST_F(ResourceDispatcherTest, DoNotSniffHTMLFromImageGIF) {
+ CheckTitleTest(L"content-sniffer-test2.html", L"", 1);
+}
+
+TEST_F(ResourceDispatcherTest, SniffNoContentTypeNoData) {
+ CheckTitleTest(L"content-sniffer-test3.html",
+ L"Content Sniffer Test 3", 1);
+ EXPECT_EQ(1, GetTabCount());
+
+ // Make sure the download shelf is not showing.
+ scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
+ ASSERT_TRUE(browser.get());
+ bool visible = false;
+ ASSERT_TRUE(browser->IsShelfVisible(&visible));
+ EXPECT_FALSE(visible);
+}
+
+TEST_F(ResourceDispatcherTest, ContentDispositionEmpty) {
+ CheckTitleTest(L"content-disposition-empty.html", L"success", 1);
+}
+
+TEST_F(ResourceDispatcherTest, ContentDispositionInline) {
+ CheckTitleTest(L"content-disposition-inline.html", L"success", 1);
+}
+
+// Test for bug #1091358.
+// Flaky: http://crbug.com/62595
+TEST_F(ResourceDispatcherTest, FLAKY_SyncXMLHttpRequest) {
+ net::TestServer test_server(net::TestServer::TYPE_HTTP,
+ FilePath(FILE_PATH_LITERAL("chrome/test/data")));
+ ASSERT_TRUE(test_server.Start());
+
+ scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
+ ASSERT_TRUE(browser_proxy.get());
+ scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
+ ASSERT_TRUE(tab.get());
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
+ tab->NavigateToURL(test_server.GetURL(
+ "files/sync_xmlhttprequest.html")));
+
+ // Let's check the XMLHttpRequest ran successfully.
+ bool success = false;
+ EXPECT_TRUE(tab->ExecuteAndExtractBool(L"",
+ L"window.domAutomationController.send(DidSyncRequestSucceed());",
+ &success));
+ EXPECT_TRUE(success);
+}
+
+// http://code.google.com/p/chromium/issues/detail?id=62776
+TEST_F(ResourceDispatcherTest, FLAKY_SyncXMLHttpRequest_Disallowed) {
+ net::TestServer test_server(net::TestServer::TYPE_HTTP,
+ FilePath(FILE_PATH_LITERAL("chrome/test/data")));
+ ASSERT_TRUE(test_server.Start());
+
+ scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
+ ASSERT_TRUE(browser_proxy.get());
+ scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
+ ASSERT_TRUE(tab.get());
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
+ tab->NavigateToURL(test_server.GetURL(
+ "files/sync_xmlhttprequest_disallowed.html")));
+
+ // Let's check the XMLHttpRequest ran successfully.
+ bool success = false;
+ EXPECT_TRUE(tab->ExecuteAndExtractBool(L"",
+ L"window.domAutomationController.send(DidSucceed());",
+ &success));
+ EXPECT_TRUE(success);
+}
+
+// Test for bug #1159553 -- A synchronous xhr (whose content-type is
+// downloadable) would trigger download and hang the renderer process,
+// if executed while navigating to a new page.
+// Disabled -- http://code.google.com/p/chromium/issues/detail?id=56264
+TEST_F(ResourceDispatcherTest, DISABLED_SyncXMLHttpRequest_DuringUnload) {
+ net::TestServer test_server(net::TestServer::TYPE_HTTP,
+ FilePath(FILE_PATH_LITERAL("chrome/test/data")));
+ ASSERT_TRUE(test_server.Start());
+
+ scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
+ ASSERT_TRUE(browser_proxy.get());
+ scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
+ ASSERT_TRUE(tab.get());
+
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
+ tab->NavigateToURL(test_server.GetURL(
+ "files/sync_xmlhttprequest_during_unload.html")));
+
+ // Confirm that the page has loaded (since it changes its title during load).
+ std::wstring tab_title;
+ EXPECT_TRUE(tab->GetTabTitle(&tab_title));
+ EXPECT_EQ(L"sync xhr on unload", tab_title);
+
+ // Navigate to a new page, to dispatch unload event and trigger xhr.
+ // (the bug would make this step hang the renderer).
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
+ tab->NavigateToURL(test_server.GetURL("files/title2.html")));
+
+ // Check that the new page got loaded, and that no download was triggered.
+ EXPECT_TRUE(tab->GetTabTitle(&tab_title));
+ EXPECT_EQ(L"Title Of Awesomeness", tab_title);
+
+ bool shelf_is_visible = false;
+ scoped_refptr<BrowserProxy> browser(automation()->GetBrowserWindow(0));
+ ASSERT_TRUE(browser.get());
+ EXPECT_TRUE(browser->IsShelfVisible(&shelf_is_visible));
+ EXPECT_FALSE(shelf_is_visible);
+}
+
+// Tests that onunload is run for cross-site requests. (Bug 1114994)
+TEST_F(ResourceDispatcherTest, CrossSiteOnunloadCookie) {
+ net::TestServer test_server(net::TestServer::TYPE_HTTP,
+ FilePath(FILE_PATH_LITERAL("chrome/test/data")));
+ ASSERT_TRUE(test_server.Start());
+
+ scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
+ ASSERT_TRUE(browser_proxy.get());
+ scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
+ ASSERT_TRUE(tab.get());
+
+ GURL url(test_server.GetURL("files/onunload_cookie.html"));
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab->NavigateToURL(url));
+
+ // Confirm that the page has loaded (since it changes its title during load).
+ std::wstring tab_title;
+ EXPECT_TRUE(tab->GetTabTitle(&tab_title));
+ EXPECT_EQ(L"set cookie on unload", tab_title);
+
+ // Navigate to a new cross-site page, to dispatch unload event and set the
+ // cookie.
+ CheckTitleTest(L"content-sniffer-test0.html",
+ L"Content Sniffer Test 0", 1);
+
+ // Check that the cookie was set.
+ std::string value_result;
+ ASSERT_TRUE(tab->GetCookieByName(url, "onunloadCookie", &value_result));
+ ASSERT_FALSE(value_result.empty());
+ ASSERT_STREQ("foo", value_result.c_str());
+}
+
+#if !defined(OS_MACOSX)
+// Tests that the onbeforeunload and onunload logic is shortcutted if the old
+// renderer is gone. In that case, we don't want to wait for the old renderer
+// to run the handlers.
+// We need to disable this on Mac because the crash causes the OS CrashReporter
+// process to kick in to analyze the poor dead renderer. Unfortunately, if the
+// app isn't stripped of debug symbols, this takes about five minutes to
+// complete and isn't conducive to quick turnarounds. As we don't currently
+// strip the app on the build bots, this is bad times.
+TEST_F(ResourceDispatcherTest, CrossSiteAfterCrash) {
+ // This test only works in multi-process mode
+ if (ProxyLauncher::in_process_renderer())
+ return;
+
+ scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
+ ASSERT_TRUE(browser_proxy.get());
+ scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
+ ASSERT_TRUE(tab.get());
+
+ // Cause the renderer to crash.
+#if defined(OS_WIN) || defined(USE_LINUX_BREAKPAD)
+ expected_crashes_ = 1;
+#endif
+ ASSERT_TRUE(tab->NavigateToURLAsync(GURL(chrome::kAboutCrashURL)));
+ // Wait for browser to notice the renderer crash.
+ base::PlatformThread::Sleep(TestTimeouts::action_timeout_ms());
+
+ // Navigate to a new cross-site page. The browser should not wait around for
+ // the old renderer's on{before}unload handlers to run.
+ CheckTitleTest(L"content-sniffer-test0.html",
+ L"Content Sniffer Test 0", 1);
+}
+#endif // !defined(OS_MACOSX)
+
+// Tests that cross-site navigations work when the new page does not go through
+// the BufferedEventHandler (e.g., non-http{s} URLs). (Bug 1225872)
+TEST_F(ResourceDispatcherTest, CrossSiteNavigationNonBuffered) {
+ scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
+ ASSERT_TRUE(browser_proxy.get());
+ scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
+ ASSERT_TRUE(tab.get());
+
+ // Start with an HTTP page.
+ CheckTitleTest(L"content-sniffer-test0.html",
+ L"Content Sniffer Test 0", 1);
+
+ // Now load a file:// page, which does not use the BufferedEventHandler.
+ // Make sure that the page loads and displays a title, and doesn't get stuck.
+ FilePath test_file(test_data_directory_);
+ test_file = test_file.AppendASCII("title2.html");
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
+ tab->NavigateToURL(net::FilePathToFileURL(test_file)));
+ EXPECT_EQ(L"Title Of Awesomeness", GetActiveTabTitle());
+}
+
+// Tests that a cross-site navigation to an error page (resulting in the link
+// doctor page) still runs the onunload handler and can support navigations
+// away from the link doctor page. (Bug 1235537)
+TEST_F(ResourceDispatcherTest, CrossSiteNavigationErrorPage) {
+ net::TestServer test_server(net::TestServer::TYPE_HTTP,
+ FilePath(FILE_PATH_LITERAL("chrome/test/data")));
+ ASSERT_TRUE(test_server.Start());
+
+ scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
+ ASSERT_TRUE(browser_proxy.get());
+ scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
+ ASSERT_TRUE(tab.get());
+
+ GURL url(test_server.GetURL("files/onunload_cookie.html"));
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab->NavigateToURL(url));
+
+ // Confirm that the page has loaded (since it changes its title during load).
+ std::wstring tab_title;
+ EXPECT_TRUE(tab->GetTabTitle(&tab_title));
+ EXPECT_EQ(L"set cookie on unload", tab_title);
+
+ // Navigate to a new cross-site URL that results in an error page.
+ // TODO(creis): If this causes crashes or hangs, it might be for the same
+ // reason as ErrorPageTest::DNSError. See bug 1199491 and
+ // http://crbug.com/22877.
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
+ tab->NavigateToURLBlockUntilNavigationsComplete(
+ GURL(URLRequestFailedDnsJob::kTestUrl), 2));
+ EXPECT_NE(L"set cookie on unload", GetActiveTabTitle());
+
+ // Check that the cookie was set, meaning that the onunload handler ran.
+ std::string value_result;
+ EXPECT_TRUE(tab->GetCookieByName(url, "onunloadCookie", &value_result));
+ EXPECT_FALSE(value_result.empty());
+ EXPECT_STREQ("foo", value_result.c_str());
+
+ // Check that renderer-initiated navigations still work. In a previous bug,
+ // the ResourceDispatcherHost would think that such navigations were
+ // cross-site, because we didn't clean up from the previous request. Since
+ // TabContents was in the NORMAL state, it would ignore the attempt to run
+ // the onunload handler, and the navigation would fail.
+ // (Test by redirecting to javascript:window.location='someURL'.)
+ GURL test_url(test_server.GetURL("files/title2.html"));
+ std::string redirect_url = "javascript:window.location='" +
+ test_url.possibly_invalid_spec() + "'";
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS,
+ tab->NavigateToURL(GURL(redirect_url)));
+ EXPECT_TRUE(tab->GetTabTitle(&tab_title));
+ EXPECT_EQ(L"Title Of Awesomeness", tab_title);
+}
+
+TEST_F(ResourceDispatcherTest, CrossOriginRedirectBlocked) {
+ // We expect the following URL requests from this test:
+ // 1- http://mock.http/cross-origin-redirect-blocked.html
+ // 2- http://mock.http/redirect-to-title2.html
+ // 3- http://mock.http/title2.html
+ //
+ // If the redirect in #2 were not blocked, we'd also see a request
+ // for http://mock.http:4000/title2.html, and the title would be different.
+ CheckTitleTest(L"cross-origin-redirect-blocked.html",
+ L"Title Of More Awesomeness", 2);
+}
+
+// Tests that ResourceDispatcherHostRequestInfo is updated correctly on failed
+// requests, to prevent calling Read on a request that has already failed.
+// See bug 40250.
+TEST_F(ResourceDispatcherTest, CrossSiteFailedRequest) {
+ scoped_refptr<BrowserProxy> browser_proxy(automation()->GetBrowserWindow(0));
+ ASSERT_TRUE(browser_proxy.get());
+ scoped_refptr<TabProxy> tab(browser_proxy->GetActiveTab());
+ ASSERT_TRUE(tab.get());
+
+ // Visit another URL first to trigger a cross-site navigation.
+ GURL url(chrome::kChromeUINewTabURL);
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab->NavigateToURL(url));
+
+ // Visit a URL that fails without calling ResourceDispatcherHost::Read.
+ GURL broken_url("chrome://theme");
+ ASSERT_EQ(AUTOMATION_MSG_NAVIGATION_SUCCESS, tab->NavigateToURL(broken_url));
+
+ // Make sure the navigation finishes.
+ std::wstring tab_title;
+ EXPECT_TRUE(tab->GetTabTitle(&tab_title));
+ EXPECT_EQ(L"chrome://theme/ is not available", tab_title);
+}
+
+} // namespace
diff --git a/content/browser/renderer_host/resource_dispatcher_host_unittest.cc b/content/browser/renderer_host/resource_dispatcher_host_unittest.cc
new file mode 100644
index 0000000..e68c3b9
--- /dev/null
+++ b/content/browser/renderer_host/resource_dispatcher_host_unittest.cc
@@ -0,0 +1,1026 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/file_path.h"
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "chrome/browser/browser_thread.h"
+#include "chrome/browser/child_process_security_policy.h"
+#include "chrome/common/chrome_plugin_lib.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/render_messages_params.h"
+#include "chrome/common/resource_response.h"
+#include "content/browser/renderer_host/resource_dispatcher_host.h"
+#include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
+#include "content/browser/renderer_host/resource_handler.h"
+#include "content/browser/renderer_host/resource_message_filter.h"
+#include "net/base/net_errors.h"
+#include "net/base/upload_data.h"
+#include "net/http/http_util.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_job.h"
+#include "net/url_request/url_request_test_job.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webkit/appcache/appcache_interfaces.h"
+
+// TODO(eroman): Write unit tests for SafeBrowsing that exercise
+// SafeBrowsingResourceHandler.
+
+namespace {
+
+// Returns the resource response header structure for this request.
+void GetResponseHead(const std::vector<IPC::Message>& messages,
+ ResourceResponseHead* response_head) {
+ ASSERT_GE(messages.size(), 2U);
+
+ // The first messages should be received response.
+ ASSERT_EQ(ViewMsg_Resource_ReceivedResponse::ID, messages[0].type());
+
+ void* iter = NULL;
+ int request_id;
+ ASSERT_TRUE(IPC::ReadParam(&messages[0], &iter, &request_id));
+ ASSERT_TRUE(IPC::ReadParam(&messages[0], &iter, response_head));
+}
+
+} // namespace
+
+static int RequestIDForMessage(const IPC::Message& msg) {
+ int request_id = -1;
+ switch (msg.type()) {
+ case ViewMsg_Resource_UploadProgress::ID:
+ case ViewMsg_Resource_ReceivedResponse::ID:
+ case ViewMsg_Resource_ReceivedRedirect::ID:
+ case ViewMsg_Resource_DataReceived::ID:
+ case ViewMsg_Resource_RequestComplete::ID:
+ request_id = IPC::MessageIterator(msg).NextInt();
+ break;
+ }
+ return request_id;
+}
+
+static ViewHostMsg_Resource_Request CreateResourceRequest(
+ const char* method,
+ ResourceType::Type type,
+ const GURL& url) {
+ ViewHostMsg_Resource_Request request;
+ request.method = std::string(method);
+ request.url = url;
+ request.first_party_for_cookies = url; // bypass third-party cookie blocking
+ request.load_flags = 0;
+ request.origin_pid = 0;
+ request.resource_type = type;
+ request.request_context = 0;
+ request.appcache_host_id = appcache::kNoHostId;
+ request.download_to_file = false;
+ request.host_renderer_id = -1;
+ request.host_render_view_id = -1;
+ return request;
+}
+
+// Spin up the message loop to kick off the request.
+static void KickOffRequest() {
+ MessageLoop::current()->RunAllPending();
+}
+
+// We may want to move this to a shared space if it is useful for something else
+class ResourceIPCAccumulator {
+ public:
+ void AddMessage(const IPC::Message& msg) {
+ messages_.push_back(msg);
+ }
+
+ // This groups the messages by their request ID. The groups will be in order
+ // that the first message for each request ID was received, and the messages
+ // within the groups will be in the order that they appeared.
+ // Note that this clears messages_.
+ typedef std::vector< std::vector<IPC::Message> > ClassifiedMessages;
+ void GetClassifiedMessages(ClassifiedMessages* msgs);
+
+ std::vector<IPC::Message> messages_;
+};
+
+// This is very inefficient as a result of repeatedly extracting the ID, use
+// only for tests!
+void ResourceIPCAccumulator::GetClassifiedMessages(ClassifiedMessages* msgs) {
+ while (!messages_.empty()) {
+ std::vector<IPC::Message> cur_requests;
+ cur_requests.push_back(messages_[0]);
+ int cur_id = RequestIDForMessage(messages_[0]);
+
+ // find all other messages with this ID
+ for (int i = 1; i < static_cast<int>(messages_.size()); i++) {
+ int id = RequestIDForMessage(messages_[i]);
+ if (id == cur_id) {
+ cur_requests.push_back(messages_[i]);
+ messages_.erase(messages_.begin() + i);
+ i--;
+ }
+ }
+ messages_.erase(messages_.begin());
+ msgs->push_back(cur_requests);
+ }
+}
+
+// This class forwards the incoming messages to the ResourceDispatcherHostTest.
+// This is used to emulate different sub-processes, since this filter will
+// have a different ID than the original. For the test, we want all the incoming
+// messages to go to the same place, which is why this forwards.
+class ForwardingFilter : public ResourceMessageFilter {
+ public:
+ explicit ForwardingFilter(IPC::Message::Sender* dest)
+ : ResourceMessageFilter(ChildProcessInfo::GenerateChildProcessUniqueId(),
+ ChildProcessInfo::RENDER_PROCESS,
+ NULL),
+ dest_(dest) {
+ OnChannelConnected(base::GetCurrentProcId());
+ }
+
+ // ResourceMessageFilter override
+ virtual bool Send(IPC::Message* msg) {
+ if (!dest_)
+ return false;
+ return dest_->Send(msg);
+ }
+
+ private:
+ IPC::Message::Sender* dest_;
+
+ DISALLOW_COPY_AND_ASSIGN(ForwardingFilter);
+};
+
+class ResourceDispatcherHostTest : public testing::Test,
+ public IPC::Message::Sender {
+ public:
+ ResourceDispatcherHostTest()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(filter_(new ForwardingFilter(this))),
+ ui_thread_(BrowserThread::UI, &message_loop_),
+ io_thread_(BrowserThread::IO, &message_loop_),
+ old_factory_(NULL),
+ resource_type_(ResourceType::SUB_RESOURCE) {
+ }
+ // IPC::Message::Sender implementation
+ virtual bool Send(IPC::Message* msg) {
+ accum_.AddMessage(*msg);
+ delete msg;
+ return true;
+ }
+
+ protected:
+ // testing::Test
+ virtual void SetUp() {
+ DCHECK(!test_fixture_);
+ test_fixture_ = this;
+ ChildProcessSecurityPolicy::GetInstance()->Add(0);
+ net::URLRequest::RegisterProtocolFactory(
+ "test",
+ &ResourceDispatcherHostTest::Factory);
+ EnsureTestSchemeIsAllowed();
+ }
+
+ virtual void TearDown() {
+ net::URLRequest::RegisterProtocolFactory("test", NULL);
+ if (!scheme_.empty())
+ net::URLRequest::RegisterProtocolFactory(scheme_, old_factory_);
+
+ DCHECK(test_fixture_ == this);
+ test_fixture_ = NULL;
+
+ host_.Shutdown();
+
+ ChildProcessSecurityPolicy::GetInstance()->Remove(0);
+
+ // The plugin lib is automatically loaded during these test
+ // and we want a clean environment for other tests.
+ ChromePluginLib::UnloadAllPlugins();
+
+ // Flush the message loop to make Purify happy.
+ message_loop_.RunAllPending();
+ }
+
+ // Creates a request using the current test object as the filter.
+ void MakeTestRequest(int render_view_id,
+ int request_id,
+ const GURL& url);
+
+ // Generates a request using the given filter. This will probably be a
+ // ForwardingFilter.
+ void MakeTestRequest(ResourceMessageFilter* filter,
+ int render_view_id,
+ int request_id,
+ const GURL& url);
+
+ void MakeCancelRequest(int request_id);
+
+ void EnsureTestSchemeIsAllowed() {
+ static bool have_white_listed_test_scheme = false;
+
+ if (!have_white_listed_test_scheme) {
+ ChildProcessSecurityPolicy::GetInstance()->RegisterWebSafeScheme("test");
+ have_white_listed_test_scheme = true;
+ }
+ }
+
+ // Sets a particular response for any request from now on. To switch back to
+ // the default bahavior, pass an empty |headers|. |headers| should be raw-
+ // formatted (NULLs instead of EOLs).
+ void SetResponse(const std::string& headers, const std::string& data) {
+ response_headers_ = headers;
+ response_data_ = data;
+ }
+
+ // Sets a particular resource type for any request from now on.
+ void SetResourceType(ResourceType::Type type) {
+ resource_type_ = type;
+ }
+
+ // Intercepts requests for the given protocol.
+ void HandleScheme(const std::string& scheme) {
+ DCHECK(scheme_.empty());
+ DCHECK(!old_factory_);
+ scheme_ = scheme;
+ old_factory_ = net::URLRequest::RegisterProtocolFactory(
+ scheme_, &ResourceDispatcherHostTest::Factory);
+ }
+
+ // Our own net::URLRequestJob factory.
+ static net::URLRequestJob* Factory(net::URLRequest* request,
+ const std::string& scheme) {
+ if (test_fixture_->response_headers_.empty()) {
+ return new net::URLRequestTestJob(request);
+ } else {
+ return new net::URLRequestTestJob(request,
+ test_fixture_->response_headers_,
+ test_fixture_->response_data_,
+ false);
+ }
+ }
+
+ scoped_refptr<ForwardingFilter> filter_;
+ MessageLoopForIO message_loop_;
+ BrowserThread ui_thread_;
+ BrowserThread io_thread_;
+ ResourceDispatcherHost host_;
+ ResourceIPCAccumulator accum_;
+ std::string response_headers_;
+ std::string response_data_;
+ std::string scheme_;
+ net::URLRequest::ProtocolFactory* old_factory_;
+ ResourceType::Type resource_type_;
+ static ResourceDispatcherHostTest* test_fixture_;
+};
+// Static.
+ResourceDispatcherHostTest* ResourceDispatcherHostTest::test_fixture_ = NULL;
+
+void ResourceDispatcherHostTest::MakeTestRequest(int render_view_id,
+ int request_id,
+ const GURL& url) {
+ MakeTestRequest(filter_.get(), render_view_id, request_id, url);
+}
+
+void ResourceDispatcherHostTest::MakeTestRequest(
+ ResourceMessageFilter* filter,
+ int render_view_id,
+ int request_id,
+ const GURL& url) {
+ ViewHostMsg_Resource_Request request =
+ CreateResourceRequest("GET", resource_type_, url);
+ ViewHostMsg_RequestResource msg(render_view_id, request_id, request);
+ bool msg_was_ok;
+ host_.OnMessageReceived(msg, filter, &msg_was_ok);
+ KickOffRequest();
+}
+
+void ResourceDispatcherHostTest::MakeCancelRequest(int request_id) {
+ host_.CancelRequest(filter_->child_id(), request_id, false);
+}
+
+void CheckSuccessfulRequest(const std::vector<IPC::Message>& messages,
+ const std::string& reference_data) {
+ // A successful request will have received 4 messages:
+ // ReceivedResponse (indicates headers received)
+ // DataReceived (data)
+ // XXX DataReceived (0 bytes remaining from a read)
+ // RequestComplete (request is done)
+ //
+ // This function verifies that we received 4 messages and that they
+ // are appropriate.
+ ASSERT_EQ(3U, messages.size());
+
+ // The first messages should be received response
+ ASSERT_EQ(ViewMsg_Resource_ReceivedResponse::ID, messages[0].type());
+
+ // followed by the data, currently we only do the data in one chunk, but
+ // should probably test multiple chunks later
+ ASSERT_EQ(ViewMsg_Resource_DataReceived::ID, messages[1].type());
+
+ void* iter = NULL;
+ int request_id;
+ ASSERT_TRUE(IPC::ReadParam(&messages[1], &iter, &request_id));
+ base::SharedMemoryHandle shm_handle;
+ ASSERT_TRUE(IPC::ReadParam(&messages[1], &iter, &shm_handle));
+ uint32 data_len;
+ ASSERT_TRUE(IPC::ReadParam(&messages[1], &iter, &data_len));
+
+ ASSERT_EQ(reference_data.size(), data_len);
+ base::SharedMemory shared_mem(shm_handle, true); // read only
+ shared_mem.Map(data_len);
+ const char* data = static_cast<char*>(shared_mem.memory());
+ ASSERT_EQ(0, memcmp(reference_data.c_str(), data, data_len));
+
+ // followed by a 0-byte read
+ //ASSERT_EQ(ViewMsg_Resource_DataReceived::ID, messages[2].type());
+
+ // the last message should be all data received
+ ASSERT_EQ(ViewMsg_Resource_RequestComplete::ID, messages[2].type());
+}
+
+// Tests whether many messages get dispatched properly.
+TEST_F(ResourceDispatcherHostTest, TestMany) {
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(0));
+
+ MakeTestRequest(0, 1, net::URLRequestTestJob::test_url_1());
+ MakeTestRequest(0, 2, net::URLRequestTestJob::test_url_2());
+ MakeTestRequest(0, 3, net::URLRequestTestJob::test_url_3());
+
+ // flush all the pending requests
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(0));
+
+ // sorts out all the messages we saw by request
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+
+ // there are three requests, so we should have gotten them classified as such
+ ASSERT_EQ(3U, msgs.size());
+
+ CheckSuccessfulRequest(msgs[0], net::URLRequestTestJob::test_data_1());
+ CheckSuccessfulRequest(msgs[1], net::URLRequestTestJob::test_data_2());
+ CheckSuccessfulRequest(msgs[2], net::URLRequestTestJob::test_data_3());
+}
+
+// Tests whether messages get canceled properly. We issue three requests,
+// cancel one of them, and make sure that each sent the proper notifications.
+TEST_F(ResourceDispatcherHostTest, Cancel) {
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(0));
+
+ MakeTestRequest(0, 1, net::URLRequestTestJob::test_url_1());
+ MakeTestRequest(0, 2, net::URLRequestTestJob::test_url_2());
+ MakeTestRequest(0, 3, net::URLRequestTestJob::test_url_3());
+ MakeCancelRequest(2);
+
+ // flush all the pending requests
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+ MessageLoop::current()->RunAllPending();
+
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(0));
+
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+
+ // there are three requests, so we should have gotten them classified as such
+ ASSERT_EQ(3U, msgs.size());
+
+ CheckSuccessfulRequest(msgs[0], net::URLRequestTestJob::test_data_1());
+ CheckSuccessfulRequest(msgs[2], net::URLRequestTestJob::test_data_3());
+
+ // Check that request 2 got canceled.
+ ASSERT_EQ(2U, msgs[1].size());
+ ASSERT_EQ(ViewMsg_Resource_ReceivedResponse::ID, msgs[1][0].type());
+ ASSERT_EQ(ViewMsg_Resource_RequestComplete::ID, msgs[1][1].type());
+
+ int request_id;
+ net::URLRequestStatus status;
+
+ void* iter = NULL;
+ ASSERT_TRUE(IPC::ReadParam(&msgs[1][1], &iter, &request_id));
+ ASSERT_TRUE(IPC::ReadParam(&msgs[1][1], &iter, &status));
+
+ EXPECT_EQ(net::URLRequestStatus::CANCELED, status.status());
+}
+
+// The host delegate acts as a second one so we can have some requests
+// pending and some canceled.
+class TestFilter : public ForwardingFilter {
+ public:
+ TestFilter()
+ : ForwardingFilter(NULL),
+ has_canceled_(false),
+ received_after_canceled_(0) {
+ }
+
+ // ForwardingFilter override
+ virtual bool Send(IPC::Message* msg) {
+ // no messages should be received when the process has been canceled
+ if (has_canceled_)
+ received_after_canceled_++;
+ delete msg;
+ return true;
+ }
+ bool has_canceled_;
+ int received_after_canceled_;
+};
+
+// Tests CancelRequestsForProcess
+TEST_F(ResourceDispatcherHostTest, TestProcessCancel) {
+ scoped_refptr<TestFilter> test_filter = new TestFilter();
+
+ // request 1 goes to the test delegate
+ ViewHostMsg_Resource_Request request = CreateResourceRequest(
+ "GET", ResourceType::SUB_RESOURCE, net::URLRequestTestJob::test_url_1());
+
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(0));
+
+ MakeTestRequest(test_filter.get(), 0, 1,
+ net::URLRequestTestJob::test_url_1());
+
+ // request 2 goes to us
+ MakeTestRequest(0, 2, net::URLRequestTestJob::test_url_2());
+
+ // request 3 goes to the test delegate
+ MakeTestRequest(test_filter.get(), 0, 3,
+ net::URLRequestTestJob::test_url_3());
+
+ // TODO(mbelshe):
+ // Now that the async IO path is in place, the IO always completes on the
+ // initial call; so the requests have already completed. This basically
+ // breaks the whole test.
+ //EXPECT_EQ(3, host_.pending_requests());
+
+ // Process each request for one level so one callback is called.
+ for (int i = 0; i < 3; i++)
+ EXPECT_TRUE(net::URLRequestTestJob::ProcessOnePendingMessage());
+
+ // Cancel the requests to the test process.
+ host_.CancelRequestsForProcess(filter_->child_id());
+ test_filter->has_canceled_ = true;
+
+ // Flush all the pending requests.
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+
+ EXPECT_EQ(0, host_.pending_requests());
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(filter_->child_id()));
+
+ // The test delegate should not have gotten any messages after being canceled.
+ ASSERT_EQ(0, test_filter->received_after_canceled_);
+
+ // We should have gotten exactly one result.
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+ ASSERT_EQ(1U, msgs.size());
+ CheckSuccessfulRequest(msgs[0], net::URLRequestTestJob::test_data_2());
+}
+
+// Tests blocking and resuming requests.
+TEST_F(ResourceDispatcherHostTest, TestBlockingResumingRequests) {
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(filter_->child_id()));
+
+ host_.BlockRequestsForRoute(filter_->child_id(), 1);
+ host_.BlockRequestsForRoute(filter_->child_id(), 2);
+ host_.BlockRequestsForRoute(filter_->child_id(), 3);
+
+ MakeTestRequest(0, 1, net::URLRequestTestJob::test_url_1());
+ MakeTestRequest(1, 2, net::URLRequestTestJob::test_url_2());
+ MakeTestRequest(0, 3, net::URLRequestTestJob::test_url_3());
+ MakeTestRequest(1, 4, net::URLRequestTestJob::test_url_1());
+ MakeTestRequest(2, 5, net::URLRequestTestJob::test_url_2());
+ MakeTestRequest(3, 6, net::URLRequestTestJob::test_url_3());
+
+ // Flush all the pending requests
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+
+ // Sort out all the messages we saw by request
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+
+ // All requests but the 2 for the RVH 0 should have been blocked.
+ ASSERT_EQ(2U, msgs.size());
+
+ CheckSuccessfulRequest(msgs[0], net::URLRequestTestJob::test_data_1());
+ CheckSuccessfulRequest(msgs[1], net::URLRequestTestJob::test_data_3());
+
+ // Resume requests for RVH 1 and flush pending requests.
+ host_.ResumeBlockedRequestsForRoute(filter_->child_id(), 1);
+ KickOffRequest();
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+
+ msgs.clear();
+ accum_.GetClassifiedMessages(&msgs);
+ ASSERT_EQ(2U, msgs.size());
+ CheckSuccessfulRequest(msgs[0], net::URLRequestTestJob::test_data_2());
+ CheckSuccessfulRequest(msgs[1], net::URLRequestTestJob::test_data_1());
+
+ // Test that new requests are not blocked for RVH 1.
+ MakeTestRequest(1, 7, net::URLRequestTestJob::test_url_1());
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+ msgs.clear();
+ accum_.GetClassifiedMessages(&msgs);
+ ASSERT_EQ(1U, msgs.size());
+ CheckSuccessfulRequest(msgs[0], net::URLRequestTestJob::test_data_1());
+
+ // Now resumes requests for all RVH (2 and 3).
+ host_.ResumeBlockedRequestsForRoute(filter_->child_id(), 2);
+ host_.ResumeBlockedRequestsForRoute(filter_->child_id(), 3);
+ KickOffRequest();
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(filter_->child_id()));
+
+ msgs.clear();
+ accum_.GetClassifiedMessages(&msgs);
+ ASSERT_EQ(2U, msgs.size());
+ CheckSuccessfulRequest(msgs[0], net::URLRequestTestJob::test_data_2());
+ CheckSuccessfulRequest(msgs[1], net::URLRequestTestJob::test_data_3());
+}
+
+// Tests blocking and canceling requests.
+TEST_F(ResourceDispatcherHostTest, TestBlockingCancelingRequests) {
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(filter_->child_id()));
+
+ host_.BlockRequestsForRoute(filter_->child_id(), 1);
+
+ MakeTestRequest(0, 1, net::URLRequestTestJob::test_url_1());
+ MakeTestRequest(1, 2, net::URLRequestTestJob::test_url_2());
+ MakeTestRequest(0, 3, net::URLRequestTestJob::test_url_3());
+ MakeTestRequest(1, 4, net::URLRequestTestJob::test_url_1());
+
+ // Flush all the pending requests.
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+
+ // Sort out all the messages we saw by request.
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+
+ // The 2 requests for the RVH 0 should have been processed.
+ ASSERT_EQ(2U, msgs.size());
+
+ CheckSuccessfulRequest(msgs[0], net::URLRequestTestJob::test_data_1());
+ CheckSuccessfulRequest(msgs[1], net::URLRequestTestJob::test_data_3());
+
+ // Cancel requests for RVH 1.
+ host_.CancelBlockedRequestsForRoute(filter_->child_id(), 1);
+ KickOffRequest();
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(filter_->child_id()));
+
+ msgs.clear();
+ accum_.GetClassifiedMessages(&msgs);
+ ASSERT_EQ(0U, msgs.size());
+}
+
+// Tests that blocked requests are canceled if their associated process dies.
+TEST_F(ResourceDispatcherHostTest, TestBlockedRequestsProcessDies) {
+ // This second filter is used to emulate a second process.
+ scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(this);
+
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(filter_->child_id()));
+ EXPECT_EQ(0,
+ host_.GetOutstandingRequestsMemoryCost(second_filter->child_id()));
+
+ host_.BlockRequestsForRoute(second_filter->child_id(), 0);
+
+ MakeTestRequest(filter_.get(), 0, 1, net::URLRequestTestJob::test_url_1());
+ MakeTestRequest(second_filter.get(), 0, 2,
+ net::URLRequestTestJob::test_url_2());
+ MakeTestRequest(filter_.get(), 0, 3, net::URLRequestTestJob::test_url_3());
+ MakeTestRequest(second_filter.get(), 0, 4,
+ net::URLRequestTestJob::test_url_1());
+
+ // Simulate process death.
+ host_.CancelRequestsForProcess(second_filter->child_id());
+
+ // Flush all the pending requests.
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(filter_->child_id()));
+ EXPECT_EQ(0,
+ host_.GetOutstandingRequestsMemoryCost(second_filter->child_id()));
+
+ // Sort out all the messages we saw by request.
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+
+ // The 2 requests for the RVH 0 should have been processed.
+ ASSERT_EQ(2U, msgs.size());
+
+ CheckSuccessfulRequest(msgs[0], net::URLRequestTestJob::test_data_1());
+ CheckSuccessfulRequest(msgs[1], net::URLRequestTestJob::test_data_3());
+
+ EXPECT_TRUE(host_.blocked_requests_map_.empty());
+}
+
+// Tests that blocked requests don't leak when the ResourceDispatcherHost goes
+// away. Note that we rely on Purify for finding the leaks if any.
+// If this test turns the Purify bot red, check the ResourceDispatcherHost
+// destructor to make sure the blocked requests are deleted.
+TEST_F(ResourceDispatcherHostTest, TestBlockedRequestsDontLeak) {
+ // This second filter is used to emulate a second process.
+ scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(this);
+
+ host_.BlockRequestsForRoute(filter_->child_id(), 1);
+ host_.BlockRequestsForRoute(filter_->child_id(), 2);
+ host_.BlockRequestsForRoute(second_filter->child_id(), 1);
+
+ MakeTestRequest(filter_.get(), 0, 1, net::URLRequestTestJob::test_url_1());
+ MakeTestRequest(filter_.get(), 1, 2, net::URLRequestTestJob::test_url_2());
+ MakeTestRequest(filter_.get(), 0, 3, net::URLRequestTestJob::test_url_3());
+ MakeTestRequest(second_filter.get(), 1, 4,
+ net::URLRequestTestJob::test_url_1());
+ MakeTestRequest(filter_.get(), 2, 5, net::URLRequestTestJob::test_url_2());
+ MakeTestRequest(filter_.get(), 2, 6, net::URLRequestTestJob::test_url_3());
+
+ // Flush all the pending requests.
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+}
+
+// Test the private helper method "CalculateApproximateMemoryCost()".
+TEST_F(ResourceDispatcherHostTest, CalculateApproximateMemoryCost) {
+ net::URLRequest req(GURL("http://www.google.com"), NULL);
+ EXPECT_EQ(4427, ResourceDispatcherHost::CalculateApproximateMemoryCost(&req));
+
+ // Add 9 bytes of referrer.
+ req.set_referrer("123456789");
+ EXPECT_EQ(4436, ResourceDispatcherHost::CalculateApproximateMemoryCost(&req));
+
+ // Add 33 bytes of upload content.
+ std::string upload_content;
+ upload_content.resize(33);
+ std::fill(upload_content.begin(), upload_content.end(), 'x');
+ req.AppendBytesToUpload(upload_content.data(), upload_content.size());
+
+ // Since the upload throttling is disabled, this has no effect on the cost.
+ EXPECT_EQ(4436, ResourceDispatcherHost::CalculateApproximateMemoryCost(&req));
+
+ // Add a file upload -- should have no effect.
+ req.AppendFileToUpload(FilePath(FILE_PATH_LITERAL("does-not-exist.png")));
+ EXPECT_EQ(4436, ResourceDispatcherHost::CalculateApproximateMemoryCost(&req));
+}
+
+// Test the private helper method "IncrementOutstandingRequestsMemoryCost()".
+TEST_F(ResourceDispatcherHostTest, IncrementOutstandingRequestsMemoryCost) {
+ // Add some counts for render_process_host=7
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(7));
+ EXPECT_EQ(1, host_.IncrementOutstandingRequestsMemoryCost(1, 7));
+ EXPECT_EQ(2, host_.IncrementOutstandingRequestsMemoryCost(1, 7));
+ EXPECT_EQ(3, host_.IncrementOutstandingRequestsMemoryCost(1, 7));
+
+ // Add some counts for render_process_host=3
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(3));
+ EXPECT_EQ(1, host_.IncrementOutstandingRequestsMemoryCost(1, 3));
+ EXPECT_EQ(2, host_.IncrementOutstandingRequestsMemoryCost(1, 3));
+
+ // Remove all the counts for render_process_host=7
+ EXPECT_EQ(3, host_.GetOutstandingRequestsMemoryCost(7));
+ EXPECT_EQ(2, host_.IncrementOutstandingRequestsMemoryCost(-1, 7));
+ EXPECT_EQ(1, host_.IncrementOutstandingRequestsMemoryCost(-1, 7));
+ EXPECT_EQ(0, host_.IncrementOutstandingRequestsMemoryCost(-1, 7));
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(7));
+
+ // Remove all the counts for render_process_host=3
+ EXPECT_EQ(2, host_.GetOutstandingRequestsMemoryCost(3));
+ EXPECT_EQ(1, host_.IncrementOutstandingRequestsMemoryCost(-1, 3));
+ EXPECT_EQ(0, host_.IncrementOutstandingRequestsMemoryCost(-1, 3));
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(3));
+
+ // When an entry reaches 0, it should be deleted.
+ EXPECT_TRUE(host_.outstanding_requests_memory_cost_map_.end() ==
+ host_.outstanding_requests_memory_cost_map_.find(7));
+ EXPECT_TRUE(host_.outstanding_requests_memory_cost_map_.end() ==
+ host_.outstanding_requests_memory_cost_map_.find(3));
+}
+
+// Test that when too many requests are outstanding for a particular
+// render_process_host_id, any subsequent request from it fails.
+TEST_F(ResourceDispatcherHostTest, TooManyOutstandingRequests) {
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(filter_->child_id()));
+
+ // Expected cost of each request as measured by
+ // ResourceDispatcherHost::CalculateApproximateMemoryCost().
+ int kMemoryCostOfTest2Req =
+ ResourceDispatcherHost::kAvgBytesPerOutstandingRequest +
+ std::string("GET").size() +
+ net::URLRequestTestJob::test_url_2().spec().size();
+
+ // Tighten the bound on the ResourceDispatcherHost, to speed things up.
+ int kMaxCostPerProcess = 440000;
+ host_.set_max_outstanding_requests_cost_per_process(kMaxCostPerProcess);
+
+ // Determine how many instance of test_url_2() we can request before
+ // throttling kicks in.
+ size_t kMaxRequests = kMaxCostPerProcess / kMemoryCostOfTest2Req;
+
+ // This second filter is used to emulate a second process.
+ scoped_refptr<ForwardingFilter> second_filter = new ForwardingFilter(this);
+
+ // Saturate the number of outstanding requests for our process.
+ for (size_t i = 0; i < kMaxRequests; ++i) {
+ MakeTestRequest(filter_.get(), 0, i + 1,
+ net::URLRequestTestJob::test_url_2());
+ }
+
+ // Issue two more requests for our process -- these should fail immediately.
+ MakeTestRequest(filter_.get(), 0, kMaxRequests + 1,
+ net::URLRequestTestJob::test_url_2());
+ MakeTestRequest(filter_.get(), 0, kMaxRequests + 2,
+ net::URLRequestTestJob::test_url_2());
+
+ // Issue two requests for the second process -- these should succeed since
+ // it is just process 0 that is saturated.
+ MakeTestRequest(second_filter.get(), 0, kMaxRequests + 3,
+ net::URLRequestTestJob::test_url_2());
+ MakeTestRequest(second_filter.get(), 0, kMaxRequests + 4,
+ net::URLRequestTestJob::test_url_2());
+
+ // Flush all the pending requests.
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+ MessageLoop::current()->RunAllPending();
+
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(filter_->child_id()));
+
+ // Sorts out all the messages we saw by request.
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+
+ // We issued (kMaxRequests + 4) total requests.
+ ASSERT_EQ(kMaxRequests + 4, msgs.size());
+
+ // Check that the first kMaxRequests succeeded.
+ for (size_t i = 0; i < kMaxRequests; ++i)
+ CheckSuccessfulRequest(msgs[i], net::URLRequestTestJob::test_data_2());
+
+ // Check that the subsequent two requests (kMaxRequests + 1) and
+ // (kMaxRequests + 2) were failed, since the per-process bound was reached.
+ for (int i = 0; i < 2; ++i) {
+ // Should have sent a single RequestComplete message.
+ int index = kMaxRequests + i;
+ EXPECT_EQ(1U, msgs[index].size());
+ EXPECT_EQ(ViewMsg_Resource_RequestComplete::ID, msgs[index][0].type());
+
+ // The RequestComplete message should have had status
+ // (CANCELLED, ERR_INSUFFICIENT_RESOURCES).
+ int request_id;
+ net::URLRequestStatus status;
+
+ void* iter = NULL;
+ EXPECT_TRUE(IPC::ReadParam(&msgs[index][0], &iter, &request_id));
+ EXPECT_TRUE(IPC::ReadParam(&msgs[index][0], &iter, &status));
+
+ EXPECT_EQ(index + 1, request_id);
+ EXPECT_EQ(net::URLRequestStatus::CANCELED, status.status());
+ EXPECT_EQ(net::ERR_INSUFFICIENT_RESOURCES, status.os_error());
+ }
+
+ // The final 2 requests should have succeeded.
+ CheckSuccessfulRequest(msgs[kMaxRequests + 2],
+ net::URLRequestTestJob::test_data_2());
+ CheckSuccessfulRequest(msgs[kMaxRequests + 3],
+ net::URLRequestTestJob::test_data_2());
+}
+
+// Tests that we sniff the mime type for a simple request.
+TEST_F(ResourceDispatcherHostTest, MimeSniffed) {
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(0));
+
+ std::string response("HTTP/1.1 200 OK\n\n");
+ std::string raw_headers(net::HttpUtil::AssembleRawHeaders(response.data(),
+ response.size()));
+ std::string response_data("<html><title>Test One</title></html>");
+ SetResponse(raw_headers, response_data);
+
+ HandleScheme("http");
+ MakeTestRequest(0, 1, GURL("http:bla"));
+
+ // Flush all pending requests.
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(0));
+
+ // Sorts out all the messages we saw by request.
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+ ASSERT_EQ(1U, msgs.size());
+
+ ResourceResponseHead response_head;
+ GetResponseHead(msgs[0], &response_head);
+ ASSERT_EQ("text/html", response_head.mime_type);
+}
+
+// Tests that we don't sniff the mime type when the server provides one.
+TEST_F(ResourceDispatcherHostTest, MimeNotSniffed) {
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(0));
+
+ std::string response("HTTP/1.1 200 OK\n"
+ "Content-type: image/jpeg\n\n");
+ std::string raw_headers(net::HttpUtil::AssembleRawHeaders(response.data(),
+ response.size()));
+ std::string response_data("<html><title>Test One</title></html>");
+ SetResponse(raw_headers, response_data);
+
+ HandleScheme("http");
+ MakeTestRequest(0, 1, GURL("http:bla"));
+
+ // Flush all pending requests.
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(0));
+
+ // Sorts out all the messages we saw by request.
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+ ASSERT_EQ(1U, msgs.size());
+
+ ResourceResponseHead response_head;
+ GetResponseHead(msgs[0], &response_head);
+ ASSERT_EQ("image/jpeg", response_head.mime_type);
+}
+
+// Tests that we don't sniff the mime type when there is no message body.
+TEST_F(ResourceDispatcherHostTest, MimeNotSniffed2) {
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(0));
+
+ std::string response("HTTP/1.1 304 Not Modified\n\n");
+ std::string raw_headers(net::HttpUtil::AssembleRawHeaders(response.data(),
+ response.size()));
+ std::string response_data;
+ SetResponse(raw_headers, response_data);
+
+ HandleScheme("http");
+ MakeTestRequest(0, 1, GURL("http:bla"));
+
+ // Flush all pending requests.
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(0));
+
+ // Sorts out all the messages we saw by request.
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+ ASSERT_EQ(1U, msgs.size());
+
+ ResourceResponseHead response_head;
+ GetResponseHead(msgs[0], &response_head);
+ ASSERT_EQ("", response_head.mime_type);
+}
+
+TEST_F(ResourceDispatcherHostTest, MimeSniff204) {
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(0));
+
+ std::string response("HTTP/1.1 204 No Content\n\n");
+ std::string raw_headers(net::HttpUtil::AssembleRawHeaders(response.data(),
+ response.size()));
+ std::string response_data;
+ SetResponse(raw_headers, response_data);
+
+ HandleScheme("http");
+ MakeTestRequest(0, 1, GURL("http:bla"));
+
+ // Flush all pending requests.
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(0));
+
+ // Sorts out all the messages we saw by request.
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+ ASSERT_EQ(1U, msgs.size());
+
+ ResourceResponseHead response_head;
+ GetResponseHead(msgs[0], &response_head);
+ ASSERT_EQ("text/plain", response_head.mime_type);
+}
+
+// Tests for crbug.com/31266 (Non-2xx + application/octet-stream).
+TEST_F(ResourceDispatcherHostTest, ForbiddenDownload) {
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(0));
+
+ std::string response("HTTP/1.1 403 Forbidden\n"
+ "Content-disposition: attachment; filename=blah\n"
+ "Content-type: application/octet-stream\n\n");
+ std::string raw_headers(net::HttpUtil::AssembleRawHeaders(response.data(),
+ response.size()));
+ std::string response_data("<html><title>Test One</title></html>");
+ SetResponse(raw_headers, response_data);
+
+ // Only MAIN_FRAMEs can trigger a download.
+ SetResourceType(ResourceType::MAIN_FRAME);
+
+ HandleScheme("http");
+ MakeTestRequest(0, 1, GURL("http:bla"));
+
+ // Flush all pending requests.
+ while (net::URLRequestTestJob::ProcessOnePendingMessage()) {}
+
+ EXPECT_EQ(0, host_.GetOutstandingRequestsMemoryCost(0));
+
+ // Sorts out all the messages we saw by request.
+ ResourceIPCAccumulator::ClassifiedMessages msgs;
+ accum_.GetClassifiedMessages(&msgs);
+
+ // We should have gotten one RequestComplete message.
+ ASSERT_EQ(1U, msgs[0].size());
+ EXPECT_EQ(ViewMsg_Resource_RequestComplete::ID, msgs[0][0].type());
+
+ // The RequestComplete message should have had status
+ // (CANCELED, ERR_FILE_NOT_FOUND).
+ int request_id;
+ net::URLRequestStatus status;
+
+ void* iter = NULL;
+ EXPECT_TRUE(IPC::ReadParam(&msgs[0][0], &iter, &request_id));
+ EXPECT_TRUE(IPC::ReadParam(&msgs[0][0], &iter, &status));
+
+ EXPECT_EQ(1, request_id);
+ EXPECT_EQ(net::URLRequestStatus::CANCELED, status.status());
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, status.os_error());
+}
+
+class DummyResourceHandler : public ResourceHandler {
+ public:
+ DummyResourceHandler() {}
+
+ // Called as upload progress is made.
+ bool OnUploadProgress(int request_id, uint64 position, uint64 size) {
+ return true;
+ }
+
+ bool OnRequestRedirected(int request_id, const GURL& url,
+ ResourceResponse* response, bool* defer) {
+ return true;
+ }
+
+ bool OnResponseStarted(int request_id, ResourceResponse* response) {
+ return true;
+ }
+
+ bool OnWillStart(int request_id, const GURL& url, bool* defer) {
+ return true;
+ }
+
+ bool OnWillRead(
+ int request_id, net::IOBuffer** buf, int* buf_size, int min_size) {
+ return true;
+ }
+
+ bool OnReadCompleted(int request_id, int* bytes_read) { return true; }
+
+ bool OnResponseCompleted(
+ int request_id,
+ const net::URLRequestStatus& status,
+ const std::string& info) {
+ return true;
+ }
+
+ void OnRequestClosed() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DummyResourceHandler);
+};
+
+class ApplyExtensionLocalizationFilterTest : public testing::Test {
+ protected:
+ void SetUp() {
+ url_.reset(new GURL(
+ "chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/popup.html"));
+ resource_type_ = ResourceType::STYLESHEET;
+ resource_handler_.reset(new DummyResourceHandler());
+ request_info_.reset(CreateNewResourceRequestInfo());
+ }
+
+ ResourceDispatcherHostRequestInfo* CreateNewResourceRequestInfo() {
+ return new ResourceDispatcherHostRequestInfo(
+ resource_handler_.get(), ChildProcessInfo::RENDER_PROCESS, 0, 0, 0,
+ ResourceType::STYLESHEET, 0U, false, false, false, -1, -1);
+ }
+
+ scoped_ptr<GURL> url_;
+ ResourceType::Type resource_type_;
+ scoped_ptr<DummyResourceHandler> resource_handler_;
+ scoped_ptr<ResourceDispatcherHostRequestInfo> request_info_;
+};
+
+TEST_F(ApplyExtensionLocalizationFilterTest, WrongScheme) {
+ url_.reset(new GURL("html://behllobkkfkfnphdnhnkndlbkcpglgmj/popup.html"));
+ ResourceDispatcherHost::ApplyExtensionLocalizationFilter(*url_,
+ resource_type_, request_info_.get());
+
+ EXPECT_FALSE(request_info_->replace_extension_localization_templates());
+}
+
+TEST_F(ApplyExtensionLocalizationFilterTest, GoodScheme) {
+ ResourceDispatcherHost::ApplyExtensionLocalizationFilter(*url_,
+ resource_type_, request_info_.get());
+
+ EXPECT_TRUE(request_info_->replace_extension_localization_templates());
+}
+
+TEST_F(ApplyExtensionLocalizationFilterTest, GoodSchemeWrongResourceType) {
+ resource_type_ = ResourceType::MAIN_FRAME;
+ ResourceDispatcherHost::ApplyExtensionLocalizationFilter(*url_,
+ resource_type_, request_info_.get());
+
+ EXPECT_FALSE(request_info_->replace_extension_localization_templates());
+}
diff --git a/content/browser/renderer_host/resource_handler.h b/content/browser/renderer_host/resource_handler.h
new file mode 100644
index 0000000..c4de36e
--- /dev/null
+++ b/content/browser/renderer_host/resource_handler.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is the browser side of the resource dispatcher, it receives requests
+// from the RenderProcessHosts, and dispatches them to URLRequests. It then
+// fowards the messages from the URLRequests back to the correct process for
+// handling.
+//
+// See http://dev.chromium.org/developers/design-documents/multi-process-resource-loading
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_RESOURCE_HANDLER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_RESOURCE_HANDLER_H_
+#pragma once
+
+#include <string>
+
+#include "chrome/browser/browser_thread.h"
+
+namespace net {
+class IOBuffer;
+class URLRequestStatus;
+} // namespace net
+
+struct ResourceResponse;
+class GURL;
+
+// The resource dispatcher host uses this interface to push load events to the
+// renderer, allowing for differences in the types of IPC messages generated.
+// See the implementations of this interface defined below.
+class ResourceHandler
+ : public base::RefCountedThreadSafe<
+ ResourceHandler, BrowserThread::DeleteOnIOThread> {
+ public:
+ // Called as upload progress is made.
+ virtual bool OnUploadProgress(int request_id,
+ uint64 position,
+ uint64 size) = 0;
+
+ // The request was redirected to a new URL. |*defer| has an initial value of
+ // false. Set |*defer| to true to defer the redirect. The redirect may be
+ // followed later on via ResourceDispatcherHost::FollowDeferredRedirect.
+ virtual bool OnRequestRedirected(int request_id, const GURL& url,
+ ResourceResponse* response,
+ bool* defer) = 0;
+
+ // Response headers and meta data are available.
+ virtual bool OnResponseStarted(int request_id,
+ ResourceResponse* response) = 0;
+
+ // Called before the net::URLRequest for |request_id| (whose url is |url|) is
+ // to be started. If the handler returns false, then the request is cancelled.
+ // Otherwise if the return value is true, the ResourceHandler can delay the
+ // request from starting by setting |*defer = true|. A deferred request will
+ // not have called net::URLRequest::Start(), and will not resume until someone
+ // calls ResourceDispatcherHost::StartDeferredRequest().
+ virtual bool OnWillStart(int request_id, const GURL& url, bool* defer) = 0;
+
+ // Data will be read for the response. Upon success, this method places the
+ // size and address of the buffer where the data is to be written in its
+ // out-params. This call will be followed by either OnReadCompleted or
+ // OnResponseCompleted, at which point the buffer may be recycled.
+ virtual bool OnWillRead(int request_id,
+ net::IOBuffer** buf,
+ int* buf_size,
+ int min_size) = 0;
+
+ // Data (*bytes_read bytes) was written into the buffer provided by
+ // OnWillRead. A return value of false cancels the request, true continues
+ // reading data.
+ virtual bool OnReadCompleted(int request_id, int* bytes_read) = 0;
+
+ // The response is complete. The final response status is given.
+ // Returns false if the handler is deferring the call to a later time.
+ virtual bool OnResponseCompleted(int request_id,
+ const net::URLRequestStatus& status,
+ const std::string& security_info) = 0;
+
+ // Signals that the request is closed (i.e. finished successfully, cancelled).
+ // This is a signal that the associated net::URLRequest isn't valid anymore.
+ virtual void OnRequestClosed() = 0;
+
+ // This notification is synthesized by the RedirectToFileResourceHandler
+ // to indicate progress of 'download_to_file' requests. OnReadCompleted
+ // calls are consumed by the RedirectToFileResourceHandler and replaced
+ // with OnDataDownloaded calls.
+ virtual void OnDataDownloaded(int request_id, int bytes_downloaded) {}
+
+ protected:
+ friend class BrowserThread;
+ friend class DeleteTask<ResourceHandler>;
+
+ virtual ~ResourceHandler() {}
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_RESOURCE_HANDLER_H_
diff --git a/content/browser/renderer_host/resource_message_filter.cc b/content/browser/renderer_host/resource_message_filter.cc
new file mode 100644
index 0000000..7b3229b
--- /dev/null
+++ b/content/browser/renderer_host/resource_message_filter.cc
@@ -0,0 +1,52 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/resource_message_filter.h"
+
+#include "chrome/browser/net/chrome_url_request_context.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/render_messages.h"
+#include "content/browser/renderer_host/resource_dispatcher_host.h"
+
+ResourceMessageFilter::ResourceMessageFilter(
+ int child_id,
+ ChildProcessInfo::ProcessType process_type,
+ ResourceDispatcherHost* resource_dispatcher_host)
+ : child_id_(child_id),
+ process_type_(process_type),
+ resource_dispatcher_host_(resource_dispatcher_host) {
+}
+
+ResourceMessageFilter::~ResourceMessageFilter() {
+}
+
+void ResourceMessageFilter::OnChannelClosing() {
+ BrowserMessageFilter::OnChannelClosing();
+
+ // Unhook us from all pending network requests so they don't get sent to a
+ // deleted object.
+ resource_dispatcher_host_->CancelRequestsForProcess(child_id_);
+}
+
+bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok) {
+ return resource_dispatcher_host_->OnMessageReceived(
+ message, this, message_was_ok);
+}
+
+ChromeURLRequestContext* ResourceMessageFilter::GetURLRequestContext(
+ const ViewHostMsg_Resource_Request& resource_request) {
+ net::URLRequestContext* rv = NULL;
+ if (url_request_context_override_.get())
+ rv = url_request_context_override_->GetRequestContext(resource_request);
+
+ if (!rv) {
+ URLRequestContextGetter* context_getter =
+ Profile::GetDefaultRequestContext();
+ if (context_getter)
+ rv = context_getter->GetURLRequestContext();
+ }
+
+ return static_cast<ChromeURLRequestContext*>(rv);
+}
diff --git a/content/browser/renderer_host/resource_message_filter.h b/content/browser/renderer_host/resource_message_filter.h
new file mode 100644
index 0000000..7e8fb93
--- /dev/null
+++ b/content/browser/renderer_host/resource_message_filter.h
@@ -0,0 +1,81 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_RESOURCE_MESSAGE_FILTER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_RESOURCE_MESSAGE_FILTER_H_
+
+#include "base/scoped_ptr.h"
+#include "chrome/browser/browser_message_filter.h"
+#include "chrome/common/child_process_info.h"
+
+class ChromeURLRequestContext;
+class ResourceDispatcherHost;
+struct ViewHostMsg_Resource_Request;
+
+namespace net {
+class URLRequestContext;
+} // namespace net
+
+// This class filters out incoming IPC messages for network requests and
+// processes them on the IPC thread. As a result, network requests are not
+// delayed by costly UI processing that may be occuring on the main thread of
+// the browser. It also means that any hangs in starting a network request
+// will not interfere with browser UI.
+class ResourceMessageFilter : public BrowserMessageFilter {
+ public:
+ // Allows overriding the net::URLRequestContext used to service requests.
+ class URLRequestContextOverride
+ : public base::RefCountedThreadSafe<URLRequestContextOverride> {
+ public:
+ URLRequestContextOverride() {}
+
+ virtual net::URLRequestContext* GetRequestContext(
+ const ViewHostMsg_Resource_Request& resource_request) = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<URLRequestContextOverride>;
+ virtual ~URLRequestContextOverride() {}
+
+ DISALLOW_COPY_AND_ASSIGN(URLRequestContextOverride);
+ };
+
+ ResourceMessageFilter(int child_id,
+ ChildProcessInfo::ProcessType process_type,
+ ResourceDispatcherHost* resource_dispatcher_host);
+
+ // BrowserMessageFilter implementation.
+ virtual void OnChannelClosing();
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok);
+
+ // Returns the net::URLRequestContext for the given request.
+ ChromeURLRequestContext* GetURLRequestContext(
+ const ViewHostMsg_Resource_Request& resource_request);
+
+ void set_url_request_context_override(URLRequestContextOverride* u) {
+ url_request_context_override_ = u;
+ }
+
+ int child_id() const { return child_id_; }
+ ChildProcessInfo::ProcessType process_type() const { return process_type_; }
+
+ protected:
+ // Protected destructor so that we can be overriden in tests.
+ virtual ~ResourceMessageFilter();
+
+ private:
+ // The ID of the child process.
+ int child_id_;
+
+ ChildProcessInfo::ProcessType process_type_;
+
+ // Owned by BrowserProcess, which is guaranteed to outlive us.
+ ResourceDispatcherHost* resource_dispatcher_host_;
+
+ scoped_refptr<URLRequestContextOverride> url_request_context_override_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ResourceMessageFilter);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_RESOURCE_MESSAGE_FILTER_H_
diff --git a/content/browser/renderer_host/resource_queue.cc b/content/browser/renderer_host/resource_queue.cc
new file mode 100644
index 0000000..ef58791
--- /dev/null
+++ b/content/browser/renderer_host/resource_queue.cc
@@ -0,0 +1,93 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/resource_queue.h"
+
+#include "base/stl_util-inl.h"
+#include "chrome/browser/browser_thread.h"
+#include "content/browser/renderer_host/global_request_id.h"
+#include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
+
+ResourceQueueDelegate::~ResourceQueueDelegate() {
+}
+
+ResourceQueue::ResourceQueue() : shutdown_(false) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+}
+
+ResourceQueue::~ResourceQueue() {
+ DCHECK(shutdown_);
+}
+
+void ResourceQueue::Initialize(const DelegateSet& delegates) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(delegates_.empty());
+ delegates_ = delegates;
+}
+
+void ResourceQueue::Shutdown() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ shutdown_ = true;
+ for (DelegateSet::iterator i = delegates_.begin();
+ i != delegates_.end(); ++i) {
+ (*i)->WillShutdownResourceQueue();
+ }
+}
+
+void ResourceQueue::AddRequest(
+ net::URLRequest* request,
+ const ResourceDispatcherHostRequestInfo& request_info) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(!shutdown_);
+
+ GlobalRequestID request_id(request_info.child_id(),
+ request_info.request_id());
+
+ DCHECK(!ContainsKey(requests_, request_id))
+ << "child_id:" << request_info.child_id()
+ << ", request_id:" << request_info.request_id();
+ requests_[request_id] = request;
+
+ DelegateSet interested_delegates;
+
+ for (DelegateSet::iterator i = delegates_.begin();
+ i != delegates_.end(); ++i) {
+ if ((*i)->ShouldDelayRequest(request, request_info, request_id))
+ interested_delegates.insert(*i);
+ }
+
+ if (interested_delegates.empty()) {
+ request->Start();
+ return;
+ }
+
+ DCHECK(!ContainsKey(interested_delegates_, request_id));
+ interested_delegates_[request_id] = interested_delegates;
+}
+
+void ResourceQueue::RemoveRequest(const GlobalRequestID& request_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ requests_.erase(request_id);
+}
+
+void ResourceQueue::StartDelayedRequest(ResourceQueueDelegate* delegate,
+ const GlobalRequestID& request_id) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ DCHECK(!shutdown_);
+
+ DCHECK(ContainsKey(interested_delegates_, request_id));
+ DCHECK(ContainsKey(interested_delegates_[request_id], delegate));
+ interested_delegates_[request_id].erase(delegate);
+ if (interested_delegates_[request_id].empty()) {
+ interested_delegates_.erase(request_id);
+
+ if (ContainsKey(requests_, request_id)) {
+ net::URLRequest* request = requests_[request_id];
+ // The request shouldn't have started (SUCCESS is the initial state).
+ DCHECK_EQ(net::URLRequestStatus::SUCCESS, request->status().status());
+ request->Start();
+ }
+ }
+}
diff --git a/content/browser/renderer_host/resource_queue.h b/content/browser/renderer_host/resource_queue.h
new file mode 100644
index 0000000..4bf45d9
--- /dev/null
+++ b/content/browser/renderer_host/resource_queue.h
@@ -0,0 +1,100 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_RESOURCE_QUEUE_H_
+#define CONTENT_BROWSER_RENDERER_HOST_RESOURCE_QUEUE_H_
+#pragma once
+
+#include <map>
+#include <set>
+
+#include "base/basictypes.h"
+
+namespace net {
+class URLRequest;
+} // namespace net
+
+class ResourceDispatcherHostRequestInfo;
+struct GlobalRequestID;
+
+// Makes decisions about delaying or not each net::URLRequest in the queue.
+// All methods are called on the IO thread.
+class ResourceQueueDelegate {
+ public:
+ // Should return true if it wants the |request| to not be started at this
+ // point. To start the delayed request, ResourceQueue::StartDelayedRequest
+ // should be used.
+ virtual bool ShouldDelayRequest(
+ net::URLRequest* request,
+ const ResourceDispatcherHostRequestInfo& request_info,
+ const GlobalRequestID& request_id) = 0;
+
+ // Called just before ResourceQueue shutdown. After that, the delegate
+ // should not use the ResourceQueue.
+ virtual void WillShutdownResourceQueue() = 0;
+
+ protected:
+ virtual ~ResourceQueueDelegate();
+};
+
+// Makes it easy to delay starting URL requests until specified conditions are
+// met.
+class ResourceQueue {
+ public:
+ typedef std::set<ResourceQueueDelegate*> DelegateSet;
+
+ // UI THREAD ONLY ------------------------------------------------------------
+
+ // Construct the queue. You must initialize it using Initialize.
+ ResourceQueue();
+ ~ResourceQueue();
+
+ // Initialize the queue with set of delegates it should ask for each incoming
+ // request.
+ void Initialize(const DelegateSet& delegates);
+
+ // IO THREAD ONLY ------------------------------------------------------------
+
+ // Must be called before destroying the queue. No other methods can be called
+ // after that.
+ void Shutdown();
+
+ // Takes care to start the |request| after all delegates allow that. If no
+ // delegate demands delaying the request it will be started immediately.
+ void AddRequest(net::URLRequest* request,
+ const ResourceDispatcherHostRequestInfo& request_info);
+
+ // Tells the queue that the net::URLRequest object associated with
+ // |request_id| is no longer valid.
+ void RemoveRequest(const GlobalRequestID& request_id);
+
+ // A delegate should call StartDelayedRequest when it wants to allow the
+ // request to start. If it was the last delegate that demanded the request
+ // to be delayed, the request will be started.
+ void StartDelayedRequest(ResourceQueueDelegate* delegate,
+ const GlobalRequestID& request_id);
+
+ private:
+ typedef std::map<GlobalRequestID, net::URLRequest*> RequestMap;
+ typedef std::map<GlobalRequestID, DelegateSet> InterestedDelegatesMap;
+
+ // The registered delegates. Will not change after the queue has been
+ // initialized.
+ DelegateSet delegates_;
+
+ // Stores net::URLRequest objects associated with each GlobalRequestID. This
+ // helps decoupling the queue from ResourceDispatcherHost.
+ RequestMap requests_;
+
+ // Maps a GlobalRequestID to the set of delegates that want to prevent the
+ // associated request from starting yet.
+ InterestedDelegatesMap interested_delegates_;
+
+ // True when we are shutting down.
+ bool shutdown_;
+
+ DISALLOW_COPY_AND_ASSIGN(ResourceQueue);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_RESOURCE_QUEUE_H_
diff --git a/content/browser/renderer_host/resource_queue_unittest.cc b/content/browser/renderer_host/resource_queue_unittest.cc
new file mode 100644
index 0000000..7041bed
--- /dev/null
+++ b/content/browser/renderer_host/resource_queue_unittest.cc
@@ -0,0 +1,289 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/message_loop.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/browser_thread.h"
+#include "content/browser/renderer_host/global_request_id.h"
+#include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
+#include "content/browser/renderer_host/resource_handler.h"
+#include "content/browser/renderer_host/resource_queue.h"
+#include "googleurl/src/gurl.h"
+#include "net/url_request/url_request.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const char kTestUrl[] = "data:text/plain,Hello World!";
+
+class DummyResourceHandler : public ResourceHandler {
+ public:
+ DummyResourceHandler() {
+ }
+
+ bool OnUploadProgress(int request_id, uint64 position, uint64 size) {
+ NOTREACHED();
+ return true;
+ }
+
+ virtual bool OnRequestRedirected(int request_id, const GURL& url,
+ ResourceResponse* response,
+ bool* defer) {
+ NOTREACHED();
+ return true;
+ }
+
+ virtual bool OnResponseStarted(int request_id,
+ ResourceResponse* response) {
+ NOTREACHED();
+ return true;
+ }
+
+ virtual bool OnWillStart(int request_id, const GURL& url, bool* defer) {
+ NOTREACHED();
+ return true;
+ }
+
+ virtual bool OnWillRead(int request_id,
+ net::IOBuffer** buf,
+ int* buf_size,
+ int min_size) {
+ NOTREACHED();
+ return true;
+ }
+
+ virtual bool OnReadCompleted(int request_id, int* bytes_read) {
+ NOTREACHED();
+ return true;
+ }
+
+ virtual bool OnResponseCompleted(int request_id,
+ const net::URLRequestStatus& status,
+ const std::string& security_info) {
+ NOTREACHED();
+ return true;
+ }
+
+ virtual void OnRequestClosed() {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DummyResourceHandler);
+};
+
+ResourceDispatcherHostRequestInfo* GetRequestInfo(int request_id) {
+ return new ResourceDispatcherHostRequestInfo(
+ new DummyResourceHandler(), ChildProcessInfo::RENDER_PROCESS, 0, 0,
+ request_id, ResourceType::MAIN_FRAME, 0, false, false, false, -1, -1);
+}
+
+void InitializeQueue(ResourceQueue* queue, ResourceQueueDelegate* delegate) {
+ ResourceQueue::DelegateSet delegate_set;
+ delegate_set.insert(delegate);
+ queue->Initialize(delegate_set);
+}
+
+void InitializeQueue(ResourceQueue* queue,
+ ResourceQueueDelegate* delegate1,
+ ResourceQueueDelegate* delegate2) {
+ ResourceQueue::DelegateSet delegate_set;
+ delegate_set.insert(delegate1);
+ delegate_set.insert(delegate2);
+ queue->Initialize(delegate_set);
+}
+
+class NeverDelayingDelegate : public ResourceQueueDelegate {
+ public:
+ NeverDelayingDelegate() {
+ }
+
+ virtual bool ShouldDelayRequest(
+ net::URLRequest* request,
+ const ResourceDispatcherHostRequestInfo& request_info,
+ const GlobalRequestID& request_id) {
+ return false;
+ }
+
+ virtual void WillShutdownResourceQueue() {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NeverDelayingDelegate);
+};
+
+class AlwaysDelayingDelegate : public ResourceQueueDelegate {
+ public:
+ explicit AlwaysDelayingDelegate(ResourceQueue* resource_queue)
+ : resource_queue_(resource_queue) {
+ }
+
+ virtual bool ShouldDelayRequest(
+ net::URLRequest* request,
+ const ResourceDispatcherHostRequestInfo& request_info,
+ const GlobalRequestID& request_id) {
+ delayed_requests_.push_back(request_id);
+ return true;
+ }
+
+ virtual void WillShutdownResourceQueue() {
+ resource_queue_ = NULL;
+ }
+
+ void StartDelayedRequests() {
+ if (!resource_queue_)
+ return;
+
+ for (RequestList::iterator i = delayed_requests_.begin();
+ i != delayed_requests_.end(); ++i) {
+ resource_queue_->StartDelayedRequest(this, *i);
+ }
+ }
+
+ private:
+ typedef std::vector<GlobalRequestID> RequestList;
+
+ ResourceQueue* resource_queue_;
+
+ RequestList delayed_requests_;
+
+ DISALLOW_COPY_AND_ASSIGN(AlwaysDelayingDelegate);
+};
+
+class ResourceQueueTest : public testing::Test,
+ public net::URLRequest::Delegate {
+ public:
+ ResourceQueueTest()
+ : response_started_count_(0),
+ message_loop_(MessageLoop::TYPE_IO),
+ ui_thread_(BrowserThread::UI, &message_loop_),
+ io_thread_(BrowserThread::IO, &message_loop_) {
+ }
+
+ virtual void OnResponseStarted(net::URLRequest* request) {
+ response_started_count_++;
+ // We're not going to do anything more with the request. Cancel it now
+ // to avoid leaking net::URLRequestJob.
+ request->Cancel();
+ }
+
+ virtual void OnReadCompleted(net::URLRequest* request, int bytes_read) {
+ }
+
+ protected:
+ int response_started_count_;
+
+ private:
+ MessageLoop message_loop_;
+ BrowserThread ui_thread_;
+ BrowserThread io_thread_;
+};
+
+TEST_F(ResourceQueueTest, Basic) {
+ // Test the simplest lifycycle of ResourceQueue.
+ ResourceQueue queue;
+ queue.Initialize(ResourceQueue::DelegateSet());
+ queue.Shutdown();
+}
+
+TEST_F(ResourceQueueTest, NeverDelayingDelegate) {
+ ResourceQueue queue;
+
+ NeverDelayingDelegate delegate;
+ InitializeQueue(&queue, &delegate);
+
+ net::URLRequest request(GURL(kTestUrl), this);
+ scoped_ptr<ResourceDispatcherHostRequestInfo> request_info(GetRequestInfo(0));
+ EXPECT_EQ(0, response_started_count_);
+ queue.AddRequest(&request, *request_info.get());
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(1, response_started_count_);
+
+ queue.Shutdown();
+}
+
+TEST_F(ResourceQueueTest, AlwaysDelayingDelegate) {
+ ResourceQueue queue;
+
+ AlwaysDelayingDelegate delegate(&queue);
+ InitializeQueue(&queue, &delegate);
+
+ net::URLRequest request(GURL(kTestUrl), this);
+ scoped_ptr<ResourceDispatcherHostRequestInfo> request_info(GetRequestInfo(0));
+ EXPECT_EQ(0, response_started_count_);
+ queue.AddRequest(&request, *request_info.get());
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(0, response_started_count_);
+ delegate.StartDelayedRequests();
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(1, response_started_count_);
+
+ queue.Shutdown();
+}
+
+TEST_F(ResourceQueueTest, AlwaysDelayingDelegateAfterShutdown) {
+ ResourceQueue queue;
+
+ AlwaysDelayingDelegate delegate(&queue);
+ InitializeQueue(&queue, &delegate);
+
+ net::URLRequest request(GURL(kTestUrl), this);
+ scoped_ptr<ResourceDispatcherHostRequestInfo> request_info(GetRequestInfo(0));
+ EXPECT_EQ(0, response_started_count_);
+ queue.AddRequest(&request, *request_info.get());
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(0, response_started_count_);
+
+ queue.Shutdown();
+
+ delegate.StartDelayedRequests();
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(0, response_started_count_);
+}
+
+TEST_F(ResourceQueueTest, TwoDelegates) {
+ ResourceQueue queue;
+
+ AlwaysDelayingDelegate always_delaying_delegate(&queue);
+ NeverDelayingDelegate never_delaying_delegate;
+ InitializeQueue(&queue, &always_delaying_delegate, &never_delaying_delegate);
+
+ net::URLRequest request(GURL(kTestUrl), this);
+ scoped_ptr<ResourceDispatcherHostRequestInfo> request_info(GetRequestInfo(0));
+ EXPECT_EQ(0, response_started_count_);
+ queue.AddRequest(&request, *request_info.get());
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(0, response_started_count_);
+ always_delaying_delegate.StartDelayedRequests();
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(1, response_started_count_);
+
+ queue.Shutdown();
+}
+
+TEST_F(ResourceQueueTest, RemoveRequest) {
+ ResourceQueue queue;
+
+ AlwaysDelayingDelegate delegate(&queue);
+ InitializeQueue(&queue, &delegate);
+
+ net::URLRequest request(GURL(kTestUrl), this);
+ scoped_ptr<ResourceDispatcherHostRequestInfo> request_info(GetRequestInfo(0));
+ GlobalRequestID request_id(request_info->child_id(),
+ request_info->request_id());
+ EXPECT_EQ(0, response_started_count_);
+ queue.AddRequest(&request, *request_info.get());
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(0, response_started_count_);
+ queue.RemoveRequest(request_id);
+ delegate.StartDelayedRequests();
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(0, response_started_count_);
+
+ queue.Shutdown();
+
+ MessageLoop::current()->RunAllPending();
+ EXPECT_EQ(0, response_started_count_);
+}
+
+} // namespace
diff --git a/content/browser/renderer_host/resource_request_details.cc b/content/browser/renderer_host/resource_request_details.cc
new file mode 100644
index 0000000..5570181
--- /dev/null
+++ b/content/browser/renderer_host/resource_request_details.cc
@@ -0,0 +1,50 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/resource_request_details.h"
+
+#include "content/browser/renderer_host/resource_dispatcher_host.h"
+#include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
+#include "chrome/browser/worker_host/worker_service.h"
+
+ResourceRequestDetails::ResourceRequestDetails(const net::URLRequest* request,
+ int cert_id)
+ : url_(request->url()),
+ original_url_(request->original_url()),
+ method_(request->method()),
+ referrer_(request->referrer()),
+ has_upload_(request->has_upload()),
+ load_flags_(request->load_flags()),
+ status_(request->status()),
+ ssl_cert_id_(cert_id),
+ ssl_cert_status_(request->ssl_info().cert_status) {
+ const ResourceDispatcherHostRequestInfo* info =
+ ResourceDispatcherHost::InfoForRequest(request);
+ DCHECK(info);
+ resource_type_ = info->resource_type();
+
+ // If request is from the worker process on behalf of a renderer, use
+ // the renderer process id, since it consumes the notification response
+ // such as ssl state etc.
+ // TODO(atwilson): need to notify all associated renderers in the case
+ // of ssl state change (http://crbug.com/25357). For now, just notify
+ // the first one (works for dedicated workers and shared workers with
+ // a single process).
+ int temp;
+ if (!WorkerService::GetInstance()->GetRendererForWorker(
+ info->child_id(), &origin_child_id_, &temp)) {
+ origin_child_id_ = info->child_id();
+ }
+}
+
+ResourceRequestDetails::~ResourceRequestDetails() {}
+
+ResourceRedirectDetails::ResourceRedirectDetails(const net::URLRequest* request,
+ int cert_id,
+ const GURL& new_url)
+ : ResourceRequestDetails(request, cert_id),
+ new_url_(new_url) {
+}
+
+ResourceRedirectDetails::~ResourceRedirectDetails() {}
diff --git a/content/browser/renderer_host/resource_request_details.h b/content/browser/renderer_host/resource_request_details.h
new file mode 100644
index 0000000..06c915f
--- /dev/null
+++ b/content/browser/renderer_host/resource_request_details.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The ResourceRequestDetails object contains additional details about a
+// resource request. It copies many of the publicly accessible member variables
+// of net::URLRequest, but exists on the UI thread.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_RESOURCE_REQUEST_DETAILS_H_
+#define CONTENT_BROWSER_RENDERER_HOST_RESOURCE_REQUEST_DETAILS_H_
+#pragma once
+
+#include <string>
+
+#include "googleurl/src/gurl.h"
+#include "net/url_request/url_request_status.h"
+#include "webkit/glue/resource_type.h"
+
+namespace net {
+class URLRequest;
+} // namespace net
+
+// Details about a resource request notification.
+class ResourceRequestDetails {
+ public:
+ ResourceRequestDetails(const net::URLRequest* request, int cert_id);
+
+ virtual ~ResourceRequestDetails();
+
+ const GURL& url() const { return url_; }
+ const GURL& original_url() const { return original_url_; }
+ const std::string& method() const { return method_; }
+ const std::string& referrer() const { return referrer_; }
+ bool has_upload() const { return has_upload_; }
+ int load_flags() const { return load_flags_; }
+ int origin_child_id() const { return origin_child_id_; }
+ const net::URLRequestStatus& status() const { return status_; }
+ int ssl_cert_id() const { return ssl_cert_id_; }
+ int ssl_cert_status() const { return ssl_cert_status_; }
+ ResourceType::Type resource_type() const { return resource_type_; }
+
+ private:
+ GURL url_;
+ GURL original_url_;
+ std::string method_;
+ std::string referrer_;
+ bool has_upload_;
+ int load_flags_;
+ int origin_child_id_;
+ net::URLRequestStatus status_;
+ int ssl_cert_id_;
+ int ssl_cert_status_;
+ ResourceType::Type resource_type_;
+};
+
+// Details about a redirection of a resource request.
+class ResourceRedirectDetails : public ResourceRequestDetails {
+ public:
+ ResourceRedirectDetails(const net::URLRequest* request,
+ int cert_id,
+ const GURL& new_url);
+ virtual ~ResourceRedirectDetails();
+
+ // The URL to which we are being redirected.
+ const GURL& new_url() const { return new_url_; }
+
+ private:
+ GURL new_url_;
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_RESOURCE_REQUEST_DETAILS_H_
diff --git a/content/browser/renderer_host/socket_stream_dispatcher_host.cc b/content/browser/renderer_host/socket_stream_dispatcher_host.cc
new file mode 100644
index 0000000..4b88ff0
--- /dev/null
+++ b/content/browser/renderer_host/socket_stream_dispatcher_host.cc
@@ -0,0 +1,164 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/socket_stream_dispatcher_host.h"
+
+#include "base/logging.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/render_messages.h"
+#include "chrome/common/render_messages_params.h"
+#include "chrome/common/net/socket_stream.h"
+#include "chrome/common/net/url_request_context_getter.h"
+#include "content/browser/renderer_host/socket_stream_host.h"
+#include "net/websockets/websocket_job.h"
+#include "net/websockets/websocket_throttle.h"
+
+SocketStreamDispatcherHost::SocketStreamDispatcherHost() {
+ net::WebSocketJob::EnsureInit();
+}
+
+SocketStreamDispatcherHost::~SocketStreamDispatcherHost() {
+ // TODO(ukai): Implement IDMap::RemoveAll().
+ for (IDMap<SocketStreamHost>::const_iterator iter(&hosts_);
+ !iter.IsAtEnd();
+ iter.Advance()) {
+ int socket_id = iter.GetCurrentKey();
+ const SocketStreamHost* socket_stream_host = iter.GetCurrentValue();
+ delete socket_stream_host;
+ hosts_.Remove(socket_id);
+ }
+}
+
+bool SocketStreamDispatcherHost::OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP_EX(SocketStreamDispatcherHost, message, *message_was_ok)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_SocketStream_Connect, OnConnect)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_SocketStream_SendData, OnSendData)
+ IPC_MESSAGE_HANDLER(ViewHostMsg_SocketStream_Close, OnCloseReq)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP_EX()
+ return handled;
+}
+
+// SocketStream::Delegate methods implementations.
+void SocketStreamDispatcherHost::OnConnected(net::SocketStream* socket,
+ int max_pending_send_allowed) {
+ int socket_id = SocketStreamHost::SocketIdFromSocketStream(socket);
+ DVLOG(1) << "SocketStreamDispatcherHost::OnConnected socket_id=" << socket_id
+ << " max_pending_send_allowed=" << max_pending_send_allowed;
+ if (socket_id == chrome_common_net::kNoSocketId) {
+ LOG(ERROR) << "NoSocketId in OnConnected";
+ return;
+ }
+ if (!Send(new ViewMsg_SocketStream_Connected(
+ socket_id, max_pending_send_allowed))) {
+ LOG(ERROR) << "ViewMsg_SocketStream_Connected failed.";
+ DeleteSocketStreamHost(socket_id);
+ }
+}
+
+void SocketStreamDispatcherHost::OnSentData(net::SocketStream* socket,
+ int amount_sent) {
+ int socket_id = SocketStreamHost::SocketIdFromSocketStream(socket);
+ DVLOG(1) << "SocketStreamDispatcherHost::OnSentData socket_id=" << socket_id
+ << " amount_sent=" << amount_sent;
+ if (socket_id == chrome_common_net::kNoSocketId) {
+ LOG(ERROR) << "NoSocketId in OnReceivedData";
+ return;
+ }
+ if (!Send(new ViewMsg_SocketStream_SentData(socket_id, amount_sent))) {
+ LOG(ERROR) << "ViewMsg_SocketStream_SentData failed.";
+ DeleteSocketStreamHost(socket_id);
+ }
+}
+
+void SocketStreamDispatcherHost::OnReceivedData(
+ net::SocketStream* socket, const char* data, int len) {
+ int socket_id = SocketStreamHost::SocketIdFromSocketStream(socket);
+ DVLOG(1) << "SocketStreamDispatcherHost::OnReceiveData socket_id="
+ << socket_id;
+ if (socket_id == chrome_common_net::kNoSocketId) {
+ LOG(ERROR) << "NoSocketId in OnReceivedData";
+ return;
+ }
+ if (!Send(new ViewMsg_SocketStream_ReceivedData(
+ socket_id, std::vector<char>(data, data + len)))) {
+ LOG(ERROR) << "ViewMsg_SocketStream_ReceivedData failed.";
+ DeleteSocketStreamHost(socket_id);
+ }
+}
+
+void SocketStreamDispatcherHost::OnClose(net::SocketStream* socket) {
+ int socket_id = SocketStreamHost::SocketIdFromSocketStream(socket);
+ DVLOG(1) << "SocketStreamDispatcherHost::OnClosed socket_id=" << socket_id;
+ if (socket_id == chrome_common_net::kNoSocketId) {
+ LOG(ERROR) << "NoSocketId in OnClose";
+ return;
+ }
+ DeleteSocketStreamHost(socket_id);
+}
+
+// Message handlers called by OnMessageReceived.
+void SocketStreamDispatcherHost::OnConnect(const GURL& url, int socket_id) {
+ DVLOG(1) << "SocketStreamDispatcherHost::OnConnect url=" << url
+ << " socket_id=" << socket_id;
+ DCHECK_NE(chrome_common_net::kNoSocketId, socket_id);
+ if (hosts_.Lookup(socket_id)) {
+ LOG(ERROR) << "socket_id=" << socket_id << " already registered.";
+ return;
+ }
+ SocketStreamHost* socket_stream_host = new SocketStreamHost(this, socket_id);
+ hosts_.AddWithID(socket_stream_host, socket_id);
+ socket_stream_host->Connect(url, GetURLRequestContext());
+ DVLOG(1) << "SocketStreamDispatcherHost::OnConnect -> " << socket_id;
+}
+
+void SocketStreamDispatcherHost::OnSendData(
+ int socket_id, const std::vector<char>& data) {
+ DVLOG(1) << "SocketStreamDispatcherHost::OnSendData socket_id=" << socket_id;
+ SocketStreamHost* socket_stream_host = hosts_.Lookup(socket_id);
+ if (!socket_stream_host) {
+ LOG(ERROR) << "socket_id=" << socket_id << " already closed.";
+ return;
+ }
+ if (!socket_stream_host->SendData(data)) {
+ // Cannot accept more data to send.
+ socket_stream_host->Close();
+ }
+}
+
+void SocketStreamDispatcherHost::OnCloseReq(int socket_id) {
+ DVLOG(1) << "SocketStreamDispatcherHost::OnCloseReq socket_id=" << socket_id;
+ SocketStreamHost* socket_stream_host = hosts_.Lookup(socket_id);
+ if (!socket_stream_host)
+ return;
+ socket_stream_host->Close();
+}
+
+void SocketStreamDispatcherHost::DeleteSocketStreamHost(int socket_id) {
+ SocketStreamHost* socket_stream_host = hosts_.Lookup(socket_id);
+ DCHECK(socket_stream_host);
+ delete socket_stream_host;
+ hosts_.Remove(socket_id);
+ if (!Send(new ViewMsg_SocketStream_Closed(socket_id))) {
+ LOG(ERROR) << "ViewMsg_SocketStream_Closed failed.";
+ }
+}
+
+net::URLRequestContext* SocketStreamDispatcherHost::GetURLRequestContext() {
+ net::URLRequestContext* rv = NULL;
+ if (url_request_context_override_.get()) {
+ ViewHostMsg_Resource_Request request;
+ rv = url_request_context_override_->GetRequestContext(request);
+ }
+ if (!rv) {
+ URLRequestContextGetter* context_getter =
+ Profile::GetDefaultRequestContext();
+ if (context_getter)
+ rv = context_getter->GetURLRequestContext();
+ }
+
+ return rv;
+}
diff --git a/content/browser/renderer_host/socket_stream_dispatcher_host.h b/content/browser/renderer_host/socket_stream_dispatcher_host.h
new file mode 100644
index 0000000..0089e0c
--- /dev/null
+++ b/content/browser/renderer_host/socket_stream_dispatcher_host.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_SOCKET_STREAM_DISPATCHER_HOST_H_
+#define CONTENT_BROWSER_RENDERER_HOST_SOCKET_STREAM_DISPATCHER_HOST_H_
+#pragma once
+
+#include <vector>
+
+#include "base/id_map.h"
+#include "chrome/browser/browser_message_filter.h"
+#include "content/browser/renderer_host/resource_message_filter.h"
+#include "net/socket_stream/socket_stream.h"
+
+class GURL;
+class SocketStreamHost;
+
+// Dispatches ViewHostMsg_SocketStream_* messages sent from renderer.
+// It also acts as SocketStream::Delegate so that it sends
+// ViewMsg_SocketStream_* messages back to renderer.
+class SocketStreamDispatcherHost : public BrowserMessageFilter,
+ public net::SocketStream::Delegate {
+ public:
+ SocketStreamDispatcherHost();
+ virtual ~SocketStreamDispatcherHost();
+
+ // BrowserMessageFilter methods.
+ virtual bool OnMessageReceived(const IPC::Message& message,
+ bool* message_was_ok);
+
+ // The object died, so cancel and detach all requests associated with it.
+ void CancelRequestsForProcess(int host_id);
+
+ // SocketStream::Delegate methods.
+ virtual void OnConnected(net::SocketStream* socket,
+ int max_pending_send_allowed);
+ virtual void OnSentData(net::SocketStream* socket, int amount_sent);
+ virtual void OnReceivedData(net::SocketStream* socket,
+ const char* data, int len);
+ virtual void OnClose(net::SocketStream* socket);
+
+ void set_url_request_context_override(
+ ResourceMessageFilter::URLRequestContextOverride* u) {
+ url_request_context_override_ = u;
+ }
+
+ private:
+ // Message handlers called by OnMessageReceived.
+ void OnConnect(const GURL& url, int socket_id);
+ void OnSendData(int socket_id, const std::vector<char>& data);
+ void OnCloseReq(int socket_id);
+
+ void DeleteSocketStreamHost(int socket_id);
+
+ net::URLRequestContext* GetURLRequestContext();
+
+ IDMap<SocketStreamHost> hosts_;
+ scoped_refptr<ResourceMessageFilter::URLRequestContextOverride>
+ url_request_context_override_;
+
+ DISALLOW_COPY_AND_ASSIGN(SocketStreamDispatcherHost);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_SOCKET_STREAM_DISPATCHER_HOST_H_
diff --git a/content/browser/renderer_host/socket_stream_host.cc b/content/browser/renderer_host/socket_stream_host.cc
new file mode 100644
index 0000000..e98f7db
--- /dev/null
+++ b/content/browser/renderer_host/socket_stream_host.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/socket_stream_host.h"
+
+#include "base/logging.h"
+#include "chrome/common/net/socket_stream.h"
+#include "net/socket_stream/socket_stream_job.h"
+
+static const char* kSocketIdKey = "socketId";
+
+class SocketStreamId : public net::SocketStream::UserData {
+ public:
+ explicit SocketStreamId(int socket_id) : socket_id_(socket_id) {}
+ virtual ~SocketStreamId() {}
+ int socket_id() const { return socket_id_; }
+
+ private:
+ int socket_id_;
+};
+
+SocketStreamHost::SocketStreamHost(
+ net::SocketStream::Delegate* delegate,
+ int socket_id)
+ : delegate_(delegate),
+ socket_id_(socket_id) {
+ DCHECK_NE(socket_id_, chrome_common_net::kNoSocketId);
+ VLOG(1) << "SocketStreamHost: socket_id=" << socket_id_;
+}
+
+/* static */
+int SocketStreamHost::SocketIdFromSocketStream(net::SocketStream* socket) {
+ net::SocketStream::UserData* d = socket->GetUserData(kSocketIdKey);
+ if (d) {
+ SocketStreamId* socket_stream_id = static_cast<SocketStreamId*>(d);
+ return socket_stream_id->socket_id();
+ }
+ return chrome_common_net::kNoSocketId;
+}
+
+SocketStreamHost::~SocketStreamHost() {
+ VLOG(1) << "SocketStreamHost destructed socket_id=" << socket_id_;
+ socket_->DetachDelegate();
+}
+
+void SocketStreamHost::Connect(const GURL& url,
+ net::URLRequestContext* request_context) {
+ VLOG(1) << "SocketStreamHost::Connect url=" << url;
+ socket_ = net::SocketStreamJob::CreateSocketStreamJob(url, delegate_);
+ socket_->set_context(request_context);
+ socket_->SetUserData(kSocketIdKey, new SocketStreamId(socket_id_));
+ socket_->Connect();
+}
+
+bool SocketStreamHost::SendData(const std::vector<char>& data) {
+ VLOG(1) << "SocketStreamHost::SendData";
+ return socket_ && socket_->SendData(&data[0], data.size());
+}
+
+void SocketStreamHost::Close() {
+ VLOG(1) << "SocketStreamHost::Close";
+ if (!socket_)
+ return;
+ socket_->Close();
+}
diff --git a/content/browser/renderer_host/socket_stream_host.h b/content/browser/renderer_host/socket_stream_host.h
new file mode 100644
index 0000000..249b35c
--- /dev/null
+++ b/content/browser/renderer_host/socket_stream_host.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_SOCKET_STREAM_HOST_H_
+#define CONTENT_BROWSER_RENDERER_HOST_SOCKET_STREAM_HOST_H_
+#pragma once
+
+#include <vector>
+
+#include "base/ref_counted.h"
+#include "net/socket_stream/socket_stream.h"
+
+class GURL;
+
+namespace net {
+class SocketStreamJob;
+class URLRequestContext;
+} // namespace net
+
+// Host of SocketStreamHandle.
+// Each SocketStreamHandle will have an unique socket_id assigned by
+// SocketStreamHost constructor. If socket id is chrome_common_net::kNoSocketId,
+// there is no SocketStreamHost.
+// Each SocketStreamHost has SocketStream to manage bi-directional
+// communication over socket stream.
+// The lifetime of an instance of this class is completely controlled by the
+// SocketStreamDispatcherHost.
+class SocketStreamHost {
+ public:
+ SocketStreamHost(net::SocketStream::Delegate* delegate, int socket_id);
+ ~SocketStreamHost();
+
+ // Gets socket_id associated with |socket|.
+ static int SocketIdFromSocketStream(net::SocketStream* socket);
+
+ int socket_id() const { return socket_id_; }
+
+ // Starts to open connection to |url|.
+ void Connect(const GURL& url, net::URLRequestContext* request_context);
+
+ // Sends |data| over the socket stream.
+ // socket stream must be open to send data.
+ // Returns true if the data is put in transmit buffer in socket stream.
+ // Returns false otherwise (transmit buffer exceeds limit, or socket
+ // stream is closed).
+ bool SendData(const std::vector<char>& data);
+
+ // Closes the socket stream.
+ void Close();
+
+ private:
+ net::SocketStream::Delegate* delegate_;
+ int socket_id_;
+
+ scoped_refptr<net::SocketStreamJob> socket_;
+
+ DISALLOW_COPY_AND_ASSIGN(SocketStreamHost);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_SOCKET_STREAM_HOST_H_
diff --git a/content/browser/renderer_host/sync_resource_handler.cc b/content/browser/renderer_host/sync_resource_handler.cc
new file mode 100644
index 0000000..1b9a8f6
--- /dev/null
+++ b/content/browser/renderer_host/sync_resource_handler.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/sync_resource_handler.h"
+
+#include "base/logging.h"
+#include "chrome/browser/debugger/devtools_netlog_observer.h"
+#include "chrome/browser/net/load_timing_observer.h"
+#include "chrome/common/render_messages.h"
+#include "content/browser/renderer_host/global_request_id.h"
+#include "content/browser/renderer_host/resource_dispatcher_host.h"
+#include "content/browser/renderer_host/resource_message_filter.h"
+#include "net/base/io_buffer.h"
+#include "net/http/http_response_headers.h"
+
+SyncResourceHandler::SyncResourceHandler(
+ ResourceMessageFilter* filter,
+ const GURL& url,
+ IPC::Message* result_message,
+ ResourceDispatcherHost* resource_dispatcher_host)
+ : read_buffer_(new net::IOBuffer(kReadBufSize)),
+ filter_(filter),
+ result_message_(result_message),
+ rdh_(resource_dispatcher_host) {
+ result_.final_url = url;
+}
+
+SyncResourceHandler::~SyncResourceHandler() {
+}
+
+bool SyncResourceHandler::OnUploadProgress(int request_id,
+ uint64 position,
+ uint64 size) {
+ return true;
+}
+
+bool SyncResourceHandler::OnRequestRedirected(int request_id,
+ const GURL& new_url,
+ ResourceResponse* response,
+ bool* defer) {
+ net::URLRequest* request = rdh_->GetURLRequest(
+ GlobalRequestID(filter_->child_id(), request_id));
+ LoadTimingObserver::PopulateTimingInfo(request, response);
+ DevToolsNetLogObserver::PopulateResponseInfo(request, response);
+ // TODO(darin): It would be much better if this could live in WebCore, but
+ // doing so requires API changes at all levels. Similar code exists in
+ // WebCore/platform/network/cf/ResourceHandleCFNet.cpp :-(
+ if (new_url.GetOrigin() != result_.final_url.GetOrigin()) {
+ LOG(ERROR) << "Cross origin redirect denied";
+ return false;
+ }
+ result_.final_url = new_url;
+ return true;
+}
+
+bool SyncResourceHandler::OnResponseStarted(int request_id,
+ ResourceResponse* response) {
+ net::URLRequest* request = rdh_->GetURLRequest(
+ GlobalRequestID(filter_->child_id(), request_id));
+ LoadTimingObserver::PopulateTimingInfo(request, response);
+ DevToolsNetLogObserver::PopulateResponseInfo(request, response);
+
+ // We don't care about copying the status here.
+ result_.headers = response->response_head.headers;
+ result_.mime_type = response->response_head.mime_type;
+ result_.charset = response->response_head.charset;
+ result_.download_file_path = response->response_head.download_file_path;
+ result_.request_time = response->response_head.request_time;
+ result_.response_time = response->response_head.response_time;
+ result_.connection_id = response->response_head.connection_id;
+ result_.connection_reused = response->response_head.connection_reused;
+ result_.load_timing = response->response_head.load_timing;
+ result_.devtools_info = response->response_head.devtools_info;
+ return true;
+}
+
+bool SyncResourceHandler::OnWillStart(int request_id,
+ const GURL& url,
+ bool* defer) {
+ return true;
+}
+
+bool SyncResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf,
+ int* buf_size, int min_size) {
+ DCHECK(min_size == -1);
+ *buf = read_buffer_.get();
+ *buf_size = kReadBufSize;
+ return true;
+}
+
+bool SyncResourceHandler::OnReadCompleted(int request_id, int* bytes_read) {
+ if (!*bytes_read)
+ return true;
+ result_.data.append(read_buffer_->data(), *bytes_read);
+ return true;
+}
+
+bool SyncResourceHandler::OnResponseCompleted(
+ int request_id,
+ const net::URLRequestStatus& status,
+ const std::string& security_info) {
+ result_.status = status;
+
+ ViewHostMsg_SyncLoad::WriteReplyParams(result_message_, result_);
+ filter_->Send(result_message_);
+ result_message_ = NULL;
+ return true;
+}
+
+void SyncResourceHandler::OnRequestClosed() {
+ if (!result_message_)
+ return;
+
+ result_message_->set_reply_error();
+ filter_->Send(result_message_);
+}
diff --git a/content/browser/renderer_host/sync_resource_handler.h b/content/browser/renderer_host/sync_resource_handler.h
new file mode 100644
index 0000000..95e8bfe
--- /dev/null
+++ b/content/browser/renderer_host/sync_resource_handler.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_SYNC_RESOURCE_HANDLER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_SYNC_RESOURCE_HANDLER_H_
+#pragma once
+
+#include <string>
+
+#include "content/browser/renderer_host/resource_handler.h"
+#include "chrome/common/resource_response.h"
+
+class ResourceDispatcherHost;
+class ResourceMessageFilter;
+
+namespace IPC {
+class Message;
+}
+
+namespace net {
+class IOBuffer;
+}
+
+// Used to complete a synchronous resource request in response to resource load
+// events from the resource dispatcher host.
+class SyncResourceHandler : public ResourceHandler {
+ public:
+ SyncResourceHandler(ResourceMessageFilter* filter,
+ const GURL& url,
+ IPC::Message* result_message,
+ ResourceDispatcherHost* resource_dispatcher_host);
+
+ virtual bool OnUploadProgress(int request_id, uint64 position, uint64 size);
+ virtual bool OnRequestRedirected(int request_id, const GURL& new_url,
+ ResourceResponse* response, bool* defer);
+ virtual bool OnResponseStarted(int request_id, ResourceResponse* response);
+ virtual bool OnWillStart(int request_id, const GURL& url, bool* defer);
+ virtual bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size,
+ int min_size);
+ virtual bool OnReadCompleted(int request_id, int* bytes_read);
+ virtual bool OnResponseCompleted(int request_id,
+ const net::URLRequestStatus& status,
+ const std::string& security_info);
+ virtual void OnRequestClosed();
+
+ private:
+ enum { kReadBufSize = 3840 };
+
+ ~SyncResourceHandler();
+
+ scoped_refptr<net::IOBuffer> read_buffer_;
+
+ SyncLoadResult result_;
+ ResourceMessageFilter* filter_;
+ IPC::Message* result_message_;
+ ResourceDispatcherHost* rdh_;
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_SYNC_RESOURCE_HANDLER_H_
diff --git a/content/browser/renderer_host/x509_user_cert_resource_handler.cc b/content/browser/renderer_host/x509_user_cert_resource_handler.cc
new file mode 100644
index 0000000..7393e73
--- /dev/null
+++ b/content/browser/renderer_host/x509_user_cert_resource_handler.cc
@@ -0,0 +1,130 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/renderer_host/x509_user_cert_resource_handler.h"
+
+#include "base/string_util.h"
+#include "chrome/browser/download/download_types.h"
+#include "chrome/browser/ssl/ssl_add_cert_handler.h"
+#include "chrome/common/resource_response.h"
+#include "chrome/common/url_constants.h"
+#include "content/browser/renderer_host/resource_dispatcher_host.h"
+#include "content/browser/renderer_host/resource_dispatcher_host_request_info.h"
+#include "net/base/io_buffer.h"
+#include "net/base/mime_sniffer.h"
+#include "net/base/mime_util.h"
+#include "net/base/x509_certificate.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_status.h"
+
+X509UserCertResourceHandler::X509UserCertResourceHandler(
+ ResourceDispatcherHost* host, net::URLRequest* request,
+ int render_process_host_id, int render_view_id)
+ : host_(host),
+ request_(request),
+ content_length_(0),
+ buffer_(new DownloadBuffer),
+ read_buffer_(NULL),
+ resource_buffer_(NULL),
+ render_process_host_id_(render_process_host_id),
+ render_view_id_(render_view_id) {
+}
+
+bool X509UserCertResourceHandler::OnUploadProgress(int request_id,
+ uint64 position,
+ uint64 size) {
+ return true;
+}
+
+bool X509UserCertResourceHandler::OnRequestRedirected(int request_id,
+ const GURL& url,
+ ResourceResponse* resp,
+ bool* defer) {
+ url_ = url;
+ return true;
+}
+
+bool X509UserCertResourceHandler::OnResponseStarted(int request_id,
+ ResourceResponse* resp) {
+ return (resp->response_head.mime_type == "application/x-x509-user-cert");
+}
+
+bool X509UserCertResourceHandler::OnWillStart(int request_id,
+ const GURL& url,
+ bool* defer) {
+ return true;
+}
+
+bool X509UserCertResourceHandler::OnWillRead(int request_id,
+ net::IOBuffer** buf,
+ int* buf_size,
+ int min_size) {
+ // TODO(gauravsh): Should we use 'min_size' here?
+ DCHECK(buf && buf_size);
+ if (!read_buffer_) {
+ read_buffer_ = new net::IOBuffer(kReadBufSize);
+ }
+ *buf = read_buffer_.get();
+ *buf_size = kReadBufSize;
+
+ return true;
+}
+
+bool X509UserCertResourceHandler::OnReadCompleted(int request_id,
+ int* bytes_read) {
+ if (!*bytes_read)
+ return true;
+
+ // We have more data to read.
+ DCHECK(read_buffer_);
+ content_length_ += *bytes_read;
+
+ // Release the ownership of the buffer, and store a reference
+ // to it. A new one will be allocated in OnWillRead().
+ net::IOBuffer* buffer = NULL;
+ read_buffer_.swap(&buffer);
+ // TODO(gauravsh): Should this be handled by a separate thread?
+ buffer_->contents.push_back(std::make_pair(buffer, *bytes_read));
+
+ return true;
+}
+
+bool X509UserCertResourceHandler::OnResponseCompleted(
+ int request_id,
+ const net::URLRequestStatus& urs,
+ const std::string& sec_info) {
+ if (urs.status() != net::URLRequestStatus::SUCCESS)
+ return false;
+
+ // TODO(gauravsh): Verify that 'request_id' was actually a keygen form post
+ // and only then import the certificate.
+ AssembleResource();
+ scoped_refptr<net::X509Certificate> cert(
+ net::X509Certificate::CreateFromBytes(resource_buffer_->data(),
+ content_length_));
+ // The handler will run the UI and delete itself when it's finished.
+ new SSLAddCertHandler(request_, cert, render_process_host_id_,
+ render_view_id_);
+ return true;
+}
+
+void X509UserCertResourceHandler::OnRequestClosed() {
+}
+
+X509UserCertResourceHandler::~X509UserCertResourceHandler() {
+}
+
+void X509UserCertResourceHandler::AssembleResource() {
+ size_t bytes_copied = 0;
+ resource_buffer_ = new net::IOBuffer(content_length_);
+
+ for (size_t i = 0; i < buffer_->contents.size(); ++i) {
+ net::IOBuffer* data = buffer_->contents[i].first;
+ const int data_len = buffer_->contents[i].second;
+ DCHECK(bytes_copied + data_len <= content_length_);
+ memcpy(resource_buffer_->data() + bytes_copied, data->data(), data_len);
+ bytes_copied += data_len;
+ }
+}
diff --git a/content/browser/renderer_host/x509_user_cert_resource_handler.h b/content/browser/renderer_host/x509_user_cert_resource_handler.h
new file mode 100644
index 0000000..0d2df61
--- /dev/null
+++ b/content/browser/renderer_host/x509_user_cert_resource_handler.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_X509_USER_CERT_RESOURCE_HANDLER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_X509_USER_CERT_RESOURCE_HANDLER_H_
+#pragma once
+
+#include <string>
+
+#include "base/scoped_ptr.h"
+#include "content/browser/renderer_host/resource_handler.h"
+#include "googleurl/src/gurl.h"
+
+namespace net {
+class URLRequest;
+class URLRequestStatus;
+} // namespace net
+
+class ResourceDispatcherHost;
+struct DownloadBuffer;
+
+// This class handles the "application/x-x509-user-cert" mime-type
+// which is a certificate generated by a CA after a previous
+// <keygen> form post.
+
+class X509UserCertResourceHandler : public ResourceHandler {
+ public:
+ X509UserCertResourceHandler(ResourceDispatcherHost* host,
+ net::URLRequest* request,
+ int render_process_host_id, int render_view_id);
+
+ virtual bool OnUploadProgress(int request_id, uint64 position, uint64 size);
+
+ // Not needed, as this event handler ought to be the final resource.
+ virtual bool OnRequestRedirected(int request_id, const GURL& url,
+ ResourceResponse* resp, bool* defer);
+
+ // Check if this indeed an X509 cert.
+ virtual bool OnResponseStarted(int request_id, ResourceResponse* resp);
+
+ // Pass-through implementation.
+ virtual bool OnWillStart(int request_id, const GURL& url, bool* defer);
+
+ // Create a new buffer to store received data.
+ virtual bool OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size,
+ int min_size);
+
+ // A read was completed, maybe allocate a new buffer for further data.
+ virtual bool OnReadCompleted(int request_id, int* bytes_read);
+
+ // Done downloading the certificate.
+ virtual bool OnResponseCompleted(int request_id,
+ const net::URLRequestStatus& urs,
+ const std::string& sec_info);
+
+ virtual void OnRequestClosed();
+
+ private:
+ virtual ~X509UserCertResourceHandler();
+
+ void AssembleResource();
+
+ GURL url_;
+ ResourceDispatcherHost* host_;
+ net::URLRequest* request_;
+ size_t content_length_;
+ scoped_ptr<DownloadBuffer> buffer_;
+ scoped_refptr<net::IOBuffer> read_buffer_;
+ scoped_refptr<net::IOBuffer> resource_buffer_; // Downloaded certificate.
+ static const int kReadBufSize = 32768;
+ // The id of the |RenderProcessHost| which started the download.
+ int render_process_host_id_;
+ // The id of the |RenderView| which started the download.
+ int render_view_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(X509UserCertResourceHandler);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_X509_USER_CERT_RESOURCE_HANDLER_H_
diff --git a/content/browser/site_instance.cc b/content/browser/site_instance.cc
new file mode 100644
index 0000000..642a6b7
--- /dev/null
+++ b/content/browser/site_instance.cc
@@ -0,0 +1,236 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/site_instance.h"
+
+#include "chrome/browser/browsing_instance.h"
+#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/webui/web_ui_factory.h"
+#include "chrome/browser/renderer_host/browser_render_process_host.h"
+#include "chrome/common/notification_service.h"
+#include "chrome/common/url_constants.h"
+#include "net/base/registry_controlled_domain.h"
+
+// We treat javascript:, about:crash, about:hang, and about:shorthang as the
+// same site as any URL since they are actually modifiers on existing pages.
+static bool IsURLSameAsAnySiteInstance(const GURL& url) {
+ if (!url.is_valid())
+ return false;
+ return url.SchemeIs(chrome::kJavaScriptScheme) ||
+ url.spec() == chrome::kAboutCrashURL ||
+ url.spec() == chrome::kAboutKillURL ||
+ url.spec() == chrome::kAboutHangURL ||
+ url.spec() == chrome::kAboutShorthangURL;
+}
+
+SiteInstance::SiteInstance(BrowsingInstance* browsing_instance)
+ : browsing_instance_(browsing_instance),
+ render_process_host_factory_(NULL),
+ process_(NULL),
+ max_page_id_(-1),
+ has_site_(false) {
+ DCHECK(browsing_instance);
+
+ registrar_.Add(this, NotificationType::RENDERER_PROCESS_TERMINATED,
+ NotificationService::AllSources());
+}
+
+SiteInstance::~SiteInstance() {
+ // Now that no one is referencing us, we can safely remove ourselves from
+ // the BrowsingInstance. Any future visits to a page from this site
+ // (within the same BrowsingInstance) can safely create a new SiteInstance.
+ if (has_site_)
+ browsing_instance_->UnregisterSiteInstance(this);
+}
+
+bool SiteInstance::HasProcess() const {
+ return (process_ != NULL);
+}
+
+RenderProcessHost* SiteInstance::GetProcess() {
+ // TODO(erikkay) It would be nice to ensure that the renderer type had been
+ // properly set before we get here. The default tab creation case winds up
+ // with no site set at this point, so it will default to TYPE_NORMAL. This
+ // may not be correct, so we'll wind up potentially creating a process that
+ // we then throw away, or worse sharing a process with the wrong process type.
+ // See crbug.com/43448.
+
+ // Create a new process if ours went away or was reused.
+ if (!process_) {
+ // See if we should reuse an old process
+ if (RenderProcessHost::ShouldTryToUseExistingProcessHost())
+ process_ = RenderProcessHost::GetExistingProcessHost(
+ browsing_instance_->profile(), GetRendererType());
+
+ // Otherwise (or if that fails), create a new one.
+ if (!process_) {
+ if (render_process_host_factory_) {
+ process_ = render_process_host_factory_->CreateRenderProcessHost(
+ browsing_instance_->profile());
+ } else {
+ process_ = new BrowserRenderProcessHost(browsing_instance_->profile());
+ }
+ }
+
+ // Make sure the process starts at the right max_page_id
+ process_->UpdateMaxPageID(max_page_id_);
+ }
+ DCHECK(process_);
+
+ return process_;
+}
+
+void SiteInstance::SetSite(const GURL& url) {
+ // A SiteInstance's site should not change.
+ // TODO(creis): When following links or script navigations, we can currently
+ // render pages from other sites in this SiteInstance. This will eventually
+ // be fixed, but until then, we should still not set the site of a
+ // SiteInstance more than once.
+ DCHECK(!has_site_);
+
+ // Remember that this SiteInstance has been used to load a URL, even if the
+ // URL is invalid.
+ has_site_ = true;
+ site_ = GetSiteForURL(browsing_instance_->profile(), url);
+
+ // Now that we have a site, register it with the BrowsingInstance. This
+ // ensures that we won't create another SiteInstance for this site within
+ // the same BrowsingInstance, because all same-site pages within a
+ // BrowsingInstance can script each other.
+ browsing_instance_->RegisterSiteInstance(this);
+}
+
+bool SiteInstance::HasRelatedSiteInstance(const GURL& url) {
+ return browsing_instance_->HasSiteInstance(url);
+}
+
+SiteInstance* SiteInstance::GetRelatedSiteInstance(const GURL& url) {
+ return browsing_instance_->GetSiteInstanceForURL(url);
+}
+
+/*static*/
+SiteInstance* SiteInstance::CreateSiteInstance(Profile* profile) {
+ return new SiteInstance(new BrowsingInstance(profile));
+}
+
+/*static*/
+SiteInstance* SiteInstance::CreateSiteInstanceForURL(Profile* profile,
+ const GURL& url) {
+ // This BrowsingInstance may be deleted if it returns an existing
+ // SiteInstance.
+ scoped_refptr<BrowsingInstance> instance(new BrowsingInstance(profile));
+ return instance->GetSiteInstanceForURL(url);
+}
+
+/*static*/
+GURL SiteInstance::GetSiteForURL(Profile* profile, const GURL& real_url) {
+ GURL url = GetEffectiveURL(profile, real_url);
+
+ // URLs with no host should have an empty site.
+ GURL site;
+
+ // TODO(creis): For many protocols, we should just treat the scheme as the
+ // site, since there is no host. e.g., file:, about:, chrome:
+
+ // If the url has a host, then determine the site.
+ if (url.has_host()) {
+ // Only keep the scheme and registered domain as given by GetOrigin. This
+ // may also include a port, which we need to drop.
+ site = url.GetOrigin();
+
+ // Remove port, if any.
+ if (site.has_port()) {
+ GURL::Replacements rep;
+ rep.ClearPort();
+ site = site.ReplaceComponents(rep);
+ }
+
+ // If this URL has a registered domain, we only want to remember that part.
+ std::string domain =
+ net::RegistryControlledDomainService::GetDomainAndRegistry(url);
+ if (!domain.empty()) {
+ GURL::Replacements rep;
+ rep.SetHostStr(domain);
+ site = site.ReplaceComponents(rep);
+ }
+ }
+ return site;
+}
+
+/*static*/
+bool SiteInstance::IsSameWebSite(Profile* profile,
+ const GURL& real_url1, const GURL& real_url2) {
+ GURL url1 = GetEffectiveURL(profile, real_url1);
+ GURL url2 = GetEffectiveURL(profile, real_url2);
+
+ // We infer web site boundaries based on the registered domain name of the
+ // top-level page and the scheme. We do not pay attention to the port if
+ // one is present, because pages served from different ports can still
+ // access each other if they change their document.domain variable.
+
+ // Some special URLs will match the site instance of any other URL. This is
+ // done before checking both of them for validity, since we want these URLs
+ // to have the same site instance as even an invalid one.
+ if (IsURLSameAsAnySiteInstance(url1) || IsURLSameAsAnySiteInstance(url2))
+ return true;
+
+ // If either URL is invalid, they aren't part of the same site.
+ if (!url1.is_valid() || !url2.is_valid())
+ return false;
+
+ // If the schemes differ, they aren't part of the same site.
+ if (url1.scheme() != url2.scheme())
+ return false;
+
+ return net::RegistryControlledDomainService::SameDomainOrHost(url1, url2);
+}
+
+/*static*/
+GURL SiteInstance::GetEffectiveURL(Profile* profile, const GURL& url) {
+ if (!profile || !profile->GetExtensionService())
+ return url;
+
+ const Extension* extension =
+ profile->GetExtensionService()->GetExtensionByWebExtent(url);
+ if (extension) {
+ // If the URL is part of an extension's web extent, convert it to an
+ // extension URL.
+ return extension->GetResourceURL(url.path());
+ } else {
+ return url;
+ }
+}
+
+/*static*/
+RenderProcessHost::Type SiteInstance::RendererTypeForURL(const GURL& url) {
+ if (!url.is_valid())
+ return RenderProcessHost::TYPE_NORMAL;
+
+ if (url.SchemeIs(chrome::kExtensionScheme))
+ return RenderProcessHost::TYPE_EXTENSION;
+
+ // TODO(erikkay) creis recommends using UseWebUIForURL instead.
+ if (WebUIFactory::HasWebUIScheme(url))
+ return RenderProcessHost::TYPE_WEBUI;
+
+ return RenderProcessHost::TYPE_NORMAL;
+}
+
+RenderProcessHost::Type SiteInstance::GetRendererType() {
+ // We may not have a site at this point, which generally means this is a
+ // normal navigation.
+ if (!has_site_)
+ return RenderProcessHost::TYPE_NORMAL;
+
+ return RendererTypeForURL(site_);
+}
+
+void SiteInstance::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK(type == NotificationType::RENDERER_PROCESS_TERMINATED);
+ RenderProcessHost* rph = Source<RenderProcessHost>(source).ptr();
+ if (rph == process_)
+ process_ = NULL;
+}
diff --git a/content/browser/site_instance.h b/content/browser/site_instance.h
new file mode 100644
index 0000000..058cc54
--- /dev/null
+++ b/content/browser/site_instance.h
@@ -0,0 +1,197 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_SITE_INSTANCE_H_
+#define CONTENT_BROWSER_RENDERER_HOST_SITE_INSTANCE_H_
+#pragma once
+
+#include "chrome/common/notification_observer.h"
+#include "chrome/common/notification_registrar.h"
+#include "content/browser/renderer_host/render_process_host.h"
+#include "googleurl/src/gurl.h"
+
+class BrowsingInstance;
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// SiteInstance class
+//
+// A SiteInstance is a data structure that is associated with all pages in a
+// given instance of a web site. Here, a web site is identified by its
+// registered domain name and scheme. An instance includes all pages
+// that are connected (i.e., either a user or a script navigated from one
+// to the other). We represent instances using the BrowsingInstance class.
+//
+// In --process-per-tab, one SiteInstance is created for each tab (i.e., in the
+// TabContents constructor), unless the tab is created by script (i.e., in
+// TabContents::CreateNewView). This corresponds to one process per
+// BrowsingInstance.
+//
+// In process-per-site-instance (the current default process model),
+// SiteInstances are created (1) when the user manually creates a new tab
+// (which also creates a new BrowsingInstance), and (2) when the user navigates
+// across site boundaries (which uses the same BrowsingInstance). If the user
+// navigates within a site, or opens links in new tabs within a site, the same
+// SiteInstance is used.
+//
+// In --process-per-site, we consolidate all SiteInstances for a given site,
+// throughout the entire profile. This ensures that only one process will be
+// dedicated to each site.
+//
+// Each NavigationEntry for a TabContents points to the SiteInstance that
+// rendered it. Each RenderViewHost also points to the SiteInstance that it is
+// associated with. A SiteInstance keeps track of the number of these
+// references and deletes itself when the count goes to zero. This means that
+// a SiteInstance is only live as long as it is accessible, either from new
+// tabs with no NavigationEntries or in NavigationEntries in the history.
+//
+///////////////////////////////////////////////////////////////////////////////
+class SiteInstance : public base::RefCounted<SiteInstance>,
+ public NotificationObserver {
+ public:
+ // Get the BrowsingInstance to which this SiteInstance belongs.
+ BrowsingInstance* browsing_instance() { return browsing_instance_; }
+
+ // Sets the factory used to create new RenderProcessHosts. This will also be
+ // passed on to SiteInstances spawned by this one.
+ //
+ // The factory must outlive the SiteInstance; ownership is not transferred. It
+ // may be NULL, in which case the default BrowserRenderProcessHost will be
+ // created (this is the behavior if you don't call this function).
+ void set_render_process_host_factory(RenderProcessHostFactory* rph_factory) {
+ render_process_host_factory_ = rph_factory;
+ }
+
+ // Update / Get the max page ID for this SiteInstance.
+ void UpdateMaxPageID(int32 page_id) {
+ if (page_id > max_page_id_)
+ max_page_id_ = page_id;
+ }
+ int32 max_page_id() const { return max_page_id_; }
+
+ // Whether this SiteInstance has a running process associated with it.
+ bool HasProcess() const;
+
+ // Returns the current process being used to render pages in this
+ // SiteInstance. If the process has crashed or otherwise gone away, then
+ // this method will create a new process and update our host ID accordingly.
+ RenderProcessHost* GetProcess();
+
+ // Set / Get the web site that this SiteInstance is rendering pages for.
+ // This includes the scheme and registered domain, but not the port. If the
+ // URL does not have a valid registered domain, then the full hostname is
+ // stored.
+ void SetSite(const GURL& url);
+ const GURL& site() const { return site_; }
+ bool has_site() const { return has_site_; }
+
+ // Returns whether there is currently a related SiteInstance (registered with
+ // BrowsingInstance) for the site of the given url. If so, we should try to
+ // avoid dedicating an unused SiteInstance to it (e.g., in a new tab).
+ bool HasRelatedSiteInstance(const GURL& url);
+
+ // Gets a SiteInstance for the given URL that shares the current
+ // BrowsingInstance, creating a new SiteInstance if necessary. This ensures
+ // that a BrowsingInstance only has one SiteInstance per site, so that pages
+ // in a BrowsingInstance have the ability to script each other. Callers
+ // should ensure that this SiteInstance becomes ref counted, by storing it in
+ // a scoped_refptr. (By having this method, we can hide the BrowsingInstance
+ // class from the rest of the codebase.)
+ // TODO(creis): This may be an argument to build a pass_refptr<T> class, as
+ // Darin suggests.
+ SiteInstance* GetRelatedSiteInstance(const GURL& url);
+
+ // Factory method to create a new SiteInstance. This will create a new
+ // new BrowsingInstance, so it should only be used when creating a new tab
+ // from scratch (or similar circumstances). Callers should ensure that
+ // this SiteInstance becomes ref counted, by storing it in a scoped_refptr.
+ //
+ // The render process host factory may be NULL. See SiteInstance constructor.
+ //
+ // TODO(creis): This may be an argument to build a pass_refptr<T> class, as
+ // Darin suggests.
+ static SiteInstance* CreateSiteInstance(Profile* profile);
+
+ // Factory method to get the appropriate SiteInstance for the given URL, in
+ // a new BrowsingInstance. Use this instead of CreateSiteInstance when you
+ // know the URL, since it allows special site grouping rules to be applied
+ // (for example, to group chrome-ui pages into the same instance).
+ static SiteInstance* CreateSiteInstanceForURL(Profile* profile,
+ const GURL& url);
+
+ // Returns the site for the given URL, which includes only the scheme and
+ // registered domain. Returns an empty GURL if the URL has no host.
+ static GURL GetSiteForURL(Profile* profile, const GURL& url);
+
+ // Return whether both URLs are part of the same web site, for the purpose of
+ // assigning them to processes accordingly. The decision is currently based
+ // on the registered domain of the URLs (google.com, bbc.co.uk), as well as
+ // the scheme (https, http). This ensures that two pages will be in
+ // the same process if they can communicate with other via JavaScript.
+ // (e.g., docs.google.com and mail.google.com have DOM access to each other
+ // if they both set their document.domain properties to google.com.)
+ static bool IsSameWebSite(Profile* profile,
+ const GURL& url1, const GURL& url2);
+
+ // Returns the renderer type for this URL.
+ static RenderProcessHost::Type RendererTypeForURL(const GURL& url);
+
+ protected:
+ friend class base::RefCounted<SiteInstance>;
+ friend class BrowsingInstance;
+
+ // Virtual to allow tests to extend it.
+ virtual ~SiteInstance();
+
+ // Create a new SiteInstance. Protected to give access to BrowsingInstance
+ // and tests; most callers should use CreateSiteInstance or
+ // GetRelatedSiteInstance instead.
+ explicit SiteInstance(BrowsingInstance* browsing_instance);
+
+ // Get the effective URL for the given actual URL. If the URL is part of an
+ // installed app, the effective URL is an extension URL with the ID of that
+ // extension as the host. This has the effect of grouping apps together in
+ // a common SiteInstance.
+ static GURL GetEffectiveURL(Profile* profile, const GURL& url);
+
+ // Returns the type of renderer process this instance belongs in, for grouping
+ // purposes.
+ RenderProcessHost::Type GetRendererType();
+
+ private:
+ // NotificationObserver implementation.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ NotificationRegistrar registrar_;
+
+ // BrowsingInstance to which this SiteInstance belongs.
+ scoped_refptr<BrowsingInstance> browsing_instance_;
+
+ // Factory for new RenderProcessHosts, not owned by this class. NULL indiactes
+ // that the default BrowserRenderProcessHost should be created.
+ const RenderProcessHostFactory* render_process_host_factory_;
+
+ // Current RenderProcessHost that is rendering pages for this SiteInstance.
+ // This pointer will only change once the RenderProcessHost is destructed. It
+ // will still remain the same even if the process crashes, since in that
+ // scenario the RenderProcessHost remains the same.
+ RenderProcessHost* process_;
+
+ // The current max_page_id in the SiteInstance's RenderProcessHost. If the
+ // rendering process dies, its replacement should start issuing page IDs that
+ // are larger than this value.
+ int32 max_page_id_;
+
+ // The web site that this SiteInstance is rendering pages for.
+ GURL site_;
+
+ // Whether SetSite has been called.
+ bool has_site_;
+
+ DISALLOW_COPY_AND_ASSIGN(SiteInstance);
+};
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_SITE_INSTANCE_H_
diff --git a/content/content_browser.gypi b/content/content_browser.gypi
index 8abb4ab..82085d1 100644
--- a/content/content_browser.gypi
+++ b/content/content_browser.gypi
@@ -12,6 +12,7 @@
#'content_common',
'../app/app.gyp:app_resources',
'../skia/skia.gyp:skia',
+ # TabContents uses zoom constants and functions from WebKit::WebView.
'../third_party/WebKit/Source/WebKit/chromium/WebKit.gyp:webkit',
'../ui/ui.gyp:ui_base',
],
@@ -19,6 +20,92 @@
'..',
],
'sources': [
+ 'browser/renderer_host/accelerated_surface_container_mac.cc',
+ 'browser/renderer_host/accelerated_surface_container_mac.h',
+ 'browser/renderer_host/accelerated_surface_container_manager_mac.cc',
+ 'browser/renderer_host/accelerated_surface_container_manager_mac.h',
+ 'browser/renderer_host/async_resource_handler.cc',
+ 'browser/renderer_host/async_resource_handler.h',
+ 'browser/renderer_host/audio_renderer_host.cc',
+ 'browser/renderer_host/audio_renderer_host.h',
+ 'browser/renderer_host/audio_sync_reader.cc',
+ 'browser/renderer_host/audio_sync_reader.h',
+ 'browser/renderer_host/backing_store.cc',
+ 'browser/renderer_host/backing_store.h',
+ 'browser/renderer_host/backing_store_mac.h',
+ 'browser/renderer_host/backing_store_mac.mm',
+ 'browser/renderer_host/backing_store_manager.cc',
+ 'browser/renderer_host/backing_store_manager.h',
+ 'browser/renderer_host/backing_store_skia.cc',
+ 'browser/renderer_host/backing_store_skia.h',
+ 'browser/renderer_host/backing_store_win.cc',
+ 'browser/renderer_host/backing_store_win.h',
+ 'browser/renderer_host/backing_store_x.cc',
+ 'browser/renderer_host/backing_store_x.h',
+ 'browser/renderer_host/blob_message_filter.cc',
+ 'browser/renderer_host/blob_message_filter.h',
+ 'browser/renderer_host/buffered_resource_handler.cc',
+ 'browser/renderer_host/buffered_resource_handler.h',
+ 'browser/renderer_host/cross_site_resource_handler.cc',
+ 'browser/renderer_host/cross_site_resource_handler.h',
+ 'browser/renderer_host/database_message_filter.cc',
+ 'browser/renderer_host/database_message_filter.h',
+ 'browser/renderer_host/file_utilities_message_filter.cc',
+ 'browser/renderer_host/file_utilities_message_filter.h',
+ 'browser/renderer_host/global_request_id.h',
+ 'browser/renderer_host/gpu_message_filter.cc',
+ 'browser/renderer_host/gpu_message_filter.h',
+ 'browser/renderer_host/pepper_file_message_filter.cc',
+ 'browser/renderer_host/pepper_file_message_filter.h',
+ 'browser/renderer_host/pepper_message_filter.cc',
+ 'browser/renderer_host/pepper_message_filter.h',
+ 'browser/renderer_host/redirect_to_file_resource_handler.cc',
+ 'browser/renderer_host/redirect_to_file_resource_handler.h',
+ 'browser/renderer_host/render_message_filter.cc',
+ 'browser/renderer_host/render_message_filter.h',
+ 'browser/renderer_host/render_message_filter_gtk.cc',
+ 'browser/renderer_host/render_message_filter_mac.mm',
+ 'browser/renderer_host/render_message_filter_win.cc',
+ 'browser/renderer_host/render_process_host.cc',
+ 'browser/renderer_host/render_process_host.h',
+ 'browser/renderer_host/render_sandbox_host_linux.cc',
+ 'browser/renderer_host/render_sandbox_host_linux.h',
+ 'browser/renderer_host/render_view_host.cc',
+ 'browser/renderer_host/render_view_host.h',
+ 'browser/renderer_host/render_view_host_delegate.cc',
+ 'browser/renderer_host/render_view_host_delegate.h',
+ 'browser/renderer_host/render_view_host_factory.cc',
+ 'browser/renderer_host/render_view_host_factory.h',
+ 'browser/renderer_host/render_view_host_notification_task.h',
+ 'browser/renderer_host/render_widget_fullscreen_host.cc',
+ 'browser/renderer_host/render_widget_fullscreen_host.h',
+ 'browser/renderer_host/render_widget_helper.cc',
+ 'browser/renderer_host/render_widget_helper.h',
+ 'browser/renderer_host/render_widget_host.cc',
+ 'browser/renderer_host/render_widget_host.h',
+ 'browser/renderer_host/render_widget_host_view.cc',
+ 'browser/renderer_host/render_widget_host_view.h',
+ 'browser/renderer_host/resource_dispatcher_host.cc',
+ 'browser/renderer_host/resource_dispatcher_host.h',
+ 'browser/renderer_host/resource_dispatcher_host_request_info.cc',
+ 'browser/renderer_host/resource_dispatcher_host_request_info.h',
+ 'browser/renderer_host/resource_handler.h',
+ 'browser/renderer_host/resource_message_filter.cc',
+ 'browser/renderer_host/resource_message_filter.h',
+ 'browser/renderer_host/resource_queue.cc',
+ 'browser/renderer_host/resource_queue.h',
+ 'browser/renderer_host/resource_request_details.cc',
+ 'browser/renderer_host/resource_request_details.h',
+ 'browser/renderer_host/socket_stream_dispatcher_host.cc',
+ 'browser/renderer_host/socket_stream_dispatcher_host.h',
+ 'browser/renderer_host/socket_stream_host.cc',
+ 'browser/renderer_host/socket_stream_host.h',
+ 'browser/renderer_host/sync_resource_handler.cc',
+ 'browser/renderer_host/sync_resource_handler.h',
+ 'browser/renderer_host/x509_user_cert_resource_handler.cc',
+ 'browser/renderer_host/x509_user_cert_resource_handler.h',
+ 'browser/site_instance.cc',
+ 'browser/site_instance.h',
'browser/tab_contents/background_contents.cc',
'browser/tab_contents/background_contents.h',
'browser/tab_contents/constrained_window.h',